@dzhechkov/p-replicator 1.6.0 → 1.10.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 (48) hide show
  1. package/.dz-manifest.json +92 -32
  2. package/CHANGELOG.md +176 -0
  3. package/README.md +106 -4
  4. package/package.json +4 -4
  5. package/sbom.json +181 -31
  6. package/src/commands/doctor.js +43 -31
  7. package/src/commands/verify.js +27 -2
  8. package/src/utils.js +27 -0
  9. package/templates/.claude/commands/myinsights.md +22 -5
  10. package/templates/.claude/commands/replicate.md +57 -1
  11. package/templates/.claude/hooks/check-docs-complete.cjs +202 -0
  12. package/templates/.claude/hooks/check-growth-trace.cjs +191 -0
  13. package/templates/.claude/hooks/check-ports.cjs +36 -4
  14. package/templates/.claude/hooks/statusline.cjs +16 -5
  15. package/templates/.claude/rules/replicate-pipeline.md +14 -4
  16. package/templates/.claude/rules/skill-interface-protocol.md +9 -0
  17. package/templates/.claude/skills/brutal-honesty-review/scripts/assess-code.sh +40 -7
  18. package/templates/.claude/skills/brutal-honesty-review/scripts/assess-tests.sh +38 -11
  19. package/templates/.claude/skills/cc-toolkit-generator-enhanced/modules/03-generate-p0.md +6 -4
  20. package/templates/.claude/skills/cc-toolkit-generator-enhanced/modules/08-skill-composition.md +2 -2
  21. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/templates/ddd-hooks-commands.md +40 -4
  22. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/templates/feature-lifecycle.md +2 -2
  23. package/templates/.claude/skills/requirements-validator/SKILL.md +52 -0
  24. package/templates/.claude/skills/reverse-engineering-unicorn/modules/01-intelligence.md +4 -4
  25. package/templates/.claude/skills/reverse-engineering-unicorn/modules/02-product-customers.md +2 -2
  26. package/templates/.claude/skills/reverse-engineering-unicorn/modules/025-cjm-prototype.md +9 -1
  27. package/templates/.claude/skills/reverse-engineering-unicorn/modules/03-market-competition.md +3 -3
  28. package/templates/.claude/skills/reverse-engineering-unicorn/modules/04-business-finance.md +3 -3
  29. package/templates/.claude/skills/reverse-engineering-unicorn/modules/05-growth-engine.md +132 -12
  30. package/templates/.claude/skills/reverse-engineering-unicorn/modules/06-playbook-synthesis.md +1 -1
  31. package/templates/.claude/skills/sparc-prd-mini/SKILL.md +9 -9
  32. package/tests/snapshot/baseline.json +25 -23
  33. package/tests/unit/absence-is-not-emptiness.test.js +255 -0
  34. package/tests/unit/assess-scripts.test.js +150 -0
  35. package/tests/unit/check-docs-complete.test.js +292 -0
  36. package/tests/unit/check-growth-trace.test.js +188 -0
  37. package/tests/unit/check-ports.test.js +99 -0
  38. package/tests/unit/generated-guard-templates.test.js +134 -0
  39. package/tests/unit/growth-axes-and-compliance.test.js +169 -0
  40. package/tests/unit/growth-gate-conditional.test.js +122 -0
  41. package/tests/unit/growth-module-b2b-gate.test.js +20 -2
  42. package/tests/unit/growth-requirements-bridge.test.js +127 -0
  43. package/tests/unit/guard-forms.test.js +302 -0
  44. package/tests/unit/insights-docs-tell-the-truth.test.js +84 -0
  45. package/tests/unit/module-copy-identity.test.js +106 -0
  46. package/tests/unit/shipped-suite-context.test.js +142 -0
  47. package/tests/unit/skill-paths-prebaked.test.js +174 -0
  48. package/tests/unit/sync-templates-guard.test.js +31 -1
@@ -198,10 +198,19 @@ function parsePlans() {
198
198
  return safeListDir(dir).filter((f) => f.endsWith('.md')).length;
199
199
  }
200
200
 
201
+ /**
202
+ * THREE states, because two were not enough.
203
+ *
204
+ * `{count:0}` was returned both for a carrier that does not exist and for one that exists and holds
205
+ * nothing — so a project that had never recorded an insight rendered identically to one being used
206
+ * and found empty. That indistinguishability is what let 27 recorded insights become 0 across four
207
+ * real projects without any surface saying so (MEASURED 2026-08-27).
208
+ */
201
209
  function parseInsights() {
202
210
  const p = path.join(CWD, '.claude', 'insights', 'index.md');
203
211
  const text = safeReadText(p);
204
- if (!text) return { count: 0, lastDate: null };
212
+ if (text === null || text === undefined) return { count: 0, lastDate: null, started: false };
213
+ if (!text.trim()) return { count: 0, lastDate: null, started: true };
205
214
  const headings = text.match(/^##\s+\d{4}-\d{2}-\d{2}/gm) || [];
206
215
  // Last date: extract from last heading
207
216
  let lastDate = null;
@@ -210,7 +219,7 @@ function parseInsights() {
210
219
  const m = last.match(/\d{4}-\d{2}-\d{2}/);
211
220
  if (m) lastDate = m[0];
212
221
  }
213
- return { count: headings.length, lastDate };
222
+ return { count: headings.length, lastDate, started: true };
214
223
  }
215
224
 
216
225
  function parseToolkit() {
@@ -236,7 +245,7 @@ function parseExpectedToolkit() {
236
245
  commandsExpected: 11,
237
246
  agentsExpected: 4, // pre-shipped only (project agents are extra)
238
247
  rulesExpected: 6, // pre-shipped only (project rules are extra)
239
- hooksExpected: 7, // 4 v1.4.1 hooks + statusline + state-update + check-ports
248
+ hooksExpected: 9, // 4 v1.4.1 hooks + statusline + state-update + 3 deliberate checks
240
249
  };
241
250
  }
242
251
 
@@ -467,7 +476,9 @@ function buildToolkit(toolkit, expected) {
467
476
 
468
477
  function buildStatus(insights, lastTest, mcpServers, settingsStatus, keysarium) {
469
478
  const parts = [];
470
- parts.push(`💡 ${bold('Insights')} ${insights.count > 0 ? green('●' + insights.count) : '0'}` +
479
+ // '0' meant two different things: no carrier at all, and a carrier holding nothing. A dash
480
+ // says the first; a zero says the second. The reader can now tell which one they are looking at.
481
+ parts.push(`💡 ${bold('Insights')} ${insights.count > 0 ? green('●' + insights.count) : insights.started ? '0' : dim('—')}` +
471
482
  (insights.lastDate ? ` ${dim('(' + insights.lastDate + ')')}` : ''));
472
483
 
473
484
  if (lastTest && typeof lastTest.passed === 'number') {
@@ -498,7 +509,7 @@ function main() {
498
509
  const validation = safeRun(() => parseValidationScore(), null);
499
510
  const adrs = safeRun(() => parseAdrs(), 0);
500
511
  const plans = safeRun(() => parsePlans(), 0);
501
- const insights = safeRun(() => parseInsights(), { count: 0, lastDate: null });
512
+ const insights = safeRun(() => parseInsights(), { count: 0, lastDate: null, started: false });
502
513
  const toolkit = safeRun(() => parseToolkit(), { skills: 0, commands: 0, agents: 0, rules: 0, hooks: 0 });
503
514
  const expected = parseExpectedToolkit();
504
515
  const settingsStatus = safeRun(() => parseSettingsStatus(manifest), null);
@@ -32,7 +32,9 @@ absent or its prerequisites are unmet, skip the UI-clone step and log a warning.
32
32
  When executing skills during the pipeline:
33
33
 
34
34
  1. Read the skill's `SKILL.md` file from `.claude/skills/[name]/SKILL.md`
35
- 2. When a skill references `/mnt/skills/user/[name]/` — read from `.claude/skills/[name]/` instead
35
+ 2. When a skill references `/mnt/skills/user/[name]/` — read from `.claude/skills/[name]/` instead.
36
+ *(The ten PRE-SHIPPED skills no longer contain such paths — since 1.8.0 they are pre-baked. This
37
+ rule is for skills you bring yourself.)*
36
38
  3. When a skill references `/mnt/user-data/uploads/` — read from `docs/` instead
37
39
  4. When a skill outputs to `/output/` — write to `docs/` or project root instead
38
40
  5. `goap-research` skill name maps to `goap-research-ed25519` in this repo
@@ -58,6 +60,7 @@ All generated files go directly into the project. Never create a separate output
58
60
 
59
61
  | Category | Path |
60
62
  |----------|------|
63
+ | Product Discovery Brief (Phase 0) | `docs/product-discovery-brief.md` |
61
64
  | SPARC documentation | `docs/` |
62
65
  | Validation report | `docs/validation-report.md` |
63
66
  | BDD scenarios | `docs/test-scenarios.md` |
@@ -154,9 +157,16 @@ are project-agnostic and can be enhanced (read by Phase 3) but never recreated.
154
157
  **Rules (6):** `replicate-pipeline`, `skill-interface-protocol`, `git-workflow`,
155
158
  `insights-capture`, `feature-lifecycle`, `docker-ports`
156
159
 
157
- **Hooks:** `.claude/settings.json` (SessionStart + Stop) + cross-platform Node
158
- scripts in `.claude/hooks/` (`session-insights.cjs`, `autocommit-roadmap.cjs`,
159
- `autocommit-insights.cjs`, `autocommit-plans.cjs`)
160
+ **Hooks (8 files in `.claude/hooks/`, cross-platform Node).** Only four are wired to an
161
+ event in `.claude/settings.json`; the rest are utilities you invoke deliberately, and the
162
+ difference matters — a hook of this package is NON-BLOCKING by contract and can only print.
163
+
164
+ *Wired to an event:* `session-insights.cjs` (SessionStart) · `autocommit-roadmap.cjs`,
165
+ `autocommit-insights.cjs`, `autocommit-plans.cjs` (Stop)
166
+
167
+ *Invoked deliberately, wired to nothing:* `statusline.cjs` (a statusLine, not a hook) ·
168
+ `state-update.cjs` (argv utility) · `check-ports.cjs` (docker-ports Правило №0, exits 0/1/2) ·
169
+ `check-growth-trace.cjs` (did the M5 growth seed reach `docs/Specification.md`, exits 0/1/2)
160
170
 
161
171
  ### Generated by /replicate Phase 3 (project-specific — create new)
162
172
 
@@ -39,6 +39,15 @@ view() .claude/skills/[skill-name]/references/[file].md
39
39
 
40
40
  Skills originating from claude.ai use `/mnt/` paths. Apply these rewrites in order:
41
41
 
42
+ > **The ten skills this package ships no longer need this.** Since p-replicator 1.8.0 their paths are
43
+ > pre-baked: `.claude/skills/<name>/` resolves directly, with no rewrite step. The table below stays
44
+ > because a skill YOU bring from claude.ai still needs it — and because the toolkit generator's own
45
+ > output-scanning instructions describe this transform.
46
+ >
47
+ > One case the table cannot express: a skill referenced but NOT installed. Rewriting its path yields
48
+ > a local-looking path that resolves to nothing, which is worse than an obviously foreign one. Declare
49
+ > it OPTIONAL with a fallback (§6) instead.
50
+
42
51
  | Source Pattern | Target Pattern | Notes |
43
52
  |----------------|----------------|-------|
44
53
  | `/mnt/skills/user/[name]/` | `.claude/skills/[name]/` | Skill root directories |
@@ -15,12 +15,34 @@ echo ""
15
15
 
16
16
  # Check if file argument provided
17
17
  if [ -z "$1" ]; then
18
+ echo "⚠️ check did NOT run: no argument given"
18
19
  echo "Usage: $0 <file-or-directory>"
19
- exit 1
20
+ exit 2
20
21
  fi
21
22
 
22
23
  TARGET="$1"
23
24
 
25
+ # ── Findings counter ─────────────────────────────────────────────────────────
26
+ #
27
+ # This script DETECTED correctly and could not FAIL. MEASURED 2026-08-27: deliberately awful input
28
+ # produced red verdicts on screen and exit 0, while a nonexistent path exited 1 — "I could not
29
+ # check" was louder than "I found violations", so any gate reading 1 could not tell them apart.
30
+ #
31
+ # Nothing about the detection changed. What was missing is that nobody counted.
32
+ #
33
+ # 0 ran, found nothing
34
+ # 1 ran, found violations — the count is printed
35
+ # 2 COULD NOT CHECK: no argument, or a target that does not exist
36
+ FINDINGS=0
37
+ finding() { FINDINGS=$((FINDINGS + 1)); }
38
+
39
+ if [ ! -e "$TARGET" ]; then
40
+ echo "⚠️ check did NOT run: '$TARGET' does not exist"
41
+ echo " → This is NOT a clean bill: nothing was examined."
42
+ exit 2
43
+ fi
44
+
45
+
24
46
  # Function to assess correctness
25
47
  assess_correctness() {
26
48
  echo "📊 CORRECTNESS CHECK"
@@ -28,7 +50,7 @@ assess_correctness() {
28
50
 
29
51
  # Check for common bug patterns
30
52
  if grep -r "TODO\|FIXME\|BUG\|HACK" "$TARGET" 2>/dev/null; then
31
- echo -e "${RED}🔴 FAILING: Found TODO/FIXME/BUG/HACK comments${NC}"
53
+ echo -e "${RED}🔴 FAILING: Found TODO/FIXME/BUG/HACK comments${NC}"; finding
32
54
  echo " → This code admits it's broken. Fix it before review."
33
55
  return 0
34
56
  fi
@@ -51,14 +73,14 @@ assess_performance() {
51
73
  # Check for nested loops (potential O(n²))
52
74
  nested_loops=$(grep -r "for.*{" "$TARGET" | wc -l)
53
75
  if [ "$nested_loops" -gt 5 ]; then
54
- echo -e "${RED}🔴 FAILING: Found $nested_loops loops${NC}"
76
+ echo -e "${RED}🔴 FAILING: Found $nested_loops loops${NC}"; finding
55
77
  echo " → Are you creating O(n²) complexity where O(n) exists?"
56
78
  echo " → Use hash maps, sets, or better algorithms."
57
79
  fi
58
80
 
59
81
  # Check for synchronous I/O in hot paths
60
82
  if grep -r "readFileSync\|writeFileSync" "$TARGET" 2>/dev/null; then
61
- echo -e "${RED}🔴 FAILING: Synchronous file I/O detected${NC}"
83
+ echo -e "${RED}🔴 FAILING: Synchronous file I/O detected${NC}"; finding
62
84
  echo " → You're blocking the event loop. Use async operations."
63
85
  fi
64
86
 
@@ -74,7 +96,7 @@ assess_error_handling() {
74
96
  # Check for try/catch usage
75
97
  try_count=$(grep -r "try\|catch" "$TARGET" 2>/dev/null | wc -l)
76
98
  if [ "$try_count" -eq 0 ]; then
77
- echo -e "${RED}🔴 FAILING: No error handling found${NC}"
99
+ echo -e "${RED}🔴 FAILING: No error handling found${NC}"; finding
78
100
  echo " → What happens when this code fails? It crashes."
79
101
  else
80
102
  echo -e "${GREEN}✓ Found error handling (verify it's sufficient)${NC}"
@@ -82,7 +104,7 @@ assess_error_handling() {
82
104
 
83
105
  # Check for empty catch blocks
84
106
  if grep -A 1 "catch" "$TARGET" 2>/dev/null | grep -q "^\s*}"; then
85
- echo -e "${RED}🔴 FAILING: Empty catch blocks detected${NC}"
107
+ echo -e "${RED}🔴 FAILING: Empty catch blocks detected${NC}"; finding
86
108
  echo " → Swallowing errors silently is worse than crashing."
87
109
  fi
88
110
  }
@@ -118,7 +140,7 @@ assess_testability() {
118
140
  if [ -d "tests" ] || [ -d "test" ] || [ -d "__tests__" ]; then
119
141
  echo -e "${GREEN}✓ Test directory exists${NC}"
120
142
  else
121
- echo -e "${RED}🔴 FAILING: No test directory found${NC}"
143
+ echo -e "${RED}🔴 FAILING: No test directory found${NC}"; finding
122
144
  echo " → Where are the tests? Did you even test this?"
123
145
  fi
124
146
 
@@ -177,3 +199,14 @@ echo " - Tests exist and pass"
177
199
  echo " - Code is clear and maintainable"
178
200
  echo ""
179
201
  echo "If you wouldn't deploy this to production, don't submit it for review."
202
+
203
+ # ── Verdict ──────────────────────────────────────────────────────────────────
204
+ # ADDED, never substituted: the closing prose above is this skill's character and a reader wants it.
205
+ # What follows is the same answer in a form a gate can act on.
206
+ echo ""
207
+ if [ "$FINDINGS" -gt 0 ]; then
208
+ echo "VERDICT: $FINDINGS finding(s). Not ready."
209
+ exit 1
210
+ fi
211
+ echo "VERDICT: 0 findings."
212
+ exit 0
@@ -15,17 +15,33 @@ echo ""
15
15
 
16
16
  # Check if test directory argument provided
17
17
  if [ -z "$1" ]; then
18
+ echo "⚠️ check did NOT run: no argument given"
18
19
  echo "Usage: $0 <test-directory>"
19
- exit 1
20
+ exit 2
20
21
  fi
21
22
 
22
23
  TEST_DIR="$1"
23
24
 
25
+ # ── Findings counter ─────────────────────────────────────────────────────────
26
+ #
27
+ # This script DETECTED correctly and could not FAIL. MEASURED 2026-08-27: deliberately awful input
28
+ # produced red verdicts on screen and exit 0, while a nonexistent path exited 1 — "I could not
29
+ # check" was louder than "I found violations", so any gate reading 1 could not tell them apart.
30
+ #
31
+ # Nothing about the detection changed. What was missing is that nobody counted.
32
+ #
33
+ # 0 ran, found nothing
34
+ # 1 ran, found violations — the count is printed
35
+ # 2 COULD NOT CHECK: no argument, or a target that does not exist
36
+ FINDINGS=0
37
+ finding() { FINDINGS=$((FINDINGS + 1)); }
38
+
39
+
24
40
  # Check if test directory exists
25
41
  if [ ! -d "$TEST_DIR" ]; then
26
- echo -e "${RED}🔴 FAILING: Test directory '$TEST_DIR' doesn't exist${NC}"
27
- echo " → Where are the tests? Did you even write any?"
28
- exit 1
42
+ echo "⚠️ check did NOT run: test directory '$TEST_DIR' does not exist"
43
+ echo " → This is NOT a clean bill: nothing was examined."
44
+ exit 2
29
45
  fi
30
46
 
31
47
  # Function to assess coverage
@@ -42,7 +58,7 @@ assess_coverage() {
42
58
  coverage=$(npm run test:coverage 2>&1 | grep -oP '\d+\.\d+(?=%)' | head -1 || echo "0")
43
59
 
44
60
  if (( $(echo "$coverage < 50" | bc -l) )); then
45
- echo -e "${RED}🔴 RAW: ${coverage}% coverage${NC}"
61
+ echo -e "${RED}🔴 RAW: ${coverage}% coverage${NC}"; finding
46
62
  echo " → This is embarrassing. You're barely testing anything."
47
63
  elif (( $(echo "$coverage < 80" | bc -l) )); then
48
64
  echo -e "${YELLOW}🟡 ACCEPTABLE: ${coverage}% coverage${NC}"
@@ -83,7 +99,7 @@ assess_edge_cases() {
83
99
  done
84
100
 
85
101
  if [ "$found_count" -eq 0 ]; then
86
- echo -e "${RED}🔴 RAW: No edge cases tested${NC}"
102
+ echo -e "${RED}🔴 RAW: No edge cases tested${NC}"; finding
87
103
  echo " → You're only testing the happy path. That's not testing."
88
104
  elif [ "$found_count" -lt 3 ]; then
89
105
  echo -e "${YELLOW}🟡 ACCEPTABLE: Found $found_count edge case patterns${NC}"
@@ -102,7 +118,7 @@ assess_clarity() {
102
118
  # Check for descriptive test names
103
119
  unclear_tests=$(grep -r "test('test" "$TEST_DIR" 2>/dev/null | wc -l)
104
120
  if [ "$unclear_tests" -gt 0 ]; then
105
- echo -e "${RED}🔴 RAW: Found $unclear_tests unclear test names${NC}"
121
+ echo -e "${RED}🔴 RAW: Found $unclear_tests unclear test names${NC}"; finding
106
122
  echo " → 'test1', 'test2' - What are you testing? Use descriptive names."
107
123
  fi
108
124
 
@@ -129,7 +145,7 @@ assess_speed() {
129
145
  duration=$((end_time - start_time))
130
146
 
131
147
  if [ "$duration" -gt 60 ]; then
132
- echo -e "${RED}🔴 RAW: Tests took ${duration}s${NC}"
148
+ echo -e "${RED}🔴 RAW: Tests took ${duration}s${NC}"; finding
133
149
  echo " → Unit tests should run in seconds, not minutes."
134
150
  echo " → Are you calling real databases/networks?"
135
151
  elif [ "$duration" -gt 10 ]; then
@@ -139,7 +155,7 @@ assess_speed() {
139
155
  echo -e "${GREEN}🟢 MICHELIN STAR: Tests took ${duration}s${NC}"
140
156
  fi
141
157
  else
142
- echo -e "${RED}🔴 FAILING: Tests don't even pass${NC}"
158
+ echo -e "${RED}🔴 FAILING: Tests don't even pass${NC}"; finding
143
159
  echo " → Fix your broken tests before worrying about speed."
144
160
  fi
145
161
  }
@@ -152,7 +168,7 @@ assess_stability() {
152
168
 
153
169
  # Check for flaky patterns
154
170
  if grep -ri "setTimeout\|sleep\|wait" "$TEST_DIR" > /dev/null 2>&1; then
155
- echo -e "${RED}🔴 RAW: Timing-based tests detected${NC}"
171
+ echo -e "${RED}🔴 RAW: Timing-based tests detected${NC}"; finding
156
172
  echo " → You're creating flaky tests. Use proper async/await."
157
173
  fi
158
174
 
@@ -166,7 +182,7 @@ assess_stability() {
166
182
  done
167
183
 
168
184
  if [ "$failures" -gt 0 ]; then
169
- echo -e "${RED}🔴 RAW: Tests failed $failures/3 times${NC}"
185
+ echo -e "${RED}🔴 RAW: Tests failed $failures/3 times${NC}"; finding
170
186
  echo " → FLAKY TESTS. These are worse than no tests."
171
187
  echo " → Fix the non-determinism before merging."
172
188
  else
@@ -221,3 +237,14 @@ echo " - 0% flaky"
221
237
  echo " - Independent tests"
222
238
  echo ""
223
239
  echo "You know what good tests look like. Why aren't you writing them?"
240
+
241
+ # ── Verdict ──────────────────────────────────────────────────────────────────
242
+ # ADDED, never substituted: the closing prose above is this skill's character and a reader wants it.
243
+ # What follows is the same answer in a form a gate can act on.
244
+ echo ""
245
+ if [ "$FINDINGS" -gt 0 ]; then
246
+ echo "VERDICT: $FINDINGS finding(s). Not ready."
247
+ exit 1
248
+ fi
249
+ echo "VERDICT: 0 findings."
250
+ exit 0
@@ -422,7 +422,7 @@ Copy these 6 skills from the user's skill set into `.claude/skills/`:
422
422
  |---|-------|-------------|-------------|
423
423
  | 11 | sparc-prd-mini | `/mnt/skills/user/sparc-prd-mini/` | `.claude/skills/sparc-prd-mini/` |
424
424
  | 12 | explore | `/mnt/skills/user/explore/` | `.claude/skills/explore/` |
425
- | 13 | goap-research | `/mnt/skills/user/goap-research/` | `.claude/skills/goap-research/` |
425
+ | 13 | goap-research | `/mnt/skills/user/goap-research/` | `.claude/skills/goap-research-ed25519/` |
426
426
  | 14 | problem-solver-enhanced | `/mnt/skills/user/problem-solver-enhanced/` | `.claude/skills/problem-solver-enhanced/` |
427
427
  | 15 | requirements-validator | `/mnt/skills/user/requirements-validator/` | `.claude/skills/requirements-validator/` |
428
428
  | 16 | brutal-honesty-review | `/mnt/skills/user/brutal-honesty-review/` | `.claude/skills/brutal-honesty-review/` |
@@ -438,7 +438,7 @@ After copying, rewrite ALL `view()` paths in `sparc-prd-mini/SKILL.md`:
438
438
  External skill paths (3 rewrites):
439
439
  ```
440
440
  /mnt/skills/user/explore/SKILL.md -> .claude/skills/explore/SKILL.md
441
- /mnt/skills/user/goap-research/SKILL.md -> .claude/skills/goap-research/SKILL.md
441
+ /mnt/skills/user/goap-research/SKILL.md -> .claude/skills/goap-research-ed25519/SKILL.md
442
442
  /mnt/skills/user/problem-solver-enhanced/SKILL.md -> .claude/skills/problem-solver-enhanced/SKILL.md
443
443
  ```
444
444
 
@@ -458,10 +458,12 @@ Lines 951-953: Dependency Version Note -- update paths to .claude/skills/
458
458
 
459
459
  **Note on `goap-research` name mapping:** The skill name `goap-research` in the
460
460
  lifecycle context maps to `goap-research-ed25519` in this repository. Ensure the
461
- correct directory name is used when copying.
461
+ correct directory name is used when copying — the **Output paths** below therefore name
462
+ `goap-research-ed25519`, not `goap-research`. Until 2026-08-27 they named the short form, which is
463
+ the alias and never a real directory: the list contradicted the note directly above it.
462
464
 
463
465
  **Output paths:** `.claude/skills/sparc-prd-mini/`, `.claude/skills/explore/`,
464
- `.claude/skills/goap-research/`, `.claude/skills/problem-solver-enhanced/`,
466
+ `.claude/skills/goap-research-ed25519/`, `.claude/skills/problem-solver-enhanced/`,
465
467
  `.claude/skills/requirements-validator/`, `.claude/skills/brutal-honesty-review/`
466
468
 
467
469
  ---
@@ -201,8 +201,8 @@ FOR each copied_file IN target_skill_directory:
201
201
 
202
202
  ```
203
203
  # Before (claude.ai format):
204
- Read `/mnt/skills/user/explore/SKILL.md` for clarification protocol.
205
- Scan `/mnt/user-data/uploads/` for documents.
204
+ Read `.claude/skills/explore/SKILL.md` for clarification protocol.
205
+ Scan `docs/` for documents.
206
206
  Write output to `/output/validation-report.md`.
207
207
 
208
208
  # After (Claude Code local format):
@@ -70,13 +70,43 @@ Enhanced hooks and commands for DDD-aware Claude Code instruments.
70
70
  #!/bin/bash
71
71
  # Validates aggregate doesn't exceed size limits
72
72
  # Source: Fitness Function FF-02
73
+ #
74
+ # THREE exit codes, and the third is the point:
75
+ # 0 within limits
76
+ # 1 over the limit — the count and the limit are both reported
77
+ # 2 THE CHECK DID NOT RUN — unreadable file, or an unsubstituted threshold
78
+ #
79
+ # A guard that answers "OK" when it could not look turns an unknown into a reassurance. MEASURED
80
+ # before this contract existed: four declarations minified onto ONE line reported OK against a limit
81
+ # of two, a missing file reported OK, and an unsubstituted {{...}} placeholder reported OK FOREVER —
82
+ # `[ 4 -gt "{{MAX_ENTITIES_FROM_FITNESS}}" ]` is an invalid comparison, so the `if` is simply false.
73
83
 
74
84
  FILE="$1"
75
85
  MAX_ENTITIES={{MAX_ENTITIES_FROM_FITNESS}}
76
86
  MAX_METHODS={{MAX_METHODS_FROM_FITNESS}}
77
87
 
78
- # Count entities (rough heuristic)
79
- ENTITY_COUNT=$(grep -c "class.*Entity" "$FILE" 2>/dev/null || echo 0)
88
+ if [ -z "$FILE" ]; then
89
+ echo "⚠️ check did NOT run: no file given (usage: $0 <file>)"
90
+ exit 2
91
+ fi
92
+ if [ ! -r "$FILE" ]; then
93
+ echo "⚠️ check did NOT run: cannot read $FILE"
94
+ exit 2
95
+ fi
96
+ # An unsubstituted placeholder must REFUSE, not pass. Otherwise a generator that failed to
97
+ # substitute ships a guard that can never say no, and says nothing about it.
98
+ case "$MAX_ENTITIES" in
99
+ ''|*[!0-9]*)
100
+ echo "⚠️ check did NOT run: MAX_ENTITIES is not a number ('$MAX_ENTITIES')."
101
+ echo " The generator did not substitute {{MAX_ENTITIES_FROM_FITNESS}}."
102
+ exit 2
103
+ ;;
104
+ esac
105
+
106
+ # OCCURRENCES, not lines: `grep -c` counts matching LINES, so four declarations on one line count
107
+ # as one. `|| true` because grep exits 1 when nothing matches, which is a legitimate count of zero.
108
+ ENTITY_COUNT=$(grep -oE "class[A-Za-z0-9_ ]*Entity" "$FILE" | wc -l | tr -d ' ')
109
+ [ -z "$ENTITY_COUNT" ] && ENTITY_COUNT=0
80
110
 
81
111
  if [ "$ENTITY_COUNT" -gt "$MAX_ENTITIES" ]; then
82
112
  echo "❌ VIOLATION: Aggregate has $ENTITY_COUNT entities (max: $MAX_ENTITIES)"
@@ -84,7 +114,7 @@ if [ "$ENTITY_COUNT" -gt "$MAX_ENTITIES" ]; then
84
114
  exit 1
85
115
  fi
86
116
 
87
- echo "✅ Aggregate size OK"
117
+ echo "✅ Aggregate size OK ($ENTITY_COUNT entities, max $MAX_ENTITIES)"
88
118
  exit 0
89
119
  ```
90
120
 
@@ -114,7 +144,13 @@ if [ "$VIOLATIONS" -gt 0 ]; then
114
144
  echo "Found $VIOLATIONS potential DDD violations"
115
145
  fi
116
146
 
117
- exit 0 # Warnings only, don't block
147
+ # ADVISORY, by design and stated out loud. This reporter never blocks: it prints what it noticed
148
+ # and exits 0 whatever it found. That is a legitimate shape — but a reader must not mistake a
149
+ # reporter for a gate, so it says so in its OWN OUTPUT rather than only in a comment nobody reads.
150
+ echo ""
151
+ echo "ℹ️ advisory only — this reporter never blocks (exit 0 regardless of findings)."
152
+ echo " For a check that can refuse, see validate-aggregate-size.sh (exit 0/1/2)."
153
+ exit 0
118
154
  ```
119
155
 
120
156
  ---
@@ -12,7 +12,7 @@ When generating the toolkit, **copy these skills from the user's skill set** int
12
12
  # Source paths (Claude.ai user skills)
13
13
  /mnt/skills/user/sparc-prd-mini/ → .claude/skills/sparc-prd-mini/
14
14
  /mnt/skills/user/explore/ → .claude/skills/explore/
15
- /mnt/skills/user/goap-research/ → .claude/skills/goap-research/
15
+ /mnt/skills/user/goap-research/ → .claude/skills/goap-research-ed25519/
16
16
  /mnt/skills/user/problem-solver-enhanced/ → .claude/skills/problem-solver-enhanced/
17
17
  /mnt/skills/user/requirements-validator/ → .claude/skills/requirements-validator/
18
18
  /mnt/skills/user/brutal-honesty-review/ → .claude/skills/brutal-honesty-review/
@@ -26,7 +26,7 @@ After copying, rewrite ALL `view()` paths in `sparc-prd-mini/SKILL.md`:
26
26
  External skill paths (3):
27
27
  ```
28
28
  /mnt/skills/user/explore/SKILL.md → .claude/skills/explore/SKILL.md
29
- /mnt/skills/user/goap-research/SKILL.md → .claude/skills/goap-research/SKILL.md
29
+ /mnt/skills/user/goap-research/SKILL.md → .claude/skills/goap-research-ed25519/SKILL.md
30
30
  /mnt/skills/user/problem-solver-enhanced/SKILL.md → .claude/skills/problem-solver-enhanced/SKILL.md
31
31
  ```
32
32
 
@@ -134,6 +134,58 @@ apply additional security validation:
134
134
  - Cross-tenant access attempt (if multi-tenant)
135
135
  - Rate limiting / brute force scenario (if auth endpoint)
136
136
 
137
+ ### Growth Traceability (scoring: +5 present / +0 not applicable / -10 applicable but absent)
138
+
139
+ Phase 0's M5 module analyses how a competitor grows and emits a `Growth Requirements Seed` table of
140
+ `FR-GROWTH-<nnn>` draft obligations into `docs/product-discovery-brief.md`. This criterion asks one
141
+ question: **did those obligations survive into `docs/Specification.md`, or were they analysed and
142
+ dropped?**
143
+
144
+ **APPLICABILITY — decide this FIRST, and it is not about project type.** The criterion applies when
145
+ **acquisition or adoption is in scope** — the same condition `/replicate` already gates M5 on
146
+ ("If acquisition/adoption in scope (incl. B2B)"). Concretely:
147
+
148
+ | Situation | Applicable? | Score |
149
+ |---|:---:|---|
150
+ | `docs/product-discovery-brief.md` exists and its seed table has ≥1 `FR-GROWTH-nnn` row | YES | +5 traced · -10 not traced |
151
+ | The brief exists and its seed table says `нет` / is empty | no | +0 |
152
+ | No acquisition or adoption objective (internal tool, on-prem, replacement of an existing internal system) | no | +0 |
153
+ | `docs/product-discovery-brief.md` is ABSENT | no | +0 — see below |
154
+
155
+ **An absent brief is +0, never -10.** Absence means Phase 0 did not run (the `--from-docs` entry
156
+ skips it). Penalising a project for not running an optional phase would send every `--from-docs`
157
+ project into a permanent NEEDS WORK loop, which is the exact trap already closed for the Measurable
158
+ criterion. "Phase 0 did not run" is not "the growth requirements are missing".
159
+
160
+ **What TRACED means.** For each `FR-GROWTH-nnn` row in the brief, one of two things is true in
161
+ `docs/Specification.md`:
162
+
163
+ - the id `FR-GROWTH-nnn` appears (case-sensitive, the exact token — not a title, not a paraphrase), **or**
164
+ - the requirement was consciously rejected, and the rejection is written down with its reason.
165
+
166
+ A silently dropped row is the defect. A row rejected on the record is not.
167
+
168
+ | Check | Red Flags |
169
+ |-------|-----------|
170
+ | Every non-SPECULATIVE seed row is traced or rejected on the record | ids present in the brief, absent from the Specification, no rejection noted |
171
+ | Rejections carry a reason | "не берём" with no reason — indistinguishable from forgetting |
172
+ | `SPECULATIVE` rows were not promoted silently | a `[H]`-sourced row promoted to a firm requirement with no human decision recorded |
173
+
174
+ **Scoring Bonus:** +5 if every applicable seed row is traced or rejected on the record, +0 if not
175
+ applicable per the table above, -10 if the seed table carries rows and the Specification traces none
176
+ of them (BLOCKED if the score drops below 50).
177
+
178
+ **This criterion scores OUTSIDE the 100-point INVEST/SMART table**, exactly like Security. It adds no
179
+ weight to any existing criterion — the weight table and everything derived from it are unchanged.
180
+
181
+ **Honest limit.** This proves an obligation was CARRIED FORWARD, not that it was built, and not that
182
+ copying the competitor's growth move is lawful. Legality is not assessed anywhere in this pipeline.
183
+
184
+ **Deterministic counterpart.** `node .claude/hooks/check-growth-trace.cjs .` answers the same
185
+ question mechanically (0 traced · 1 rows present and none traced · 2 the check did not run). This
186
+ section is a prose gate read by a model; the utility is the deterministic one. Run it when the answer
187
+ has to be more than a judgement.
188
+
137
189
  ### BDD Scenario Generation
138
190
 
139
191
  For each requirement, generate scenarios covering:
@@ -38,7 +38,7 @@
38
38
 
39
39
  ### 🔵 Режим DEEP
40
40
 
41
- > ⚙️ **Перед началом:** `view(/mnt/skills/user/goap-research-ed25519/SKILL.md)`
41
+ > ⚙️ **Перед началом:** `view(.claude/skills/goap-research-ed25519/SKILL.md)`
42
42
  > Примени GOAP-методологию вместо статического списка.
43
43
 
44
44
  **Phase 1 — State Assessment:**
@@ -110,7 +110,7 @@ confidence = base_reliability × recency_factor
110
110
  ### 🟣 Режим VERIFIED (Ed25519)
111
111
 
112
112
  > ⚙️ **Перед началом:**
113
- > 1. `view(/mnt/skills/user/goap-research-ed25519/SKILL.md)`
113
+ > 1. `view(.claude/skills/goap-research-ed25519/SKILL.md)`
114
114
  > 2. Установи зависимости:
115
115
  > ```bash
116
116
  > python3 -m venv .venv
@@ -120,8 +120,8 @@ confidence = base_reliability × recency_factor
120
120
  > 3. Запусти инициализацию:
121
121
  > ```python
122
122
  > # Скопируй и запусти скрипт из:
123
- > # /mnt/skills/user/goap-research-ed25519/scripts/ed25519_verifier.py
124
- > # /mnt/skills/user/goap-research-ed25519/scripts/goap_planner.py
123
+ > # .claude/skills/goap-research-ed25519/scripts/ed25519_verifier.py
124
+ > # .claude/skills/goap-research-ed25519/scripts/goap_planner.py
125
125
  > ```
126
126
 
127
127
  **Всё из режима DEEP, плюс:**
@@ -37,7 +37,7 @@
37
37
  ### 🔵 Режим DEEP
38
38
 
39
39
  > ⚙️ **Загрузи:**
40
- > 1. `view(/mnt/skills/user/goap-research-ed25519/SKILL.md)` — адаптивный поиск отзывов
40
+ > 1. `view(.claude/skills/goap-research-ed25519/SKILL.md)` — адаптивный поиск отзывов
41
41
  > 2. `view(references/jtbd-canvas.md)` — JTBD framework + примеры
42
42
 
43
43
  **GOAP State Assessment:**
@@ -73,7 +73,7 @@ sample_size_factor: 1.0 (≥20 reviews), 0.8 (10-19), 0.5 (<10)
73
73
 
74
74
  ### 🟣 Режим VERIFIED
75
75
 
76
- > ⚙️ **Дополнительно:** `view(/mnt/skills/user/goap-research-ed25519/SKILL.md)`
76
+ > ⚙️ **Дополнительно:** `view(.claude/skills/goap-research-ed25519/SKILL.md)`
77
77
 
78
78
  Всё из DEEP, плюс:
79
79
  - Каждая цитата клиента получает `source_hash` и, где доступно, provenance signature
@@ -99,7 +99,15 @@ why_now: [из M2 Section E — 4 фактора]
99
99
 
100
100
  ### Step 3: Generate React Prototype
101
101
 
102
- > ⚙️ `view(/mnt/skills/public/frontend-design/SKILL.md)` — для design quality
102
+ > ⚙️ **`frontend-design` — OPTIONAL, ВНЕШНИЙ.** Этот пакет его не отгружает. Пути вида
103
+ > `.claude/skills/frontend-design/` здесь намеренно НЕТ: он выглядел бы рабочим и не резолвился бы,
104
+ > а путь, который врёт, хуже честно чужого.
105
+ >
106
+ > Если навык установлен — читайте его для design quality. **Fallback, если его нет:** стройте
107
+ > прототип по секции ниже, она самодостаточна; в отчёте пометьте, что оценка design quality не
108
+ > проводилась. Молча пропускать нельзя (см. `.claude/rules/skill-interface-protocol.md` §6).
109
+ >
110
+ > Установить: `dz init --select frontend-design`.
103
111
  > ⚙️ `view(examples/noom-cjm-example.md)` — few-shot: структура .jsx
104
112
 
105
113
  **Создай один .jsx файл** со следующей архитектурой:
@@ -40,8 +40,8 @@
40
40
  ### 🔵 Режим DEEP
41
41
 
42
42
  > ⚙️ **Загрузи перед началом:**
43
- > 1. `view(/mnt/skills/user/goap-research-ed25519/SKILL.md)` — для рыночного research
44
- > 2. `view(/mnt/skills/user/problem-solver-enhanced/SKILL.md)` — Modules 4, 5, 6 — для конкурентного анализа
43
+ > 1. `view(.claude/skills/goap-research-ed25519/SKILL.md)` — для рыночного research
44
+ > 2. `view(.claude/skills/problem-solver-enhanced/SKILL.md)` — Modules 4, 5, 6 — для конкурентного анализа
45
45
 
46
46
  #### PHASE A: GOAP Market Research
47
47
 
@@ -159,7 +159,7 @@ Incumbent │ (-2, +1) | (0, +2) │ Ценовая война
159
159
 
160
160
  ### 🟣 Режим VERIFIED (Ed25519)
161
161
 
162
- > ⚙️ **Дополнительно:** `view(/mnt/skills/user/goap-research-ed25519/SKILL.md)`
162
+ > ⚙️ **Дополнительно:** `view(.claude/skills/goap-research-ed25519/SKILL.md)`
163
163
 
164
164
  Всё из режима DEEP, плюс:
165
165
 
@@ -41,8 +41,8 @@
41
41
  ### 🔵 Режим DEEP
42
42
 
43
43
  > ⚙️ **Загрузи:**
44
- > 1. `view(/mnt/skills/user/goap-research-ed25519/SKILL.md)` — адаптивный research
45
- > 2. `view(/mnt/skills/user/problem-solver-enhanced/SKILL.md)` — Modules 1, 6
44
+ > 1. `view(.claude/skills/goap-research-ed25519/SKILL.md)` — адаптивный research
45
+ > 2. `view(.claude/skills/problem-solver-enhanced/SKILL.md)` — Modules 1, 6
46
46
 
47
47
  #### PHASE A: GOAP Financial Research
48
48
 
@@ -111,7 +111,7 @@ Physical: "Команда должна быть БОЛЬШОЙ (для скор
111
111
 
112
112
  ### 🟣 Режим VERIFIED
113
113
 
114
- > ⚙️ **Дополнительно:** `view(/mnt/skills/user/goap-research-ed25519/SKILL.md)`
114
+ > ⚙️ **Дополнительно:** `view(.claude/skills/goap-research-ed25519/SKILL.md)`
115
115
 
116
116
  Всё из DEEP, плюс:
117
117
  - Все benchmark числа получают source_hash; issuer-grade crypto используется только при valid signature under pinned active key