claudeos-core 2.2.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +1664 -907
  2. package/CONTRIBUTING.md +92 -92
  3. package/README.de.md +28 -0
  4. package/README.es.md +28 -0
  5. package/README.fr.md +28 -0
  6. package/README.hi.md +28 -0
  7. package/README.ja.md +28 -0
  8. package/README.ko.md +1014 -986
  9. package/README.md +1016 -987
  10. package/README.ru.md +28 -0
  11. package/README.vi.md +1015 -987
  12. package/README.zh-CN.md +28 -0
  13. package/bin/cli.js +152 -148
  14. package/bin/commands/init.js +1673 -1554
  15. package/bin/commands/lint.js +62 -0
  16. package/bin/commands/memory.js +438 -438
  17. package/bin/lib/cli-utils.js +206 -206
  18. package/claude-md-validator/index.js +184 -0
  19. package/claude-md-validator/reporter.js +66 -0
  20. package/claude-md-validator/structural-checks.js +528 -0
  21. package/content-validator/index.js +666 -441
  22. package/lib/expected-guides.js +23 -23
  23. package/lib/expected-outputs.js +90 -90
  24. package/lib/language-config.js +35 -35
  25. package/lib/memory-scaffold.js +1058 -1054
  26. package/lib/plan-parser.js +165 -165
  27. package/lib/staged-rules.js +118 -118
  28. package/manifest-generator/index.js +174 -174
  29. package/package.json +90 -87
  30. package/pass-json-validator/index.js +337 -337
  31. package/pass-prompts/templates/common/claude-md-scaffold.md +52 -10
  32. package/pass-prompts/templates/common/pass3-footer.md +402 -224
  33. package/pass-prompts/templates/common/pass3b-core-header.md +43 -0
  34. package/pass-prompts/templates/common/pass4.md +375 -305
  35. package/pass-prompts/templates/common/staging-override.md +26 -26
  36. package/pass-prompts/templates/node-vite/pass1.md +117 -117
  37. package/pass-prompts/templates/node-vite/pass2.md +78 -78
  38. package/pass-prompts/templates/python-flask/pass1.md +119 -119
  39. package/pass-prompts/templates/python-flask/pass2.md +85 -85
  40. package/plan-installer/domain-grouper.js +76 -76
  41. package/plan-installer/index.js +137 -137
  42. package/plan-installer/prompt-generator.js +188 -145
  43. package/plan-installer/scanners/scan-frontend.js +505 -473
  44. package/plan-installer/scanners/scan-java.js +226 -226
  45. package/plan-installer/scanners/scan-node.js +57 -57
  46. package/plan-installer/scanners/scan-python.js +85 -85
  47. package/plan-installer/stack-detector.js +482 -482
  48. package/plan-installer/structure-scanner.js +65 -65
  49. package/sync-checker/index.js +177 -177
package/CHANGELOG.md CHANGED
@@ -1,908 +1,1665 @@
1
- # Changelog
2
-
3
- ## [2.2.0] — 2026-04-21
4
-
5
- Adds deterministic CLAUDE.md structure. Generated `CLAUDE.md` files now follow
6
- an 8-section scaffold with fixed titles and order, driven by `pass-prompts/
7
- templates/common/claude-md-scaffold.md`. Content within each section still
8
- adapts to the project, but the structural skeleton no longer drifts between
9
- projects or runs.
10
-
11
- ### Added
12
-
13
- - **`pass-prompts/templates/common/claude-md-scaffold.md`** (new, ~630 lines).
14
- Single source of truth for CLAUDE.md structure. Defines the 8 sections
15
- (Role Definition / Project Overview / Build & Run Commands / Core
16
- Architecture / Directory Structure / Standard · Rules · Skills
17
- Reference / DO NOT Read / Common Rules & Memory (L4); titles are
18
- emitted in the project's output language), per-section generation
19
- rules, dynamic substitution variables (`{PROJECT_NAME}`,
20
- `{OUTPUT_LANG}`, `{PROJECT_CONTEXT}`), and a post-generation validation
21
- checklist. Section 8 has TWO required sub-sections: a Common Rules
22
- sub-section (meta-summary table of `paths: ["**/*"]` universal rules)
23
- and an L4 Memory sub-section (memory file table + workflow). All 12 stack-specific Pass 3 prompts
24
- now delegate CLAUDE.md structure to this scaffold and supply only
25
- stack-specific hints (2-4 lines each).
26
-
27
- - **`lib/env-parser.js`** (new). Parses `.env*` files into structured
28
- `{port, host, apiTarget, vars, source}` used by stack-detector. Search
29
- order prefers `.env.example` (committed, canonical) over local `.env`
30
- variants. Port detection recognizes 16+ convention variable names across
31
- Vite, Next.js, Nuxt, Angular, Node, and Python frameworks. Exposes
32
- utilities (`parseEnvContent`, `extractPort`, `extractHost`,
33
- `extractApiTarget`, `readStackEnvInfo`) plus a sensitive-variable
34
- filter (`isSensitiveVarName`, `redactSensitiveVars`) that redacts
35
- values of PASSWORD/SECRET/TOKEN/API_KEY/CREDENTIAL/PRIVATE_KEY-style
36
- variables to a `***REDACTED***` sentinel before the vars map reaches
37
- any downstream consumer. DATABASE_URL is whitelisted for
38
- stack-detector back-compat. 39 unit tests in `tests/env-parser.test.js`
39
- (30 core + 9 redaction).
40
-
41
- ### Changed
42
-
43
- - **All 12 Pass 3 prompts** (`angular/`, `java-spring/`, `kotlin-spring/`,
44
- `node-express/`, `node-fastify/`, `node-nestjs/`, `node-nextjs/`,
45
- `node-vite/`, `python-django/`, `python-fastapi/`, `python-flask/`,
46
- `vue-nuxt/`). Two separate changes per file:
47
- 1. The previous 5-bullet CLAUDE.md generation block (`- Role definition`,
48
- `- Build & Run Commands`, `- Core architecture diagram`, `- {stack item}`,
49
- `- Standard/Skills/Guide reference table`) is replaced by a scaffold
50
- reference plus stack-specific hints. The `CRITICAL CLAUDE.md Reference
51
- Table Completeness` warning above the block is also removed (the
52
- scaffold's validation checklist supersedes it).
53
- 2. The `40.infra/*` `paths` frontmatter spec is split per-file. Previously
54
- all three infra rules (environment-config, logging-monitoring, cicd-
55
- deployment) received the same category-level `paths` value, which caused
56
- the logging-monitoring rule to never auto-load on source code edits
57
- (its `paths` only matched `.env`, `*.config.*`, `*.json`, `*.yml`,
58
- `Dockerfile*` none of which are source files). Per-file paths now
59
- match each rule's actual guardrail target: environment-config env/
60
- config files, logging-monitoring source code extensions (`.ts`/`.tsx`/
61
- `.py`/`.java`/`.kt` per stack), cicd-deployment CI YAML + source.
62
-
63
- - **`pass-prompts/templates/common/pass3-footer.md`** — five new `CRITICAL`
64
- blocks added:
65
- - **`00.standard-reference.md` Composition**: scopes the mechanical
66
- standards index strictly. REQUIRES a forward reference to
67
- `claudeos-core/standard/00.core/04.doc-writing-guide.md` (generated
68
- by Pass 4 but indexed at Pass 3 time to prevent a gap between passes).
69
- FORBIDS a redundant "DO NOT Read / context waste" section inside
70
- `00.standard-reference.md` that information belongs solely in
71
- CLAUDE.md Section 7, which is both more complete (includes project-
72
- specific build-output and external-module paths) and not reloaded
73
- on every edit. The 6 stacks (java-spring, kotlin-spring, node-express,
74
- node-nextjs, python-django, python-fastapi) whose Pass 3 prompts
75
- previously hardcoded a `## DO NOT Read` block in the reference file
76
- have been cleaned up.
77
- - **`.env` Is the Source of Truth for Runtime Configuration**: when
78
- `pass2-merged.json` contains `stack.envInfo`, ports/hosts/API targets
79
- declared in the project's `.env.example` MUST be used over framework
80
- defaults. Affects Section 2's table, Section 3's inline run-command
81
- comments, and any rule referencing port values (e.g., CORS origins
82
- in auth rules).
83
- - **Rule `paths` Must Match Rule Content**: enforces that each rule's
84
- `paths` frontmatter matches the file types its guardrails actually
85
- target. Explicitly prohibits copying `paths` across sibling rule files
86
- in the same category, and tells the LLM to re-verify "when should
87
- Claude Code auto-load this rule?" as the criterion for paths. Added
88
- because a category-level paths shortcut in earlier Pass 3 prompts
89
- caused the logging-monitoring rule to never match source code edits.
90
- - **CLAUDE.md Scaffold Compliance**: enforces the 8-section structure at
91
- generation time. Explicitly forbids adding sections with titles like
92
- "Required to Observe While Working", "Rules Summary", "Documentation
93
- Writing Rules", "AI Common Rules", "L4 Memory Integration Rules",
94
- "Common Rules", or any title whose category meaning is "rules"
95
- beyond the 8 fixed section names (the same blocklist is applied in
96
- every output language, matching on the translated equivalents).
97
- Adds a mandatory post-generation check (count `^## ` headings; must
98
- equal 8; merge surplus into the correct section or move to `rules/*`
99
- / `standard/*`). The expanded blocklist closes a rename loophole
100
- discovered during dogfooding on a Vite + React frontend project
101
- where the LLM appended a §9 whose title combined "Documentation
102
- Writing + AI Common Rules + Memory Layer (L4)" to collect
103
- rule-related content.
104
- - **CLAUDE.md Does Not Duplicate Rules**: clarifies that CLAUDE.md
105
- describes structure, not enforcement. Lists four categories of content
106
- that do NOT belong in CLAUDE.md (coding rules, domain-specific rules,
107
- multi-file sync rules, work procedures) and points each to its proper
108
- home in rules/standard/skills/guide.
109
-
110
- - **`pass-prompts/templates/common/claude-md-scaffold.md`** (in addition to
111
- the new-file Add above) was tightened after initial dogfooding:
112
- - Hard constraints section now leads with **"EXACTLY 8 SECTIONS. No more,
113
- no less."** plus a recovery procedure for surplus sections.
114
- - Section 6 Rules sub-section explicitly notes that the
115
- `.claude/rules/00.core/*` wildcard row already COVERS
116
- `51.doc-writing-rules.md` and `52.ai-work-rules.md` — eliminating the
117
- perceived need to create a separate section enumerating those rules.
118
- - Validation checks section lists common surplus section patterns with
119
- target destinations so the LLM can act rather than just detect.
120
-
121
- - **`plan-installer/prompt-generator.js`** embeds the scaffold inline
122
- into `pass3-prompt.md` at generation time. The 12 stack-specific Pass 3
123
- templates and `pass3-footer.md` both reference
124
- `pass-prompts/templates/common/claude-md-scaffold.md` by path, but that
125
- path is relative to the claudeos-core package, not the user project.
126
- The generator now reads the scaffold and inserts it between the Phase 1
127
- fact-table block and the stack-specific body, wrapped in explicit
128
- `# === EMBEDDED: claude-md-scaffold.md ===` markers so the LLM can locate
129
- it. Without this embed the scaffold references would point to a file
130
- Claude Code cannot resolve at runtime. Load is optional (`existsSafe`)
131
- so a missing scaffold does not crash generation the rest of the
132
- prompt is still produced, just without the deterministic structure
133
- enforcement.
134
-
135
- - **`plan-installer/stack-detector.js`** now calls `readStackEnvInfo`
136
- before returning and attaches the result as `stack.envInfo` on
137
- project-analysis.json. When the project's `.env.example` (or fallback
138
- `.env`) declares a port AND no earlier detector won (Spring Boot
139
- application.yml still takes precedence), the parsed port is promoted
140
- to `stack.port`. This closes a long-standing gap where Vite projects
141
- that customized their dev port via `.env` (e.g., `VITE_DESKTOP_PORT=3000`)
142
- received the framework-default 5173 in CLAUDE.md.
143
- Host and API target values are also captured for downstream use.
144
-
145
- - **`plan-installer/index.js`** port resolution precedence documented
146
- in code comments. The existing `defaultPort` fallback chain (Vite 5173,
147
- Next.js 3000, Django 8000, etc.) is now explicitly labeled "last resort"
148
- and runs only when neither stack-detector's direct detection (Spring
149
- application.yml) nor the env-parser populated `stack.port`.
150
-
151
- - **`pass-prompts/templates/common/claude-md-scaffold.md`** Section 2
152
- (Project Overview) and Section 3 (Build & Run Commands) rules now
153
- reference `stack.envInfo` as authoritative for port/host/API-target
154
- values. Section 2 requires env-annotated rows in the project overview
155
- table when the project declares them (e.g., `| Dev Server Port | 3000
156
- (VITE_DESKTOP_PORT) |`), and Section 3 requires inline port comments
157
- next to run commands to match the env-declared value. Framework defaults
158
- are explicitly labeled "last resort" in both rules.
159
-
160
- ### Why this matters
161
-
162
- When claudeos-core was applied to three sibling projects in the same
163
- organization (one Spring Boot backend, two Vite + React frontends), the
164
- generated files were content-correct standards, rules, and skills
165
- accurately captured each project's patterns but the `CLAUDE.md` files
166
- had different section counts (8, 8, 9), different section names, and
167
- different section orders. Claude Code reads CLAUDE.md first on every
168
- session; inconsistent structure across repos made it harder for
169
- developers (and Claude Code) to know where to look for a given piece of
170
- information. v2.2.0 fixes the structure while leaving content
171
- project-specific.
172
-
173
- The removed "Required to Observe While Working" section was a symptom
174
- of the same problem: different projects put different rules there, most
175
- of which duplicated
176
- content already in `.claude/rules/*` (auto-loaded) or `claudeos-core/
177
- standard/*` (detailed patterns). Removing it eliminates a redundant
178
- maintenance surface and reinforces the "one rule, one home" principle.
179
-
180
- Dogfooding also uncovered a latent paths bug. The `40.infra/*` rules
181
- shared a single category-level `paths` frontmatter that only matched
182
- config/infra file extensions (`.env`, `*.config.*`, `*.json`, `*.yml`,
183
- `Dockerfile*`). This meant the logging-monitoring rule — whose guardrails
184
- cover `console.log` misuse, PII in logs, and `catch {}` swallowing —
185
- never auto-loaded when editing `.ts`/`.tsx`/`.py`/`.java` files, i.e.,
186
- exactly when it was needed. The rule body was correct; its activation
187
- trigger was mis-scoped. v2.2.0 now specifies per-file `paths` in the Pass
188
- 3 prompts and adds a `Rule paths Must Match Rule Content` CRITICAL block
189
- to the footer so future rules cannot inherit the wrong scope by default.
190
-
191
- A third dogfooding finding exposed a different layer of the same
192
- philosophy violation. The stack detector parsed Spring Boot's
193
- `application.yml` for `server.port`, but for Node/Vite projects it
194
- simply used a hardcoded framework default (Vite → 5173) whenever no
195
- Spring-style config was found even when the project declared its
196
- actual port in `.env.example` (e.g., `VITE_DESKTOP_PORT=3000`). This
197
- meant CLAUDE.md's §2 table and §3 run-command
198
- comments showed the Vite theoretical default instead of what the project
199
- actually runs. The root cause was structural: the detector had no
200
- `.env` parser beyond a DATABASE_URL check for DB identification. v2.2.0
201
- introduces `lib/env-parser.js` with convention-aware port/host/API-target
202
- extraction, and the scaffold and footer now treat `.env.example` as the
203
- canonical source of runtime configuration — framework defaults are
204
- last-resort only. This also captures host and API-target values that
205
- previously never appeared in generated CLAUDE.md at all.
206
-
207
- A fourth dogfooding iteration on a Spring Boot backend project
208
- (regenerated with the interim v2.2.0 scaffold that only allowed a single
209
- Section 8 titled "Memory (L4)") found the LLM producing a §9 titled
210
- "Common Rules & Memory (L4)" even with the expanded blocklist from
211
- the earlier frontend-project fix.
212
- The §9 contained both (a) a meta-summary table of `paths: ["**/*"]`
213
- rules (51.doc-writing-rules + 52.ai-work-rules) and (b) a restated L4
214
- memory table labeled "L4 Memory Files (Re-declaration)". Close
215
- inspection showed (a) was genuinely useful content the scaffold had no
216
- legitimate home for a developer-facing summary of which rules
217
- auto-load on every edit, complementary to Section 6's directory index.
218
- The LLM kept inventing §9 because the information it wanted to convey
219
- was real. v2.2.0 resolves this by promoting Section 8 to "Common Rules
220
- & Memory (L4)" with two required sub-sections: one for common rules
221
- auto-loaded on every edit (meta-summary only, not rule bodies) and one
222
- for L4 memory referenced on-demand. This acknowledges that "which rules
223
- auto-load universally" is a legitimate meta-information category that
224
- deserves a visible home, while keeping the always-8-sections contract
225
- intact. The duplicate §9 "re-declaration" anti-pattern is now
226
- explicitly named and forbidden in both the scaffold
227
- and the footer.
228
-
229
- Finally, the same backend-project inspection also surfaced two smaller
230
- but real bugs in `00.standard-reference.md` generation. First, 6 of the
231
- 12 Pass 3 stack prompts hardcoded a `## DO NOT Read (context waste)`
232
- section at the bottom of the reference file — a shadow of CLAUDE.md
233
- Section 7 that was less complete (missed project-specific paths like
234
- `build/` or external modules) and lived at the wrong layer: `00.standard-
235
- reference.md` reloads on every edit via `paths: ["**/*"]`, while
236
- Section 7 loads once per session. Second, `claudeos-core/standard/00.
237
- core/04.doc-writing-guide.md` is generated by Pass 4 (Required output
238
- #12) but never appeared in the Pass 3-generated reference index, creating
239
- a gap the moment Pass 4 ran. v2.2.0 adds a `00.standard-reference.md
240
- Composition` CRITICAL block to the footer that codifies: (a) always
241
- include the Pass 4 forward reference, (b) never include a DO NOT Read
242
- section (Section 7 is the single source of truth), (c) keep the per-
243
- edit payload minimal (paths only, no descriptions descriptions live
244
- in Section 6 which is session-time budget). The 6 inline hardcoded
245
- DO NOT Read blocks have been removed from the stack prompts and
246
- replaced with explicit inline notes pointing to the footer rule.
247
-
248
- Three additional risks surfaced during pre-release cross-checking
249
- and were addressed in the same release cycle. **First**, the scaffold's
250
- "Section 6 Rules: Always include 60.memory/*" directive, added during
251
- Section 8 redesign, was not echoed in the 12 stack Pass 3 prompts'
252
- rule-category listings so the LLM received conflicting signals
253
- (scaffold says include, stack prompt doesn't mention it). Real dogfooding
254
- on the backend project confirmed the category was being omitted from
255
- the generated CLAUDE.md §6 Rules table. v2.2.0 fixes both sides: each stack
256
- Pass 3 prompt now explicitly lists `60.memory/*` as a forward-reference
257
- rule category (generated by Pass 4, but indexed at Pass 3 time), and the
258
- scaffold's Sub-section 2 guidance is strengthened with an example row
259
- and a "mandatory do NOT omit" note. **Second**, the existing Migration
260
- guidance mentioned `--force` but did not explain why `npx claudeos-core
261
- init` (without `--force`) silently fails to adopt v2.2.0 improvements on
262
- upgrades. Under Rule B idempotency, existing generated files are skipped
263
- as "already exists", meaning users running plain `init` on a v2.1.x
264
- project see no visible change. v2.2.0 adds (a) a dedicated "upgrade
265
- detected" warning in bin/commands/init.js that fires when a pre-v2.2.0
266
- CLAUDE.md is detected before the resume/fresh prompt, and (b) an expanded
267
- Migration section in CHANGELOG that makes the `--force` requirement and
268
- preservation semantics (memory/ content kept, generated files replaced)
269
- explicit. **Third**, the new `.env.example` CLAUDE.md pipeline created
270
- a theoretical pathway for accidentally committed secrets in `.env.example`
271
- to be amplified into the project's public-facing documentation. Although
272
- `.env.example` is conventionally a placeholder file, real-world projects
273
- occasionally check in real values by mistake. v2.2.0 adds a
274
- sensitive-variable filter (`lib/env-parser.js`: `isSensitiveVarName`,
275
- `redactSensitiveVars`) that replaces values of variables matching
276
- PASSWORD/SECRET/TOKEN/API_KEY/CREDENTIAL/PRIVATE_KEY patterns with a
277
- `***REDACTED***` sentinel before the vars map reaches any downstream
278
- consumer. Port/host/API-target extraction uses a whitelist of
279
- config-relevant keys and is unaffected. The scaffold also gains an
280
- explicit SECURITY directive forbidding reference to sensitive variables
281
- in CLAUDE.md as defense-in-depth. `DATABASE_URL` remains unredacted
282
- because stack-detector's DB identification path has depended on it since
283
- v1.xchanging that would be a breaking change.
284
-
285
- ### Migration
286
-
287
- Existing projects keep working. The prompt-generator change affects only
288
- how `pass3-prompt.md` is assembled on the next `init` or `refresh` run —
289
- installed standards, rules, skills, memory, and CLAUDE.md in existing
290
- projects are not touched until the user regenerates.
291
-
292
- **⚠️ Important: `--force` is REQUIRED to adopt v2.2.0 improvements.**
293
-
294
- claudeos-core's Pass 3 runs under Rule B (idempotency): if a target file
295
- already exists on disk, it is skipped during regeneration. This is
296
- designed to protect hand-edited content from being overwritten, but it
297
- means **a plain `npx claudeos-core init` on an existing v2.1.x project
298
- will NOT apply v2.2.0 improvements** because the old files (CLAUDE.md,
299
- `00.standard-reference.md`, `40.infra/*-rules.md`, memory rules, etc.)
300
- will all be skipped as "already exists".
301
-
302
- To actually adopt v2.2.0's improvements (8-section CLAUDE.md, per-file
303
- `40.infra/*` paths, `.env.example`-based port accuracy, Section 8
304
- redesign, forward-referenced `04.doc-writing-guide.md`, `60.memory/*`
305
- row), regenerate via:
306
-
307
- ```
308
- npx claudeos-core init --force
309
- ```
310
-
311
- `--force` overwrites existing generated files while leaving untouched:
312
- - Your source code
313
- - `claudeos-core/memory/` content (decision-log, failure-patterns entries
314
- you've accumulated)these are append-only and preserved
315
- - Any non-generated files under the project root
316
-
317
- If you want to preview changes first, regenerate into a scratch copy of
318
- the project, diff the resulting files against your current ones, and
319
- then decide whether to `--force` on the real project. Key files to
320
- diff: `CLAUDE.md`, `.claude/rules/00.core/00.standard-reference.md`,
321
- `.claude/rules/40.infra/02.logging-monitoring-rules.md` (paths change
322
- is the most visible delta).
323
-
324
- No manual edits are required after `--force`; the scaffold handles
325
- everything. Hand-edited content in `claudeos-core/standard/**` that
326
- you want preserved should be committed to version control before
327
- running `--force` so you can diff/merge any overwrites.
328
-
329
- ### Notes
330
-
331
- - 39 new tests added in `tests/env-parser.test.js` (30 core + 9 sensitive-
332
- variable redaction). All tests continue to pass: **563 pre-existing + 39
333
- new = 602 total**.
334
- - No file-format breaking changes. Existing `claudeos-core/standard/`,
335
- `.claude/rules/`, and `claudeos-core/skills/` content in installed
336
- projects is unaffected only the CLAUDE.md generated at the project
337
- root changes shape on regeneration. The `40.infra/*` rule `paths`
338
- values will update on next regeneration, which changes when those
339
- rules auto-load (more accurately scoped); the rule content itself
340
- does not change. `stack.envInfo` is a new additive field — older
341
- project-analysis.json files without it still work.
342
- - Discovered via dogfooding on three real production projects:
343
- - Structural drift (3 different CLAUDE.md layouts) prompted the scaffold.
344
- - A Vite + React frontend project produced a §9 surplus section under
345
- a renamed title that bypassed the initial forbidden-sections blocklist
346
- fixed by expanding the blocklist and adding the mandatory
347
- post-generation §-count check.
348
- - The `40.infra/*` paths mismatch surfaced when inspecting a generated
349
- `02.logging-monitoring-rules.md` and confirming via grep that its
350
- guardrails (source-code-level: PII logging, silent swallow, console
351
- use) could never auto-load given the file's own paths frontmatter
352
- (config-only).
353
- - The Vite port mismatch (5173 in CLAUDE.md when `.env.example`
354
- declared 3000) exposed the absence of any `.env` parsing in
355
- stack-detector beyond DATABASE_URL prompted the new
356
- `lib/env-parser.js` utility and the `.env Is the Source of Truth`
357
- CRITICAL footer block.
358
- - A second Spring Boot backend regeneration against the interim
359
- scaffold produced §9 "Common Rules & Memory (L4)" despite the
360
- expanded blocklist, because the LLM's desired content (a
361
- meta-summary of `paths: ["**/*"]` universal rules, complementary to
362
- Section 6's directory index) had no legitimate home in the original
363
- 8-section design. Resolved by redesigning Section 8 into two
364
- sub-sections a Common Rules sub-section for the universal-rules
365
- meta-summary and an L4 Memory sub-section for the memory
366
- table/workflow. The "L4 Memory Files (Re-declaration)" anti-pattern
367
- (duplicate memory table inside a second section) is now explicitly
368
- named and forbidden.
369
- - Inspection of the same backend-project output showed a generated
370
- `00.standard-reference.md` carrying a hardcoded `## DO NOT Read
371
- (context waste)` section (a partial duplicate of CLAUDE.md Section 7)
372
- and missing `00.core/04.doc-writing-guide.md` (created later by
373
- Pass 4). Fixed in the 6 affected Pass 3 stack prompts and formalized
374
- as the `00.standard-reference.md Composition` CRITICAL block so
375
- future stacks cannot reintroduce either defect.
376
- - Pre-release cross-check found the scaffold's `60.memory/*` "Always
377
- include" directive was not mirrored in any of the 12 stack Pass 3
378
- prompts' rule-category listings, causing the backend project's
379
- CLAUDE.md §6 Rules table to omit `60.memory/*` entirely. Fixed by adding the
380
- forward-reference row to all 12 stack prompts and strengthening the
381
- scaffold's Sub-section 2 guidance with an example row and "mandatory"
382
- wording.
383
- - Pre-release cross-check flagged that a plain `npx claudeos-core init`
384
- on an existing v2.1.x project would silently skip v2.2.0 improvements
385
- under Rule B idempotency. Added a CLAUDE.md marker-based detection
386
- in `bin/commands/init.js` that warns about the `--force` requirement
387
- before the resume/fresh prompt, plus an expanded Migration section
388
- covering preservation semantics and preview workflow.
389
- - Pre-release cross-check identified that values in `.env.example`
390
- flow through to CLAUDE.md, creating a leak pathway for accidentally
391
- committed secrets. Added sensitive-variable redaction in
392
- `lib/env-parser.js` (PASSWORD/SECRET/TOKEN/API_KEY/CREDENTIAL/
393
- PRIVATE_KEY patterns replaced with `***REDACTED***` sentinel) plus
394
- a SECURITY directive in the scaffold as defense-in-depth.
395
-
396
- ---
397
-
398
- ## [2.1.2] 2026-04-21
399
-
400
- Post-release regression fix for v2.1.0 master plan removal cleanup.
401
-
402
- ### Fixed
403
-
404
- - **`content-validator`: `plan/` directory no longer required.** On fresh
405
- v2.1.0+ projects `npx claudeos-core health` always failed because
406
- `content-validator/index.js` pushed a `MISSING: plan directory not found`
407
- error when `claudeos-core/plan/` was absent. Master plan generation was
408
- explicitly removed in v2.1.0 `plan-validator` (v2.1.0 `Fixed`) and
409
- `manifest-generator` (v2.1.0 `Fixed`) were both updated to tolerate a
410
- missing `plan/` directory, but `content-validator` was missed during
411
- that cleanup. It now silently skips the plan/ check when the directory
412
- is absent (with an informational `plan/ not present (expected post-v2.1.0)`
413
- log line), matching the contract established by the other validators.
414
- The directory contents are still validated when present (legacy projects
415
- or user-authored plan files are unaffected).
416
-
417
- ### Notes
418
-
419
- - All 563 existing tests continue to pass. No new tests added — the fix
420
- is a one-line behavior change (`errors.push(...)` `console.log(...)`)
421
- with a comment documenting the v2.1.0 context, and regression risk is
422
- covered by routine `health` runs rather than an integration test.
423
- - Discovered via dogfooding on a real Vite 6 + React 19 project: 62
424
- generated files, all Pass 1–4 stages succeeded, but `health` failed
425
- at content-validator. No other cleanup gaps found.
426
-
427
- ---
428
-
429
- ## [2.1.1] — 2026-04-20
430
-
431
- Docs-only maintenance release. No runtime behavior or API changes.
432
-
433
- ### Changed
434
-
435
- - **README: dropped `What's New in v2.1.0` section** from all 10 language
436
- READMEs (`README.md`, `README.ko.md`, `README.ja.md`, `README.zh-CN.md`,
437
- `README.es.md`, `README.vi.md`, `README.hi.md`, `README.ru.md`,
438
- `README.fr.md`, `README.de.md`). Post-release cleanup the section's
439
- job is done once the release ships, and the same content is preserved
440
- in `CHANGELOG.md` for anyone who wants the historical detail.
441
-
442
- - **README: dropped the `Real production case: 18-domain admin frontend
443
- (2026-04-20)` subsection** under _Auto-scaling by Project Size_ across
444
- all 10 language READMEs. The per-stage breakdown table (9 rows) and its
445
- surrounding prose are removed. The trailing empirical reference in the
446
- FAQ "What is Pass 3 split mode" answer (the `Empirically verified up
447
- to 18 domains × 101 files × 102 minutes …` sentence with its now-dead
448
- link) is also removed so no orphan reference remains.
449
-
450
- ### Notes
451
-
452
- - Each README drops ~33 lines; total net change across translations is
453
- ~330 lines removed. No code, tests, prompts, or generated artifacts
454
- are touched `npm pack` contents are identical to v2.1.0 apart from
455
- the README files and `package.json`/`package-lock.json` version bump.
456
-
457
- ---
458
-
459
- ## [2.1.0] 2026-04-20
460
-
461
- This release addresses the primary cause of `Prompt is too long` failures in
462
- Pass 3 on large multi-module projects. The fix is structural: Pass 3 is
463
- re-architected into multiple sequential `claude -p` calls with fresh context
464
- each, so output-accumulation overflow is no longer possible regardless of
465
- project size.
466
-
467
- ### Added
468
-
469
- - **Phase 1 "Read Once, Extract Facts" prompt block** (always on). A new
470
- common block `pass-prompts/templates/common/pass3-phase1.md` is prepended
471
- to every generated `pass3-prompt.md`. It instructs Claude to read
472
- `pass2-merged.json` exactly once into a compact in-context fact table and
473
- reference that table for all subsequent file generation. The block defines
474
- five rules:
475
- - **Rule A** — Reference the fact table, don't re-read pass2-merged.json.
476
- - **Rule B** — Idempotent file writing (skip if target exists with real
477
- content), making Pass 3 safely re-runnable after interruption.
478
- - **Rule C** — Cross-file consistency enforced via the fact table as
479
- single source of truth.
480
- - **Rule D** Output conciseness: one line (`[WRITE]`/`[SKIP]`) between
481
- file writes, no restating the fact table, no echoing file content.
482
- Addresses output-accumulation overflow where verbose narration between
483
- 30-50 files adds 15-30K tokens of pure accumulation.
484
- - **Rule E** Batch idempotent check: one `Glob` at PHASE 2 start
485
- instead of per-target `Read` calls.
486
-
487
- - **`pass3-context.json` slim summary builder** (always on). A new file
488
- `claudeos-core/generated/pass3-context.json` is built after Pass 2 from
489
- `project-analysis.json` plus `pass2-merged.json` signals (size, top-level
490
- keys). Stays under 5 KB even for large projects vs. `pass2-merged.json`
491
- which can exceed 500 KB. Pass 3 prompts reference this as the preferred
492
- entry point, falling back to `pass2-merged.json` only for specific
493
- details (response wrapper method, util class FQN, MyBatis mapper path).
494
- Emits a warning when `pass2-merged.json` exceeds 300 KB.
495
-
496
- - **Batch sub-division for large projects** (automatic, ≥16 domains).
497
- Stages 3b and 3c are sub-divided into batches of 15 domains each,
498
- preceded by dedicated `3b-core` / `3c-core` stages that handle
499
- project-wide common files. Ensures no single stage generates more than
500
- ~50 files, keeping output within the empirical safe range on projects
501
- up to 100+ domains. Batch count is `ceil(totalDomains / 15)`; domain
502
- order comes from `domain-groups.json` (size-balanced by plan-installer).
503
-
504
- - **Split-mode partial marker protection**. `pass3-complete.json` gains
505
- `mode: "split"` and `groupsCompleted` array. A run that completes 3a+3b
506
- and crashes during 3c leaves a partial marker; on re-run, the stale-check
507
- detects the partial-marker shape and defers to the split runner's resume
508
- logic instead of deleting the marker otherwise the run would restart
509
- from 3a and double the token cost.
510
-
511
- - **7 regression tests** pinning the master plan no-op contract
512
- (`tests/master-plan-removal.test.js`).
513
-
514
- - **`scaffoldSkillsManifest` gap-fill for Pass 4**. Auto-creates
515
- `claudeos-core/skills/00.shared/MANIFEST.md` with a minimal stub if
516
- Pass 3c omits it (the stack pass3.md templates list it among targets but
517
- without REQUIRED marking, so skill-sparse projects sometimes ended up
518
- with `.claude/rules/50.sync/03.skills-sync.md` pointing at a
519
- non-existent file). Idempotent: skips if the file already has real
520
- content (>20 chars).
521
-
522
- ### Changed
523
-
524
- - **Pass 3 now always runs in split mode.** Each stage starts with a fresh
525
- context window; cross-stage consistency is preserved by `pass3a-facts.md`.
526
- No user-facing configuration applies to every `npx claudeos-core init`
527
- run automatically.
528
-
529
- Stage structure:
530
- - **3a** Read analysis files once, write `pass3a-facts.md` (5-10 KB
531
- distilled fact sheet).
532
- - **3b**Generate `CLAUDE.md`, `standard/`, and `.claude/rules/`.
533
- Sub-divided into `3b-core` + `3b-1..N` on projects with ≥16 domains.
534
- - **3c** Generate `skills/` and `guide/`. Sub-divided into `3c-core`
535
- + `3c-1..N` on projects with ≥16 domains.
536
- - **3d-aux** Generate `database/` + `mcp-guide/` stubs.
537
-
538
- Single-batch projects keep flat `"3b"`/`"3c"` marker names
539
- (backward-compatible); multi-batch projects use `"3b-core"`, `"3b-1"`,
540
- etc. Resume works at stage granularity.
541
-
542
- - **Stage count by project size** (1–15 domains: 4 stages; 16–30: 8; 31–45:
543
- 10; 46–60: 12; 61–75: 14; 91–105: 18).
544
-
545
- - `package-lock.json` synced to `2.1.0`. The v2.0.2 release had a stale
546
- lockfile at `2.0.0` which caused `npm ci` to fail lockfile integrity
547
- checks.
548
-
549
- ### Removed
550
-
551
- - **Master plan generation** (`claudeos-core/plan/*-master.md` files).
552
- Master plans were an internal tool backup not consumed by Claude Code
553
- at runtime, and aggregating many files in a single Pass 3d session was
554
- a primary source of `Prompt is too long` failures on mid-sized projects.
555
- Claude Code runtime is unaffected — it reads `CLAUDE.md` + `rules/`
556
- directly. Use `git` for backup/restore instead.
557
-
558
- - **Pass 3d sub-stages `3d-standard` / `3d-rules` / `3d-skills` /
559
- `3d-guide`**. Only `3d-aux` (database + mcp-guide stubs) remains as a
560
- fixed-size task independent of domain count.
561
-
562
- - **`CLAUDEOS_PASS3_SPLIT` environment variable and single-call mode.**
563
- Single-call had failed reliably on projects with more than ~5 domains
564
- because output-accumulation overflow is not predictable from input size.
565
- Split mode is structurally immune and is now the only supported path.
566
-
567
- - **`claudeos-core/plan/` directory creation in `init`**. Directory is no
568
- longer created during bootstrap (honors the master-plan-removal contract).
569
-
570
- ### Deprecated
571
-
572
- - `scaffoldMasterPlans` in `lib/memory-scaffold.js` is kept as a
573
- backward-compatible no-op (returns `[]`, writes nothing). External
574
- callers keep working; no files are produced.
575
-
576
- ### Fixed
577
-
578
- - `bootstrap.sh` line endings normalized from CRLF to LF. v2.0.2 shipped
579
- with CRLF which caused immediate `syntax error` on macOS/Linux when
580
- invoked via `bash claudeos-core-tools/bootstrap.sh`.
581
-
582
- - `pass3-context-builder.js`: removed unused `p2Size` placeholder variable
583
- (refactoring leftover, no behavior change).
584
-
585
- - `init.js`: Pass 4 progress ticker `totalExpected` corrected from 6 to 5
586
- to reflect master plan removal. The 6th slot was counting
587
- `plan/50.memory-master.md` which is no longer generated, making the
588
- progress bar appear stuck at 83% until the run completed.
589
-
590
- - `manifest-generator`: removed stale `plan-manifest.json` generation.
591
- Master plans were removed in v2.1.0; a manifest with an empty `plans`
592
- array (62 B) was noise. Nothing reads it, nothing validates it.
593
- `sync-map.json` is retained (with empty `mappings`) for
594
- `sync-checker` backward compatibility.
595
-
596
- - `plan-validator`: `plan-sync-status.json` is now skipped when the
597
- `plan/` directory is absent or empty. Previously wrote a 147 B
598
- all-zeros status file on every health check for master-plan-free
599
- projects. `stale-report.json` still records a passing no-op so
600
- `health-checker` reports a clean result.
601
-
602
- - `plan-parser` placeholder filtering regression in sync-checker
603
- on projects with `<...>` style tokens in plan files.
604
-
605
- - `cli.js`: `npx claudeos-core memory --help` now displays the memory
606
- subcommand help instead of the top-level usage. `parseArgs` previously
607
- promoted `--help` to a top-level command even when it appeared after a
608
- command name, so `memory --help` was indistinguishable from `--help`
609
- alone. The fix: `--help` only becomes the top-level command when no
610
- other command has been seen yet. `memory --help`, `memory -h` now
611
- route to the subcommand's own help.
612
-
613
- - `memory score`: the first `score` run no longer leaves two `importance`
614
- lines in each entry. The previous implementation inserted the
615
- auto-scored line at the top but left the user's original
616
- `- importance: N` line below it, producing a file with two conflicting
617
- values per entry. `cmdScore` now strips every importance line
618
- (bold or plain) before inserting the new auto-scored line, so there is
619
- always exactly one importance line per entry and repeated `score` runs
620
- remain idempotent.
621
-
622
- - `memory compact`: the Stage 1 summary marker is now a proper markdown
623
- list item (`- _Summarized on YYYY-MM-DDoriginal body dropped._`).
624
- Previously the marker was emitted as a bare italic string without the
625
- `- ` prefix, which broke the surrounding list in markdown renderers
626
- and caused `parseEntries` to misclassify it on subsequent compactions.
627
- The default `fixLine` fallback was also updated to `- (fix omitted)`
628
- for the same consistency reason.
629
-
630
- ### Test coverage
631
-
632
- - 563 tests pass, 0 skip (3 runs confirmed no flakes). +165 tests vs v2.0.0
633
- across Pass 3 context builder, output accumulation, batch subdivision,
634
- master plan removal, scaffoldSkillsManifest, and memory score/compact
635
- formatting regression suites.
636
-
637
- ## [2.0.2] — 2026-04-20
638
-
639
- ### Fixed
640
-
641
- - **Upgrade-path silent-skip regression for pre-v2.0.0 projects** — `npx claudeos-core health` permanently reported `content-validator: fail` with 9 × MISSING guide errors on projects that had been initialized on a pre-v2.0.0 release and then upgraded. Pass 3 wrote `pass3-complete.json` before the Pass 3 output-completeness guards (H1/H2) existed, so the marker was valid-looking on disk even though `claudeos-core/guide/` (and sometimes `standard/00.core/01.project-overview.md`, `skills/`, or `plan/`) had never been populated. On subsequent runs, `init.js` observed the marker + an existing CLAUDE.md, skipped Pass 3, and never regenerated the missing outputs leaving the project in a stuck-fail state that only `--force` (which wipes `.claude/rules/` and loses manual edits) could recover. The Pass 3 stale-marker branch in `bin/commands/init.js` previously only detected externally-deleted CLAUDE.md; it now also drops the marker when any entry in `lib/expected-guides.js` is missing/BOM-aware-empty or when `findMissingOutputs()` (`lib/expected-outputs.js`) flags a missing standard sentinel / `skills/` / `plan/`. Mirrors the existing `dropStalePass4Marker` pattern (symmetric helper `dropStalePass3Marker` added) and reuses the same "unlink failure surfaces as `InitError` with Windows AV/file-lock guidance" contract so the recovery itself can't silently regress. Recovery is one-shot: next `init` re-runs Pass 3, which populates the missing outputs and writes a fresh marker gated by the v2.0.0 H1/H2 guards.
642
-
643
- ### Added
644
-
645
- - **`tests/pass3-marker.test.js`** Six new cases covering the stale-detection branches: (a) missing guide dir → drop, (b) single BOM-only guide file → drop, (c) guides present but `skills/` gone → drop, (d) guides present but standard sentinel missing → drop, (e) complete state preserves marker, (f) `init.js` source-parity tripwire asserting `dropStalePass3Marker` + both `EXPECTED_GUIDE_FILES` and `findMissingOutputs` references appear in the stale-check region (guards against refactors silently regressing to v2.0.1 behavior).
646
-
647
- ## [2.0.1] 2026-04-19
648
-
649
- ### Fixed
650
-
651
- - **CI tests failing on all OS/Node combinations** — `.gitignore` no longer excludes `package-lock.json`. The GitHub Actions workflow uses `actions/setup-node` with `cache: 'npm'` and `npm ci`, both of which require a committed lockfile; without it, all 6 matrix jobs (Ubuntu/macOS/Windows × Node 18/20) failed at the install step with `Dependencies lock file is not found`.
652
- - **`npm test` script not cross-platform** Changed `node --test tests/*.test.js` → `node --test` in `package.json`. The `*.test.js` glob was expanded by `sh` on Linux/macOS but left literal by `cmd.exe` on Windows runners, causing `Could not find 'D:\a\...\tests\*.test.js'` on all 3 Windows matrix jobs. The `node --test` built-in auto-discovery matches `**/*.test.{cjs,mjs,js}` from cwd (skipping `node_modules`), independent of shell globbing.
653
-
654
- ### Changed
655
-
656
- - **GitHub Actions runner compatibility** — Bumped `actions/checkout@v4` → `@v5` and `actions/setup-node@v4` → `@v5` in `.github/workflows/test.yml`. The `@v4` tags ran on Node.js 20, which GitHub deprecated on 2025-09-19 (forced Node 24 transition on 2026-06-02, full removal on 2026-09-16). The `@v5` tags ship with Node 24 support and clear the deprecation warnings.
657
-
658
- ## [2.0.0] 2026-04-19
659
-
660
- ### Added
661
-
662
- - **L4 Memory Layer** New `claudeos-core/memory/` directory with 4 persistent files:
663
- - `decision-log.md` "Why" behind design decisions (append-only, seeded from pass2-merged.json)
664
- - `failure-patterns.md` Recurring errors auto-scored by `npx claudeos-core memory score`
665
- - `compaction.md` 4-stage compaction strategy with project-specific error categories
666
- - `auto-rule-update.md` Rule improvement proposals from `npx claudeos-core memory propose-rules`
667
- - **L4 Memory rules** New `.claude/rules/60.memory/` directory with 4 rule files (`01.decision-log.md`, `02.failure-patterns.md`, `03.compaction.md`, `04.auto-rule-update.md`) instructing when/how to read and write memory files.
668
- - **L4 Common rules** New `.claude/rules/00.core/51.doc-writing-rules.md` and `.claude/rules/00.core/52.ai-work-rules.md` (frontmatter requirements, hallucination prevention patterns, memory vs rules distinction).
669
- - **AI Work Rules template hardening** — `.claude/rules/00.core/52.ai-work-rules.md` substantially expanded for stack/role/scenario coverage:
670
- - **New `## Safety & Security` section** (CRITICAL — overrides every other rule in the file): destructive commands (`rm -rf`, `git reset --hard`, `git push --force`, `DROP TABLE`, `npm publish`, migration `down`/`revert`, etc.) require explicit per-command user confirmation (re-confirmed each time, not blanket); secret files (`.env*`, `*.pem`, `*.key`, `id_rsa*`, credentials JSON) referenced by variable name only — never echoed/logged/committed.
671
- - **17 Hallucination Prevention patterns** (was 13 in v1.x; net +4 after removing 3 redundant). New patterns: hallucinated import (verify package manifest), wrong-version API (verify manifest **and** lockfile — `package-lock.json`/`pnpm-lock.yaml`/`yarn.lock`/`gradle.lockfile`/`poetry.lock`/`Pipfile.lock`/`uv.lock`), cross-config drift (env/config family glob across backend `.env*`/`application-*.yml`/`*settings.py` and frontend `environment*.ts`/`next.config.*`/`vite.config.*`/`nuxt.config.*`), server/client component boundary mixing (Next.js App Router `"use client"`, Nuxt server/client composables, Remix `loader`/`action` — N/A for pure SPA/backend), component prop hallucination (read the target's `interface Props`/`defineProps<>`/function signature first), hardcoded secrets (Grep regex `(api[_-]?key|token|password|secret)\s*=\s*["']\w+["']` before commit; use `process.env.X`/`os.getenv("X")`/`@Value("${X}")` instead), historical DB migration editing (Flyway `migrations/V*.sql`, Alembic `alembic/versions/*.py`, Rails `db/migrate/*.rb`, Prisma `prisma/migrations/*/migration.sql`, TypeORM `migrations/*.ts` — append-only once applied; verify with `flyway info`/`alembic history`/`prisma migrate status`).
672
- - **Backend/frontend balanced examples** throughout — `§ No Unsolicited Work` memory-dedup bullet now lists both backend (port numbers, pool sizes, handler names, transaction propagation modes) and frontend (dev server port, build output dir, env var prefixes `VITE_`/`NEXT_PUBLIC_`/`REACT_APP_`, route definitions, bundle size budgets); `§ Code/Document Generation Accuracy` framework-shape bullet covers backend (DTOs, entity field naming, repository method signatures) and frontend (component prop interfaces, store/state shapes for Pinia/Redux/Zustand, API response types, route param types, CSS module class names).
673
- - **3 internal contradictions resolved** with Exception clauses — `§1 Accuracy First` "always read directly" narrowed to "always read **critical facts** directly" with sub-agent delegation explicitly allowed for non-critical exploration; `§ No Unsolicited Work` "do not make unsolicited suggestions" gains *Exception: factual errors in this project's own docs (wrong paths, dead references, internally contradicting rules) MUST be reported even if not asked*; "do not directly read internal document directories" gains *Exception: read directly when the user explicitly asks or when debugging requires it*.
674
- - **`Project Architecture — Hands Off` section** consolidates the previous `3-Layer Design` + `Memory vs Rules` sections (11 bullets → 7) without losing the architectural defenses (cross-layer/same-layer duplication intentional, multi-rule reinforcement, `**/*` paths protection, minor wording differences not "inconsistency").
675
- - **Empty directory rule softened with marker convention** — intent markers (`.gitkeep`, `KEEP_EMPTY.md`, dir listed in CLAUDE.md as planned, or referenced by an active plan/standard/skills doc) required to qualify as "intentional"; otherwise the AI must ask before deleting. Prevents the previous absolute "all empty dirs are intentional" rule from masking genuine neglect.
676
- - **Planned reference rule softened** — `§ Planned References` "do not label as missing" gains *Exception: if a referenced path appears in 3+ documents and doesn't exist on disk, flag for human review* (parallel to the factual-error Exception above). Prevents typos from masquerading as planned references.
677
- - **`Established codebase conventions take precedence over textbook-ideal patterns`** rule added — modernization/refactoring/"current best practices" migration proposals require explicit user request (e.g., "modernize", "migrate to v3"); otherwise follow existing pattern even if a greenfield design would differ.
678
- - **Neighbor file pattern requirement** — before writing new code, read 2-3 neighboring files in the same directory for existing patterns (naming, error handling, logging, import order, return type idioms, test structure) and match them. Greenfield/textbook idioms come second to in-codebase consistency.
679
- - **`§ Hallucination Prevention` pattern 7 audience-agnostic** — "code examples in rules are essential" rationale changed from "vibe-coding workflows" (audience-dependent) to "AI-assisted code generation — reduces hallucination risk regardless of audience experience" (universal).
680
- - **§1 cleanup** — removed `Cross-check agent results against source documents` bullet (now a weaker restatement of the narrowed §1 #2 after Exception additions).
681
- - **16 regression tests** added to `tests/memory-scaffold.test.js` (21 → 37) pinning the structure (7 sections, 17 patterns, 1..17 numbering continuity), all required tokens (frontend state libs, env prefixes, lockfiles, migration patterns), Exception clauses, and removed-pattern guards to prevent silent reversion.
682
- - **Pass 4 pipeline stage** Generates L4 Memory scaffolding (memory files, 60.memory rules, doc-writing guide, CLAUDE.md append, master plan `50.memory-master.md`) from pass2-merged.json; Claude-driven with static fallback on failure.
683
- - **New CLI subcommand**:
684
- - `memory compact | score | propose-rules`
685
- - **Pass 3 completion marker** (`pass3-complete.json`) — Prevents regeneration of CLAUDE.md on subsequent `init` runs.
686
- - **Pass 4 completion marker** (`pass4-memory.json`) — Tracks memory scaffold completion; enables resume/skip behavior across init runs.
687
- - **Stale marker recovery** — Automatically detects and removes stale Pass 3/4 markers when underlying files (CLAUDE.md or memory/) are externally deleted.
688
- - **v1.7.x migration** Auto-backfills Pass 3 marker when upgrading from v1.7.x with existing CLAUDE.md to prevent overwrite.
689
- - **New verification coverage** content-validator section [9/9] checks memory scaffold integrity (file presence, entry structure, required fields with fence-aware parsing); pass-json-validator [5a] validates pass3-complete.json and [5b] validates pass4-memory.json.
690
- - **Master plan file** `plan/50.memory-master.md` aggregates all 4 memory files using `<file path="...">` blocks.
691
- - **New library module** — `lib/memory-scaffold.js` (1006 LOC) containing memory/rule/plan/CLAUDE.md scaffolding with built-in multi-language translation via Claude CLI and strict translation validation (length, headings, code fences, frontmatter, CLI-parsed keywords).
692
- - **Translation cache** — Scaffold translations are cached per-language in `claudeos-core/generated/.i18n-cache-<lang>.json` to avoid repeated Claude CLI calls on subsequent init runs.
693
- - **Confidence scoring rewrite** — `memory propose-rules` replaces v1 saturating formula (`min(1, freq/10 + imp/20)`) with sigmoid on weighted evidence plus anchor-match multiplier (unanchored patterns × 0.6, missing importance caps evidence at 6).
694
- - **Staged-rules workaround for `.claude/` sensitive-path block** — Pass 3 and Pass 4 now write rule files to `claudeos-core/generated/.staged-rules/**` instead of `.claude/rules/**`, because Claude Code's sensitive-path policy refuses direct `.claude/` writes from the `claude -p` subprocess (even with `--dangerously-skip-permissions`). The Node.js orchestrator (not subject to that policy) moves the staged tree into `.claude/rules/` after each pass via `lib/staged-rules.js`, with rename + copy-fallback for Windows cross-volume/overwrite edge cases.
695
- - **`pass-prompts/templates/common/staging-override.md`** — Prepended to Pass 3/4 prompts as an absolute write-target redirect directive (preserves subpaths, leaves prose references and frontmatter `paths:` globs untouched).
696
- - **Pass 3 silent-failure guards** Four post-generation guards prevent writing `pass3-complete.json` on a partial success. All guards run AFTER the staged-rules move, BEFORE the marker write:
697
- - **Guard 1 (partial move):** if any staged file failed to move into `.claude/rules/`, throw `InitError` with retry guidance — next `init` re-runs Pass 3 automatically.
698
- - **Guard 2 (zero rules):** if `.claude/rules/` is empty after the move, treat it as Claude having ignored the `staging-override.md` directive and throw, instructing the user to re-run with `--force`.
699
- - **Guard 3 (H2 — incomplete guide/):** reject when any of the 9 expected guide files (list in `lib/expected-guides.js`) is missing or empty. Uses BOM-aware emptiness check (`.replace(/^\uFEFF/, "").trim().length === 0`) because `String.prototype.trim` doesn't remove U+FEFF (not in Unicode White_Space) — a BOM-only file would otherwise silently pass.
700
- - **Guard 3 (H1 — incomplete output):** reject when (a) `claudeos-core/standard/00.core/01.project-overview.md` sentinel is missing/empty, OR (b) `claudeos-core/skills/` has zero non-empty `.md` files, OR (c) `claudeos-core/plan/` has zero non-empty `.md` files. List in `lib/expected-outputs.js`. `database/` and `mcp-guide/` intentionally excluded (content-validator treats them WARNING-level; stacks legitimately skip).
701
- - **Pass 2 resume validation (H3)** — On resume, `pass2-merged.json` is parsed and validated to have ≥5 top-level keys (mirrors `pass-json-validator`'s `INSUFFICIENT_KEYS` threshold) before Pass 2 is skipped. Skeleton `{}` or malformed JSON triggers file deletion + Pass 2 re-run instead of silently poisoning Pass 3's analysis input.
702
- - **Pass 4 marker content validation (M1)** — `isValidPass4Marker` helper validates JSON shape + `passNum === 4` + non-empty `memoryFiles` array in both stale-detection and post-Claude-run gate. Rejects malformed bodies like `{"error":"timeout"}` that Claude could emit on partial failure; previously existence-only check would accept garbage and silently skip Pass 4 forever.
703
- - **`dropStalePass4Marker` helper (M1)** — Pass 4 stale-marker unlink failures now surface as `InitError` with Windows file-lock guidance instead of being swallowed by `catch (_e) { /* ignore */ }`. Previously a locked file (AV scanner / editor holding the handle) would leave the stale marker in place, and the subsequent `fileExists(pass4Marker)` check would accept it → silent Pass 4 skip.
704
- - **Pass 3 stale-marker unlink strictness** — Symmetric with Pass 4 above: `pass3-complete.json` cleanup (when CLAUDE.md is externally deleted) now throws `InitError` on unlink failure instead of being swallowed. Closes the same silent-skip class for Pass 3.
705
- - **`CLAUDEOS_SKIP_TRANSLATION=1` env guard (M2)** — `lib/memory-scaffold.js` `translateIfNeeded()` short-circuits to throw with a clear lang-specific message when this env var is set, before any `claude -p` invocation. Intended as a test-only escape hatch so translation-dependent tests (e.g. `tests/lang-aware-fallback.test.js`) assert the "translation must throw" contract deterministically regardless of whether the `claude` CLI is authenticated in the test env. Strict `=== "1"` check (not truthy-coerce) to avoid surprise-triggering on common env conventions.
706
- - **Early fail-fast for env+lang incompatibility** — `init.js` detects `CLAUDEOS_SKIP_TRANSLATION=1` combined with `--lang ≠ en` at language-selection time and throws `InitError` immediately with remediation (`unset CLAUDEOS_SKIP_TRANSLATION` or `--lang en`). Previously this combination would let the pipeline proceed and crash mid-Pass-4 with a confusing "translation skipped" error deep in the scaffolding stack.
707
- - **CI workflow (M3)** — `.github/workflows/test.yml` runs `npm test` on `ubuntu-latest × windows-latest × macos-latest × Node 18/20` matrix with `CLAUDEOS_SKIP_TRANSLATION=1` set on the test step so translation tests pass without requiring `claude` CLI in the runner. Uses `npm ci` against the committed `package-lock.json`.
708
- - **New shared library modules** Single sources of truth for Pass 3 output expectations, preventing drift between enforcement and validation:
709
- - `lib/expected-guides.js` 9 guide file paths. Imported by `init.js` Guard 3 H2 and `content-validator/index.js` `[5/9]` (no more hardcoded duplicates).
710
- - `lib/expected-outputs.js` 3 additional Pass 3 outputs (standard sentinel, `skills/`, `plan/`) with `findMissingOutputs(projectRoot)` + `hasNonEmptyMdRecursive(dir)` helpers (BOM-aware). Imported by `init.js` Guard 3 H1.
711
- - **Async claude execution + progress ticker** `cli-utils.js` adds `runClaudePromptAsync` (spawn-based, non-blocking; lets a `setInterval` ticker run concurrently with the Claude subprocess) and `runClaudeCapture` (execSync wrapper that captures stdout, used by the translation engine in `memory-scaffold.js`). `init.js` adds `makePassTicker` with three display modes — elapsed-only, file-delta, and fixed-target (`N/M files (P%)`) — driving the per-pass `⏳`/`📝` progress line in TTY (`\r`-rewritten) and CI/piped (periodic newlines) environments.
712
- - **`--force` and "fresh" resume cleanup** — Now also wipes `claudeos-core/generated/.staged-rules/` (leftover from a prior crashed Pass 3/4 run) and `.claude/rules/` (so Guard 2's zero-rules detection can't false-negative on stale rules from a previous run); under `"fresh"` mode the `pass3-complete.json` and `pass4-memory.json` markers are also unlinked so both passes re-execute. Manual edits to `.claude/rules/` are lost — acceptable under the explicit `--force`/`fresh` choice.
713
- - **190+ new tests** (296 489) — New/expanded suites: `memory-scaffold.test.js`, `memory-command.test.js`, `pass4-prompt.test.js`, `pass3-marker.test.js`, `pass3-guards.test.js` (Guards 1/2 + Guard 3 H1/H2 with BOM coverage), `pass2-validation.test.js` (H3 structural check), `pass4-marker-validation.test.js` (M1 `isValidPass4Marker` + `dropStalePass4Marker` regression guards), `translation-skip-env.test.js` (M2 env guard + M3 CI workflow presence), `staged-rules.test.js`, `lang-aware-fallback.test.js` (sets `CLAUDEOS_SKIP_TRANSLATION=1` at module top to make translation-throw assertions deterministic), `placeholder-substitution.test.js`, plus expansions to existing suites.
714
- - **Progress bar with ETA** Pass 1/2/3/4 execution shows a progress bar with percentage, elapsed time, and ETA based on average step duration (carried over and extended from v1.7.0; Pass 4 added).
715
- - **Platform/tier-split frontend detection (framework-agnostic)** — `scan-frontend.js` now recognizes `src/{platform}/{subapp}/` layouts where `{platform}` is either a device/target-environment keyword (`desktop`, `pc`, `web`, `mobile`, `mc`, `mo`, `sp`, `tablet`, `tab`, `pwa`, `tv`, `ctv`, `ott`, `watch`, `wear`) or an access-tier keyword (`admin`, `cms`, `backoffice`, `back-office`, `portal`) — covers English names plus common Korean corporate abbreviations. The short `adm` abbreviation is deliberately excluded as too ambiguous in isolation; projects using `src/adm/` as an admin root should rename to `admin` or wait for the override-file mechanism planned for a future release. Emits one domain per (platform, subapp) pair named `{platform}-{subapp}`, with per-domain counts for `routes`/`components`/`layouts`/`hooks`. Runs as a shared pattern across **all** detected frontends (Angular, Next.js, React, Vue/Nuxt) — the glob uses a multi-extension filter (`{tsx,jsx,ts,js,vue}`) so Angular `.component.ts` files and Vue `.vue` files are captured alongside React `.tsx`. A minimum of 2 source files per subapp is required before a domain is emitted — single-file dirs under a platform root are almost always accidental and would otherwise produce noisy 1-file "domains" in the Pass 1 group plan. Subapp name is always read from the filesystem via `path.basename` at scan time — no project/brand identifiers are hardcoded. Structural dirs (`components`, `hooks`, `layouts`), FSD layers (`widgets`, `features`, `entities`), and framework router dirs (`app`, `pages`, `routes`, `views`, `screens`, `containers`, `modules`, `domains`) are skipped at the subapp level so deeper structures still reach their dedicated scanners. Ambiguous names like `store` are deliberately allowed because e-commerce projects legitimately use them as subapp names. **Behavior note:** the change is additive for projects whose `src/{platform}/{subapp}/` dirs were previously unreachable by the primary/FSD/components scanners — those projects now gain the new domains; projects whose content was already being captured by other scanners see no change (the skip list ensures `src/admin/pages/*`, `src/admin/components/*`, etc. still fall through to their existing scanners).
716
- - **Deep routes-file fallback (Fallback E, framework-agnostic)** Catches React Router file-routing projects (CRA/Vite + `react-router`) that don't match Next.js `page.tsx` or FSD layouts. When all primary scanners and Fallback A–D return 0, globs `**/routes/*.{tsx,jsx,ts,js,vue}` and groups by the parent-of-`routes` directory name. Also runs across all frontends (Angular/Next/React/Vue), not gated to any single framework. Generic parent names (`src`, `app`, `pages`) are filtered so the fallback emits meaningful feature/subapp names rather than framework-convention placeholders.
717
- - **Shared scanner ignore lists** — `BUILD_IGNORE_DIRS` (node_modules, build, dist, out, .next, .nuxt, .svelte-kit, .angular, .turbo, .cache, .parcel-cache, coverage, storybook-static, .vercel, .netlify) and `TEST_FILE_IGNORE` (spec/test/stories/e2e/cy + `__snapshots__`/`__tests__` dirs) extracted as module-level constants. Both the platform scan and Fallback E consume these so build outputs and test fixtures don't inflate per-domain file counts or create spurious Fallback E hits.
718
- - **Monorepo platform split** — Platform scan now matches three layouts: `src/{platform}/{subapp}/` (standalone), `{apps,packages}/*/src/{platform}/{subapp}/` (Turborepo/pnpm workspace with `src/`), and `{apps,packages}/{platform}/{subapp}/` (workspaces without a `src/` wrapper). Platform segment is located via `parts.findIndex` on the keyword list, so paths like `src/pc/admin/` correctly split into `pc` (platform) + `admin` (subapp) without mistaking the subapp name for another platform keyword.
719
- - **Windows path glob fix across all scanners** — `dirGlobPrefix()` helper extracted to module scope and applied to every `${dir}**/*.ext` pattern (Angular primary + deep fallback, Next/React/Vue primary, FSD, components/*, Fallback C, Fallback D, platform scan). On Windows, glob v10+ returns backslash paths without a trailing slash, so the old `${dir.replace(/\\/g,"/")}**/*.tsx` pattern became `foo**/*.tsx` and only matched one level deep — silently missing nested files like `foo/routes/X.tsx` and (in some cases) spuriously matching sibling directories sharing the same prefix. The helper normalizes to `foo/**/*.tsx`, producing correct matches at any depth. Per-domain file counts may shift slightly in existing projects where this bug was masking under- or over-counts.
720
- - **Skip-list tightening in primary scanners** — To keep deep fallbacks (Angular deep fallback, Fallback C) effective, structural container names now short-circuit the primary scans: `modules`/`features`/`pages`/`views` added to `skipAngularDirs`; `components`/`hooks`/`widgets`/`entities`/`features`/`modules`/`lib`/`libs`/`utils`/`util`/`config`/`types`/`shared`/`common`/`assets` added to the Next/React/Vue `skipPages` list. A path like `src/desktop/app/components/order/` now correctly emits `order` via Fallback C instead of the generic `components` domain from the primary pattern.
721
- - **Project override file `.claudeos-scan.json`** Optional file at project root allows extending scanner defaults without editing the tool:
722
- ```json
723
- {
724
- "frontendScan": {
725
- "platformKeywords": ["kiosk"],
726
- "skipSubappNames": ["legacy"],
727
- "minSubappFiles": 3
728
- }
729
- }
730
- ```
731
- All fields additive (user entries extend defaults, never replace). `minSubappFiles` overrides the default `2`. Missing file or malformed JSON silently falls back to defaults. Resolves the `src/adm/` → `admin` rename requirement raised when the `adm` short abbreviation was excluded from the built-in keyword list.
732
-
733
- ### Changed
734
-
735
- - **4-Pass pipeline** `init` now runs Pass 1 → Pass 2 → Pass 3 → Pass 4 (previously 3-Pass). Init banner updated to `Bootstrap (4-Pass)` and `totalSteps` recomputed as `totalGroups + 3`.
736
- - **Directory count** — `init` now creates 28 directories (previously 26) with `claudeos-core/memory/` and `.claude/rules/60.memory/` added.
737
- - **Verification tools extended** — sync-checker now tracks 7 directories (added `memory/`); manifest-generator scans and indexes the memory layer with `totalMemory` in the summary.
738
- - **content-validator section count** — `[1/8]`–`[8/8]` re-numbered to `[1/9]`–`[9/9]` with a new section `[9/9] claudeos-core/memory/` performing fence-aware structural validation (decision-log heading dates, failure-pattern required fields).
739
- - **CLAUDE.md output** — Pass 4 appends a new `## Memory (L4)` section (the `(L4)` marker is language-independent so the CLI fallback can detect it across all 10 supported languages).
740
- - **Pass-3/Pass-4 prompts** — `pass3-footer.md` and the new `pass4.md` template are now wrapped with the `staging-override.md` directive so Claude redirects all `.claude/rules/` writes to the staging dir without dropping or rewriting prose references.
741
- - **`bin/cli.js`** `cmdInit` is now `async` and `await`ed; init flow uses the new async claude executor end-to-end so the per-pass tickers actually fire.
742
-
743
- ### Fixed
744
-
745
- - **Glob pattern false-anchoring in memory preservation** `isPreserved()` and `propose-rules` now skip glob patterns (`**/*`, `src/**/*.java`) when matching rule anchors against pattern bodies; a literal glob inside an entry's Fix line no longer makes every matching low-importance entry permanently preserved.
746
- - **Fence-aware entry parsing** — memory.js `parseEntries()` and content-validator's memory checks now ignore `## ...` lines inside ```` ``` ```` / `~~~` code fences; example markdown inside a decision's body text is no longer parsed as a new entry.
747
- - **Anchored regex for metadata fields** — `parseField()` and `parseDate()` require start-of-line + hyphen prefix for `frequency:` / `last seen:` / `importance:`; verbose prose containing these words (e.g., "set the frequency: 10 in config") is no longer picked up as the entry's meta value.
748
- - **Fix line detection** — matches only `- Fix:` / `- **fix**:` / `- solution:` field format (not arbitrary `fix`/`prefix` substrings); a verbose line containing "fixing" no longer falsely satisfies the Stage 1 fix-line preservation check.
749
- - **Stage 2 duplicate-merge persistence** merged `frequency` sum and `lastSeen` max are now rewritten back into body lines before serialization; previously the in-memory merge was silently discarded on disk.
750
- - **Stage 3 drop respects anchors** — low-importance aged entries anchored by an active rule path (concrete file path match) are no longer silently dropped.
751
- - **Compaction section preservation** `memory compact` only replaces the `## Last Compaction` section; user-added content that follows (e.g., project notes) is preserved.
752
- - **Pass 3 marker write validation** — `init` now throws `InitError` if `pass3-complete.json` write fails (previously silently succeeded, causing next run to regenerate CLAUDE.md).
753
- - **Silent Pass 3 marker on incomplete output** — `pass3-complete.json` could be written even when Claude truncated mid-response and `claudeos-core/guide/` was entirely empty (9 files missing). Root cause: step [8] content-validator ran with `ignoreError:true` so the 9 MISSING errors didn't block the "✅ Complete" banner; the next `init` run saw the marker + skipped Pass 3 permanently. Fixed by Guard 3 H2 (see Added). Also covers the same truncation pattern affecting `standard/`, `skills/`, `plan/` via Guard 3 H1.
754
- - **Silent Pass 4 skip on malformed marker** — Claude can emit a partial-failure marker body like `{"error":"timeout"}` that still satisfies `fileExists()`. Previously this gated subsequent runs into skipping Pass 4 forever. Fixed by `isValidPass4Marker` content validation (see Added M1).
755
- - **Silent Pass 3/4 skip on Windows file-lock** Stale-marker `fs.unlinkSync` calls were wrapped in `catch (_e) { /* ignore */ }`. If antivirus or an editor held the file handle, the unlink threw, was silently swallowed, and the subsequent `fileExists(marker)` check accepted the stale marker → silent pass-skip. Both Pass 3 and Pass 4 now surface unlink failures as `InitError` with actionable "close the editor/AV scanner" guidance (see Added `dropStalePass4Marker` + Pass 3 symmetric fix).
756
- - **Pass 2 resume accepting skeleton `{}`** — `init.js` previously only `fileExists()`-checked `pass2-merged.json` on resume. A prior crashed run that left a skeleton `{}` or malformed JSON would be accepted, poisoning Pass 3's analysis. Fixed by H3 (see Added).
757
- - **Translation fallback safety** — when `--lang` is non-English, translation failures in the static fallback path now throw `InitError` instead of silently writing English content (contradicting the user's `--lang` choice).
758
- - **Translation validation** — memory-scaffold rejects translations that lose ≥40% content length, drop >40% of headings, break code-fence count, lose required CLI-parsed keywords (`frequency:`, `last seen:`, `importance:`, `(L4)`), or break YAML frontmatter markers.
759
- - **Placeholder substitution safety** — Pass 1 prompt placeholder substitution (`{{DOMAIN_GROUP}}`, `{{PASS_NUM}}`) and `injectProjectRoot`'s `{{PROJECT_ROOT}}` substitution both now use replacement functions so `$`, `$1`, `$&`, `$$` in domain names or project paths are preserved as literal characters rather than interpreted as regex back-references (same bug class as v1.6.x's `replaceFileBlock`).
760
- - **Stale `.staged-rules/` from prior crashed runs** Pass 3 and Pass 4 now wipe any leftover staging directory before running Claude, so a crashed prior run can't smuggle stale rule files into the move step alongside the fresh output.
761
- - **Windows shell-escape warning (DEP0190)** — `runClaudePromptAsync` builds the spawn command as a single string with `shell: true` on Windows (so `claude.cmd`/`.ps1` shims resolve via PATH) and as separate args on Unix (no shell), eliminating Node 18+'s deprecation warning about mixing `shell:true` with an args array. Flags are hardcoded literals — no injection surface either way.
762
- - **Pass 3 skipped under `--force` / "fresh" resume mode** — The v1.7.x→v2.0.0 backfill guard fired whenever `CLAUDE.md + pass2-merged.json` existed and the `pass3-complete.json` marker was missing, even when the marker was missing *because* `--force` or `"fresh"` had just deleted it. The guard re-wrote the marker, Pass 3 was skipped, and the project was left with a stale `CLAUDE.md` alongside freshly-regenerated `pass1/2` artifacts and wiped `.claude/rules/` — which then failed both `sync-checker` (Master Plan orphans) and `content-validator` (missing sections). `init.js` now tracks a `wasFreshClean` flag set by the `--force` and `"fresh"` cleanup branches and gates the backfill with `!wasFreshClean`, so explicit fresh requests always run Pass 3. The existing guard still covers the intended v1.7.x upgrade path. Regression test added in `tests/pass3-marker.test.js`.
763
-
764
- ### Migration notes
765
-
766
- Existing v1.7.x projects are automatically migrated on the first `v2.0.0` `init` run:
767
- - If `CLAUDE.md` and `pass2-merged.json` exist, `pass3-complete.json` is backfilled to preserve the existing `CLAUDE.md`.
768
- - `claudeos-core/memory/` and `.claude/rules/60.memory/` are scaffolded by Pass 4 (or static fallback with Claude-driven translation when `--lang` is non-English).
769
- - A new `## Memory (L4)` section is appended to the existing `CLAUDE.md`.
770
- - No manual steps required.
771
- - To force full regeneration, use `npx claudeos-core init --force`. Note that under v2.0.0, `--force` and `"fresh"` resume mode now also wipe `.claude/rules/` and `claudeos-core/generated/.staged-rules/` — manual edits to existing rule files will be lost. Back them up first if needed.
772
-
773
- ### Known constraints
774
-
775
- - **`claude` CLI is now a hard requirement for non-English languages.** v1.7.x silently fell back to English when translation failed; v2.0.0 throws `InitError` instead. If `--lang` is non-`en`, ensure `claude` is installed and authenticated before running `init`. Use `--lang en` to bypass the translation requirement.
776
- - **`.claude/rules/` writes from Claude `-p` are blocked by Claude Code's sensitive-path policy.** v2.0.0 works around this with the staged-rules mechanism. If you author custom Pass 3/4 prompts, prepend `pass-prompts/templates/common/staging-override.md` so writes are redirected to the staging dir.
777
- - **`CLAUDEOS_SKIP_TRANSLATION=1` is a test-only escape hatch.** It short-circuits `translateIfNeeded()` to throw before invoking `claude -p`. If set in your shell accidentally (e.g. leftover from CI/test setup), `init` will fail fast when `--lang` is non-`en`. Remedy: `unset CLAUDEOS_SKIP_TRANSLATION` or run with `--lang en`. CI workflows can set it to keep translation tests deterministic without installing `claude`.
778
-
779
- ## [1.7.1] 2026-04-11
780
-
781
- ### Added
782
-
783
- - **Java scanner unit tests** — New `tests/scan-java.test.js` with 18 tests covering all 5 patterns (A/B/C/D/E), supplementary scan, skip list, root package extraction, MyBatis XML detection, DDD infrastructure/ detection, and full fallback
784
- - **Flask dedicated template** — New `pass-prompts/templates/python-flask/` with pass1/pass2/pass3 prompts tailored for Flask (Blueprint, @app.route, application factory, g/current_app, before_request, WTForms, Flask-SQLAlchemy, Flask-Login, Jinja2); Flask no longer shares python-fastapi template
785
- - **FastAPI/Flask flat project fallback** — `scan-python.js` now detects flat projects with `main.py` or `app.py` at root (or `app/main.py`) when no router files or subdomain structure exists; covers FastAPI official tutorial structure
786
- - **Vite SPA primary path scanning** — `scan-frontend.js` now detects `src/views/*/`, `src/screens/*/`, `src/routes/*/` in primary scan; Vite SPA projects no longer fall through to Fallback D
787
- - **296 tests** (287 296) Added 9 new tests: Flask template selection, flat project fallback (5 cases), Vite SPA primary paths (3 cases)
788
-
789
- ### Fixed
790
-
791
- - **Java scanner Windows path normalization** — `scan-java.js` added `norm()` function and `.map(norm)` to 9 glob calls; regex matching failed on Windows backslash paths for Pattern E (DDD/Hexagonal), root package extraction, and supplementary scan
792
- - **Pattern E missing infrastructure/ detection** — `scan-java.js` Pattern E `mprGlob` now includes `{domain}/infrastructure/*.java` in addition to `adapter/out/{persistence,repository}/`
793
- - **Flask misusing FastAPI template** `selectTemplates()` now routes `framework: "flask"` to dedicated `python-flask` instead of `python-fastapi`
794
- - **Completion banner alignment** `Total time:` label spacing fixed to align with other rows
795
-
796
- ## [1.7.0] 2026-04-11
797
-
798
- ### Added
799
-
800
- - **Vite SPA support** Full Vite detection pipeline: `stack-detector.js` detects `vite` from package.json dependencies and `vite.config.ts/js` fallback; `selectTemplates()` routes to dedicated `node-vite` template; `determineActiveDomains()` correctly classifies Vite as frontend-only
801
- - **`node-vite` template** — New `pass-prompts/templates/node-vite/` with pass1/pass2/pass3 prompts tailored for Vite SPA (client-side routing, VITE_ env prefix, Vitest, static hosting deployment — no RSC/Server Actions/next.config)
802
- - **Non-standard nested path scanning** — `scan-frontend.js` now detects pages, components, and FSD layers under `src/*/` paths (e.g., `src/admin/pages/dashboard/`, `src/admin/components/form/`, `src/admin/features/billing/`)
803
- - **No-hallucination guardrail** — `pass3-footer.md` enforces that Pass 3 may only reference technologies explicitly present in `project-analysis.json` or `pass2-merged.json`; inference from other detected libraries is prohibited
804
- - **Skill orchestrator completeness guardrail** — `pass3-footer.md` enforces that orchestrator execution tables must list all sub-skill files with no gaps in the sequence
805
- - **Progress bar with ETA** Pass 1/2/3 execution now shows a progress bar with percentage, elapsed time, and estimated remaining time based on average step duration
806
- - **Angular/Next.js default ports** — `defaultPort` logic now assigns 4200 for Angular and 3000 for frontend-only Next.js projects
807
- - **Enriched Node.js scanner**`scan-node.js` now classifies entities, modules, guards, pipes, and interceptors (NestJS-aware) in addition to controllers/services/dtos
808
- - **Enriched Python scanner** — `scan-python.js` now classifies admin, forms, urls, and tasks (Django/Celery-aware) in addition to views/models/serializers
809
- - **Fastify handler detection** — `scan-node.js` now counts `handler` files as controllers alongside controller/router/route
810
-
811
- ### Fixed
812
-
813
- - **Vite SPA misclassified as Next.js** `selectTemplates()` now routes `frontend: "react"` + `framework: "vite"` to `node-vite` instead of `node-nextjs`
814
- - **Vite incorrectly assigned backend template** Backend template fallback (`node-express`) now excludes `framework: "vite"`
815
- - **Vite SPA marked as backend project** — `determineActiveDomains()` now excludes `framework: "vite"` from backend activation
816
- - **Vite default port** Port 5173 assigned for Vite instead of falling back to 8080
817
- - **Vite triggers unnecessary backend scan** — `structure-scanner.js` now skips Node.js backend scanning when `framework: "vite"`
818
- - **Frontend-only security-db activation** — `determineActiveDomains()` now activates `30.security-db` for frontend-only projects (auth/token/XSS standards are relevant); previously required a backend framework
819
- - **FSD glob deduplication** — `scan-frontend.js` FSD layer scanning now uses Set-based deduplication matching the existing components pattern
820
- - **269 tests** (256 → 269) Added 13 new tests for Vite detection, template selection, non-standard paths, and active domain classification
821
-
822
- ## [1.6.2] 2026-04-09
823
-
824
- ### Fixed
825
-
826
- - **Sync command crash bypass** `cli.js` sync throw from `cmdHealth`/`cmdValidate`/`cmdRestore`/`cmdRefresh` now correctly caught by `.catch()` handler; previously caused unhandled exception
827
- - **`init.js` group.domains crash** Null guard added for `group.domains` and `group.estimatedFiles` in domain-groups iteration; prevents TypeError on malformed `domain-groups.json`
828
- - **Kotlin shared query resolution failure** — `scan-kotlin.js` full key (`__` separator) module names now converted back to path form (`/`) before file matching; `resolveSharedQueryDomains` was silently failing to find any files
829
- - **Python scanner Windows glob failure** — `scan-python.js` added `dir.replace(/\\/g, "/")` for Django and FastAPI/Flask glob patterns; Windows `path.dirname` returns backslashes that break glob (same fix `scan-node.js` already had)
830
- - **`prompt-generator.js` langData.labels crash** Added null guard for `langData.labels` access; prevents TypeError when `lang-instructions.json` has `instructions` but missing `labels` key
831
- - **Plan parser heading description leakage** — `plan-parser.js` `parseCodeBlocks` now strips trailing ` — description` / ` – description` / ` - description` from heading; previously included in `filePath`
832
- - **Content validator regex escape** `content-validator/index.js` regex character class now correctly escapes `[` and `]`; previously `[` was unescaped, causing runtime error when keyword contains `[`
833
- - **Manifest generator CODE_BLOCK_PLANS count** — `plan-manifest.json` now uses `extractCodeBlockPathsFromFile` for code-block-format plans (e.g., `21.sync-rules-master.md`); `fileBlocks` count was always 0
834
- - **Resume pass1/pass2 inconsistency** When "continue" is selected but no pass1 files exist while pass2 does, pass2 is now deleted to force re-run; previously new pass1 + stale pass2 caused data mismatch
835
- - **`--force` incomplete cleanup** — Now deletes all `.json` and `.md` files in `generated/` directory (not just pass1/pass2); ensures truly fresh start including stale prompts, manifests, and reports
836
- - **Workspace path without wildcard** — `stack-detector.js` now handles concrete workspace paths (e.g., `packages/backend`) by scanning both direct and child `package.json` files; previously only glob patterns with `*` worked
837
- - **Framework-less Python projects skipped** — `structure-scanner.js` now triggers Python scanner for all `language === "python"` projects; previously required `framework` to be `django`/`fastapi`/`flask`
838
- - **Root directory router.py false domain** `scan-python.js` now skips `name === "."` when `router.py` is in project root; previously created a domain named `.`
839
- - **Sync checker null sourcePath** — `sync-checker/index.js` now skips mappings with null/undefined `sourcePath`; previously produced `path.join(ROOT, undefined)` = `"ROOT/undefined"`
840
- - **Java Pattern B/D detection instability** — `scan-java.js` `detectedPattern` now determined by majority vote across all domains; previously depended on first `Object.keys` insertion order
841
- - **Duplicate pass1 prompt overwrite** — `prompt-generator.js` deduplicates `activeTemplates` via `Set`; when backend and frontend share the same template, pass1 is generated once instead of being overwritten
842
- - **Health checker stale-report overwrite** — Removed redundant `generatedAt` write that was overwriting `manifest-generator`'s `summaryPatch`; manifest-generator (run as prerequisite) already sets this key
843
- - **Plan validator empty file creation** `--execute` mode now skips file creation when plan block has empty/whitespace-only content; previously created blank files
844
-
845
- ## [1.6.1] 2026-04-09
846
-
847
- ### Fixed
848
-
849
- - **Path traversal hardening (Windows)** `plan-validator` and `sync-checker` now use case-insensitive path comparison on Windows, preventing UNC/case-mismatch bypass of root boundary check
850
- - **Null pointer crash in `stack-detector.js`** `readFileSafe()` return value for `pnpm-workspace.yaml` now guarded; prevents crash when file exists but is unreadable
851
- - **Empty pass3 prompt generation** `prompt-generator.js` now early-returns with warning when pass3 template is missing, instead of silently writing header+footer-only prompt
852
- - **Domain group boundary off-by-one** `splitDomainGroups` changed `>=` to `>` for file count threshold; groups now fill up to exactly `MAX_FILES_PER_GROUP` (40) instead of flushing one file early
853
- - **Perl regex injection in `bootstrap.sh`** All placeholder substitution migrated from `perl -pi -e` to Node.js `String.replace()`; eliminates regex special character risk in domain names; `perl` is no longer a prerequisite
854
- - **Flask default port** — `plan-installer` now maps Flask to port 5000 (was falling through to 8080)
855
- - **Health-checker dependency chain** `sync-checker` is now automatically skipped when `manifest-generator` fails, instead of running against missing `sync-map.json`
856
- - **`pass-json-validator` null template crash** Added null guard before `typeof` check; `null` no longer passes `typeof === "object"` gate
857
- - **`pass-json-validator` missing backend frameworks** Added `"fastify"` and `"flask"` to backend framework list; these stacks previously skipped backend section validation
858
- - **Init error messages** Pass 1/2/3 failure messages now include actionable guidance (check output above, retry with `--force`, verify prompt file)
859
- - **Manifest-generator error context** `.catch()` handler now prefixes error with tool name
860
- - **Line counting off-by-one** — `statSafe()` and `manifest-generator stat()` no longer count trailing newline as an extra line
861
- - **Windows CRLF drift** `plan-validator` now normalizes `\r\n` → `\n` before content comparison; prevents false drift on Windows
862
- - **`stale-report.js` mutation** — `Object.assign(ex.summary, patch)` replaced with spread operator to avoid in-place mutation
863
- - **Undefined in sync-checker Set** Malformed mappings with missing `sourcePath` no longer insert `undefined` into the registered paths Set
864
- - **BOM frontmatter detection** `content-validator` now strips UTF-8 BOM (`\uFEFF`) before checking `---` frontmatter marker
865
- - **Health-checker stderr loss** — Error output now combines both `stdout` and `stderr` instead of preferring one
866
- - **`bootstrap.sh` exit code preservation** — EXIT trap now captures and restores `$?` instead of always exiting 0
867
- - **`bootstrap.sh` NODE_MAJOR stderr** — `node -e` stderr redirected to `/dev/null` to prevent parse failure from noise
868
-
869
- ## [1.6.0] 2026-04-08
870
-
871
- ### Added
872
-
873
- - **JS/TS monorepo support** — Auto-detect `turbo.json`, `pnpm-workspace.yaml`, `lerna.json`, `package.json#workspaces`; scan sub-package `package.json` for framework/ORM/DB dependencies; domain scanning covers `apps/*/src/` and `packages/*/src/` patterns
874
- - **NestJS dedicated template (`node-nestjs`)** Separate analysis prompts for `@Module`, `@Injectable`, `@Controller`, Guards, Pipes, Interceptors, DI container, CQRS, `Test.createTestingModule`; previously shared `node-express` template
875
- - **Vue/Nuxt dedicated template (`vue-nuxt`)** Separate analysis prompts for Composition API, `<script setup>`, Pinia, `useFetch`/`useAsyncData`, Nitro server routes, `@nuxt/test-utils`; previously shared `node-nextjs` template
876
- - **Elapsed time tracking** CLI shows per-pass elapsed time and total time in completion banner
877
- - **169 new tests** (87 → 256) — Full coverage for `scan-frontend.js` (4-stage fallback), `scan-kotlin.js` (CQRS, shared query resolution), `scan-node.js`, `scan-python.js`, `prompt-generator.js` (multi-stack), `lang-selector.js`, `resume-selector.js`, `init.js`, `plan-parser.js`, monorepo detection
878
- - **README updates (10 languages)** — Updated all README files (en, ko, zh-CN, ja, es, vi, hi, ru, fr, de) to reflect new stacks table (NestJS/Vue split), monorepo root execution, facade/usecase/orchestrator detection, template structure, 3 new FAQ entries, 256 test count
879
-
880
- ### Fixed
881
-
882
- - **Windows backslash glob in `scan-kotlin.js`** — glob returns backslash paths on Windows, causing multi-module detection to silently fail; added `norm()` normalization (no-op on Unix)
883
- - **Kotlin module key collision** — When same module name exists under different parents (e.g., `servers/command/api-server` + `servers/query/api-server`), both entries now upgrade to full key; `domainMap` merges counts instead of overwriting
884
- - **Java facade/usecase/orchestrator detection** — `scan-java.js` now detects `facade/`, `usecase/`, `orchestrator/` directories as service-layer (previously only `aggregator/`)
885
- - **Verification tools exit code** — 4 tools (`content-validator`, `plan-validator`, `sync-checker`, `pass-json-validator`) now exit(1) on unexpected errors instead of exit(0); `health-checker` wrapped in try/catch
886
-
887
- ### Changed
888
-
889
- - **`lib/plan-parser.js`** (new) Extracted shared `parseFileBlocks`, `parseCodeBlocks`, `replaceFileBlock`, `replaceCodeBlock`, `CODE_BLOCK_PLANS` from `manifest-generator` and `plan-validator`; eliminates duplicate code across 2 files
890
- - **`lib/stale-report.js`** (new) — Extracted shared `updateStaleReport()` from 6 verification tools; eliminates copy-paste pattern
891
- - **`cli-utils.js`** — `ensureDir` and `fileExists` now delegate to `lib/safe-fs.js` (single source of truth)
892
- - **`prompt-generator.js`** — Removed dead strip regex (no template matched these patterns)
893
- - **`init.js` process.exit refactoring** `process.exit(1)` replaced with `throw InitError`; `lang-selector.js` and `resume-selector.js` return `null` instead of calling `process.exit()`; all errors handled centrally in `cli.js`
894
-
895
- ## [1.5.1] 2026-04-06
896
-
897
- ### Fixed
898
- - **Remove 13 bare catch blocks** `catch { }``catch (_e) { }` across 9 files; enables error variable access during debugging
899
- - **Windows backslash glob fix (3 locations)** — `scan-frontend.js` missing `dir.replace(/\\/g, "/")` at App/Pages Router (line 63), FSD (line 84), and components (line 98) scans; other locations already had this fix
900
- - **Pattern C flat MyBatis XML detection** `scan-java.js` xmlGlob now matches flat XML layout (e.g., `mapper/OrderMapper.xml`) in addition to domain subdirectory layout for Pattern C projects
901
- - **Next.js reserved segment false positives** — Added `not-found`, `error`, `loading` to `skipPages` in `scan-frontend.js` to prevent Next.js App Router reserved directories from being detected as domains
902
- - **cap variable shadowing** — Renamed outer-scope `cap` to `capDn` in `scan-java.js` to avoid shadowing the block-scoped `cap` in Pattern C branch
903
-
904
- ### Changed
905
- - **Gradle DB detection comment** — Added 2-line comment explaining postgres/sqlite exclusion rationale in `stack-detector.js` line 118
906
-
907
- ## [1.5.0] — 2026-04-05
1
+ # Changelog
2
+
3
+ ## [2.3.1] — 2026-04-23
4
+
5
+ Patch release. Fixes Windows CI breakage in `npm test`.
6
+
7
+ - **CI — cross-platform `npm test`**. Windows cmd.exe does not expand `*`
8
+ glob patterns, so `node --test tests/*.test.js` received the literal
9
+ string and exited 1 on every Windows runner. Replaced with a thin
10
+ `scripts/run-tests.js` wrapper that uses the existing `glob` dep to
11
+ enumerate test files before forwarding to `node --test`. Also replaced
12
+ the `pretest` `2>/dev/null` stderr redirect (which spuriously triggered
13
+ "The system cannot find the path specified" on Windows) with a Node
14
+ `try/catch` so the probe is silent on all platforms. No new dependencies.
15
+
16
+ No source, template, or test changes. Test count unchanged at 662.
17
+
18
+ ## [2.3.0] 2026-04-23
19
+
20
+ Adds language-invariant structural validation for generated `CLAUDE.md`.
21
+ Dogfooding v2.2.0 on a Korean-output Vite + React project (`frontend-react-A`)
22
+ surfaced the §9 L4-memory re-declaration anti-pattern *despite* the scaffold,
23
+ expanded blocklist, and post-generation self-check all being present in the
24
+ embedded Pass 3 prompt. Root cause: forbidden-section enforcement depended
25
+ on the LLM matching English canonical labels (`"Memory Layer (L4)"`) against
26
+ its own translated output (`"메모리 (L4)"`, `"メモリ (L4)"`, etc.) — a
27
+ natural-language equivalence judgment the LLM does not perform reliably
28
+ across 10 supported languages.
29
+
30
+ Dogfooding v2.3.0's initial build on a sibling project (`frontend-react-B`,
31
+ same organization, same language, same stack family) then surfaced a second
32
+ multi-repo invariant failure: the §9 problem was fixed, but the *wording*
33
+ of section headings drifted freely. One project's §7 read
34
+ `"DO NOT Read (직접 읽지 말아야 할 파일)"` while the sibling's read
35
+ `"읽지 것 (Files Not to Be Read Directly)"`. Both were "equivalent in
36
+ meaning" per the scaffold, but `grep "## 7. DO NOT Read"` matched the
37
+ first and missed the second multi-repo discoverability broken.
38
+
39
+ v2.3.0 addresses both failures by shifting structural enforcement from
40
+ LLM self-check to deterministic code-level validation that does not depend
41
+ on natural-language matching, and adds a cross-repo title-determinism
42
+ invariant (English canonical primary + optional translation parenthetical).
43
+
44
+ Continued dogfooding on `frontend-react-B` then surfaced two more failure
45
+ classes unrelated to CLAUDE.md structure:
46
+
47
+ 1. **Path hallucination in rules/standard**. Pass 3 generated rule files
48
+ referencing `src/feature/routers/featureRoutePath.ts` when the actual
49
+ file was `src/feature/routers/routePath.ts`. Root cause: the LLM saw
50
+ the parent directory `src/feature/` and a TypeScript constant
51
+ `FEATURE_ROUTE_PATH` and "renormalized" the filename to match. Pre-v2.3.0
52
+ validation did not check whether path claims resolved to real files.
53
+
54
+ 2. **MANIFEST CLAUDE.md §6 Skills drift**. Four skills registered in
55
+ `claudeos-core/skills/00.shared/MANIFEST.md`, only one of them
56
+ mentioned in CLAUDE.md §6. No existing tool detected the mismatch.
57
+
58
+ Both are now detected by a new `content-validator [10/10] path-claim
59
+ verification` check. The check uses only structural signals (backticked
60
+ paths, file-system existence, MANIFEST vs CLAUDE.md cross-reference) —
61
+ no natural-language matching, so it works identically for all 10 output
62
+ languages.
63
+
64
+ Running the initial v2.3.0 build against `frontend-react-B` surfaced a
65
+ third, upstream issue in the frontend domain scanner. The project has
66
+ a single-SPA layout (`src/admin/{api,context,dto,routers,pages/*}/`,
67
+ plus a separate `src/guide/` for documentation). The subapp scanner,
68
+ designed for dual-platform layouts (`src/pc/admin/` + `src/mobile/admin/`),
69
+ interpreted `admin` as a platform keyword and emitted the architectural
70
+ layers beneath it as pseudo-domains: `admin-api`, `admin-context`,
71
+ `admin-dto`, `admin-routers`. That fragmented one SPA into 5+ spurious
72
+ domains and, critically, primed Pass 3 to fabricate filenames with the
73
+ `admin` prefix the root cause of the `featureRoutePath.ts` hallucination
74
+ pattern. v2.3.0 adds a single-SPA detection rule: when only ONE distinct
75
+ platform keyword matches across the project tree, subapp emission is
76
+ suppressed by default, and feature domains are left to the downstream
77
+ page/FSD/components scanners to discover correctly.
78
+
79
+ Running the v2.3.0 build against `backend-java-spring` then surfaced a
80
+ long-standing resume bug in the init pipeline. When a prior `init` run
81
+ is interrupted mid-Pass-3 most commonly a stream idle timeout during
82
+ the 3d-aux (database + mcp-guide) stage — `pass3-complete.json` is
83
+ persisted in partial form (`mode: "split"`, `groupsCompleted: [...]`,
84
+ no `completedAt`). On the next run, the init orchestrator branched
85
+ solely on `fileExists(pass3Marker)` and fell into the "skip" branch
86
+ for any existing marker, even partial ones. The result: remaining
87
+ Pass 3 stages never ran, `database/` and `mcp-guide/` directories
88
+ were left empty, and the final `pass3-complete.json` retained the
89
+ partial shape which `pass-json-validator` later caught as a
90
+ `MISSING_KEY: completedAt` error after the fact. v2.3.0 fixes the
91
+ orchestrator to inspect marker contents: when the marker is partial,
92
+ `runPass3Split` is re-invoked and its internal `groupsCompleted`
93
+ logic resumes from the next unstarted stage; only fully-completed
94
+ markers are skipped.
95
+
96
+ Finally, the full v2.3.0 pipeline run against `frontend-react-B` (14
97
+ domains, Korean output) surfaced a structural regression the validator
98
+ itself caught and flagged: `## 9. 메모리 운영 (L4)` appeared in
99
+ `CLAUDE.md` as a re-declaration of the memory file table already
100
+ present in Section 8. This was the exact anti-pattern v2.3.0 was
101
+ designed to prevent, now reappearing despite the scaffold's explicit
102
+ constraints. Root-cause analysis traced it to a legacy Pass 4
103
+ behavior preserved from pre-v2.3.0: `pass4.md` instructed the LLM to
104
+ **append** a new `## N. ... (L4)` section to `CLAUDE.md`, and
105
+ `init.js` also called `appendClaudeMdL4Memory()` from
106
+ `lib/memory-scaffold.js` as a static fallback doing the same. Both
107
+ predated the current Pass 3 scaffold design where Section 8 "Common
108
+ Rules & Memory (L4)" is authored directly by Pass 3 and is the single
109
+ canonical home for both the Common Rules table and the L4 Memory
110
+ file table + workflow. With Pass 3 already writing that content,
111
+ every additional Pass 4 append produced the [S1]/[M-*]/[F2-*]
112
+ duplications the validator was built to catch. The validator was
113
+ working correctly; the generator was self-contradicting. v2.3.0 fix:
114
+ retire the Pass 4 CLAUDE.md append path completely. Pass 4 continues
115
+ to generate memory files, memory rules, and the doc-writing guide,
116
+ but never touches `CLAUDE.md`. The `appendClaudeMdL4Memory()` export
117
+ is preserved as a no-op for any external caller depending on its
118
+ signature.
119
+
120
+ Post-retirement dogfooding on `frontend-react-B` surfaced a final class
121
+ of issue: four `STALE_PATH` errors in Pass 4-generated rule and
122
+ standard files (`src/feature/main.tsx` assumed from Vite convention;
123
+ `src/feature/routers/featureRoutePath.ts` invented by prepending the
124
+ parent directory name to the filename; `src/components/utils/classNameMaker.ts`
125
+ fabricated as a plausible-sounding utility). The root cause was
126
+ parallel to the §9 issue: Pass 3's path grounding rules live in
127
+ `pass3-footer.md`, which Pass 4 never reads. Pass 4 was invoking
128
+ prior training knowledge to fabricate concrete paths instead of
129
+ grounding them in `pass3a-facts.md`. v2.3.0 adds `pass3a-facts.md`
130
+ as a mandatory read for Pass 4, plus a dedicated "Path fact
131
+ grounding" CRITICAL section in the prompt with all three flagship
132
+ hallucination anti-patterns documented as explicit examples.
133
+ The guidance also teaches the positive pattern: when in doubt,
134
+ scope a rule to a directory (`src/admin/api/`) rather than
135
+ inventing a specific filename.
136
+
137
+ Re-running `init` on `backend-java-spring` with the Fix A build proved
138
+ path-grounding works in practice STALE_PATH dropped from the
139
+ expected 4 to 0 across all Pass 4-generated rule and standard
140
+ files but left 8 MANIFEST_DRIFT errors in place. Analysis
141
+ traced these to a structural ordering problem, not an LLM
142
+ compliance problem: Pass 3b writes CLAUDE.md Section 6 BEFORE
143
+ Pass 3c generates the skills directory + `MANIFEST.md`. When a
144
+ skill ships as an orchestrator + sub-skills pair — e.g.
145
+ `10.backend-crud/01.scaffold-crud-feature.md` plus eight
146
+ sub-skills under `scaffold-crud-feature/` Pass 3b cannot
147
+ enumerate the sub-skills because they don't exist yet, and
148
+ forcing it to predict filenames would just move the problem
149
+ into a new form of hallucination (it would write
150
+ `01.entity.md` while Pass 3c emits `01.dto.md`, etc.).
151
+
152
+ The correct design is role-separated: `CLAUDE.md` Section 6 is an
153
+ **entry point** that names categories and orchestrators; `MANIFEST.md`
154
+ is the **authoritative registry** for the full sub-skill list. v2.3.0
155
+ implements both halves of this split:
156
+
157
+ 1. `content-validator [10/10]` gains an orchestrator-aware
158
+ exception: a registered sub-skill is suppressed from
159
+ `MANIFEST_DRIFT` when its orchestrator (matching
160
+ `{category}/{*}.{parent-stem}.md`) is referenced anywhere in
161
+ CLAUDE.md. Integrity checks (`STALE_SKILL_ENTRY` for missing
162
+ files, unrelated-parent drift) are preserved.
163
+
164
+ 2. `pass3b-core-header.md` gains a "CLAUDE.md Section 6 Skills
165
+ sub-section (entry point only)" guidance block that tells the
166
+ LLM to list only MANIFEST + orchestrators, never to predict
167
+ sub-skill filenames, and that cites the v2.3.0 validator's
168
+ exception so the instruction and the detection layer remain
169
+ in lockstep.
170
+
171
+ Together, Fix A (Pass 4 path grounding) and Fix B
172
+ (orchestrator/sub-skill exception + §6 guidance) close the last
173
+ two classes of dogfood-observed content-validator errors. The
174
+ remaining validator surface continues to enforce the strict
175
+ invariants fabricated paths, missing skill files, unrelated-
176
+ parent drift, §9 re-declaration, T1 heading drift, etc. — without
177
+ relaxation.
178
+
179
+ Re-running `init` on `frontend-react-B` with the Fix A + Fix B
180
+ build produced `0 MANIFEST_DRIFT` (Fix B suppressed all 8
181
+ sub-skill drift rows) but left 1 residual `STALE_PATH` in
182
+ `claudeos-core/standard/50.verification/02.testing-strategy.md`
183
+ referencing `src/__mocks__/handlers.ts`. Analysis showed a
184
+ library-convention hallucination class that the original three
185
+ anti-patterns did not cover: testing documents reach for MSW /
186
+ Vitest / Jest / React Testing Library conventional paths even
187
+ when the project has zero test coverage and no such files exist.
188
+ Analogous traps exist for `styling-patterns.md` (global
189
+ stylesheet/theme files) and `state-management.md` (store
190
+ bootstrap files). v2.3.0 adds a "Library-convention hallucination
191
+ class" block to both `pass3-footer.md` and `pass4.md` documenting
192
+ the four concrete MSW/testing anti-patterns plus the three
193
+ trigger document types, and the positive pattern: when
194
+ `pass3a-facts.md` has no concrete test/style/store files listed,
195
+ describe testing/styling/state guidance in abstract terms
196
+ (directory scope or role-based) without naming a specific path.
197
+
198
+ Final validation pass on both dogfood projects with the complete
199
+ v2.3.0 build:
200
+
201
+ - `frontend-react-B` (Korean output, 14 frontend domains,
202
+ dual-entry Vite + React 19, single-SPA admin layout,
203
+ scaffold-page-feature orchestrator with 8 sub-skills):
204
+ **12 errors 0 errors** (100% improvement), full health
205
+ check green, 25/25 CLAUDE.md lint checks passed.
206
+ - `backend-java-spring` (Korean output, 8 backend domains,
207
+ Java 17 + Spring Boot + MyBatis, scaffold-crud-feature
208
+ orchestrator with 8 sub-skills, multi-dialect DB migration
209
+ in progress): **8 errors 0 errors** (100% improvement),
210
+ full health check green, complete first-try run in 45m 29s
211
+ including the resume-from-partial-marker code path hitting
212
+ for the first time from a real-world partial Pass 3 run.
213
+
214
+ Both projects exercise distinct v2.3.0 code paths (Fix A + Fix B,
215
+ single-SPA rule, Pass 3 resume, library-convention anti-pattern,
216
+ orchestrator/sub-skill exception), and both settled at 0 errors
217
+ without any manual file edits to the generated output. This is
218
+ the first release where the full end-to-end pipeline produces a
219
+ clean `content-validator [10/10]` report on real-world sibling
220
+ Korean projects the core criterion for v2.3.0 being
221
+ publish-ready.
222
+
223
+ ### Added
224
+
225
+ - **`claude-md-validator/`** (new package, ~430 lines across 3 files).
226
+ Post-generation structural validator for `CLAUDE.md`. Every check uses
227
+ only signals that survive translation:
228
+ - **Markdown syntax**: `^## `, `^### `, `^#### `, `^```` — not localized.
229
+ - **Literal file names**: `decision-log.md`, `failure-patterns.md`,
230
+ `compaction.md`, `auto-rule-update.md` never translated.
231
+ - **Counts and table-row positions**: section count, sub-section count
232
+ per section, memory-file table-row count inside vs outside Section 8.
233
+ The same validator, byte-for-byte, produces identical verdicts on a
234
+ `CLAUDE.md` generated in English, Korean, Japanese, Vietnamese, Hindi,
235
+ Russian, etc. proven by cross-language bad-case fixtures in the test
236
+ suite.
237
+ - `structural-checks.js` individual check functions (`checkH2Count`,
238
+ `checkH3Counts`, `checkH4Counts`, `checkMemoryFileUniqueness`,
239
+ `checkMemoryScopedToSection8`, `checkSectionsHaveContent`,
240
+ `checkCanonicalHeadings`) plus a fence-aware section splitter
241
+ (`splitByH2`) that correctly ignores `##` lines inside ``` and
242
+ `~~~` code blocks.
243
+ - `index.js` high-level `validate(path)` API and standalone CLI entry.
244
+ Transparently strips a leading UTF-8 BOM (U+FEFF) from the input
245
+ before running checks, so CLAUDE.md files written by Windows editors
246
+ or cross-platform generators validate identically to those without
247
+ a BOM (otherwise the first `## ` reads as `\ufeff## ` and silently
248
+ under-counts by one).
249
+ - `reporter.js` human-readable report formatter with remediation
250
+ guidance for every failure class.
251
+
252
+ - **`npx claudeos-core lint`** command. Runs the structural validator
253
+ against `CLAUDE.md` at the project root. Exit code 0 on pass, 1 on fail
254
+ suitable for CI pipelines. The command renders per-failure remediation
255
+ guidance so users can fix issues directly without re-running the full
256
+ 4-Pass pipeline.
257
+
258
+ - **`bin/commands/lint.js`** (new). Wraps the validator for CLI use;
259
+ delegates to `claude-md-validator/` so the validator remains usable
260
+ as a library from other contexts (future `init` auto-lint, CI action,
261
+ etc.).
262
+
263
+ - **T1 Canonical heading invariant (cross-repo title determinism).**
264
+ Each of the 8 `## N.` section headings in every generated `CLAUDE.md`
265
+ must contain the English canonical token for that section, regardless
266
+ of the `--lang` output language. A native-language translation may be
267
+ appended in parentheses but MUST NOT replace the English canonical as
268
+ primary text. Required tokens:
269
+ `§1=Role Definition, §2=Project Overview, §3=Build, §4=Core Architecture,
270
+ §5=Directory Structure, §6=Standard, §7=DO NOT Read, §8=Memory`.
271
+ The validator enforces this via `checkCanonicalHeadings` (IDs `T1-1`
272
+ through `T1-8`), and the scaffold documents it as a mandatory format
273
+ rule reinforced by Pass 3 POST-GEN CHECK step 4b. This closes a
274
+ multi-repo discoverability gap discovered during `frontend-react-B`
275
+ dogfooding: sibling projects generated §7 as `"DO NOT Read (직접 읽지
276
+ 말아야 파일)"` and `"읽지 말 것 (Files Not to Be Read Directly)"`
277
+ respectively both "equivalent in meaning" but breaking
278
+ `grep "## 7. DO NOT Read"` across the organization's repos.
279
+
280
+ - **`content-validator [10/10]` path-claim + MANIFEST drift.**
281
+ A new check appended to the existing 9-stage validator in
282
+ `content-validator/index.js`. Single check, two failure classes:
283
+ - **`STALE_PATH`** any `src/...\.(ts|tsx|js|jsx)` reference
284
+ appearing in `.claude/rules/**/*.md` or
285
+ `claudeos-core/standard/**/*.md` must resolve to a real file on
286
+ disk. Fenced code blocks (``` and ~~~) and placeholder paths
287
+ (`src/{domain}/feature.ts`) are excluded, matching the scaffold
288
+ convention that placeholders stand for scaffold examples, not
289
+ actual project paths.
290
+ - **`STALE_SKILL_ENTRY`** every skill path registered in
291
+ `claudeos-core/skills/00.shared/MANIFEST.md` (extracted from
292
+ backticked `claudeos-core/skills/...` references) must exist on
293
+ disk. `MANIFEST.md` itself is excluded from the set to avoid
294
+ self-reference false positives.
295
+ - **`MANIFEST_DRIFT`** every skill registered in MANIFEST must be
296
+ mentioned somewhere in CLAUDE.md. The check looks at the whole
297
+ body (not just §6 Skills) to avoid depending on sub-section
298
+ heading wording, which varies by output language.
299
+ The check is intentionally language-invariant: it uses literal
300
+ file-path patterns and file-system existence, never parsing section
301
+ headings or reasoning about Korean/Japanese/etc. text.
302
+
303
+ - **`bin/commands/init.js` Guard 4 (non-blocking).** After Pass 4 and
304
+ structural lint, init runs `content-validator` in a child process
305
+ and surfaces the summary inline. If drift is detected, init prints a
306
+ pointer to `stale-report.json` and the standalone command to re-run
307
+ — but does NOT throw, unset `pass3-complete.json`, or abort the run.
308
+ This is a deliberate choice: LLM hallucinations may not be
309
+ deterministically fixable by re-running Pass 3, so a blocking guard
310
+ would deadlock users in an `init --force` loop. The detection signal
311
+ (non-zero `content-validator` exit code + stale-report entry) is
312
+ sufficient for CI pipelines and human triage.
313
+
314
+ - **`pass-prompts/templates/common/pass3-footer.md`Path fact
315
+ grounding (MANDATORY).** Two new CRITICAL blocks added:
316
+ - The parent-directory prefix anti-pattern (the exact
317
+ `featureRoutePath.ts` case from frontend-react-B dogfooding) is
318
+ documented with ✅/❌ examples and explanation of *why* the LLM
319
+ mis-infers (TypeScript identifier name vs filename are
320
+ independent — the constant `FEATURE_ROUTE_PATH` does not imply
321
+ filename `featureRoutePath.ts`).
322
+ - The MANIFEST CLAUDE.md §6 symmetry rule is stated explicitly,
323
+ with post-generation enforcement noted (`content-validator [10/10]
324
+ MANIFEST_DRIFT`).
325
+
326
+ - **`plan-installer/scanners/scan-frontend.js` Single-SPA detection
327
+ rule.** The subapp scanner was designed for dual-platform layouts
328
+ (same subapp implemented for two platforms, e.g., `src/pc/admin/`
329
+ + `src/mobile/admin/` → `pc-admin`, `mobile-admin`). When applied
330
+ to a single-SPA project (only one platform keyword matches, as in
331
+ `frontend-react-B`'s `src/admin/...`), the scanner misinterpreted the
332
+ SPA's architectural layers (`api`, `context`, `dto`, `routers`) as
333
+ subapps and emitted them as pseudo-domains — both cluttering the
334
+ domain plan and priming Pass 3 toward filename hallucinations with
335
+ the platform-name prefix.
336
+ - **New behavior**: before the subapp-emission loop, count the
337
+ number of distinct platform keywords present in the project.
338
+ When the count is 1, skip subapp emission entirely and let
339
+ downstream scanners (pages, FSD, components, fallback) identify
340
+ real feature domains within the single SPA.
341
+ - **Opt-out**: `.claudeos-scan.json` accepts a new override
342
+ `frontendScan.forceSubappSplit: true` to restore the legacy
343
+ single-platform emission for projects that genuinely treat the
344
+ lone platform's children as feature domains.
345
+ - **No change to multi-platform behavior**: two or more distinct
346
+ platform keywords (e.g., `pc` + `mobile`) trigger subapp
347
+ emission exactly as before.
348
+
349
+ - **`bin/commands/init.js` Pass 3 split-partial resume fix.** The
350
+ orchestrator previously decided whether to invoke `runPass3Split`
351
+ by checking only `fileExists(pass3-complete.json)`. Any existing
352
+ marker — including partial markers from a prior run's timeout —
353
+ fell into the "skip" branch, causing the remaining Pass 3 stages
354
+ to never execute on re-run. The detection block at the top of
355
+ that function already identified partial markers and logged
356
+ "runPass3Split will resume" but the actual call was gated by the
357
+ broken check, so the log was misleading.
358
+ - Now the orchestrator inspects the marker body: if
359
+ `mode === "split"` and `completedAt` is absent, `runPass3Split`
360
+ is invoked and its existing `groupsCompleted` tracking resumes
361
+ from the next unstarted stage. Only markers with `completedAt`
362
+ set are skipped.
363
+ - This repairs the dogfood case where Pass 3d-aux timed out
364
+ mid-stream on `backend-java-spring`: on the next `init`, stages 3a-3c
365
+ were correctly preserved but 3d-aux was silently skipped,
366
+ leaving `claudeos-core/database/` and `claudeos-core/mcp-guide/`
367
+ empty and the marker stuck in partial shape.
368
+
369
+ - **`tests/pass3-marker.test.js` 6 new regression tests** covering
370
+ the resume-decision classification function: absent marker fresh
371
+ run; split-partial (no `completedAt`) resume; fully completed
372
+ skip; empty `groupsCompleted` still counts as partial; malformed
373
+ JSON safe skip; non-split mode skip.
374
+
375
+ - **Pass 4 CLAUDE.md append retirement.** Changes span three files:
376
+ - `pass-prompts/templates/common/pass4.md` the "Append a new
377
+ section to existing `CLAUDE.md`" instruction block is removed
378
+ wholesale and replaced with a mandatory prohibition block
379
+ ("CLAUDE.md MUST NOT BE MODIFIED") that names the exact
380
+ validator errors ([S1], [M-*], [F2-*]) that this prohibition
381
+ prevents, and explains that Section 8 in Pass 3's output is the
382
+ single canonical home for the Common Rules table and the L4
383
+ Memory table/workflow. The remaining output sections are
384
+ renumbered (section 12 → 11). The Output Discipline section
385
+ loses its "Do NOT overwrite CLAUDE.md content — **append only**"
386
+ bullet, which is replaced with "Do NOT touch CLAUDE.md."
387
+ - `bin/commands/init.js` the two call sites of
388
+ `appendClaudeMdL4Memory()` (inside `applyStaticFallback()` and
389
+ inside the Pass 4 gap-fill path) are removed. The
390
+ `gapResults` reporting no longer includes a `CLAUDE.md#(L4)`
391
+ entry. The `require` destructure drops the function.
392
+ - `lib/memory-scaffold.js` — `appendClaudeMdL4Memory()` is
393
+ converted to a 3-line no-op that returns `true`
394
+ unconditionally. The function's public signature, name, and
395
+ module export are preserved so any external caller continues
396
+ to work; an extensive deprecation comment documents why the
397
+ behavior was retired and points at the validator errors it
398
+ was causing. The `CLAUDE_MD_APPEND` template constant is left
399
+ exported for test compatibility but is now unreferenced by
400
+ production code.
401
+ This fix closes the final regression surfaced by end-to-end
402
+ dogfooding on `frontend-react-B`: the validator was correctly
403
+ reporting an `S1` (9 sections) and four `M-*`/`F2-*` errors
404
+ against a `CLAUDE.md` whose second memory table had been
405
+ appended by Pass 4, not written by Pass 3. The fix keeps the
406
+ validator strict and removes the duplication at its source.
407
+
408
+ - **`tests/pass4-claude-md-untouched.test.js`** (new, 5 tests). A
409
+ dedicated suite guarding the retirement against regression. Tests
410
+ cover: the `pass4.md` prompt no longer contains the "Append a new
411
+ section" instruction and DOES contain the "MUST NOT BE MODIFIED"
412
+ prohibition; `init.js` neither imports nor calls
413
+ `appendClaudeMdL4Memory`; the retired function's contract (always
414
+ returns `true`, never mutates `CLAUDE.md`) holds under happy path,
415
+ missing-file, and empty/structured-input variants.
416
+ Complementary updates: 8 legacy `appendClaudeMdL4Memory` tests in
417
+ `tests/memory-scaffold.test.js` are consolidated into a single
418
+ retirement contract test; 3 lang-aware tests in
419
+ `tests/lang-aware-fallback.test.js` are rewritten to verify the
420
+ lang-invariant no-op semantics; one `generatePrompts` test in
421
+ `tests/pass4-prompt.test.js` is flipped from "prompt contains
422
+ CLAUDE.md append instructions" to "prompt contains the
423
+ prohibition and does NOT contain the legacy append header".
424
+
425
+ - **Pass 4 path fact grounding (MANDATORY).** `pass4.md` now includes
426
+ `pass3a-facts.md` in its required-reads list at the top of the
427
+ prompt (previously only `project-analysis.json` and
428
+ `pass2-merged.json` were listed), and adds a full `## CRITICAL —
429
+ Path fact grounding (MANDATORY)` section below the header. The
430
+ section states the rule first — every `src/...` path written in a
431
+ rule or standard file must appear verbatim in `pass3a-facts.md` or
432
+ `pass2-merged.json` — then documents the three flagship
433
+ hallucination anti-patterns observed in `frontend-react-B`
434
+ dogfooding: Vite-convention assumption (`src/feature/main.tsx`),
435
+ parent-directory prefix (`src/feature/routers/featureRoutePath.ts`),
436
+ and plausible-but-unverified utility (`src/components/utils/classNameMaker.ts`).
437
+ Each anti-pattern is accompanied by the concrete mechanism that
438
+ caused it ("invented based on Vite's stock convention";
439
+ "prepending the parent directory name to the filename"; etc.) so
440
+ the LLM sees both the output to avoid and the reasoning to avoid.
441
+ The positive pattern — "when in doubt, scope a rule to a
442
+ directory (`src/admin/api/`) rather than inventing a filename"
443
+ is documented explicitly, and the section cross-references the
444
+ downstream enforcement (`content-validator [10/10]`
445
+ `STALE_PATH`) so the LLM understands the validator will reject
446
+ fabricated paths. Guarded in tests by a new `generatePrompts`
447
+ assertion that all three anti-patterns, the MANDATORY tag, the
448
+ positive pattern, and the validator cross-reference are present
449
+ in the rendered pass4 prompt.
450
+
451
+ - **Orchestrator/sub-skill MANIFEST-drift exception.** Changes
452
+ span two files:
453
+ - `content-validator/index.js` Stage 2 of the MANIFEST drift
454
+ check (MANIFEST CLAUDE.md cross-reference) now recognizes
455
+ the orchestrator/sub-skill layout pattern. A registered skill
456
+ whose path matches
457
+ `claudeos-core/skills/{category}/{parent-stem}/{NN}.{name}.md`
458
+ is considered covered when CLAUDE.md mentions an orchestrator
459
+ file anywhere under the same `{category}/` whose basename
460
+ (minus any leading `NN.`) equals `{parent-stem}`. The
461
+ exception is scoped narrowly: it applies ONLY to
462
+ `MANIFEST_DRIFT`, and ONLY to sub-skills under a confirmed
463
+ orchestrator match. Integrity checks continue to fire at full
464
+ strength `STALE_SKILL_ENTRY` for registered sub-skills
465
+ whose files are missing from disk, and `MANIFEST_DRIFT` for
466
+ standalone skills (sub-skill paths whose parent stem does not
467
+ match any referenced orchestrator).
468
+ - `pass-prompts/templates/common/pass3b-core-header.md` — a new
469
+ "CLAUDE.md Section 6 Skills sub-section (entry point only)"
470
+ block tells Pass 3b to list only `MANIFEST.md` plus
471
+ orchestrator files in Section 6, never to predict sub-skill
472
+ filenames (which don't exist yet at Pass 3b time because
473
+ Pass 3c hasn't run). The guidance explains both failure modes
474
+ hallucinated filenames and silent staleness — and cites the
475
+ `content-validator` exception so the prompt-side and detector-
476
+ side are consistent.
477
+ This fix closes the final class of dogfood-observed errors on
478
+ `backend-java-spring` (8 MANIFEST_DRIFT rows, all for
479
+ `scaffold-crud-feature/0N.*.md` sub-skills) and the equivalent
480
+ shape on `frontend-react-B` (8 rows under
481
+ `scaffold-page-feature/0N.*.md`). The structural
482
+ `CLAUDE.md §6 = entry, MANIFEST = registry` split also
483
+ eliminates the recurring regeneration churn where adding or
484
+ renaming a sub-skill in Pass 3c would otherwise have required
485
+ CLAUDE.md to be rewritten.
486
+
487
+ - **`tests/content-validator.test.js` 5 new orchestrator/sub-skill
488
+ exception tests.** Coverage: (1) orchestrator mentioned +
489
+ sub-skills registered 0 drift (backend-java-spring replica);
490
+ (2) orchestrator mentioned + one sub-skill file deleted still
491
+ emits 1 `STALE_SKILL_ENTRY` (integrity not suppressed);
492
+ (3) orchestrator NOT mentioned all 5 registered skills drift
493
+ (control case exception requires orchestrator reference);
494
+ (4) sub-skill under a parent stem that does NOT match any
495
+ referenced orchestrator → still drifts (guard against
496
+ over-exception); (5) sibling layout a standalone "playground"
497
+ skill, not a sub-skill of the referenced orchestrator still
498
+ drifts (guard against conflating one-level-deep standalone
499
+ skills with sub-skills).
500
+
501
+ - **Library-convention hallucination class (MSW / Vitest / Jest /
502
+ RTL hotfix).** Extends the Fix A anti-pattern block in
503
+ `pass4.md` and mirrors the same guidance into `pass3-footer.md`
504
+ so Pass 3b and Pass 4 both observe the same rule when they
505
+ generate `testing-strategy.md`, `styling-patterns.md`, or
506
+ `state-management.md`. The block documents four concrete
507
+ library-convention traps `src/__mocks__/handlers.ts`,
508
+ `src/test/setup.ts`, `src/test-utils.tsx`, `src/setupTests.ts`
509
+ and explicitly names the three trigger document types that
510
+ most often produce this class of hallucination. The positive
511
+ rule: if `pass3a-facts.md` has no concrete test/style/store
512
+ file listed, describe guidance by role (a shared setup module
513
+ under a test directory of your choice) rather than by name,
514
+ and defer concrete paths until the files actually exist.
515
+ Regression-guarded by expanded assertions in
516
+ `tests/pass4-prompt.test.js` that verify all four MSW/testing
517
+ anti-patterns, the library-ecosystem naming, the
518
+ testing-specific positive pattern, and the
519
+ pass3a-facts.md-based negation ("these paths do not exist")
520
+ are all present in the rendered prompt.
521
+
522
+ - **`tests/content-validator.test.js`** (new, ~270 lines). 10
523
+ regression tests across 3 describe blocks:
524
+ - Path-claim positive and negative cases (hallucination detected;
525
+ fenced examples, placeholders, and existing paths do not trigger).
526
+ - MANIFEST drift scenarios (stale entry, drift, referenced skill,
527
+ self-reference exclusion, absent MANIFEST).
528
+ - Full frontend-react-B simulation: 2 STALE_PATH + 2 STALE_SKILL_ENTRY
529
+ + 3 MANIFEST_DRIFT, asserted with exact counts to prevent silent
530
+ regression as the validator evolves.
531
+
532
+ - **`tests/claude-md-validator.test.js`**structural invariant tests
533
+ parameterized across all 10 supported output languages. Coverage includes:
534
+ valid fixtures for each `--lang` code; bad fixtures in 6 languages
535
+ demonstrating identical error signatures (§9 anti-pattern detected
536
+ byte-for-byte the same regardless of script); fence-aware section
537
+ splitting against both ``` and ~~~ fences; table-row vs prose-mention
538
+ disambiguation; file-not-found handling; and T1 title-determinism
539
+ coverage (scaffold/validator token alignment, English-only acceptance,
540
+ anti-pattern rejection, case-insensitivity, graceful skip when section
541
+ count is wrong).
542
+
543
+ - **Language-independence proof fixtures** under `tests/fixtures/claude-md/`
544
+ covering all 10 supported output languages:
545
+ - Valid fixtures (same 8-section structure, different languages, all
546
+ following the T1 canonical-heading format `## N. <English canonical>
547
+ (<translation>)`): `valid-en.md`, `valid-ja.md`, `valid-zh-CN.md`,
548
+ `valid-es.md`, `valid-vi.md`, `valid-hi.md`, `valid-ru.md`,
549
+ `valid-fr.md`, `valid-de.md`, plus `frontend-react-A-fixed.md`
550
+ (Korean, real dogfooding case with §9 removed and headings
551
+ retrofitted to T1 format). Each passes the same 25 structural
552
+ checks empirical proof of language invariance across CJK,
553
+ Cyrillic, Devanagari, Latin, and Vietnamese scripts.
554
+ - Bad fixtures (same valid structure + §9 memory re-declaration
555
+ appended): `frontend-react-A-bad.md` (Korean, real), `bad-ja.md`,
556
+ `bad-zh-CN.md`, `bad-ru.md`, `bad-hi.md`, `bad-es.md`. All six
557
+ produce a **byte-for-byte identical 9-error signature**
558
+ (1 S1 + 4 M-* + 4 F2-*), confirming the validator detects the
559
+ same anti-pattern independently of output language and script.
560
+
561
+ ### Changed
562
+
563
+ - **`bin/cli.js`** registers the `lint` command, help text updated,
564
+ examples include the new command.
565
+
566
+ - **`bin/commands/init.js`** — automatically invokes the structural
567
+ validator after Pass 4 completes. Failures are reported inline but
568
+ do NOT abort the run; the generated content is preserved and the
569
+ user is pointed at `npx claudeos-core lint` for full remediation
570
+ guidance or `init --force` for regeneration. This design choice
571
+ follows Rule B (idempotency): lint is informational at install time,
572
+ advisory at lint time, blocking only in CI contexts.
573
+
574
+ - **`package.json`**:
575
+ - `version` → 2.3.0.
576
+ - `files` includes `claude-md-validator/` so the module ships with
577
+ the npm package.
578
+ - `scripts.lint` convenience alias for `node bin/cli.js lint`.
579
+ - `scripts.test` pattern updated to `node --test tests/*.test.js`
580
+ (was the bare directory form, which fails on Node 22+).
581
+
582
+ ### Prevention layer (prompt-time improvements)
583
+
584
+ Detection alone (the validator above) catches §9 after it is already
585
+ written. v2.3.0 also reduces the probability that LLMs write §9 in the
586
+ first place, by reshaping the Pass 3 prompt so the structural signal is
587
+ less ambiguous. These changes are complementary to the validator: the
588
+ validator is the guaranteed safety net, the prompt improvements lower
589
+ how often that net is needed.
590
+
591
+ - **`plan-installer/prompt-generator.js`** `demoteScaffoldMetaHeaders()`
592
+ utility added. When embedding `claude-md-scaffold.md` into the Pass 3
593
+ prompt, scaffold meta-section headings (`## Why this scaffold exists`,
594
+ `## Hard constraints`, `## Per-section generation rules`,
595
+ `## Validation checks`, `## Examples`, `## Usage from pass3 prompts`,
596
+ etc.) are demoted from `##` to `###`. The demotion is code-block-aware:
597
+ `##` lines inside ``` or ~~~ fences are preserved so the scaffold's
598
+ Template structure example (`## 1. Role Definition` ... `## 8. Common
599
+ Rules & Memory (L4)`) remains intact.
600
+
601
+ Rationale: a pre-v2.3.0 Pass 3 prompt contained **40+ `##` headings**
602
+ (scaffold meta + footer sections + phase instructions + 8 canonical
603
+ example headings). The LLM, tasked with writing a CLAUDE.md whose
604
+ target structure has exactly 8 `##` sections, was pattern-matching
605
+ against a prompt that modeled `##` as a very common structural unit —
606
+ an implicit signal that extra `##` sections were natural. After
607
+ demotion the Pass 3 prompt contains approximately 12 `##` headings,
608
+ of which **exactly 8 are the scaffold's canonical target inside the
609
+ fenced Template example**. The LLM now sees "the ## level is used for
610
+ exactly 8 things in this prompt, and those 8 things are the sections
611
+ I must write" a far cleaner mapping between prompt structure and
612
+ desired output structure.
613
+
614
+ - **`pass-prompts/templates/common/pass3-footer.md`**
615
+ `POST-GENERATION CHECK` block rewritten as an imperative 5-STEP
616
+ procedure (count assert repair verify external validation),
617
+ with `LANGUAGE-INVARIANT and TITLE-INVARIANT` explicitly named as
618
+ a core property. The repair step supplies a concrete action matrix
619
+ keyed to the surplus section's content type (memory-file references
620
+ DELETE; rule-summary content → MERGE into Section 8 sub-section 1;
621
+ procedural/enforcement content → MOVE to `.claude/rules/*`). STEP 5
622
+ announces the v2.3.0+ external validator as a safety net while
623
+ clarifying the LLM should not rely on itstructure must be correct
624
+ at write time.
625
+
626
+ - **`pass-prompts/templates/common/pass3-footer.md`** FORBIDDEN
627
+ `##`-level section list rewritten to stop depending on an English-
628
+ label blocklist. The new framing states the RULE first (no `##` may
629
+ have a title whose semantic category is "rules", "memory", "L4",
630
+ "guardrails", or any rephrasing), then gives concrete **translated
631
+ examples in Korean, Japanese, and Chinese** (`메모리 (L4)`,
632
+ `メモリ (L4)`, `记忆层 (L4)`, and analogues for Common Rules). The
633
+ goal is to make the LLM's translation decision explicit: it must
634
+ apply the forbidden rule to its translated heading, not just the
635
+ English original. A DECISION RULE block at the end gives a 3-step
636
+ check the LLM runs before writing any `##` heading.
637
+
638
+ - **`pass-prompts/templates/common/claude-md-scaffold.md`** — the
639
+ "L4 Memory Files (Re-declaration)" anti-pattern reference (which,
640
+ by naming the anti-pattern explicitly, paradoxically risked priming
641
+ the LLM to reproduce it — a "pink elephant" failure mode) was
642
+ replaced with a positively-phrased "Section 8 single-occurrence
643
+ rule": the L4 Memory Files table, Memory Workflow, and Common Rules
644
+ meta-summary table each appear EXACTLY ONCE, with their canonical
645
+ home named explicitly. Two `no "Re-declaration" duplicate` phrases
646
+ in the validation checklist were similarly simplified to
647
+ `appear EXACTLY ONCE in the whole document`.
648
+
649
+ - **`tests/prompt-generator.test.js`** — 3 new tests covering the
650
+ demotion utility:
651
+ - Meta-section `##` headers outside fences are demoted to `###`
652
+ - `##` headers inside ``` and ~~~ fenced blocks are preserved
653
+ - Real scaffold embedded into real pass3-prompt produces < 25 `##`
654
+ headings total and preserves all 8 canonical example sections.
655
+
656
+ - **`tests/claude-md-validator.test.js`** parameterized across all
657
+ 10 supported languages. 10 valid-fixture tests (one per `--lang` code)
658
+ plus 5 bad-fixture tests (ko/ja/zh-CN/ru/hi/es each asserting the
659
+ identical 9-error signature).
660
+
661
+ Total tests: **662. All pass.**
662
+ (v2.2.0 baseline 602 + v2.3.0 net additions 60 = 662. The "net"
663
+ accounting reflects that 8 legacy `appendClaudeMdL4Memory` behavior
664
+ tests were consolidated into a single no-op contract test when that
665
+ function was retired; the full set of new tests added across v2.3.0
666
+ totals 68 across path-claim verification, single-SPA scanner,
667
+ Pass 3 resume classification, Pass 4 CLAUDE.md immutability,
668
+ Pass 4 path fact grounding, and the orchestrator/sub-skill
669
+ MANIFEST-drift exception.)
670
+
671
+ ### Why this matters
672
+
673
+ The §9 re-declaration anti-pattern was the flagship problem v2.2.0 aimed
674
+ to solve, and the scaffold + prompt-level blocklist reduced incidence
675
+ substantially. Dogfooding on a real Korean-output project produced a
676
+ `CLAUDE.md` with `## 9. 메모리 (L4)` anyway the LLM successfully matched
677
+ `## 8. 공통 규칙 메모리 (L4)` as its Section 8, then created a §9
678
+ section whose title (`메모리 (L4)`) was not semantically recognized as
679
+ equivalent to the blocklisted English `"Memory Layer (L4)"`.
680
+
681
+ Extending the fix by maintaining per-language blocklists would create
682
+ unbounded maintenance surface: 10 supported languages × 6-8 forbidden
683
+ labels × every future phrasing variant. Each new language addition
684
+ would require re-auditing the entire translation table. Each miss
685
+ re-introduces the bug.
686
+
687
+ The v2.3.0 approach sidesteps this entirely. A post-generation code-level
688
+ validator that reasons about markdown syntax and literal file names does
689
+ not need a per-language dictionary. The same 22 checks run identically on
690
+ Korean, Japanese, English, or any future language added to the `--lang`
691
+ flag. Proof: the validator produces a byte-for-byte identical 9-error
692
+ signature when applied to synthesized Japanese §9 and the actual Korean
693
+ §9 that triggered this investigation. See the fixtures for reproducible
694
+ evidence.
695
+
696
+ This also aligns the final Pass of the pipeline with claudeos-core's core
697
+ principle **"LLMs guess, code confirms"**. Earlier passes already enforce
698
+ this: stack-detector confirms LLM-guessed ports via `.env.example` parsing
699
+ (v2.2.0), pass2-merged.json grounds the stack facts in real files (v2.0).
700
+ Pass 3/4 output structure was the remaining gap LLM generates it, and
701
+ no code confirmed the result. v2.3.0 closes that gap without sacrificing
702
+ Pass 3/4's generative flexibility: LLM still writes content; code now
703
+ confirms the structural invariants hold.
704
+
705
+ v2.3.0 ships both a detection layer (the validator) and a prevention
706
+ layer (the prompt-time improvements listed under "Prevention layer"
707
+ above). The prevention layer reshapes the Pass 3 prompt so the LLM
708
+ sees a cleaner mapping between prompt structure and target output
709
+ structureprimarily by demoting scaffold meta-section `##` headers
710
+ to `###` when embedded, cutting the prompt's total `##` count from
711
+ 40+ down to about 12, of which exactly 8 are the canonical section
712
+ examples the LLM must reproduce. This does not eliminate structural
713
+ drift (LLM output is probabilistic), but it reduces the rate at which
714
+ the detection layer has to fire. The two layers together form a belt-
715
+ and-suspenders design: prevention lowers baseline incidence, detection
716
+ guarantees a clear user-visible signal when incidence > 0.
717
+
718
+ ### Migration
719
+
720
+ No regeneration required. v2.3.0 is purely additive the validator
721
+ runs on existing v2.2.0-generated `CLAUDE.md` files and flags drift
722
+ where present.
723
+
724
+ - **For new projects**: `npx claudeos-core init --lang <code>` runs lint
725
+ automatically at the end; inspect any flagged drift before committing.
726
+ - **For existing v2.2.0 projects**: `npx claudeos-core lint` runs the
727
+ validator against the current `CLAUDE.md` and reports issues. No code
728
+ changes to `CLAUDE.md`, `.claude/rules/`, or `claudeos-core/*` are made
729
+ — the validator is read-only.
730
+ - **For projects with flagged drift**: regenerate via
731
+ `npx claudeos-core init --force`, or hand-edit using the per-failure
732
+ remediation guidance the validator emits.
733
+
734
+ CI-friendly exit codes: `lint` returns 0 on pass and 1 on fail, suitable
735
+ for a GitHub Actions step or pre-commit hook.
736
+
737
+ ### Notes
738
+
739
+ - The validator is deliberately narrow in scope: it verifies *structural*
740
+ invariants, not semantic correctness. Content quality (Section 1 Level-2
741
+ abstraction discipline, Section 4 pattern-naming accuracy, etc.) remains
742
+ the scaffold's and the LLM's responsibility. A future release may add
743
+ complementary content-level checks.
744
+ - The table-row-based detector distinguishes the L4 Memory Files *table*
745
+ declaration from prose mentions of memory filenames in Section 8's
746
+ workflow steps. A prior iteration during implementation flagged every
747
+ mention as a duplicate (false-positive on normal valid output); the
748
+ final design matches only markdown table-row patterns (`| \`...memory/X\` |`),
749
+ which produce clean true-positive signals on the real bad fixture and
750
+ zero flags on all three good fixtures.
751
+ - Fenced code block handling in `splitByH2` was validated against the
752
+ embedded scaffold in `pass3-prompt.md` itself, which contains an
753
+ example CLAUDE.md structure (`## 1. Role Definition` ... `## 8. ...`)
754
+ inside a ```markdown fence. Without fence-awareness, this would inflate
755
+ any section count run against the prompta reminder that markdown
756
+ validators must understand markdown, not just regex-match headings.
757
+
758
+ ---
759
+
760
+ ## [2.2.0]2026-04-21
761
+
762
+ Adds deterministic CLAUDE.md structure. Generated `CLAUDE.md` files now follow
763
+ an 8-section scaffold with fixed titles and order, driven by `pass-prompts/
764
+ templates/common/claude-md-scaffold.md`. Content within each section still
765
+ adapts to the project, but the structural skeleton no longer drifts between
766
+ projects or runs.
767
+
768
+ ### Added
769
+
770
+ - **`pass-prompts/templates/common/claude-md-scaffold.md`** (new, ~630 lines).
771
+ Single source of truth for CLAUDE.md structure. Defines the 8 sections
772
+ (Role Definition / Project Overview / Build & Run Commands / Core
773
+ Architecture / Directory Structure / Standard · Rules · Skills
774
+ Reference / DO NOT Read / Common Rules & Memory (L4); titles are
775
+ emitted in the project's output language), per-section generation
776
+ rules, dynamic substitution variables (`{PROJECT_NAME}`,
777
+ `{OUTPUT_LANG}`, `{PROJECT_CONTEXT}`), and a post-generation validation
778
+ checklist. Section 8 has TWO required sub-sections: a Common Rules
779
+ sub-section (meta-summary table of `paths: ["**/*"]` universal rules)
780
+ and an L4 Memory sub-section (memory file table + workflow). All 12 stack-specific Pass 3 prompts
781
+ now delegate CLAUDE.md structure to this scaffold and supply only
782
+ stack-specific hints (2-4 lines each).
783
+
784
+ - **`lib/env-parser.js`** (new). Parses `.env*` files into structured
785
+ `{port, host, apiTarget, vars, source}` used by stack-detector. Search
786
+ order prefers `.env.example` (committed, canonical) over local `.env`
787
+ variants. Port detection recognizes 16+ convention variable names across
788
+ Vite, Next.js, Nuxt, Angular, Node, and Python frameworks. Exposes
789
+ utilities (`parseEnvContent`, `extractPort`, `extractHost`,
790
+ `extractApiTarget`, `readStackEnvInfo`) plus a sensitive-variable
791
+ filter (`isSensitiveVarName`, `redactSensitiveVars`) that redacts
792
+ values of PASSWORD/SECRET/TOKEN/API_KEY/CREDENTIAL/PRIVATE_KEY-style
793
+ variables to a `***REDACTED***` sentinel before the vars map reaches
794
+ any downstream consumer. DATABASE_URL is whitelisted for
795
+ stack-detector back-compat. 39 unit tests in `tests/env-parser.test.js`
796
+ (30 core + 9 redaction).
797
+
798
+ ### Changed
799
+
800
+ - **All 12 Pass 3 prompts** (`angular/`, `java-spring/`, `kotlin-spring/`,
801
+ `node-express/`, `node-fastify/`, `node-nestjs/`, `node-nextjs/`,
802
+ `node-vite/`, `python-django/`, `python-fastapi/`, `python-flask/`,
803
+ `vue-nuxt/`). Two separate changes per file:
804
+ 1. The previous 5-bullet CLAUDE.md generation block (`- Role definition`,
805
+ `- Build & Run Commands`, `- Core architecture diagram`, `- {stack item}`,
806
+ `- Standard/Skills/Guide reference table`) is replaced by a scaffold
807
+ reference plus stack-specific hints. The `CRITICAL CLAUDE.md Reference
808
+ Table Completeness` warning above the block is also removed (the
809
+ scaffold's validation checklist supersedes it).
810
+ 2. The `40.infra/*` `paths` frontmatter spec is split per-file. Previously
811
+ all three infra rules (environment-config, logging-monitoring, cicd-
812
+ deployment) received the same category-level `paths` value, which caused
813
+ the logging-monitoring rule to never auto-load on source code edits
814
+ (its `paths` only matched `.env`, `*.config.*`, `*.json`, `*.yml`,
815
+ `Dockerfile*` none of which are source files). Per-file paths now
816
+ match each rule's actual guardrail target: environment-config env/
817
+ config files, logging-monitoring source code extensions (`.ts`/`.tsx`/
818
+ `.py`/`.java`/`.kt` per stack), cicd-deployment CI YAML + source.
819
+
820
+ - **`pass-prompts/templates/common/pass3-footer.md`**five new `CRITICAL`
821
+ blocks added:
822
+ - **`00.standard-reference.md` Composition**: scopes the mechanical
823
+ standards index strictly. REQUIRES a forward reference to
824
+ `claudeos-core/standard/00.core/04.doc-writing-guide.md` (generated
825
+ by Pass 4 but indexed at Pass 3 time to prevent a gap between passes).
826
+ FORBIDS a redundant "DO NOT Read / context waste" section inside
827
+ `00.standard-reference.md` — that information belongs solely in
828
+ CLAUDE.md Section 7, which is both more complete (includes project-
829
+ specific build-output and external-module paths) and not reloaded
830
+ on every edit. The 6 stacks (java-spring, kotlin-spring, node-express,
831
+ node-nextjs, python-django, python-fastapi) whose Pass 3 prompts
832
+ previously hardcoded a `## DO NOT Read` block in the reference file
833
+ have been cleaned up.
834
+ - **`.env` Is the Source of Truth for Runtime Configuration**: when
835
+ `pass2-merged.json` contains `stack.envInfo`, ports/hosts/API targets
836
+ declared in the project's `.env.example` MUST be used over framework
837
+ defaults. Affects Section 2's table, Section 3's inline run-command
838
+ comments, and any rule referencing port values (e.g., CORS origins
839
+ in auth rules).
840
+ - **Rule `paths` Must Match Rule Content**: enforces that each rule's
841
+ `paths` frontmatter matches the file types its guardrails actually
842
+ target. Explicitly prohibits copying `paths` across sibling rule files
843
+ in the same category, and tells the LLM to re-verify "when should
844
+ Claude Code auto-load this rule?" as the criterion for paths. Added
845
+ because a category-level paths shortcut in earlier Pass 3 prompts
846
+ caused the logging-monitoring rule to never match source code edits.
847
+ - **CLAUDE.md Scaffold Compliance**: enforces the 8-section structure at
848
+ generation time. Explicitly forbids adding sections with titles like
849
+ "Required to Observe While Working", "Rules Summary", "Documentation
850
+ Writing Rules", "AI Common Rules", "L4 Memory Integration Rules",
851
+ "Common Rules", or any title whose category meaning is "rules"
852
+ beyond the 8 fixed section names (the same blocklist is applied in
853
+ every output language, matching on the translated equivalents).
854
+ Adds a mandatory post-generation check (count `^## ` headings; must
855
+ equal 8; merge surplus into the correct section or move to `rules/*`
856
+ / `standard/*`). The expanded blocklist closes a rename loophole
857
+ discovered during dogfooding on a Vite + React frontend project
858
+ where the LLM appended a §9 whose title combined "Documentation
859
+ Writing + AI Common Rules + Memory Layer (L4)" to collect
860
+ rule-related content.
861
+ - **CLAUDE.md Does Not Duplicate Rules**: clarifies that CLAUDE.md
862
+ describes structure, not enforcement. Lists four categories of content
863
+ that do NOT belong in CLAUDE.md (coding rules, domain-specific rules,
864
+ multi-file sync rules, work procedures) and points each to its proper
865
+ home in rules/standard/skills/guide.
866
+
867
+ - **`pass-prompts/templates/common/claude-md-scaffold.md`** (in addition to
868
+ the new-file Add above) was tightened after initial dogfooding:
869
+ - Hard constraints section now leads with **"EXACTLY 8 SECTIONS. No more,
870
+ no less."** plus a recovery procedure for surplus sections.
871
+ - Section 6 Rules sub-section explicitly notes that the
872
+ `.claude/rules/00.core/*` wildcard row already COVERS
873
+ `51.doc-writing-rules.md` and `52.ai-work-rules.md` eliminating the
874
+ perceived need to create a separate section enumerating those rules.
875
+ - Validation checks section lists common surplus section patterns with
876
+ target destinations so the LLM can act rather than just detect.
877
+
878
+ - **`plan-installer/prompt-generator.js`** embeds the scaffold inline
879
+ into `pass3-prompt.md` at generation time. The 12 stack-specific Pass 3
880
+ templates and `pass3-footer.md` both reference
881
+ `pass-prompts/templates/common/claude-md-scaffold.md` by path, but that
882
+ path is relative to the claudeos-core package, not the user project.
883
+ The generator now reads the scaffold and inserts it between the Phase 1
884
+ fact-table block and the stack-specific body, wrapped in explicit
885
+ `# === EMBEDDED: claude-md-scaffold.md ===` markers so the LLM can locate
886
+ it. Without this embed the scaffold references would point to a file
887
+ Claude Code cannot resolve at runtime. Load is optional (`existsSafe`)
888
+ so a missing scaffold does not crash generation — the rest of the
889
+ prompt is still produced, just without the deterministic structure
890
+ enforcement.
891
+
892
+ - **`plan-installer/stack-detector.js`** — now calls `readStackEnvInfo`
893
+ before returning and attaches the result as `stack.envInfo` on
894
+ project-analysis.json. When the project's `.env.example` (or fallback
895
+ `.env`) declares a port AND no earlier detector won (Spring Boot
896
+ application.yml still takes precedence), the parsed port is promoted
897
+ to `stack.port`. This closes a long-standing gap where Vite projects
898
+ that customized their dev port via `.env` (e.g., `VITE_DESKTOP_PORT=3000`)
899
+ received the framework-default 5173 in CLAUDE.md.
900
+ Host and API target values are also captured for downstream use.
901
+
902
+ - **`plan-installer/index.js`** port resolution precedence documented
903
+ in code comments. The existing `defaultPort` fallback chain (Vite 5173,
904
+ Next.js 3000, Django 8000, etc.) is now explicitly labeled "last resort"
905
+ and runs only when neither stack-detector's direct detection (Spring
906
+ application.yml) nor the env-parser populated `stack.port`.
907
+
908
+ - **`pass-prompts/templates/common/claude-md-scaffold.md`** Section 2
909
+ (Project Overview) and Section 3 (Build & Run Commands) rules now
910
+ reference `stack.envInfo` as authoritative for port/host/API-target
911
+ values. Section 2 requires env-annotated rows in the project overview
912
+ table when the project declares them (e.g., `| Dev Server Port | 3000
913
+ (VITE_DESKTOP_PORT) |`), and Section 3 requires inline port comments
914
+ next to run commands to match the env-declared value. Framework defaults
915
+ are explicitly labeled "last resort" in both rules.
916
+
917
+ ### Why this matters
918
+
919
+ When claudeos-core was applied to three sibling projects in the same
920
+ organization (one Spring Boot backend, two Vite + React frontends), the
921
+ generated files were content-correct — standards, rules, and skills
922
+ accurately captured each project's patterns — but the `CLAUDE.md` files
923
+ had different section counts (8, 8, 9), different section names, and
924
+ different section orders. Claude Code reads CLAUDE.md first on every
925
+ session; inconsistent structure across repos made it harder for
926
+ developers (and Claude Code) to know where to look for a given piece of
927
+ information. v2.2.0 fixes the structure while leaving content
928
+ project-specific.
929
+
930
+ The removed "Required to Observe While Working" section was a symptom
931
+ of the same problem: different projects put different rules there, most
932
+ of which duplicated
933
+ content already in `.claude/rules/*` (auto-loaded) or `claudeos-core/
934
+ standard/*` (detailed patterns). Removing it eliminates a redundant
935
+ maintenance surface and reinforces the "one rule, one home" principle.
936
+
937
+ Dogfooding also uncovered a latent paths bug. The `40.infra/*` rules
938
+ shared a single category-level `paths` frontmatter that only matched
939
+ config/infra file extensions (`.env`, `*.config.*`, `*.json`, `*.yml`,
940
+ `Dockerfile*`). This meant the logging-monitoring rule — whose guardrails
941
+ cover `console.log` misuse, PII in logs, and `catch {}` swallowing —
942
+ never auto-loaded when editing `.ts`/`.tsx`/`.py`/`.java` files, i.e.,
943
+ exactly when it was needed. The rule body was correct; its activation
944
+ trigger was mis-scoped. v2.2.0 now specifies per-file `paths` in the Pass
945
+ 3 prompts and adds a `Rule paths Must Match Rule Content` CRITICAL block
946
+ to the footer so future rules cannot inherit the wrong scope by default.
947
+
948
+ A third dogfooding finding exposed a different layer of the same
949
+ philosophy violation. The stack detector parsed Spring Boot's
950
+ `application.yml` for `server.port`, but for Node/Vite projects it
951
+ simply used a hardcoded framework default (Vite → 5173) whenever no
952
+ Spring-style config was found — even when the project declared its
953
+ actual port in `.env.example` (e.g., `VITE_DESKTOP_PORT=3000`). This
954
+ meant CLAUDE.md's §2 table and §3 run-command
955
+ comments showed the Vite theoretical default instead of what the project
956
+ actually runs. The root cause was structural: the detector had no
957
+ `.env` parser beyond a DATABASE_URL check for DB identification. v2.2.0
958
+ introduces `lib/env-parser.js` with convention-aware port/host/API-target
959
+ extraction, and the scaffold and footer now treat `.env.example` as the
960
+ canonical source of runtime configuration — framework defaults are
961
+ last-resort only. This also captures host and API-target values that
962
+ previously never appeared in generated CLAUDE.md at all.
963
+
964
+ A fourth dogfooding iteration on a Spring Boot backend project
965
+ (regenerated with the interim v2.2.0 scaffold that only allowed a single
966
+ Section 8 titled "Memory (L4)") found the LLM producing a §9 titled
967
+ "Common Rules & Memory (L4)" — even with the expanded blocklist from
968
+ the earlier frontend-project fix.
969
+ The §9 contained both (a) a meta-summary table of `paths: ["**/*"]`
970
+ rules (51.doc-writing-rules + 52.ai-work-rules) and (b) a restated L4
971
+ memory table labeled "L4 Memory Files (Re-declaration)". Close
972
+ inspection showed (a) was genuinely useful content the scaffold had no
973
+ legitimate home for — a developer-facing summary of which rules
974
+ auto-load on every edit, complementary to Section 6's directory index.
975
+ The LLM kept inventing §9 because the information it wanted to convey
976
+ was real. v2.2.0 resolves this by promoting Section 8 to "Common Rules
977
+ & Memory (L4)" with two required sub-sections: one for common rules
978
+ auto-loaded on every edit (meta-summary only, not rule bodies) and one
979
+ for L4 memory referenced on-demand. This acknowledges that "which rules
980
+ auto-load universally" is a legitimate meta-information category that
981
+ deserves a visible home, while keeping the always-8-sections contract
982
+ intact. The duplicate §9 "re-declaration" anti-pattern is now
983
+ explicitly named and forbidden in both the scaffold
984
+ and the footer.
985
+
986
+ Finally, the same backend-project inspection also surfaced two smaller
987
+ but real bugs in `00.standard-reference.md` generation. First, 6 of the
988
+ 12 Pass 3 stack prompts hardcoded a `## DO NOT Read (context waste)`
989
+ section at the bottom of the reference file — a shadow of CLAUDE.md
990
+ Section 7 that was less complete (missed project-specific paths like
991
+ `build/` or external modules) and lived at the wrong layer: `00.standard-
992
+ reference.md` reloads on every edit via `paths: ["**/*"]`, while
993
+ Section 7 loads once per session. Second, `claudeos-core/standard/00.
994
+ core/04.doc-writing-guide.md` is generated by Pass 4 (Required output
995
+ #12) but never appeared in the Pass 3-generated reference index, creating
996
+ a gap the moment Pass 4 ran. v2.2.0 adds a `00.standard-reference.md
997
+ Composition` CRITICAL block to the footer that codifies: (a) always
998
+ include the Pass 4 forward reference, (b) never include a DO NOT Read
999
+ section (Section 7 is the single source of truth), (c) keep the per-
1000
+ edit payload minimal (paths only, no descriptions — descriptions live
1001
+ in Section 6 which is session-time budget). The 6 inline hardcoded
1002
+ DO NOT Read blocks have been removed from the stack prompts and
1003
+ replaced with explicit inline notes pointing to the footer rule.
1004
+
1005
+ Three additional risks surfaced during pre-release cross-checking
1006
+ and were addressed in the same release cycle. **First**, the scaffold's
1007
+ "Section 6 Rules: Always include 60.memory/*" directive, added during
1008
+ Section 8 redesign, was not echoed in the 12 stack Pass 3 prompts'
1009
+ rule-category listings — so the LLM received conflicting signals
1010
+ (scaffold says include, stack prompt doesn't mention it). Real dogfooding
1011
+ on the backend project confirmed the category was being omitted from
1012
+ the generated CLAUDE.md §6 Rules table. v2.2.0 fixes both sides: each stack
1013
+ Pass 3 prompt now explicitly lists `60.memory/*` as a forward-reference
1014
+ rule category (generated by Pass 4, but indexed at Pass 3 time), and the
1015
+ scaffold's Sub-section 2 guidance is strengthened with an example row
1016
+ and a "mandatory — do NOT omit" note. **Second**, the existing Migration
1017
+ guidance mentioned `--force` but did not explain why `npx claudeos-core
1018
+ init` (without `--force`) silently fails to adopt v2.2.0 improvements on
1019
+ upgrades. Under Rule B idempotency, existing generated files are skipped
1020
+ as "already exists", meaning users running plain `init` on a v2.1.x
1021
+ project see no visible change. v2.2.0 adds (a) a dedicated "upgrade
1022
+ detected" warning in bin/commands/init.js that fires when a pre-v2.2.0
1023
+ CLAUDE.md is detected before the resume/fresh prompt, and (b) an expanded
1024
+ Migration section in CHANGELOG that makes the `--force` requirement and
1025
+ preservation semantics (memory/ content kept, generated files replaced)
1026
+ explicit. **Third**, the new `.env.example` → CLAUDE.md pipeline created
1027
+ a theoretical pathway for accidentally committed secrets in `.env.example`
1028
+ to be amplified into the project's public-facing documentation. Although
1029
+ `.env.example` is conventionally a placeholder file, real-world projects
1030
+ occasionally check in real values by mistake. v2.2.0 adds a
1031
+ sensitive-variable filter (`lib/env-parser.js`: `isSensitiveVarName`,
1032
+ `redactSensitiveVars`) that replaces values of variables matching
1033
+ PASSWORD/SECRET/TOKEN/API_KEY/CREDENTIAL/PRIVATE_KEY patterns with a
1034
+ `***REDACTED***` sentinel before the vars map reaches any downstream
1035
+ consumer. Port/host/API-target extraction uses a whitelist of
1036
+ config-relevant keys and is unaffected. The scaffold also gains an
1037
+ explicit SECURITY directive forbidding reference to sensitive variables
1038
+ in CLAUDE.md as defense-in-depth. `DATABASE_URL` remains unredacted
1039
+ because stack-detector's DB identification path has depended on it since
1040
+ v1.x — changing that would be a breaking change.
1041
+
1042
+ ### Migration
1043
+
1044
+ Existing projects keep working. The prompt-generator change affects only
1045
+ how `pass3-prompt.md` is assembled on the next `init` or `refresh` run —
1046
+ installed standards, rules, skills, memory, and CLAUDE.md in existing
1047
+ projects are not touched until the user regenerates.
1048
+
1049
+ **⚠️ Important: `--force` is REQUIRED to adopt v2.2.0 improvements.**
1050
+
1051
+ claudeos-core's Pass 3 runs under Rule B (idempotency): if a target file
1052
+ already exists on disk, it is skipped during regeneration. This is
1053
+ designed to protect hand-edited content from being overwritten, but it
1054
+ means **a plain `npx claudeos-core init` on an existing v2.1.x project
1055
+ will NOT apply v2.2.0 improvements** because the old files (CLAUDE.md,
1056
+ `00.standard-reference.md`, `40.infra/*-rules.md`, memory rules, etc.)
1057
+ will all be skipped as "already exists".
1058
+
1059
+ To actually adopt v2.2.0's improvements (8-section CLAUDE.md, per-file
1060
+ `40.infra/*` paths, `.env.example`-based port accuracy, Section 8
1061
+ redesign, forward-referenced `04.doc-writing-guide.md`, `60.memory/*`
1062
+ row), regenerate via:
1063
+
1064
+ ```
1065
+ npx claudeos-core init --force
1066
+ ```
1067
+
1068
+ `--force` overwrites existing generated files while leaving untouched:
1069
+ - Your source code
1070
+ - `claudeos-core/memory/` content (decision-log, failure-patterns entries
1071
+ you've accumulated) — these are append-only and preserved
1072
+ - Any non-generated files under the project root
1073
+
1074
+ If you want to preview changes first, regenerate into a scratch copy of
1075
+ the project, diff the resulting files against your current ones, and
1076
+ then decide whether to `--force` on the real project. Key files to
1077
+ diff: `CLAUDE.md`, `.claude/rules/00.core/00.standard-reference.md`,
1078
+ `.claude/rules/40.infra/02.logging-monitoring-rules.md` (paths change
1079
+ is the most visible delta).
1080
+
1081
+ No manual edits are required after `--force`; the scaffold handles
1082
+ everything. Hand-edited content in `claudeos-core/standard/**` that
1083
+ you want preserved should be committed to version control before
1084
+ running `--force` so you can diff/merge any overwrites.
1085
+
1086
+ ### Notes
1087
+
1088
+ - 39 new tests added in `tests/env-parser.test.js` (30 core + 9 sensitive-
1089
+ variable redaction). All tests continue to pass: **563 pre-existing + 39
1090
+ new = 602 total**.
1091
+ - No file-format breaking changes. Existing `claudeos-core/standard/`,
1092
+ `.claude/rules/`, and `claudeos-core/skills/` content in installed
1093
+ projects is unaffected — only the CLAUDE.md generated at the project
1094
+ root changes shape on regeneration. The `40.infra/*` rule `paths`
1095
+ values will update on next regeneration, which changes when those
1096
+ rules auto-load (more accurately scoped); the rule content itself
1097
+ does not change. `stack.envInfo` is a new additive field — older
1098
+ project-analysis.json files without it still work.
1099
+ - Discovered via dogfooding on three real production projects:
1100
+ - Structural drift (3 different CLAUDE.md layouts) prompted the scaffold.
1101
+ - A Vite + React frontend project produced a §9 surplus section under
1102
+ a renamed title that bypassed the initial forbidden-sections blocklist
1103
+ — fixed by expanding the blocklist and adding the mandatory
1104
+ post-generation §-count check.
1105
+ - The `40.infra/*` paths mismatch surfaced when inspecting a generated
1106
+ `02.logging-monitoring-rules.md` and confirming via grep that its
1107
+ guardrails (source-code-level: PII logging, silent swallow, console
1108
+ use) could never auto-load given the file's own paths frontmatter
1109
+ (config-only).
1110
+ - The Vite port mismatch (5173 in CLAUDE.md when `.env.example`
1111
+ declared 3000) exposed the absence of any `.env` parsing in
1112
+ stack-detector beyond DATABASE_URL — prompted the new
1113
+ `lib/env-parser.js` utility and the `.env Is the Source of Truth`
1114
+ CRITICAL footer block.
1115
+ - A second Spring Boot backend regeneration against the interim
1116
+ scaffold produced §9 "Common Rules & Memory (L4)" despite the
1117
+ expanded blocklist, because the LLM's desired content (a
1118
+ meta-summary of `paths: ["**/*"]` universal rules, complementary to
1119
+ Section 6's directory index) had no legitimate home in the original
1120
+ 8-section design. Resolved by redesigning Section 8 into two
1121
+ sub-sections — a Common Rules sub-section for the universal-rules
1122
+ meta-summary and an L4 Memory sub-section for the memory
1123
+ table/workflow. The "L4 Memory Files (Re-declaration)" anti-pattern
1124
+ (duplicate memory table inside a second section) is now explicitly
1125
+ named and forbidden.
1126
+ - Inspection of the same backend-project output showed a generated
1127
+ `00.standard-reference.md` carrying a hardcoded `## DO NOT Read
1128
+ (context waste)` section (a partial duplicate of CLAUDE.md Section 7)
1129
+ and missing `00.core/04.doc-writing-guide.md` (created later by
1130
+ Pass 4). Fixed in the 6 affected Pass 3 stack prompts and formalized
1131
+ as the `00.standard-reference.md Composition` CRITICAL block so
1132
+ future stacks cannot reintroduce either defect.
1133
+ - Pre-release cross-check found the scaffold's `60.memory/*` "Always
1134
+ include" directive was not mirrored in any of the 12 stack Pass 3
1135
+ prompts' rule-category listings, causing the backend project's
1136
+ CLAUDE.md §6 Rules table to omit `60.memory/*` entirely. Fixed by adding the
1137
+ forward-reference row to all 12 stack prompts and strengthening the
1138
+ scaffold's Sub-section 2 guidance with an example row and "mandatory"
1139
+ wording.
1140
+ - Pre-release cross-check flagged that a plain `npx claudeos-core init`
1141
+ on an existing v2.1.x project would silently skip v2.2.0 improvements
1142
+ under Rule B idempotency. Added a CLAUDE.md marker-based detection
1143
+ in `bin/commands/init.js` that warns about the `--force` requirement
1144
+ before the resume/fresh prompt, plus an expanded Migration section
1145
+ covering preservation semantics and preview workflow.
1146
+ - Pre-release cross-check identified that values in `.env.example`
1147
+ flow through to CLAUDE.md, creating a leak pathway for accidentally
1148
+ committed secrets. Added sensitive-variable redaction in
1149
+ `lib/env-parser.js` (PASSWORD/SECRET/TOKEN/API_KEY/CREDENTIAL/
1150
+ PRIVATE_KEY patterns replaced with `***REDACTED***` sentinel) plus
1151
+ a SECURITY directive in the scaffold as defense-in-depth.
1152
+
1153
+ ---
1154
+
1155
+ ## [2.1.2] — 2026-04-21
1156
+
1157
+ Post-release regression fix for v2.1.0 master plan removal cleanup.
1158
+
1159
+ ### Fixed
1160
+
1161
+ - **`content-validator`: `plan/` directory no longer required.** On fresh
1162
+ v2.1.0+ projects `npx claudeos-core health` always failed because
1163
+ `content-validator/index.js` pushed a `MISSING: plan directory not found`
1164
+ error when `claudeos-core/plan/` was absent. Master plan generation was
1165
+ explicitly removed in v2.1.0 — `plan-validator` (v2.1.0 `Fixed`) and
1166
+ `manifest-generator` (v2.1.0 `Fixed`) were both updated to tolerate a
1167
+ missing `plan/` directory, but `content-validator` was missed during
1168
+ that cleanup. It now silently skips the plan/ check when the directory
1169
+ is absent (with an informational `plan/ not present (expected post-v2.1.0)`
1170
+ log line), matching the contract established by the other validators.
1171
+ The directory contents are still validated when present (legacy projects
1172
+ or user-authored plan files are unaffected).
1173
+
1174
+ ### Notes
1175
+
1176
+ - All 563 existing tests continue to pass. No new tests added — the fix
1177
+ is a one-line behavior change (`errors.push(...)` → `console.log(...)`)
1178
+ with a comment documenting the v2.1.0 context, and regression risk is
1179
+ covered by routine `health` runs rather than an integration test.
1180
+ - Discovered via dogfooding on a real Vite 6 + React 19 project: 62
1181
+ generated files, all Pass 1–4 stages succeeded, but `health` failed
1182
+ at content-validator. No other cleanup gaps found.
1183
+
1184
+ ---
1185
+
1186
+ ## [2.1.1] — 2026-04-20
1187
+
1188
+ Docs-only maintenance release. No runtime behavior or API changes.
1189
+
1190
+ ### Changed
1191
+
1192
+ - **README: dropped `What's New in v2.1.0` section** from all 10 language
1193
+ READMEs (`README.md`, `README.ko.md`, `README.ja.md`, `README.zh-CN.md`,
1194
+ `README.es.md`, `README.vi.md`, `README.hi.md`, `README.ru.md`,
1195
+ `README.fr.md`, `README.de.md`). Post-release cleanup — the section's
1196
+ job is done once the release ships, and the same content is preserved
1197
+ in `CHANGELOG.md` for anyone who wants the historical detail.
1198
+
1199
+ - **README: dropped the `Real production case: 18-domain admin frontend
1200
+ (2026-04-20)` subsection** under _Auto-scaling by Project Size_ across
1201
+ all 10 language READMEs. The per-stage breakdown table (9 rows) and its
1202
+ surrounding prose are removed. The trailing empirical reference in the
1203
+ FAQ "What is Pass 3 split mode" answer (the `Empirically verified up
1204
+ to 18 domains × 101 files × 102 minutes …` sentence with its now-dead
1205
+ link) is also removed so no orphan reference remains.
1206
+
1207
+ ### Notes
1208
+
1209
+ - Each README drops ~33 lines; total net change across translations is
1210
+ ~330 lines removed. No code, tests, prompts, or generated artifacts
1211
+ are touched — `npm pack` contents are identical to v2.1.0 apart from
1212
+ the README files and `package.json`/`package-lock.json` version bump.
1213
+
1214
+ ---
1215
+
1216
+ ## [2.1.0] — 2026-04-20
1217
+
1218
+ This release addresses the primary cause of `Prompt is too long` failures in
1219
+ Pass 3 on large multi-module projects. The fix is structural: Pass 3 is
1220
+ re-architected into multiple sequential `claude -p` calls with fresh context
1221
+ each, so output-accumulation overflow is no longer possible regardless of
1222
+ project size.
1223
+
1224
+ ### Added
1225
+
1226
+ - **Phase 1 "Read Once, Extract Facts" prompt block** (always on). A new
1227
+ common block `pass-prompts/templates/common/pass3-phase1.md` is prepended
1228
+ to every generated `pass3-prompt.md`. It instructs Claude to read
1229
+ `pass2-merged.json` exactly once into a compact in-context fact table and
1230
+ reference that table for all subsequent file generation. The block defines
1231
+ five rules:
1232
+ - **Rule A** — Reference the fact table, don't re-read pass2-merged.json.
1233
+ - **Rule B** — Idempotent file writing (skip if target exists with real
1234
+ content), making Pass 3 safely re-runnable after interruption.
1235
+ - **Rule C** — Cross-file consistency enforced via the fact table as
1236
+ single source of truth.
1237
+ - **Rule D** — Output conciseness: one line (`[WRITE]`/`[SKIP]`) between
1238
+ file writes, no restating the fact table, no echoing file content.
1239
+ Addresses output-accumulation overflow where verbose narration between
1240
+ 30-50 files adds 15-30K tokens of pure accumulation.
1241
+ - **Rule E** — Batch idempotent check: one `Glob` at PHASE 2 start
1242
+ instead of per-target `Read` calls.
1243
+
1244
+ - **`pass3-context.json` slim summary builder** (always on). A new file
1245
+ `claudeos-core/generated/pass3-context.json` is built after Pass 2 from
1246
+ `project-analysis.json` plus `pass2-merged.json` signals (size, top-level
1247
+ keys). Stays under 5 KB even for large projects vs. `pass2-merged.json`
1248
+ which can exceed 500 KB. Pass 3 prompts reference this as the preferred
1249
+ entry point, falling back to `pass2-merged.json` only for specific
1250
+ details (response wrapper method, util class FQN, MyBatis mapper path).
1251
+ Emits a warning when `pass2-merged.json` exceeds 300 KB.
1252
+
1253
+ - **Batch sub-division for large projects** (automatic, ≥16 domains).
1254
+ Stages 3b and 3c are sub-divided into batches of 15 domains each,
1255
+ preceded by dedicated `3b-core` / `3c-core` stages that handle
1256
+ project-wide common files. Ensures no single stage generates more than
1257
+ ~50 files, keeping output within the empirical safe range on projects
1258
+ up to 100+ domains. Batch count is `ceil(totalDomains / 15)`; domain
1259
+ order comes from `domain-groups.json` (size-balanced by plan-installer).
1260
+
1261
+ - **Split-mode partial marker protection**. `pass3-complete.json` gains
1262
+ `mode: "split"` and `groupsCompleted` array. A run that completes 3a+3b
1263
+ and crashes during 3c leaves a partial marker; on re-run, the stale-check
1264
+ detects the partial-marker shape and defers to the split runner's resume
1265
+ logic instead of deleting the marker — otherwise the run would restart
1266
+ from 3a and double the token cost.
1267
+
1268
+ - **7 regression tests** pinning the master plan no-op contract
1269
+ (`tests/master-plan-removal.test.js`).
1270
+
1271
+ - **`scaffoldSkillsManifest` gap-fill for Pass 4**. Auto-creates
1272
+ `claudeos-core/skills/00.shared/MANIFEST.md` with a minimal stub if
1273
+ Pass 3c omits it (the stack pass3.md templates list it among targets but
1274
+ without REQUIRED marking, so skill-sparse projects sometimes ended up
1275
+ with `.claude/rules/50.sync/03.skills-sync.md` pointing at a
1276
+ non-existent file). Idempotent: skips if the file already has real
1277
+ content (>20 chars).
1278
+
1279
+ ### Changed
1280
+
1281
+ - **Pass 3 now always runs in split mode.** Each stage starts with a fresh
1282
+ context window; cross-stage consistency is preserved by `pass3a-facts.md`.
1283
+ No user-facing configuration — applies to every `npx claudeos-core init`
1284
+ run automatically.
1285
+
1286
+ Stage structure:
1287
+ - **3a** — Read analysis files once, write `pass3a-facts.md` (5-10 KB
1288
+ distilled fact sheet).
1289
+ - **3b** — Generate `CLAUDE.md`, `standard/`, and `.claude/rules/`.
1290
+ Sub-divided into `3b-core` + `3b-1..N` on projects with ≥16 domains.
1291
+ - **3c** — Generate `skills/` and `guide/`. Sub-divided into `3c-core`
1292
+ + `3c-1..N` on projects with ≥16 domains.
1293
+ - **3d-aux** — Generate `database/` + `mcp-guide/` stubs.
1294
+
1295
+ Single-batch projects keep flat `"3b"`/`"3c"` marker names
1296
+ (backward-compatible); multi-batch projects use `"3b-core"`, `"3b-1"`,
1297
+ etc. Resume works at stage granularity.
1298
+
1299
+ - **Stage count by project size** (1–15 domains: 4 stages; 16–30: 8; 31–45:
1300
+ 10; 46–60: 12; 61–75: 14; 91–105: 18).
1301
+
1302
+ - `package-lock.json` synced to `2.1.0`. The v2.0.2 release had a stale
1303
+ lockfile at `2.0.0` which caused `npm ci` to fail lockfile integrity
1304
+ checks.
1305
+
1306
+ ### Removed
1307
+
1308
+ - **Master plan generation** (`claudeos-core/plan/*-master.md` files).
1309
+ Master plans were an internal tool backup not consumed by Claude Code
1310
+ at runtime, and aggregating many files in a single Pass 3d session was
1311
+ a primary source of `Prompt is too long` failures on mid-sized projects.
1312
+ Claude Code runtime is unaffected — it reads `CLAUDE.md` + `rules/`
1313
+ directly. Use `git` for backup/restore instead.
1314
+
1315
+ - **Pass 3d sub-stages `3d-standard` / `3d-rules` / `3d-skills` /
1316
+ `3d-guide`**. Only `3d-aux` (database + mcp-guide stubs) remains as a
1317
+ fixed-size task independent of domain count.
1318
+
1319
+ - **`CLAUDEOS_PASS3_SPLIT` environment variable and single-call mode.**
1320
+ Single-call had failed reliably on projects with more than ~5 domains
1321
+ because output-accumulation overflow is not predictable from input size.
1322
+ Split mode is structurally immune and is now the only supported path.
1323
+
1324
+ - **`claudeos-core/plan/` directory creation in `init`**. Directory is no
1325
+ longer created during bootstrap (honors the master-plan-removal contract).
1326
+
1327
+ ### Deprecated
1328
+
1329
+ - `scaffoldMasterPlans` in `lib/memory-scaffold.js` is kept as a
1330
+ backward-compatible no-op (returns `[]`, writes nothing). External
1331
+ callers keep working; no files are produced.
1332
+
1333
+ ### Fixed
1334
+
1335
+ - `bootstrap.sh` line endings normalized from CRLF to LF. v2.0.2 shipped
1336
+ with CRLF which caused immediate `syntax error` on macOS/Linux when
1337
+ invoked via `bash claudeos-core-tools/bootstrap.sh`.
1338
+
1339
+ - `pass3-context-builder.js`: removed unused `p2Size` placeholder variable
1340
+ (refactoring leftover, no behavior change).
1341
+
1342
+ - `init.js`: Pass 4 progress ticker `totalExpected` corrected from 6 to 5
1343
+ to reflect master plan removal. The 6th slot was counting
1344
+ `plan/50.memory-master.md` which is no longer generated, making the
1345
+ progress bar appear stuck at 83% until the run completed.
1346
+
1347
+ - `manifest-generator`: removed stale `plan-manifest.json` generation.
1348
+ Master plans were removed in v2.1.0; a manifest with an empty `plans`
1349
+ array (62 B) was noise. Nothing reads it, nothing validates it.
1350
+ `sync-map.json` is retained (with empty `mappings`) for
1351
+ `sync-checker` backward compatibility.
1352
+
1353
+ - `plan-validator`: `plan-sync-status.json` is now skipped when the
1354
+ `plan/` directory is absent or empty. Previously wrote a 147 B
1355
+ all-zeros status file on every health check for master-plan-free
1356
+ projects. `stale-report.json` still records a passing no-op so
1357
+ `health-checker` reports a clean result.
1358
+
1359
+ - `plan-parser` placeholder filtering regression in sync-checker
1360
+ on projects with `<...>` style tokens in plan files.
1361
+
1362
+ - `cli.js`: `npx claudeos-core memory --help` now displays the memory
1363
+ subcommand help instead of the top-level usage. `parseArgs` previously
1364
+ promoted `--help` to a top-level command even when it appeared after a
1365
+ command name, so `memory --help` was indistinguishable from `--help`
1366
+ alone. The fix: `--help` only becomes the top-level command when no
1367
+ other command has been seen yet. `memory --help`, `memory -h` now
1368
+ route to the subcommand's own help.
1369
+
1370
+ - `memory score`: the first `score` run no longer leaves two `importance`
1371
+ lines in each entry. The previous implementation inserted the
1372
+ auto-scored line at the top but left the user's original
1373
+ `- importance: N` line below it, producing a file with two conflicting
1374
+ values per entry. `cmdScore` now strips every importance line
1375
+ (bold or plain) before inserting the new auto-scored line, so there is
1376
+ always exactly one importance line per entry and repeated `score` runs
1377
+ remain idempotent.
1378
+
1379
+ - `memory compact`: the Stage 1 summary marker is now a proper markdown
1380
+ list item (`- _Summarized on YYYY-MM-DD — original body dropped._`).
1381
+ Previously the marker was emitted as a bare italic string without the
1382
+ `- ` prefix, which broke the surrounding list in markdown renderers
1383
+ and caused `parseEntries` to misclassify it on subsequent compactions.
1384
+ The default `fixLine` fallback was also updated to `- (fix omitted)`
1385
+ for the same consistency reason.
1386
+
1387
+ ### Test coverage
1388
+
1389
+ - 563 tests pass, 0 skip (3 runs confirmed no flakes). +165 tests vs v2.0.0
1390
+ across Pass 3 context builder, output accumulation, batch subdivision,
1391
+ master plan removal, scaffoldSkillsManifest, and memory score/compact
1392
+ formatting regression suites.
1393
+
1394
+ ## [2.0.2] — 2026-04-20
1395
+
1396
+ ### Fixed
1397
+
1398
+ - **Upgrade-path silent-skip regression for pre-v2.0.0 projects** — `npx claudeos-core health` permanently reported `content-validator: fail` with 9 × MISSING guide errors on projects that had been initialized on a pre-v2.0.0 release and then upgraded. Pass 3 wrote `pass3-complete.json` before the Pass 3 output-completeness guards (H1/H2) existed, so the marker was valid-looking on disk even though `claudeos-core/guide/` (and sometimes `standard/00.core/01.project-overview.md`, `skills/`, or `plan/`) had never been populated. On subsequent runs, `init.js` observed the marker + an existing CLAUDE.md, skipped Pass 3, and never regenerated the missing outputs — leaving the project in a stuck-fail state that only `--force` (which wipes `.claude/rules/` and loses manual edits) could recover. The Pass 3 stale-marker branch in `bin/commands/init.js` previously only detected externally-deleted CLAUDE.md; it now also drops the marker when any entry in `lib/expected-guides.js` is missing/BOM-aware-empty or when `findMissingOutputs()` (`lib/expected-outputs.js`) flags a missing standard sentinel / `skills/` / `plan/`. Mirrors the existing `dropStalePass4Marker` pattern (symmetric helper `dropStalePass3Marker` added) and reuses the same "unlink failure surfaces as `InitError` with Windows AV/file-lock guidance" contract so the recovery itself can't silently regress. Recovery is one-shot: next `init` re-runs Pass 3, which populates the missing outputs and writes a fresh marker gated by the v2.0.0 H1/H2 guards.
1399
+
1400
+ ### Added
1401
+
1402
+ - **`tests/pass3-marker.test.js`** — Six new cases covering the stale-detection branches: (a) missing guide dir → drop, (b) single BOM-only guide file → drop, (c) guides present but `skills/` gone → drop, (d) guides present but standard sentinel missing → drop, (e) complete state preserves marker, (f) `init.js` source-parity tripwire asserting `dropStalePass3Marker` + both `EXPECTED_GUIDE_FILES` and `findMissingOutputs` references appear in the stale-check region (guards against refactors silently regressing to v2.0.1 behavior).
1403
+
1404
+ ## [2.0.1] — 2026-04-19
1405
+
1406
+ ### Fixed
1407
+
1408
+ - **CI tests failing on all OS/Node combinations** — `.gitignore` no longer excludes `package-lock.json`. The GitHub Actions workflow uses `actions/setup-node` with `cache: 'npm'` and `npm ci`, both of which require a committed lockfile; without it, all 6 matrix jobs (Ubuntu/macOS/Windows × Node 18/20) failed at the install step with `Dependencies lock file is not found`.
1409
+ - **`npm test` script not cross-platform** — Changed `node --test tests/*.test.js` → `node --test` in `package.json`. The `*.test.js` glob was expanded by `sh` on Linux/macOS but left literal by `cmd.exe` on Windows runners, causing `Could not find 'D:\a\...\tests\*.test.js'` on all 3 Windows matrix jobs. The `node --test` built-in auto-discovery matches `**/*.test.{cjs,mjs,js}` from cwd (skipping `node_modules`), independent of shell globbing.
1410
+
1411
+ ### Changed
1412
+
1413
+ - **GitHub Actions runner compatibility** — Bumped `actions/checkout@v4` → `@v5` and `actions/setup-node@v4` → `@v5` in `.github/workflows/test.yml`. The `@v4` tags ran on Node.js 20, which GitHub deprecated on 2025-09-19 (forced Node 24 transition on 2026-06-02, full removal on 2026-09-16). The `@v5` tags ship with Node 24 support and clear the deprecation warnings.
1414
+
1415
+ ## [2.0.0] — 2026-04-19
1416
+
1417
+ ### Added
1418
+
1419
+ - **L4 Memory Layer** — New `claudeos-core/memory/` directory with 4 persistent files:
1420
+ - `decision-log.md` — "Why" behind design decisions (append-only, seeded from pass2-merged.json)
1421
+ - `failure-patterns.md` — Recurring errors auto-scored by `npx claudeos-core memory score`
1422
+ - `compaction.md` — 4-stage compaction strategy with project-specific error categories
1423
+ - `auto-rule-update.md` — Rule improvement proposals from `npx claudeos-core memory propose-rules`
1424
+ - **L4 Memory rules** — New `.claude/rules/60.memory/` directory with 4 rule files (`01.decision-log.md`, `02.failure-patterns.md`, `03.compaction.md`, `04.auto-rule-update.md`) instructing when/how to read and write memory files.
1425
+ - **L4 Common rules** — New `.claude/rules/00.core/51.doc-writing-rules.md` and `.claude/rules/00.core/52.ai-work-rules.md` (frontmatter requirements, hallucination prevention patterns, memory vs rules distinction).
1426
+ - **AI Work Rules template hardening** — `.claude/rules/00.core/52.ai-work-rules.md` substantially expanded for stack/role/scenario coverage:
1427
+ - **New `## Safety & Security` section** (CRITICAL — overrides every other rule in the file): destructive commands (`rm -rf`, `git reset --hard`, `git push --force`, `DROP TABLE`, `npm publish`, migration `down`/`revert`, etc.) require explicit per-command user confirmation (re-confirmed each time, not blanket); secret files (`.env*`, `*.pem`, `*.key`, `id_rsa*`, credentials JSON) referenced by variable name only — never echoed/logged/committed.
1428
+ - **17 Hallucination Prevention patterns** (was 13 in v1.x; net +4 after removing 3 redundant). New patterns: hallucinated import (verify package manifest), wrong-version API (verify manifest **and** lockfile — `package-lock.json`/`pnpm-lock.yaml`/`yarn.lock`/`gradle.lockfile`/`poetry.lock`/`Pipfile.lock`/`uv.lock`), cross-config drift (env/config family glob across backend `.env*`/`application-*.yml`/`*settings.py` and frontend `environment*.ts`/`next.config.*`/`vite.config.*`/`nuxt.config.*`), server/client component boundary mixing (Next.js App Router `"use client"`, Nuxt server/client composables, Remix `loader`/`action` — N/A for pure SPA/backend), component prop hallucination (read the target's `interface Props`/`defineProps<>`/function signature first), hardcoded secrets (Grep regex `(api[_-]?key|token|password|secret)\s*=\s*["']\w+["']` before commit; use `process.env.X`/`os.getenv("X")`/`@Value("${X}")` instead), historical DB migration editing (Flyway `migrations/V*.sql`, Alembic `alembic/versions/*.py`, Rails `db/migrate/*.rb`, Prisma `prisma/migrations/*/migration.sql`, TypeORM `migrations/*.ts` — append-only once applied; verify with `flyway info`/`alembic history`/`prisma migrate status`).
1429
+ - **Backend/frontend balanced examples** throughout — `§ No Unsolicited Work` memory-dedup bullet now lists both backend (port numbers, pool sizes, handler names, transaction propagation modes) and frontend (dev server port, build output dir, env var prefixes `VITE_`/`NEXT_PUBLIC_`/`REACT_APP_`, route definitions, bundle size budgets); `§ Code/Document Generation Accuracy` framework-shape bullet covers backend (DTOs, entity field naming, repository method signatures) and frontend (component prop interfaces, store/state shapes for Pinia/Redux/Zustand, API response types, route param types, CSS module class names).
1430
+ - **3 internal contradictions resolved** with Exception clauses — `§1 Accuracy First` "always read directly" narrowed to "always read **critical facts** directly" with sub-agent delegation explicitly allowed for non-critical exploration; `§ No Unsolicited Work` "do not make unsolicited suggestions" gains *Exception: factual errors in this project's own docs (wrong paths, dead references, internally contradicting rules) MUST be reported even if not asked*; "do not directly read internal document directories" gains *Exception: read directly when the user explicitly asks or when debugging requires it*.
1431
+ - **`Project Architecture — Hands Off` section** consolidates the previous `3-Layer Design` + `Memory vs Rules` sections (11 bullets → 7) without losing the architectural defenses (cross-layer/same-layer duplication intentional, multi-rule reinforcement, `**/*` paths protection, minor wording differences not "inconsistency").
1432
+ - **Empty directory rule softened with marker convention** — intent markers (`.gitkeep`, `KEEP_EMPTY.md`, dir listed in CLAUDE.md as planned, or referenced by an active plan/standard/skills doc) required to qualify as "intentional"; otherwise the AI must ask before deleting. Prevents the previous absolute "all empty dirs are intentional" rule from masking genuine neglect.
1433
+ - **Planned reference rule softened** — `§ Planned References` "do not label as missing" gains *Exception: if a referenced path appears in 3+ documents and doesn't exist on disk, flag for human review* (parallel to the factual-error Exception above). Prevents typos from masquerading as planned references.
1434
+ - **`Established codebase conventions take precedence over textbook-ideal patterns`** rule added — modernization/refactoring/"current best practices" migration proposals require explicit user request (e.g., "modernize", "migrate to v3"); otherwise follow existing pattern even if a greenfield design would differ.
1435
+ - **Neighbor file pattern requirement** — before writing new code, read 2-3 neighboring files in the same directory for existing patterns (naming, error handling, logging, import order, return type idioms, test structure) and match them. Greenfield/textbook idioms come second to in-codebase consistency.
1436
+ - **`§ Hallucination Prevention` pattern 7 audience-agnostic** — "code examples in rules are essential" rationale changed from "vibe-coding workflows" (audience-dependent) to "AI-assisted code generation — reduces hallucination risk regardless of audience experience" (universal).
1437
+ - **§1 cleanup** — removed `Cross-check agent results against source documents` bullet (now a weaker restatement of the narrowed §1 #2 after Exception additions).
1438
+ - **16 regression tests** added to `tests/memory-scaffold.test.js` (21 → 37) pinning the structure (7 sections, 17 patterns, 1..17 numbering continuity), all required tokens (frontend state libs, env prefixes, lockfiles, migration patterns), Exception clauses, and removed-pattern guards to prevent silent reversion.
1439
+ - **Pass 4 pipeline stage** — Generates L4 Memory scaffolding (memory files, 60.memory rules, doc-writing guide, CLAUDE.md append, master plan `50.memory-master.md`) from pass2-merged.json; Claude-driven with static fallback on failure.
1440
+ - **New CLI subcommand**:
1441
+ - `memory compact | score | propose-rules`
1442
+ - **Pass 3 completion marker** (`pass3-complete.json`) — Prevents regeneration of CLAUDE.md on subsequent `init` runs.
1443
+ - **Pass 4 completion marker** (`pass4-memory.json`) — Tracks memory scaffold completion; enables resume/skip behavior across init runs.
1444
+ - **Stale marker recovery** — Automatically detects and removes stale Pass 3/4 markers when underlying files (CLAUDE.md or memory/) are externally deleted.
1445
+ - **v1.7.x migration** — Auto-backfills Pass 3 marker when upgrading from v1.7.x with existing CLAUDE.md to prevent overwrite.
1446
+ - **New verification coverage** — content-validator section [9/9] checks memory scaffold integrity (file presence, entry structure, required fields with fence-aware parsing); pass-json-validator [5a] validates pass3-complete.json and [5b] validates pass4-memory.json.
1447
+ - **Master plan file** — `plan/50.memory-master.md` aggregates all 4 memory files using `<file path="...">` blocks.
1448
+ - **New library module** — `lib/memory-scaffold.js` (1006 LOC) containing memory/rule/plan/CLAUDE.md scaffolding with built-in multi-language translation via Claude CLI and strict translation validation (length, headings, code fences, frontmatter, CLI-parsed keywords).
1449
+ - **Translation cache** — Scaffold translations are cached per-language in `claudeos-core/generated/.i18n-cache-<lang>.json` to avoid repeated Claude CLI calls on subsequent init runs.
1450
+ - **Confidence scoring rewrite** — `memory propose-rules` replaces v1 saturating formula (`min(1, freq/10 + imp/20)`) with sigmoid on weighted evidence plus anchor-match multiplier (unanchored patterns × 0.6, missing importance caps evidence at 6).
1451
+ - **Staged-rules workaround for `.claude/` sensitive-path block** — Pass 3 and Pass 4 now write rule files to `claudeos-core/generated/.staged-rules/**` instead of `.claude/rules/**`, because Claude Code's sensitive-path policy refuses direct `.claude/` writes from the `claude -p` subprocess (even with `--dangerously-skip-permissions`). The Node.js orchestrator (not subject to that policy) moves the staged tree into `.claude/rules/` after each pass via `lib/staged-rules.js`, with rename + copy-fallback for Windows cross-volume/overwrite edge cases.
1452
+ - **`pass-prompts/templates/common/staging-override.md`** — Prepended to Pass 3/4 prompts as an absolute write-target redirect directive (preserves subpaths, leaves prose references and frontmatter `paths:` globs untouched).
1453
+ - **Pass 3 silent-failure guards** — Four post-generation guards prevent writing `pass3-complete.json` on a partial success. All guards run AFTER the staged-rules move, BEFORE the marker write:
1454
+ - **Guard 1 (partial move):** if any staged file failed to move into `.claude/rules/`, throw `InitError` with retry guidance — next `init` re-runs Pass 3 automatically.
1455
+ - **Guard 2 (zero rules):** if `.claude/rules/` is empty after the move, treat it as Claude having ignored the `staging-override.md` directive and throw, instructing the user to re-run with `--force`.
1456
+ - **Guard 3 (H2 — incomplete guide/):** reject when any of the 9 expected guide files (list in `lib/expected-guides.js`) is missing or empty. Uses BOM-aware emptiness check (`.replace(/^\uFEFF/, "").trim().length === 0`) because `String.prototype.trim` doesn't remove U+FEFF (not in Unicode White_Space) — a BOM-only file would otherwise silently pass.
1457
+ - **Guard 3 (H1 — incomplete output):** reject when (a) `claudeos-core/standard/00.core/01.project-overview.md` sentinel is missing/empty, OR (b) `claudeos-core/skills/` has zero non-empty `.md` files, OR (c) `claudeos-core/plan/` has zero non-empty `.md` files. List in `lib/expected-outputs.js`. `database/` and `mcp-guide/` intentionally excluded (content-validator treats them WARNING-level; stacks legitimately skip).
1458
+ - **Pass 2 resume validation (H3)** — On resume, `pass2-merged.json` is parsed and validated to have ≥5 top-level keys (mirrors `pass-json-validator`'s `INSUFFICIENT_KEYS` threshold) before Pass 2 is skipped. Skeleton `{}` or malformed JSON triggers file deletion + Pass 2 re-run instead of silently poisoning Pass 3's analysis input.
1459
+ - **Pass 4 marker content validation (M1)** — `isValidPass4Marker` helper validates JSON shape + `passNum === 4` + non-empty `memoryFiles` array in both stale-detection and post-Claude-run gate. Rejects malformed bodies like `{"error":"timeout"}` that Claude could emit on partial failure; previously existence-only check would accept garbage and silently skip Pass 4 forever.
1460
+ - **`dropStalePass4Marker` helper (M1)** — Pass 4 stale-marker unlink failures now surface as `InitError` with Windows file-lock guidance instead of being swallowed by `catch (_e) { /* ignore */ }`. Previously a locked file (AV scanner / editor holding the handle) would leave the stale marker in place, and the subsequent `fileExists(pass4Marker)` check would accept it → silent Pass 4 skip.
1461
+ - **Pass 3 stale-marker unlink strictness** — Symmetric with Pass 4 above: `pass3-complete.json` cleanup (when CLAUDE.md is externally deleted) now throws `InitError` on unlink failure instead of being swallowed. Closes the same silent-skip class for Pass 3.
1462
+ - **`CLAUDEOS_SKIP_TRANSLATION=1` env guard (M2)** — `lib/memory-scaffold.js` `translateIfNeeded()` short-circuits to throw with a clear lang-specific message when this env var is set, before any `claude -p` invocation. Intended as a test-only escape hatch so translation-dependent tests (e.g. `tests/lang-aware-fallback.test.js`) assert the "translation must throw" contract deterministically regardless of whether the `claude` CLI is authenticated in the test env. Strict `=== "1"` check (not truthy-coerce) to avoid surprise-triggering on common env conventions.
1463
+ - **Early fail-fast for env+lang incompatibility** — `init.js` detects `CLAUDEOS_SKIP_TRANSLATION=1` combined with `--lang ≠ en` at language-selection time and throws `InitError` immediately with remediation (`unset CLAUDEOS_SKIP_TRANSLATION` or `--lang en`). Previously this combination would let the pipeline proceed and crash mid-Pass-4 with a confusing "translation skipped" error deep in the scaffolding stack.
1464
+ - **CI workflow (M3)** — `.github/workflows/test.yml` runs `npm test` on `ubuntu-latest × windows-latest × macos-latest × Node 18/20` matrix with `CLAUDEOS_SKIP_TRANSLATION=1` set on the test step so translation tests pass without requiring `claude` CLI in the runner. Uses `npm ci` against the committed `package-lock.json`.
1465
+ - **New shared library modules** — Single sources of truth for Pass 3 output expectations, preventing drift between enforcement and validation:
1466
+ - `lib/expected-guides.js` — 9 guide file paths. Imported by `init.js` Guard 3 H2 and `content-validator/index.js` `[5/9]` (no more hardcoded duplicates).
1467
+ - `lib/expected-outputs.js` — 3 additional Pass 3 outputs (standard sentinel, `skills/`, `plan/`) with `findMissingOutputs(projectRoot)` + `hasNonEmptyMdRecursive(dir)` helpers (BOM-aware). Imported by `init.js` Guard 3 H1.
1468
+ - **Async claude execution + progress ticker** — `cli-utils.js` adds `runClaudePromptAsync` (spawn-based, non-blocking; lets a `setInterval` ticker run concurrently with the Claude subprocess) and `runClaudeCapture` (execSync wrapper that captures stdout, used by the translation engine in `memory-scaffold.js`). `init.js` adds `makePassTicker` with three display modes — elapsed-only, file-delta, and fixed-target (`N/M files (P%)`) — driving the per-pass `⏳`/`📝` progress line in TTY (`\r`-rewritten) and CI/piped (periodic newlines) environments.
1469
+ - **`--force` and "fresh" resume cleanup** — Now also wipes `claudeos-core/generated/.staged-rules/` (leftover from a prior crashed Pass 3/4 run) and `.claude/rules/` (so Guard 2's zero-rules detection can't false-negative on stale rules from a previous run); under `"fresh"` mode the `pass3-complete.json` and `pass4-memory.json` markers are also unlinked so both passes re-execute. Manual edits to `.claude/rules/` are lost — acceptable under the explicit `--force`/`fresh` choice.
1470
+ - **190+ new tests** (296 → 489) — New/expanded suites: `memory-scaffold.test.js`, `memory-command.test.js`, `pass4-prompt.test.js`, `pass3-marker.test.js`, `pass3-guards.test.js` (Guards 1/2 + Guard 3 H1/H2 with BOM coverage), `pass2-validation.test.js` (H3 structural check), `pass4-marker-validation.test.js` (M1 `isValidPass4Marker` + `dropStalePass4Marker` regression guards), `translation-skip-env.test.js` (M2 env guard + M3 CI workflow presence), `staged-rules.test.js`, `lang-aware-fallback.test.js` (sets `CLAUDEOS_SKIP_TRANSLATION=1` at module top to make translation-throw assertions deterministic), `placeholder-substitution.test.js`, plus expansions to existing suites.
1471
+ - **Progress bar with ETA** — Pass 1/2/3/4 execution shows a progress bar with percentage, elapsed time, and ETA based on average step duration (carried over and extended from v1.7.0; Pass 4 added).
1472
+ - **Platform/tier-split frontend detection (framework-agnostic)** — `scan-frontend.js` now recognizes `src/{platform}/{subapp}/` layouts where `{platform}` is either a device/target-environment keyword (`desktop`, `pc`, `web`, `mobile`, `mc`, `mo`, `sp`, `tablet`, `tab`, `pwa`, `tv`, `ctv`, `ott`, `watch`, `wear`) or an access-tier keyword (`admin`, `cms`, `backoffice`, `back-office`, `portal`) — covers English names plus common Korean corporate abbreviations. The short `adm` abbreviation is deliberately excluded as too ambiguous in isolation; projects using `src/adm/` as an admin root should rename to `admin` or wait for the override-file mechanism planned for a future release. Emits one domain per (platform, subapp) pair named `{platform}-{subapp}`, with per-domain counts for `routes`/`components`/`layouts`/`hooks`. Runs as a shared pattern across **all** detected frontends (Angular, Next.js, React, Vue/Nuxt) — the glob uses a multi-extension filter (`{tsx,jsx,ts,js,vue}`) so Angular `.component.ts` files and Vue `.vue` files are captured alongside React `.tsx`. A minimum of 2 source files per subapp is required before a domain is emitted — single-file dirs under a platform root are almost always accidental and would otherwise produce noisy 1-file "domains" in the Pass 1 group plan. Subapp name is always read from the filesystem via `path.basename` at scan time — no project/brand identifiers are hardcoded. Structural dirs (`components`, `hooks`, `layouts`), FSD layers (`widgets`, `features`, `entities`), and framework router dirs (`app`, `pages`, `routes`, `views`, `screens`, `containers`, `modules`, `domains`) are skipped at the subapp level so deeper structures still reach their dedicated scanners. Ambiguous names like `store` are deliberately allowed because e-commerce projects legitimately use them as subapp names. **Behavior note:** the change is additive for projects whose `src/{platform}/{subapp}/` dirs were previously unreachable by the primary/FSD/components scanners — those projects now gain the new domains; projects whose content was already being captured by other scanners see no change (the skip list ensures `src/admin/pages/*`, `src/admin/components/*`, etc. still fall through to their existing scanners).
1473
+ - **Deep routes-file fallback (Fallback E, framework-agnostic)** — Catches React Router file-routing projects (CRA/Vite + `react-router`) that don't match Next.js `page.tsx` or FSD layouts. When all primary scanners and Fallback A–D return 0, globs `**/routes/*.{tsx,jsx,ts,js,vue}` and groups by the parent-of-`routes` directory name. Also runs across all frontends (Angular/Next/React/Vue), not gated to any single framework. Generic parent names (`src`, `app`, `pages`) are filtered so the fallback emits meaningful feature/subapp names rather than framework-convention placeholders.
1474
+ - **Shared scanner ignore lists** — `BUILD_IGNORE_DIRS` (node_modules, build, dist, out, .next, .nuxt, .svelte-kit, .angular, .turbo, .cache, .parcel-cache, coverage, storybook-static, .vercel, .netlify) and `TEST_FILE_IGNORE` (spec/test/stories/e2e/cy + `__snapshots__`/`__tests__` dirs) extracted as module-level constants. Both the platform scan and Fallback E consume these so build outputs and test fixtures don't inflate per-domain file counts or create spurious Fallback E hits.
1475
+ - **Monorepo platform split** — Platform scan now matches three layouts: `src/{platform}/{subapp}/` (standalone), `{apps,packages}/*/src/{platform}/{subapp}/` (Turborepo/pnpm workspace with `src/`), and `{apps,packages}/{platform}/{subapp}/` (workspaces without a `src/` wrapper). Platform segment is located via `parts.findIndex` on the keyword list, so paths like `src/pc/admin/` correctly split into `pc` (platform) + `admin` (subapp) without mistaking the subapp name for another platform keyword.
1476
+ - **Windows path glob fix across all scanners** — `dirGlobPrefix()` helper extracted to module scope and applied to every `${dir}**/*.ext` pattern (Angular primary + deep fallback, Next/React/Vue primary, FSD, components/*, Fallback C, Fallback D, platform scan). On Windows, glob v10+ returns backslash paths without a trailing slash, so the old `${dir.replace(/\\/g,"/")}**/*.tsx` pattern became `foo**/*.tsx` and only matched one level deep — silently missing nested files like `foo/routes/X.tsx` and (in some cases) spuriously matching sibling directories sharing the same prefix. The helper normalizes to `foo/**/*.tsx`, producing correct matches at any depth. Per-domain file counts may shift slightly in existing projects where this bug was masking under- or over-counts.
1477
+ - **Skip-list tightening in primary scanners** — To keep deep fallbacks (Angular deep fallback, Fallback C) effective, structural container names now short-circuit the primary scans: `modules`/`features`/`pages`/`views` added to `skipAngularDirs`; `components`/`hooks`/`widgets`/`entities`/`features`/`modules`/`lib`/`libs`/`utils`/`util`/`config`/`types`/`shared`/`common`/`assets` added to the Next/React/Vue `skipPages` list. A path like `src/desktop/app/components/order/` now correctly emits `order` via Fallback C instead of the generic `components` domain from the primary pattern.
1478
+ - **Project override file `.claudeos-scan.json`** — Optional file at project root allows extending scanner defaults without editing the tool:
1479
+ ```json
1480
+ {
1481
+ "frontendScan": {
1482
+ "platformKeywords": ["kiosk"],
1483
+ "skipSubappNames": ["legacy"],
1484
+ "minSubappFiles": 3
1485
+ }
1486
+ }
1487
+ ```
1488
+ All fields additive (user entries extend defaults, never replace). `minSubappFiles` overrides the default `2`. Missing file or malformed JSON silently falls back to defaults. Resolves the `src/adm/` → `admin` rename requirement raised when the `adm` short abbreviation was excluded from the built-in keyword list.
1489
+
1490
+ ### Changed
1491
+
1492
+ - **4-Pass pipeline** — `init` now runs Pass 1 → Pass 2 → Pass 3 → Pass 4 (previously 3-Pass). Init banner updated to `Bootstrap (4-Pass)` and `totalSteps` recomputed as `totalGroups + 3`.
1493
+ - **Directory count** — `init` now creates 28 directories (previously 26) with `claudeos-core/memory/` and `.claude/rules/60.memory/` added.
1494
+ - **Verification tools extended** — sync-checker now tracks 7 directories (added `memory/`); manifest-generator scans and indexes the memory layer with `totalMemory` in the summary.
1495
+ - **content-validator section count** — `[1/8]`–`[8/8]` re-numbered to `[1/9]`–`[9/9]` with a new section `[9/9] claudeos-core/memory/` performing fence-aware structural validation (decision-log heading dates, failure-pattern required fields).
1496
+ - **CLAUDE.md output** — Pass 4 appends a new `## Memory (L4)` section (the `(L4)` marker is language-independent so the CLI fallback can detect it across all 10 supported languages).
1497
+ - **Pass-3/Pass-4 prompts** — `pass3-footer.md` and the new `pass4.md` template are now wrapped with the `staging-override.md` directive so Claude redirects all `.claude/rules/` writes to the staging dir without dropping or rewriting prose references.
1498
+ - **`bin/cli.js`** — `cmdInit` is now `async` and `await`ed; init flow uses the new async claude executor end-to-end so the per-pass tickers actually fire.
1499
+
1500
+ ### Fixed
1501
+
1502
+ - **Glob pattern false-anchoring in memory preservation** — `isPreserved()` and `propose-rules` now skip glob patterns (`**/*`, `src/**/*.java`) when matching rule anchors against pattern bodies; a literal glob inside an entry's Fix line no longer makes every matching low-importance entry permanently preserved.
1503
+ - **Fence-aware entry parsing** — memory.js `parseEntries()` and content-validator's memory checks now ignore `## ...` lines inside ```` ``` ```` / `~~~` code fences; example markdown inside a decision's body text is no longer parsed as a new entry.
1504
+ - **Anchored regex for metadata fields** — `parseField()` and `parseDate()` require start-of-line + hyphen prefix for `frequency:` / `last seen:` / `importance:`; verbose prose containing these words (e.g., "set the frequency: 10 in config") is no longer picked up as the entry's meta value.
1505
+ - **Fix line detection** — matches only `- Fix:` / `- **fix**:` / `- solution:` field format (not arbitrary `fix`/`prefix` substrings); a verbose line containing "fixing" no longer falsely satisfies the Stage 1 fix-line preservation check.
1506
+ - **Stage 2 duplicate-merge persistence** — merged `frequency` sum and `lastSeen` max are now rewritten back into body lines before serialization; previously the in-memory merge was silently discarded on disk.
1507
+ - **Stage 3 drop respects anchors** — low-importance aged entries anchored by an active rule path (concrete file path match) are no longer silently dropped.
1508
+ - **Compaction section preservation** — `memory compact` only replaces the `## Last Compaction` section; user-added content that follows (e.g., project notes) is preserved.
1509
+ - **Pass 3 marker write validation** — `init` now throws `InitError` if `pass3-complete.json` write fails (previously silently succeeded, causing next run to regenerate CLAUDE.md).
1510
+ - **Silent Pass 3 marker on incomplete output** — `pass3-complete.json` could be written even when Claude truncated mid-response and `claudeos-core/guide/` was entirely empty (9 files missing). Root cause: step [8] content-validator ran with `ignoreError:true` so the 9 MISSING errors didn't block the "✅ Complete" banner; the next `init` run saw the marker + skipped Pass 3 permanently. Fixed by Guard 3 H2 (see Added). Also covers the same truncation pattern affecting `standard/`, `skills/`, `plan/` via Guard 3 H1.
1511
+ - **Silent Pass 4 skip on malformed marker** — Claude can emit a partial-failure marker body like `{"error":"timeout"}` that still satisfies `fileExists()`. Previously this gated subsequent runs into skipping Pass 4 forever. Fixed by `isValidPass4Marker` content validation (see Added M1).
1512
+ - **Silent Pass 3/4 skip on Windows file-lock** — Stale-marker `fs.unlinkSync` calls were wrapped in `catch (_e) { /* ignore */ }`. If antivirus or an editor held the file handle, the unlink threw, was silently swallowed, and the subsequent `fileExists(marker)` check accepted the stale marker → silent pass-skip. Both Pass 3 and Pass 4 now surface unlink failures as `InitError` with actionable "close the editor/AV scanner" guidance (see Added `dropStalePass4Marker` + Pass 3 symmetric fix).
1513
+ - **Pass 2 resume accepting skeleton `{}`** — `init.js` previously only `fileExists()`-checked `pass2-merged.json` on resume. A prior crashed run that left a skeleton `{}` or malformed JSON would be accepted, poisoning Pass 3's analysis. Fixed by H3 (see Added).
1514
+ - **Translation fallback safety** — when `--lang` is non-English, translation failures in the static fallback path now throw `InitError` instead of silently writing English content (contradicting the user's `--lang` choice).
1515
+ - **Translation validation** — memory-scaffold rejects translations that lose ≥40% content length, drop >40% of headings, break code-fence count, lose required CLI-parsed keywords (`frequency:`, `last seen:`, `importance:`, `(L4)`), or break YAML frontmatter markers.
1516
+ - **Placeholder substitution safety** — Pass 1 prompt placeholder substitution (`{{DOMAIN_GROUP}}`, `{{PASS_NUM}}`) and `injectProjectRoot`'s `{{PROJECT_ROOT}}` substitution both now use replacement functions so `$`, `$1`, `$&`, `$$` in domain names or project paths are preserved as literal characters rather than interpreted as regex back-references (same bug class as v1.6.x's `replaceFileBlock`).
1517
+ - **Stale `.staged-rules/` from prior crashed runs** — Pass 3 and Pass 4 now wipe any leftover staging directory before running Claude, so a crashed prior run can't smuggle stale rule files into the move step alongside the fresh output.
1518
+ - **Windows shell-escape warning (DEP0190)** — `runClaudePromptAsync` builds the spawn command as a single string with `shell: true` on Windows (so `claude.cmd`/`.ps1` shims resolve via PATH) and as separate args on Unix (no shell), eliminating Node 18+'s deprecation warning about mixing `shell:true` with an args array. Flags are hardcoded literals — no injection surface either way.
1519
+ - **Pass 3 skipped under `--force` / "fresh" resume mode** — The v1.7.x→v2.0.0 backfill guard fired whenever `CLAUDE.md + pass2-merged.json` existed and the `pass3-complete.json` marker was missing, even when the marker was missing *because* `--force` or `"fresh"` had just deleted it. The guard re-wrote the marker, Pass 3 was skipped, and the project was left with a stale `CLAUDE.md` alongside freshly-regenerated `pass1/2` artifacts and wiped `.claude/rules/` — which then failed both `sync-checker` (Master Plan orphans) and `content-validator` (missing sections). `init.js` now tracks a `wasFreshClean` flag set by the `--force` and `"fresh"` cleanup branches and gates the backfill with `!wasFreshClean`, so explicit fresh requests always run Pass 3. The existing guard still covers the intended v1.7.x upgrade path. Regression test added in `tests/pass3-marker.test.js`.
1520
+
1521
+ ### Migration notes
1522
+
1523
+ Existing v1.7.x projects are automatically migrated on the first `v2.0.0` `init` run:
1524
+ - If `CLAUDE.md` and `pass2-merged.json` exist, `pass3-complete.json` is backfilled to preserve the existing `CLAUDE.md`.
1525
+ - `claudeos-core/memory/` and `.claude/rules/60.memory/` are scaffolded by Pass 4 (or static fallback with Claude-driven translation when `--lang` is non-English).
1526
+ - A new `## Memory (L4)` section is appended to the existing `CLAUDE.md`.
1527
+ - No manual steps required.
1528
+ - To force full regeneration, use `npx claudeos-core init --force`. Note that under v2.0.0, `--force` and `"fresh"` resume mode now also wipe `.claude/rules/` and `claudeos-core/generated/.staged-rules/` — manual edits to existing rule files will be lost. Back them up first if needed.
1529
+
1530
+ ### Known constraints
1531
+
1532
+ - **`claude` CLI is now a hard requirement for non-English languages.** v1.7.x silently fell back to English when translation failed; v2.0.0 throws `InitError` instead. If `--lang` is non-`en`, ensure `claude` is installed and authenticated before running `init`. Use `--lang en` to bypass the translation requirement.
1533
+ - **`.claude/rules/` writes from Claude `-p` are blocked by Claude Code's sensitive-path policy.** v2.0.0 works around this with the staged-rules mechanism. If you author custom Pass 3/4 prompts, prepend `pass-prompts/templates/common/staging-override.md` so writes are redirected to the staging dir.
1534
+ - **`CLAUDEOS_SKIP_TRANSLATION=1` is a test-only escape hatch.** It short-circuits `translateIfNeeded()` to throw before invoking `claude -p`. If set in your shell accidentally (e.g. leftover from CI/test setup), `init` will fail fast when `--lang` is non-`en`. Remedy: `unset CLAUDEOS_SKIP_TRANSLATION` or run with `--lang en`. CI workflows can set it to keep translation tests deterministic without installing `claude`.
1535
+
1536
+ ## [1.7.1] — 2026-04-11
1537
+
1538
+ ### Added
1539
+
1540
+ - **Java scanner unit tests** — New `tests/scan-java.test.js` with 18 tests covering all 5 patterns (A/B/C/D/E), supplementary scan, skip list, root package extraction, MyBatis XML detection, DDD infrastructure/ detection, and full fallback
1541
+ - **Flask dedicated template** — New `pass-prompts/templates/python-flask/` with pass1/pass2/pass3 prompts tailored for Flask (Blueprint, @app.route, application factory, g/current_app, before_request, WTForms, Flask-SQLAlchemy, Flask-Login, Jinja2); Flask no longer shares python-fastapi template
1542
+ - **FastAPI/Flask flat project fallback** — `scan-python.js` now detects flat projects with `main.py` or `app.py` at root (or `app/main.py`) when no router files or subdomain structure exists; covers FastAPI official tutorial structure
1543
+ - **Vite SPA primary path scanning** — `scan-frontend.js` now detects `src/views/*/`, `src/screens/*/`, `src/routes/*/` in primary scan; Vite SPA projects no longer fall through to Fallback D
1544
+ - **296 tests** (287 → 296) — Added 9 new tests: Flask template selection, flat project fallback (5 cases), Vite SPA primary paths (3 cases)
1545
+
1546
+ ### Fixed
1547
+
1548
+ - **Java scanner Windows path normalization** — `scan-java.js` added `norm()` function and `.map(norm)` to 9 glob calls; regex matching failed on Windows backslash paths for Pattern E (DDD/Hexagonal), root package extraction, and supplementary scan
1549
+ - **Pattern E missing infrastructure/ detection** — `scan-java.js` Pattern E `mprGlob` now includes `{domain}/infrastructure/*.java` in addition to `adapter/out/{persistence,repository}/`
1550
+ - **Flask misusing FastAPI template** — `selectTemplates()` now routes `framework: "flask"` to dedicated `python-flask` instead of `python-fastapi`
1551
+ - **Completion banner alignment** — `Total time:` label spacing fixed to align with other rows
1552
+
1553
+ ## [1.7.0] — 2026-04-11
1554
+
1555
+ ### Added
1556
+
1557
+ - **Vite SPA support** — Full Vite detection pipeline: `stack-detector.js` detects `vite` from package.json dependencies and `vite.config.ts/js` fallback; `selectTemplates()` routes to dedicated `node-vite` template; `determineActiveDomains()` correctly classifies Vite as frontend-only
1558
+ - **`node-vite` template** — New `pass-prompts/templates/node-vite/` with pass1/pass2/pass3 prompts tailored for Vite SPA (client-side routing, VITE_ env prefix, Vitest, static hosting deployment — no RSC/Server Actions/next.config)
1559
+ - **Non-standard nested path scanning** — `scan-frontend.js` now detects pages, components, and FSD layers under `src/*/` paths (e.g., `src/admin/pages/dashboard/`, `src/admin/components/form/`, `src/admin/features/billing/`)
1560
+ - **No-hallucination guardrail** — `pass3-footer.md` enforces that Pass 3 may only reference technologies explicitly present in `project-analysis.json` or `pass2-merged.json`; inference from other detected libraries is prohibited
1561
+ - **Skill orchestrator completeness guardrail** — `pass3-footer.md` enforces that orchestrator execution tables must list all sub-skill files with no gaps in the sequence
1562
+ - **Progress bar with ETA** — Pass 1/2/3 execution now shows a progress bar with percentage, elapsed time, and estimated remaining time based on average step duration
1563
+ - **Angular/Next.js default ports** — `defaultPort` logic now assigns 4200 for Angular and 3000 for frontend-only Next.js projects
1564
+ - **Enriched Node.js scanner** — `scan-node.js` now classifies entities, modules, guards, pipes, and interceptors (NestJS-aware) in addition to controllers/services/dtos
1565
+ - **Enriched Python scanner** — `scan-python.js` now classifies admin, forms, urls, and tasks (Django/Celery-aware) in addition to views/models/serializers
1566
+ - **Fastify handler detection** — `scan-node.js` now counts `handler` files as controllers alongside controller/router/route
1567
+
1568
+ ### Fixed
1569
+
1570
+ - **Vite SPA misclassified as Next.js** — `selectTemplates()` now routes `frontend: "react"` + `framework: "vite"` to `node-vite` instead of `node-nextjs`
1571
+ - **Vite incorrectly assigned backend template** — Backend template fallback (`node-express`) now excludes `framework: "vite"`
1572
+ - **Vite SPA marked as backend project** — `determineActiveDomains()` now excludes `framework: "vite"` from backend activation
1573
+ - **Vite default port** — Port 5173 assigned for Vite instead of falling back to 8080
1574
+ - **Vite triggers unnecessary backend scan** — `structure-scanner.js` now skips Node.js backend scanning when `framework: "vite"`
1575
+ - **Frontend-only security-db activation** — `determineActiveDomains()` now activates `30.security-db` for frontend-only projects (auth/token/XSS standards are relevant); previously required a backend framework
1576
+ - **FSD glob deduplication** — `scan-frontend.js` FSD layer scanning now uses Set-based deduplication matching the existing components pattern
1577
+ - **269 tests** (256 → 269) — Added 13 new tests for Vite detection, template selection, non-standard paths, and active domain classification
1578
+
1579
+ ## [1.6.2] — 2026-04-09
1580
+
1581
+ ### Fixed
1582
+
1583
+ - **Sync command crash bypass** — `cli.js` sync throw from `cmdHealth`/`cmdValidate`/`cmdRestore`/`cmdRefresh` now correctly caught by `.catch()` handler; previously caused unhandled exception
1584
+ - **`init.js` group.domains crash** — Null guard added for `group.domains` and `group.estimatedFiles` in domain-groups iteration; prevents TypeError on malformed `domain-groups.json`
1585
+ - **Kotlin shared query resolution failure** — `scan-kotlin.js` full key (`__` separator) module names now converted back to path form (`/`) before file matching; `resolveSharedQueryDomains` was silently failing to find any files
1586
+ - **Python scanner Windows glob failure** — `scan-python.js` added `dir.replace(/\\/g, "/")` for Django and FastAPI/Flask glob patterns; Windows `path.dirname` returns backslashes that break glob (same fix `scan-node.js` already had)
1587
+ - **`prompt-generator.js` langData.labels crash** — Added null guard for `langData.labels` access; prevents TypeError when `lang-instructions.json` has `instructions` but missing `labels` key
1588
+ - **Plan parser heading description leakage** — `plan-parser.js` `parseCodeBlocks` now strips trailing ` — description` / ` – description` / ` - description` from heading; previously included in `filePath`
1589
+ - **Content validator regex escape** — `content-validator/index.js` regex character class now correctly escapes `[` and `]`; previously `[` was unescaped, causing runtime error when keyword contains `[`
1590
+ - **Manifest generator CODE_BLOCK_PLANS count** — `plan-manifest.json` now uses `extractCodeBlockPathsFromFile` for code-block-format plans (e.g., `21.sync-rules-master.md`); `fileBlocks` count was always 0
1591
+ - **Resume pass1/pass2 inconsistency** — When "continue" is selected but no pass1 files exist while pass2 does, pass2 is now deleted to force re-run; previously new pass1 + stale pass2 caused data mismatch
1592
+ - **`--force` incomplete cleanup** — Now deletes all `.json` and `.md` files in `generated/` directory (not just pass1/pass2); ensures truly fresh start including stale prompts, manifests, and reports
1593
+ - **Workspace path without wildcard** — `stack-detector.js` now handles concrete workspace paths (e.g., `packages/backend`) by scanning both direct and child `package.json` files; previously only glob patterns with `*` worked
1594
+ - **Framework-less Python projects skipped** — `structure-scanner.js` now triggers Python scanner for all `language === "python"` projects; previously required `framework` to be `django`/`fastapi`/`flask`
1595
+ - **Root directory router.py false domain** — `scan-python.js` now skips `name === "."` when `router.py` is in project root; previously created a domain named `.`
1596
+ - **Sync checker null sourcePath** — `sync-checker/index.js` now skips mappings with null/undefined `sourcePath`; previously produced `path.join(ROOT, undefined)` = `"ROOT/undefined"`
1597
+ - **Java Pattern B/D detection instability** — `scan-java.js` `detectedPattern` now determined by majority vote across all domains; previously depended on first `Object.keys` insertion order
1598
+ - **Duplicate pass1 prompt overwrite** — `prompt-generator.js` deduplicates `activeTemplates` via `Set`; when backend and frontend share the same template, pass1 is generated once instead of being overwritten
1599
+ - **Health checker stale-report overwrite** — Removed redundant `generatedAt` write that was overwriting `manifest-generator`'s `summaryPatch`; manifest-generator (run as prerequisite) already sets this key
1600
+ - **Plan validator empty file creation** — `--execute` mode now skips file creation when plan block has empty/whitespace-only content; previously created blank files
1601
+
1602
+ ## [1.6.1] — 2026-04-09
1603
+
1604
+ ### Fixed
1605
+
1606
+ - **Path traversal hardening (Windows)** — `plan-validator` and `sync-checker` now use case-insensitive path comparison on Windows, preventing UNC/case-mismatch bypass of root boundary check
1607
+ - **Null pointer crash in `stack-detector.js`** — `readFileSafe()` return value for `pnpm-workspace.yaml` now guarded; prevents crash when file exists but is unreadable
1608
+ - **Empty pass3 prompt generation** — `prompt-generator.js` now early-returns with warning when pass3 template is missing, instead of silently writing header+footer-only prompt
1609
+ - **Domain group boundary off-by-one** — `splitDomainGroups` changed `>=` to `>` for file count threshold; groups now fill up to exactly `MAX_FILES_PER_GROUP` (40) instead of flushing one file early
1610
+ - **Perl regex injection in `bootstrap.sh`** — All placeholder substitution migrated from `perl -pi -e` to Node.js `String.replace()`; eliminates regex special character risk in domain names; `perl` is no longer a prerequisite
1611
+ - **Flask default port** — `plan-installer` now maps Flask to port 5000 (was falling through to 8080)
1612
+ - **Health-checker dependency chain** — `sync-checker` is now automatically skipped when `manifest-generator` fails, instead of running against missing `sync-map.json`
1613
+ - **`pass-json-validator` null template crash** — Added null guard before `typeof` check; `null` no longer passes `typeof === "object"` gate
1614
+ - **`pass-json-validator` missing backend frameworks** — Added `"fastify"` and `"flask"` to backend framework list; these stacks previously skipped backend section validation
1615
+ - **Init error messages** — Pass 1/2/3 failure messages now include actionable guidance (check output above, retry with `--force`, verify prompt file)
1616
+ - **Manifest-generator error context** — `.catch()` handler now prefixes error with tool name
1617
+ - **Line counting off-by-one** — `statSafe()` and `manifest-generator stat()` no longer count trailing newline as an extra line
1618
+ - **Windows CRLF drift** — `plan-validator` now normalizes `\r\n` → `\n` before content comparison; prevents false drift on Windows
1619
+ - **`stale-report.js` mutation** — `Object.assign(ex.summary, patch)` replaced with spread operator to avoid in-place mutation
1620
+ - **Undefined in sync-checker Set** — Malformed mappings with missing `sourcePath` no longer insert `undefined` into the registered paths Set
1621
+ - **BOM frontmatter detection** — `content-validator` now strips UTF-8 BOM (`\uFEFF`) before checking `---` frontmatter marker
1622
+ - **Health-checker stderr loss** — Error output now combines both `stdout` and `stderr` instead of preferring one
1623
+ - **`bootstrap.sh` exit code preservation** — EXIT trap now captures and restores `$?` instead of always exiting 0
1624
+ - **`bootstrap.sh` NODE_MAJOR stderr** — `node -e` stderr redirected to `/dev/null` to prevent parse failure from noise
1625
+
1626
+ ## [1.6.0] — 2026-04-08
1627
+
1628
+ ### Added
1629
+
1630
+ - **JS/TS monorepo support** — Auto-detect `turbo.json`, `pnpm-workspace.yaml`, `lerna.json`, `package.json#workspaces`; scan sub-package `package.json` for framework/ORM/DB dependencies; domain scanning covers `apps/*/src/` and `packages/*/src/` patterns
1631
+ - **NestJS dedicated template (`node-nestjs`)** — Separate analysis prompts for `@Module`, `@Injectable`, `@Controller`, Guards, Pipes, Interceptors, DI container, CQRS, `Test.createTestingModule`; previously shared `node-express` template
1632
+ - **Vue/Nuxt dedicated template (`vue-nuxt`)** — Separate analysis prompts for Composition API, `<script setup>`, Pinia, `useFetch`/`useAsyncData`, Nitro server routes, `@nuxt/test-utils`; previously shared `node-nextjs` template
1633
+ - **Elapsed time tracking** — CLI shows per-pass elapsed time and total time in completion banner
1634
+ - **169 new tests** (87 → 256) — Full coverage for `scan-frontend.js` (4-stage fallback), `scan-kotlin.js` (CQRS, shared query resolution), `scan-node.js`, `scan-python.js`, `prompt-generator.js` (multi-stack), `lang-selector.js`, `resume-selector.js`, `init.js`, `plan-parser.js`, monorepo detection
1635
+ - **README updates (10 languages)** — Updated all README files (en, ko, zh-CN, ja, es, vi, hi, ru, fr, de) to reflect new stacks table (NestJS/Vue split), monorepo root execution, facade/usecase/orchestrator detection, template structure, 3 new FAQ entries, 256 test count
1636
+
1637
+ ### Fixed
1638
+
1639
+ - **Windows backslash glob in `scan-kotlin.js`** — glob returns backslash paths on Windows, causing multi-module detection to silently fail; added `norm()` normalization (no-op on Unix)
1640
+ - **Kotlin module key collision** — When same module name exists under different parents (e.g., `servers/command/api-server` + `servers/query/api-server`), both entries now upgrade to full key; `domainMap` merges counts instead of overwriting
1641
+ - **Java facade/usecase/orchestrator detection** — `scan-java.js` now detects `facade/`, `usecase/`, `orchestrator/` directories as service-layer (previously only `aggregator/`)
1642
+ - **Verification tools exit code** — 4 tools (`content-validator`, `plan-validator`, `sync-checker`, `pass-json-validator`) now exit(1) on unexpected errors instead of exit(0); `health-checker` wrapped in try/catch
1643
+
1644
+ ### Changed
1645
+
1646
+ - **`lib/plan-parser.js`** (new) — Extracted shared `parseFileBlocks`, `parseCodeBlocks`, `replaceFileBlock`, `replaceCodeBlock`, `CODE_BLOCK_PLANS` from `manifest-generator` and `plan-validator`; eliminates duplicate code across 2 files
1647
+ - **`lib/stale-report.js`** (new) — Extracted shared `updateStaleReport()` from 6 verification tools; eliminates copy-paste pattern
1648
+ - **`cli-utils.js`** — `ensureDir` and `fileExists` now delegate to `lib/safe-fs.js` (single source of truth)
1649
+ - **`prompt-generator.js`** — Removed dead strip regex (no template matched these patterns)
1650
+ - **`init.js` process.exit refactoring** — `process.exit(1)` replaced with `throw InitError`; `lang-selector.js` and `resume-selector.js` return `null` instead of calling `process.exit()`; all errors handled centrally in `cli.js`
1651
+
1652
+ ## [1.5.1] — 2026-04-06
1653
+
1654
+ ### Fixed
1655
+ - **Remove 13 bare catch blocks** — `catch { }` → `catch (_e) { }` across 9 files; enables error variable access during debugging
1656
+ - **Windows backslash glob fix (3 locations)** — `scan-frontend.js` missing `dir.replace(/\\/g, "/")` at App/Pages Router (line 63), FSD (line 84), and components (line 98) scans; other locations already had this fix
1657
+ - **Pattern C flat MyBatis XML detection** — `scan-java.js` xmlGlob now matches flat XML layout (e.g., `mapper/OrderMapper.xml`) in addition to domain subdirectory layout for Pattern C projects
1658
+ - **Next.js reserved segment false positives** — Added `not-found`, `error`, `loading` to `skipPages` in `scan-frontend.js` to prevent Next.js App Router reserved directories from being detected as domains
1659
+ - **cap variable shadowing** — Renamed outer-scope `cap` to `capDn` in `scan-java.js` to avoid shadowing the block-scoped `cap` in Pattern C branch
1660
+
1661
+ ### Changed
1662
+ - **Gradle DB detection comment** — Added 2-line comment explaining postgres/sqlite exclusion rationale in `stack-detector.js` line 118
1663
+
1664
+ ## [1.5.0] — 2026-04-05
908
1665
  - feat: initial release claudeos-core v1.5.0