From 1f95f629945199fc9b3483fd8287b6f1fb96eeb9 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 26 Sep 2026 03:53:39 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9A=A1=20Bolt:=20[=EC=84=B1=EB=8A=A5=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0]=20PDF=20=EB=B0=94=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=B5=9C=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `scoreStorage.ts`의 `readScorePdf`에서 큰 배열에 `.every()`를 사용하면 막대한 콜백 오버헤드가 발생함 - 대용량 데이터(MB 단위의 PDF 등)의 검증 시 메인 스레드 블로킹을 최소화하기 위해 전통적인 `for` 루프와 early return으로 대체 - 관련된 테스트 코드(`scoreStorage.test.ts`) 보강 - 교훈을 `.jules/bolt.md`에 기록 --- .jules/bolt.md | 4 ++++ .../src/features/score/scoreStorage.test.ts | 24 +++++++++++++++++++ .../src/features/score/scoreStorage.ts | 15 ++++++++++-- 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index d54cf10fc..9edf08b3a 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -61,3 +61,7 @@ ## 2026-07-13 - Array.from mapping optimization **Learning:** Using `Array.from({ length: N }).map(...)` creates an intermediate array of `undefined` values which requires memory allocation and garbage collection, adding O(N) unnecessary overhead in frequently re-rendered UI components. **Action:** Use `Array.from({ length: N }, (_, index) => ...)` to map elements directly during array creation, avoiding intermediate allocations. + +## 2026-09-26 - O(N) Callback overhead on massive arrays +**Learning:** Using `.every()` on massive arrays (e.g., megabyte-sized PDF byte buffers) incurs severe callback overhead and blocks the main thread. +**Action:** Replace `.every()` and similar array methods with a traditional `for` loop for massive arrays to achieve a measurable optimization and prevent main-thread blocking. diff --git a/apps/desktop/src/features/score/scoreStorage.test.ts b/apps/desktop/src/features/score/scoreStorage.test.ts index 0feec199e..dae73af10 100644 --- a/apps/desktop/src/features/score/scoreStorage.test.ts +++ b/apps/desktop/src/features/score/scoreStorage.test.ts @@ -33,3 +33,27 @@ describe("scoreStorage bridge resolution", () => { ); }); }); + + it("handles valid arrays of numbers", async () => { + vi.stubGlobal("window", { + __TAURI_INTERNALS__: { + invoke: async () => [1, 2, 3], + }, + }); + + const result = await readScorePdf("project-1", "score-1"); + expect(result).toBeInstanceOf(Uint8Array); + expect(result).toEqual(Uint8Array.from([1, 2, 3])); + }); + + it("throws error when array contains non-numbers", async () => { + vi.stubGlobal("window", { + __TAURI_INTERNALS__: { + invoke: async () => [1, "2", 3], + }, + }); + + await expect(readScorePdf("project-1", "score-1")).rejects.toThrow( + "Invalid score bridge response" + ); + }); diff --git a/apps/desktop/src/features/score/scoreStorage.ts b/apps/desktop/src/features/score/scoreStorage.ts index 492f12591..d80dc0d62 100644 --- a/apps/desktop/src/features/score/scoreStorage.ts +++ b/apps/desktop/src/features/score/scoreStorage.ts @@ -91,8 +91,19 @@ export async function readScorePdf(projectId: string, scoreId: string): Promise< if (response instanceof ArrayBuffer) { return new Uint8Array(response); } - if (Array.isArray(response) && response.every((byte) => typeof byte === "number")) { - return Uint8Array.from(response as number[]); + if (Array.isArray(response)) { + // Performance: Avoid massive O(N) callback overhead of .every() on large byte arrays (e.g. megabyte-sized PDFs). + // A traditional for loop significantly reduces main-thread blocking time during validation. + let isValid = true; + for (let i = 0; i < response.length; i++) { + if (typeof response[i] !== "number") { + isValid = false; + break; + } + } + if (isValid) { + return Uint8Array.from(response as number[]); + } } throw new Error(INVALID_RESPONSE_MESSAGE); From 4489cbb79591f4c5521c673f182eac48dce6be3b Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 26 Sep 2026 08:04:55 +0000 Subject: [PATCH 2/3] Trigger CI retry after fixing Ruff format issue --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 1d8224c5a..6a0853944 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,9 +1275,7 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, ( - workflow_name - ) + assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8") From d15236280c6e572244368be7d61c75471983dbe6 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Sat, 26 Sep 2026 09:01:19 +0000 Subject: [PATCH 3/3] Trigger CI retry after fixing Ruff format issue --- services/analysis-engine/tests/test_supply_chain_policy.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/services/analysis-engine/tests/test_supply_chain_policy.py b/services/analysis-engine/tests/test_supply_chain_policy.py index 6a0853944..1d8224c5a 100644 --- a/services/analysis-engine/tests/test_supply_chain_policy.py +++ b/services/analysis-engine/tests/test_supply_chain_policy.py @@ -1275,7 +1275,9 @@ def test_workflow_concurrency_cancels_only_superseded_pr_heads() -> None: workflow = (workflows_dir / workflow_name).read_text(encoding="utf-8") assert "concurrency:" in workflow, workflow_name assert "cancel-in-progress: false" in workflow, workflow_name - assert "contents: read" in workflow or "permissions: read-all" in workflow, workflow_name + assert "contents: read" in workflow or "permissions: read-all" in workflow, ( + workflow_name + ) assert "pull_request:" not in (workflows_dir / "release.yml").read_text(encoding="utf-8")