Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
- name: Lint markdown docs
run: |
npm install -g markdownlint-cli2@0.18.1
markdownlint-cli2 README.md AGENTS.md ARCHITECTURE.md CLAUDE.md CONTRIBUTING.md .github/**/*.md docs/**/*.md
markdownlint-cli2 README.md AGENTS.md ARCHITECTURE.md CLAUDE.md CONTRIBUTING.md .github/**/*.md docs/**/*.md .jules/*.md

- name: Lint GitHub workflows
run: |
Expand Down
13 changes: 13 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
# Bolt Journal

## 2024-07-04 - R μ–Έμ–΄μ—μ„œ 루프 λ‚΄ 데이터 ν”„λ ˆμž„ 탐색 병λͺ© μ΅œμ ν™”

**Learning:** Rμ—μ„œ 루프λ₯Ό λŒλ©΄μ„œ 맀번 데이터 ν”„λ ˆμž„μ„ μ„œλΈŒμ…‹νŒ…(subsetting)ν•˜λŠ” μž‘μ—…μ€ 볡사 μ˜€λ²„ν—€λ“œλ‘œ 인해 맀우 느렀질 수 μžˆμŠ΅λ‹ˆλ‹€. 특히 곡톡 λ¬Έν•­ μˆ˜κ°€ λ§Žμ•„μ§ˆ 경우 O(N^2)의 λΉ„νš¨μœ¨μ„ μ΄ˆλž˜ν•©λ‹ˆλ‹€.
**Action:** 루프 λ‚΄μ—μ„œ μˆ˜ν–‰ν•˜λ˜ 데이터 ν”„λ ˆμž„ 쑰회λ₯Ό 루프 μ™ΈλΆ€μ—μ„œ ν•œ λ²ˆμ— `as.character(unlist(...))`둜 μ²˜λ¦¬ν•˜λŠ” 벑터 μ—°μ‚°μœΌλ‘œ λ³€κ²½ν•˜μ—¬ νƒ€μž… λ³€ν™˜ 없이 O(1) μˆ˜μ€€μœΌλ‘œ μ„±λŠ₯을 크게 ν–₯μƒμ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

## 2024-07-07 - R μ–Έμ–΄μ—μ„œ 데이터 ν”„λ ˆμž„μ˜ νŠΉμ • ν•­λͺ© 탐색을 μΊμ‹±ν•˜μ—¬ O(N) 검색 병λͺ© μ΅œμ ν™”

**Learning:** Rμ—μ„œ 반볡문 λ‚΄λΆ€μ—μ„œ νŠΉμ • 쑰건을 λ§Œμ‘±ν•˜λŠ” λ°μ΄ν„°μ˜ μœ„μΉ˜λ₯Ό μ°ΎκΈ° μœ„ν•΄ `which()`λ₯Ό μ—¬λŸ¬ 번 반볡 ν˜ΈμΆœν•˜λŠ” 것은 O(N) μ‹œκ°„ λ³΅μž‘λ„λ₯Ό κ°€μ Έ 맀번 λΆˆν•„μš”ν•œ λ°°μ—΄ μŠ€μΊ”μ„ μœ λ°œν•©λ‹ˆλ‹€. μ΄λŠ” λ£¨ν”„μ˜ 반볡 νšŸμˆ˜κ°€ 많고, 탐색해야할 데이터가 클 수둝 μ„±λŠ₯ μ €ν•˜μ˜ μ£Ό 원인이 λ©λ‹ˆλ‹€.
**Action:** 쑰건에 λ§žλŠ” 인덱슀λ₯Ό 졜초 탐색 μ‹œ λ³€μˆ˜μ— 캐싱(`newIdx`, `oldIdx` λ“±)ν•˜μ—¬ μ €μž₯ν•˜κ³  이후 λ™μΌν•œ 데이터 μ ‘κ·Ό μ‹œ μΊμ‹±λœ 인덱슀λ₯Ό μ‚¬μš©ν•¨μœΌλ‘œμ¨ O(1) μˆ˜μ€€μœΌλ‘œ μ„±λŠ₯을 ν–₯μƒμ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€. μΆ”κ°€λ‘œ 슀칼라 값에 λŒ€ν•œ λΆˆν•„μš”ν•œ `paste0()` ν•¨μˆ˜ ν˜ΈμΆœμ„ μ œκ±°ν•˜μ—¬ μ˜€λ²„ν—€λ“œλ₯Ό μ€„μž…λ‹ˆλ‹€.

## 2024-07-08 - R μ–Έμ–΄μ—μ„œ 루프 λ‚΄ 인덱슀 검색(which) O(N) 병λͺ© μ΅œμ ν™”

**Learning:** Rμ—μ„œ 반볡문 λ‚΄λΆ€μ—μ„œ νŠΉμ • 쑰건을 λ§Œμ‘±ν•˜λŠ” λ°μ΄ν„°μ˜ μœ„μΉ˜λ₯Ό μ°ΎκΈ° μœ„ν•΄ `which()`λ₯Ό μ—¬λŸ¬ 번 ν˜ΈμΆœν•˜λ©΄ 맀번 O(N)의 μ„ ν˜• 탐색(linear scan)이 λ°œμƒν•˜μ—¬ 데이터 크기가 클수둝 μ„±λŠ₯이 크게 μ €ν•˜λ©λ‹ˆλ‹€. λ˜ν•œ `paste0()`λ₯Ό μ΄μš©ν•œ λΆˆν•„μš”ν•œ λ°°μ—΄ λ‹¨μœ„ λ¬Έμžμ—΄ 생성은 반볡문 μ˜€λ²„ν—€λ“œλ₯Ό κ°€μ€‘μ‹œν‚΅λ‹ˆλ‹€.
**Action:** 쑰건에 λ§žλŠ” 인덱슀λ₯Ό 졜초 ν•œ 번 `split(seq_len(nrow(df)), df$column)`λ₯Ό 톡해 리슀트 ν˜•νƒœλ‘œ 캐싱(dictionary lookup)ν•˜μ—¬ 루프 μ™ΈλΆ€μ—μ„œ O(1) 검색 μ²΄κ³„λ‘œ λ§Œλ“€κ³ , 슀칼라 값에 λŒ€ν•œ λΆˆν•„μš”ν•œ `paste0()` ν•¨μˆ˜ ν˜ΈμΆœμ„ μ΅œμ ν™”(`paste(..., collapse=' ')`)ν•˜μ—¬ μ˜€λ²„ν—€λ“œλ₯Ό μ€„μž…λ‹ˆλ‹€.

## 2026-07-11 - R μ–Έμ–΄μ—μ„œ 루프 λ‚΄ 벑터 동적 ν™•μž₯ 및 쑰건뢀 탐색 μ΅œμ ν™”

**Learning:** Rμ—μ„œ for 루프 내에 λ™μ μœΌλ‘œ 벑터 크기λ₯Ό λŠ˜λ¦¬λ©΄μ„œ (`vector[i] <- value`) 쑰건을 κ²€μ‚¬ν•˜λŠ” 것은 O(N^2)의 볡사 μ˜€λ²„ν—€λ“œ(copy-on-modify)λ₯Ό λ°œμƒμ‹œν‚€λ©° λ§€ λ°˜λ³΅λ§ˆλ‹€ `match()` μŠ€μΊ”μ„ μˆ˜ν–‰ν•˜λ©΄ μ„±λŠ₯ μ €ν•˜λ₯Ό μ΄ˆλž˜ν•©λ‹ˆλ‹€.
**Action:** 루프 외뢀에 λ²‘ν„°ν™”λœ `match()`λ₯Ό ν•œ 번만 μˆ˜ν–‰ν•˜μ—¬ μœ νš¨ν•œ 인덱슀λ₯Ό μ°Ύκ³ , 벑터 인덱싱(`vector[idx]`)으둜 ν•œ λ²ˆμ— 데이터λ₯Ό μΆ”μΆœν•˜μ—¬ λΆˆν•„μš”ν•œ 루프 μ˜€λ²„ν—€λ“œ 및 동적 λ©”λͺ¨λ¦¬ μž¬ν• λ‹Ήμ„ λ°©μ§€ν•˜μ—¬ O(1) μˆ˜μ€€μœΌλ‘œ μ„±λŠ₯을 κ°œμ„ ν•΄μ•Ό ν•©λ‹ˆλ‹€.

## 2024-07-12 - R μ–Έμ–΄μ—μ„œ λ°μ΄ν„°ν”„λ ˆμž„ μ„œλΈŒμ…‹νŒ… μ‹œ λΆˆν•„μš”ν•œ which() 및 반볡 평가 제거

**Learning:** 데이터 ν”„λ ˆμž„μ˜ νŠΉμ • 둜우(row)λ₯Ό λ³€κ²½ν•  λ•Œ `df[which(df$col == "val"), ]`와 같이 `which()`λ₯Ό μ‚¬μš©ν•˜λ©΄ λ‚΄λΆ€μ μœΌλ‘œ μΆ”κ°€ ν•¨μˆ˜ 호좜 및 논리 벑터 평가 μ˜€λ²„ν—€λ“œκ°€ λ°œμƒν•©λ‹ˆλ‹€. λ˜ν•œ, μ—¬λŸ¬ 값을 μ—…λ°μ΄νŠΈν•˜κΈ° μœ„ν•΄ λ™μΌν•œ 쑰건식을 μ—°μ†μœΌλ‘œ μ‚¬μš©ν•˜λ©΄ 맀번 λ™μΌν•œ O(N) 논리 벑터 평가가 μ€‘λ³΅ν•΄μ„œ μΌμ–΄λ‚©λ‹ˆλ‹€. λΆˆν•„μš”ν•œ `paste0("GROUP")` ν˜ΈμΆœλ„ μ˜€λ²„ν—€λ“œλ₯Ό λ”ν•©λ‹ˆλ‹€.
**Action:** `which()`λ₯Ό μƒλž΅ν•˜κ³  직접 논리 인덱싱(`df$col == "val"`)을 μ‚¬μš©ν•˜λ©°, λ™μΌν•œ 쑰건식을 두 번 이상 μ—°μ†μœΌλ‘œ μ‚¬μš©ν•  경우 ν•΄λ‹Ή 논리 벑터λ₯Ό λ³€μˆ˜μ— 캐싱(`idx <- df$col == "val"`)ν•˜μ—¬ μ—¬λŸ¬ 번 μž¬μ‚¬μš©ν•¨μœΌλ‘œμ¨ μ€‘λ³΅λœ O(N) μ„ ν˜• μŠ€μΊ”μ„ ν”Όν•˜κ³  μ„±λŠ₯을 μ΅œμ ν™”ν•΄μ•Ό ν•©λ‹ˆλ‹€. λ˜ν•œ λΆˆν•„μš”ν•œ λ¬Έμžμ—΄ 연산을 μ œκ±°ν•©λ‹ˆλ‹€.

## 2025-02-12 - R μ–Έμ–΄μ—μ„œ 반볡적인 mirt λͺ¨λΈ 생성 μ‹œ λΆˆν•„μš”ν•œ λ°μ΄ν„°ν”„λ ˆμž„ λΆ€λΆ„μ§‘ν•© μΆ”μΆœ μ΅œμ ν™”

**Learning:** Rμ—μ„œ λ°μ΄ν„°ν”„λ ˆμž„μ˜ νŠΉμ • 열을 μΆ”μΆœν•˜λŠ” μž‘μ—…(`df[cols]`)은 O(N)의 λ©”λͺ¨λ¦¬ 볡사λ₯Ό μˆ˜λ°˜ν•©λ‹ˆλ‹€. `autoFIPC`μ—μ„œ `mirt` λͺ¨λΈμ˜ νŒŒλΌλ―Έν„°λ₯Ό μ„€μ •ν•˜κ±°λ‚˜ ν˜ΈμΆœν•˜λŠ” κ³Όμ • 쀑에 `newformXDataK[colnames(newFormModel@Data$data)]` μ½”λ“œκ°€ λ°˜λ³΅ν•΄μ„œ μ‚¬μš©λ˜μ—ˆκ³ , 심지어 `ncol()`을 μœ„ν•΄ λ‹¨μˆœνžˆ 개수λ₯Ό ꡬ할 λ•Œλ„ μ‚¬μš©λ˜μ–΄ λΆˆν•„μš”ν•œ λ©”λͺ¨λ¦¬ ν• λ‹Ήκ³Ό μ˜€λ²„ν—€λ“œλ₯Ό μ΄ˆλž˜ν–ˆμŠ΅λ‹ˆλ‹€.
**Action:** μ‘°κ±΄λ¬Έμ΄λ‚˜ 반볡문 λ‚΄λΆ€μ—μ„œ λΆˆν•„μš”ν•˜κ²Œ λ°μ΄ν„°ν”„λ ˆμž„ λΆ€λΆ„μ§‘ν•© 연산이 λ°˜λ³΅λ˜μ§€ μ•Šλ„λ‘ μ™ΈλΆ€μ—μ„œ ν•œ 번만 `linkedFormData <- newformXDataK[colnames(newFormModel@Data$data)]`둜 캐싱(caching)ν•œ λ’€, `ncol(linkedFormData)`와 `data = linkedFormData` ν˜•νƒœλ‘œ μž¬μ‚¬μš©ν•˜μ—¬ λ©”λͺ¨λ¦¬ 볡사와 O(N) μ˜€λ²„ν—€λ“œλ₯Ό λ°©μ§€ν•΄μ•Ό ν•©λ‹ˆλ‹€.
4 changes: 4 additions & 0 deletions .jules/palette.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
# Palette Journal

## 2024-06-24 - Pure R Backend Package

**Learning:** The aFIPC repository is a pure R backend package without any frontend web components or UI. Therefore, standard micro-UX enhancements such as ARIA labels, loading states, and CSS styling cannot be applied.
**Action:** Stop and do not create a PR, as no suitable web UX enhancements can be identified.

## 2026-06-30 - No Frontend Surface

**Learning:** The package contains R calibration code and package metadata, not HTML, React, CSS, or other UI surfaces.
**Action:** Palette tasks should stop after recording that no UX enhancement applies unless a future frontend artifact is introduced.
9 changes: 9 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
# Sentinel Journal

## 2024-07-12 - Fix missing parameter validations

**Vulnerability:** Unvalidated inputs passed to `if()` statements can cause process crashes (`condition has length > 1`) or unexpected coercion vulnerabilities.
**Learning:** In R, optional boolean parameters that default to `NULL` should be validated using explicit runtime type validation (e.g., `if (!is.null(flag) && (!is.logical(flag) || length(flag) != 1 || is.na(flag)))`).
**Prevention:** Always implement explicit runtime type validation for optional boolean parameters.

## 2024-07-13 - Fix integer overflow coercion vulnerability in readline input

**Vulnerability:** Interactive `readline()` user inputs that are loosely validated against unbounded digit classes (e.g., `^[0-9]+$`) can evaluate to `NA` when passed to `as.integer()` if the string represents an excessively large number, which leads to integer overflow coercion vulnerabilities and possible process crashes.
**Learning:** For R scripts, any validation on interactive `readline()` numeric inputs should strictly match against exact expected values (e.g., `^[12]$`) rather than unbounded digit classes.
**Prevention:** Strictly validate `readline()` inputs against expected discrete values instead of using unbounded numerical regular expressions.
1 change: 1 addition & 0 deletions .markdownlint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"MD013": false}
6 changes: 3 additions & 3 deletions R/aFIPC.R
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ autoFIPC <-
}
for (attempt in seq_len(3)) {
n <- readline(prompt = "Is it correct? (1: Yes 2: No) : ")
if (grepl("^[0-9]+$", n)) {
if (grepl("^[12]$", n)) {
return(as.integer(n))
}
}
Expand Down Expand Up @@ -171,7 +171,7 @@ autoFIPC <-
readline(
prompt = "Do you want to use default BILOG-MG priors for oldform Data? (1: Yes 2: No) : "
)
if (grepl("^[0-9]+$", n)) {
if (grepl("^[12]$", n)) {
return(as.integer(n))
}
}
Expand Down Expand Up @@ -390,7 +390,7 @@ autoFIPC <-
readline(
prompt = "Do you want to use default BILOG-MG priors for newform Data? (1: Yes 2: No) : "
)
if (grepl("^[0-9]+$", n)) {
if (grepl("^[12]$", n)) {
return(as.integer(n))
}
}
Expand Down
Loading