@dzhechkov/p-replicator 1.10.3 → 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.
- package/.dz-manifest.json +119 -47
- package/CHANGELOG.md +85 -0
- package/MULTIPLATFORM_ROADMAP.md +1 -1
- package/README/eng/01_quickstart.md +3 -3
- package/README/eng/02_user_guide.md +1 -1
- package/README/eng/03_admin_guide.md +2 -2
- package/README/eng/04_api_reference.md +11 -5
- package/README/eng/README.md +1 -1
- package/README/ru/01_quickstart.md +3 -3
- package/README/ru/02_user_guide.md +1 -1
- package/README/ru/03_admin_guide.md +2 -2
- package/README/ru/04_api_reference.md +11 -5
- package/README/ru/README.md +1 -1
- package/README/ru/html/index.html +7 -7
- package/README.md +154 -38
- package/bin/cli.js +0 -0
- package/package.json +15 -13
- package/sbom.json +226 -46
- package/scripts/check-pipeline-gaps.sh +413 -0
- package/src/commands/doctor.js +94 -4
- package/src/rule-components.json +11 -0
- package/src/utils.js +3 -8
- package/templates/.claude/agents/harvest-coordinator.md +10 -1
- package/templates/.claude/commands/feature.md +57 -9
- package/templates/.claude/commands/go.md +11 -0
- package/templates/.claude/commands/harvest.md +41 -3
- package/templates/.claude/commands/myinsights.md +21 -26
- package/templates/.claude/commands/replicate.md +10 -1
- package/templates/.claude/commands/start.md +8 -0
- package/templates/.claude/hooks/check-ports.cjs +409 -20
- package/templates/.claude/hooks/session-insights.cjs +158 -25
- package/templates/.claude/hooks/statusline.cjs +2 -2
- package/templates/.claude/hooks/write-insight.cjs +253 -0
- package/templates/.claude/rules/cost-of-detection-ladder.md +96 -0
- package/templates/.claude/rules/docker-ports.md +41 -19
- package/templates/.claude/rules/feature-lifecycle.md +14 -3
- package/templates/.claude/rules/honest-configuration.md +54 -0
- package/templates/.claude/rules/insights-capture.md +10 -5
- package/templates/.claude/rules/replicate-pipeline.md +4 -2
- package/templates/.claude/rules/skill-interface-protocol.md +1 -0
- package/templates/.claude/rules/swarm-file-evidence.md +46 -0
- package/templates/.claude/settings.json +13 -1
- package/templates/.claude/skills/knowledge-extractor/modules/01-agent-review.md +16 -5
- package/templates/.claude/skills/sparc-prd-mini/SKILL.md +86 -16
- package/tests/e2e/lifecycle.test.js +55 -9
- package/tests/e2e/packed-insights-writer.test.js +308 -0
- package/tests/fixtures/prep-traceability-fixture/docs/features/order-refund/01_specification.md +29 -0
- package/tests/fixtures/prep-traceability-fixture/docs/features/order-refund/02_pseudocode.md +57 -0
- package/tests/snapshot/baseline.json +24 -20
- package/tests/snapshot/templates.test.js +47 -0
- package/tests/unit/absence-is-not-emptiness.test.js +15 -1
- package/tests/unit/check-pipeline-gaps.test.js +94 -0
- package/tests/unit/check-ports.test.js +729 -2
- package/tests/unit/db-port-rule.test.js +36 -5
- package/tests/unit/detection-ladder-contract.test.js +302 -0
- package/tests/unit/detection-ladder-registry.test.js +52 -0
- package/tests/unit/doctor-insight-flow.test.js +315 -0
- package/tests/unit/external-dependency-check.test.js +19 -19
- package/tests/unit/honest-failure-rules.test.js +492 -0
- package/tests/unit/hooks-project-anchored.test.js +67 -3
- package/tests/unit/insights-docs-tell-the-truth.test.js +52 -31
- package/tests/unit/insights-dz-delegation.test.js +197 -0
- package/tests/unit/insights-writer.test.js +285 -0
- package/tests/unit/shipped-suite-context.test.js +3 -1
- package/tests/unit/traceability-machine-ids.test.js +413 -0
- package/tests/unit/traceability-negative-fixture.test.js +322 -0
- package/tests/unit/utils.test.js +3 -2
- 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.
|
|
93
|
-
3.
|
|
94
|
-
4. Coordinator
|
|
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
|
-
##
|
|
56
|
+
## Prompt-Time Injection on UserPromptSubmit
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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 (
|
|
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,
|
|
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. **
|
|
170
|
-
2. **
|
|
171
|
-
3. **
|
|
172
|
-
4. **
|
|
173
|
-
5. **
|
|
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 —
|
|
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 —
|
|
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
|
-
|
|
486
|
-
|
|
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
|
|
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
|
|
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 `
|
|
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 —
|
|
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
|
-
|
|
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
|
-
Какую сторону править — решается по РОЛИ документа, а не по старшинству. `
|
|
672
|
-
ЛОГИЧЕСКУЮ модель (что означает поле), `
|
|
738
|
+
Какую сторону править — решается по РОЛИ документа, а не по старшинству. `PSEUDOCODE_FILE` держит
|
|
739
|
+
ЛОГИЧЕСКУЮ модель (что означает поле), `ARCHITECTURE_FILE` — ФИЗИЧЕСКУЮ (где и как оно лежит).
|
|
673
740
|
Поэтому: если эта фаза ввела осознанное физическое ограничение (тип хранилища, индекс, длина) —
|
|
674
|
-
правится `
|
|
741
|
+
правится `PSEUDOCODE_FILE`; если выбранная технология НАРУШАЕТ требуемую семантику (теряются значения,
|
|
675
742
|
исчезает состояние, которым пользуется алгоритм) — меняется выбор ЗДЕСЬ, потому что требование
|
|
676
743
|
старше удобства реализации. Секция `## Data Architecture` этого документа остаётся на месте, но
|
|
677
744
|
описывает отображение на хранилище и связи, а НЕ пересказывает список полей: второй экземпляр списка
|
|
678
745
|
становится вторым местом, где начинается расхождение.
|
|
679
746
|
|
|
680
|
-
Результат записывается в `
|
|
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
|
-
сверялось: «Расхождений с `
|
|
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 —
|
|
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 —
|
|
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
|
|
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
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
'
|
|
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
|
|
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 {
|