@linimin/pi-letscook 0.1.33 → 0.1.36
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/CHANGELOG.md +25 -0
- package/README.md +48 -77
- package/agents/completion-implementer.md +11 -2
- package/extensions/completion/index.ts +370 -222
- package/extensions/completion/role-reporting.js +107 -20
- package/package.json +2 -1
- package/scripts/active-slice-contract-test.sh +242 -0
- package/scripts/canonical-evidence-artifact-test.sh +348 -0
- package/scripts/context-proposal-test.sh +50 -49
- package/scripts/evaluator-calibration-test.sh +363 -0
- package/scripts/refocus-test.sh +31 -0
- package/scripts/release-check.sh +5 -1
- package/scripts/smoke-test.sh +56 -1
- package/skills/completion-protocol/SKILL.md +4 -1
- package/skills/completion-protocol/references/completion.md +24 -0
|
@@ -76,6 +76,52 @@ function parseFirstNumber(value) {
|
|
|
76
76
|
return Number.isFinite(parsed) ? parsed : undefined;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
function parseNoneLikeValue(value) {
|
|
80
|
+
const raw = asString(value);
|
|
81
|
+
if (!raw) return { noneLike: false, suffix: "" };
|
|
82
|
+
const trimmed = raw.trim();
|
|
83
|
+
const patterns = [
|
|
84
|
+
/^\(none\)(.*)$/i,
|
|
85
|
+
/^none(?:\b|$)(.*)$/i,
|
|
86
|
+
/^n\/a(?:\b|$)(.*)$/i,
|
|
87
|
+
/^na(?:\b|$)(.*)$/i,
|
|
88
|
+
/^not applicable(?:\b|$)(.*)$/i,
|
|
89
|
+
];
|
|
90
|
+
for (const pattern of patterns) {
|
|
91
|
+
const match = trimmed.match(pattern);
|
|
92
|
+
if (match) {
|
|
93
|
+
return {
|
|
94
|
+
noneLike: true,
|
|
95
|
+
suffix: match[1] ?? "",
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return { noneLike: false, suffix: "" };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function normalizeNoneLikeSuffix(suffix) {
|
|
103
|
+
return suffix.replace(/^[\s,.;:/-]+/, "").trim();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isNoneLike(value) {
|
|
107
|
+
return parseNoneLikeValue(value).noneLike;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function isPureNoneLike(value) {
|
|
111
|
+
const parsed = parseNoneLikeValue(value);
|
|
112
|
+
return parsed.noneLike && normalizeNoneLikeSuffix(parsed.suffix).length === 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isReviewerProceedToAuditorRoutingValue(value) {
|
|
116
|
+
const raw = asString(value);
|
|
117
|
+
if (!raw) return false;
|
|
118
|
+
return /^none\s*;\s*proceed to completion-auditor(?:[\p{P}\s]*)$/iu.test(raw);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function isReviewerNoFollowUpValue(value) {
|
|
122
|
+
return isPureNoneLike(value) || isReviewerProceedToAuditorRoutingValue(value);
|
|
123
|
+
}
|
|
124
|
+
|
|
79
125
|
function rubricVerdicts(reportFields) {
|
|
80
126
|
return RUBRIC_DIMENSIONS.map((dimension) => {
|
|
81
127
|
const value = reportFields[dimension];
|
|
@@ -138,22 +184,26 @@ function validateRoleReport(role, output, reportFields = parseReportFields(outpu
|
|
|
138
184
|
if (role === "completion-reviewer") {
|
|
139
185
|
validateRequiredFields(reportFields, REVIEWER_REQUIRED_FIELDS, errors, role);
|
|
140
186
|
const acceptable = parseYesNo(reportFields["Acceptable as-is"]);
|
|
187
|
+
const followUpSlice = asString(reportFields["Smallest follow-up slice"]);
|
|
141
188
|
if (acceptable === undefined) errors.push("Reviewer output must answer 'Acceptable as-is' with yes or no.");
|
|
142
189
|
if (anyFail && acceptable === true) {
|
|
143
190
|
errors.push("Reviewer output cannot mark 'Acceptable as-is: yes' when any rubric line is fail.");
|
|
144
191
|
}
|
|
145
|
-
if (acceptable ===
|
|
146
|
-
errors.push("Reviewer output
|
|
192
|
+
if (acceptable === true && followUpSlice && !isReviewerNoFollowUpValue(followUpSlice)) {
|
|
193
|
+
errors.push("Reviewer output cannot mark 'Acceptable as-is: yes' while naming a follow-up slice other than none.");
|
|
194
|
+
}
|
|
195
|
+
if (acceptable === false) {
|
|
196
|
+
if (!followUpSlice) {
|
|
197
|
+
errors.push("Reviewer output must include a smallest follow-up slice when acceptance is no.");
|
|
198
|
+
} else if (isNoneLike(followUpSlice)) {
|
|
199
|
+
errors.push("Reviewer output must name a non-none smallest follow-up slice when acceptance is no.");
|
|
200
|
+
}
|
|
147
201
|
}
|
|
148
202
|
} else if (role === "completion-auditor") {
|
|
149
203
|
validateRequiredFields(reportFields, AUDITOR_REQUIRED_FIELDS, errors, role);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (parseFirstNumber(reportFields["High-value gap count"]) === undefined) {
|
|
154
|
-
errors.push("Auditor output must include a numeric High-value gap count.");
|
|
155
|
-
}
|
|
156
|
-
validateYesNoField(
|
|
204
|
+
const blockerCount = parseFirstNumber(reportFields["Blocker count"]);
|
|
205
|
+
const highValueGapCount = parseFirstNumber(reportFields["High-value gap count"]);
|
|
206
|
+
const worktreeClean = validateYesNoField(
|
|
157
207
|
reportFields,
|
|
158
208
|
"Tracked and unignored worktree is clean",
|
|
159
209
|
errors,
|
|
@@ -171,6 +221,25 @@ function validateRoleReport(role, output, reportFields = parseReportFields(outpu
|
|
|
171
221
|
errors,
|
|
172
222
|
"Auditor output must answer 'Plan truthfully captures remaining slice backlog' with yes or no.",
|
|
173
223
|
);
|
|
224
|
+
if (blockerCount === undefined) {
|
|
225
|
+
errors.push("Auditor output must include a numeric Blocker count.");
|
|
226
|
+
}
|
|
227
|
+
if (highValueGapCount === undefined) {
|
|
228
|
+
errors.push("Auditor output must include a numeric High-value gap count.");
|
|
229
|
+
}
|
|
230
|
+
const worktreeBlockers = asString(reportFields["Worktree blockers"]);
|
|
231
|
+
const nextMandatorySlice = asString(reportFields["Next mandatory slice"]);
|
|
232
|
+
const openContractIds = asString(reportFields["Open top-level contract IDs"]);
|
|
233
|
+
const hasRemainingWork = !isNoneLike(openContractIds) || (blockerCount ?? 0) > 0 || (highValueGapCount ?? 0) > 0;
|
|
234
|
+
if (worktreeClean === true && worktreeBlockers && !isPureNoneLike(worktreeBlockers)) {
|
|
235
|
+
errors.push("Auditor output cannot mark 'Tracked and unignored worktree is clean: yes' while listing worktree blockers.");
|
|
236
|
+
}
|
|
237
|
+
if (worktreeClean === false && (!worktreeBlockers || isNoneLike(worktreeBlockers))) {
|
|
238
|
+
errors.push("Auditor output must describe worktree blockers when 'Tracked and unignored worktree is clean: no'.");
|
|
239
|
+
}
|
|
240
|
+
if (hasRemainingWork && nextMandatorySlice && isNoneLike(nextMandatorySlice)) {
|
|
241
|
+
errors.push("Auditor output cannot leave 'Next mandatory slice' as none while open contracts, blockers, or high-value gaps remain.");
|
|
242
|
+
}
|
|
174
243
|
} else if (role === "completion-stop-judge") {
|
|
175
244
|
validateRequiredFields(reportFields, STOP_JUDGE_REQUIRED_FIELDS, errors, role);
|
|
176
245
|
const canStop = validateYesNoField(
|
|
@@ -179,27 +248,45 @@ function validateRoleReport(role, output, reportFields = parseReportFields(outpu
|
|
|
179
248
|
errors,
|
|
180
249
|
"Stop-judge output must answer 'Can the project stop now' with yes or no.",
|
|
181
250
|
);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (parseFirstNumber(reportFields["Blocker count"]) === undefined) {
|
|
186
|
-
errors.push("Stop-judge output must include a numeric Blocker count.");
|
|
187
|
-
}
|
|
188
|
-
if (parseFirstNumber(reportFields["High-value gap count"]) === undefined) {
|
|
189
|
-
errors.push("Stop-judge output must include a numeric High-value gap count.");
|
|
190
|
-
}
|
|
191
|
-
validateYesNoField(
|
|
251
|
+
const blockerCount = parseFirstNumber(reportFields["Blocker count"]);
|
|
252
|
+
const highValueGapCount = parseFirstNumber(reportFields["High-value gap count"]);
|
|
253
|
+
const docsParity = validateYesNoField(
|
|
192
254
|
reportFields,
|
|
193
255
|
"Docs/config/runbooks match shipped behavior",
|
|
194
256
|
errors,
|
|
195
257
|
"Stop-judge output must answer 'Docs/config/runbooks match shipped behavior' with yes or no.",
|
|
196
258
|
);
|
|
197
|
-
validateYesNoField(
|
|
259
|
+
const worktreeClean = validateYesNoField(
|
|
198
260
|
reportFields,
|
|
199
261
|
"Tracked and unignored worktree is clean",
|
|
200
262
|
errors,
|
|
201
263
|
"Stop-judge output must answer 'Tracked and unignored worktree is clean' with yes or no.",
|
|
202
264
|
);
|
|
265
|
+
if (anyFail && canStop === true) {
|
|
266
|
+
errors.push("Stop-judge output cannot mark 'Can the project stop now: yes' when any rubric line is fail.");
|
|
267
|
+
}
|
|
268
|
+
if (blockerCount === undefined) {
|
|
269
|
+
errors.push("Stop-judge output must include a numeric Blocker count.");
|
|
270
|
+
}
|
|
271
|
+
if (highValueGapCount === undefined) {
|
|
272
|
+
errors.push("Stop-judge output must include a numeric High-value gap count.");
|
|
273
|
+
}
|
|
274
|
+
const openContractIds = asString(reportFields["Exact remaining open top-level contract IDs"]);
|
|
275
|
+
if (canStop === true && openContractIds && !isPureNoneLike(openContractIds)) {
|
|
276
|
+
errors.push("Stop-judge output cannot mark 'Can the project stop now: yes' while naming remaining open top-level contract IDs.");
|
|
277
|
+
}
|
|
278
|
+
if (canStop === true && (blockerCount ?? 0) > 0) {
|
|
279
|
+
errors.push("Stop-judge output cannot mark 'Can the project stop now: yes' when Blocker count is greater than 0.");
|
|
280
|
+
}
|
|
281
|
+
if (canStop === true && (highValueGapCount ?? 0) > 0) {
|
|
282
|
+
errors.push("Stop-judge output cannot mark 'Can the project stop now: yes' when High-value gap count is greater than 0.");
|
|
283
|
+
}
|
|
284
|
+
if (canStop === true && docsParity === false) {
|
|
285
|
+
errors.push("Stop-judge output cannot mark 'Can the project stop now: yes' when docs/config/runbooks do not match shipped behavior.");
|
|
286
|
+
}
|
|
287
|
+
if (canStop === true && worktreeClean === false) {
|
|
288
|
+
errors.push("Stop-judge output cannot mark 'Can the project stop now: yes' when the tracked and unignored worktree is not clean.");
|
|
289
|
+
}
|
|
203
290
|
}
|
|
204
291
|
|
|
205
292
|
return { valid: errors.length === 0, errors, reportFields, rubric };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linimin/pi-letscook",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36",
|
|
4
4
|
"description": "Pi package for long-running completion workflows with canonical .agent state, role-based subagents, continuity, and verification helpers.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"refocus-test": "bash ./scripts/refocus-test.sh",
|
|
30
30
|
"context-proposal-test": "bash ./scripts/context-proposal-test.sh",
|
|
31
31
|
"observability-status-test": "bash ./scripts/observability-status-test.sh",
|
|
32
|
+
"evaluator-calibration-test": "bash ./scripts/evaluator-calibration-test.sh",
|
|
32
33
|
"rubric-contract-test": "bash ./scripts/rubric-contract-test.sh",
|
|
33
34
|
"release-check": "bash ./scripts/release-check.sh"
|
|
34
35
|
},
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
PKG_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
5
|
+
TMPDIR="$(mktemp -d)"
|
|
6
|
+
trap 'rm -rf "$TMPDIR"' EXIT
|
|
7
|
+
|
|
8
|
+
cd "$PKG_ROOT"
|
|
9
|
+
|
|
10
|
+
node <<'NODE'
|
|
11
|
+
const fs = require('node:fs');
|
|
12
|
+
|
|
13
|
+
const read = (file) => fs.readFileSync(file, 'utf8');
|
|
14
|
+
const assertIncludes = (file, snippet) => {
|
|
15
|
+
const text = read(file);
|
|
16
|
+
if (!text.includes(snippet)) {
|
|
17
|
+
throw new Error(`${file} is missing required active-slice-contract text: ${snippet}`);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
assertIncludes('agents/completion-implementer.md', 'canonical implementation contract');
|
|
22
|
+
assertIncludes('agents/completion-implementer.md', '`implementation_surfaces`');
|
|
23
|
+
assertIncludes('agents/completion-implementer.md', '`verification_commands`');
|
|
24
|
+
assertIncludes('agents/completion-implementer.md', '`basis_commit`');
|
|
25
|
+
assertIncludes('agents/completion-implementer.md', '`remaining_contract_ids_before`');
|
|
26
|
+
assertIncludes('agents/completion-implementer.md', '`release_blocker_count_before`');
|
|
27
|
+
assertIncludes('agents/completion-implementer.md', '`high_value_gap_count_before`');
|
|
28
|
+
assertIncludes('README.md', 'canonical implementation contract for selected, in-progress, committed, and done slices');
|
|
29
|
+
assertIncludes('README.md', 'The selected plan slice must mirror that exact contract across goal, contract IDs, acceptance criteria');
|
|
30
|
+
assertIncludes('README.md', '`basis_commit`');
|
|
31
|
+
assertIncludes('README.md', '`remaining_contract_ids_before` plus `release_blocker_count_before` / `high_value_gap_count_before`');
|
|
32
|
+
assertIncludes('README.md', 'Deterministic active-slice contract regression now lives in `bash scripts/active-slice-contract-test.sh`');
|
|
33
|
+
assertIncludes('README.md', 'includes deterministic active-slice contract coverage plus observability coverage');
|
|
34
|
+
assertIncludes('scripts/release-check.sh', 'bash ./scripts/active-slice-contract-test.sh');
|
|
35
|
+
assertIncludes('.agent/verify_completion_stop.sh', 'npm run release-check >/dev/null');
|
|
36
|
+
assertIncludes('extensions/completion/index.ts', "const planMirrorFields = ['locked_notes', 'must_fix_findings', 'implementation_surfaces', 'verification_commands', 'basis_commit', 'remaining_contract_ids_before', 'release_blocker_count_before', 'high_value_gap_count_before'];");
|
|
37
|
+
assertIncludes('extensions/completion/index.ts', 'Selected/in-progress/committed/done .agent/active-slice.json is the canonical implementation contract.');
|
|
38
|
+
assertIncludes('extensions/completion/index.ts', 'Active slice contract drift: ${activeContractDrift}');
|
|
39
|
+
assertIncludes('extensions/completion/index.ts', 'Canonical active-slice contract drift is currently: ${activeContractDrift}');
|
|
40
|
+
assertIncludes('extensions/completion/index.ts', '`active_slice_contract_drift_fields: ${activeContractDrift}`');
|
|
41
|
+
assertIncludes('extensions/completion/index.ts', 'treat .agent/active-slice.json as the canonical implementation contract');
|
|
42
|
+
assertIncludes('.agent/verify_completion_control_plane.sh', "const planMirrorFields = ['locked_notes', 'must_fix_findings', 'implementation_surfaces', 'verification_commands', 'basis_commit', 'remaining_contract_ids_before', 'release_blocker_count_before', 'high_value_gap_count_before'];");
|
|
43
|
+
assertIncludes('.agent/verify_completion_control_plane.sh', 'slice_id must match a slice in .agent/plan.json when status carries an exact handoff');
|
|
44
|
+
assertIncludes('.agent/verify_completion_control_plane.sh', '.agent/active-slice.json must match the selected .agent/plan.json slice across: ');
|
|
45
|
+
NODE
|
|
46
|
+
|
|
47
|
+
ROOT="$TMPDIR/repo"
|
|
48
|
+
PROMPT="$TMPDIR/resume-prompt.txt"
|
|
49
|
+
mkdir -p "$ROOT"
|
|
50
|
+
cd "$ROOT"
|
|
51
|
+
git init -q
|
|
52
|
+
|
|
53
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
54
|
+
pi -e "$PKG_ROOT" -p "/cook active-slice contract fixture" \
|
|
55
|
+
>"$TMPDIR/pi-active-slice-bootstrap.out" 2>"$TMPDIR/pi-active-slice-bootstrap.err"
|
|
56
|
+
|
|
57
|
+
python3 - <<'PY'
|
|
58
|
+
import json
|
|
59
|
+
from pathlib import Path
|
|
60
|
+
|
|
61
|
+
mission = 'Exercise active-slice contract parity.'
|
|
62
|
+
task_type = 'completion-workflow'
|
|
63
|
+
evaluation_profile = 'completion-rubric-v1'
|
|
64
|
+
verification_commands = [
|
|
65
|
+
'bash .agent/verify_completion_control_plane.sh',
|
|
66
|
+
'bash scripts/active-slice-contract-test.sh',
|
|
67
|
+
'npm run release-check',
|
|
68
|
+
]
|
|
69
|
+
implementation_surfaces = [
|
|
70
|
+
'extensions/completion/index.ts',
|
|
71
|
+
'agents/completion-implementer.md',
|
|
72
|
+
'README.md',
|
|
73
|
+
'.agent/verify_completion_control_plane.sh',
|
|
74
|
+
'scripts/release-check.sh',
|
|
75
|
+
'scripts/active-slice-contract-test.sh',
|
|
76
|
+
]
|
|
77
|
+
locked_notes = [
|
|
78
|
+
'Keep scope locked to active-slice contract parity.',
|
|
79
|
+
'Do not broaden into canonical evidence artifacts.',
|
|
80
|
+
]
|
|
81
|
+
must_fix_findings = [
|
|
82
|
+
'Ensure release-check covers the active-slice contract regression.',
|
|
83
|
+
]
|
|
84
|
+
remaining_contracts = ['ACTIVE-SLICE-CONTRACT-V2', 'CANONICAL-EVIDENCE-ARTIFACTS']
|
|
85
|
+
acceptance = [
|
|
86
|
+
'Selected active-slice data is treated as the canonical implementation contract.',
|
|
87
|
+
'Control-plane parity checks fail closed on active-vs-plan drift.',
|
|
88
|
+
'Release-check includes deterministic active-slice contract regression coverage.',
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
state = {
|
|
92
|
+
'schema_version': 1,
|
|
93
|
+
'mission_anchor': mission,
|
|
94
|
+
'current_phase': 'implement',
|
|
95
|
+
'continuation_policy': 'continue',
|
|
96
|
+
'continuation_reason': 'Fixture for active-slice contract regression coverage.',
|
|
97
|
+
'project_done': False,
|
|
98
|
+
'task_type': task_type,
|
|
99
|
+
'evaluation_profile': evaluation_profile,
|
|
100
|
+
'requires_reground': False,
|
|
101
|
+
'slices_since_last_reground': 0,
|
|
102
|
+
'remaining_release_blockers': 0,
|
|
103
|
+
'remaining_high_value_gaps': 2,
|
|
104
|
+
'unsatisfied_contract_ids': remaining_contracts,
|
|
105
|
+
'release_blocker_ids': [],
|
|
106
|
+
'next_mandatory_action': 'Implement selected slice active-slice-fixture.',
|
|
107
|
+
'next_mandatory_role': 'completion-implementer',
|
|
108
|
+
'remaining_stop_judges': 3,
|
|
109
|
+
'last_reground_at': '2026-05-03T00:00:00Z',
|
|
110
|
+
'last_auditor_verdict': None,
|
|
111
|
+
'contract_status': 'selected_slice_pending_implementation',
|
|
112
|
+
'latest_completed_slice': 'fixturebasis',
|
|
113
|
+
'latest_verified_slice': 'fixturebasis',
|
|
114
|
+
}
|
|
115
|
+
plan = {
|
|
116
|
+
'schema_version': 1,
|
|
117
|
+
'mission_anchor': mission,
|
|
118
|
+
'task_type': task_type,
|
|
119
|
+
'evaluation_profile': evaluation_profile,
|
|
120
|
+
'last_reground_at': '2026-05-03T00:00:00Z',
|
|
121
|
+
'plan_basis': 'active_slice_contract_fixture',
|
|
122
|
+
'candidate_slices': [
|
|
123
|
+
{
|
|
124
|
+
'slice_id': 'active-slice-fixture',
|
|
125
|
+
'goal': 'Tighten active-slice implementation contract enforcement.',
|
|
126
|
+
'acceptance_criteria': acceptance,
|
|
127
|
+
'contract_ids': ['ACTIVE-SLICE-CONTRACT-V2'],
|
|
128
|
+
'priority': 80,
|
|
129
|
+
'status': 'selected',
|
|
130
|
+
'why_now': 'Fixture for active-slice contract parity.',
|
|
131
|
+
'blocked_on': [],
|
|
132
|
+
'evidence': [],
|
|
133
|
+
'locked_notes': locked_notes,
|
|
134
|
+
'must_fix_findings': must_fix_findings,
|
|
135
|
+
'implementation_surfaces': implementation_surfaces,
|
|
136
|
+
'verification_commands': verification_commands,
|
|
137
|
+
'basis_commit': 'fixturebasis',
|
|
138
|
+
'remaining_contract_ids_before': remaining_contracts,
|
|
139
|
+
'release_blocker_count_before': 0,
|
|
140
|
+
'high_value_gap_count_before': 2,
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
}
|
|
144
|
+
active = {
|
|
145
|
+
'schema_version': 1,
|
|
146
|
+
'mission_anchor': mission,
|
|
147
|
+
'task_type': task_type,
|
|
148
|
+
'evaluation_profile': evaluation_profile,
|
|
149
|
+
'status': 'selected',
|
|
150
|
+
'slice_id': 'active-slice-fixture',
|
|
151
|
+
'goal': 'Tighten active-slice implementation contract enforcement.',
|
|
152
|
+
'contract_ids': ['ACTIVE-SLICE-CONTRACT-V2'],
|
|
153
|
+
'acceptance_criteria': acceptance,
|
|
154
|
+
'blocked_on': [],
|
|
155
|
+
'locked_notes': locked_notes,
|
|
156
|
+
'must_fix_findings': must_fix_findings,
|
|
157
|
+
'implementation_surfaces': implementation_surfaces,
|
|
158
|
+
'verification_commands': verification_commands,
|
|
159
|
+
'basis_commit': 'fixturebasis',
|
|
160
|
+
'remaining_contract_ids_before': remaining_contracts,
|
|
161
|
+
'release_blocker_count_before': 0,
|
|
162
|
+
'high_value_gap_count_before': 2,
|
|
163
|
+
'priority': 80,
|
|
164
|
+
'why_now': 'Fixture for active-slice contract parity.',
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
Path('.agent/state.json').write_text(json.dumps(state, indent=2) + '\n')
|
|
168
|
+
Path('.agent/plan.json').write_text(json.dumps(plan, indent=2) + '\n')
|
|
169
|
+
Path('.agent/active-slice.json').write_text(json.dumps(active, indent=2) + '\n')
|
|
170
|
+
Path('.agent/verification-evidence.json').write_text(json.dumps({
|
|
171
|
+
'schema_version': 1,
|
|
172
|
+
'artifact_type': 'completion-verification-evidence',
|
|
173
|
+
'subject_type': 'selected_slice',
|
|
174
|
+
'slice_id': active['slice_id'],
|
|
175
|
+
'goal': active['goal'],
|
|
176
|
+
'contract_ids': active['contract_ids'],
|
|
177
|
+
'basis_commit': active['basis_commit'],
|
|
178
|
+
'head_sha': active['basis_commit'],
|
|
179
|
+
'verification_commands': verification_commands,
|
|
180
|
+
'outcome': 'passed',
|
|
181
|
+
'recorded_at': '2026-05-03T00:00:00Z',
|
|
182
|
+
'summary': 'Fixture evidence matches the selected active-slice contract.',
|
|
183
|
+
}, indent=2) + '\n')
|
|
184
|
+
PY
|
|
185
|
+
|
|
186
|
+
PI_COMPLETION_SKIP_DRIVER_KICKOFF=1 \
|
|
187
|
+
PI_COMPLETION_TEST_DRIVER_PROMPT_PATH="$PROMPT" \
|
|
188
|
+
pi -e "$PKG_ROOT" -p "/cook" \
|
|
189
|
+
>"$TMPDIR/pi-active-slice-resume.out" 2>"$TMPDIR/pi-active-slice-resume.err"
|
|
190
|
+
|
|
191
|
+
python3 - "$PROMPT" <<'PY'
|
|
192
|
+
import sys
|
|
193
|
+
from pathlib import Path
|
|
194
|
+
|
|
195
|
+
text = Path(sys.argv[1]).read_text()
|
|
196
|
+
assert 'treat .agent/active-slice.json as the canonical implementation contract' in text, text
|
|
197
|
+
assert 'drifts from the selected plan slice or the exact handoff is unclear' in text, text
|
|
198
|
+
PY
|
|
199
|
+
|
|
200
|
+
bash .agent/verify_completion_control_plane.sh >/dev/null
|
|
201
|
+
|
|
202
|
+
python3 - <<'PY'
|
|
203
|
+
import copy
|
|
204
|
+
import json
|
|
205
|
+
import subprocess
|
|
206
|
+
from pathlib import Path
|
|
207
|
+
|
|
208
|
+
plan_path = Path('.agent/plan.json')
|
|
209
|
+
base_plan = json.loads(plan_path.read_text())
|
|
210
|
+
|
|
211
|
+
cases = [
|
|
212
|
+
('slice_id', lambda slice: slice.__setitem__('slice_id', 'different-slice')),
|
|
213
|
+
('goal', lambda slice: slice.__setitem__('goal', 'Drifted goal')),
|
|
214
|
+
('contract_ids', lambda slice: slice.__setitem__('contract_ids', ['OTHER-CONTRACT'])),
|
|
215
|
+
('acceptance_criteria', lambda slice: slice.__setitem__('acceptance_criteria', ['Different criterion'])),
|
|
216
|
+
('blocked_on', lambda slice: slice.__setitem__('blocked_on', ['fixture-blocker'])),
|
|
217
|
+
('priority', lambda slice: slice.__setitem__('priority', 1)),
|
|
218
|
+
('why_now', lambda slice: slice.__setitem__('why_now', 'Different why_now')),
|
|
219
|
+
('implementation_surfaces', lambda slice: slice.pop('implementation_surfaces', None)),
|
|
220
|
+
('verification_commands', lambda slice: slice.__setitem__('verification_commands', ['bash .agent/verify_completion_control_plane.sh'])),
|
|
221
|
+
('locked_notes', lambda slice: slice.__setitem__('locked_notes', ['different note'])),
|
|
222
|
+
('must_fix_findings', lambda slice: slice.__setitem__('must_fix_findings', ['different finding'])),
|
|
223
|
+
('basis_commit', lambda slice: slice.__setitem__('basis_commit', 'differentbasis')),
|
|
224
|
+
('remaining_contract_ids_before', lambda slice: slice.__setitem__('remaining_contract_ids_before', ['ACTIVE-SLICE-CONTRACT-V2'])),
|
|
225
|
+
('release_blocker_count_before', lambda slice: slice.__setitem__('release_blocker_count_before', 1)),
|
|
226
|
+
('high_value_gap_count_before', lambda slice: slice.__setitem__('high_value_gap_count_before', 99)),
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
for label, mutate in cases:
|
|
230
|
+
plan = copy.deepcopy(base_plan)
|
|
231
|
+
slice_data = plan['candidate_slices'][0]
|
|
232
|
+
mutate(slice_data)
|
|
233
|
+
plan_path.write_text(json.dumps(plan, indent=2) + '\n')
|
|
234
|
+
result = subprocess.run(['bash', '.agent/verify_completion_control_plane.sh'], capture_output=True, text=True)
|
|
235
|
+
combined = (result.stdout or '') + (result.stderr or '')
|
|
236
|
+
assert result.returncode != 0, f'expected verifier failure for {label}'
|
|
237
|
+
assert label in combined, f'expected verifier output to mention {label}, got: {combined}'
|
|
238
|
+
|
|
239
|
+
plan_path.write_text(json.dumps(base_plan, indent=2) + '\n')
|
|
240
|
+
PY
|
|
241
|
+
|
|
242
|
+
echo "active-slice contract test passed: $TMPDIR"
|