harness-everything 0.10.0 → 0.11.1
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +21 -0
- package/ci/contract-integrity.test.js +203 -0
- package/ci/mechanism-2ah-workflow-lifecycle.test.js +34 -1
- package/contract-integrity/fixtures/consistent-trace.json +94 -0
- package/contract-integrity/fixtures/reconciled-trace.json +105 -0
- package/contract-integrity/fixtures/ticket-drift-trace.json +94 -0
- package/contract-integrity/schemas/trace.schema.json +77 -0
- package/contract-integrity/scripts/audit.js +536 -0
- package/docs/contract-integrity.md +109 -0
- package/hooks/scripts/lib/workflow-runtime.js +2 -1
- package/hooks/scripts/workflow-disposition.js +7 -2
- package/hooks/scripts/workflow-gate.js +4 -3
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +4 -2
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/scripts/lib/workflow-runtime.js +2 -1
- package/plugins/harness-everything/hooks/scripts/workflow-disposition.js +7 -2
- package/plugins/harness-everything/hooks/scripts/workflow-gate.js +4 -3
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/self-evolve/SKILL.md +1 -1
- package/plugins/harness-everything/skills/self-evolve/scripts/self-regression.js +2 -1
- package/self-evolve/SKILL.md +1 -1
- package/self-evolve/scripts/self-regression.js +2 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "harness-everything",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.11.1",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Miya Daniel"
|
|
16
16
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-everything",
|
|
3
3
|
"description": "Harness OS: an orchestrated agent operating system — tier routing, circuit breaker guardrails, verification gates, WAL session memory, and self-evolving workspace rules.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.11.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Miya Daniel"
|
|
7
7
|
},
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.11.1](https://github.com/dyphn1/Harness-everything/compare/v0.11.0...v0.11.1) (2026-09-18)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
* **workflow:** admit all trusted controller commands ([65ec5f3](https://github.com/dyphn1/Harness-everything/commit/65ec5f31e9d218d7c32e1d9ed82992814be79b9b))
|
|
10
|
+
* **workflow:** share controller command contract ([b9ba2dc](https://github.com/dyphn1/Harness-everything/commit/b9ba2dc59a5c2dce10533aad79b9a5303b921506))
|
|
11
|
+
* **workflow:** validate shared controller command set ([8d3247d](https://github.com/dyphn1/Harness-everything/commit/8d3247d51d8aa0f1cfa61328c1061cb239dfba6a))
|
|
12
|
+
|
|
13
|
+
## [0.11.0](https://github.com/dyphn1/Harness-everything/compare/v0.10.0...v0.11.0) (2026-09-18)
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
* **contract-integrity:** add trace reconciliation audit ([bb99f7a](https://github.com/dyphn1/Harness-everything/commit/bb99f7a8c29e14bd718a896ef30c7b820dcb2d7c))
|
|
18
|
+
* **contract-integrity:** bind evidence to current living spec ([77f203a](https://github.com/dyphn1/Harness-everything/commit/77f203a1f59d968d1a4075e3441e2113adeb0942))
|
|
19
|
+
* **contract-integrity:** define trace and probe schema ([d688f8a](https://github.com/dyphn1/Harness-everything/commit/d688f8a7dba66e3e7faa8e33fd3d659b09982143))
|
|
20
|
+
* **contract-integrity:** expose audit and regression scripts ([fa33f11](https://github.com/dyphn1/Harness-everything/commit/fa33f118b4a1995ce02feb69e76b0145d6b7e5d2))
|
|
21
|
+
|
|
22
|
+
### Bug Fixes
|
|
23
|
+
|
|
24
|
+
* **contract-integrity:** align runtime validation with schema ([b4d702a](https://github.com/dyphn1/Harness-everything/commit/b4d702a248f7f088e4ce97dee21b92250390a451))
|
|
25
|
+
|
|
5
26
|
## [0.10.0](https://github.com/dyphn1/Harness-everything/compare/v0.9.0...v0.10.0) (2026-09-18)
|
|
6
27
|
|
|
7
28
|
### Features
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const os = require('os');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { spawnSync } = require('child_process');
|
|
8
|
+
|
|
9
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
10
|
+
const {
|
|
11
|
+
DRIFT,
|
|
12
|
+
evaluateTrace,
|
|
13
|
+
markdown,
|
|
14
|
+
validateTrace,
|
|
15
|
+
} = require(path.join(ROOT, 'contract-integrity/scripts/audit.js'));
|
|
16
|
+
const { evaluateEvidence } = require(path.join(ROOT, 'tdd/scripts/quality-gate.js'));
|
|
17
|
+
|
|
18
|
+
let failed = 0;
|
|
19
|
+
function check(condition, message, detail = '') {
|
|
20
|
+
if (condition) console.log(` PASS ${message}`);
|
|
21
|
+
else {
|
|
22
|
+
console.error(` FAIL ${message}${detail ? ` — ${detail}` : ''}`);
|
|
23
|
+
failed++;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function clone(value) {
|
|
27
|
+
return JSON.parse(JSON.stringify(value));
|
|
28
|
+
}
|
|
29
|
+
function fixture(name) {
|
|
30
|
+
return JSON.parse(fs.readFileSync(path.join(ROOT, 'contract-integrity', 'fixtures', name), 'utf8'));
|
|
31
|
+
}
|
|
32
|
+
function tddEvidence(specPath = 'docs/specs/api.md', section = 'REQ-001') {
|
|
33
|
+
const value = JSON.parse(fs.readFileSync(path.join(ROOT, 'tdd', 'fixtures', 'pass-evidence.json'), 'utf8'));
|
|
34
|
+
value.requirements[0].source.path = specPath;
|
|
35
|
+
value.requirements[0].source.section = section;
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
function req(report, id = 'REQ-001') {
|
|
39
|
+
return report.requirements.find(item => item.requirementId === id);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
console.log('=== Contract Integrity Phase 1 (#84) ===');
|
|
43
|
+
|
|
44
|
+
const baseline = fixture('consistent-trace.json');
|
|
45
|
+
const baselineTdd = tddEvidence();
|
|
46
|
+
const legacyQuality = evaluateEvidence(baselineTdd);
|
|
47
|
+
check(legacyQuality.result === 'PASS' && legacyQuality.score === 100, '#58 completeness evaluator remains independently backward compatible');
|
|
48
|
+
|
|
49
|
+
const baselineReport = evaluateTrace(baseline, { tddEvidence: baselineTdd });
|
|
50
|
+
check(baselineReport.result === 'PASS' && baselineReport.completionGate === 'PASS', 'fully traced current contract passes strict reconciliation');
|
|
51
|
+
check(baselineReport.scores.completenessScore === 100 && baselineReport.scores.protectionScore === 100 && baselineReport.scores.contractIntegrityScore === 100, 'integrity score is min(100 completeness, 100 protection)');
|
|
52
|
+
check(req(baselineReport).drift.length === 1 && req(baselineReport).drift[0] === 'CONSISTENT', 'consistent requirement has no fabricated drift category');
|
|
53
|
+
check(req(baselineReport).protection.status === 'PROTECTED' && req(baselineReport).protection.required === 1, 'only predeclared required probe evidence determines protection');
|
|
54
|
+
|
|
55
|
+
const drift = fixture('ticket-drift-trace.json');
|
|
56
|
+
const driftReport = evaluateTrace(drift, { tddEvidence: baselineTdd });
|
|
57
|
+
check(driftReport.result === 'FAIL', 'strict mode fails when ticket feedback moves behavior ahead of living contract');
|
|
58
|
+
check(req(driftReport).drift.includes('STALE_SPEC') && req(driftReport).drift.includes('UNTRACED_CHANGE'), 'ticket-ahead fixture reports exact stale-spec and untraced-change categories');
|
|
59
|
+
check(req(driftReport).reasonCodes.some(code => code.startsWith('ticket-ahead-of-contract:')), 'drift report points to the ticket that outran the contract');
|
|
60
|
+
|
|
61
|
+
const reconciled = fixture('reconciled-trace.json');
|
|
62
|
+
const reconciledTdd = tddEvidence('docs/specs/api.md', 'REQ-001-v2');
|
|
63
|
+
const reconciledReport = evaluateTrace(reconciled, { tddEvidence: reconciledTdd });
|
|
64
|
+
check(reconciledReport.result === 'PASS' && req(reconciledReport).drift.includes('CONSISTENT'), 'ADR amendment + living spec/test/implementation revision reconciliation restores PASS');
|
|
65
|
+
check(reconciledReport.graph.links.some(link => link.relation === 'supersedes' && link.from === 'ADR-002' && link.to === 'ADR-001'), 'decision supersession is linked instead of appending ticket chronology to old ADR');
|
|
66
|
+
|
|
67
|
+
const staleQuality = evaluateTrace(reconciled, { tddEvidence: tddEvidence('docs/old-api.md', 'REQ-001') });
|
|
68
|
+
check(req(staleQuality).drift.includes('STALE_TEST') && req(staleQuality).reasonCodes.includes('tdd-source-not-current-spec'), '#58 evidence bound to an old source cannot certify the current living spec');
|
|
69
|
+
check(staleQuality.result === 'FAIL', 'stale #58 source binding blocks strict completion despite a 100 completeness score');
|
|
70
|
+
|
|
71
|
+
const staleProbe = clone(reconciled);
|
|
72
|
+
staleProbe.probes[0].sourceSection = 'docs/old-api.md#REQ-001';
|
|
73
|
+
const staleProbeReport = evaluateTrace(staleProbe, { tddEvidence: reconciledTdd });
|
|
74
|
+
check(req(staleProbeReport).protection.status === 'NOT_EVALUATED', 'probe derived from a stale source section is rejected as protection evidence');
|
|
75
|
+
check(staleProbeReport.scores.protectionScore === 0 && staleProbeReport.scores.contractIntegrityScore === 0, 'stale probe evidence fails closed to zero protection');
|
|
76
|
+
|
|
77
|
+
const padding = clone(baseline);
|
|
78
|
+
padding.probes[0].status = 'SURVIVED';
|
|
79
|
+
padding.probes[0].failureClass = 'none';
|
|
80
|
+
for (let i = 0; i < 10; i++) {
|
|
81
|
+
padding.probes.push({
|
|
82
|
+
probeId: `PAD-${i}`,
|
|
83
|
+
requirementId: 'REQ-001',
|
|
84
|
+
status: 'KILLED',
|
|
85
|
+
sourceSection: 'docs/specs/api.md#REQ-001',
|
|
86
|
+
evidenceRef: `probe:padding-${i}`,
|
|
87
|
+
workspaceIsolation: 'isolated',
|
|
88
|
+
failureClass: 'contract',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const paddedReport = evaluateTrace(padding, { tddEvidence: baselineTdd });
|
|
92
|
+
check(req(paddedReport).protection.status === 'SURVIVED' && paddedReport.scores.protectionScore === 0, 'extra killed probes cannot dilute one surviving predeclared required probe');
|
|
93
|
+
check(paddedReport.result === 'FAIL', 'padding tests/probes cannot turn a weak protection result green');
|
|
94
|
+
|
|
95
|
+
const invalidKill = clone(baseline);
|
|
96
|
+
invalidKill.probes[0].failureClass = 'build';
|
|
97
|
+
const invalidErrors = validateTrace(invalidKill);
|
|
98
|
+
check(invalidErrors.some(error => /KILLED requires failureClass=contract/.test(error)), 'compile/build failure cannot be counted as a killed contract probe');
|
|
99
|
+
const invalidKillReport = evaluateTrace(invalidKill, { tddEvidence: baselineTdd });
|
|
100
|
+
check(invalidKillReport.errors.length > 0 && invalidKillReport.result === 'FAIL', 'invalid probe evidence fails closed rather than lowering thresholds');
|
|
101
|
+
|
|
102
|
+
const notEvaluated = clone(baseline);
|
|
103
|
+
notEvaluated.probes = [];
|
|
104
|
+
const notEvaluatedReport = evaluateTrace(notEvaluated, { tddEvidence: baselineTdd });
|
|
105
|
+
check(req(notEvaluatedReport).protection.status === 'NOT_EVALUATED', 'missing required probe is explicit NOT_EVALUATED');
|
|
106
|
+
check(notEvaluatedReport.completionGate === 'NOT_EVALUATED' && notEvaluatedReport.result === 'FAIL', 'strict mode cannot authorize completion without protection evidence');
|
|
107
|
+
|
|
108
|
+
const auditOnly = clone(baseline);
|
|
109
|
+
auditOnly.mode = 'audit';
|
|
110
|
+
auditOnly.requirements[0].ticketIds = [];
|
|
111
|
+
auditOnly.requirements[0].testIds = [];
|
|
112
|
+
auditOnly.requirements[0].implementationIds = [];
|
|
113
|
+
auditOnly.requirements[0].requiredProbeIds = [];
|
|
114
|
+
auditOnly.probes = [];
|
|
115
|
+
const auditReport = evaluateTrace(auditOnly, { tddEvidence: null });
|
|
116
|
+
check(auditReport.result === 'AUDIT' && auditReport.gateEligible === false, 'audit-only adoption reports legacy debt without claiming completion eligibility');
|
|
117
|
+
check(req(auditReport).drift.includes('UNTRACED_CHANGE') && req(auditReport).drift.includes('STALE_TEST') && req(auditReport).drift.includes('STALE_IMPLEMENTATION') && req(auditReport).drift.includes('NOT_EVALUATED'), 'legacy audit exposes missing ticket/test/implementation/protection links');
|
|
118
|
+
|
|
119
|
+
const sourceDefect = clone(baseline);
|
|
120
|
+
sourceDefect.requirements[0].sourceValidity = 'SOURCE_DEFECT';
|
|
121
|
+
const defectReport = evaluateTrace(sourceDefect, { tddEvidence: baselineTdd });
|
|
122
|
+
check(req(defectReport).drift.includes('SOURCE_DEFECT') && defectReport.result === 'FAIL', 'source defect stays distinct and fail-closed in strict mode');
|
|
123
|
+
|
|
124
|
+
const sourceConflict = clone(baseline);
|
|
125
|
+
sourceConflict.requirements[0].sourceValidity = 'SOURCE_CONFLICT';
|
|
126
|
+
const conflictReport = evaluateTrace(sourceConflict, { tddEvidence: baselineTdd });
|
|
127
|
+
check(req(conflictReport).drift.includes('SOURCE_CONFLICT') && conflictReport.result === 'FAIL', 'source conflict stays distinct and fail-closed');
|
|
128
|
+
|
|
129
|
+
const supersession = clone(baseline);
|
|
130
|
+
supersession.requirements.unshift({
|
|
131
|
+
requirementId: 'REQ-OLD',
|
|
132
|
+
revision: 1,
|
|
133
|
+
status: 'SUPERSEDED',
|
|
134
|
+
specId: 'SPEC-001',
|
|
135
|
+
decisionIds: [],
|
|
136
|
+
ticketIds: [],
|
|
137
|
+
testIds: [],
|
|
138
|
+
implementationIds: [],
|
|
139
|
+
requiredProbeIds: [],
|
|
140
|
+
sourceValidity: 'VALID',
|
|
141
|
+
supersededBy: 'REQ-001',
|
|
142
|
+
});
|
|
143
|
+
const supersessionReport = evaluateTrace(supersession, { tddEvidence: baselineTdd });
|
|
144
|
+
check(req(supersessionReport, 'REQ-OLD').drift.includes('INTENTIONAL_SUPERSESSION'), 'explicit requirement supersession is distinguished from stale/untraced drift');
|
|
145
|
+
check(supersessionReport.result === 'PASS', 'historical superseded requirement does not poison the current contract gate');
|
|
146
|
+
|
|
147
|
+
const reportMd = markdown(driftReport);
|
|
148
|
+
check(/Contract Integrity Report/.test(reportMd) && /STALE_SPEC/.test(reportMd) && /min\(completeness, protection\)/.test(reportMd), 'Markdown report exposes drift and score formula');
|
|
149
|
+
check(Object.keys(driftReport.driftCounts).every(status => DRIFT.has(status)), 'machine report uses only the declared drift vocabulary');
|
|
150
|
+
check(driftReport.graph.nodes.some(node => node.kind === 'REQUIREMENT') && driftReport.graph.links.some(link => link.relation === 'protected-by'), 'machine report publishes a traceable contract graph');
|
|
151
|
+
|
|
152
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-contract-integrity-'));
|
|
153
|
+
try {
|
|
154
|
+
const tracePath = path.join(tmp, 'trace.json');
|
|
155
|
+
const tddPath = path.join(tmp, 'tdd.json');
|
|
156
|
+
const jsonPath = path.join(tmp, 'report.json');
|
|
157
|
+
const mdPath = path.join(tmp, 'report.md');
|
|
158
|
+
fs.writeFileSync(tracePath, JSON.stringify(baseline), 'utf8');
|
|
159
|
+
fs.writeFileSync(tddPath, JSON.stringify(baselineTdd), 'utf8');
|
|
160
|
+
const cli = spawnSync(process.execPath, [
|
|
161
|
+
path.join(ROOT, 'contract-integrity/scripts/audit.js'),
|
|
162
|
+
tracePath,
|
|
163
|
+
'--tdd-evidence', tddPath,
|
|
164
|
+
'--output', jsonPath,
|
|
165
|
+
'--markdown', mdPath,
|
|
166
|
+
], { encoding: 'utf8' });
|
|
167
|
+
check(cli.status === 0 && fs.existsSync(jsonPath) && fs.existsSync(mdPath), 'strict CLI writes both machine-readable JSON and concise Markdown reports');
|
|
168
|
+
const cliReport = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
|
|
169
|
+
check(cliReport.result === 'PASS' && /Contract Integrity Report/.test(fs.readFileSync(mdPath, 'utf8')), 'CLI reports retain deterministic PASS evidence');
|
|
170
|
+
|
|
171
|
+
const auditPath = path.join(tmp, 'audit.json');
|
|
172
|
+
fs.writeFileSync(auditPath, JSON.stringify(auditOnly), 'utf8');
|
|
173
|
+
const auditCli = spawnSync(process.execPath, [path.join(ROOT, 'contract-integrity/scripts/audit.js'), auditPath], { encoding: 'utf8' });
|
|
174
|
+
check(auditCli.status === 0 && /Contract Integrity: AUDIT/.test(auditCli.stdout), 'audit mode exits zero for migration/debt inventory without pretending to pass');
|
|
175
|
+
|
|
176
|
+
const strictMissing = clone(notEvaluated);
|
|
177
|
+
fs.writeFileSync(tracePath, JSON.stringify(strictMissing), 'utf8');
|
|
178
|
+
const strictCli = spawnSync(process.execPath, [
|
|
179
|
+
path.join(ROOT, 'contract-integrity/scripts/audit.js'), tracePath, '--tdd-evidence', tddPath,
|
|
180
|
+
], { encoding: 'utf8' });
|
|
181
|
+
check(strictCli.status === 1 && /Gate: NOT_EVALUATED/.test(strictCli.stdout), 'strict CLI returns failure for valid-but-incomplete protection evidence');
|
|
182
|
+
|
|
183
|
+
fs.writeFileSync(tracePath, '{', 'utf8');
|
|
184
|
+
const malformedCli = spawnSync(process.execPath, [path.join(ROOT, 'contract-integrity/scripts/audit.js'), tracePath], { encoding: 'utf8' });
|
|
185
|
+
check(malformedCli.status === 2, 'malformed trace returns infrastructure/schema exit 2');
|
|
186
|
+
} finally {
|
|
187
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const unknownField = clone(baseline);
|
|
191
|
+
unknownField.artifacts[0].agentScore = 100;
|
|
192
|
+
check(validateTrace(unknownField).some(error => /unknown field: agentScore/.test(error)), 'runtime rejects fields that the JSON schema does not permit');
|
|
193
|
+
|
|
194
|
+
const absolutePath = clone(baseline);
|
|
195
|
+
absolutePath.artifacts[0].path = path.resolve(ROOT, 'docs/adr/0001-api-contract.md');
|
|
196
|
+
check(validateTrace(absolutePath).some(error => /repo-relative/.test(error)), 'trace report rejects machine-specific absolute artifact paths');
|
|
197
|
+
|
|
198
|
+
const schema = JSON.parse(fs.readFileSync(path.join(ROOT, 'contract-integrity/schemas/trace.schema.json'), 'utf8'));
|
|
199
|
+
check(schema.properties?.probes?.items?.properties?.status?.enum?.includes('SURVIVED'), 'versioned trace schema includes probe disposition vocabulary');
|
|
200
|
+
check(schema.properties?.requirements?.items?.properties?.requiredProbeIds?.uniqueItems === true, 'schema prevents duplicate required-probe padding');
|
|
201
|
+
|
|
202
|
+
console.log(`\n${failed === 0 ? 'PASS' : 'FAIL'}: #84 contract integrity phase 1 (${failed} failure${failed === 1 ? '' : 's'})`);
|
|
203
|
+
process.exit(failed === 0 ? 0 : 1);
|
|
@@ -113,6 +113,24 @@ try {
|
|
|
113
113
|
check(gate('Write', { file_path: path.join(linked, 'src.js') }).status === 2, 'blocked state still prohibits mutation');
|
|
114
114
|
for (let i = 0; i < 3; i++) { check(control('start').status === 0, 'bounded replan attempt ' + i); control('block', '--evidence', 'fixture blocker'); }
|
|
115
115
|
check(control('start').status === 2 && read(file).state === 'blocked', 'exhausted replan budget remains blocked');
|
|
116
|
+
check(read(file).budget?.state === 'budget-exhausted' && read(file).budget?.reasonCode === 'replan-budget-exhausted', 'Fable replan exhaustion is auditable before recovery');
|
|
117
|
+
const dispositionPath = path.join(runtimeRoot, 'hooks/scripts/workflow-disposition.js');
|
|
118
|
+
const resetCommand = `node "${dispositionPath}" reset-budget --session-id ${sessionId} --evidence fixture-fable-budget-reset`;
|
|
119
|
+
check(gate('Bash', { command: resetCommand }, repo).status === 0, 'blocked Fable workflow admits trusted reset-budget controller through gate');
|
|
120
|
+
check(gate('Bash', { command: resetCommand + ' && echo bypass' }, repo).status === 2, 'controller shell chaining is not exempted');
|
|
121
|
+
check(gate('Bash', { command: resetCommand + ' | echo bypass' }, repo).status === 2, 'controller pipe composition is not exempted');
|
|
122
|
+
check(gate('Bash', { command: `node "${dispositionPath}" reset-budget --session-id ${sessionId} --evidence $(echo bypass)` }, repo).status === 2, 'controller command substitution is not exempted');
|
|
123
|
+
check(gate('Bash', { command: `node "${path.join(runtimeRoot, 'hooks/scripts/not-workflow-disposition.js')}" reset-budget --session-id ${sessionId} --evidence wrong-path` }, repo).status === 2, 'wrong controller script path is not exempted');
|
|
124
|
+
check(control('reset-budget', '--evidence', 'fixture-fable-budget-reset').status === 0, 'reset-budget executes after gate admission');
|
|
125
|
+
const fableReset = read(file);
|
|
126
|
+
check(fableReset.state === 'pending' && fableReset.budget?.state === 'active' && fableReset.budget?.epoch === 1, 'budget reset returns Fable workflow to pending with audited epoch');
|
|
127
|
+
check(gate('Write', { file_path: path.join(repo, 'still-primary.js') }, repo).status === 2, 'budget reset does not weaken primary-tree worktree isolation');
|
|
128
|
+
const restartCommand = `node "${dispositionPath}" start --session-id ${sessionId}`;
|
|
129
|
+
check(gate('Bash', { command: restartCommand }, repo).status === 0, 'trusted start controller remains admitted after reset');
|
|
130
|
+
check(control('start').status === 0 && read(file).state === 'running', 'Fable workflow resumes after audited budget reset');
|
|
131
|
+
const revisionCommand = `node "${dispositionPath}" revision --session-id ${sessionId} --evidence fixture-revision`;
|
|
132
|
+
check(gate('Bash', { command: revisionCommand }, repo).status === 0, 'revision controller is admitted through workflow gate');
|
|
133
|
+
check(control('revision', '--evidence', 'fixture-revision').status === 0 && read(file).budget?.counters?.revisionRounds === 1, 'revision controller reaches budget accounting');
|
|
116
134
|
fs.writeFileSync(file, '{broken');
|
|
117
135
|
check(route('continue').status === 2 && fs.readFileSync(file, 'utf8') === '{broken', 'router cannot overwrite unreadable unresolved state');
|
|
118
136
|
check(stop().status === 2, 'malformed state cannot silently pass completion');
|
|
@@ -120,7 +138,22 @@ try {
|
|
|
120
138
|
check(gate('Write', { file_path: path.join(linked, 'src.js') }).status === 2 && stop().status === 2, 'partial JSON state cannot bypass workflow gates');
|
|
121
139
|
fs.unlinkSync(file);
|
|
122
140
|
check(route('Fix this checkout bug with a regression test').status === 0 && read(file).strategy === 'iterative-single', 'bounded fix selects iterative lifecycle');
|
|
123
|
-
|
|
141
|
+
const iterationLimit = read(file).workflowPlan.limits.maxIterations;
|
|
142
|
+
check(Number.isInteger(iterationLimit) && iterationLimit > 0, 'iterative route exposes a numeric iteration budget');
|
|
143
|
+
for (let i = 0; i < iterationLimit; i++) {
|
|
144
|
+
check(gate('Write', { file_path: path.join(repo, `small-${i}.js`) }, repo).status === 0, `iterative mutation ${i + 1}/${iterationLimit} stays within budget`);
|
|
145
|
+
}
|
|
146
|
+
check(gate('Write', { file_path: path.join(repo, 'small-overflow.js') }, repo).status === 2, 'iteration beyond maxIterations is blocked');
|
|
147
|
+
const iterativeExhausted = read(file);
|
|
148
|
+
check(iterativeExhausted.state === 'blocked' && iterativeExhausted.budget?.state === 'budget-exhausted' &&
|
|
149
|
+
iterativeExhausted.budget?.reasonCode === 'iteration-budget-exhausted', 'iterative exhaustion records blocked budget state');
|
|
150
|
+
const iterativeResetCommand = `node "${dispositionPath}" reset-budget --session-id ${sessionId} --evidence fixture-iteration-budget-reset`;
|
|
151
|
+
check(gate('Bash', { command: iterativeResetCommand }, repo).status === 0, 'iteration-exhausted workflow admits reset-budget through gate');
|
|
152
|
+
check(control('reset-budget', '--evidence', 'fixture-iteration-budget-reset').status === 0, 'iteration budget reset executes');
|
|
153
|
+
check(read(file).state === 'pending' && read(file).budget?.epoch === 1 && read(file).budget?.counters?.iterations === 0, 'iteration budget reset is audited and clears counters');
|
|
154
|
+
const iterativeStartCommand = `node "${dispositionPath}" start --session-id ${sessionId}`;
|
|
155
|
+
check(gate('Bash', { command: iterativeStartCommand }, repo).status === 0, 'iterative workflow restart is admitted through gate');
|
|
156
|
+
check(control('start').status === 0 && read(file).state === 'running', 'iteration-exhausted workflow resumes after reset');
|
|
124
157
|
check(stop().status === 2, 'direct/iterative route cannot complete unverified mutation');
|
|
125
158
|
check(stop({ stop_hook_active: true }).status === 0 && read(file).state === 'blocked', 'iterative retry reports incomplete state');
|
|
126
159
|
check(control('start').status === 0, 'blocked iterative route can resume without a Fable stage map');
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0.0",
|
|
3
|
+
"mode": "strict",
|
|
4
|
+
"artifacts": [
|
|
5
|
+
{
|
|
6
|
+
"id": "ADR-001",
|
|
7
|
+
"kind": "ADR",
|
|
8
|
+
"path": "docs/adr/0001-api-contract.md",
|
|
9
|
+
"status": "ACTIVE",
|
|
10
|
+
"revision": 1,
|
|
11
|
+
"supersedes": [],
|
|
12
|
+
"evidenceRef": null
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "SPEC-001",
|
|
16
|
+
"kind": "SPEC",
|
|
17
|
+
"path": "docs/specs/api.md",
|
|
18
|
+
"status": "ACTIVE",
|
|
19
|
+
"revision": 1,
|
|
20
|
+
"supersedes": [],
|
|
21
|
+
"evidenceRef": null
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "TICKET-001",
|
|
25
|
+
"kind": "TICKET",
|
|
26
|
+
"path": "tickets/001.md",
|
|
27
|
+
"status": "ACTIVE",
|
|
28
|
+
"revision": 1,
|
|
29
|
+
"requirementId": "REQ-001",
|
|
30
|
+
"requirementRevision": 1,
|
|
31
|
+
"changeImpact": "implementation",
|
|
32
|
+
"supersedes": [],
|
|
33
|
+
"evidenceRef": null
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "TEST-001",
|
|
37
|
+
"kind": "TEST",
|
|
38
|
+
"path": "tests/api.test.js",
|
|
39
|
+
"status": "ACTIVE",
|
|
40
|
+
"revision": 1,
|
|
41
|
+
"requirementId": "REQ-001",
|
|
42
|
+
"requirementRevision": 1,
|
|
43
|
+
"supersedes": [],
|
|
44
|
+
"evidenceRef": "tests/api.test.js#contract"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "IMPL-001",
|
|
48
|
+
"kind": "IMPLEMENTATION",
|
|
49
|
+
"path": "src/api.js",
|
|
50
|
+
"status": "ACTIVE",
|
|
51
|
+
"revision": 1,
|
|
52
|
+
"requirementId": "REQ-001",
|
|
53
|
+
"requirementRevision": 1,
|
|
54
|
+
"supersedes": [],
|
|
55
|
+
"evidenceRef": "commit:baseline"
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"requirements": [
|
|
59
|
+
{
|
|
60
|
+
"requirementId": "REQ-001",
|
|
61
|
+
"revision": 1,
|
|
62
|
+
"status": "CURRENT",
|
|
63
|
+
"specId": "SPEC-001",
|
|
64
|
+
"decisionIds": [
|
|
65
|
+
"ADR-001"
|
|
66
|
+
],
|
|
67
|
+
"ticketIds": [
|
|
68
|
+
"TICKET-001"
|
|
69
|
+
],
|
|
70
|
+
"testIds": [
|
|
71
|
+
"TEST-001"
|
|
72
|
+
],
|
|
73
|
+
"implementationIds": [
|
|
74
|
+
"IMPL-001"
|
|
75
|
+
],
|
|
76
|
+
"requiredProbeIds": [
|
|
77
|
+
"PROBE-001"
|
|
78
|
+
],
|
|
79
|
+
"sourceValidity": "VALID",
|
|
80
|
+
"supersededBy": null
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"probes": [
|
|
84
|
+
{
|
|
85
|
+
"probeId": "PROBE-001",
|
|
86
|
+
"requirementId": "REQ-001",
|
|
87
|
+
"status": "KILLED",
|
|
88
|
+
"sourceSection": "docs/specs/api.md#REQ-001",
|
|
89
|
+
"evidenceRef": "probe:baseline-red",
|
|
90
|
+
"workspaceIsolation": "isolated",
|
|
91
|
+
"failureClass": "contract"
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0.0",
|
|
3
|
+
"mode": "strict",
|
|
4
|
+
"artifacts": [
|
|
5
|
+
{
|
|
6
|
+
"id": "ADR-001",
|
|
7
|
+
"kind": "ADR",
|
|
8
|
+
"path": "docs/adr/0001-api-contract.md",
|
|
9
|
+
"status": "SUPERSEDED",
|
|
10
|
+
"revision": 1,
|
|
11
|
+
"supersedes": [],
|
|
12
|
+
"evidenceRef": null
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "ADR-002",
|
|
16
|
+
"kind": "ADR",
|
|
17
|
+
"path": "docs/adr/0002-api-contract-v2.md",
|
|
18
|
+
"status": "ACTIVE",
|
|
19
|
+
"revision": 2,
|
|
20
|
+
"supersedes": [
|
|
21
|
+
"ADR-001"
|
|
22
|
+
],
|
|
23
|
+
"evidenceRef": "decision:approved-v2"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "SPEC-001",
|
|
27
|
+
"kind": "SPEC",
|
|
28
|
+
"path": "docs/specs/api.md",
|
|
29
|
+
"status": "ACTIVE",
|
|
30
|
+
"revision": 2,
|
|
31
|
+
"supersedes": [],
|
|
32
|
+
"evidenceRef": "spec:REQ-001-v2"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": "TICKET-001",
|
|
36
|
+
"kind": "TICKET",
|
|
37
|
+
"path": "tickets/001.md",
|
|
38
|
+
"status": "ACTIVE",
|
|
39
|
+
"revision": 2,
|
|
40
|
+
"requirementId": "REQ-001",
|
|
41
|
+
"requirementRevision": 2,
|
|
42
|
+
"changeImpact": "architecture",
|
|
43
|
+
"supersedes": [],
|
|
44
|
+
"evidenceRef": "feedback:approved-v2"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "TEST-001",
|
|
48
|
+
"kind": "TEST",
|
|
49
|
+
"path": "tests/api.test.js",
|
|
50
|
+
"status": "ACTIVE",
|
|
51
|
+
"revision": 2,
|
|
52
|
+
"requirementId": "REQ-001",
|
|
53
|
+
"requirementRevision": 2,
|
|
54
|
+
"supersedes": [],
|
|
55
|
+
"evidenceRef": "tests/api.test.js#contract-v2"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "IMPL-001",
|
|
59
|
+
"kind": "IMPLEMENTATION",
|
|
60
|
+
"path": "src/api.js",
|
|
61
|
+
"status": "ACTIVE",
|
|
62
|
+
"revision": 2,
|
|
63
|
+
"requirementId": "REQ-001",
|
|
64
|
+
"requirementRevision": 2,
|
|
65
|
+
"supersedes": [],
|
|
66
|
+
"evidenceRef": "commit:implemented-v2"
|
|
67
|
+
}
|
|
68
|
+
],
|
|
69
|
+
"requirements": [
|
|
70
|
+
{
|
|
71
|
+
"requirementId": "REQ-001",
|
|
72
|
+
"revision": 2,
|
|
73
|
+
"status": "CURRENT",
|
|
74
|
+
"specId": "SPEC-001",
|
|
75
|
+
"decisionIds": [
|
|
76
|
+
"ADR-002"
|
|
77
|
+
],
|
|
78
|
+
"ticketIds": [
|
|
79
|
+
"TICKET-001"
|
|
80
|
+
],
|
|
81
|
+
"testIds": [
|
|
82
|
+
"TEST-001"
|
|
83
|
+
],
|
|
84
|
+
"implementationIds": [
|
|
85
|
+
"IMPL-001"
|
|
86
|
+
],
|
|
87
|
+
"requiredProbeIds": [
|
|
88
|
+
"PROBE-001"
|
|
89
|
+
],
|
|
90
|
+
"sourceValidity": "VALID",
|
|
91
|
+
"supersededBy": null
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
"probes": [
|
|
95
|
+
{
|
|
96
|
+
"probeId": "PROBE-001",
|
|
97
|
+
"requirementId": "REQ-001",
|
|
98
|
+
"status": "KILLED",
|
|
99
|
+
"sourceSection": "docs/specs/api.md#REQ-001-v2",
|
|
100
|
+
"evidenceRef": "probe:v2-red",
|
|
101
|
+
"workspaceIsolation": "isolated",
|
|
102
|
+
"failureClass": "contract"
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "1.0.0",
|
|
3
|
+
"mode": "strict",
|
|
4
|
+
"artifacts": [
|
|
5
|
+
{
|
|
6
|
+
"id": "ADR-001",
|
|
7
|
+
"kind": "ADR",
|
|
8
|
+
"path": "docs/adr/0001-api-contract.md",
|
|
9
|
+
"status": "ACTIVE",
|
|
10
|
+
"revision": 1,
|
|
11
|
+
"supersedes": [],
|
|
12
|
+
"evidenceRef": null
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "SPEC-001",
|
|
16
|
+
"kind": "SPEC",
|
|
17
|
+
"path": "docs/specs/api.md",
|
|
18
|
+
"status": "ACTIVE",
|
|
19
|
+
"revision": 1,
|
|
20
|
+
"supersedes": [],
|
|
21
|
+
"evidenceRef": null
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "TICKET-001",
|
|
25
|
+
"kind": "TICKET",
|
|
26
|
+
"path": "tickets/001.md",
|
|
27
|
+
"status": "ACTIVE",
|
|
28
|
+
"revision": 2,
|
|
29
|
+
"requirementId": "REQ-001",
|
|
30
|
+
"requirementRevision": 2,
|
|
31
|
+
"changeImpact": "behavior",
|
|
32
|
+
"supersedes": [],
|
|
33
|
+
"evidenceRef": "feedback:changed-acceptance"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "TEST-001",
|
|
37
|
+
"kind": "TEST",
|
|
38
|
+
"path": "tests/api.test.js",
|
|
39
|
+
"status": "ACTIVE",
|
|
40
|
+
"revision": 1,
|
|
41
|
+
"requirementId": "REQ-001",
|
|
42
|
+
"requirementRevision": 1,
|
|
43
|
+
"supersedes": [],
|
|
44
|
+
"evidenceRef": "tests/api.test.js#contract"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "IMPL-001",
|
|
48
|
+
"kind": "IMPLEMENTATION",
|
|
49
|
+
"path": "src/api.js",
|
|
50
|
+
"status": "ACTIVE",
|
|
51
|
+
"revision": 1,
|
|
52
|
+
"requirementId": "REQ-001",
|
|
53
|
+
"requirementRevision": 1,
|
|
54
|
+
"supersedes": [],
|
|
55
|
+
"evidenceRef": "commit:baseline"
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"requirements": [
|
|
59
|
+
{
|
|
60
|
+
"requirementId": "REQ-001",
|
|
61
|
+
"revision": 1,
|
|
62
|
+
"status": "CURRENT",
|
|
63
|
+
"specId": "SPEC-001",
|
|
64
|
+
"decisionIds": [
|
|
65
|
+
"ADR-001"
|
|
66
|
+
],
|
|
67
|
+
"ticketIds": [
|
|
68
|
+
"TICKET-001"
|
|
69
|
+
],
|
|
70
|
+
"testIds": [
|
|
71
|
+
"TEST-001"
|
|
72
|
+
],
|
|
73
|
+
"implementationIds": [
|
|
74
|
+
"IMPL-001"
|
|
75
|
+
],
|
|
76
|
+
"requiredProbeIds": [
|
|
77
|
+
"PROBE-001"
|
|
78
|
+
],
|
|
79
|
+
"sourceValidity": "VALID",
|
|
80
|
+
"supersededBy": null
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"probes": [
|
|
84
|
+
{
|
|
85
|
+
"probeId": "PROBE-001",
|
|
86
|
+
"requirementId": "REQ-001",
|
|
87
|
+
"status": "KILLED",
|
|
88
|
+
"sourceSection": "docs/specs/api.md#REQ-001",
|
|
89
|
+
"evidenceRef": "probe:baseline-red",
|
|
90
|
+
"workspaceIsolation": "isolated",
|
|
91
|
+
"failureClass": "contract"
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
}
|