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
4 changes: 4 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,7 @@
^\.jules(/.*)?$
^\.trivyignore\.yaml$
^trivy\.yaml$
^\.semgrepignore$
^test_dummy\.R$
^test_validation\.R$
^\.markdownlint\.json$
13 changes: 13 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
# Bolt Learnings

## 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 Learnings

## 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.
33 changes: 30 additions & 3 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
# Sentinel Learnings

## 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.

**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-08-05 - Fix integer overflow coercion vulnerability in readline() inputs

**Vulnerability:** Interactive prompts using `readline()` validated numeric
inputs with an unbounded digit class regex (`^[0-9]+$`). This could allow
excessively large numeric strings (e.g. "999999999999999") to pass the check,
but then evaluate to `NA` when passed to `as.integer()`. This can cause
subsequent process crashes or unexpected logical paths.

**Learning:** In R, unbounded numeric strings do not automatically translate to
valid integers due to maximum integer limits (`.Machine$integer.max`). When
using `as.integer()` on validated strings, the regex must bound the length or,
preferably, match exactly the expected values to prevent coercion to `NA`.

**Prevention:** Strictly match against exact expected values (e.g., `^[12]$`)
instead of unbounded digit classes (`^[0-9]+$`) when validating `readline()`
inputs that are expected to be specific integer options.
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