@dzhechkov/p-replicator 1.5.18 → 1.6.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 (69) hide show
  1. package/.dz-manifest.json +126 -46
  2. package/CHANGELOG.md +135 -0
  3. package/MULTIPLATFORM_ROADMAP.md +1 -1
  4. package/README/eng/01_quickstart.md +2 -2
  5. package/README/eng/02_user_guide.md +1 -1
  6. package/README/eng/03_admin_guide.md +2 -2
  7. package/README/eng/05_architecture.md +6 -2
  8. package/README/eng/README.md +1 -1
  9. package/README/ru/01_quickstart.md +2 -2
  10. package/README/ru/02_user_guide.md +1 -1
  11. package/README/ru/03_admin_guide.md +2 -2
  12. package/README/ru/05_architecture.md +1 -1
  13. package/README/ru/README.md +1 -1
  14. package/README/ru/html/index.html +7 -7
  15. package/README.md +36 -15
  16. package/bin/cli.js +0 -0
  17. package/package.json +11 -10
  18. package/sbom.json +245 -45
  19. package/src/utils.js +2 -0
  20. package/templates/.claude/agents/doc-validator.md +2 -1
  21. package/templates/.claude/agents/product-discoverer.md +1 -1
  22. package/templates/.claude/commands/next.md +16 -0
  23. package/templates/.claude/commands/replicate.md +126 -8
  24. package/templates/.claude/commands/start.md +19 -1
  25. package/templates/.claude/hooks/autocommit-insights.cjs +95 -10
  26. package/templates/.claude/hooks/autocommit-plans.cjs +95 -10
  27. package/templates/.claude/hooks/autocommit-roadmap.cjs +96 -13
  28. package/templates/.claude/hooks/check-ports.cjs +232 -0
  29. package/templates/.claude/hooks/session-insights.cjs +13 -1
  30. package/templates/.claude/hooks/state-update.cjs +13 -1
  31. package/templates/.claude/hooks/statusline.cjs +145 -18
  32. package/templates/.claude/rules/docker-ports.md +123 -0
  33. package/templates/.claude/rules/replicate-pipeline.md +5 -2
  34. package/templates/.claude/settings.json +5 -5
  35. package/templates/.claude/skills/cc-toolkit-generator-enhanced/modules/01-detect-parse.md +57 -14
  36. package/templates/.claude/skills/cc-toolkit-generator-enhanced/modules/02-analyze-map.md +9 -7
  37. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/enhanced-recommendations.md +6 -4
  38. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/extended-mapping.md +2 -2
  39. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/templates/ddd-agents.md +2 -2
  40. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/templates/ddd-skills.md +1 -1
  41. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/templates/enhanced-claude-md.md +2 -2
  42. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/templates/feature-suggestions.md +11 -2
  43. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/templates/start-command.md +1 -1
  44. package/templates/.claude/skills/requirements-validator/SKILL.md +7 -0
  45. package/templates/.claude/skills/requirements-validator/references/scoring-system.md +28 -0
  46. package/templates/.claude/skills/sparc-prd-mini/SKILL.md +143 -1
  47. package/tests/e2e/lifecycle.test.js +21 -10
  48. package/tests/snapshot/baseline.json +29 -27
  49. package/tests/snapshot/update-baseline.js +2 -1
  50. package/tests/unit/adr-decision-coverage.test.js +137 -0
  51. package/tests/unit/adr-scanner-contract.test.js +108 -0
  52. package/tests/unit/autocommit-deletion.test.js +242 -0
  53. package/tests/unit/check-ports.test.js +184 -0
  54. package/tests/unit/db-port-rule.test.js +216 -0
  55. package/tests/unit/detect-parse-anchor.test.js +109 -0
  56. package/tests/unit/external-dependency-check.test.js +209 -0
  57. package/tests/unit/growth-module-b2b-gate.test.js +104 -0
  58. package/tests/unit/hooks-project-anchored.test.js +223 -0
  59. package/tests/unit/hooks-report-failures.test.js +207 -0
  60. package/tests/unit/pipeline-file-ownership.test.js +95 -0
  61. package/tests/unit/roadmap-one-schema.test.js +179 -0
  62. package/tests/unit/sparc-reconciliation.test.js +117 -0
  63. package/tests/unit/spec-pseudocode-traceability.test.js +146 -0
  64. package/tests/unit/statusline-honest-labels.test.js +178 -0
  65. package/tests/unit/statusline-two-roots.test.js +237 -0
  66. package/tests/unit/sync-templates-guard.test.js +209 -0
  67. package/tests/unit/utils.test.js +2 -2
  68. package/tests/unit/validation-gate-teeth.test.js +158 -0
  69. package/LICENSE +0 -21
@@ -121,6 +121,9 @@ Always pass these constraints to sparc-prd-mini (Phase 1):
121
121
  - Infrastructure: VPS (AdminVPS/HOSTKEY)
122
122
  - Deploy: Docker Compose direct deploy
123
123
  - AI Integration: MCP servers
124
+ - Storage: у баз и очередей НЕТ публикации на хост — кроме привязки к петле (`127.0.0.1` / `::1`).
125
+ Ни `0.0.0.0`, ни `[::]`, ни один явный внешний адрес, ни `network_mode: host`
126
+ (см. `.claude/rules/docker-ports.md`, «Правило №0»)
124
127
 
125
128
  ## Git Discipline During Pipeline
126
129
 
@@ -148,8 +151,8 @@ are project-agnostic and can be enhanced (read by Phase 3) but never recreated.
148
151
  **Agents (4):** `replicate-coordinator`, `product-discoverer`, `doc-validator`,
149
152
  `harvest-coordinator`
150
153
 
151
- **Rules (5):** `replicate-pipeline`, `skill-interface-protocol`, `git-workflow`,
152
- `insights-capture`, `feature-lifecycle`
154
+ **Rules (6):** `replicate-pipeline`, `skill-interface-protocol`, `git-workflow`,
155
+ `insights-capture`, `feature-lifecycle`, `docker-ports`
153
156
 
154
157
  **Hooks:** `.claude/settings.json` (SessionStart + Stop) + cross-platform Node
155
158
  scripts in `.claude/hooks/` (`session-insights.cjs`, `autocommit-roadmap.cjs`,
@@ -3,7 +3,7 @@
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, 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.",
4
4
  "statusLine": {
5
5
  "type": "command",
6
- "command": "node .claude/hooks/statusline.cjs"
6
+ "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/statusline.cjs\""
7
7
  },
8
8
  "hooks": {
9
9
  "SessionStart": [
@@ -12,7 +12,7 @@
12
12
  "hooks": [
13
13
  {
14
14
  "type": "command",
15
- "command": "node .claude/hooks/session-insights.cjs",
15
+ "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/session-insights.cjs\"",
16
16
  "timeout": 5
17
17
  }
18
18
  ]
@@ -24,17 +24,17 @@
24
24
  "hooks": [
25
25
  {
26
26
  "type": "command",
27
- "command": "node .claude/hooks/autocommit-roadmap.cjs",
27
+ "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/autocommit-roadmap.cjs\"",
28
28
  "timeout": 10
29
29
  },
30
30
  {
31
31
  "type": "command",
32
- "command": "node .claude/hooks/autocommit-insights.cjs",
32
+ "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/autocommit-insights.cjs\"",
33
33
  "timeout": 10
34
34
  },
35
35
  {
36
36
  "type": "command",
37
- "command": "node .claude/hooks/autocommit-plans.cjs",
37
+ "command": "node \"${CLAUDE_PROJECT_DIR}/.claude/hooks/autocommit-plans.cjs\"",
38
38
  "timeout": 10
39
39
  }
40
40
  ]
@@ -1,7 +1,7 @@
1
1
  # Module: Detect & Parse
2
2
 
3
3
  Phase 1 of the CC-Toolkit-Generator Enhanced pipeline.
4
- Scans the documentation directory, detects the pipeline type, identifies project
4
+ Scans the PROJECT ROOT (see `docs_path` below — it is the root, not `docs/`), detects the pipeline type, identifies project
5
5
  characteristics, and builds the Internal Project Model (IPM) used by all
6
6
  subsequent modules.
7
7
 
@@ -11,7 +11,7 @@ subsequent modules.
11
11
 
12
12
  | Parameter | Type | Description |
13
13
  |-----------|------|-------------|
14
- | `docs_path` | string | Path to documentation directory. In Claude.ai context this is `/mnt/user-data/uploads/`. In Claude Code / replicate context this is `docs/` (project root). |
14
+ | `docs_path` | string | The directory every probe below resolves from. In Claude.ai context: `/mnt/user-data/uploads/`. In Claude Code / replicate context: **the PROJECT ROOT** — not `docs/`. It has to be the root, because the probes reach both `.ai-context/` (a root-level directory, beside `.claude/`) and `docs/…` below it; anchoring at `docs/` would force `../` on half of them. |
15
15
 
16
16
  No prior module output is required. This is the foundation module.
17
17
 
@@ -25,7 +25,7 @@ Scan `docs_path` recursively and catalog every file by category.
25
25
 
26
26
  ```
27
27
  SCAN docs_path FOR:
28
- # SPARC pipeline files (top-level or docs/)
28
+ # SPARC pipeline files — under docs/ (replicate.md:233-243 writes them all there)
29
29
  PRD.md
30
30
  Solution_Strategy.md
31
31
  Specification.md
@@ -41,7 +41,8 @@ SCAN docs_path FOR:
41
41
  docs/prd/PRD.md
42
42
  docs/ddd/strategic/ # bounded-contexts.md, context-map.md
43
43
  docs/ddd/tactical/ # aggregates/, entities/, value-objects/, events/, repositories/
44
- docs/adr/*.md # ADR-001-*.md, ADR-002-*.md, ...
44
+ docs/adr/*.md # ADR-001-*.md, ADR-002-*.md, ... (directory shape)
45
+ docs/ADR.md # single-file shape — /replicate writes ALL decisions here
45
46
  docs/c4/*.mermaid # context.mermaid, container.mermaid, component.mermaid
46
47
  docs/pseudocode/*.pseudo
47
48
  docs/tests/*.feature # Gherkin scenarios
@@ -72,13 +73,18 @@ def detect_pipeline(docs_path: str) -> str:
72
73
  has_ddd = exists(f"{docs_path}/docs/ddd/")
73
74
  has_ai_context = exists(f"{docs_path}/.ai-context/")
74
75
  has_gherkin = glob(f"{docs_path}/docs/tests/*.feature")
75
- has_adr = len(glob(f"{docs_path}/docs/adr/*.md")) > 5
76
- has_sparc_arch = exists(f"{docs_path}/Architecture.md")
77
- has_sparc_sol = exists(f"{docs_path}/Solution_Strategy.md")
76
+ # Pipeline-IDENTITY marker: the docs/adr/ DIRECTORY is an idea2prd fingerprint. The
77
+ # expression is deliberately unchanged (>5) so routing stays byte-identical for every
78
+ # existing project. The DECISION-presence flag `has_adr` (see Collector below) never
79
+ # participates in routing: it can now be true via docs/ADR.md — a /replicate fingerprint,
80
+ # not an idea2prd one — and flipping routing on it would silently reroute SPARC projects.
81
+ has_idea2prd_adr_dir = len(glob(f"{docs_path}/docs/adr/*.md")) > 5
82
+ has_sparc_arch = exists(f"{docs_path}/docs/Architecture.md") # replicate.md:237 writes it here
83
+ has_sparc_sol = exists(f"{docs_path}/docs/Solution_Strategy.md") # replicate.md:234 writes it here
78
84
 
79
85
  if has_ddd and has_ai_context:
80
86
  return "IDEA2PRD_FULL" # Complete idea2prd-manual output
81
- elif has_ddd or has_adr:
87
+ elif has_ddd or has_idea2prd_adr_dir:
82
88
  return "IDEA2PRD_PARTIAL" # Partial idea2prd output
83
89
  elif has_sparc_arch and has_sparc_sol:
84
90
  return "SPARC" # Full SPARC documentation set
@@ -88,12 +94,49 @@ def detect_pipeline(docs_path: str) -> str:
88
94
  return "MINIMAL" # Basic PRD only, or mixed
89
95
  ```
90
96
 
97
+ ### ADR collector — ONE normalizer for both storage shapes
98
+
99
+ ADRs arrive in two shapes: a `docs/adr/*.md` directory (idea2prd-manual, one decision per file) and
100
+ a single `docs/ADR.md` (what `/replicate` writes). ONE collector handles both; no other step in this
101
+ skill may glob ADR paths for itself — every consumer iterates `detected_docs.idea2prd.adrs`.
102
+
103
+ ```python
104
+ def collect_adrs(docs_path: str) -> list:
105
+ entries = []
106
+ for f in glob(f"{docs_path}/docs/adr/*.md"): # shape A: one decision per file
107
+ entries.append({ "id": id_from_filename(f), # ADR-001-title.md -> "ADR-001"
108
+ "title": first_heading(f),
109
+ "source_path": f })
110
+ single = f"{docs_path}/docs/ADR.md" # shape B: /replicate's single file
111
+ if exists(single):
112
+ for section in split_on_h2_headings(read(single)): # each "## ..." heading opens one decision
113
+ entries.append({ "id": id_from_heading(section), # "## ADR-003: X" -> "ADR-003"; else slug of title
114
+ "title": heading_title(section),
115
+ "source_path": single })
116
+ # STABLE-FIRST dedupe: keep the FIRST entry for a given id (falling back to title when an id is
117
+ # absent), and directory entries are appended first — so "directory wins" is a property of the
118
+ # ORDER above, not an unstated convention inside the helper.
119
+ return dedupe_stable_first_by_id_then_title(entries) # both shapes present -> merged, no double-count
120
+
121
+ has_adr = len(collect_adrs(docs_path)) > 0 # THE one definition — decisions, not files
122
+ ```
123
+
124
+ Worked examples (the behavioural contract, verbatim acid ids):
125
+
126
+ - **A1** — `docs/ADR.md` with two `##` sections ⇒ 2 entries, `has_adr: true`. The single-file shape
127
+ is a first-class citizen, not a fallback.
128
+ - **A4** — `docs/ADR.md` exists but holds prose with NO `##` headings ⇒ 0 entries,
129
+ `has_adr: false`. A file is not a decision; presence of the path never flips the flag by itself.
130
+ - **A5** — BOTH shapes present and `docs/adr/ADR-002-caching.md` duplicates the id of a `## ADR-002`
131
+ section ⇒ one merged list, the duplicate dropped (directory entry wins — it is the finer-grained
132
+ source).
133
+
91
134
  | Pipeline | Minimum Docs | Typical File Count |
92
135
  |----------|-------------|-------------------|
93
136
  | IDEA2PRD_FULL | `docs/ddd/` + `.ai-context/` | 30-50 files |
94
137
  | IDEA2PRD_PARTIAL | `docs/ddd/` or `docs/adr/` (>5) | 15-30 files |
95
- | SPARC | `Architecture.md` + `Solution_Strategy.md` | 8-11 files |
96
- | SPARC_MINIMAL | `Architecture.md` only | 2-5 files |
138
+ | SPARC | `docs/Architecture.md` + `docs/Solution_Strategy.md` | 8-11 files |
139
+ | SPARC_MINIMAL | `docs/Architecture.md` only | 2-5 files |
97
140
  | MINIMAL | PRD.md only | 1-3 files |
98
141
 
99
142
  ### Step 3: Detect Project Characteristics
@@ -116,7 +159,7 @@ IF any keyword found → has_external_apis = true
116
159
  #### 3b. `has_database`
117
160
 
118
161
  ```
119
- SCAN Architecture.md, Specification.md, docker-compose.yml,
162
+ SCAN docs/Architecture.md, docs/Specification.md, docker-compose.yml (project root),
120
163
  ADR-*-data.md, repositories/*.md FOR:
121
164
  keywords: "PostgreSQL", "Postgres", "MongoDB", "MySQL", "Redis",
122
165
  "database", "Prisma", "TypeORM", "Drizzle", "Knex",
@@ -185,7 +228,7 @@ IPM = {
185
228
  events: [paths] | [],
186
229
  repositories: [paths] | []
187
230
  },
188
- adrs: [paths] | [],
231
+ adrs: [{id, title, source_path}] | [], // collect_adrs() output — entries, not bare paths
189
232
  c4_diagrams: [paths] | [],
190
233
  pseudocode: [paths] | [],
191
234
  gherkin_tests: [paths] | [],
@@ -217,7 +260,7 @@ IPM = {
217
260
  has_ddd_strategic: bool, // same as has_ddd (alias for readability)
218
261
  has_gherkin: bool, // detected_docs.idea2prd.gherkin_tests non-empty
219
262
  has_fitness: bool, // detected_docs.idea2prd.fitness non-empty
220
- has_adr: bool, // detected_docs.idea2prd.adr length > 0
263
+ has_adr: bool, // collect_adrs() output non-empty — the ONE definition (see ADR collector)
221
264
  has_c4: bool, // detected_docs.idea2prd.c4 non-empty
222
265
  has_ai_context: bool, // detected_docs.ai_context.readme non-null
223
266
  has_pseudocode: bool, // detected_docs.sparc.pseudocode OR idea2prd.pseudocode non-null
@@ -291,7 +334,7 @@ All of the following must be satisfied before the IPM is considered valid:
291
334
 
292
335
  | Check | Condition | Action on Failure |
293
336
  |-------|-----------|-------------------|
294
- | Minimum docs | SPARC: at least `PRD.md` + `Architecture.md` present | Halt. Ask user to upload missing docs. |
337
+ | Minimum docs | SPARC: at least `docs/PRD.md` + `docs/Architecture.md` present | Halt. Ask user to upload missing docs. |
295
338
  | Minimum docs | idea2prd: at least `docs/ddd/` directory present | Halt. Ask user to upload missing docs. |
296
339
  | Pipeline resolved | `pipeline_type` is not ambiguous | If mixed signals, default to "SPARC" with unified mapping. |
297
340
  | Characteristics scanned | All 4 characteristic flags have been evaluated | Re-scan if any flag is missing. |
@@ -128,7 +128,9 @@ DDD TACTICAL:
128
128
  - Event handlers → PostToolUse hooks (P2)
129
129
 
130
130
  ADR DOCUMENTS:
131
- FOR EACH adr/*.md:
131
+ # via detected_docs.idea2prd.adrs — the Module-1 collector output, BOTH storage
132
+ # shapes (docs/adr/*.md AND the single docs/ADR.md). Never glob ADR paths here.
133
+ FOR EACH entry IN detected_docs.idea2prd.adrs (read entry.source_path):
132
134
  CLASSIFY by keywords:
133
135
  "security", "auth", "encryption" → security.md rule
134
136
  "performance", "scale", "cache" → performance rules
@@ -217,8 +219,8 @@ aggregates/ has >3 files → ddd-validator.md (+8), +5 if >7 files
217
219
  aggregates/ has >5 files → aggregate-patterns/ (+5)
218
220
  events/ has >3 files → event-handlers/ (+5)
219
221
  events/ has >0 files → event-handlers/ (+10)
220
- docs/adr/ has >10 files → architect.md (+10)
221
- docs/adr/ has >15 files → architect.md (+5 additional)
222
+ adrs has >10 decisions → architect.md (+10) # detected_docs.idea2prd.adrs — decisions, not files
223
+ adrs has >15 decisions → architect.md (+5 additional)
222
224
  docs/tests/*.feature exists → testing-patterns/ (+10), tdd-guide.md (+8)
223
225
  tests/ has >10 scenarios → testing-patterns/ (+5)
224
226
  docs/fitness/ exists → fitness-functions.md (+10)
@@ -226,10 +228,10 @@ fitness has >5 functions → validation hooks (+5)
226
228
  .ai-context/ exists → INTEGRATE into CLAUDE.md, project-context/ (+10)
227
229
  .ai-context/ has >4 files → project-context/ RECOMMEND
228
230
 
229
- # ADR Security Boost
230
- FOR EACH adr/*.md containing "security": security.md += 2
231
- FOR EACH adr/*.md containing "authentication": security.md += 1
232
- FOR EACH adr/*.md containing "encryption": security.md += 1
231
+ # ADR Security Boost (over detected_docs.idea2prd.adrs — read each entry.source_path)
232
+ FOR EACH adr entry containing "security": security.md += 2
233
+ FOR EACH adr entry containing "authentication": security.md += 1
234
+ FOR EACH adr entry containing "encryption": security.md += 1
233
235
  ```
234
236
 
235
237
  #### 4c. Assign Priority Tiers
@@ -13,7 +13,9 @@ def detect_pipeline(uploads_path: str) -> str:
13
13
  has_ddd = exists(f"{uploads_path}/docs/ddd/")
14
14
  has_ai_context = exists(f"{uploads_path}/.ai-context/")
15
15
  has_gherkin = glob(f"{uploads_path}/docs/tests/*.feature")
16
- has_adr = len(glob(f"{uploads_path}/docs/adr/*.md")) > 5
16
+ # Shape marker only (mirrors Module 1 detect_pipeline). The DECISION flag has_adr is
17
+ # defined ONCE, in Module 1 ADR collector — never redefined here.
18
+ has_idea2prd_adr_dir = len(glob(f"{uploads_path}/docs/adr/*.md")) > 5
17
19
  has_sparc = exists(f"{uploads_path}/Architecture.md")
18
20
 
19
21
  if has_ddd and has_ai_context:
@@ -124,7 +126,7 @@ if len(events) > 5:
124
126
  ```python
125
127
  score = 10 # base (always somewhat useful)
126
128
 
127
- adrs = glob("docs/adr/*.md")
129
+ adrs = detected_docs.idea2prd.adrs # Module-1 collector: both storage shapes
128
130
  if len(adrs) > 5:
129
131
  score += 5
130
132
  if len(adrs) > 10:
@@ -145,8 +147,8 @@ if exists("docs/c4/container.mermaid"):
145
147
  ```python
146
148
  security_boost = 0
147
149
 
148
- for adr in glob("docs/adr/*.md"):
149
- content = read(adr)
150
+ for adr in detected_docs.idea2prd.adrs: # both storage shapes
151
+ content = read(adr.source_path)
150
152
  if "security" in content.lower():
151
153
  security_boost += 2
152
154
  if "authentication" in content.lower():
@@ -211,7 +211,7 @@ EXTRACT events/*.md:
211
211
  ### From ADRs
212
212
 
213
213
  ```
214
- FOR EACH adr/*.md:
214
+ FOR EACH entry IN detected_docs.idea2prd.adrs (read entry.source_path):
215
215
  EXTRACT:
216
216
  - Title → Rule/skill name candidate
217
217
  - Status → (if Accepted) include, (if Deprecated) exclude
@@ -348,7 +348,7 @@ IF fitness has architecture rules: +5 to ddd-validator
348
348
  ### From ADR Integration Decisions
349
349
 
350
350
  ```
351
- SCAN adr/*.md for:
351
+ SCAN detected_docs.idea2prd.adrs (each entry.source_path) for:
352
352
  - "GitHub" → @modelcontextprotocol/server-github
353
353
  - "PostgreSQL", "Postgres" → @modelcontextprotocol/server-postgres
354
354
  - "MongoDB" → @modelcontextprotocol/server-mongodb
@@ -425,7 +425,7 @@ APPROVE / REQUEST_CHANGES / COMMENT
425
425
  - Context diagram summary
426
426
  - Container descriptions
427
427
 
428
- 2. Extract from `docs/adr/`:
428
+ 2. Extract from `detected_docs.idea2prd.adrs` (docs/adr/*.md or the single docs/ADR.md):
429
429
  - Top 5-10 most important ADRs
430
430
  - Architecture-related decisions
431
431
 
@@ -448,7 +448,7 @@ APPROVE / REQUEST_CHANGES / COMMENT
448
448
  1. Extract from `docs/fitness/`:
449
449
  - All fitness functions as checklist
450
450
 
451
- 2. Extract from `docs/adr/`:
451
+ 2. Extract from `detected_docs.idea2prd.adrs` (docs/adr/*.md or the single docs/ADR.md):
452
452
  - Key decisions affecting code
453
453
 
454
454
  3. Extract from `docs/ddd/tactical/`:
@@ -345,6 +345,6 @@ src/
345
345
  1. From `.ai-context/coding-standards.md`:
346
346
  - Direct integration
347
347
 
348
- 2. From `docs/adr/`:
348
+ 2. From `detected_docs.idea2prd.adrs` (docs/adr/*.md or the single docs/ADR.md):
349
349
  - Extract coding-related decisions
350
350
  - Extract rejected alternatives as anti-patterns
@@ -188,9 +188,9 @@ If specific .ai-context file missing, extract from:
188
188
  |---------|----------------|------------------|
189
189
  | Overview | docs/prd/PRD.md | Research_Findings.md |
190
190
  | Architecture | docs/c4/*.mermaid | Architecture.md |
191
- | Key Decisions | docs/adr/*.md | Solution_Strategy.md |
191
+ | Key Decisions | detected_docs.idea2prd.adrs (docs/adr/*.md or docs/ADR.md) | Solution_Strategy.md |
192
192
  | Domain Model | docs/ddd/strategic/ | Specification.md |
193
- | Tech Stack | docs/adr/*-technology.md | Architecture.md |
193
+ | Tech Stack | adrs entries titled *technology* | Architecture.md |
194
194
  | Testing | docs/tests/*.feature | Refinement.md |
195
195
  | Deployment | docs/completion/ | Completion.md |
196
196
 
@@ -4,13 +4,21 @@ Templates for generating a feature navigation and contextual suggestion system.
4
4
  Combines CLAUDE.md roadmap, SessionStart hook for dynamic context, `/next` command,
5
5
  and `feature-navigator` skill for on-demand roadmap navigation.
6
6
 
7
- Architecture: **feature-roadmap.json** as single source of truth → consumed by hook, skill, and command.
7
+ Architecture: **feature-roadmap.json** as the system's single data file → consumed by hook, skill,
8
+ and command. (The SCHEMA of that file is defined once, in `.claude/commands/next.md`; this document
9
+ describes the system built on it, not the file's fields.)
8
10
 
9
11
  ---
10
12
 
11
13
  ## 1. Data File Template: `feature-roadmap.json`
12
14
 
13
- Generate as `.claude/feature-roadmap.json` — pre-populate from project's PRD/Specification:
15
+ Generate as `.claude/feature-roadmap.json` — pre-populate from project's PRD/Specification.
16
+
17
+ **Fields: follow `.claude/commands/next.md`, which holds the single schema.** In particular every
18
+ feature carries `priority` from the closed set `mvp | high | medium | low`; a roadmap without it
19
+ cannot be read by the shipped status line. The sketch below shows only the ENVELOPE and this system's
20
+ OPTIONAL extensions — `project`, `current_sprint`, `description`, `sprint`, `files` — which the
21
+ canonical schema does not forbid:
14
22
 
15
23
  ```json
16
24
  {
@@ -21,6 +29,7 @@ Generate as `.claude/feature-roadmap.json` — pre-populate from project's PRD/S
21
29
  "id": "{{feature-id}}",
22
30
  "name": "{{Feature Name}}",
23
31
  "description": "{{Brief description of the feature}}",
32
+ "priority": "mvp|high|medium|low",
24
33
  "status": "done|in_progress|next|planned|blocked",
25
34
  "files": ["src/path/to/main-file.ts"],
26
35
  "depends_on": ["{{other-feature-id}}"],
@@ -139,7 +139,7 @@ List ALL documentation files the /start should read.
139
139
  ```markdown
140
140
  - `docs/ddd/strategic/` → bounded contexts, context map
141
141
  - `docs/ddd/tactical/` → aggregates, entities, events
142
- - `docs/adr/` → technology decisions
142
+ - `detected_docs.idea2prd.adrs` (docs/adr/ or docs/ADR.md) → technology decisions
143
143
  - `docs/tests/*.feature` → Gherkin test scenarios
144
144
  ```
145
145
 
@@ -64,6 +64,13 @@ Always flag these terms and suggest specific replacements:
64
64
 
65
65
  **Score <50 = BLOCKED from development.** Provide rewrite suggestions.
66
66
 
67
+ **Blocking floor — the weakest link decides, never the average.** Independently of the score, a
68
+ requirement is BLOCKED if `Testable = 0` (no acceptance criteria) or `Completeness = 0` (rubric "No
69
+ AC"). A story can total 72/100 with neither, and 72 reads as "minor fixes" in the table above. A non-zero `Testable` or `Completeness` REQUIRES quoting the acceptance criteria it scores — no quote
70
+ means 0, because the agent that scores is the agent the floor binds. The
71
+ floor is closed at those two: `Measurable` and `Traceability` are deliberately excluded — see
72
+ `references/scoring-system.md` → "Blocking floor" for the worked case and the reason.
73
+
67
74
  ## Output Format
68
75
 
69
76
  ### Requirements Analysis Report
@@ -53,6 +53,34 @@ Total Score = INVEST Score (50%) + SMART Score (30%) + Quality Score (20%)
53
53
  | 50-69 | Fair | 🔶 REWORK | Significant clarification needed |
54
54
  | **0-49** | **Poor** | **🚫 BLOCKED** | **Requires complete rewrite** |
55
55
 
56
+ ### Blocking floor (overrides the total, both directions)
57
+
58
+ **The weakest link decides, never the average.** A requirement is BLOCKED — whatever its total says —
59
+ if either of these is zero:
60
+
61
+ | Criterion | Zero means | Why it vetoes |
62
+ |-----------|-----------|---------------|
63
+ | `Testable` (INVEST, 8) | no acceptance criteria exist | the gate exists to block untestable requirements |
64
+ | `Completeness` (Quality, 10) | "No AC" on the rubric above | nothing states what "done" is |
65
+
66
+ Without this floor the total alone lets an untestable requirement through. Worked case: a story with
67
+ NO acceptance criteria and NO test links loses `Testable` 8, `Completeness` 10 and `Traceability` 10,
68
+ keeps everything else, and totals **72/100** — above the 70 line, filed as "fix minor issues, then
69
+ proceed". The floor blocks it on `Testable = 0` and `Completeness = 0`.
70
+
71
+ **The floor keys on the artifact, not on the number you wrote.** The same agent scores these
72
+ criteria AND is bound by the floor, so a score is not evidence of anything — reporting `Testable = 4`
73
+ ("vague AC") on a story with no AC at all evades the veto without lying about the rubric. Therefore:
74
+
75
+ > A non-zero `Testable` or `Completeness` REQUIRES quoting the acceptance criteria being scored —
76
+ > the actual text, with its document and heading. `Testable = 4` means the AC exist and are vague:
77
+ > quote them. **No quote ⇒ the score is 0 ⇒ the floor applies.**
78
+
79
+ **The floor is CLOSED at these two.** `Measurable` and `Traceability` are deliberately NOT on it: a
80
+ requirement with no number is often correct ("the user can export the report as PDF" is specific,
81
+ testable and unmeasurable), so vetoing on `Measurable` would turn a false pass into a false block.
82
+ Widening this list needs the same kind of worked case as the one above.
83
+
56
84
  ## Quality Gate Rules
57
85
 
58
86
  ### BLOCKED (Score < 50)
@@ -382,16 +382,24 @@ view("/mnt/skills/user/problem-solver-enhanced/SKILL.md")
382
382
 
383
383
  **User Story Format:**
384
384
  ```
385
- As a [persona],
385
+ US-<nnn>: As a [persona],
386
386
  I want to [action],
387
387
  So that [benefit].
388
388
 
389
389
  Acceptance Criteria:
390
+ [SC-<story-id>-1]
390
391
  Given [context]
391
392
  When [action]
392
393
  Then [expected result]
393
394
  ```
394
395
 
396
+ Every user story carries an ID `US-<nnn>` — three digits, assigned in order, never reused even after
397
+ a story is deleted. Every acceptance scenario under it carries `SC-<US-id>-<n>`, numbered from 1
398
+ within its story: the scenarios of `US-007` are `SC-US-007-1`, `SC-US-007-2`, and so on. Phase 4
399
+ traces algorithms back to these IDs, and there is nothing to trace to if a scenario has no name — nor
400
+ anything reliable to trace to if two scenarios can end up with the same name, which is what the
401
+ never-reused rule prevents.
402
+
395
403
  **PRD Generation:**
396
404
  ```
397
405
  view("templates/prd.md")
@@ -437,6 +445,7 @@ type EntityName = {
437
445
  ## Core Algorithms
438
446
 
439
447
  ### Algorithm: [Name]
448
+ REALISES: [SC-… ids this algorithm implements, comma-separated]
440
449
  INPUT: [parameters]
441
450
  OUTPUT: [result]
442
451
 
@@ -471,6 +480,54 @@ Response (4xx/5xx):
471
480
  [Error categories and responses]
472
481
  ```
473
482
 
483
+ **Шаг 4.9 — ПОКРЫТИЕ СЦЕНАРИЕВ (обязательный, до чекпойнта).**
484
+
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
487
+ case, including the one where everything is covered**, because an absent block and a block saying
488
+ "all covered" are indistinguishable to the next reader:
489
+
490
+ ```
491
+ ## Scenario Coverage
492
+
493
+ Scenarios in Specification.md: [N] · claimed by an algorithm: [M]
494
+
495
+ Not claimed by any algorithm:
496
+ | Scenario | Reason |
497
+ |---|---|
498
+ | SC-… | ui-only |
499
+
500
+ Claimed by an algorithm but absent from Specification.md:
501
+ | Algorithm | Claimed ID |
502
+ |---|---|
503
+ | [name] | SC-… |
504
+ ```
505
+
506
+ **Both tables are required, and both may be the single word `none`.** A traceability check that runs
507
+ one way only is half a check: without the second table an algorithm can declare `REALISES: SC-US-009-3`
508
+ for a scenario nobody ever wrote, and the dangling reference reads exactly like coverage. `none` is
509
+ written out rather than left blank, because an empty table and a forgotten table look identical.
510
+
511
+ **Reasons are a CLOSED list of five**, and nothing else is accepted:
512
+
513
+ | Reason | Means |
514
+ |---|---|
515
+ | `ui-only` | realised entirely in the interface, no algorithm to write |
516
+ | `external-service` | performed by a third party, see `Architecture.md` → External Dependencies |
517
+ | `out-of-mvp-scope` | deliberately not built yet |
518
+ | `data-only` | satisfied by a schema or constraint, not by a procedure |
519
+ | `config-only` | satisfied by OUR OWN configuration — a server setting, a header, a policy file — with no procedure to write |
520
+
521
+ Free text is NOT a reason, and `N/A` is NOT a reason. A field that accepts anything records nothing:
522
+ the whole value of the list is that an unclaimed scenario has to be one of a small number of
523
+ recognisable things, and if it is none of them, the gap is real and belongs in the algorithms.
524
+
525
+ **What this establishes, and what it does not.** It establishes that a CLAIM exists and that its two
526
+ ends name each other. It does NOT establish that the algorithm's steps actually perform the check the
527
+ scenario describes — no comparison of names can. So this catches *"nobody wrote anything about this
528
+ scenario"*; it does not catch *"someone wrote a line that mentions it"*. Say so here rather than
529
+ letting a later reader assume the stronger thing.
530
+
474
531
  **[MANUAL] CP4:**
475
532
  ```
476
533
  ═══════════════════════════════════════════════════════════════
@@ -541,6 +598,51 @@ graph TB
541
598
  | Queue | | |
542
599
  | Infrastructure | | |
543
600
 
601
+ ## External Dependencies
602
+
603
+ Every capability this product needs from someone else's service. One row per capability, not one row
604
+ per vendor: "sends email" and "reports bounces" are two questions, and a provider can do one without
605
+ the other.
606
+
607
+ | Capability needed | Provider / API | Evidence | Verdict | Requirements relying on it |
608
+ |---|---|---|---|---|
609
+ | [what the product needs it to DO] | [service] | [link to the provider's own docs naming this capability] · checked [YYYY-MM-DD] | CONFIRMED | [REQ ids] |
610
+
611
+ **Evidence — what counts, and what does not.** Evidence is a link to the PROVIDER'S OWN documentation
612
+ that names the specific capability, plus the date it was checked, plus **a short verbatim QUOTE from
613
+ that page stating the capability**. These do NOT count, and each is a way this check gets faked: a
614
+ landing page or marketing page; a pricing page; recollection — "the model knows this API supports
615
+ it"; and **a URL nobody opened** — a plausible-looking link is the cheapest possible forgery, which
616
+ is exactly why the quote is required and the link alone is not enough. A capability nobody could cite
617
+ is not confirmed, and saying so is the point of the middle verdict.
618
+
619
+ **Verdicts — exactly three, because two would hide a difference that matters:**
620
+
621
+ | Verdict | Means | Consequence in Phase 2 |
622
+ |---|---|---|
623
+ | CONFIRMED | cited, and the citation names this capability | none |
624
+ | UNCONFIRMED | nobody could produce a citation | the REQUIREMENTS in that row's last column cannot enter Phase 3 — defer, remove or replace them. Unrelated work continues; the run is 🟡 CAVEATS at best and the row is NAMED in the report |
625
+ | CONTRADICTED | the provider's own docs say it cannot | 🔴 NEEDS WORK — the requirement rests on something that is not there |
626
+
627
+ `UNCONFIRMED` is not a failure to be hidden; it is the honest state of a project on a machine with no
628
+ web access. Collapsing it into CONFIRMED overstates what is known; collapsing it into CONTRADICTED
629
+ blocks work that may be perfectly fine — and would push people to write a citation that isn't one,
630
+ which is how a gate becomes theatre.
631
+
632
+ But it does not pass for free either. **The consequence is scoped to the REQUIREMENT, not to the
633
+ run:** the requirements listed in an `UNCONFIRMED` row do not enter Phase 3 until they are deferred,
634
+ removed, or rewritten onto something confirmable. Everything not resting on that capability proceeds.
635
+ Without this scoping an inventory of nothing but `UNCONFIRMED` rows would reach Phase 3 with a
636
+ caveat, and external feasibility would never have to be established at all — the check would be
637
+ optional in practice while looking mandatory on paper.
638
+
639
+ **If this product has no external dependencies**, write exactly that: *"No external dependencies —
640
+ this product calls no third-party service."* An empty section and an absent section are
641
+ indistinguishable, and only one of them means anything.
642
+
643
+ Names in the row above are PLACEHOLDERS. Do not copy a real provider or a real capability from any
644
+ example: what an API can do drifts, and a stale fact recorded as evidence is worse than none.
645
+
544
646
  ## Data Architecture
545
647
  [Data models, relationships, storage strategy]
546
648
 
@@ -551,6 +653,46 @@ graph TB
551
653
  [Horizontal/vertical scaling, bottlenecks]
552
654
  ```
553
655
 
656
+ **Шаг 5.9 — СВЕРКА С ПСЕВДОКОДОМ (обязательный, до чекпойнта).**
657
+
658
+ Фаза 4 написала модель данных ДО того, как эта фаза выбрала хранилище и технологии, и до сих пор
659
+ ничто их не сверяло. Именно отсюда берутся расхождения, которые всплывают уже в коде: поле осталось
660
+ булевым, когда схема получила перечисление; алгоритм пользуется полем, которого в схеме нет; у
661
+ статуса три значения в одном документе и пять в другом.
662
+
663
+ Перечитай в `Pseudocode.md` ДВЕ секции — `## Data Structures` и `## Core Algorithms` — и сверь их с
664
+ тем, что выбрано ЗДЕСЬ. Алгоритмы нужны обязательно: расхождение «алгоритм читает поле, которого в
665
+ схеме нет» по одним лишь структурам данных не обнаруживается. Ищи три вида расхождений:
666
+
667
+ - **смена типа** — поле объявлено одним типом, а хранилище требует другого (булево против перечисления);
668
+ - **отсутствующая колонка** — алгоритм читает или пишет поле, которого в схеме нет;
669
+ - **несовпадение набора значений** — у одного и того же поля разное число допустимых значений.
670
+
671
+ Какую сторону править — решается по РОЛИ документа, а не по старшинству. `Pseudocode.md` держит
672
+ ЛОГИЧЕСКУЮ модель (что означает поле), `Architecture.md` — ФИЗИЧЕСКУЮ (где и как оно лежит).
673
+ Поэтому: если эта фаза ввела осознанное физическое ограничение (тип хранилища, индекс, длина) —
674
+ правится `Pseudocode.md`; если выбранная технология НАРУШАЕТ требуемую семантику (теряются значения,
675
+ исчезает состояние, которым пользуется алгоритм) — меняется выбор ЗДЕСЬ, потому что требование
676
+ старше удобства реализации. Секция `## Data Architecture` этого документа остаётся на месте, но
677
+ описывает отображение на хранилище и связи, а НЕ пересказывает список полей: второй экземпляр списка
678
+ становится вторым местом, где начинается расхождение.
679
+
680
+ Результат записывается в `Architecture.md` ВСЕГДА, отдельным блоком:
681
+
682
+ ```markdown
683
+ ## Reconciliation with Pseudocode
684
+
685
+ | Сущность.поле | Вид расхождения | Что сделано |
686
+ |---|---|---|
687
+ | … | смена типа / отсутствующая колонка / несовпадение набора значений | … |
688
+ ```
689
+
690
+ Если сверка не нашла ничего — блок всё равно пишется, и он ОБЯЗАН назвать, что именно
691
+ сверялось: «Расхождений с `Pseudocode.md` не найдено. Сверены сущности: <перечисление>; алгоритмы:
692
+ <перечисление>.» Одна фраза «расхождений нет» без перечня — это церемония, которую модель напишет
693
+ не глядя; перечень делает утверждение проверяемым. Молчание не является результатом сверки: по нему
694
+ нельзя отличить «сверили и чисто» от «не сверяли».
695
+
554
696
  **[MANUAL] CP5:**
555
697
  ```
556
698
  ═══════════════════════════════════════════════════════════════