7.5 KiB
issue, issue_title
| issue | issue_title |
|---|---|
| 290 | Reduce stripJsonComments complexity in config-loader.ts |
Retro: #290 — Reduce stripJsonComments complexity in config-loader.ts
Stage: Planning (2026-05-31T15:14:25Z)
Session summary
Produced a numbered implementation plan to lower stripJsonComments cognitive complexity (31 → < 15) by replacing the five-flag single-loop scanner with a stateless dispatcher delegating to three private consume helpers (consumeLineComment, consumeBlockComment, consumeString), each returning a ScanSegment value.
The plan is behavior-preserving, adds direct unit tests for the already-exported stripJsonComments, and is structured as three TDD commits (test: pin contract → refactor: dispatcher → docs: architecture update).
Observations
- Chose the issue's consume-helper option over the mode-discriminant step-function option: a
step(state, char)function would mutate a shared state bag (output-argument smell) and re-encode the same five flags, so it relocates rather than removes the interleaving. Each consume helper returns a value and owns one JSONC sub-grammar — genuine decomposition per thecode-designheuristics. Did not invokeask_user— the choice is resolvable by project design principles and the change is small and reversible. stripJsonCommentsisexported and consumed by bothconfig-loader.ts(loadUnifiedConfig) andpolicy-loader.ts, but had no dedicated unit test — Step 1 pins its full contract directly before the refactor, so the new tests pass against today's implementation and act as the behavior-preservation net.- No exports change and no symbol is renamed, so no
index.tsbarrel, package skill, or other doc needs updating — onlydocs/architecture/architecture.md(Phase 2 Step 5, findings row 5, worst-CRAP-risk line, metrics). design-reviewskill judged not applicable: the change is one self-contained pure function with no shared-interface or layer-wiring impact.- Block-comment scan is planned to switch from a character loop to
indexOf("*/")(behavior-identical, including the unterminated-to-EOF branch) — flagged as a risk with a dedicated test. - markdownlint is not installed locally (
markdownlint-cli2not found; no.markdownlint*config); relied on themarkdown-conventionsskill.rumdl fmtran in the pre-commit hook and passed.
Stage: Implementation — TDD (2026-05-31T15:23:14Z)
Session summary
Completed all 3 TDD steps: pinned 14 direct unit tests for stripJsonComments (Step 1), replaced the five-flag scanner with the stateless dispatcher + three consume helpers (Step 2), and updated docs/architecture/architecture.md to mark Phase 2 Step 5 complete (Step 3).
Test count: 1614 → 1628 (+14).
A style: cleanup commit was added after the pre-completion review to fix helper ordering.
Observations
- Step 1 required two assertion corrections: (1) the space before
//is emitted verbatim, so the expected output was'{ \n"k": 1}'not'{\n"k": 1}'; (2) the combined JSONC round-trip test had a stray,after a stripped block comment rendering the output invalid JSON — restructured the document so comments are inline on value lines. Both caught before the step 1 commit; the pre-existing implementation was never at fault. - ESLint auto-fixed bracket notation to dot notation (
parsed["debugLog"]→parsed.debugLog) during the pre-commit hook; accepted the change. - The
refactor:commit placed the three consume helpers beforestripJsonComments, inverting the stepdown rule (plan said "placed directly belowstripJsonComments"). The pre-completion reviewer flagged this as WARN; fixed in astyle:commit (4ff870a1) after the review. fallow health --targetsconfirmedconfig-loader.ts/stripJsonCommentsno longer appears as a refactoring target after the refactor; architecture doc updated accordingly (targets 4 → 3).- Pre-completion reviewer: WARN (one finding — stepdown order, resolved before final commit). All deterministic checks PASS.
Stage: Final Retrospective (2026-05-31T15:39:45Z)
Session summary
Shipped issue #290 across three stages (plan → TDD → ship) with no logic rework: a behavior-preserving decomposition of stripJsonComments into a stateless dispatcher plus three pure consume helpers, with 14 new unit tests pinning the contract (1614 → 1628).
CI passed first try; no release-please PR (all commits were test:/refactor:/style:/docs:).
The single follow-up was a style: commit (4ff870a1) fixing helper ordering, prompted by the pre-completion reviewer's one WARN.
Observations
What went well
- The plan's design reasoning held up end-to-end.
The consume-helper approach (chosen over the mode-discriminant alternative) was behavior-preserving as predicted, and
fallow health --targetsconfirmedconfig-loader.tsdropped off the refactoring-target list (4 → 3) exactly as the plan's Open Question anticipated. - Verification ran incrementally, not end-loaded.
Green baseline (
check/lint/test) before any TDD cycle, each cycle ran the affected file red-then-green, and the full suite pluscheck/lint/fallow dead-coderan after the last step. - The two Step 1 test-assertion bugs were caught during the red phase, before the commit — the space-before-
//preservation and the stray-comma invalid-JSON case were both fixed without touching committed code or the production implementation.
What caused friction (agent side)
instruction-violation(reviewer-caught) — therefactor:commit (483be378) placed the three consume helpers abovestripJsonComments, inverting the stepdown rule. The plan explicitly prescribed "placed directly belowstripJsonCommentsper the stepdown rule," so the implementation had a written instruction and did not follow it. Impact: one follow-upstyle:commit (4ff870a1), no logic rework, no user intervention. This is the second consecutive issue with the identical friction: #289 fixed the same private-helper-before-export ordering instyle:commit55d2774a, also reviewer-caught. Both sessions wrote the extracted helper above its caller (a "define before use" instinct that JS/TS hoisting makes unnecessary) and relied on the pre-completion reviewer to catch the stepdown inversion.
What caused friction (user side)
- None.
The design choice was resolvable from
code-designprinciples, so noask_userwas warranted; the user's only involvement was launching each stage.
Diagnostic details
- Model-performance correlation — the only subagent dispatch was the
pre-completion-reviewer(pinned toanthropic/claude-sonnet-4-6, a valid registry alias); a judgment-heavy review on an appropriate model, no mismatch. - Feedback-loop gap analysis — verification was incremental throughout; no end-loaded-verification flag.
- Escalation-delay and unused-tool lenses found nothing notable: the two Step 1 assertion bugs were each fixed in one edit, no sequence exceeded five tool calls on the same error, and no rabbit-holes or missing-context gaps arose that a subagent or
colgrepwould have prevented.
Changes made
- Appended this Final Retrospective stage entry to
packages/pi-permission-system/docs/retro/0290-decompose-strip-json-comments.md. - Added a one-line note to the Stepdown rule in
.pi/skills/code-design/SKILL.md: extracted helpers go below their caller, not above (hoisting makes "define before use" unnecessary). This closes the recurring stepdown-order friction caught by the pre-completion reviewer in both #289 (55d2774a) and #290 (4ff870a1).