@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.
@@ -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 === false && !asString(reportFields["Smallest follow-up slice"])) {
146
- errors.push("Reviewer output must include a smallest follow-up slice when acceptance is no.");
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
- if (parseFirstNumber(reportFields["Blocker count"]) === undefined) {
151
- errors.push("Auditor output must include a numeric Blocker count.");
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
- if (anyFail && canStop === true) {
183
- errors.push("Stop-judge output cannot mark 'Can the project stop now: yes' when any rubric line is fail.");
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.33",
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"