@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
@@ -281,16 +281,24 @@ describe('e2e: unknown command', () => {
281
281
  // ---------------------------------------------------------------------------
282
282
 
283
283
  describe('e2e: --help shows correct component counts', () => {
284
- test('mentions 10 skills, 11 commands, 4 agents, 5 rules', () => {
284
+ test('--help agrees with COMPONENTS for every group', () => {
285
+ // DERIVED, not hardcoded. This assertion has now gone stale twice for the same reason — the
286
+ // comment above records "cli.js used to say 1 rule while EXPECTED_RULES had 2 entries", and
287
+ // adding the docker-ports rule broke it again with a literal 5. cli.js:57 already computes the
288
+ // numbers from COMPONENTS; a test that re-types them is a second source of truth, which is the
289
+ // very defect it was written to catch.
290
+ const { COMPONENTS } = require(path.join(PKG_DIR, 'src', 'utils.js'));
285
291
  const dir = tmpDir();
286
292
  try {
287
293
  const r = runCli(['--help'], dir);
288
294
  assert.equal(r.exitCode, 0);
289
- assert.match(r.stdout, /10\s+skills/i, 'should mention 10 skills');
290
- assert.match(r.stdout, /11\s+commands/i, 'should mention 11 commands (post v1.4)');
291
- assert.match(r.stdout, /4\s+agents/i, 'should mention 4 agents');
292
- assert.match(r.stdout, /5\s+rules/i,
293
- 'should say "5 rules" (post v1.4: + 3 generic rules)');
295
+ for (const group of ['skills', 'commands', 'agents', 'rules']) {
296
+ const n = Object.keys(COMPONENTS[group].items).length;
297
+ // singular/plural: cli.js prints "1 rule" but "6 rules"
298
+ const word = n === 1 ? group.replace(/s$/, '') : group;
299
+ assert.match(r.stdout, new RegExp(n + '\\s+' + word, 'i'),
300
+ '--help must say "' + n + ' ' + word + '" to match COMPONENTS.' + group);
301
+ }
294
302
  } finally { rmRf(dir); }
295
303
  });
296
304
  });
@@ -440,8 +448,10 @@ describe('e2e: v1.4.1 cross-platform hooks', () => {
440
448
  assert.doesNotMatch(cmd, /\|\|\s*true/,
441
449
  `bash-specific || true in: ${cmd}`);
442
450
  // Each command should invoke node + a script
443
- assert.match(cmd, /node\s+\.claude[/\\]hooks/,
444
- `expected 'node .claude/hooks/<script>.cjs', got: ${cmd}`);
451
+ // Anchored at the project root, not the drifting cwd (PR-013): still node + the exact
452
+ // script, and now additionally the anchor — strictly stronger than the old assertion.
453
+ assert.match(cmd, /node\s+"\$\{CLAUDE_PROJECT_DIR\}\/\.claude\/hooks\/[A-Za-z0-9._-]+\.cjs"/,
454
+ `expected 'node "\${CLAUDE_PROJECT_DIR}/.claude/hooks/<script>.cjs"', got: ${cmd}`);
445
455
  }
446
456
  } finally { rmRf(dir); }
447
457
  });
@@ -599,8 +609,9 @@ describe('e2e: v1.5.0 statusline dashboard', () => {
599
609
  );
600
610
  assert.ok(settings.statusLine, 'statusLine field should be present in settings.json');
601
611
  assert.equal(settings.statusLine.type, 'command');
602
- assert.match(settings.statusLine.command, /node\s+\.claude[/\\]hooks[/\\]statusline\.cjs/,
603
- 'should invoke statusline.cjs via node');
612
+ assert.match(settings.statusLine.command,
613
+ /node\s+"\$\{CLAUDE_PROJECT_DIR\}\/\.claude\/hooks\/statusline\.cjs"/,
614
+ 'should invoke statusline.cjs via node, anchored at the project root (PR-013)');
604
615
  } finally { rmRf(dir); }
605
616
  });
606
617
 
@@ -1,10 +1,10 @@
1
1
  {
2
- "generatedAt": "2026-08-24T19:04:59.064Z",
3
- "totalFiles": 134,
2
+ "generatedAt": "2026-08-27T09:01:34.335Z",
3
+ "totalFiles": 136,
4
4
  "files": {
5
- ".claude/agents/doc-validator.md": "a46651637239b4f078b5c3022ea2bf9ef4fb84ab2eb529cdc41273c42deaddf5",
5
+ ".claude/agents/doc-validator.md": "e57950758cd4764b2cb54b3f1905567e9dbc1ed20ac6ecd30998614894689f4f",
6
6
  ".claude/agents/harvest-coordinator.md": "70fa884057669902e18744915305705cf9e45e738a83d7bb5ab011a3b72d683c",
7
- ".claude/agents/product-discoverer.md": "ea341407faa6f31a152508945b24e650c5a71459bbc9e1f91cdab7621660a54d",
7
+ ".claude/agents/product-discoverer.md": "f74683fe91b4357d6fb4024ed47b4d5a129ea6375ca6b223dee97176d74acf1c",
8
8
  ".claude/agents/replicate-coordinator.md": "e86060110ab188109cf908878206543f414993e14dad01f8dc19f041ee6497a7",
9
9
  ".claude/commands/deploy.md": "27f5424ee217163c55e5e6b9322a1f0858159407bd24ebd6d6588d6c22a5f890",
10
10
  ".claude/commands/docs.md": "be03592cb3f1d61b60dc96d3e26d71281ad4eb2d4626012c080ce609a32c96e4",
@@ -12,23 +12,25 @@
12
12
  ".claude/commands/go.md": "655dc9cad37b61a9f283cef15e435c492ee7cbc88a6ce59295ac32c975dee858",
13
13
  ".claude/commands/harvest.md": "c074fe7799f594dda44c5f8166da707f25cee51b12f758dea562a8c1e6ac5dcd",
14
14
  ".claude/commands/myinsights.md": "2e73e822384f976383d9c3313940888b9853f1781d2c39a2fb89825f221149dc",
15
- ".claude/commands/next.md": "c417f86626a91d716cb9b5ed98e1445a0820055b22947da33fc90fc65fe974b2",
15
+ ".claude/commands/next.md": "1e4984306c3aa80dfc8734dc1c975dfe5fdf2cf6396ddd759e4c7635646107b8",
16
16
  ".claude/commands/plan.md": "6fef889f51168981bf4a98b8392a0d595e3d199e1cfaa8e08c0a181ec6c440e4",
17
- ".claude/commands/replicate.md": "27fb978d1b4f19169d48795c5ce1a8ad4e5b6a03e9ae478b64857cdeb9d8b4bb",
17
+ ".claude/commands/replicate.md": "13f5e2ebe109f33b63cbcd75e3cf1b94224ab066aba1b0cb10cf34f668144a8a",
18
18
  ".claude/commands/run.md": "87144dfe8c56c15ba7c117daa544d8a08b84d7205f21dafcc48d27c6c3995059",
19
- ".claude/commands/start.md": "8ebd4393caf6fb453bf607022e6209b963847dd61cb95a78e7bb5f98e039c774",
20
- ".claude/hooks/autocommit-insights.cjs": "36fa95a1cfa74098f2f222ee7038d1bd22478a42070746614eb9e2d8514cad61",
21
- ".claude/hooks/autocommit-plans.cjs": "ac831fa835557e268a73e7a039977194c638689c3c65ce8cba427535d88ec3b2",
22
- ".claude/hooks/autocommit-roadmap.cjs": "ab78ae2f163400865bee520b73b7624094bf6c32c63478dfd5d6a1715ca7f135",
23
- ".claude/hooks/session-insights.cjs": "cbb60842149fb90aaa968576f99d0695c92628377c3ca3120d28c5ff0bc692f2",
24
- ".claude/hooks/state-update.cjs": "39a20e59278e457f4ec4eadd66f3699fee43c79695934a44a4cdca0c18f6cec2",
25
- ".claude/hooks/statusline.cjs": "7e3e230f68b2ba76908127887e7f75428599dbd62826be62ed626fc054ca1e65",
19
+ ".claude/commands/start.md": "4aaebc10ca8d607d4b2b43883d82392e9788c2f183878a49fc0d1ed54d07ee95",
20
+ ".claude/hooks/autocommit-insights.cjs": "5e7cef87b24d457f25315d79fe15f0dfaf94d9269495d638817f0faee2593c6f",
21
+ ".claude/hooks/autocommit-plans.cjs": "9bb404b7f4fda5de03e6c2f16bf8939a79ec1944538767b04bcccc6cf50cc03d",
22
+ ".claude/hooks/autocommit-roadmap.cjs": "f4dcc9ffd9a3aed91dd56e011e699fcf8af903cc81594e4ab1ef61e5432a36ad",
23
+ ".claude/hooks/check-ports.cjs": "e5f2d2bd8899df27c2dfedf76128467becae1868210eb8525a1bad2869a8c78f",
24
+ ".claude/hooks/session-insights.cjs": "46f8357e3886a6d54db1ab4af969b9b8643ae5937ba59038a5651ffbcfd0d30e",
25
+ ".claude/hooks/state-update.cjs": "47146a76b768ac2273abe8f2f017856a0e28b45afaf90048148da417dd19604e",
26
+ ".claude/hooks/statusline.cjs": "1eca683baf67a17874dc5db6dfe38eb11944f550c66f46330bce0f794732245f",
27
+ ".claude/rules/docker-ports.md": "bef41c25c875479341f68783c2d64572800d4710f55401e55519245f03d22efb",
26
28
  ".claude/rules/feature-lifecycle.md": "f9ddc8c889ab7938a7207b36ae46c36be05e9b870956ef16617bf31b2f78c758",
27
29
  ".claude/rules/git-workflow.md": "7d8d2ded47fcc0fb95da3dc226efa6cb3c80d6adf7296977588c181dfce20192",
28
30
  ".claude/rules/insights-capture.md": "488d694caffe7a32915b0ec1beb7a84b223fe602722c5bb91ab8bf2785626387",
29
- ".claude/rules/replicate-pipeline.md": "2eaa40ece405b506f0b94818013e2e235be6c9fb3bd73e964dd675afc34b6535",
31
+ ".claude/rules/replicate-pipeline.md": "b65e802d32a1e50bdfcd0cfc79c78e9234eac48a94f0cd1f5647cf4fea6c0162",
30
32
  ".claude/rules/skill-interface-protocol.md": "fe18fcb9217002572f462f3a7f38de22cdd7ba0dd4e906fdcd0c0e8cfed9f2aa",
31
- ".claude/settings.json": "1afea754d42e3b2aa7cee0a17a8347ba594427b40daa293ce7b21f3dfd0607c3",
33
+ ".claude/settings.json": "b1df6cf834ccfa227f3318a70862115c17838ab31253e9d8d89381b7763e61fd",
32
34
  ".claude/skills/brutal-honesty-review/evals/brutal-honesty-review.yaml": "855c155d55575e12aab23fb3a5fb2fada74d93974ff755dfedac6f2d5bd41f13",
33
35
  ".claude/skills/brutal-honesty-review/README.md": "2a026b4af50b21456e50424fbfa089cb983fbbaa0c68024c8c17c695bea25a57",
34
36
  ".claude/skills/brutal-honesty-review/resources/assessment-rubrics.md": "dac09c91c765e2211799cc230738fb72a966f47511edb872306a906e49640cbe",
@@ -38,8 +40,8 @@
38
40
  ".claude/skills/brutal-honesty-review/scripts/assess-tests.sh": "8c6444bdb5ce97e49223cdaa6b1c58ac8fcec710cd6ee8d56de9bc677379bd88",
39
41
  ".claude/skills/brutal-honesty-review/scripts/validate-config.json": "7418284839f89735d01f50eefdb488b013e0076f6003a67683062429c14984c6",
40
42
  ".claude/skills/brutal-honesty-review/SKILL.md": "21e41129dac8e99485b501dcb3767d5f4461ce527782fa7bc71f89dabe5bd2bd",
41
- ".claude/skills/cc-toolkit-generator-enhanced/modules/01-detect-parse.md": "45441d525ff5092766b9ab743e783dbf3b25e9f9515bdda888c8ca1cc3e0a6f0",
42
- ".claude/skills/cc-toolkit-generator-enhanced/modules/02-analyze-map.md": "231ac3c42b800e26d035562633da79abd77ea90540e8d9245cc9360867dc91ab",
43
+ ".claude/skills/cc-toolkit-generator-enhanced/modules/01-detect-parse.md": "85c72f6daa5af88b4a7ff520a387f47a7c92b81cb1f0757550c8b58100a97be6",
44
+ ".claude/skills/cc-toolkit-generator-enhanced/modules/02-analyze-map.md": "5939f3fe6ae542fabb8e665dc373a20410d8de917362fcb3ecac504286f41836",
43
45
  ".claude/skills/cc-toolkit-generator-enhanced/modules/03-generate-p0.md": "b093a5d66663e2dfd6415afef7c7444409f03379f857001b811685ded410038b",
44
46
  ".claude/skills/cc-toolkit-generator-enhanced/modules/04-generate-p1.md": "081284d3858c5dd89b45fc8a2b83bbc0477843f864cbceb6d9f8c3629b198a0a",
45
47
  ".claude/skills/cc-toolkit-generator-enhanced/modules/05-generate-p2p3.md": "9535eaa5fa1be0804369aa4e8ee6f6f580839ea2c3c7b7aa51584e298a928093",
@@ -49,21 +51,21 @@
49
51
  ".claude/skills/cc-toolkit-generator-enhanced/modules/09-cross-project-learning.md": "c395bc42a40ed14c0b8fe274df516082db0490228c82aba6a56f23b8f41d3d43",
50
52
  ".claude/skills/cc-toolkit-generator-enhanced/modules/README.md": "9dbcc9c0978d06cbe31caffa909741fdb3a38d3cd1e6e98addf95628ec8394a0",
51
53
  ".claude/skills/cc-toolkit-generator-enhanced/references/claude-md-strategy.md": "88cdab1687096d58dc4450cdf81d24d00fdb493b2cc4960030b30fd57d1ca2da",
52
- ".claude/skills/cc-toolkit-generator-enhanced/references/enhanced-recommendations.md": "0a828ed5302a3436ab9c21cdeeb2ebf47329031106dfb242517ca04f226033dd",
53
- ".claude/skills/cc-toolkit-generator-enhanced/references/extended-mapping.md": "0c906fe8622ffa147a09af592ab75a8b1034b7634501e576da58e858477c3141",
54
+ ".claude/skills/cc-toolkit-generator-enhanced/references/enhanced-recommendations.md": "657e35db0cb77d3a9d1a4d01215a9b8d50441dd863593bee3a84f62aad8b00bc",
55
+ ".claude/skills/cc-toolkit-generator-enhanced/references/extended-mapping.md": "5e87c91956eabc70b4a1084b47b2c9a20ede18bb245735812ead956eafabf3af",
54
56
  ".claude/skills/cc-toolkit-generator-enhanced/references/maturity-integration.md": "aa4e743bcd0281525336a298d0489c31481f69ff1a534e1ed1d262d888937deb",
55
57
  ".claude/skills/cc-toolkit-generator-enhanced/references/security-patterns-library.md": "5f1d650c426cbd2c9536b385306605ea62a5a520ee4566d23ca7254409c672a5",
56
58
  ".claude/skills/cc-toolkit-generator-enhanced/references/templates/automation-commands.md": "23b8204fdc70b5afc5430cbe503af709a04b26716d2d2e07889283a755858fee",
57
- ".claude/skills/cc-toolkit-generator-enhanced/references/templates/ddd-agents.md": "208619880fe6fea47b25f5f31a7fc5ef27f2a80b2235c2f8c1aa2b4140f48363",
59
+ ".claude/skills/cc-toolkit-generator-enhanced/references/templates/ddd-agents.md": "c91f81da197a072b57378fd0018eea26c35d909a558bdca6f070eb60153c97b3",
58
60
  ".claude/skills/cc-toolkit-generator-enhanced/references/templates/ddd-hooks-commands.md": "d70a0c4977d322602c35c53bf04880177b5dd8f0de2b339d68a0f55993903745",
59
- ".claude/skills/cc-toolkit-generator-enhanced/references/templates/ddd-skills.md": "9cbdbc251026297ba30dca8b4311906a85ba7a8bf85fd35c41aa1dc358ec7574",
60
- ".claude/skills/cc-toolkit-generator-enhanced/references/templates/enhanced-claude-md.md": "6121b4bec44fa726a8b70d6680c17ecf428fdf937bb3e13d9ee7e3b56ad33913",
61
+ ".claude/skills/cc-toolkit-generator-enhanced/references/templates/ddd-skills.md": "2f1019d5da7d695983aee79186a47fcefccbfa24db48219c04f9bb9feb757d3a",
62
+ ".claude/skills/cc-toolkit-generator-enhanced/references/templates/enhanced-claude-md.md": "db6898b7202b84965cbff9195a6ce780cadd6aeb64adae3fbfd951443a6bc43d",
61
63
  ".claude/skills/cc-toolkit-generator-enhanced/references/templates/feature-lifecycle-ent.md": "eb9025b687f0408b33280bf5fcc6c8be9b1670a544dd90b510557b89ae6cc123",
62
64
  ".claude/skills/cc-toolkit-generator-enhanced/references/templates/feature-lifecycle.md": "c537b47e7a4ff2d692767f4aac130e2ae9b1923782106750218696e87f515feb",
63
- ".claude/skills/cc-toolkit-generator-enhanced/references/templates/feature-suggestions.md": "455fd4e4147f15175e1bb2dbfeda53d6ae0a4ac3f8d985544d0db5d24eba935d",
65
+ ".claude/skills/cc-toolkit-generator-enhanced/references/templates/feature-suggestions.md": "3b1c20d2394e180720447416767fbfae99168cf9ca846bf64907f6fe5cd906da",
64
66
  ".claude/skills/cc-toolkit-generator-enhanced/references/templates/insights-system.md": "244c2ef5f417c5d0097accf5ccfffd9de93d167efbfc6818cb9a8196139e096e",
65
67
  ".claude/skills/cc-toolkit-generator-enhanced/references/templates/mcp.md": "63d1e6dabf5bb29534e6fa11d9dd531ec55a48cda661f0faf7a29dad6f49296f",
66
- ".claude/skills/cc-toolkit-generator-enhanced/references/templates/start-command.md": "67cf259f8d22c1b94c5242c6fd48d557e97581beb162cf176eb2e6851bab1962",
68
+ ".claude/skills/cc-toolkit-generator-enhanced/references/templates/start-command.md": "e037a7e73e65e5216f95f27a255009c4c2b95f0f2d6b7c575d70813efac34438",
67
69
  ".claude/skills/cc-toolkit-generator-enhanced/SKILL.md": "26fb1a3c0961fbb1f5f7e81ec5134bbe49ba0f99175c394cdf0c6b8a35568be2",
68
70
  ".claude/skills/explore/references/questioning-techniques.md": "ec0dd00871d3abe950f9cf375216a43043827897166c4165c3fa581839851195",
69
71
  ".claude/skills/explore/references/task-brief-templates.md": "4e9292479e120afce944b747bb7b4a7226c41e9880708d620de3ea4fa4ef0249",
@@ -116,9 +118,9 @@
116
118
  ".claude/skills/requirements-validator/assets/templates/bdd-feature-template.feature": "5b1e47f10447ce574d0eb3917b69e2613b6e3f9c48b30ff5f4cfb57758c99a1d",
117
119
  ".claude/skills/requirements-validator/references/bdd-patterns.md": "04611a8c52c81f3a6d4fc2f9661ebc56159a125b6fb44a4d18b432e2b82de6e3",
118
120
  ".claude/skills/requirements-validator/references/invest-criteria.md": "ac6976adf776b9b60c784e1a0f6a0d96dd3332b16f4c12b72d3e59f9325bb626",
119
- ".claude/skills/requirements-validator/references/scoring-system.md": "79d421c79ab2cc594e685c27178368495774a1154e2b65cdbcd2a94cc28e9dbb",
121
+ ".claude/skills/requirements-validator/references/scoring-system.md": "49afe072f31f5b4f9a33f6b59487244f494b0f5587b6cc10c797c4de59510114",
120
122
  ".claude/skills/requirements-validator/references/smart-criteria.md": "619a33fdb6bc336872fdf9f1fc91924f8ccd3c2ebef023912124756692b45aef",
121
- ".claude/skills/requirements-validator/SKILL.md": "baa5cf7257de711ad64d4233ff5a26eb4573fb75a3392fda9a75c2c2e5a73ef9",
123
+ ".claude/skills/requirements-validator/SKILL.md": "e50c5269ad181966a81c14ef596946cea67cbf4b8f8f648c90fd66ecad948933",
122
124
  ".claude/skills/reverse-engineering-unicorn/examples/noom-cjm-example.md": "01d33586e26bf093a57a7a1082bf3bdba3af801bb8348a685c0418f76987a0b0",
123
125
  ".claude/skills/reverse-engineering-unicorn/examples/noom-module1-example.md": "7009cf2e2624a1254913ad2e1ff554e7ff675358b0b99bc0632f2e12132663a3",
124
126
  ".claude/skills/reverse-engineering-unicorn/modules/01-intelligence.md": "4af16873637c26baba0d30728748108c79f5b96765de08d54fc65fe1008f4280",
@@ -133,7 +135,7 @@
133
135
  ".claude/skills/reverse-engineering-unicorn/references/jtbd-canvas.md": "7938e657af631109ca7b4752002c21ac84a7dad3a35cc522be088f814d102773",
134
136
  ".claude/skills/reverse-engineering-unicorn/SKILL.md": "20d6b667971f9749d091c1d47e2f75df957f259647cbb861ac54174280096c71",
135
137
  ".claude/skills/sparc-prd-mini/references/sparc-methodology.md": "9c93dafe8149590809ff6b27508d7df52c8efa7f31f8e8f8052b7124a6561fee",
136
- ".claude/skills/sparc-prd-mini/SKILL.md": "2555c4de0edce73f6c2f637db1d839900c7fa27a7d25ca88ec0cf5a70a47325d",
138
+ ".claude/skills/sparc-prd-mini/SKILL.md": "50e829635b828b444487e4b04219518a595dbd4b94f91b66fb03ddf4a7b21712",
137
139
  ".claude/skills/sparc-prd-mini/templates/CLAUDE.md": "9eeb66752cc4dec5c3c3fa5a8f7cfc3dd282f75d245bd36835f690a945b534ed",
138
140
  ".claude/skills/sparc-prd-mini/templates/prd.md": "7b4497bc76bb421fae7a07ecb46af01e021412180a394a5487a3c3c42dc004b6"
139
141
  }
@@ -44,7 +44,8 @@ function sha256(filePath) {
44
44
  function main() {
45
45
  if (!fs.existsSync(TEMPLATES)) {
46
46
  console.error(`[snapshot] templates/ not found at ${TEMPLATES}`);
47
- console.error('[snapshot] Run prepublishOnly first: node scripts/sync-templates.js');
47
+ console.error('[snapshot] templates/ ships with this package — a missing one means a broken');
48
+ console.error('[snapshot] checkout, not a missing build step. Restore it from git.');
48
49
  process.exit(1);
49
50
  }
50
51
 
@@ -0,0 +1,137 @@
1
+ 'use strict';
2
+
3
+ // /replicate writes docs/ADR.md in Phase 1 — commands/replicate.md:243, "(if applicable)" — and
4
+ // nothing ever reads it again. A decision could be written down, agreed, and quietly not built.
5
+ //
6
+ // This cannot be a script: MEASURED, `find templates -name '*.mjs' -o -name 'check*.js'` returns
7
+ // nothing — the templates carry no executable gate at all. It is a mandatory prose step held by this
8
+ // test, the shape that shipped for scenario coverage the day before.
9
+ //
10
+ // TWO TRAPS INHERITED VERBATIM from that sibling, both found the expensive way:
11
+ // - PLACEMENT. A step inside the fenced [MANUAL] checkpoint block is skipped by an AUTO run, so a
12
+ // "mandatory" step would run in half of runs. "Precedes the checkpoint text" and "outside the
13
+ // MANUAL-only block" are two different facts and only the second is the property.
14
+ // - BOTH DIRECTIONS. A one-way check lets a reference to a decision nobody wrote read exactly like
15
+ // coverage.
16
+ //
17
+ // WHAT THIS CLAIMS, and the artifact says so itself: a decision is NAMED downstream. NOT that it was
18
+ // implemented — no comparison of identifiers can establish that. The nearest precedent, the K2 gate,
19
+ // records the same limit about its own coverage check in its own source.
20
+
21
+ const { test, describe } = require('node:test');
22
+ const assert = require('node:assert/strict');
23
+ const fs = require('node:fs');
24
+ const path = require('node:path');
25
+
26
+ const REPLICATE = path.join(__dirname, '..', '..', 'templates', '.claude', 'commands',
27
+ 'replicate.md');
28
+ const read = () => fs.readFileSync(REPLICATE, 'utf-8');
29
+
30
+ /** Phase 2, from its heading to Phase 3's. */
31
+ function phase2(src) {
32
+ const start = src.indexOf('### Phase 2: VALIDATION');
33
+ const end = src.indexOf('### Phase 3', start + 1);
34
+ assert.ok(start > 0 && end > start, 'replicate.md must have a Phase 2 before Phase 3');
35
+ return { start, end, text: src.slice(start, end) };
36
+ }
37
+
38
+ describe('a recorded decision cannot be quietly left unbuilt (PR-007 G5)', () => {
39
+ test('P1 — the step is INSIDE Phase 2 and OUTSIDE the [MANUAL] checkpoint block', () => {
40
+ const src = read();
41
+ const { text } = phase2(src);
42
+ const step = text.indexOf('**Шаг 2.9 — ПОКРЫТИЕ РЕШЕНИЙ');
43
+ assert.ok(step > 0, 'the coverage step must live inside Phase 2');
44
+
45
+ const checkpoint = text.indexOf('**Checkpoint:**');
46
+ assert.ok(checkpoint > step,
47
+ 'and BEFORE the checkpoint, not after it: ' + step + ' vs ' + checkpoint);
48
+
49
+ // The checkpoint's body is a fenced block an AUTO run may skip. The step must not be in it —
50
+ // and this is asserted from the FENCE, not inferred from the ordering above, because those are
51
+ // two different facts and the sibling feature shipped the weaker one first.
52
+ const fenceStart = text.indexOf('```', checkpoint);
53
+ const fenceEnd = text.indexOf('```', fenceStart + 3);
54
+ assert.ok(fenceStart > 0 && fenceEnd > fenceStart, 'the checkpoint must have a fenced body');
55
+ assert.ok(step < fenceStart || step > fenceEnd,
56
+ 'the step sits inside the MANUAL-only fenced block, where an AUTO run skips it');
57
+ });
58
+
59
+ test('P2 — the reconciliation runs BOTH ways, and both may say none', () => {
60
+ const { text } = phase2(read());
61
+ assert.match(text, /Recorded but named nowhere:/,
62
+ 'decisions nobody mentions must be listed');
63
+ assert.match(text, /Named downstream but absent from docs\/ADR\.md:/,
64
+ 'and references matching no decision — without this, a dangling ADR-009 reads as coverage');
65
+ assert.match(text, /Both tables are required, and both may be the single word `none`/,
66
+ 'an empty table and a forgotten table look identical');
67
+ });
68
+
69
+ test('P3 — the block is required in EVERY case, including all-covered', () => {
70
+ const { text } = phase2(read());
71
+ assert.match(text, /## Decision Coverage/,
72
+ 'the required output block must be named, or "reconcile" is advice');
73
+ assert.match(text, /in every case\*\*, including\s+the one where everything is covered/,
74
+ 'an absent block and a block saying "all covered" are indistinguishable to the next reader');
75
+ });
76
+
77
+ test('P4 — three states of the ADR file, each with its own line', () => {
78
+ // Cross-family QE found two defects here. The first version had ONE empty case, so an ADR file
79
+ // that exists but records no ids was unclassified. And its sentence claimed the project "recorded
80
+ // no architectural decisions" — which absence of one FILE cannot establish; decisions may live
81
+ // where this step does not look, and asserting otherwise is a false claim in a generated report.
82
+ const { text } = phase2(read());
83
+ assert.match(text, /Three states of the ADR file/, 'all three must be enumerated');
84
+ assert.match(text, /docs\/ADR\.md is absent, so no decision ids were collected from it/,
85
+ 'the absent case must say what is absent — the FILE — and nothing more');
86
+ assert.ok(!/recorded no architectural decisions/.test(text),
87
+ 'the step must not claim what it cannot see: decisions may live elsewhere');
88
+ assert.match(text, /exists but records no decision ids/,
89
+ 'a present-but-empty ADR is a third state, not the same as an absent file');
90
+ assert.match(text, /In all three states the second table\s*\n?still runs/,
91
+ 'a dangling reference is a defect whether or not an ADR file was ever written');
92
+ });
93
+
94
+ test('P7 — the searched files are ENUMERATED, and the two self-defeating ones excluded', () => {
95
+ // The finding that mattered most: "across the SPARC documents" did not exclude docs/ADR.md, whose
96
+ // own headings contain every id — so every decision appeared named and the check passed BY
97
+ // CONSTRUCTION, on any project. And docs/validation-report.md is where this step WRITES, so
98
+ // counting it would let the previous run's output satisfy the next one.
99
+ const { text } = phase2(read());
100
+ assert.match(text, /Where to look, named file by file/,
101
+ 'the search set must be enumerated, not described');
102
+ for (const f of ['docs/PRD.md', 'docs/Specification.md', 'docs/Architecture.md']) {
103
+ assert.ok(text.includes(f), 'the search set must name: ' + f);
104
+ }
105
+ assert.match(text, /Two files are EXCLUDED/, 'and the exclusions must be explicit');
106
+ assert.match(text, /the check would pass by construction/,
107
+ 'with the reason for excluding the ADR file itself');
108
+ assert.match(text, /the check would start proving itself/,
109
+ 'and for excluding the report this step writes');
110
+ });
111
+
112
+ test('P8 — a mention is DEFINED, and a superseded decision has a policy', () => {
113
+ const { text } = phase2(read());
114
+ assert.match(text, /The exact token `ADR-<nnn>`, case-sensitive/,
115
+ '"mention" must be defined, or every reader draws a different line');
116
+ assert.match(text, /Not a title, not a paraphrase/,
117
+ 'and what does NOT count must be named too');
118
+ assert.match(text, /A superseded decision needs no current mention/,
119
+ 'an id that outlives its decision needs a stated policy, or it reads as a permanent gap');
120
+ });
121
+
122
+ test('P5 — the artifact states its own limit', () => {
123
+ // A coverage block LOOKS like proof of implementation. The backlog item demanded this restraint
124
+ // explicitly, and the limit belongs in the template a reader sees, not only in a feature report.
125
+ const { text } = phase2(read());
126
+ assert.match(text, /It does NOT establish that the decision was implemented/,
127
+ 'the limit must be written into the artifact');
128
+ assert.match(text, /the decision was written down and then forgotten/,
129
+ 'and the class it DOES catch must be named, so the claim is bounded on both sides');
130
+ });
131
+
132
+ test('P6 — the decision id has a defined form, or there is nothing to trace on', () => {
133
+ const { text } = phase2(read());
134
+ assert.match(text, /`ADR-<nnn>` — three digits, assigned in order, never\s+reused/,
135
+ 'an id without a form and a uniqueness rule is an intention, not an identifier');
136
+ });
137
+ });
@@ -0,0 +1,108 @@
1
+ 'use strict';
2
+
3
+ // adr-scanner-both-shapes (ADR-001) — the content contract of the toolkit generator's ADR handling.
4
+ //
5
+ // Why content assertions: these are PROMPT modules executed by a model, so the strongest
6
+ // deterministic layer available is the text itself. Before this feature, `has_adr` required a
7
+ // docs/adr/ DIRECTORY with >5 files while /replicate writes ONE docs/ADR.md — the flag was
8
+ // unreachable and every `IF has_adr:` consumer was dead code. Worse, a naive fix (relaxing the
9
+ // threshold) would have rerouted SPARC projects, because the flag sat inside pipeline routing.
10
+ //
11
+ // P1..P5 below mirror the ADR's Confirmation section. Each is DISCRIMINATING: restoring the old
12
+ // text at any site turns the matching assertion red (verified by mutation at Step 8).
13
+
14
+ const { test, describe } = require('node:test');
15
+ const assert = require('node:assert/strict');
16
+ const fs = require('node:fs');
17
+ const path = require('node:path');
18
+
19
+ const GEN = path.join(__dirname, '..', '..', 'templates', '.claude', 'skills', 'cc-toolkit-generator-enhanced');
20
+
21
+ function read(rel) {
22
+ return fs.readFileSync(path.join(GEN, rel), 'utf-8');
23
+ }
24
+
25
+ /** Every .md under the generator skill, relative paths. */
26
+ function allModules(dir = GEN, acc = []) {
27
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
28
+ const p = path.join(dir, e.name);
29
+ if (e.isDirectory()) allModules(p, acc);
30
+ else if (e.name.endsWith('.md')) acc.push(path.relative(GEN, p));
31
+ }
32
+ return acc;
33
+ }
34
+
35
+ describe('ADR scanner contract (adr-scanner-both-shapes ADR-001)', () => {
36
+ test('P1 — exactly ONE has_adr definition across the WHOLE generator, and it counts decisions', () => {
37
+ // AM-1: a second `>5` copy hid in references/enhanced-recommendations.md — hence the tree-wide
38
+ // sweep, not a single-file check.
39
+ const defs = [];
40
+ for (const rel of allModules()) {
41
+ const src = read(rel);
42
+ for (const line of src.split('\n')) {
43
+ if (/^\s*has_adr\s*=/.test(line)) defs.push({ rel, line: line.trim() });
44
+ }
45
+ }
46
+ assert.equal(defs.length, 1,
47
+ 'exactly one has_adr definition may exist; found: ' + JSON.stringify(defs));
48
+ assert.match(defs[0].line, /collect_adrs/,
49
+ 'the one definition must count collector output (decisions), not raw files');
50
+ assert.ok(!/>\s*5/.test(defs[0].line), 'the unreachable >5 file threshold must not survive');
51
+ });
52
+
53
+ test('P2 — the scan catalog names BOTH storage shapes', () => {
54
+ const src = read(path.join('modules', '01-detect-parse.md'));
55
+ assert.ok(src.includes('docs/adr/*.md'), 'directory shape must stay catalogued');
56
+ assert.ok(src.includes('docs/ADR.md'), 'the single-file shape /replicate writes must be catalogued');
57
+ });
58
+
59
+ test('P3 — pipeline routing never consults has_adr (the SPARC-reroute trap)', () => {
60
+ const src = read(path.join('modules', '01-detect-parse.md'));
61
+ const m = src.match(/def detect_pipeline[\s\S]*?\n```/);
62
+ assert.ok(m, 'detect_pipeline block must exist');
63
+ // AM-2: strip comment lines first. A comment EXPLAINING why the flag is absent is not the flag
64
+ // participating in routing — the first draft of this assertion failed on its own rationale
65
+ // comment. Mention is not use; the check must read code, not prose.
66
+ const code = m[0].split('\n').filter((l) => !/^\s*#/.test(l)).join('\n');
67
+ assert.ok(!/\bhas_adr\b/.test(code),
68
+ 'has_adr inside detect_pipeline CODE re-creates the reroute hazard the ADR refuses');
69
+ assert.ok(/has_idea2prd_adr_dir\s*=\s*len\(glob\(f"\{docs_path\}\/docs\/adr\/\*\.md"\)\)\s*>\s*5/.test(m[0]),
70
+ 'the shape marker must keep the byte-identical routing expression');
71
+ });
72
+
73
+ test('P4 — zero private ADR globs outside the collector', () => {
74
+ // The dead-code class came from consumers re-reading a directory for themselves. Only the
75
+ // collector (inside 01-detect-parse.md) may touch ADR paths.
76
+ const offenders = [];
77
+ for (const rel of allModules()) {
78
+ if (rel === path.join('modules', '01-detect-parse.md')) continue;
79
+ const src = read(rel);
80
+ for (const line of src.split('\n')) {
81
+ // AM-2: the pipeline-IDENTITY shape marker is the ONE named exception. It COUNTS files to
82
+ // recognise which pipeline produced the docs; it never reads ADR content, so it cannot
83
+ // re-create the dead-code class. It must carry the marker name — an unnamed glob is an
84
+ // offender even if its expression is identical.
85
+ // Cross-family QE (gpt-5.6-sol): keying the exemption on the NAME ANYWHERE let a private
86
+ // glob smuggle itself past by mentioning the marker in a trailing comment. The exemption now
87
+ // requires the line to BE the marker's assignment.
88
+ if (/^\s*has_idea2prd_adr_dir\s*=/.test(line)) continue;
89
+ if (/glob\([^)]*adr\/\*\.md/.test(line) || /FOR EACH adr\/\*\.md/.test(line) || /SCAN adr\/\*\.md/.test(line)) {
90
+ offenders.push(rel + ': ' + line.trim());
91
+ }
92
+ }
93
+ }
94
+ assert.deepEqual(offenders, [],
95
+ 'consumer modules must iterate detected_docs.idea2prd.adrs, never glob for themselves');
96
+ });
97
+
98
+ test('P5 — the collector carries the worked examples, acid ids verbatim', () => {
99
+ const src = read(path.join('modules', '01-detect-parse.md'));
100
+ for (const acid of ['A1', 'A4', 'A5']) {
101
+ assert.ok(new RegExp('\\*\\*' + acid + '\\*\\*').test(src),
102
+ 'worked example ' + acid + ' must be stated in the collector section');
103
+ }
104
+ assert.ok(src.includes('collect_adrs'), 'the collector must exist by its contract name');
105
+ assert.ok(src.includes('dedupe_stable_first_by_id_then_title'),
106
+ 'the merge must dedupe STABLE-FIRST (A5) — precedence stated, not left to the helper');
107
+ });
108
+ });