claudeos-core 2.3.1 → 2.4.0

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 (43) hide show
  1. package/CHANGELOG.md +1460 -73
  2. package/CODE_OF_CONDUCT.md +15 -0
  3. package/README.de.md +321 -883
  4. package/README.es.md +322 -883
  5. package/README.fr.md +322 -883
  6. package/README.hi.md +322 -883
  7. package/README.ja.md +322 -883
  8. package/README.ko.md +322 -882
  9. package/README.md +321 -883
  10. package/README.ru.md +322 -885
  11. package/README.vi.md +322 -883
  12. package/README.zh-CN.md +321 -881
  13. package/SECURITY.md +51 -0
  14. package/bin/commands/init.js +570 -264
  15. package/content-validator/index.js +185 -12
  16. package/health-checker/index.js +44 -10
  17. package/package.json +92 -90
  18. package/pass-json-validator/index.js +58 -7
  19. package/pass-prompts/templates/angular/pass3.md +15 -14
  20. package/pass-prompts/templates/common/claude-md-scaffold.md +203 -20
  21. package/pass-prompts/templates/common/pass3-footer.md +297 -56
  22. package/pass-prompts/templates/common/pass3a-facts.md +48 -3
  23. package/pass-prompts/templates/common/pass4.md +78 -40
  24. package/pass-prompts/templates/java-spring/pass1.md +54 -0
  25. package/pass-prompts/templates/java-spring/pass3.md +20 -19
  26. package/pass-prompts/templates/kotlin-spring/pass1.md +45 -0
  27. package/pass-prompts/templates/kotlin-spring/pass3.md +24 -23
  28. package/pass-prompts/templates/node-express/pass3.md +18 -17
  29. package/pass-prompts/templates/node-fastify/pass3.md +11 -10
  30. package/pass-prompts/templates/node-nestjs/pass3.md +11 -10
  31. package/pass-prompts/templates/node-nextjs/pass3.md +18 -17
  32. package/pass-prompts/templates/node-vite/pass3.md +11 -10
  33. package/pass-prompts/templates/python-django/pass3.md +18 -17
  34. package/pass-prompts/templates/python-fastapi/pass3.md +18 -17
  35. package/pass-prompts/templates/python-flask/pass3.md +9 -8
  36. package/pass-prompts/templates/vue-nuxt/pass3.md +9 -8
  37. package/plan-installer/domain-grouper.js +45 -5
  38. package/plan-installer/index.js +34 -1
  39. package/plan-installer/pass3-context-builder.js +14 -0
  40. package/plan-installer/scanners/scan-frontend.js +2 -1
  41. package/plan-installer/scanners/scan-java.js +98 -2
  42. package/plan-installer/source-paths.js +242 -0
  43. package/plan-installer/stack-detector.js +522 -42
@@ -259,14 +259,30 @@ Do NOT declare CLAUDE.md generation complete until count = 8 AND
259
259
  Section 8 contains exactly 2 `###` sub-sections.
260
260
 
261
261
  STEP 4b — Title determinism check. For each of the 8 `## N.` headings,
262
- confirm that the English canonical phrase is present as the primary
263
- heading text (a native-language translation may follow in parentheses).
264
- This rule is LANGUAGE-INVARIANT — applied regardless of `{OUTPUT_LANG}`:
265
-
266
- `## 7. DO NOT Read`
267
- `## 7. DO NOT Read (직접 읽지 말아야 파일)`
268
- ❌ `## 7. 읽지 (Files Not to Be Read Directly)`
269
- — English canonical must be PRIMARY, not parenthetical
262
+ confirm that:
263
+ (a) the English canonical phrase is present as the primary heading text
264
+ (LANGUAGE-INVARIANT — applied regardless of `{OUTPUT_LANG}`), AND
265
+ (b) if `{OUTPUT_LANG}` != `en`, the canonical native-language gloss is
266
+ appended in parentheses (REQUIRED, not optional). If `{OUTPUT_LANG}`
267
+ == `en`, no gloss is emitted. The canonical gloss table lives in
268
+ `claude-md-scaffold.md` under "Section heading format".
269
+
270
+ en output:
271
+ ✅ `## 7. DO NOT Read`
272
+ ❌ `## 7. DO NOT Read (Files Not to Be Read Directly)`
273
+ — no gloss when OUTPUT_LANG == en
274
+
275
+ ko output:
276
+ ✅ `## 7. DO NOT Read (직접 읽지 말아야 할 파일)`
277
+ ❌ `## 7. DO NOT Read`
278
+ — gloss is required when OUTPUT_LANG != en
279
+ ❌ `## 7. 읽지 말 것 (Files Not to Be Read Directly)`
280
+ — English canonical must be PRIMARY, not parenthetical
281
+
282
+ ja output:
283
+ ✅ `## 7. DO NOT Read (直接読まないファイル)`
284
+ ❌ `## 7. DO NOT Read`
285
+ — gloss is required when OUTPUT_LANG != en
270
286
 
271
287
  Required canonical tokens by section number:
272
288
  §1 → "Role Definition"
@@ -300,71 +316,192 @@ write in rules, standard, or CLAUDE.md MUST be a literal, verbatim path
300
316
  from `pass2-merged.json` / `project-analysis.json` / `pass1-*.json`.
301
317
  Do not invent, augment, or "normalize" filenames.
302
318
 
303
- The single most common Pass 3 hallucination pattern is inferring a
304
- filename from its parent directory:
319
+ **PRIMARY CHECK pass3a-facts.md's Allowed Source Paths section**
320
+ **(v2.3.x+ allowlist):**
321
+
322
+ Before writing ANY `src/...`, `packages/...`, `apps/...`, or
323
+ language-specific source path in a rule or standard file, you MUST
324
+ verify the path appears VERBATIM in the `## Allowed Source Paths`
325
+ section of `pass3a-facts.md`. If the section is in "full" mode (lists
326
+ individual files), the exact filename must be present. If the section
327
+ is in "rollup" mode (lists directories because the project exceeds the
328
+ enumeration budget), at minimum the path's parent directory must match
329
+ a listed directory entry — and for the specific filename you must
330
+ cross-check `pass2-merged.json` before writing it.
331
+
332
+ A path NOT in the allowlist is a fabrication. Do not write it under
333
+ any circumstance. Not even when:
334
+ - The framework's documentation shows that path as "canonical" — this
335
+ covers Next.js app-router provider files, Vite test-setup or mock
336
+ handler modules, Spring resource config files, Django settings
337
+ modules, and similar framework-canonical locations. These paths
338
+ may be canonical in framework docs yet absent from THIS project's
339
+ allowlist because the project chose a different layout; citing
340
+ the canonical form is still a fabrication. (Do NOT quote the
341
+ specific canonical paths here — describe the category abstractly
342
+ if you must warn about it.)
343
+ - A similar path appears in the allowlist and you are tempted to
344
+ "extrapolate" a sibling — for example, if the allowlist has a
345
+ list-operation file under some API directory, do NOT cite a
346
+ hypothetical create-operation file in the same directory just
347
+ because it would be a natural sibling. Cite only what the
348
+ allowlist actually contains.
349
+ - A TypeScript constant, Java annotation, or Python decorator name
350
+ suggests a filename (an ALL_CAPS constant naming a route path does
351
+ NOT mean a matching camelCase-basename .ts file exists; this was
352
+ the canonical v2.3.0 failure case and must remain a category
353
+ warning, not a filename warning).
354
+
355
+ If the concept you want to describe has no corresponding file in the
356
+ allowlist, either:
357
+ (a) describe the pattern abstractly ("a central router module"
358
+ rather than a fabricated concrete filename), OR
359
+ (b) omit the concrete citation entirely.
305
360
 
306
- Directory seen in facts: src/feature/routers/
307
- File seen in facts: src/feature/routers/routePath.ts
308
- src/feature/routers/routeComponentMap.ts
361
+ This rule is enforced post-generation by `content-validator [10/10]
362
+ path-claim verification`. Fabricated paths will be flagged as
363
+ `STALE_PATH` advisories. The allowlist exists so that those advisories
364
+ become rare — not so they become suppressible.
309
365
 
310
- ❌ WRONG: write `src/feature/routers/featureRoutePath.ts`
311
- (prepended "feature" because the dir is `feature/`)
312
- ❌ WRONG: write `src/feature/routers/FEATURE_COMPONENT_MAP.ts`
313
- (uppercased to match the TS constant name)
314
- ✅ RIGHT: write `src/feature/routers/routePath.ts`
366
+ **Secondary: filename-from-parent-directory hallucination:**
315
367
 
316
- Mechanism of the error: the model sees a constant named
317
- `FEATURE_ROUTE_PATH` (a TypeScript identifier) and "renormalizes" the
318
- filename to match it. TypeScript identifiers and filenames are
319
- independent do NOT let the constant's name drive the filename you
320
- write. The filename in `pass2-merged.json` is authoritative.
368
+ The single most common Pass 3 hallucination pattern is inferring a
369
+ filename from its parent directory. Consider a hypothetical project
370
+ where pass3a-facts.md lists a directory `src/feature/Xxx/` containing
371
+ files `Yyy.ts` and `Zzz.ts`:
372
+
373
+ Directory seen in facts: src/feature/Xxx/
374
+ File seen in facts: src/feature/Xxx/Yyy.ts
375
+ src/feature/Xxx/Zzz.ts
376
+
377
+ ❌ WRONG: invent a new filename by prepending the parent directory
378
+ name (writing `src/feature/Xxx/featureYyy.ts` because the
379
+ parent dir is named `feature/`).
380
+ ❌ WRONG: uppercase the filename to match a TypeScript constant
381
+ name (writing `src/feature/Xxx/YYY_MAP.ts` because the
382
+ module exports a constant named `YYY_MAP`).
383
+ ✅ RIGHT: write the filename exactly as pass3a-facts.md lists it
384
+ (`src/feature/Xxx/Yyy.ts`).
385
+
386
+ Mechanism of the error: the model sees an ALL_CAPS TypeScript
387
+ constant name and "renormalizes" the filename to match it. TypeScript
388
+ identifiers and filenames are independent — do NOT let the constant's
389
+ name drive the filename you write. The filename in
390
+ `pass2-merged.json` is authoritative.
321
391
 
322
392
  Same rule applies to domain prefixes in general:
323
- - `src/feature/X.ts` is the correct reference even when the file's
393
+ - `src/feature/Xxx.ts` is the correct reference even when the file's
324
394
  exported symbols start with `Feature*` or `FEATURE_*`.
325
395
  - Do NOT prepend the parent directory name to the filename as a
326
396
  disambiguator. The directory path is already the scope.
327
397
 
328
398
  Library-convention hallucination class (equally important):
329
399
 
330
- When generating a standard file about testing, mocking, styling,
331
- state management, or similar library-centric topics, do NOT reach
332
- for "the canonical file location" from the library's own docs. There
333
- is no canonical location every project chooses its own.
334
-
335
- ❌ WRONG: writing `testing-strategy.md` and referencing
336
- `src/__mocks__/handlers.ts`, `src/test/setup.ts`,
337
- `src/setupTests.ts`, or `src/test-utils.tsx` because MSW,
338
- Vitest, or Jest docs show these paths.
339
- WRONG: writing `styling-patterns.md` and referencing a
340
- `src/styles/globals.css` or `src/theme.ts` because the
341
- framework's quick-start uses them.
342
-
343
- RIGHT: if pass3a-facts.md lists no test setup file (e.g.,
344
- the project has 0% coverage with vitest installed but
345
- no tests), write the testing guidance in abstract terms
346
- ("a shared setup module in a test directory of your
347
- choice") without naming a specific path.
348
- RIGHT: for any library-centric standard, if the specific
349
- file is not in pass3a-facts.md, describe the pattern
350
- by role (what the file does) rather than by name (what
351
- it is called).
352
-
353
- Critical triggers for this class:
354
- - `testing-strategy.md` / `testing-patterns.md` almost always
355
- tries to name a mock handler file or test setup file.
356
- - `styling-patterns.md` almost always tries to name a global
357
- stylesheet or theme file.
358
- - `state-management.md` almost always tries to name a store
359
- bootstrap file (Redux, Zustand, etc.) even when the project
360
- uses only React Context.
400
+ When discussing testing, mocking, styling, state management, environment
401
+ configuration, or similar library-centric topics in ANY generated file
402
+ (standard, rule, skill, guide not just files named for the topic), do
403
+ NOT reach for "the canonical file location" from the library's own docs.
404
+ There is no canonical location — every project chooses its own.
405
+
406
+ **Scope note (v2.3.2+):** this constraint applies to EVERY file being
407
+ generated, not only to files whose names match the topic. A rule file
408
+ called `52.ai-work-rules.md` discussing test-running protocol can still
409
+ trip this trap; an infra file `01.environment-config.md` discussing env
410
+ typing can still trip it. The trigger is **the topic being described**,
411
+ not the filename of the document describing it.
412
+
413
+ Guidance (by topic):
414
+
415
+ - **Testing / mocking.** If pass3a-facts.md lists no test setup file
416
+ (e.g., the project has 0% coverage with vitest installed but no
417
+ tests), write the testing guidance in abstract terms ("a shared setup
418
+ module in a test directory of your choice") without citing a
419
+ framework-canonical path.
420
+ - **Environment typing.** If no `.d.ts` file appears in the allowlist,
421
+ describe the `ImportMetaEnv` interface inline ("augment `ImportMetaEnv`
422
+ in a type-declaration file of your project's choosing") without
423
+ naming a specific filename.
424
+ - **Styling / theming / state management.** If the specific file is
425
+ not in pass3a-facts.md, describe the pattern by role (what the file
426
+ does) rather than by name (what it is called).
427
+
428
+ **Importanthow to write examples in rules (v2.3.2+):** rule files
429
+ (especially `52.ai-work-rules.md` and similar "meta" rules) sometimes
430
+ need to illustrate bad path habits as educational examples. When they
431
+ do, write the illustrative paths as **abstract placeholders** so the
432
+ validator recognizes them as teaching examples, not as concrete path
433
+ claims:
434
+
435
+ - Use generic directory references with a glob or ellipsis:
436
+ `src/test/…`, `src/types/*.d.ts`, `src/*/mocks/…`
437
+ - Use the `{placeholder}` form the validator already skips:
438
+ `src/{feature}/api/{Entity}.ts`, `src/hooks/use{Domain}.ts`
439
+ - Use the prose form: "a mock handler file under a `__mocks__/`
440
+ directory of your choice" rather than a concrete filename.
441
+
442
+ Do NOT cite a specific literal path (e.g., naming an exact file and
443
+ extension that happens to be the library's canonical location) in an
444
+ educational example. Literal paths are interpreted as real claims by
445
+ `content-validator [10/10]` regardless of surrounding prose. If the
446
+ example you want to give MUST be literal, add it to pass3a-facts.md
447
+ first (i.e., only literal paths that actually exist in the project).
448
+
449
+ **Hypothetical / future-tense framing is NOT a loophole (v2.3.2+):**
450
+ wrapping a framework-canonical path in conditional or future-tense
451
+ language (`if we adopted X`, `were this feature introduced, it would
452
+ live at …`, `for a future Y`, `when Z is added later`, etc., in ANY
453
+ output language) does NOT make the literal path safe. The validator
454
+ is content-blind and will flag
455
+ ``if middleware is added later, place it at `src/middleware.ts` `` as
456
+ a `STALE_PATH` advisory because `src/middleware.ts` does not exist on
457
+ disk. This is the Next.js / Vite / Spring blind spot: the LLM, when
458
+ discussing future extensions, reaches for the framework's canonical
459
+ path as the "natural" location and writes it verbatim.
460
+
461
+ The correct hypothetical form describes the ROLE or DIRECTORY without
462
+ committing to a filename:
463
+
464
+ - ❌ WRONG: "If middleware is added, place it at `src/middleware.ts`."
465
+ - ❌ WRONG: "For a health endpoint in the future,
466
+ `src/app/api/health/route.ts`."
467
+ - ✅ RIGHT: "If middleware is added later, place it at the path
468
+ Next.js's routing convention expects for the version in use
469
+ (describe the role; do not cite a literal filename)."
470
+ - ✅ RIGHT: "A future health endpoint belongs under the project's
471
+ API route directory structure (see Next.js documentation for the
472
+ routing convention — do not assume a specific filename until the
473
+ file exists)."
474
+ - ✅ RIGHT: "If an env-typing file is introduced, augment
475
+ `ImportMetaEnv` in a type-declaration file placed under the
476
+ project's chosen convention."
477
+
478
+ In every case, if you cannot name the role + directory without
479
+ committing to a specific `src/...` path that does NOT appear in
480
+ pass3a-facts.md, OMIT the example entirely. An omitted example is
481
+ better than a fabricated path that downstream readers may treat as
482
+ authoritative. This rule applies regardless of the output language:
483
+ translated conditional phrases (Korean, Japanese, Chinese, etc.) do
484
+ not change the validator's literal-path detection — use a placeholder
485
+ form or omit.
486
+
487
+ Critical triggers for this class (the **topic**, not the filename):
488
+ - Testing / mocking / test-runner protocol — almost always tempts a mock
489
+ handler file or test setup file, even in AI-work or onboarding rules.
490
+ - Environment configuration / env typing — almost always tempts a
491
+ `.d.ts` file for `ImportMetaEnv` augmentation.
492
+ - Styling / theming — almost always tempts a global stylesheet or
493
+ theme file.
494
+ - State management — almost always tempts a store bootstrap file
495
+ (Redux, Zustand, etc.) even when the project uses only React Context.
361
496
 
362
497
  The rule in all cases: if pass3a-facts.md has the concrete path,
363
498
  use it verbatim; if not, describe the role without naming a file.
364
499
 
365
500
  This rule is enforced post-generation by `content-validator [10/10]
366
501
  path-claim verification`. Fabricated paths will be flagged as
367
- `STALE_PATH` errors with the path quoted back, forcing re-generation.
502
+ `STALE_PATH` advisories with the path quoted back; the allowlist and
503
+ this explicit denylist exist so that those advisories become rare —
504
+ not so they become suppressible.
368
505
 
369
506
  CRITICAL — MANIFEST ↔ CLAUDE.md §6 Skills consistency:
370
507
 
@@ -398,5 +535,109 @@ level (the scaffold's PROJECT_CONTEXT mentions architecture style),
398
535
  but it does NOT enumerate rules. Section 6 "Standard / Rules / Skills
399
536
  Reference" provides a REFERENCE INDEX (what exists), not rule content.
400
537
 
538
+ CRITICAL — Standard files MUST include both ✅ and ❌ examples:
539
+ Every file under `claudeos-core/standard/**/*.md` (except pure index/overview
540
+ files like `00.core/01.project-overview.md`) MUST contain BOTH:
541
+ - At least one ✅ Correct example (a fenced code block showing the right way)
542
+ - At least one ❌ Incorrect example (a fenced code block showing the wrong way)
543
+
544
+ Skipping the ❌ block is a common Pass 3b LLM failure mode that produces
545
+ `[NO_BAD_EXAMPLE]` advisories from `content-validator`. Even if you can only
546
+ think of a minimal contrastive ❌ (a one-line wrong call, a missing
547
+ annotation, a typo'd config key), include it. The contrast is what gives
548
+ the standard its didactic value — a file with only ✅ examples reads as
549
+ "this is one way to do it", whereas ✅+❌ reads as "this is the way; here is
550
+ the failure mode".
551
+
552
+ Self-check before finalizing each standard file:
553
+ - Does this file have at least one ```...``` block marked ✅?
554
+ - Does this file have at least one ```...``` block marked ❌?
555
+ If either is missing, add it before moving on.
556
+
557
+ CRITICAL — Per-domain folder convention (`70.domains/{type}/`, plural):
558
+
559
+ When generating per-domain files (any output that's specific to one
560
+ project domain — e.g. `payment.md`, `order.md`, `member.md`), use the
561
+ canonical `70.domains/` folder (PLURAL, collection-style) and ALWAYS
562
+ include a `{type}/` sub-folder (`backend/` or `frontend/`) regardless
563
+ of single-stack or multi-stack project. Uniform-convention rationale
564
+ in the next paragraph.
565
+
566
+ - Per-domain standard:
567
+ `claudeos-core/standard/70.domains/{type}/{domain}.md`
568
+ where `{type}` is `backend` or `frontend`
569
+ - Per-domain rule:
570
+ `.claude/rules/70.domains/{type}/{domain}-rules.md`
571
+ (via staging-override path
572
+ `claudeos-core/generated/.staged-rules/70.domains/{type}/{domain}-rules.md`,
573
+ each rule file MUST have a `paths:` frontmatter glob scoping to that
574
+ domain's source directories)
575
+
576
+ **Why ALWAYS the `{type}/` sub-folder, even for single-stack projects?**
577
+ A single-stack project pays a 1-folder depth cost
578
+ (`70.domains/backend/order.md` instead of `70.domains/order.md`). In
579
+ exchange:
580
+
581
+ 1. **Zero migration when the other stack is added.** A backend-only
582
+ project that later adds a Next.js frontend does NOT need to move
583
+ existing files to `backend/` — they're already there.
584
+ 2. **No LLM probabilistic drift.** Pass 3 LLM never has to decide
585
+ "is this single-stack or multi?" — the pattern is always the
586
+ same.
587
+ 3. **One pattern for validators.** `content-validator` and
588
+ `claude-md-validator` recognize a single layout instead of
589
+ branching.
590
+ 4. **Future-proof for new stack types** (mobile, cli, agent, ...).
591
+
592
+ The Pass 3 orchestrator (`bin/commands/init.js`) classifies each
593
+ domain via `project-analysis.json` and emits per-domain target paths
594
+ explicitly in the batch scope note. **Follow the explicit
595
+ per-domain target paths shown in the scope note** rather than infer
596
+ from the domain name. If the scope note shows
597
+ `order → claudeos-core/standard/70.domains/backend/order.md`, that's
598
+ the path to use — the type sub-folder is already classified for you.
599
+
600
+ - Per-domain skill notes:
601
+ `claudeos-core/skills/{category}/domains/{domain}.md`
602
+ (sub-folder under skill category — `skills/` is a separate namespace
603
+ from standard/rules, so no number prefix here. The `{category}`
604
+ folder name already encodes stack: `10.backend-crud/`,
605
+ `20.frontend-page/`. So skills don't need the additional `{type}/`
606
+ sub-folder that standard/rules use.)
607
+ - Per-domain skill ORCHESTRATOR (sibling to the `domains/` sub-folder):
608
+ `claudeos-core/skills/{category}/02.domains.md`
609
+ The orchestrator is REQUIRED when the `domains/` sub-folder is
610
+ populated. It mirrors the canonical pattern already used for
611
+ `01.scaffold-crud-feature.md` ↔ `scaffold-crud-feature/` (orchestrator
612
+ file at category root + sub-folder of the same stem). The basename
613
+ stem (`domains`) MUST match the sub-folder name so
614
+ `content-validator`'s standard orchestrator-stem matching covers
615
+ the sub-skills directly — without depending on the
616
+ global-MANIFEST coverage fallback. The orchestrator content lists
617
+ every per-domain note file in the `domains/` sub-folder, links to
618
+ `00.shared/MANIFEST.md`, and describes the per-domain anti-pattern
619
+ catalog if applicable. Numbered `02.` because `01.scaffold-*-feature.md`
620
+ already occupies the `01.` slot at the category root.
621
+
622
+ Why `70.` and not `60.`: `60.memory/*` is the canonical L4 memory rules
623
+ folder (regression-guarded since v2.0.0; cannot move to 70). Putting
624
+ domains at `60.domains/` causes a `60.` prefix collision with
625
+ `60.memory/` that makes directory listings ambiguous. `70.domains/` sits
626
+ after memory and before `90.optional/`, giving per-domain content its
627
+ own clean slot.
628
+
629
+ Why PLURAL `domains/`: the folder holds N files, one per project domain
630
+ — `payment.md`, `order.md`, etc. This matches the standard filesystem
631
+ convention for collections (`users/user.md`, `posts/post.md`). The other
632
+ numbered category folders (`00.core/`, `10.backend/`, etc.) are singular
633
+ because each represents ONE topic, not a collection of items.
634
+
635
+ DO NOT use:
636
+ - `60.domains/` (collides with `60.memory/`)
637
+ - `70.domain/` (singular is wrong for a collection folder)
638
+ - Inlining per-domain content into `00.core/*` or topical files
639
+ (per-domain content is DOMAIN-scoped, topical files are TOPIC-scoped
640
+ — `paths` glob scoping breaks if they share files)
641
+
401
642
  After completion, run the following commands in order:
402
643
  1. npx claudeos-core health
@@ -124,6 +124,42 @@ IMPORT, not redefine.
124
124
 
125
125
  - ...
126
126
  - ...
127
+
128
+ ## Allowed Source Paths (v2.3.x+ — MANDATORY)
129
+
130
+ Copy the **entire** `allowedSourcePaths` section from `pass3-context.json`
131
+ verbatim into this pass3a-facts.md. Do NOT summarize it, do NOT truncate
132
+ it, do NOT reword it. The shape to copy:
133
+
134
+ - Header: whether the list is in `full` mode (individual file paths) or
135
+ `rollup` mode (parent directories, used when the project exceeds the
136
+ enumeration budget).
137
+ - Body: the exact list of paths (or directories in rollup mode), each
138
+ wrapped in backticks as a markdown bullet.
139
+
140
+ This section is the authoritative allowlist that Pass 3b/3c/3d consult
141
+ when deciding whether a `src/...`, `packages/...`, `apps/...`, or
142
+ language-specific source path may appear in a generated rule or standard
143
+ file. A path not appearing in this allowlist MUST NOT be written by
144
+ later passes — no exceptions, not even for paths that are "obvious" from
145
+ framework convention.
146
+
147
+ Rendering spec:
148
+
149
+ ```markdown
150
+ ## Allowed Source Paths
151
+
152
+ Source files on disk (total: <N>). [full-mode description paragraph from
153
+ pass3-context.json's allowedSourcePaths.paths field, copied verbatim.]
154
+
155
+ - `path/to/file1.ts`
156
+ - `path/to/file2.ts`
157
+ - ... (every entry from pass3-context.json)
158
+ ```
159
+
160
+ If `pass3-context.json`'s `allowedSourcePaths.paths` array is empty
161
+ (collection failed), emit a single line `(allowlist unavailable —
162
+ fall back to pass2-merged.json verification per file)` instead.
127
163
  ```
128
164
 
129
165
  ## Rules for Pass 3a
@@ -131,13 +167,22 @@ IMPORT, not redefine.
131
167
  1. **Read each input file AT MOST ONCE.** After reading, all fact extraction
132
168
  must be from your in-context memory of the file.
133
169
  2. **Be terse.** This document will be loaded into every subsequent Pass 3
134
- step's context. Keep it under 10 KB. Use bullet lists, not prose.
170
+ step's context. Keep the non-allowlist portions under 10 KB. The
171
+ `## Allowed Source Paths` section is exempt from the 10 KB budget
172
+ because it is the authoritative path reference that prevents Pass 3
173
+ hallucination — its size is bounded by the MAX_PATHS (500) / MAX_DIRS
174
+ (300) caps in plan-installer/source-paths.js.
135
175
  3. **Exact values only.** Every class name, method name, package path, and
136
176
  file path must be verbatim from the analysis data. If a value is not
137
177
  captured in the analysis, write `(not in analysis)` — do NOT guess.
138
- 4. **Do NOT write any other files.** CLAUDE.md, standard/, rules/, etc.
178
+ 4. **Allowed Source Paths section is COPIED, not extracted.** Do not apply
179
+ judgment, ranking, or "relevance filtering" to the allowlist. The
180
+ whole point is that Pass 3b/3c/3d get the complete enumeration; any
181
+ path you drop here becomes a path they can fabricate later without
182
+ the downstream validator catching it until after Pass 3 completes.
183
+ 5. **Do NOT write any other files.** CLAUDE.md, standard/, rules/, etc.
139
184
  come in later Pass 3 steps. Writing them here is a bug.
140
- 5. **Do NOT read source code.** All information comes from the three JSON
185
+ 6. **Do NOT read source code.** All information comes from the three JSON
141
186
  inputs. The source has already been analyzed in Pass 1 and Pass 2.
142
187
 
143
188
  Once `pass3a-facts.md` is written, Pass 3a is complete.
@@ -29,48 +29,86 @@ MUST appear verbatim in `pass3a-facts.md` or `pass2-merged.json`. If a
29
29
  concrete path is not in those analysis artifacts, OMIT it — do NOT write it.
30
30
 
31
31
  Do NOT invent paths based on framework convention, prior training knowledge,
32
- or extrapolation from symbol names. The most common v2.3.0 dogfood failures
33
- observed were all in this class:
34
-
35
- `src/feature/main.tsx`
36
- — invented based on Vite's stock convention. This project may use a
37
- different entry (e.g. `index.html` referencing a non-`main.tsx` file),
38
- or may be a multi-entry project where no single `main.tsx` exists.
39
- Check pass3a-facts.md; if no such path is listed, do not write it.
40
-
41
- `src/feature/routers/featureRoutePath.ts`
42
- invented by prepending the parent directory name ("feature") to the
43
- filename, or by converting a TypeScript constant name
44
- (`FEATURE_ROUTE_PATH`) into a filename. TypeScript identifiers and
45
- filenames are independent. The authoritative filename is in
46
- pass3a-facts.md under the "Routing" or "Shared imports" section.
47
- If pass3a-facts.md says `routePath.ts`, write `routePath.ts`, not
48
- `featureRoutePath.ts`.
49
-
50
- `src/components/utils/classNameMaker.ts`
51
- plausibly-named utility, but unverified. "Plausible" is not a
52
- sufficient ground for a concrete path claim. If you want to reference
53
- a utility layer in a rule file, reference the directory
54
- (`src/components/utils/`) rather than inventing a filename.
55
-
56
- `src/__mocks__/handlers.ts`, `src/test/setup.ts`, `src/test-utils.tsx`,
57
- `src/setupTests.ts`
58
- invented based on testing-library conventions (MSW, Vitest, Jest,
59
- React Testing Library). If the project has no tests (0% coverage in
60
- pass3a-facts.md) or uses a different layout, these paths do not
61
- exist. Anti-pattern triggers: writing a `testing-strategy.md` or
62
- similar standard file and reaching for "the canonical test-setup
63
- location" from the library's docs. There is no canonical location —
64
- every project chooses its own, and only pass3a-facts.md knows which.
65
- If pass3a-facts.md lists no test files, write the testing guidance
66
- in general terms ("a shared setup module under a test directory of
67
- your choice") rather than naming a specific path.
68
-
69
- If pass3a-facts.md shows `ApiClient` (`src/admin/api/apiClient.ts`) as
70
- the response wrapper, write `src/admin/api/apiClient.ts` verbatim.
32
+ or extrapolation from symbol names. The three most common hallucination
33
+ failures are described below by MECHANISM (not by literal example path,
34
+ because literal examples in educational prose have historically leaked
35
+ into generated outputs as real claims):
36
+
37
+ **Framework-convention entry-point invention.**
38
+ Citing a framework's stock entry-point file (Vite's main module, Next.js
39
+ app-router files, Spring's default resource config, etc.) because the
40
+ framework docs show that as the canonical location. This project may use
41
+ a different entry, a renamed entry, or a multi-entry layout where no
42
+ single canonical file exists. Check pass3a-facts.md; if no such path is
43
+ listed, do NOT write it.
44
+
45
+ **Parent-directory or constant-name renormalization of a filename.**
46
+ Inventing a filename by prepending the parent directory's name to it
47
+ (the <dirname><actual-basename>.ts anti-pattern), or by converting a
48
+ TypeScript constant / Java annotation / Python decorator name into
49
+ a filename (an ALL_CAPS_CONSTANT becoming a camelCase or PascalCase
50
+ filename). Identifiers and filenames are independent. The
51
+ authoritative filename is whatever pass3a-facts.md lists under the
52
+ relevant section use that verbatim, do not "normalize" it.
53
+
54
+ **Plausibly-named utility invention.**
55
+ Writing a concrete filename for a utility that "would naturally" exist
56
+ under a seen directory (a class-name-builder helper under a `utils/`
57
+ directory, a string-formatter under a `helpers/` directory). "Plausible"
58
+ is not a sufficient ground for a concrete path claim. If you want to
59
+ reference a utility layer in a rule file, reference the directory
60
+ itself rather than inventing a filename.
61
+
62
+ Library-convention canonical paths (testing / env typing / styling /
63
+ state management / routing conventions) invented based on framework
64
+ or library documentation.
65
+ Testing frameworks (MSW, Vitest, Jest, RTL) show canonical mock /
66
+ setup / test-utility locations in their docs; TypeScript + Vite /
67
+ CRA show a canonical `ImportMetaEnv` augmentation location; CSS-in-JS
68
+ and state-management libraries show canonical store / theme
69
+ locations. These are PROJECT-CHOICE files every project picks its
70
+ own structure, and the library's canonical path may not exist here.
71
+ **Scope note (v2.3.2+):** this trap fires whenever the topic of a
72
+ generated file touches these areas (testing, env typing, styling,
73
+ state management), not only when the file is named after the topic.
74
+ It fires in `ai-work-rules.md`, onboarding guides, infra rules,
75
+ CRUD skills — anywhere the topic appears. If pass3a-facts.md lacks
76
+ the concrete file, describe the pattern by role ("a shared setup
77
+ module under a test directory of your choice", "augment
78
+ `ImportMetaEnv` in a type-declaration file of your choosing"), not
79
+ by filename. When illustrating bad path habits as an educational
80
+ example in a rule, use abstract placeholders (`{placeholder}`,
81
+ `src/*/dir/…`) rather than literal paths — literal example paths
82
+ are interpreted as real claims by `content-validator [10/10]`
83
+ regardless of surrounding prose.
84
+
85
+ ❌ **Hypothetical / future-tense framing is NOT a loophole (v2.3.2+).**
86
+ Wrapping a framework-canonical path in conditional or future-tense
87
+ language (`if we adopted X`, `were this feature introduced, it
88
+ would live at …`, `for a future Y`, `when Z is added later`, etc.,
89
+ in ANY output language — translated conditional phrases do not
90
+ change the validator's literal-path detection) does NOT make the
91
+ literal path safe. The validator is content-blind and will flag
92
+ ``if middleware is added later, place it at `src/middleware.ts` ``
93
+ as a `STALE_PATH` advisory because the file does not exist on disk.
94
+ This is the Next.js / Vite / Spring blind spot: the LLM, when
95
+ discussing future extensions, reaches for the framework's canonical
96
+ path as the "natural" location and writes it verbatim. The correct
97
+ hypothetical form describes the ROLE or DIRECTORY without
98
+ committing to a filename (e.g., "If middleware is added, place it
99
+ at the path the routing convention expects — do not cite a
100
+ specific filename until the file actually exists"). When in doubt,
101
+ OMIT the hypothetical example entirely; an omitted example is
102
+ better than a fabricated path readers may treat as authoritative.
103
+
104
+ ✅ If pass3a-facts.md shows a specific filename and path for a role
105
+ (e.g., a response-wrapper module at a specific location under the
106
+ api directory), write that exact path verbatim — do not re-describe
107
+ it, do not abbreviate it, do not rename it.
71
108
 
72
109
  ✅ When in doubt, write the rule in terms of the pattern (e.g., "handwritten
73
- API modules under `src/admin/api/`") rather than a specific file name.
110
+ API modules under the api directory listed in pass3a-facts.md") rather
111
+ than a specific file name invented on the spot.
74
112
  A directory-scoped rule is correct; an invented file path is a bug.
75
113
 
76
114
  ✅ For testing-strategy documents specifically: if the project has zero