@dzhechkov/p-replicator 1.10.4 → 1.12.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 (68) hide show
  1. package/.dz-manifest.json +119 -47
  2. package/CHANGELOG.md +85 -0
  3. package/MULTIPLATFORM_ROADMAP.md +1 -1
  4. package/README/eng/01_quickstart.md +3 -3
  5. package/README/eng/02_user_guide.md +1 -1
  6. package/README/eng/03_admin_guide.md +2 -2
  7. package/README/eng/04_api_reference.md +11 -5
  8. package/README/eng/README.md +1 -1
  9. package/README/ru/01_quickstart.md +3 -3
  10. package/README/ru/02_user_guide.md +1 -1
  11. package/README/ru/03_admin_guide.md +2 -2
  12. package/README/ru/04_api_reference.md +11 -5
  13. package/README/ru/README.md +1 -1
  14. package/README/ru/html/index.html +7 -7
  15. package/README.md +154 -38
  16. package/bin/cli.js +0 -0
  17. package/package.json +12 -10
  18. package/sbom.json +226 -46
  19. package/scripts/check-pipeline-gaps.sh +413 -0
  20. package/src/commands/doctor.js +94 -4
  21. package/src/rule-components.json +11 -0
  22. package/src/utils.js +3 -8
  23. package/templates/.claude/agents/harvest-coordinator.md +10 -1
  24. package/templates/.claude/commands/feature.md +57 -9
  25. package/templates/.claude/commands/go.md +11 -0
  26. package/templates/.claude/commands/harvest.md +41 -3
  27. package/templates/.claude/commands/myinsights.md +21 -26
  28. package/templates/.claude/commands/replicate.md +10 -1
  29. package/templates/.claude/commands/start.md +8 -0
  30. package/templates/.claude/hooks/check-ports.cjs +409 -20
  31. package/templates/.claude/hooks/session-insights.cjs +158 -25
  32. package/templates/.claude/hooks/statusline.cjs +2 -2
  33. package/templates/.claude/hooks/write-insight.cjs +253 -0
  34. package/templates/.claude/rules/cost-of-detection-ladder.md +96 -0
  35. package/templates/.claude/rules/docker-ports.md +41 -19
  36. package/templates/.claude/rules/feature-lifecycle.md +14 -3
  37. package/templates/.claude/rules/honest-configuration.md +54 -0
  38. package/templates/.claude/rules/insights-capture.md +10 -5
  39. package/templates/.claude/rules/replicate-pipeline.md +4 -2
  40. package/templates/.claude/rules/skill-interface-protocol.md +1 -0
  41. package/templates/.claude/rules/swarm-file-evidence.md +46 -0
  42. package/templates/.claude/settings.json +13 -1
  43. package/templates/.claude/skills/knowledge-extractor/modules/01-agent-review.md +16 -5
  44. package/templates/.claude/skills/sparc-prd-mini/SKILL.md +86 -16
  45. package/tests/e2e/lifecycle.test.js +55 -9
  46. package/tests/e2e/packed-insights-writer.test.js +308 -0
  47. package/tests/fixtures/prep-traceability-fixture/docs/features/order-refund/01_specification.md +29 -0
  48. package/tests/fixtures/prep-traceability-fixture/docs/features/order-refund/02_pseudocode.md +57 -0
  49. package/tests/snapshot/baseline.json +24 -20
  50. package/tests/snapshot/templates.test.js +47 -0
  51. package/tests/unit/absence-is-not-emptiness.test.js +15 -1
  52. package/tests/unit/check-pipeline-gaps.test.js +94 -0
  53. package/tests/unit/check-ports.test.js +729 -2
  54. package/tests/unit/db-port-rule.test.js +36 -5
  55. package/tests/unit/detection-ladder-contract.test.js +302 -0
  56. package/tests/unit/detection-ladder-registry.test.js +52 -0
  57. package/tests/unit/doctor-insight-flow.test.js +315 -0
  58. package/tests/unit/external-dependency-check.test.js +19 -19
  59. package/tests/unit/honest-failure-rules.test.js +492 -0
  60. package/tests/unit/hooks-project-anchored.test.js +67 -3
  61. package/tests/unit/insights-docs-tell-the-truth.test.js +52 -31
  62. package/tests/unit/insights-dz-delegation.test.js +197 -0
  63. package/tests/unit/insights-writer.test.js +285 -0
  64. package/tests/unit/shipped-suite-context.test.js +3 -1
  65. package/tests/unit/traceability-machine-ids.test.js +413 -0
  66. package/tests/unit/traceability-negative-fixture.test.js +322 -0
  67. package/tests/unit/utils.test.js +3 -2
  68. package/LICENSE +0 -21
@@ -89,13 +89,22 @@ After 3 retries with 🔴, halt and surface to user.
89
89
  **Strategy:** maximum parallelism via `Task` tool.
90
90
 
91
91
  1. Identify independent work units from Phase 1's Architecture
92
- 2. Spawn one Task per unit
93
- 3. Each Task: read SPARC sections + implement + test + commit
94
- 4. Coordinator merges/integrates after all Tasks complete
92
+ 2. Allocate one `RUN_ID`, a unique `WORK_UNIT_ID`, and an absolute `TRACE_PATH` per unit
93
+ 3. Spawn one Task per unit; each writes its substantive trace before its one-line pointer
94
+ 4. Coordinator validates every trace before merge/integration
95
95
  5. Run full test suite
96
96
 
97
97
  **Quality gate:** tests pass, lint clean, build succeeds.
98
98
 
99
+ ### Positive file receipt (required)
100
+
101
+ Each worker must write a substantive body ending in `Status: completed` or `Status: failed` to its unique
102
+ `TRACE_PATH` before returning a one-line report. Before integration, the coordinator verifies every
103
+ path is a regular non-symlink file, non-whitespace, post-launch, and terminal. Narrative output or
104
+ silence is not a receipt. Name missing, stale, partial, unreadable, duplicate, failed, dead-PID, or
105
+ probe-error units and refuse merge/completion unless every required receipt is valid and completed.
106
+ See [`swarm-file-evidence`](./swarm-file-evidence.md) for the write protocol and bounded exception.
107
+
99
108
  ## Phase 4: REVIEW (brutal-honesty-review)
100
109
 
101
110
  **Skill:** `.claude/skills/brutal-honesty-review/SKILL.md`
@@ -118,6 +127,8 @@ After 3 retries with 🔴, halt and surface to user.
118
127
  | Tests + lint + build | 3 | Re-run, max 3 attempts |
119
128
  | No `blocker` findings | 4 | Loop Phase 4 until clean |
120
129
 
130
+ Place each gate on the strongest reliable enforcement layer; see [`cost-of-detection-ladder`](./cost-of-detection-ladder.md).
131
+
121
132
  ## Commit Discipline
122
133
 
123
134
  - After Phase 1: `docs(<feature>): SPARC plan`
@@ -0,0 +1,54 @@
1
+ # Honest Configuration
2
+
3
+ ## Rule
4
+
5
+ A value that controls external output, access, limits, routing, or an authoritative measurement must
6
+ not become a plausible or permissive result when its meaning is absent or unproven. Refuse or expose
7
+ unknown; never manufacture health.
8
+
9
+ ## Mechanics
10
+
11
+ ### Substitution axis
12
+
13
+ Absence is not permission to invent a runtime value. Validate required values at the boundary and
14
+ derive degradation from the value actually obtained, even when no failure-only sentinel was set.
15
+
16
+ ### Interpretation axis
17
+
18
+ Keep `undefined` distinct from `''`. Validate empty, misspelled, unmapped, and authority-derived values
19
+ against a closed set in versioned code. Environment variables may select a code-owned variant; they
20
+ must not define the allowlist. A declared input that no decision reads is fail-open-by-omission.
21
+
22
+ | Case | Observable signal | Required response |
23
+ |---|---|---|
24
+ | CFG-S1 | Required runtime value is absent | REFUSE and name the external consequence; no plausible default. |
25
+ | CFG-S2 | Obtained value is invalid although the failure sentinel is unset | REFUSE from the obtained value. |
26
+ | CFG-I1 | Value is `undefined` | REFUSE or UNKNOWN; preserve the absent state. |
27
+ | CFG-I2 | Value is the empty string `''` | REFUSE; do not collapse it into `undefined` or unrestricted. |
28
+ | CFG-I3 | Variant is misspelled, unknown, or unmapped | REFUSE; list the code-owned recognized variants. |
29
+ | CFG-I4 | Source of truth is unreachable | UNKNOWN or REFUSE; do not use cached permissive meaning. |
30
+ | CFG-I5 | Declared allowlist/config input is never read by the decision | REFUSE and wire the decision to the input. |
31
+ | CFG-I6 | Empty CIDR becomes `/0`, allowlist is empty, `BASE_URL='/'`, or tariff is unmapped | REFUSE; no unlimited access or plausible output. |
32
+ | CFG-I7 | Ratio denominator is zero (`0/0`) | UNAVAILABLE or UNKNOWN; render empty with the reason, never `0%`. |
33
+ | CFG-I8 | Allowlist or recognized-variant universe comes from environment | CODE-OWNED closed set; environment selects only. |
34
+
35
+ ## Bounded exception
36
+
37
+ A named build phase may use a substitute only when that phase cannot emit or publish the external
38
+ result; generic “non-production” is not a boundary. An optional dependency may fall back only when
39
+ its absence cannot alter the governed external output, access, limit, route, or measurement.
40
+
41
+ ## Observable violation → replacement
42
+
43
+ | Observable violation | Required replacement |
44
+ |---|---|
45
+ | Missing URL/project name becomes localhost, `/`, or an inferred name | Validate at the boundary and refuse with the affected output named. |
46
+ | Empty/unknown access or limit value becomes unrestricted | Reject before the access, routing, or limiting decision. |
47
+ | Failure is logged but the invalid obtained value still emits a healthy result | Compute status from the value/outcome and stop the emitting action. |
48
+ | Undefined measurement renders as numeric zero | Render unavailable/empty and preserve why it could not be measured. |
49
+
50
+ ## Self-check
51
+
52
+ For each governed value, enumerate absent, empty, invalid, unknown, and unreachable states beside one
53
+ explicit valid control. Trace the value into the decision that emits output. If any bad state reaches
54
+ a default, `ALLOW`, unlimited behavior, `/0`, or `0%` for `0/0`, the boundary is fail-open.
@@ -1,7 +1,7 @@
1
1
  # Insights Capture Rules
2
2
 
3
3
  When and how to record development "грабли" (rakes) into the project knowledge
4
- base. Used by `/myinsights` and the `SessionStart` hook
4
+ base. Used by `/myinsights` and the `SessionStart`/`UserPromptSubmit` hook
5
5
  (`.claude/hooks/session-insights.cjs`).
6
6
 
7
7
  ## When to Capture
@@ -53,11 +53,16 @@ Use lowercase, hyphenated, specific tags:
53
53
 
54
54
  Aim for 2-5 tags per entry.
55
55
 
56
- ## Auto-Injection on SessionStart
56
+ ## Prompt-Time Injection on UserPromptSubmit
57
57
 
58
- The `SessionStart` hook runs `node .claude/hooks/session-insights.cjs` which
59
- prints recent insights to stdout. Claude Code captures stdout and injects
60
- it into the initial session context.
58
+ Markdown at `.claude/insights/index.md` remains the source of truth. After it is
59
+ established, capture makes a best-effort idempotent `dz teach` duplicate; optional dz
60
+ failure cannot undo the file write.
61
+
62
+ On `UserPromptSubmit`, a successful non-empty `dz recall` result from the insight
63
+ domain is the only state that suppresses local output. Absent, failing, or empty recall
64
+ uses the local fallback of the three most recent Markdown entries, never both sources.
65
+ `SessionStart` retains only the missing-carrier hint.
61
66
 
62
67
  ## Storage Lifecycle
63
68
 
@@ -154,8 +154,10 @@ are project-agnostic and can be enhanced (read by Phase 3) but never recreated.
154
154
  **Agents (4):** `replicate-coordinator`, `product-discoverer`, `doc-validator`,
155
155
  `harvest-coordinator`
156
156
 
157
- **Rules (6):** `replicate-pipeline`, `skill-interface-protocol`, `git-workflow`,
158
- `insights-capture`, `feature-lifecycle`, `docker-ports`
157
+ **Rules (9):** `replicate-pipeline`, `skill-interface-protocol`, `git-workflow`,
158
+ `insights-capture`, `feature-lifecycle`, `docker-ports`,
159
+ [`cost-of-detection-ladder`](cost-of-detection-ladder.md), `swarm-file-evidence`,
160
+ `honest-configuration`
159
161
 
160
162
  **Hooks (8 files in `.claude/hooks/`, cross-platform Node).** Only four are wired to an
161
163
  event in `.claude/settings.json`; the rest are utilities you invoke deliberately, and the
@@ -73,6 +73,7 @@ Skills with `modules/` subdirectories MUST structure each module with these sect
73
73
 
74
74
  Modules are numbered (`01-name.md`, `02-name.md`) to indicate execution order.
75
75
  SKILL.md acts as orchestrator, referencing modules in sequence.
76
+ Place module checks on the strongest reliable enforcement layer; see [`cost-of-detection-ladder`](./cost-of-detection-ladder.md).
76
77
 
77
78
  ## 5. Dependency Declaration
78
79
 
@@ -0,0 +1,46 @@
1
+ # Swarm File Evidence
2
+
3
+ ## Rule
4
+
5
+ Every parallel work unit has a named file result. A narrative reply is only a pointer; silence is
6
+ neither progress nor completion. The coordinator may aggregate only positive, attributable terminal
7
+ receipts from the assigned files.
8
+
9
+ ## Mechanics
10
+
11
+ 1. Before dispatch, allocate a run-unique `RUN_ID` and a unique `WORK_UNIT_ID`. Resolve one absolute
12
+ `TRACE_PATH` per `(RUN_ID, WORK_UNIT_ID)`, record its pre-launch state, and pass both fields to the
13
+ worker. Two workers never share a path.
14
+ 2. The worker writes a substantive Markdown body to a temporary regular file in the same directory,
15
+ appends exactly `Status: completed` or `Status: failed` as the final line, renames it to
16
+ `TRACE_PATH`, then returns a one-line pointer. The terminal marker is written last.
17
+ 3. Before merge, synthesis, or completion, the coordinator checks each assigned path: absolute and
18
+ unique; regular and non-symlink; readable and non-whitespace; absent before launch or observably
19
+ changed after launch; final line terminal. It reads the file as the payload and reports
20
+ `valid receipts / required receipts` with every failed `WORK_UNIT_ID` and path.
21
+ 4. `Status: completed` permits consumption. `Status: failed` is a delivered failure and blocks a
22
+ successful aggregate. Missing, empty, stale, partial, unreadable, duplicate, or probe-error
23
+ evidence is undelivered or inconclusive. A dead PID stops waiting as failure; a live PID may only
24
+ extend waiting. Neither PID state proves delivery.
25
+
26
+ ## Bounded exception
27
+
28
+ If atomic rename is unavailable, write directly to `TRACE_PATH` and append the terminal marker last;
29
+ until that line exists the file is partial. Host-authoritative liveness may extend a deadline, but it
30
+ cannot replace the file result or turn missing evidence into success.
31
+
32
+ ## Observable violation → replacement
33
+
34
+ | Observable violation | Required replacement |
35
+ |---|---|
36
+ | Task report exists but `TRACE_PATH` does not | Name the unit/path, mark undelivered, and refuse aggregation. |
37
+ | File is empty, stale, symlinked, unreadable, or non-terminal | Keep the evidence out of the aggregate and rerun or diagnose that unit. |
38
+ | Status says `running` but its recorded PID is dead | Close the unit as failed; do not report continued work from silence. |
39
+ | Fewer than all required receipts are terminal-completed | Report the partial ratio and refuse completion. |
40
+
41
+ ## Self-check
42
+
43
+ For every parallel unit, point to its assignment containing `WORK_UNIT_ID` and absolute `TRACE_PATH`,
44
+ then point to the coordinator check performed before aggregation. Exercise missing, empty, stale,
45
+ partial, failed, dead-PID, and probe-error traces; only a fresh substantive file ending in
46
+ `Status: completed` may satisfy delivery.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
3
- "_comment": "Default hooks + statusline shipped by @dzhechkov/p-replicator init. Cross-platform Node scripts (no bash dependencies). Auto-commits roadmap/insights/plans on Stop, injects relevant insights on SessionStart. Statusline displays pipeline+roadmap+toolkit dashboard above the prompt. Project-specific hooks (DDD, fitness functions, etc.) are merged in by /replicate Phase 3.",
3
+ "_comment": "Default hooks + statusline shipped by @dzhechkov/p-replicator init. Cross-platform Node scripts (no bash dependencies). Auto-commits roadmap/insights/plans on Stop, keeps missing-carrier visibility on SessionStart, and selects insights on UserPromptSubmit. Statusline displays pipeline+roadmap+toolkit dashboard above the prompt. Project-specific hooks (DDD, fitness functions, etc.) are merged in by /replicate Phase 3.",
4
4
  "statusLine": {
5
5
  "type": "command",
6
6
  "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/statusline.cjs\""
@@ -18,6 +18,18 @@
18
18
  ]
19
19
  }
20
20
  ],
21
+ "UserPromptSubmit": [
22
+ {
23
+ "matcher": "*",
24
+ "hooks": [
25
+ {
26
+ "type": "command",
27
+ "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/session-insights.cjs\"",
28
+ "timeout": 5
29
+ }
30
+ ]
31
+ }
32
+ ],
21
33
  "Stop": [
22
34
  {
23
35
  "matcher": "*",
@@ -18,6 +18,16 @@ snippets, skills, and hooks that could be reused in other projects.
18
18
  Launch 5 parallel agents via Task tool. Each agent independently scans the codebase
19
19
  from its perspective.
20
20
 
21
+ ### Positive file receipt contract
22
+
23
+ Before dispatch, allocate one `RUN_ID` and give every extraction perspective a unique `WORK_UNIT_ID`
24
+ and absolute `TRACE_PATH`. Each agent must write a substantive raw-findings body ending in
25
+ `Status: completed` or `Status: failed` to `TRACE_PATH` before its one-line pointer. Before the
26
+ Merging Strategy, verify each path is a regular non-symlink file, non-whitespace, post-launch, and
27
+ terminal. Narrative output or silence is not a receipt. Name missing, stale, partial, unreadable,
28
+ duplicate, failed, dead-PID, or probe-error perspectives and refuse synthesis/completion unless every
29
+ required receipt is valid and completed. See `.claude/rules/swarm-file-evidence.md`.
30
+
21
31
  ### Agent: extractor-patterns
22
32
 
23
33
  **Scope:** Architecture and code patterns
@@ -166,11 +176,12 @@ from its perspective.
166
176
 
167
177
  After all 5 agents complete:
168
178
 
169
- 1. **Deduplicate** — same finding reported by multiple agents keep richest description
170
- 2. **Cross-reference** — if a pattern is also a template, note both categories
171
- 3. **Sort by confidence** — HIGH reusability first
172
- 4. **Count** — total findings, per-category breakdown
173
- 5. **Include TOOLKIT_HARVEST.md markers** — merge manual markers with auto-discovered
179
+ 1. **Validate traces** — read findings only from five valid completed `TRACE_PATH` receipts
180
+ 2. **Deduplicate** — same finding reported by multiple agents keep richest description
181
+ 3. **Cross-reference** — if a pattern is also a template, note both categories
182
+ 4. **Sort by confidence** — HIGH reusability first
183
+ 5. **Count** — total findings, per-category breakdown
184
+ 6. **Include TOOLKIT_HARVEST.md markers** — merge manual markers with auto-discovered
174
185
 
175
186
  ## Output: Raw Findings List
176
187
 
@@ -86,6 +86,65 @@ Checkpoint после каждой фазы. Пользователь подтв
86
86
  └── CLAUDE.md # AI tools integration guide
87
87
  ```
88
88
 
89
+ ## Document Role Map Contract
90
+
91
+ The five SPARC documents are addressed by role, never by a caller-specific filename. A caller may
92
+ provide `DOCUMENT_ROLE_MAP`; otherwise the project-level default below applies. The active map must
93
+ contain exactly these roles: `specification`, `pseudocode`, `architecture`, `refinement`, and
94
+ `completion`.
95
+
96
+ Resolve the active map once before Phase 3. Bind its values as `SPECIFICATION_FILE`,
97
+ `PSEUDOCODE_FILE`, `ARCHITECTURE_FILE`, `REFINEMENT_FILE`, and `COMPLETION_FILE`, respectively. Every
98
+ phase and traceability gate reads or writes those resolved targets. The literal project-level names
99
+ elsewhere in this document describe the default contour; they do not override a supplied map.
100
+
101
+ ### Project-level default
102
+
103
+ ```yaml
104
+ DOCUMENT_ROLE_MAP:
105
+ specification: Specification.md
106
+ pseudocode: Pseudocode.md
107
+ architecture: Architecture.md
108
+ refinement: Refinement.md
109
+ completion: Completion.md
110
+ ```
111
+
112
+ ### Atomic validation
113
+
114
+ If a caller supplies `DOCUMENT_ROLE_MAP`, validate the complete map before Phase 3. Missing roles,
115
+ unknown roles, empty filenames, or a mixture of supplied and default values are an unresolved target
116
+ contract: STOP and report the received map plus every missing, unknown, or empty role. Never fill an
117
+ individual role from the project-level default. When the caller also supplies `TARGET_CATALOG`, join
118
+ that catalog with every filename only after the complete role map passes validation.
119
+
120
+ ### FR/NFR/AC machine-key wire format
121
+
122
+ Machine keys are exact, case-sensitive joins between the `specification` and `pseudocode` roles:
123
+ `FR-<slug>-<n>`, `NFR-<slug>-<n>`, or `AC-<slug>-<n>`. For a feature contour, `<slug>` is the
124
+ lowercase hyphenated feature directory name and `<n>` is one or more decimal digits.
125
+
126
+ Declare each key in `SPECIFICATION_FILE` only as a level-three Markdown heading:
127
+
128
+ ```markdown
129
+ ### FR-order-refund-1
130
+ ### NFR-order-refund-2 — bounded latency
131
+ ### AC-order-refund-3 - accepted result
132
+ ```
133
+
134
+ Every `### Algorithm:` block in `PSEUDOCODE_FILE` must carry at least one standalone, exact claim;
135
+ repeat the line when one algorithm addresses several keys:
136
+
137
+ ```markdown
138
+ ### Algorithm: Validate refund
139
+
140
+ REQUIREMENT: `FR-order-refund-1`
141
+ REQUIREMENT: `NFR-order-refund-2`
142
+ ```
143
+
144
+ Prose, comments, tables, examples, `REALISES: SC-...`, and nested `SC-FR-*` scenario IDs are not
145
+ machine-key declarations. Duplicate declarations on either role are invalid; matching sets prove
146
+ cross-document linkage only, not that an algorithm semantically implements the requirement.
147
+
89
148
  ## Workflow Architecture
90
149
 
91
150
  ```
@@ -373,7 +432,7 @@ view(".claude/skills/problem-solver-enhanced/SKILL.md")
373
432
 
374
433
  **Inputs:** Product Brief (Phase 0) + Research (Phase 1) + Solution (Phase 2)
375
434
 
376
- **Output — Specification.md + PRD.md:**
435
+ **Output — `SPECIFICATION_FILE` + PRD.md:**
377
436
  - Executive Summary
378
437
  - User Stories with Acceptance Criteria (Gherkin)
379
438
  - Feature Matrix (MVP/v1/v2)
@@ -430,7 +489,7 @@ view("templates/prd.md")
430
489
 
431
490
  **Цель:** Определить алгоритмы и data flow.
432
491
 
433
- **Output — Pseudocode.md:**
492
+ **Output — `PSEUDOCODE_FILE`:**
434
493
  ```markdown
435
494
  ## Data Structures
436
495
 
@@ -482,22 +541,24 @@ Response (4xx/5xx):
482
541
 
483
542
  **Шаг 4.9 — ПОКРЫТИЕ СЦЕНАРИЕВ (обязательный, до чекпойнта).**
484
543
 
485
- Re-read `Specification.md` and collect every `SC-` scenario ID. Collect every algorithm's `REALISES`
486
- line from `Pseudocode.md`. Write a `## Scenario Coverage` block into `Pseudocode.md` — **in every
544
+ Resolve role `specification` through `DOCUMENT_ROLE_MAP` as `SPECIFICATION_FILE`.
545
+ Resolve role `pseudocode` through `DOCUMENT_ROLE_MAP` as `PSEUDOCODE_FILE`. Re-read `SPECIFICATION_FILE` and
546
+ collect every `SC-` scenario ID. Collect every algorithm's `REALISES` line from `PSEUDOCODE_FILE`.
547
+ Write a `## Scenario Coverage` block into `PSEUDOCODE_FILE` — **in every
487
548
  case, including the one where everything is covered**, because an absent block and a block saying
488
549
  "all covered" are indistinguishable to the next reader:
489
550
 
490
551
  ```
491
552
  ## Scenario Coverage
492
553
 
493
- Scenarios in Specification.md: [N] · claimed by an algorithm: [M]
554
+ Scenarios in [SPECIFICATION_FILE]: [N] · claimed by an algorithm: [M]
494
555
 
495
556
  Not claimed by any algorithm:
496
557
  | Scenario | Reason |
497
558
  |---|---|
498
559
  | SC-… | ui-only |
499
560
 
500
- Claimed by an algorithm but absent from Specification.md:
561
+ Claimed by an algorithm but absent from [SPECIFICATION_FILE]:
501
562
  | Algorithm | Claimed ID |
502
563
  |---|---|
503
564
  | [name] | SC-… |
@@ -513,7 +574,7 @@ written out rather than left blank, because an empty table and a forgotten table
513
574
  | Reason | Means |
514
575
  |---|---|
515
576
  | `ui-only` | realised entirely in the interface, no algorithm to write |
516
- | `external-service` | performed by a third party, see `Architecture.md` → External Dependencies |
577
+ | `external-service` | performed by a third party, see the `architecture` role resolved through `DOCUMENT_ROLE_MAP` → External Dependencies |
517
578
  | `out-of-mvp-scope` | deliberately not built yet |
518
579
  | `data-only` | satisfied by a schema or constraint, not by a procedure |
519
580
  | `config-only` | satisfied by OUR OWN configuration — a server setting, a header, a policy file — with no procedure to write |
@@ -528,6 +589,10 @@ scenario describes — no comparison of names can. So this catches *"nobody wrot
528
589
  scenario"*; it does not catch *"someone wrote a line that mentions it"*. Say so here rather than
529
590
  letting a later reader assume the stronger thing.
530
591
 
592
+ With the project-level default, `[SPECIFICATION_FILE]` renders as `Specification.md`, including the
593
+ label `Claimed by an algorithm but absent from Specification.md:`. A supplied map renders the same
594
+ label with its resolved `specification` filename.
595
+
531
596
  **[MANUAL] CP4:**
532
597
  ```
533
598
  ═══════════════════════════════════════════════════════════════
@@ -559,7 +624,7 @@ view("references/sparc-methodology.md")
559
624
  → Секция Architecture для best practices
560
625
  ```
561
626
 
562
- **Output — Architecture.md:**
627
+ **Output — `ARCHITECTURE_FILE`:**
563
628
  ```markdown
564
629
  ## Architecture Overview
565
630
 
@@ -660,7 +725,9 @@ example: what an API can do drifts, and a stale fact recorded as evidence is wor
660
725
  булевым, когда схема получила перечисление; алгоритм пользуется полем, которого в схеме нет; у
661
726
  статуса три значения в одном документе и пять в другом.
662
727
 
663
- Перечитай в `Pseudocode.md` ДВЕ секции — `## Data Structures` и `## Core Algorithms` — и сверь их с
728
+ Resolve role `pseudocode` through `DOCUMENT_ROLE_MAP` as `PSEUDOCODE_FILE`.
729
+ Resolve role `architecture` through `DOCUMENT_ROLE_MAP` as `ARCHITECTURE_FILE`. Перечитай в `PSEUDOCODE_FILE`
730
+ ДВЕ секции — `## Data Structures` и `## Core Algorithms` — и сверь их с
664
731
  тем, что выбрано ЗДЕСЬ. Алгоритмы нужны обязательно: расхождение «алгоритм читает поле, которого в
665
732
  схеме нет» по одним лишь структурам данных не обнаруживается. Ищи три вида расхождений:
666
733
 
@@ -668,16 +735,16 @@ example: what an API can do drifts, and a stale fact recorded as evidence is wor
668
735
  - **отсутствующая колонка** — алгоритм читает или пишет поле, которого в схеме нет;
669
736
  - **несовпадение набора значений** — у одного и того же поля разное число допустимых значений.
670
737
 
671
- Какую сторону править — решается по РОЛИ документа, а не по старшинству. `Pseudocode.md` держит
672
- ЛОГИЧЕСКУЮ модель (что означает поле), `Architecture.md` — ФИЗИЧЕСКУЮ (где и как оно лежит).
738
+ Какую сторону править — решается по РОЛИ документа, а не по старшинству. `PSEUDOCODE_FILE` держит
739
+ ЛОГИЧЕСКУЮ модель (что означает поле), `ARCHITECTURE_FILE` — ФИЗИЧЕСКУЮ (где и как оно лежит).
673
740
  Поэтому: если эта фаза ввела осознанное физическое ограничение (тип хранилища, индекс, длина) —
674
- правится `Pseudocode.md`; если выбранная технология НАРУШАЕТ требуемую семантику (теряются значения,
741
+ правится `PSEUDOCODE_FILE`; если выбранная технология НАРУШАЕТ требуемую семантику (теряются значения,
675
742
  исчезает состояние, которым пользуется алгоритм) — меняется выбор ЗДЕСЬ, потому что требование
676
743
  старше удобства реализации. Секция `## Data Architecture` этого документа остаётся на месте, но
677
744
  описывает отображение на хранилище и связи, а НЕ пересказывает список полей: второй экземпляр списка
678
745
  становится вторым местом, где начинается расхождение.
679
746
 
680
- Результат записывается в `Architecture.md` ВСЕГДА, отдельным блоком:
747
+ Результат записывается в `ARCHITECTURE_FILE` ВСЕГДА, отдельным блоком:
681
748
 
682
749
  ```markdown
683
750
  ## Reconciliation with Pseudocode
@@ -688,11 +755,14 @@ example: what an API can do drifts, and a stale fact recorded as evidence is wor
688
755
  ```
689
756
 
690
757
  Если сверка не нашла ничего — блок всё равно пишется, и он ОБЯЗАН назвать, что именно
691
- сверялось: «Расхождений с `Pseudocode.md` не найдено. Сверены сущности: <перечисление>; алгоритмы:
758
+ сверялось: «Расхождений с `[PSEUDOCODE_FILE]` не найдено. Сверены сущности: <перечисление>; алгоритмы:
692
759
  <перечисление>.» Одна фраза «расхождений нет» без перечня — это церемония, которую модель напишет
693
760
  не глядя; перечень делает утверждение проверяемым. Молчание не является результатом сверки: по нему
694
761
  нельзя отличить «сверили и чисто» от «не сверяли».
695
762
 
763
+ With the project-level default, `PSEUDOCODE_FILE` is `Pseudocode.md` and `ARCHITECTURE_FILE` is
764
+ `Architecture.md`; a supplied map changes only those resolved filenames, not the reconciliation.
765
+
696
766
  **[MANUAL] CP5:**
697
767
  ```
698
768
  ═══════════════════════════════════════════════════════════════
@@ -718,7 +788,7 @@ example: what an API can do drifts, and a stale fact recorded as evidence is wor
718
788
 
719
789
  **Цель:** Edge cases, тестирование, оптимизация.
720
790
 
721
- **Output — Refinement.md:**
791
+ **Output — `REFINEMENT_FILE`:**
722
792
  ```markdown
723
793
  ## Edge Cases Matrix
724
794
 
@@ -796,7 +866,7 @@ Scenario: [Error case]
796
866
 
797
867
  **Цель:** Deployment и operational readiness.
798
868
 
799
- **Output — Completion.md + CLAUDE.md:**
869
+ **Output — `COMPLETION_FILE` + CLAUDE.md:**
800
870
 
801
871
  **Completion.md:**
802
872
  ```markdown
@@ -328,23 +328,38 @@ describe('e2e: v1.4 pre-shipped generic toolkit', () => {
328
328
  } finally { rmRf(dir); }
329
329
  });
330
330
 
331
- test('init installs all 5 generic rules', () => {
331
+ test('init installs every registered rule and the consumer detection ladder is readable', () => {
332
332
  const dir = tmpDir();
333
333
  try {
334
334
  runCli(['init'], dir);
335
- const expected = [
336
- 'replicate-pipeline',
337
- 'skill-interface-protocol',
338
- 'git-workflow',
339
- 'insights-capture',
340
- 'feature-lifecycle',
341
- ];
335
+ const { COMPONENTS } = require(path.join(PKG_DIR, 'src', 'utils.js'));
336
+ const expected = Object.keys(COMPONENTS.rules.items);
337
+ assert.ok(expected.includes('cost-of-detection-ladder'),
338
+ 'the explicit ladder slug prevents registry-derived omission from self-confirming');
342
339
  for (const rule of expected) {
343
340
  assert.ok(
344
341
  exists(dir, `.claude/rules/${rule}.md`),
345
- `${rule}.md should be installed by init (v1.4 pre-shipped)`
342
+ `${rule}.md should be installed by init`
346
343
  );
347
344
  }
345
+ const ladder = fs.readFileSync(
346
+ path.join(dir, '.claude/rules/cost-of-detection-ladder.md'), 'utf8');
347
+ assert.match(ladder, /strongest layer that can reliably express the property/i);
348
+ assert.match(ladder, /\| Reaction \| Owner \|/i);
349
+ } finally { rmRf(dir); }
350
+ });
351
+
352
+ test('doctor and verify reject a missing registered detection ladder', () => {
353
+ const dir = tmpDir();
354
+ try {
355
+ runCli(['init'], dir);
356
+ fs.unlinkSync(path.join(dir, '.claude/rules/cost-of-detection-ladder.md'));
357
+ const doctor = runCli(['doctor'], dir);
358
+ const verify = runCli(['verify'], dir);
359
+ assert.notEqual(doctor.exitCode, 0, 'doctor must reject a missing load-bearing rule');
360
+ assert.notEqual(verify.exitCode, 0, 'verify must reject a missing load-bearing rule');
361
+ assert.match(doctor.stdout + doctor.stderr, /cost-of-detection-ladder/);
362
+ assert.match(verify.stdout + verify.stderr, /cost-of-detection-ladder/);
348
363
  } finally { rmRf(dir); }
349
364
  });
350
365
 
@@ -490,6 +505,37 @@ describe('e2e: v1.4.1 cross-platform hooks', () => {
490
505
  describe('e2e: v1.4.2 settings.json merge on --force', () => {
491
506
  const USER_HOOK_COMMAND = 'echo "USER-CUSTOM-HOOK-MARKER-12345"';
492
507
 
508
+ test('P18 - init force adds one p-replicator prompt hook without replacing foreign hooks', () => {
509
+ const dir = tmpDir();
510
+ const foreign = 'node foreign-user-prompt-hook.cjs';
511
+ try {
512
+ runCli(['init'], dir);
513
+ const settingsPath = path.join(dir, '.claude/settings.json');
514
+ const manifestPath = path.join(dir, MANIFEST);
515
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
516
+ settings.hooks.UserPromptSubmit = [{
517
+ matcher: '*',
518
+ hooks: [{ type: 'command', command: foreign, timeout: 7 }],
519
+ }];
520
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
521
+
522
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
523
+ delete manifest.shippedDefaults['settings.json'].hooks.UserPromptSubmit;
524
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
525
+
526
+ assert.equal(runCli(['init', '--force'], dir).exitCode, 0);
527
+ assert.equal(runCli(['init', '--force'], dir).exitCode, 0);
528
+ const merged = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
529
+ const commands = merged.hooks.UserPromptSubmit
530
+ .flatMap((matcher) => matcher.hooks || [])
531
+ .map((hook) => hook.command);
532
+ assert.equal(commands.filter((command) => command === foreign).length, 1,
533
+ 'the consumer-owned prompt hook must survive both upgrades');
534
+ assert.equal(commands.filter((command) => command.includes('session-insights.cjs')).length, 1,
535
+ 'the package prompt hook must be added exactly once');
536
+ } finally { rmRf(dir); }
537
+ });
538
+
493
539
  test('init --force preserves user-added hooks in settings.json', () => {
494
540
  const dir = tmpDir();
495
541
  try {