claudeos-core 2.3.1 → 2.3.2

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.
@@ -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
 
@@ -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
@@ -1,6 +1,60 @@
1
1
  Read claudeos-core/generated/project-analysis.json and
2
2
  perform a deep analysis of the following domains only: {{DOMAIN_GROUP}}
3
3
 
4
+ ## MANDATORY: Configuration file verification (read before analysis)
5
+
6
+ Before analyzing domain source code, read the following configuration
7
+ files if they exist in the project root. The stack metadata in
8
+ `project-analysis.json` is produced by a regex-based static analyzer
9
+ and may be incomplete for modern Gradle/Maven projects. Reading these
10
+ files directly gives you the ground truth for Java version, server
11
+ port, active profile, datasource configuration, and logging setup.
12
+
13
+ Required reads (if the file exists):
14
+
15
+ 1. **`build.gradle` or `build.gradle.kts`** (or **`pom.xml`** for Maven).
16
+ Specifically check:
17
+ - Java version: look inside `java { ... }`, `java { toolchain { ... } }`,
18
+ `sourceCompatibility`, `targetCompatibility`, or `<java.version>` /
19
+ `<maven.compiler.source>` in pom.xml. If the value is a variable
20
+ reference (e.g., `sourceCompatibility = "${javaVersion}"`), resolve
21
+ the variable inside `ext { ... }` or `<properties>`. Record the
22
+ ACTUAL Java version — do NOT infer "Java 17+" from the Spring
23
+ Boot version.
24
+ - Spring Boot version: verify it matches `project-analysis.json`'s
25
+ frameworkVersion field; if they disagree, trust the build file.
26
+ - Dependencies that indicate specific patterns (MyBatis/iBatis/JPA,
27
+ multiple DB drivers, Jasypt, JWT library, Logback extras).
28
+
29
+ 2. **`application.yml` / `application.yaml` / `application.properties`
30
+ and their profile variants** (`application-{profile}.{yml,yaml,properties}`).
31
+ Specifically check:
32
+ - Server port: look for `server.port`. Spring Boot accepts property
33
+ placeholders with defaults like `port: ${G_PORT:8090}` — extract the
34
+ default value (the post-colon number) as the ACTUAL port. Do NOT
35
+ assume "port 8080" from the Spring Boot framework default.
36
+ - Active profile(s): `spring.profiles.active` and `spring.profiles.group`.
37
+ - Datasource: every `spring.datasource.*` block (multi-dialect projects
38
+ declare more than one; a `group` profile block like
39
+ `"local": "local,postgres"` reveals which DB is active per profile).
40
+ - Logging configuration file reference: `logging.config` points to
41
+ the real log setup (e.g., `classpath:logback-app.xml`). Read that
42
+ file too to understand appenders, levels, and JDBC logging
43
+ adapters like `log4jdbc`.
44
+
45
+ 3. **Any referenced logging configuration files**: `logback*.xml`,
46
+ `logback*.groovy`, `log4j*.xml`, `log4j*.properties`,
47
+ `log4jdbc*.properties`. These reveal the actual logging framework
48
+ in use (Logback is the Spring Boot default but legacy projects may
49
+ use Log4j2 or mix with JDBC adapters).
50
+
51
+ When `project-analysis.json` and the configuration files disagree,
52
+ record the configuration-file value as the truth and note the
53
+ discrepancy in your analysis output. This is the path to eliminate
54
+ "Java 17+" / "port 8080" hallucinations observed in pre-v2.3.2 runs.
55
+
56
+ ## Domain analysis
57
+
4
58
  For each domain, select one representative file per layer, read its code, and analyze it.
5
59
  Prioritize files with the richest patterns.
6
60
 
@@ -114,7 +114,7 @@ Generation targets:
114
114
  - `10.backend/*` rules: `paths: ["**/*"]` — always loaded (backend rules needed for any source editing)
115
115
  - `30.security-db/*` rules: `paths: ["**/*"]` — always loaded (cross-cutting concerns)
116
116
  - `40.infra/01.environment-config-rules.md` paths: `["**/*.properties", "**/*.yml", "**/*.yaml", "**/.env*", "**/config/**", "**/application*.properties"]` — Spring config files
117
- - `40.infra/02.logging-monitoring-rules.md` paths: `["**/*.java", "**/logback*.xml", "**/log4j*.xml"]` — source code where logs live + log config
117
+ - `40.infra/02.logging-monitoring-rules.md` paths: `["**/*.java", "**/logback*.xml", "**/logback*.groovy", "**/log4j*.xml", "**/log4j*.properties", "**/log4jdbc*.properties"]` — source code where logs live + log config (covers Logback XML/Groovy DSL, Log4j/Log4j2 XML/properties, and log4jdbc JDBC-logging adapter properties)
118
118
  - `40.infra/03.cicd-deployment-rules.md` paths: `["**/*.yml", "**/*.yaml", "**/Dockerfile*", "**/*.gradle*", "**/pom.xml", "**/*.java"]` — CI / build config + source
119
119
  - `50.sync/*` rules: `paths: ["**/claudeos-core/**", "**/.claude/**"]` — loaded only when editing claudeos-core files
120
120
  - `60.memory/*` rules: forward reference — Pass 4 will generate 4 files (01.decision-log, 02.failure-patterns, 03.compaction, 04.auto-rule-update), each with file-specific `paths`. Pass 3 must STILL list ```.claude/rules/60.memory/*``` as a row in CLAUDE.md Section 6 Rules table so developers/Claude see the category exists.
@@ -1,6 +1,51 @@
1
1
  Read claudeos-core/generated/project-analysis.json and
2
2
  perform a deep analysis of the following domains only: {{DOMAIN_GROUP}}
3
3
 
4
+ ## MANDATORY: Configuration file verification (read before analysis)
5
+
6
+ Before analyzing domain source code, read the following configuration
7
+ files if they exist in the project root. The stack metadata in
8
+ `project-analysis.json` is produced by a regex-based static analyzer
9
+ and may be incomplete for modern Gradle/Maven projects. Reading these
10
+ files directly gives you the ground truth for Kotlin/Java version,
11
+ server port, active profile, datasource configuration, and logging.
12
+
13
+ Required reads (if the file exists):
14
+
15
+ 1. **`build.gradle.kts` or `build.gradle`** (or **`pom.xml`** for Maven).
16
+ Specifically check:
17
+ - Kotlin version: look inside `plugins { kotlin("jvm") version "..." }`
18
+ or `libs.versions.toml`. If it's a variable reference, resolve it.
19
+ - Java target: look inside `kotlin { jvmToolchain(N) }`,
20
+ `tasks.withType<KotlinCompile> { kotlinOptions.jvmTarget = "..." }`,
21
+ or `java { toolchain { languageVersion = JavaLanguageVersion.of(N) } }`.
22
+ Record the ACTUAL target — do NOT infer from the Spring Boot version.
23
+ - Spring Boot version: verify it matches `project-analysis.json`'s
24
+ frameworkVersion field; if they disagree, trust the build file.
25
+ - Dependencies that indicate specific patterns (Exposed/JPA/jOOQ/R2DBC,
26
+ multiple DB drivers, Koin/Kodein, coroutines extensions).
27
+
28
+ 2. **`application.yml` / `application.yaml` / `application.properties`
29
+ and their profile variants** (`application-{profile}.{yml,yaml,properties}`).
30
+ Specifically check:
31
+ - Server port: look for `server.port`. Spring Boot accepts property
32
+ placeholders with defaults like `port: ${PORT:8080}` — extract the
33
+ default value (the post-colon number) as the ACTUAL port. Do NOT
34
+ assume "port 8080" from the Spring Boot framework default.
35
+ - Active profile(s): `spring.profiles.active` and `spring.profiles.group`.
36
+ - Datasource: every `spring.datasource.*` block.
37
+ - Logging configuration file reference: `logging.config` points to
38
+ the real log setup. Read that file too.
39
+
40
+ 3. **Any referenced logging configuration files**: `logback*.xml`,
41
+ `logback*.groovy`, `log4j*.xml`, `log4j*.properties`.
42
+
43
+ When `project-analysis.json` and the configuration files disagree,
44
+ record the configuration-file value as the truth and note the
45
+ discrepancy in your analysis output.
46
+
47
+ ## Domain analysis
48
+
4
49
  For each domain, select one representative file per layer, read its code, and analyze it.
5
50
  Prioritize files with the richest patterns.
6
51
  For multi-module domains, analyze files from EACH module (command, query, bff) separately.
@@ -119,7 +119,7 @@ Generation targets:
119
119
  - `10.backend/*` rules: `paths: ["**/*"]` — always loaded (backend rules needed for any source editing)
120
120
  - `30.security-db/*` rules: `paths: ["**/*"]` — always loaded (cross-cutting concerns)
121
121
  - `40.infra/01.environment-config-rules.md` paths: `["**/*.properties", "**/*.yml", "**/*.yaml", "**/.env*", "**/config/**", "**/application*.properties"]` — Spring config files
122
- - `40.infra/02.logging-monitoring-rules.md` paths: `["**/*.kt", "**/*.kts", "**/logback*.xml"]` — source code where logs live + log config
122
+ - `40.infra/02.logging-monitoring-rules.md` paths: `["**/*.kt", "**/*.kts", "**/logback*.xml", "**/logback*.groovy", "**/log4j*.xml", "**/log4j*.properties", "**/log4jdbc*.properties"]` — source code where logs live + log config (covers Logback XML/Groovy DSL, Log4j/Log4j2 XML/properties, and log4jdbc JDBC-logging adapter properties)
123
123
  - `40.infra/03.cicd-deployment-rules.md` paths: `["**/*.yml", "**/*.yaml", "**/Dockerfile*", "**/*.gradle*", "**/*.kt", "**/*.kts"]` — CI / build config + source
124
124
  - `50.sync/*` rules: `paths: ["**/claudeos-core/**", "**/.claude/**"]` — loaded only when editing claudeos-core files
125
125
  - `60.memory/*` rules: forward reference — Pass 4 will generate 4 files (01.decision-log, 02.failure-patterns, 03.compaction, 04.auto-rule-update), each with file-specific `paths`. Pass 3 must STILL list ```.claude/rules/60.memory/*``` as a row in CLAUDE.md Section 6 Rules table so developers/Claude see the category exists.