@dzhechkov/p-replicator 1.6.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dz-manifest.json +59 -23
- package/CHANGELOG.md +127 -0
- package/README.md +106 -4
- package/package.json +4 -4
- package/sbom.json +112 -22
- package/src/utils.js +2 -0
- package/templates/.claude/commands/replicate.md +57 -1
- package/templates/.claude/hooks/check-docs-complete.cjs +174 -0
- package/templates/.claude/hooks/check-growth-trace.cjs +191 -0
- package/templates/.claude/hooks/statusline.cjs +1 -1
- package/templates/.claude/rules/replicate-pipeline.md +14 -4
- package/templates/.claude/rules/skill-interface-protocol.md +9 -0
- package/templates/.claude/skills/cc-toolkit-generator-enhanced/modules/03-generate-p0.md +6 -4
- package/templates/.claude/skills/cc-toolkit-generator-enhanced/modules/08-skill-composition.md +2 -2
- package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/templates/feature-lifecycle.md +2 -2
- package/templates/.claude/skills/requirements-validator/SKILL.md +52 -0
- package/templates/.claude/skills/reverse-engineering-unicorn/modules/01-intelligence.md +4 -4
- package/templates/.claude/skills/reverse-engineering-unicorn/modules/02-product-customers.md +2 -2
- package/templates/.claude/skills/reverse-engineering-unicorn/modules/025-cjm-prototype.md +9 -1
- package/templates/.claude/skills/reverse-engineering-unicorn/modules/03-market-competition.md +3 -3
- package/templates/.claude/skills/reverse-engineering-unicorn/modules/04-business-finance.md +3 -3
- package/templates/.claude/skills/reverse-engineering-unicorn/modules/05-growth-engine.md +132 -12
- package/templates/.claude/skills/reverse-engineering-unicorn/modules/06-playbook-synthesis.md +1 -1
- package/templates/.claude/skills/sparc-prd-mini/SKILL.md +9 -9
- package/tests/snapshot/baseline.json +20 -18
- package/tests/unit/check-docs-complete.test.js +249 -0
- package/tests/unit/check-growth-trace.test.js +188 -0
- package/tests/unit/growth-axes-and-compliance.test.js +169 -0
- package/tests/unit/growth-gate-conditional.test.js +122 -0
- package/tests/unit/growth-module-b2b-gate.test.js +20 -2
- package/tests/unit/growth-requirements-bridge.test.js +127 -0
- package/tests/unit/module-copy-identity.test.js +76 -0
- package/tests/unit/skill-paths-prebaked.test.js +174 -0
package/sbom.json
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"hashes": [
|
|
16
16
|
{
|
|
17
17
|
"alg": "SHA-256",
|
|
18
|
-
"content": "
|
|
18
|
+
"content": "57b20e3bfd20ed2a97f783d77af52bb6c74ceff5466cacb20ddb058c0a344c48"
|
|
19
19
|
}
|
|
20
20
|
]
|
|
21
21
|
},
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"hashes": [
|
|
56
56
|
{
|
|
57
57
|
"alg": "SHA-256",
|
|
58
|
-
"content": "
|
|
58
|
+
"content": "d65437974585200f0db8ef082dfbd0519f61cc29cadf3224732ae7a459a20186"
|
|
59
59
|
}
|
|
60
60
|
]
|
|
61
61
|
},
|
|
@@ -275,7 +275,7 @@
|
|
|
275
275
|
"hashes": [
|
|
276
276
|
{
|
|
277
277
|
"alg": "SHA-256",
|
|
278
|
-
"content": "
|
|
278
|
+
"content": "195281e31da3a3e5ecde1b7087ec5904f35d8f2639e6270f2d8ae685adcfa0c5"
|
|
279
279
|
}
|
|
280
280
|
]
|
|
281
281
|
},
|
|
@@ -355,7 +355,7 @@
|
|
|
355
355
|
"hashes": [
|
|
356
356
|
{
|
|
357
357
|
"alg": "SHA-256",
|
|
358
|
-
"content": "
|
|
358
|
+
"content": "d4fc297e21682290c15aa64499190ee49b7aa62e69d527c2b5332fffad02f401"
|
|
359
359
|
}
|
|
360
360
|
]
|
|
361
361
|
},
|
|
@@ -485,7 +485,7 @@
|
|
|
485
485
|
"hashes": [
|
|
486
486
|
{
|
|
487
487
|
"alg": "SHA-256",
|
|
488
|
-
"content": "
|
|
488
|
+
"content": "8862d0dc117815b1c3fc7f6e929996e2458783f1ac443eb70bc2a7ebe63a67c3"
|
|
489
489
|
}
|
|
490
490
|
]
|
|
491
491
|
},
|
|
@@ -539,6 +539,26 @@
|
|
|
539
539
|
}
|
|
540
540
|
]
|
|
541
541
|
},
|
|
542
|
+
{
|
|
543
|
+
"type": "file",
|
|
544
|
+
"name": "templates/.claude/hooks/check-docs-complete.cjs",
|
|
545
|
+
"hashes": [
|
|
546
|
+
{
|
|
547
|
+
"alg": "SHA-256",
|
|
548
|
+
"content": "13eca8ab8f18325031ca707baa2f7068f8c05484f8e4b12d4129a1023cc7a478"
|
|
549
|
+
}
|
|
550
|
+
]
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
"type": "file",
|
|
554
|
+
"name": "templates/.claude/hooks/check-growth-trace.cjs",
|
|
555
|
+
"hashes": [
|
|
556
|
+
{
|
|
557
|
+
"alg": "SHA-256",
|
|
558
|
+
"content": "91994fc109c8440306c9dd3d5eea176c533eb0818fcdb09bac2b309397d9908a"
|
|
559
|
+
}
|
|
560
|
+
]
|
|
561
|
+
},
|
|
542
562
|
{
|
|
543
563
|
"type": "file",
|
|
544
564
|
"name": "templates/.claude/hooks/check-ports.cjs",
|
|
@@ -575,7 +595,7 @@
|
|
|
575
595
|
"hashes": [
|
|
576
596
|
{
|
|
577
597
|
"alg": "SHA-256",
|
|
578
|
-
"content": "
|
|
598
|
+
"content": "948edf98ccabaa4e2dc59078435fb444639aa740baa08967d3a78067e4de3416"
|
|
579
599
|
}
|
|
580
600
|
]
|
|
581
601
|
},
|
|
@@ -625,7 +645,7 @@
|
|
|
625
645
|
"hashes": [
|
|
626
646
|
{
|
|
627
647
|
"alg": "SHA-256",
|
|
628
|
-
"content": "
|
|
648
|
+
"content": "99289f711c34cfddddbef5328b74749fc63d1f74636c6a4e4d61caa202fad05d"
|
|
629
649
|
}
|
|
630
650
|
]
|
|
631
651
|
},
|
|
@@ -635,7 +655,7 @@
|
|
|
635
655
|
"hashes": [
|
|
636
656
|
{
|
|
637
657
|
"alg": "SHA-256",
|
|
638
|
-
"content": "
|
|
658
|
+
"content": "7669309b0ae4330bc6cf6b26bd9cfc01d7b99a8e70c1f056a52574430db9378e"
|
|
639
659
|
}
|
|
640
660
|
]
|
|
641
661
|
},
|
|
@@ -775,7 +795,7 @@
|
|
|
775
795
|
"hashes": [
|
|
776
796
|
{
|
|
777
797
|
"alg": "SHA-256",
|
|
778
|
-
"content": "
|
|
798
|
+
"content": "e1ae2dd69db0d68fe276c1d0e57bd19921bf98fe91fe054716add76338442891"
|
|
779
799
|
}
|
|
780
800
|
]
|
|
781
801
|
},
|
|
@@ -825,7 +845,7 @@
|
|
|
825
845
|
"hashes": [
|
|
826
846
|
{
|
|
827
847
|
"alg": "SHA-256",
|
|
828
|
-
"content": "
|
|
848
|
+
"content": "683b254e400414b432827bedf4153a122322125eff053da4ccfeb3a62bab2a77"
|
|
829
849
|
}
|
|
830
850
|
]
|
|
831
851
|
},
|
|
@@ -965,7 +985,7 @@
|
|
|
965
985
|
"hashes": [
|
|
966
986
|
{
|
|
967
987
|
"alg": "SHA-256",
|
|
968
|
-
"content": "
|
|
988
|
+
"content": "7b01140a304c060fb085ded8711189ac3f259f999df1cb42380d0274191f1b25"
|
|
969
989
|
}
|
|
970
990
|
]
|
|
971
991
|
},
|
|
@@ -1495,7 +1515,7 @@
|
|
|
1495
1515
|
"hashes": [
|
|
1496
1516
|
{
|
|
1497
1517
|
"alg": "SHA-256",
|
|
1498
|
-
"content": "
|
|
1518
|
+
"content": "d2b8e71f4053c642f455a0f245ae18471ab25b7a21e0658f0fe410912a47bf8b"
|
|
1499
1519
|
}
|
|
1500
1520
|
]
|
|
1501
1521
|
},
|
|
@@ -1585,7 +1605,7 @@
|
|
|
1585
1605
|
"hashes": [
|
|
1586
1606
|
{
|
|
1587
1607
|
"alg": "SHA-256",
|
|
1588
|
-
"content": "
|
|
1608
|
+
"content": "0770bd63a6a1eeb34e873cb101e2ba0cf88d507e9bc03242046eaa837b9f587c"
|
|
1589
1609
|
}
|
|
1590
1610
|
]
|
|
1591
1611
|
},
|
|
@@ -1595,7 +1615,7 @@
|
|
|
1595
1615
|
"hashes": [
|
|
1596
1616
|
{
|
|
1597
1617
|
"alg": "SHA-256",
|
|
1598
|
-
"content": "
|
|
1618
|
+
"content": "499bbded50ddd368a62301083e828a42bd0223aa58322b6a952f00086b462972"
|
|
1599
1619
|
}
|
|
1600
1620
|
]
|
|
1601
1621
|
},
|
|
@@ -1605,7 +1625,7 @@
|
|
|
1605
1625
|
"hashes": [
|
|
1606
1626
|
{
|
|
1607
1627
|
"alg": "SHA-256",
|
|
1608
|
-
"content": "
|
|
1628
|
+
"content": "11e2e101c99d42306ac1d0931475c5a8899e929514e41a393b6173e03856cfc3"
|
|
1609
1629
|
}
|
|
1610
1630
|
]
|
|
1611
1631
|
},
|
|
@@ -1615,7 +1635,7 @@
|
|
|
1615
1635
|
"hashes": [
|
|
1616
1636
|
{
|
|
1617
1637
|
"alg": "SHA-256",
|
|
1618
|
-
"content": "
|
|
1638
|
+
"content": "7c2e7164463371239d92f28153777e40ee0d6dad293d59886a4cf5b306fa3627"
|
|
1619
1639
|
}
|
|
1620
1640
|
]
|
|
1621
1641
|
},
|
|
@@ -1625,7 +1645,7 @@
|
|
|
1625
1645
|
"hashes": [
|
|
1626
1646
|
{
|
|
1627
1647
|
"alg": "SHA-256",
|
|
1628
|
-
"content": "
|
|
1648
|
+
"content": "dd08226391d498a82f111f0da665ea7cea1fa8a97683fab11070f879142727f7"
|
|
1629
1649
|
}
|
|
1630
1650
|
]
|
|
1631
1651
|
},
|
|
@@ -1635,7 +1655,7 @@
|
|
|
1635
1655
|
"hashes": [
|
|
1636
1656
|
{
|
|
1637
1657
|
"alg": "SHA-256",
|
|
1638
|
-
"content": "
|
|
1658
|
+
"content": "84658390dc7beb590f70164b391ace915e42926660bc1015e4be300752b74a37"
|
|
1639
1659
|
}
|
|
1640
1660
|
]
|
|
1641
1661
|
},
|
|
@@ -1645,7 +1665,7 @@
|
|
|
1645
1665
|
"hashes": [
|
|
1646
1666
|
{
|
|
1647
1667
|
"alg": "SHA-256",
|
|
1648
|
-
"content": "
|
|
1668
|
+
"content": "3cd76a218aa9f94e0ff8ff2fc257647540674f68df3883f0de5e76e1cea26d9b"
|
|
1649
1669
|
}
|
|
1650
1670
|
]
|
|
1651
1671
|
},
|
|
@@ -1685,7 +1705,7 @@
|
|
|
1685
1705
|
"hashes": [
|
|
1686
1706
|
{
|
|
1687
1707
|
"alg": "SHA-256",
|
|
1688
|
-
"content": "
|
|
1708
|
+
"content": "71f97e269e8b1807550a4ef91b2f0ac4dd892a074781504890d33db8f32685d2"
|
|
1689
1709
|
}
|
|
1690
1710
|
]
|
|
1691
1711
|
},
|
|
@@ -1735,7 +1755,7 @@
|
|
|
1735
1755
|
"hashes": [
|
|
1736
1756
|
{
|
|
1737
1757
|
"alg": "SHA-256",
|
|
1738
|
-
"content": "
|
|
1758
|
+
"content": "0d5af8aadd98bf1f5f07a02b9d6d7a5dd243d4263a5b03de1a249fdaa5111a56"
|
|
1739
1759
|
}
|
|
1740
1760
|
]
|
|
1741
1761
|
},
|
|
@@ -1789,6 +1809,26 @@
|
|
|
1789
1809
|
}
|
|
1790
1810
|
]
|
|
1791
1811
|
},
|
|
1812
|
+
{
|
|
1813
|
+
"type": "file",
|
|
1814
|
+
"name": "tests/unit/check-docs-complete.test.js",
|
|
1815
|
+
"hashes": [
|
|
1816
|
+
{
|
|
1817
|
+
"alg": "SHA-256",
|
|
1818
|
+
"content": "0dbc15c1077c9c02766cfa9b1eb357b30b59639f2fa91003e44e3c6485da3e7b"
|
|
1819
|
+
}
|
|
1820
|
+
]
|
|
1821
|
+
},
|
|
1822
|
+
{
|
|
1823
|
+
"type": "file",
|
|
1824
|
+
"name": "tests/unit/check-growth-trace.test.js",
|
|
1825
|
+
"hashes": [
|
|
1826
|
+
{
|
|
1827
|
+
"alg": "SHA-256",
|
|
1828
|
+
"content": "733d87fe8eca897fcfc5cad9342d4db2085270c21d0d8866d817a9b9418511c2"
|
|
1829
|
+
}
|
|
1830
|
+
]
|
|
1831
|
+
},
|
|
1792
1832
|
{
|
|
1793
1833
|
"type": "file",
|
|
1794
1834
|
"name": "tests/unit/check-ports.test.js",
|
|
@@ -1829,13 +1869,43 @@
|
|
|
1829
1869
|
}
|
|
1830
1870
|
]
|
|
1831
1871
|
},
|
|
1872
|
+
{
|
|
1873
|
+
"type": "file",
|
|
1874
|
+
"name": "tests/unit/growth-axes-and-compliance.test.js",
|
|
1875
|
+
"hashes": [
|
|
1876
|
+
{
|
|
1877
|
+
"alg": "SHA-256",
|
|
1878
|
+
"content": "88e4d8c7ae278c46dfce4d89fcbd1dee49c0b545ebc07e459306bfdc70567048"
|
|
1879
|
+
}
|
|
1880
|
+
]
|
|
1881
|
+
},
|
|
1882
|
+
{
|
|
1883
|
+
"type": "file",
|
|
1884
|
+
"name": "tests/unit/growth-gate-conditional.test.js",
|
|
1885
|
+
"hashes": [
|
|
1886
|
+
{
|
|
1887
|
+
"alg": "SHA-256",
|
|
1888
|
+
"content": "bff4149b339294ac822d1a8fe732d21560ea8d70bcb80b10f3491105a4a9b75c"
|
|
1889
|
+
}
|
|
1890
|
+
]
|
|
1891
|
+
},
|
|
1832
1892
|
{
|
|
1833
1893
|
"type": "file",
|
|
1834
1894
|
"name": "tests/unit/growth-module-b2b-gate.test.js",
|
|
1835
1895
|
"hashes": [
|
|
1836
1896
|
{
|
|
1837
1897
|
"alg": "SHA-256",
|
|
1838
|
-
"content": "
|
|
1898
|
+
"content": "05ce46882984905ba41983c34b8d983e9fa9bf5eb4b1af988841a46e78142098"
|
|
1899
|
+
}
|
|
1900
|
+
]
|
|
1901
|
+
},
|
|
1902
|
+
{
|
|
1903
|
+
"type": "file",
|
|
1904
|
+
"name": "tests/unit/growth-requirements-bridge.test.js",
|
|
1905
|
+
"hashes": [
|
|
1906
|
+
{
|
|
1907
|
+
"alg": "SHA-256",
|
|
1908
|
+
"content": "2f560a16f1757df46d5d52e5d29a7aad3e4c327cd07ea9cc69240c7dd1c93f2a"
|
|
1839
1909
|
}
|
|
1840
1910
|
]
|
|
1841
1911
|
},
|
|
@@ -1859,6 +1929,16 @@
|
|
|
1859
1929
|
}
|
|
1860
1930
|
]
|
|
1861
1931
|
},
|
|
1932
|
+
{
|
|
1933
|
+
"type": "file",
|
|
1934
|
+
"name": "tests/unit/module-copy-identity.test.js",
|
|
1935
|
+
"hashes": [
|
|
1936
|
+
{
|
|
1937
|
+
"alg": "SHA-256",
|
|
1938
|
+
"content": "a1269ad9f68a38faa7710fc63ce850feacb2304996b85ef38237b222815a3532"
|
|
1939
|
+
}
|
|
1940
|
+
]
|
|
1941
|
+
},
|
|
1862
1942
|
{
|
|
1863
1943
|
"type": "file",
|
|
1864
1944
|
"name": "tests/unit/pipeline-file-ownership.test.js",
|
|
@@ -1879,6 +1959,16 @@
|
|
|
1879
1959
|
}
|
|
1880
1960
|
]
|
|
1881
1961
|
},
|
|
1962
|
+
{
|
|
1963
|
+
"type": "file",
|
|
1964
|
+
"name": "tests/unit/skill-paths-prebaked.test.js",
|
|
1965
|
+
"hashes": [
|
|
1966
|
+
{
|
|
1967
|
+
"alg": "SHA-256",
|
|
1968
|
+
"content": "efb13ca5d2fa68f3186e5af4142509d260d7cf63c902f59e915110bb550887df"
|
|
1969
|
+
}
|
|
1970
|
+
]
|
|
1971
|
+
},
|
|
1882
1972
|
{
|
|
1883
1973
|
"type": "file",
|
|
1884
1974
|
"name": "tests/unit/sparc-reconciliation.test.js",
|
package/src/utils.js
CHANGED
|
@@ -334,6 +334,8 @@ const COMPONENTS = {
|
|
|
334
334
|
'statusline': 'Multi-line dashboard (pipeline, roadmap, toolkit) for Claude Code statusLine',
|
|
335
335
|
'state-update': 'Argv-driven helper for pipeline commands to publish current command + phase + progress',
|
|
336
336
|
'check-ports': 'Enforce docker-ports Правило №0 against a real compose (invoke deliberately; exits 0/1/2)',
|
|
337
|
+
'check-growth-trace': 'Did the M5 growth seed reach docs/Specification.md (invoke deliberately; exits 0/1/2)',
|
|
338
|
+
'check-docs-complete': 'Are Phase-1 documents written and placeholder-free, before the Phase-2 swarm (invoke deliberately; exits 0/1/2)',
|
|
337
339
|
},
|
|
338
340
|
},
|
|
339
341
|
// ─── Project-generated groups (created by /replicate Phase 3) ───────────
|
|
@@ -181,7 +181,22 @@ discovered: a B2B run now spends M5's time.
|
|
|
181
181
|
Honest limit: line 62 of the module says to *switch* to a sales-led framework; it does not define
|
|
182
182
|
one. A B2B run gets the type-appropriate instruction, not a type-appropriate playbook.
|
|
183
183
|
|
|
184
|
-
**Output
|
|
184
|
+
**Output — WRITE FIRST, then hand off. Both, in this order:**
|
|
185
|
+
|
|
186
|
+
1. **Write** the full Product Discovery Brief to `docs/product-discovery-brief.md`.
|
|
187
|
+
2. **Then** pass it to Phase 1 as pre-filled context, exactly as before.
|
|
188
|
+
|
|
189
|
+
The hand-off is unchanged; the file is an ADDITION. Until this was added the brief existed only in
|
|
190
|
+
the conversation — it made Phase 1 skip its own Phase 0 (`sparc-prd-mini/SKILL.md:993-999`) and then
|
|
191
|
+
vanished, so the M5 growth analysis had no artifact any later step could read. Nothing downstream was
|
|
192
|
+
ignoring it: there was nothing to ignore.
|
|
193
|
+
|
|
194
|
+
The file MUST include M5's `Growth Requirements Seed` table verbatim when M5 ran — that table is the
|
|
195
|
+
only place `FR-GROWTH-nnn` obligations exist before Phase 1 promotes them.
|
|
196
|
+
|
|
197
|
+
**When this file is absent it means Phase 0 did not run** — the `--from-docs` / `--skip-discovery`
|
|
198
|
+
entry skips Phase 0 entirely (see the alternative-entry section). Absence is NOT evidence that the
|
|
199
|
+
project has no growth requirements, and no consumer may read it that way.
|
|
185
200
|
|
|
186
201
|
**Checkpoint:**
|
|
187
202
|
```
|
|
@@ -253,8 +268,49 @@ Created [N] documents in docs/
|
|
|
253
268
|
═══════════════════════════════════════════════════════════════
|
|
254
269
|
```
|
|
255
270
|
|
|
271
|
+
### Прерванный прогон: как продолжить с того места
|
|
272
|
+
|
|
273
|
+
`/replicate` — интерактивный конвейер с четырьмя чекпоинтами, и продолжить его можно **уже сейчас,
|
|
274
|
+
без всякой новой машинерии**. Три сигнала, каждый существует независимо от этого раздела:
|
|
275
|
+
|
|
276
|
+
| Вопрос | Чем отвечается |
|
|
277
|
+
|---|---|
|
|
278
|
+
| До какой фазы дошли? | `git log --oneline` — после КАЖДОЙ фазы делается свой коммит (`docs: SPARC…`, `docs: validation report…`, `feat: Claude Code toolkit…`, `chore: initial project setup…`) |
|
|
279
|
+
| Документы Фазы 1 дописаны? | `node .claude/hooks/check-docs-complete.cjs .` — `0` дописаны, `1` названо, чего не хватает, `2` Фаза 1 не запускалась |
|
|
280
|
+
| Тулкит Фазы 3 сгенерирован? | `npx @dzhechkov/p-replicator verify` — раздел «Post-/replicate» |
|
|
281
|
+
|
|
282
|
+
**Как продолжить:** посмотрите последний коммит фазы, затем скажите `/replicate` прямым текстом:
|
|
283
|
+
*«продолжай с Фазы 3, Фазы 0-2 уже сделаны»*. Конвейер интерактивный — человек на чекпоинте и есть
|
|
284
|
+
механизм возобновления.
|
|
285
|
+
|
|
286
|
+
**Почему здесь нет автоматического определения фазы.** Оно рассматривалось (бэклог `58575b07`) и
|
|
287
|
+
сознательно НЕ реализовано: три сигнала выше уже дают ответ, а свежая логика ветвления в
|
|
288
|
+
интерактивном конвейере — это то, что может сработать неверно ровно тогда, когда прогон и так пошёл
|
|
289
|
+
не по плану. Запись решения важнее самого решения: если вы вернётесь к этому вопросу, начинайте с
|
|
290
|
+
того, что перечисленного выше оказалось недостаточно.
|
|
291
|
+
|
|
256
292
|
### Phase 2: VALIDATION
|
|
257
293
|
|
|
294
|
+
**Шаг 2.0 — ДЕТЕРМИНИРОВАННАЯ ПРОВЕРКА ПОЛНОТЫ. Выполняется ПЕРВОЙ, до запуска роя.**
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
node .claude/hooks/check-docs-complete.cjs .
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
| Код | Что делать |
|
|
301
|
+
|:---:|---|
|
|
302
|
+
| `0` | продолжайте — рой валидации запускается |
|
|
303
|
+
| `1` | **НЕ запускайте рой.** Вернитесь в Фазу 1 и допишите названные документы |
|
|
304
|
+
| `2` | проверка не выполнена — почините вызов и повторите; это НЕ «всё в порядке» |
|
|
305
|
+
|
|
306
|
+
Причина, по которой шаг стоит здесь, а не внутри роя: существование файла, его пустота и
|
|
307
|
+
незаполненный шаблон решаются сорока строками кода. Отправлять на этот вопрос рой агентов — значит
|
|
308
|
+
платить вероятностной проверкой за то, что решается детерминированно. Рою остаётся то, ради чего он
|
|
309
|
+
и нужен: тестируемость, полнота требований, реализуемость.
|
|
310
|
+
|
|
311
|
+
Ограничение, которое проверка печатает сама: она доказывает, что документы НАПИСАНЫ, а не что они
|
|
312
|
+
верны. Верность — работа роя.
|
|
313
|
+
|
|
258
314
|
Read the skill: `.claude/skills/requirements-validator/SKILL.md`
|
|
259
315
|
|
|
260
316
|
**Goal:** Verify all documentation for completeness, testability, and implementation readiness.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* check-docs-complete.cjs — the cheap question, asked before the expensive one.
|
|
6
|
+
*
|
|
7
|
+
* Phase 2 of /replicate launches a SWARM of validation agents over whatever Phase 1 produced. Between
|
|
8
|
+
* the two there was nothing: existence, emptiness and unfilled placeholders are decidable by this
|
|
9
|
+
* script, and sending a multi-agent swarm to discover them is a layer-1 check living at layer 3.
|
|
10
|
+
*
|
|
11
|
+
* NOT an event hook, like `state-update.cjs`, `check-ports.cjs` and `check-growth-trace.cjs`. This
|
|
12
|
+
* package's hooks are NON-BLOCKING by contract, so a hook could print but never refuse. Invoke it:
|
|
13
|
+
*
|
|
14
|
+
* node .claude/hooks/check-docs-complete.cjs [path-to-project]
|
|
15
|
+
*
|
|
16
|
+
* Exit codes:
|
|
17
|
+
* 0 every required document exists, has content, and carries no unfilled placeholder
|
|
18
|
+
* 1 a NAMED document is missing, empty, or still a template
|
|
19
|
+
* 2 THE CHECK DID NOT RUN — no docs/ directory, or it could not be read
|
|
20
|
+
*
|
|
21
|
+
* Honest limit, and it is printed on the passing path: this proves the documents were WRITTEN, not
|
|
22
|
+
* that they are correct. Correctness is what the Phase-2 swarm is for; this only stops the swarm
|
|
23
|
+
* being spent discovering an empty file.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const fs = require('node:fs');
|
|
27
|
+
const path = require('node:path');
|
|
28
|
+
|
|
29
|
+
/** What Phase 1 writes. `optional: true` mirrors replicate.md's own `(if applicable)`. */
|
|
30
|
+
const DOCS = [
|
|
31
|
+
{ file: 'PRD.md' },
|
|
32
|
+
{ file: 'Solution_Strategy.md' },
|
|
33
|
+
{ file: 'Specification.md' },
|
|
34
|
+
{ file: 'Pseudocode.md' },
|
|
35
|
+
{ file: 'Architecture.md' },
|
|
36
|
+
{ file: 'Refinement.md' },
|
|
37
|
+
{ file: 'Completion.md' },
|
|
38
|
+
{ file: 'Research_Findings.md' },
|
|
39
|
+
{ file: 'Final_Summary.md' },
|
|
40
|
+
{ file: 'C4_Diagrams.md', optional: true },
|
|
41
|
+
{ file: 'ADR.md', optional: true },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
/** Below this a document has a heading and nothing under it. */
|
|
45
|
+
const MIN_CHARS = 200;
|
|
46
|
+
|
|
47
|
+
function say(s) { process.stdout.write(s + '\n'); }
|
|
48
|
+
|
|
49
|
+
function cannotCheck(reason, hint) {
|
|
50
|
+
say('⚠️ проверка НЕ выполнена: ' + reason);
|
|
51
|
+
if (hint) say(' ' + hint);
|
|
52
|
+
process.exit(2);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Unfilled placeholders — and the reason this is split into two confidence levels.
|
|
57
|
+
*
|
|
58
|
+
* The first version used one rule: a bracketed token not followed by `(`. Cross-family review
|
|
59
|
+
* destroyed it with two inputs, both REPRODUCED before this rewrite:
|
|
60
|
+
*
|
|
61
|
+
* 1. `A[Web App]` — a mermaid node. The BUNDLED sparc-prd-mini skill REQUIRES mermaid diagrams in
|
|
62
|
+
* Architecture.md (SKILL.md:570-583), so the gate blocked every normally generated project.
|
|
63
|
+
* 2. `[GAP: needs performance targets]` — which Phase 1 writes DELIBERATELY in --from-docs mode for
|
|
64
|
+
* Phase 2 to resolve (replicate.md:82-94). Blocking on it deadlocks the documented flow: the
|
|
65
|
+
* only step that can clear the marker is the one the gate refuses to start.
|
|
66
|
+
*
|
|
67
|
+
* The lesson is about CONFIDENCE, not about patterns. A false block here stops the whole pipeline,
|
|
68
|
+
* which is worse than a missed placeholder the Phase-2 swarm would have caught anyway. So:
|
|
69
|
+
*
|
|
70
|
+
* BLOCKING — shapes that cannot be anything else: `{{…}}`, TODO/TBD/XXX/FIXME.
|
|
71
|
+
* WARNING — bracketed prose. Reported by name, never blocking, because this script cannot tell
|
|
72
|
+
* `[описание продукта]` from a diagram label or a citation without understanding the
|
|
73
|
+
* document, and guessing wrong costs more than staying quiet.
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
/** Fenced code blocks hold mermaid, arrays and code — none of it prose, none of it ours to judge. */
|
|
77
|
+
function stripFences(body) {
|
|
78
|
+
return body.replace(/^```[\s\S]*?^```/gm, '').replace(/`[^`\n]*`/g, '');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const BLOCKING = [
|
|
82
|
+
{ re: /\{\{[^}\n]{1,80}\}\}/g, what: 'незаполненный {{шаблон}}' },
|
|
83
|
+
{ re: /(?<![\p{L}\p{N}])(TODO|TBD|XXX|FIXME)(?![\p{L}\p{N}])/giu, what: 'маркер TODO/TBD/XXX' },
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
/** A structured marker Phase 2 OWNS. It must reach Phase 2, so it is never a finding here. */
|
|
87
|
+
const GAP = /\[GAP:[^\]\n]*\]/g;
|
|
88
|
+
|
|
89
|
+
const SUSPECT = /\[[^\]\n]{1,80}\](?![(\[])/g;
|
|
90
|
+
|
|
91
|
+
function scan(body) {
|
|
92
|
+
const clean = stripFences(body).replace(GAP, '');
|
|
93
|
+
const blocking = [];
|
|
94
|
+
for (const { re, what } of BLOCKING) {
|
|
95
|
+
re.lastIndex = 0;
|
|
96
|
+
for (let m = re.exec(clean); m !== null && blocking.length < 3; m = re.exec(clean)) {
|
|
97
|
+
blocking.push(what + ' «' + m[0].slice(0, 40) + '»');
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const warn = [];
|
|
101
|
+
SUSPECT.lastIndex = 0;
|
|
102
|
+
for (let m = SUSPECT.exec(clean); m !== null && warn.length < 3; m = SUSPECT.exec(clean)) {
|
|
103
|
+
const t = m[0];
|
|
104
|
+
if (/^\[\^?\d+\]$/.test(t)) continue; // a citation or footnote, not a placeholder
|
|
105
|
+
warn.push(t.slice(0, 40));
|
|
106
|
+
}
|
|
107
|
+
return { blocking, warn };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function main() {
|
|
111
|
+
const root = process.argv[2] || '.';
|
|
112
|
+
const docs = path.join(root, 'docs');
|
|
113
|
+
let st;
|
|
114
|
+
try { st = fs.statSync(docs); } catch {
|
|
115
|
+
cannotCheck('нет каталога ' + docs,
|
|
116
|
+
'Фаза 1 ещё не отработала — это НЕ «документы неполны», это «проверять нечего»');
|
|
117
|
+
}
|
|
118
|
+
if (!st.isDirectory()) cannotCheck(docs + ' существует, но это не каталог');
|
|
119
|
+
|
|
120
|
+
const problems = [];
|
|
121
|
+
const warnings = [];
|
|
122
|
+
let checked = 0;
|
|
123
|
+
|
|
124
|
+
for (const d of DOCS) {
|
|
125
|
+
const abs = path.join(docs, d.file);
|
|
126
|
+
let body;
|
|
127
|
+
try { body = fs.readFileSync(abs, 'utf-8'); } catch (e) {
|
|
128
|
+
if (e && e.code === 'ENOENT') {
|
|
129
|
+
if (!d.optional) problems.push(d.file + ': отсутствует');
|
|
130
|
+
continue; // an optional absence is a legitimate answer
|
|
131
|
+
}
|
|
132
|
+
cannotCheck('не читается ' + d.file + ': ' + ((e && e.message) || e));
|
|
133
|
+
}
|
|
134
|
+
checked++;
|
|
135
|
+
if (body.trim().length < MIN_CHARS) {
|
|
136
|
+
problems.push(d.file + ': пуст или почти пуст (' + body.trim().length + ' симв., порог '
|
|
137
|
+
+ MIN_CHARS + ')');
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
const { blocking, warn } = scan(body);
|
|
141
|
+
if (blocking.length) problems.push(d.file + ': остались ' + blocking.join(', '));
|
|
142
|
+
if (warn.length) warnings.push(d.file + ': возможно незаполнено — ' + warn.join(', '));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!checked) {
|
|
146
|
+
cannotCheck('в ' + docs + ' не нашлось ни одного SPARC-документа',
|
|
147
|
+
'каталог есть, но пуст — это не «всё в порядке»');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const sayWarnings = () => {
|
|
151
|
+
if (!warnings.length) return;
|
|
152
|
+
say('⚠️ на глаз (НЕ блокирует — скрипт не отличает шаблон от подписи к диаграмме):');
|
|
153
|
+
for (const w of warnings) say(' • ' + w);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
if (problems.length) {
|
|
157
|
+
sayWarnings();
|
|
158
|
+
say('❌ документы Фазы 1 не готовы к валидации (' + problems.length + '):');
|
|
159
|
+
for (const p of problems) say(' • ' + p);
|
|
160
|
+
say(' Рой валидации запускать рано: он потратит агентов на то, что видно отсюда.');
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
sayWarnings();
|
|
165
|
+
say('✅ все обязательные документы на месте, непусты и без незаполненных шаблонов ('
|
|
166
|
+
+ checked + ' проверено)');
|
|
167
|
+
say(' Ограничение: это доказывает, что документы НАПИСАНЫ, а не что они верны.');
|
|
168
|
+
say(' Верность — работа роя валидации Фазы 2; проверка лишь не даёт потратить его впустую.');
|
|
169
|
+
process.exit(0);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try { main(); } catch (err) {
|
|
173
|
+
cannotCheck('внутренняя ошибка проверки: ' + String((err && err.message) || err));
|
|
174
|
+
}
|