@dzhechkov/p-replicator 1.13.1 → 1.13.3
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 +93 -25
- package/README.md +19 -1
- package/package.json +5 -4
- package/sbom.json +194 -24
- package/scripts/check-pipeline-gaps.sh +510 -21
- package/src/utils.js +2 -0
- package/templates/.claude/commands/feature.md +43 -1
- package/templates/.claude/commands/replicate.md +9 -1
- package/templates/.claude/hooks/check-dangling-refs.cjs +89 -0
- package/templates/.claude/hooks/check-docs-complete.cjs +7 -0
- package/templates/.claude/hooks/check-review-contract.cjs +205 -0
- package/templates/.claude/hooks/statusline.cjs +1 -1
- package/templates/.claude/rules/cost-of-detection-ladder.md +37 -4
- package/templates/.claude/rules/docker-ports.md +28 -0
- package/templates/.claude/rules/feature-lifecycle.md +21 -0
- package/templates/.claude/rules/replicate-pipeline.md +5 -4
- package/templates/.claude/skills/brutal-honesty-review/resources/assessment-rubrics.md +12 -2
- package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/security-patterns-library.md +45 -0
- package/templates/.claude/skills/requirements-validator/SKILL.md +18 -5
- package/templates/.claude/skills/requirements-validator/references/feature-report-contracts.md +67 -0
- package/templates/.claude/skills/requirements-validator/references/scoring-system.md +15 -6
- package/tests/e2e/feature-contour.test.js +176 -0
- package/tests/fixtures/feature-contour/docs/features/demo-gate/01_specification.md +15 -0
- package/tests/fixtures/feature-contour/docs/features/demo-gate/02_pseudocode.md +19 -0
- package/tests/fixtures/feature-contour/docs/features/demo-gate/03_architecture.md +3 -0
- package/tests/fixtures/feature-contour/docs/features/demo-gate/04_refinement.md +3 -0
- package/tests/fixtures/feature-contour/docs/features/demo-gate/05_completion.md +7 -0
- package/tests/fixtures/feature-contour/docs/features/demo-gate/review-report.md +11 -0
- package/tests/fixtures/feature-contour/docs/features/demo-gate/validation-report.md +10 -0
- package/tests/fixtures/feature-contour/tests/demo.test.js +16 -0
- package/tests/snapshot/baseline.json +17 -14
- package/tests/unit/capture-source-path.test.js +73 -24
- package/tests/unit/check-dangling-refs.test.js +91 -0
- package/tests/unit/check-review-contract.test.js +181 -0
- package/tests/unit/guard-honest-input-meta.test.js +49 -0
- package/tests/unit/honest-failure-rules.test.js +40 -3
- package/tests/unit/negative-conclusion-gate.test.js +3 -3
- package/tests/unit/optional-doc-idiom.test.js +83 -0
- package/tests/unit/quote-provenance.test.js +4 -0
- package/tests/unit/sync-templates-guard.test.js +46 -3
- package/tests/unit/traceability-completion-gate.test.js +267 -0
- package/tests/unit/traceability-negative-fixture.test.js +19 -5
- package/tests/unit/verdict-vocabulary.test.js +72 -0
|
@@ -65,11 +65,14 @@ Always flag these terms and suggest specific replacements:
|
|
|
65
65
|
**Score <50 = BLOCKED from development.** Provide rewrite suggestions.
|
|
66
66
|
|
|
67
67
|
**Blocking floor — the weakest link decides, never the average.** Independently of the score, a
|
|
68
|
-
requirement is BLOCKED if `Testable = 0` (no acceptance criteria)
|
|
69
|
-
AC")
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
`
|
|
68
|
+
requirement is BLOCKED if `Testable = 0` (no acceptance criteria), `Completeness = 0` (rubric "No
|
|
69
|
+
AC"), or `Traceability = 0` (no AC has a named scenario in the Criterion scenarios table). A story can
|
|
70
|
+
total 72/100 with no acceptance criteria, and 72 reads as "minor fixes" in the table above. A
|
|
71
|
+
non-zero `Testable` or `Completeness` REQUIRES quoting the acceptance criteria it scores; a non-zero
|
|
72
|
+
`Traceability` REQUIRES quoting the Criterion scenarios table or referencing its document and heading.
|
|
73
|
+
No artifact means 0, because the agent that scores is the agent the floor binds. The floor is closed
|
|
74
|
+
at those three; `Measurable` is deliberately excluded — see `references/scoring-system.md` →
|
|
75
|
+
"Blocking floor" for the worked cases and the reason.
|
|
73
76
|
|
|
74
77
|
## Output Format
|
|
75
78
|
|
|
@@ -77,6 +80,7 @@ floor is closed at those two: `Measurable` and `Traceability` are deliberately e
|
|
|
77
80
|
|
|
78
81
|
```markdown
|
|
79
82
|
# Requirements Testability Analysis
|
|
83
|
+
Spec revision: sha256:<digest of 01_specification.md>
|
|
80
84
|
|
|
81
85
|
## Summary
|
|
82
86
|
- Stories analyzed: X
|
|
@@ -90,6 +94,11 @@ floor is closed at those two: `Measurable` and `Traceability` are deliberately e
|
|
|
90
94
|
| US-001 | ... | 92/100 | 6/6 ✓ | 5/5 ✓ | READY |
|
|
91
95
|
| US-002 | ... | 45/100 | 3/6 ✗ | 2/5 ✗ | BLOCKED |
|
|
92
96
|
|
|
97
|
+
## Criterion scenarios
|
|
98
|
+
| Criterion | Scenario |
|
|
99
|
+
|-----------|----------|
|
|
100
|
+
| AC-example-1 | Successful example flow |
|
|
101
|
+
|
|
93
102
|
## Detailed Analysis: US-002 (BLOCKED)
|
|
94
103
|
|
|
95
104
|
### INVEST Analysis
|
|
@@ -110,6 +119,10 @@ floor is closed at those two: `Measurable` and `Traceability` are deliberately e
|
|
|
110
119
|
- Add AC: "Given X, when Y, then Z within 200ms"
|
|
111
120
|
```
|
|
112
121
|
|
|
122
|
+
Compute the revision line from the specification bytes with
|
|
123
|
+
`sha256sum docs/features/<f>/01_specification.md`. The `## Criterion scenarios` table is the artifact
|
|
124
|
+
the Traceability score keys on: it maps each AC id to a named scenario.
|
|
125
|
+
|
|
113
126
|
### Security Acceptance Criteria (scoring: +5 present / -10 missing, see Scoring Bonus below)
|
|
114
127
|
|
|
115
128
|
When requirements involve authentication, data storage, external APIs, or multi-tenancy,
|
package/templates/.claude/skills/requirements-validator/references/feature-report-contracts.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Feature report contracts (`/feature`, Phases 2–4)
|
|
2
|
+
|
|
3
|
+
Byte-level formats that the packaged gates enforce. Read this when WRITING one of the three reports;
|
|
4
|
+
the gates are the machine half: `scripts/check-pipeline-gaps.sh` (`--report-revision`,
|
|
5
|
+
`--criterion-scenarios`, `--completion`) and `.claude/hooks/check-review-contract.cjs`. Exit codes
|
|
6
|
+
everywhere: `0` pass · `1` named gap(s), one line each · `2` could not establish (missing, unreadable,
|
|
7
|
+
symlinked or malformed input). Neither non-zero status is ever a warning.
|
|
8
|
+
|
|
9
|
+
## Shared keys
|
|
10
|
+
|
|
11
|
+
- **AC ids** are `### AC-<slug>-<n>` headings in `docs/features/<f>/01_specification.md`
|
|
12
|
+
(`(FR|NFR|AC)-[A-Za-z0-9]+(-[A-Za-z0-9]+)*-[0-9]+`; fenced code blocks are ignored). FR/NFR stay under
|
|
13
|
+
the Phase 1 specification↔pseudocode gate; the three contracts below key on **AC-** ids only.
|
|
14
|
+
- **Spec revision line** — `Spec revision: sha256:<64 lowercase hex>`, the SHA-256 of the BYTES of
|
|
15
|
+
`01_specification.md` (`sha256sum docs/features/<f>/01_specification.md`), within the first 20 lines
|
|
16
|
+
of `validation-report.md` and of `review-report.md`. A report is bound to the revision it judged;
|
|
17
|
+
editing the specification afterwards makes the report stale by construction.
|
|
18
|
+
|
|
19
|
+
## validation-report.md (Phase 2)
|
|
20
|
+
|
|
21
|
+
```markdown
|
|
22
|
+
# Requirements Testability Analysis
|
|
23
|
+
Spec revision: sha256:<digest of 01_specification.md>
|
|
24
|
+
|
|
25
|
+
## Criterion scenarios
|
|
26
|
+
| Criterion | Scenario |
|
|
27
|
+
|-----------|----------|
|
|
28
|
+
| AC-example-1 | Successful example flow |
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`## Criterion scenarios` maps every AC id to the named BDD scenario that covers it — the artifact the
|
|
32
|
+
`Traceability` blocking floor keys on (`references/scoring-system.md`). It is NOT the algorithm-level
|
|
33
|
+
`## Scenario Coverage` block that `sparc-prd-mini` writes into `02_pseudocode.md`. An AC id without a
|
|
34
|
+
row, an undeclared id, or an empty scenario cell is a named gap; a missing table cannot be established.
|
|
35
|
+
|
|
36
|
+
## 05_completion.md (Phase 3)
|
|
37
|
+
|
|
38
|
+
```markdown
|
|
39
|
+
## Criterion coverage
|
|
40
|
+
| Criterion | Test file | Test title |
|
|
41
|
+
|-----------|-----------|------------|
|
|
42
|
+
| AC-example-1 | tests/example.test.js | rejects an invalid request |
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
One row per AC id. `Test file` is relative to the project root and must be a regular file inside it
|
|
46
|
+
(absolute, `..`-escaping and symlinked paths are refused); `Test title` must occur verbatim in that
|
|
47
|
+
file. Both directions compare: an AC id without a row and a row whose id is not in the specification
|
|
48
|
+
are gaps. A table of names is not evidence — the gate opens the file.
|
|
49
|
+
|
|
50
|
+
## review-report.md (Phase 4)
|
|
51
|
+
|
|
52
|
+
```markdown
|
|
53
|
+
Reviewer family: claude|codex|human|unknown
|
|
54
|
+
Spec revision: sha256:<64 lowercase hex>
|
|
55
|
+
|
|
56
|
+
## Spec conformance
|
|
57
|
+
| Criterion | Verdict | Evidence |
|
|
58
|
+
|-----------|---------|----------|
|
|
59
|
+
| AC-example-1 | met | tests/example.test.js — rejects an invalid request |
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Input contract: the review receives `01_specification.md` and `validation-report.md`; a review that
|
|
63
|
+
was not given the specification is incomplete by construction and must say so instead of grading code
|
|
64
|
+
quality alone. Output contract: both header lines within the first 20 lines (`Reviewer family:` is a
|
|
65
|
+
DISCLOSURE — the package refuses silence but cannot verify which family reviewed); every AC id exactly
|
|
66
|
+
once in `## Spec conformance`; `Verdict` is exactly `met`, `not met` or `unverifiable` (lowercase);
|
|
67
|
+
a `met` or `not met` row carries non-empty evidence; a `not met` row ideally carries a reproducer.
|
|
@@ -31,7 +31,7 @@ Total Score = INVEST Score (50%) + SMART Score (30%) + Quality Score (20%)
|
|
|
31
31
|
|
|
32
32
|
| Criterion | Weight | Points | Calculation |
|
|
33
33
|
|-----------|--------|--------|-------------|
|
|
34
|
-
| Traceability | 10% | 0-10 | 10 if
|
|
34
|
+
| Traceability | 10% | 0-10 | 10 if every AC id has a named scenario in the Criterion scenarios table; 5 if some do (the uncovered AC ids MUST be listed by name); 0 if none do, or the block is absent ⇒ the floor applies |
|
|
35
35
|
| Completeness | 10% | 0-10 | See completeness rubric below |
|
|
36
36
|
|
|
37
37
|
### Completeness Rubric
|
|
@@ -56,12 +56,13 @@ Total Score = INVEST Score (50%) + SMART Score (30%) + Quality Score (20%)
|
|
|
56
56
|
### Blocking floor (overrides the total, both directions)
|
|
57
57
|
|
|
58
58
|
**The weakest link decides, never the average.** A requirement is BLOCKED — whatever its total says —
|
|
59
|
-
if
|
|
59
|
+
if any of these is zero:
|
|
60
60
|
|
|
61
61
|
| Criterion | Zero means | Why it vetoes |
|
|
62
62
|
|-----------|-----------|---------------|
|
|
63
63
|
| `Testable` (INVEST, 8) | no acceptance criteria exist | the gate exists to block untestable requirements |
|
|
64
64
|
| `Completeness` (Quality, 10) | "No AC" on the rubric above | nothing states what "done" is |
|
|
65
|
+
| `Traceability` (Quality, 10) | no AC has a named scenario in the Criterion scenarios table | a story can otherwise pass validation with no scenario or test on any criterion |
|
|
65
66
|
|
|
66
67
|
Without this floor the total alone lets an untestable requirement through. Worked case: a story with
|
|
67
68
|
NO acceptance criteria and NO test links loses `Testable` 8, `Completeness` 10 and `Traceability` 10,
|
|
@@ -76,10 +77,18 @@ criteria AND is bound by the floor, so a score is not evidence of anything — r
|
|
|
76
77
|
> the actual text, with its document and heading. `Testable = 4` means the AC exist and are vague:
|
|
77
78
|
> quote them. **No quote ⇒ the score is 0 ⇒ the floor applies.**
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
> A non-zero `Traceability` REQUIRES the Criterion scenarios table (AC id → named scenario) quoted,
|
|
81
|
+
> or referenced by document and heading. **No table ⇒ the score is 0 ⇒ the floor applies.**
|
|
82
|
+
|
|
83
|
+
Measured worked case: story FR-009 had 27 criteria with no IDs, more than 10 of them uncovered by
|
|
84
|
+
any scenario, and passed validation. Its validation report 3.1 was written against document revision
|
|
85
|
+
2; only a human noticed. With no named AC-id → scenario artifact, `Traceability = 0` and the floor
|
|
86
|
+
blocks that contour instead of letting its total hide the missing coverage.
|
|
87
|
+
|
|
88
|
+
**The floor is CLOSED at these three.** `Measurable` is deliberately NOT on it: a requirement with no
|
|
89
|
+
number is often correct ("the user can export the report as PDF" is specific, testable and
|
|
90
|
+
unmeasurable), so vetoing on `Measurable` would turn a false pass into a false block. Widening this
|
|
91
|
+
list needs the same kind of worked case as the ones above.
|
|
83
92
|
|
|
84
93
|
## Quality Gate Rules
|
|
85
94
|
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Assert lifecycle contract content, not a template hash: a sha256 pin detects any change but
|
|
4
|
+
// cannot distinguish a defect from an intentional edit that preserves every required gate.
|
|
5
|
+
|
|
6
|
+
const { describe, test } = require('node:test');
|
|
7
|
+
const assert = require('node:assert/strict');
|
|
8
|
+
const crypto = require('node:crypto');
|
|
9
|
+
const fs = require('node:fs');
|
|
10
|
+
const os = require('node:os');
|
|
11
|
+
const path = require('node:path');
|
|
12
|
+
const { spawnSync } = require('node:child_process');
|
|
13
|
+
|
|
14
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..', '..');
|
|
15
|
+
const FIXTURE = path.join(PACKAGE_ROOT, 'tests', 'fixtures', 'feature-contour');
|
|
16
|
+
const CHECKER = path.join(PACKAGE_ROOT, 'scripts', 'check-pipeline-gaps.sh');
|
|
17
|
+
const REVIEW_HOOK = path.join(
|
|
18
|
+
PACKAGE_ROOT, 'templates', '.claude', 'hooks', 'check-review-contract.cjs',
|
|
19
|
+
);
|
|
20
|
+
const FEATURE_TEMPLATE = path.join(
|
|
21
|
+
PACKAGE_ROOT, 'templates', '.claude', 'commands', 'feature.md',
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
// The fixture ships only the consumer's OWN files (docs/, tests/). The two shipped templates the
|
|
25
|
+
// checker reads for its role maps are materialised from templates/ at run time, byte-identical:
|
|
26
|
+
// a stub copy inside the fixture would be a third copy of a canonical skill and trip no-skill-drift.
|
|
27
|
+
const TEMPLATE_FILES = [
|
|
28
|
+
['commands', 'feature.md'],
|
|
29
|
+
['skills', 'sparc-prd-mini', 'SKILL.md'],
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
function project(t) {
|
|
33
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'feature-contour-e2e-'));
|
|
34
|
+
fs.cpSync(FIXTURE, root, { recursive: true });
|
|
35
|
+
for (const parts of TEMPLATE_FILES) {
|
|
36
|
+
const target = path.join(root, '.claude', ...parts);
|
|
37
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
38
|
+
fs.copyFileSync(path.join(PACKAGE_ROOT, 'templates', '.claude', ...parts), target);
|
|
39
|
+
}
|
|
40
|
+
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
|
|
41
|
+
return root;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function execute(command, args, cwd) {
|
|
45
|
+
const result = spawnSync(command, args, { cwd, encoding: 'utf8', timeout: 10000 });
|
|
46
|
+
return {
|
|
47
|
+
status: result.status,
|
|
48
|
+
signal: result.signal,
|
|
49
|
+
output: `${result.stdout || ''}${result.stderr || ''}`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function checker(root, flags = [
|
|
54
|
+
'--traceability', '--completion', '--report-revision', '--criterion-scenarios',
|
|
55
|
+
]) {
|
|
56
|
+
return execute('bash', [CHECKER, root, ...flags], root);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function hook(root) {
|
|
60
|
+
return execute(process.execPath, [REVIEW_HOOK, root, 'demo-gate'], root);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function replace(root, relative, transform) {
|
|
64
|
+
const target = path.join(root, relative);
|
|
65
|
+
fs.writeFileSync(target, transform(fs.readFileSync(target, 'utf8')));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function phase(text, number, nextNumber) {
|
|
69
|
+
const start = text.indexOf(`### Phase ${number}:`);
|
|
70
|
+
assert.notEqual(start, -1, `Phase ${number} heading missing`);
|
|
71
|
+
const end = nextNumber === undefined ? text.length : text.indexOf(`### Phase ${nextNumber}:`, start);
|
|
72
|
+
assert.notEqual(end, -1, `Phase ${nextNumber} heading missing`);
|
|
73
|
+
return text.slice(start, end);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
describe('feature contour consumer contract', () => {
|
|
77
|
+
test('green fixture binds specification, validation, completion, review, and executable evidence', (t) => {
|
|
78
|
+
const root = project(t);
|
|
79
|
+
const specification = fs.readFileSync(path.join(
|
|
80
|
+
root, 'docs', 'features', 'demo-gate', '01_specification.md',
|
|
81
|
+
));
|
|
82
|
+
const validation = fs.readFileSync(path.join(
|
|
83
|
+
root, 'docs', 'features', 'demo-gate', 'validation-report.md',
|
|
84
|
+
), 'utf8');
|
|
85
|
+
const expectedDigest = crypto.createHash('sha256').update(specification).digest('hex');
|
|
86
|
+
assert.match(validation, new RegExp(`^Spec revision: sha256:${expectedDigest}$`, 'm'),
|
|
87
|
+
'the checked-in fixture revision must match the specification bytes');
|
|
88
|
+
|
|
89
|
+
const gate = checker(root);
|
|
90
|
+
assert.equal(gate.status, 0, gate.output);
|
|
91
|
+
assert.match(gate.output, /VERDICT traceability=PASS/);
|
|
92
|
+
assert.match(gate.output, /VERDICT completion=PASS/);
|
|
93
|
+
assert.match(gate.output, /VERDICT report-revision=PASS/);
|
|
94
|
+
assert.match(gate.output, /VERDICT criterion-scenarios=PASS/);
|
|
95
|
+
|
|
96
|
+
const review = hook(root);
|
|
97
|
+
assert.equal(review.status, 0, review.output);
|
|
98
|
+
const fixtureTests = execute(process.execPath, ['--test', 'tests/demo.test.js'], root);
|
|
99
|
+
assert.equal(fixtureTests.status, 0, fixtureTests.output);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('deleting AC-demo-gate-2 coverage is a named completion gap', (t) => {
|
|
103
|
+
const root = project(t);
|
|
104
|
+
replace(root, 'docs/features/demo-gate/05_completion.md', (body) =>
|
|
105
|
+
body.replace(/^\| AC-demo-gate-2 .*\n/m, ''));
|
|
106
|
+
const result = checker(root, ['--completion']);
|
|
107
|
+
assert.equal(result.status, 1, result.output);
|
|
108
|
+
assert.match(result.output,
|
|
109
|
+
/GAP contour=demo-gate completion AC-demo-gate-2 has no row in Criterion coverage/);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('changing specification bytes invalidates both report revision contracts', (t) => {
|
|
113
|
+
const root = project(t);
|
|
114
|
+
fs.appendFileSync(path.join(
|
|
115
|
+
root, 'docs', 'features', 'demo-gate', '01_specification.md',
|
|
116
|
+
), '\n');
|
|
117
|
+
const revision = checker(root, ['--report-revision']);
|
|
118
|
+
assert.equal(revision.status, 1, revision.output);
|
|
119
|
+
assert.match(revision.output,
|
|
120
|
+
/GAP contour=demo-gate report-revision validation-report\.md sha256:.* != specification sha256:/);
|
|
121
|
+
|
|
122
|
+
const review = hook(root);
|
|
123
|
+
assert.equal(review.status, 1, review.output);
|
|
124
|
+
assert.match(review.output, /GAP Spec revision mismatch report=.* specification=/);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('removing Reviewer family is a named review-contract gap', (t) => {
|
|
128
|
+
const root = project(t);
|
|
129
|
+
replace(root, 'docs/features/demo-gate/review-report.md', (body) =>
|
|
130
|
+
body.replace(/^Reviewer family:.*\n/m, ''));
|
|
131
|
+
const review = hook(root);
|
|
132
|
+
assert.equal(review.status, 1, review.output);
|
|
133
|
+
assert.match(review.output, /GAP Reviewer family line missing/);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('changing a test title breaks completion evidence by name', (t) => {
|
|
137
|
+
const root = project(t);
|
|
138
|
+
replace(root, 'tests/demo.test.js', (body) =>
|
|
139
|
+
body.replace('rejects an invalid value', 'declines an invalid value'));
|
|
140
|
+
const result = checker(root, ['--completion']);
|
|
141
|
+
assert.equal(result.status, 1, result.output);
|
|
142
|
+
assert.match(result.output,
|
|
143
|
+
/AC-demo-gate-2 test file tests\/demo\.test\.js does not contain title "rejects an invalid value"/);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('deleting AC-demo-gate-1 scenario is a named traceability-floor gap', (t) => {
|
|
147
|
+
const root = project(t);
|
|
148
|
+
replace(root, 'docs/features/demo-gate/validation-report.md', (body) =>
|
|
149
|
+
body.replace(/^\| AC-demo-gate-1 .*\n/m, ''));
|
|
150
|
+
const result = checker(root, ['--criterion-scenarios']);
|
|
151
|
+
assert.equal(result.status, 1, result.output);
|
|
152
|
+
assert.match(result.output,
|
|
153
|
+
/GAP contour=demo-gate criterion-scenarios AC-demo-gate-1 has no scenario row/);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test('feature template carries each blocking gate in its owning phase and AUTO mode', () => {
|
|
157
|
+
const template = fs.readFileSync(FEATURE_TEMPLATE, 'utf8');
|
|
158
|
+
const phase2 = phase(template, 2, 3);
|
|
159
|
+
const phase3 = phase(template, 3, 4);
|
|
160
|
+
const phase4 = phase(template, 4);
|
|
161
|
+
assert.match(phase2, /--report-revision/);
|
|
162
|
+
assert.match(phase2, /--criterion-scenarios/);
|
|
163
|
+
assert.match(phase3, /--completion/);
|
|
164
|
+
assert.match(phase3, /## Criterion coverage/);
|
|
165
|
+
assert.match(phase4, /check-review-contract\.cjs/);
|
|
166
|
+
|
|
167
|
+
const autoStart = template.indexOf('## AUTO mode');
|
|
168
|
+
const autoEnd = template.indexOf('\n## Related', autoStart);
|
|
169
|
+
assert.notEqual(autoStart, -1, 'AUTO mode heading missing');
|
|
170
|
+
assert.notEqual(autoEnd, -1, 'AUTO mode boundary missing');
|
|
171
|
+
const auto = template.slice(autoStart, autoEnd);
|
|
172
|
+
assert.match(auto, /--report-revision/);
|
|
173
|
+
assert.match(auto, /--criterion-scenarios/);
|
|
174
|
+
assert.match(auto, /--completion/);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Specification — Demo gate
|
|
2
|
+
|
|
3
|
+
## Requirements
|
|
4
|
+
|
|
5
|
+
### FR-demo-gate-1 — Validate a submitted value
|
|
6
|
+
|
|
7
|
+
The demo validator must distinguish accepted values from rejected values.
|
|
8
|
+
|
|
9
|
+
### AC-demo-gate-1 — Accept a valid value
|
|
10
|
+
|
|
11
|
+
Given a valid value, when the validator runs, then it reports acceptance.
|
|
12
|
+
|
|
13
|
+
### AC-demo-gate-2 — Reject an invalid value
|
|
14
|
+
|
|
15
|
+
Given an invalid value, when the validator runs, then it reports rejection.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Pseudocode — Demo gate
|
|
2
|
+
|
|
3
|
+
## Validation algorithm
|
|
4
|
+
|
|
5
|
+
### Algorithm: Validate submitted value
|
|
6
|
+
|
|
7
|
+
REQUIREMENT: `FR-demo-gate-1`
|
|
8
|
+
REQUIREMENT: `AC-demo-gate-1`
|
|
9
|
+
REQUIREMENT: `AC-demo-gate-2`
|
|
10
|
+
|
|
11
|
+
INPUT: submitted value
|
|
12
|
+
|
|
13
|
+
OUTPUT: acceptance decision
|
|
14
|
+
|
|
15
|
+
STEPS:
|
|
16
|
+
|
|
17
|
+
1. Compare the submitted value with the accepted fixture value.
|
|
18
|
+
2. Return acceptance for a match.
|
|
19
|
+
3. Return rejection otherwise.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Review report — Demo gate
|
|
2
|
+
Reviewer family: claude
|
|
3
|
+
Spec revision: sha256:264708a769ad0d07d715420d82278461c5f1b831dd365eaaf6be051db18d47e8
|
|
4
|
+
|
|
5
|
+
The fixture implementation and its executable tests conform to both declared criteria.
|
|
6
|
+
|
|
7
|
+
## Spec conformance
|
|
8
|
+
| Criterion | Verdict | Evidence |
|
|
9
|
+
|-----------|---------|----------|
|
|
10
|
+
| AC-demo-gate-1 | met | tests/demo.test.js — accepts a valid value |
|
|
11
|
+
| AC-demo-gate-2 | met | tests/demo.test.js — rejects an invalid value |
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Validation report — Demo gate
|
|
2
|
+
Spec revision: sha256:264708a769ad0d07d715420d82278461c5f1b831dd365eaaf6be051db18d47e8
|
|
3
|
+
|
|
4
|
+
The specification is ready for implementation: both acceptance criteria have concrete scenarios.
|
|
5
|
+
|
|
6
|
+
## Criterion scenarios
|
|
7
|
+
| Criterion | Scenario |
|
|
8
|
+
|-----------|----------|
|
|
9
|
+
| AC-demo-gate-1 | Valid input is accepted |
|
|
10
|
+
| AC-demo-gate-2 | Invalid input is rejected |
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { test } = require('node:test');
|
|
4
|
+
const assert = require('node:assert/strict');
|
|
5
|
+
|
|
6
|
+
function accepts(value) {
|
|
7
|
+
return value === 'valid';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
test('accepts a valid value', () => {
|
|
11
|
+
assert.equal(accepts('valid'), true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('rejects an invalid value', () => {
|
|
15
|
+
assert.equal(accepts('invalid'), false);
|
|
16
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generatedAt": "2026-09-
|
|
3
|
-
"totalFiles":
|
|
2
|
+
"generatedAt": "2026-09-03T05:28:07.394Z",
|
|
3
|
+
"totalFiles": 167,
|
|
4
4
|
"files": {
|
|
5
5
|
".claude/agents/doc-validator.md": "e57950758cd4764b2cb54b3f1905567e9dbc1ed20ac6ecd30998614894689f4f",
|
|
6
6
|
".claude/agents/harvest-coordinator.md": "71e524e8379df46bbf457dbc1d4480634e05c99b7d3a4f59a356b8d71a0a4166",
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
".claude/agents/replicate-coordinator.md": "bbfe18a7642954f357db08e849349fee02681e2f0ffa036539c45d98dbaba960",
|
|
9
9
|
".claude/commands/deploy.md": "27f5424ee217163c55e5e6b9322a1f0858159407bd24ebd6d6588d6c22a5f890",
|
|
10
10
|
".claude/commands/docs.md": "be03592cb3f1d61b60dc96d3e26d71281ad4eb2d4626012c080ce609a32c96e4",
|
|
11
|
-
".claude/commands/feature.md": "
|
|
11
|
+
".claude/commands/feature.md": "6908fa6c8ba770f829378a021d5f7e9a9bc79a15bdd67b278d8c6a765968b291",
|
|
12
12
|
".claude/commands/go.md": "e7337012dccaab09e0cf27a696ebff14bea4626fb5167538af7da2f5cc7b597a",
|
|
13
13
|
".claude/commands/harvest.md": "ed6355c102af0f2c62bf6714fce63157e0cb1c1ee85bc32c96f137b8b0c0e1ba",
|
|
14
14
|
".claude/commands/myinsights.md": "f31c5e133b5027197359e4179cbba1ab535f990634a69ea7c5c73bd2bc329d68",
|
|
15
15
|
".claude/commands/next.md": "1e4984306c3aa80dfc8734dc1c975dfe5fdf2cf6396ddd759e4c7635646107b8",
|
|
16
16
|
".claude/commands/plan.md": "6fef889f51168981bf4a98b8392a0d595e3d199e1cfaa8e08c0a181ec6c440e4",
|
|
17
|
-
".claude/commands/replicate.md": "
|
|
17
|
+
".claude/commands/replicate.md": "f176d8ec871459b5a8d2111b8527331f34340d6e169da8cc7792953e3e334774",
|
|
18
18
|
".claude/commands/run.md": "87144dfe8c56c15ba7c117daa544d8a08b84d7205f21dafcc48d27c6c3995059",
|
|
19
19
|
".claude/commands/start.md": "fb8a7762f4073b9e8689dec3830680907efb9ec68bb2faa39b8a547793d9a078",
|
|
20
20
|
".claude/hooks/autocommit-insights.cjs": "5e7cef87b24d457f25315d79fe15f0dfaf94d9269495d638817f0faee2593c6f",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
".claude/hooks/autocommit-roadmap.cjs": "f4dcc9ffd9a3aed91dd56e011e699fcf8af903cc81594e4ab1ef61e5432a36ad",
|
|
23
23
|
".claude/hooks/capture-source-path.cjs": "bed92548c3313d69110b6cc06e6b2b858d9d548b8c18c82750ae90d514212bf2",
|
|
24
24
|
".claude/hooks/check-canon.cjs": "99e6bff9b0078eb2ec23f7df98b7ac06c894f9018eba4a8c358e8f92e5ec7b06",
|
|
25
|
-
".claude/hooks/check-
|
|
25
|
+
".claude/hooks/check-dangling-refs.cjs": "8f012876b1af110a1093319d36a2f84208a15780dbb8abb5a43eb4aeb8f76865",
|
|
26
|
+
".claude/hooks/check-docs-complete.cjs": "0f516df0882c9da53d166a1b4e30ff1fc37bd5a2027c7e5fbdab6cba116ca828",
|
|
26
27
|
".claude/hooks/check-embed-contract.cjs": "9e963a07c12350b760264133021e75fb699439b1f060332e1ccdd962b645b4a0",
|
|
27
28
|
".claude/hooks/check-external-deps.cjs": "7bce49a744170a3193edf4b2238bcc5a6f1b0efa29333760a52e4fd145739d18",
|
|
28
29
|
".claude/hooks/check-file-ownership.cjs": "c6273e7b0817d15e375586131924a08095722aff7d0e6a6a04c857a480788e1f",
|
|
@@ -34,30 +35,31 @@
|
|
|
34
35
|
".claude/hooks/check-metric-source.cjs": "315deda6354f91737165786bdb22b799517bd80530e5b4584593c4f4df84c56e",
|
|
35
36
|
".claude/hooks/check-model-cost.cjs": "425bda547661e0b31f313d6bb371db4d303cc83a43189fb4dc981e1fb43fac06",
|
|
36
37
|
".claude/hooks/check-ports.cjs": "2b458cf78367ec7752174f08aa40109b3b6217e405f99a46e18e75e4b69bc87e",
|
|
38
|
+
".claude/hooks/check-review-contract.cjs": "44e32ea31a4a71ceb6eccb0956006519de270eab787a79ab4884978bc04385ab",
|
|
37
39
|
".claude/hooks/check-source-version.cjs": "b9dc2eee7403119481ea2c49a94581613f2c134685448736c2734948a638a010",
|
|
38
40
|
".claude/hooks/check-swarm-receipts.cjs": "9fa83d78b085d8eea28958b4722999ba44a7bd47c98fe5a96127fc5b5230b40c",
|
|
39
41
|
".claude/hooks/check-webhook-contract.cjs": "dfa27bd24cee8868fd60be5d24749c206f303fe4afcc1e72024d526324562a39",
|
|
40
42
|
".claude/hooks/session-insights.cjs": "31fbe90d81b01dfb56557ed5d68dbde2211a14d5469c5d3761f45fb63c07c3a7",
|
|
41
43
|
".claude/hooks/state-update.cjs": "47146a76b768ac2273abe8f2f017856a0e28b45afaf90048148da417dd19604e",
|
|
42
|
-
".claude/hooks/statusline.cjs": "
|
|
44
|
+
".claude/hooks/statusline.cjs": "bac385da01dd9f1548d0db48a2acdada5e89a7822a7f0ecfb61cb74ff4cda138",
|
|
43
45
|
".claude/hooks/write-insight.cjs": "743f2d85b037cf522dfaf4775947b0cd331e52f2f304e88360c160e0e8e9a589",
|
|
44
|
-
".claude/rules/cost-of-detection-ladder.md": "
|
|
45
|
-
".claude/rules/docker-ports.md": "
|
|
46
|
+
".claude/rules/cost-of-detection-ladder.md": "d2e7da3d56c8d4c6d1aa58e793fa0d5eecd65a5c01be38dfa0dc434273306838",
|
|
47
|
+
".claude/rules/docker-ports.md": "a9e5bb9a797ea4ef4c0671cee2cbec95f2557f8b9e1d20b84bbcee7ce4c21162",
|
|
46
48
|
".claude/rules/embeddable-widget.md": "0159224f8e9599b67a860738bb42e3e40adf598176c48ea6c86a01c891f0f18c",
|
|
47
|
-
".claude/rules/feature-lifecycle.md": "
|
|
49
|
+
".claude/rules/feature-lifecycle.md": "a7d0adf6bc63b942d3dec339b3ae68972c3dedc0b48a91d27653309c7da7f11f",
|
|
48
50
|
".claude/rules/git-workflow.md": "7d8d2ded47fcc0fb95da3dc226efa6cb3c80d6adf7296977588c181dfce20192",
|
|
49
51
|
".claude/rules/honest-configuration.md": "851bc28e9e2baf1043bc09b982f3df3431bc7af1e076f8ff9df3d6729835bad5",
|
|
50
52
|
".claude/rules/incoming-webhooks.md": "92f3de02814ed0610df644acf554f4b41b2bae2f9d281695e2e217a1d6558e84",
|
|
51
53
|
".claude/rules/insights-capture.md": "d529e6be79178636ca362d83366b1dd7a843371e8ba4f2b09d65c009fab52dc4",
|
|
52
54
|
".claude/rules/long-running-job.md": "e6bd0efff89338998d34440a94dc9288cf7bcf790b27bcaa51b83d43f57942ed",
|
|
53
55
|
".claude/rules/model-call-cost.md": "26c3b20ac3ed7f9cf6deb5e94302ade87f9e3e51fffca7ac2377ff31bc144f34",
|
|
54
|
-
".claude/rules/replicate-pipeline.md": "
|
|
56
|
+
".claude/rules/replicate-pipeline.md": "9c4bbaa9ac628ec5d4e44d3ace6eec0b5bd602b5dccc5d36f123d73be448f159",
|
|
55
57
|
".claude/rules/skill-interface-protocol.md": "c3f624d0a8a4c7e0f5744e529eb4486cc0589fdb732b9799bccc75091a6d63ca",
|
|
56
58
|
".claude/rules/swarm-file-evidence.md": "c71a216dbc3fd633c30949823ceac354f8439b96279a1a8dd606afb1b3ef5c45",
|
|
57
59
|
".claude/settings.json": "18db09827dce410667b93e2b1a41ef8e46d4431114733e4304a5fc2192b2fc66",
|
|
58
60
|
".claude/skills/brutal-honesty-review/evals/brutal-honesty-review.yaml": "855c155d55575e12aab23fb3a5fb2fada74d93974ff755dfedac6f2d5bd41f13",
|
|
59
61
|
".claude/skills/brutal-honesty-review/README.md": "2a026b4af50b21456e50424fbfa089cb983fbbaa0c68024c8c17c695bea25a57",
|
|
60
|
-
".claude/skills/brutal-honesty-review/resources/assessment-rubrics.md": "
|
|
62
|
+
".claude/skills/brutal-honesty-review/resources/assessment-rubrics.md": "b64894e1ddf8b9da7f2f146c737d35215e7281e3d8be10891d4a9b8aa2bd6d07",
|
|
61
63
|
".claude/skills/brutal-honesty-review/resources/review-template.md": "95182c8605b1d821360257e1c5dbac4300bda333d861785de417cbb84090b25b",
|
|
62
64
|
".claude/skills/brutal-honesty-review/schemas/output.json": "2e252a3497b86322e1a6e6bd1aca8708b44389db3a2d81a6f893d467b6351210",
|
|
63
65
|
".claude/skills/brutal-honesty-review/scripts/assess-code.sh": "2b10d2972b1379adde9a68ddf8bcdb399b7d65075e458a54523ffd8d99ddc746",
|
|
@@ -78,7 +80,7 @@
|
|
|
78
80
|
".claude/skills/cc-toolkit-generator-enhanced/references/enhanced-recommendations.md": "657e35db0cb77d3a9d1a4d01215a9b8d50441dd863593bee3a84f62aad8b00bc",
|
|
79
81
|
".claude/skills/cc-toolkit-generator-enhanced/references/extended-mapping.md": "5e87c91956eabc70b4a1084b47b2c9a20ede18bb245735812ead956eafabf3af",
|
|
80
82
|
".claude/skills/cc-toolkit-generator-enhanced/references/maturity-integration.md": "aa4e743bcd0281525336a298d0489c31481f69ff1a534e1ed1d262d888937deb",
|
|
81
|
-
".claude/skills/cc-toolkit-generator-enhanced/references/security-patterns-library.md": "
|
|
83
|
+
".claude/skills/cc-toolkit-generator-enhanced/references/security-patterns-library.md": "4a7fae9ea1870c30b71c923fd5d3626a8926c5e960209fad709f4b8e0534fc80",
|
|
82
84
|
".claude/skills/cc-toolkit-generator-enhanced/references/templates/automation-commands.md": "9ce3db4c3c5b9696a312152067c1869b1f421ce77fbcfdca83edaa797c5caf3f",
|
|
83
85
|
".claude/skills/cc-toolkit-generator-enhanced/references/templates/ddd-agents.md": "c91f81da197a072b57378fd0018eea26c35d909a558bdca6f070eb60153c97b3",
|
|
84
86
|
".claude/skills/cc-toolkit-generator-enhanced/references/templates/ddd-hooks-commands.md": "8398f2493434db101476300e514a34bf8124a51eb732cf81923f9a5b5a3d5db4",
|
|
@@ -145,10 +147,11 @@
|
|
|
145
147
|
".claude/skills/problem-solver-enhanced/SKILL.md": "031378aa2e10c98928e35de07d454135e73550cd0c49f0cd886d53e4208d6b9f",
|
|
146
148
|
".claude/skills/requirements-validator/assets/templates/bdd-feature-template.feature": "5b1e47f10447ce574d0eb3917b69e2613b6e3f9c48b30ff5f4cfb57758c99a1d",
|
|
147
149
|
".claude/skills/requirements-validator/references/bdd-patterns.md": "04611a8c52c81f3a6d4fc2f9661ebc56159a125b6fb44a4d18b432e2b82de6e3",
|
|
150
|
+
".claude/skills/requirements-validator/references/feature-report-contracts.md": "df263cbce17fb5d9dec6514561c66f6283b3f19e2fc85056f0531bac615be0f1",
|
|
148
151
|
".claude/skills/requirements-validator/references/invest-criteria.md": "ac6976adf776b9b60c784e1a0f6a0d96dd3332b16f4c12b72d3e59f9325bb626",
|
|
149
|
-
".claude/skills/requirements-validator/references/scoring-system.md": "
|
|
152
|
+
".claude/skills/requirements-validator/references/scoring-system.md": "72104b74f07e0610f67f00b246b5225a530203bff225bacf7bf384bc6fd21cdf",
|
|
150
153
|
".claude/skills/requirements-validator/references/smart-criteria.md": "619a33fdb6bc336872fdf9f1fc91924f8ccd3c2ebef023912124756692b45aef",
|
|
151
|
-
".claude/skills/requirements-validator/SKILL.md": "
|
|
154
|
+
".claude/skills/requirements-validator/SKILL.md": "b4b394adf08feee552f39c83331398f119e0d4abf21e683fe4706da438b4a426",
|
|
152
155
|
".claude/skills/reverse-engineering-unicorn/examples/noom-cjm-example.md": "01d33586e26bf093a57a7a1082bf3bdba3af801bb8348a685c0418f76987a0b0",
|
|
153
156
|
".claude/skills/reverse-engineering-unicorn/examples/noom-module1-example.md": "7009cf2e2624a1254913ad2e1ff554e7ff675358b0b99bc0632f2e12132663a3",
|
|
154
157
|
".claude/skills/reverse-engineering-unicorn/modules/01-intelligence.md": "0770bd63a6a1eeb34e873cb101e2ba0cf88d507e9bc03242046eaa837b9f587c",
|