project-tiny-context-harness 0.2.83 → 0.2.85
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/README.md +5 -5
- package/assets/README.md +8 -8
- package/assets/README.zh-CN.md +4 -4
- package/assets/protected-harness-baseline.json +6 -4
- package/assets/skills/composite-long-task-workflow/SKILL.md +12 -5
- package/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md +19 -0
- package/assets/skills/composite-long-task-workflow/assets/goal-objective.template.md +2 -2
- package/assets/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md +47 -26
- package/dist/commands/composite-long-task.js +21 -3
- package/dist/lib/package-source.js +11 -1
- package/dist/lib/superpowers-task-command-run-correlation.d.ts +8 -0
- package/dist/lib/superpowers-task-command-run-correlation.js +103 -0
- package/dist/lib/superpowers-task-completion-output.d.ts +91 -0
- package/dist/lib/superpowers-task-completion-output.js +374 -0
- package/dist/lib/superpowers-task-current-evidence.js +22 -0
- package/dist/lib/superpowers-task-derive.js +66 -8
- package/dist/lib/superpowers-task-evidence-kernel.d.ts +7 -0
- package/dist/lib/superpowers-task-evidence-kernel.js +67 -71
- package/dist/lib/superpowers-task-evidence.js +7 -1
- package/dist/lib/superpowers-task-final-card.d.ts +3 -0
- package/dist/lib/superpowers-task-final-card.js +35 -0
- package/dist/lib/superpowers-task-gates.d.ts +2 -2
- package/dist/lib/superpowers-task-gates.js +179 -27
- package/dist/lib/superpowers-task-harness-drift.js +6 -2
- package/dist/lib/superpowers-task-protected-baseline.js +21 -2
- package/dist/lib/superpowers-task-state-schema.d.ts +37 -0
- package/dist/lib/superpowers-task-state.js +10 -1
- package/dist/lib/superpowers-task-unregistered-evidence.d.ts +11 -0
- package/dist/lib/superpowers-task-unregistered-evidence.js +72 -0
- package/dist/lib/superpowers-task-validator.js +41 -0
- package/package.json +69 -69
|
@@ -2,9 +2,12 @@ import path from "node:path";
|
|
|
2
2
|
import { ensureDir, pathExists, readText, writeTextIfChanged } from "./fs.js";
|
|
3
3
|
import { stableJson, loadSuperpowersState } from "./superpowers-task-state.js";
|
|
4
4
|
import { evaluateProofLayerAssertions } from "./superpowers-task-assertions.js";
|
|
5
|
+
import { completionOutputContractFromState } from "./superpowers-task-completion-output.js";
|
|
6
|
+
import { renderFinalCard } from "./superpowers-task-final-card.js";
|
|
5
7
|
export async function deriveSuperpowersArtifacts(workdir) {
|
|
6
8
|
const state = await loadSuperpowersState(workdir);
|
|
7
9
|
const derived = deriveObjects(state);
|
|
10
|
+
const contract = completionOutputContractFromState(state);
|
|
8
11
|
const derivedDir = path.join(workdir, "derived");
|
|
9
12
|
await ensureDir(derivedDir);
|
|
10
13
|
const files = [];
|
|
@@ -14,11 +17,12 @@ export async function deriveSuperpowersArtifacts(workdir) {
|
|
|
14
17
|
await writeDerived(files, path.join(derivedDir, "evidence-index.json"), stableJson(deriveEvidenceIndex(state)));
|
|
15
18
|
await writeDerived(files, path.join(derivedDir, "plan-conformance-matrix.md"), matrixMarkdown(derived.matrix));
|
|
16
19
|
await writeDerived(files, path.join(derivedDir, "final-acceptance-verdict.md"), verdictMarkdown(derived.verdict));
|
|
17
|
-
await writeDerived(files, path.join(derivedDir, "local-audit.md"), localAuditMarkdown(state));
|
|
20
|
+
await writeDerived(files, path.join(derivedDir, "local-audit.md"), localAuditMarkdown(state, contract));
|
|
18
21
|
await writeDerived(files, path.join(derivedDir, "progress-ledger.md"), progressMarkdown(derived.progress));
|
|
19
22
|
await writeDerived(files, path.join(derivedDir, "evidence-index.md"), evidenceMarkdown(state));
|
|
20
23
|
await writeDerived(files, path.join(derivedDir, "context-alignment.md"), contextMarkdown(state));
|
|
21
|
-
await writeDerived(files, path.join(derivedDir, "final-summary.md"), finalSummaryMarkdown(state, derived.verdict));
|
|
24
|
+
await writeDerived(files, path.join(derivedDir, "final-summary.md"), finalSummaryMarkdown(state, derived.verdict, contract));
|
|
25
|
+
await writeDerived(files, path.join(derivedDir, "final-card.md"), renderFinalCard(contract, state));
|
|
22
26
|
return { matrix: derived.matrix, verdict: derived.verdict, files };
|
|
23
27
|
}
|
|
24
28
|
export function deriveObjects(state) {
|
|
@@ -140,9 +144,28 @@ export function deriveObjects(state) {
|
|
|
140
144
|
.slice(0, 5)
|
|
141
145
|
.map(([layerId]) => layerId)
|
|
142
146
|
};
|
|
147
|
+
const contract = completionOutputContractFromState(state);
|
|
148
|
+
const completionMetadata = {
|
|
149
|
+
completion_output_status: contract.completion_output_status,
|
|
150
|
+
final_answer_allowed: contract.final_answer_allowed,
|
|
151
|
+
required_user_visible_status: contract.required_user_visible_status,
|
|
152
|
+
product_goal_complete: contract.product_goal_complete,
|
|
153
|
+
acceptance_target_status: contract.acceptance_target_status,
|
|
154
|
+
audit_task_complete: contract.audit_task_complete
|
|
155
|
+
};
|
|
143
156
|
return {
|
|
144
|
-
matrix: {
|
|
145
|
-
|
|
157
|
+
matrix: {
|
|
158
|
+
diagnostic_scope: "plan_conformance_rows_only",
|
|
159
|
+
overall_status: allComplete ? "complete" : "partial",
|
|
160
|
+
...completionMetadata,
|
|
161
|
+
items: matrixRows
|
|
162
|
+
},
|
|
163
|
+
verdict: {
|
|
164
|
+
diagnostic_scope: "acceptance_evidence_rows_only",
|
|
165
|
+
overall_status: allComplete ? "complete" : "partial",
|
|
166
|
+
...completionMetadata,
|
|
167
|
+
acceptance_items: verdictRows
|
|
168
|
+
},
|
|
146
169
|
progress
|
|
147
170
|
};
|
|
148
171
|
}
|
|
@@ -289,12 +312,19 @@ missing_layer_count: ${missing}
|
|
|
289
312
|
${rows.map((row) => `- ${row.ac_id}: ${row.status}`).join("\n")}
|
|
290
313
|
`;
|
|
291
314
|
}
|
|
292
|
-
function localAuditMarkdown(state) {
|
|
315
|
+
function localAuditMarkdown(state, contract) {
|
|
316
|
+
const auditLine = contract.completion_output_status === "accept"
|
|
317
|
+
? "Final-gate accepted the current attempt."
|
|
318
|
+
: "Audit workflow completed; acceptance target not complete.";
|
|
293
319
|
return `# Local Audit
|
|
294
320
|
|
|
295
321
|
audit_task_complete: ${state.final.audit_task_complete}
|
|
296
322
|
acceptance_target_status: ${state.final.acceptance_target_status}
|
|
297
323
|
product_goal_complete: ${state.final.product_goal_complete}
|
|
324
|
+
completion_output_status: ${contract.completion_output_status}
|
|
325
|
+
final_answer_allowed: ${contract.final_answer_allowed}
|
|
326
|
+
|
|
327
|
+
${auditLine}
|
|
298
328
|
`;
|
|
299
329
|
}
|
|
300
330
|
function progressMarkdown(progress) {
|
|
@@ -326,10 +356,38 @@ Product Context Delta: ${state.context.product_context_delta}
|
|
|
326
356
|
Technical Context Delta: ${state.context.technical_context_delta}
|
|
327
357
|
`;
|
|
328
358
|
}
|
|
329
|
-
function finalSummaryMarkdown(state, verdict) {
|
|
359
|
+
function finalSummaryMarkdown(state, verdict, contract) {
|
|
360
|
+
const reasons = contract.completion_output_status === "blocked" ? contract.blocked_reasons : contract.rejection_reasons;
|
|
361
|
+
const reasonBlock = reasons.length > 0 ? reasons.map((reason) => `- ${reason}`).join("\n") : "- none";
|
|
362
|
+
const triage = contract.blocker_triage;
|
|
363
|
+
const triageBlock = triage
|
|
364
|
+
? `blocker_triage_category: ${triage.category}
|
|
365
|
+
blocker_triage_self_recoverable: ${triage.self_recoverable}
|
|
366
|
+
blocker_triage_recovery_attempted: ${triage.recovery_attempted}
|
|
367
|
+
blocker_triage_next_action: ${triage.next_action}`
|
|
368
|
+
: `blocker_triage_category: none
|
|
369
|
+
blocker_triage_self_recoverable: false
|
|
370
|
+
blocker_triage_recovery_attempted: false
|
|
371
|
+
blocker_triage_next_action: no blocker remains`;
|
|
372
|
+
const auditLine = contract.completion_output_status === "accept"
|
|
373
|
+
? "Final-gate accepted the current attempt."
|
|
374
|
+
: "Audit workflow completed; acceptance target not complete.";
|
|
330
375
|
return `# Final Summary
|
|
331
376
|
|
|
332
|
-
|
|
333
|
-
product_goal_complete: ${
|
|
377
|
+
diagnostic_overall_status: ${verdict.overall_status}
|
|
378
|
+
product_goal_complete: ${contract.product_goal_complete}
|
|
379
|
+
acceptance_target_status: ${contract.acceptance_target_status}
|
|
380
|
+
completion_output_status: ${contract.completion_output_status}
|
|
381
|
+
final_answer_allowed: ${contract.final_answer_allowed}
|
|
382
|
+
required_user_visible_status: ${contract.required_user_visible_status}
|
|
383
|
+
exit_code: ${contract.exit_code}
|
|
384
|
+
${triageBlock}
|
|
385
|
+
|
|
386
|
+
Final answer: ${contract.final_answer}
|
|
387
|
+
|
|
388
|
+
${auditLine}
|
|
389
|
+
|
|
390
|
+
Reasons:
|
|
391
|
+
${reasonBlock}
|
|
334
392
|
`;
|
|
335
393
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type IgnoredUnregisteredEvidence } from "./superpowers-task-unregistered-evidence.js";
|
|
1
2
|
import { type SuperpowersTaskState } from "./superpowers-task-state-schema.js";
|
|
2
3
|
export interface TrustedEvidenceKernelResult {
|
|
3
4
|
product_goal_complete: boolean;
|
|
@@ -6,7 +7,13 @@ export interface TrustedEvidenceKernelResult {
|
|
|
6
7
|
ac_statuses: Record<string, string>;
|
|
7
8
|
pi_statuses: Record<string, string>;
|
|
8
9
|
stale_evidence_ids: string[];
|
|
10
|
+
ignored_unregistered_evidence: IgnoredUnregisteredEvidence[];
|
|
11
|
+
invalidated_evidence_ids: string[];
|
|
12
|
+
kernel_order: string[];
|
|
13
|
+
ac_findings: Record<string, string[]>;
|
|
14
|
+
pi_findings: Record<string, string[]>;
|
|
9
15
|
harness_task_final_verdict?: "passed" | "failed";
|
|
10
16
|
}
|
|
17
|
+
export declare const TRUSTED_EVIDENCE_KERNEL_ORDER: readonly ["load_three_inputs", "recompute_source_hashes", "load_task_state", "resolve_current_attempt", "load_required_command_specs", "load_command_run_records", "load_registered_evidence_records", "discard_stale_evidence", "scan_unregistered_assertion_json", "scan_contradictions", "run_ac010_bootstrap_prevention", "run_under_specified_ac_checks", "run_harness_drift_lock", "run_protected_baseline_guard", "validate_scope_conflicts", "recompute_every_ac", "recompute_every_pi", "recompute_acceptance_target_status", "recompute_product_goal_complete", "regenerate_derived", "append_final_gate_event"];
|
|
11
18
|
export declare function evaluateTrustedEvidenceKernel(workdir: string, providedState?: SuperpowersTaskState): Promise<TrustedEvidenceKernelResult>;
|
|
12
19
|
export declare function applyTrustedEvidenceKernelResult(state: SuperpowersTaskState, result: TrustedEvidenceKernelResult): void;
|
|
@@ -1,17 +1,45 @@
|
|
|
1
|
-
import { deriveRequiredCommandSpecs
|
|
1
|
+
import { deriveRequiredCommandSpecs } from "./superpowers-task-command-specs.js";
|
|
2
|
+
import { validateCommandRunsForSpec, validateRequiredCommandCorrelation } from "./superpowers-task-command-run-correlation.js";
|
|
2
3
|
import { evaluateAc010Bootstrap } from "./superpowers-task-ac010.js";
|
|
3
4
|
import { evaluateCurrentAttemptArtifact } from "./superpowers-task-current-evidence.js";
|
|
4
5
|
import { scanSuperpowersContradictions } from "./superpowers-task-contradictions.js";
|
|
5
6
|
import { detectHarnessDrift } from "./superpowers-task-harness-drift.js";
|
|
6
7
|
import { evaluateProtectedBaseline } from "./superpowers-task-protected-baseline.js";
|
|
8
|
+
import { validateScopeConflicts } from "./superpowers-task-delivery.js";
|
|
7
9
|
import { evaluateProofLayerAssertions, isMachineVerifiableLayer } from "./superpowers-task-assertions.js";
|
|
10
|
+
import { scanUnregisteredAssertionEvidence } from "./superpowers-task-unregistered-evidence.js";
|
|
8
11
|
import { findUnderSpecifiedAcs } from "./superpowers-task-under-specified.js";
|
|
9
12
|
import { loadSuperpowersState, sourceRecords } from "./superpowers-task-state.js";
|
|
13
|
+
export const TRUSTED_EVIDENCE_KERNEL_ORDER = [
|
|
14
|
+
"load_three_inputs",
|
|
15
|
+
"recompute_source_hashes",
|
|
16
|
+
"load_task_state",
|
|
17
|
+
"resolve_current_attempt",
|
|
18
|
+
"load_required_command_specs",
|
|
19
|
+
"load_command_run_records",
|
|
20
|
+
"load_registered_evidence_records",
|
|
21
|
+
"discard_stale_evidence",
|
|
22
|
+
"scan_unregistered_assertion_json",
|
|
23
|
+
"scan_contradictions",
|
|
24
|
+
"run_ac010_bootstrap_prevention",
|
|
25
|
+
"run_under_specified_ac_checks",
|
|
26
|
+
"run_harness_drift_lock",
|
|
27
|
+
"run_protected_baseline_guard",
|
|
28
|
+
"validate_scope_conflicts",
|
|
29
|
+
"recompute_every_ac",
|
|
30
|
+
"recompute_every_pi",
|
|
31
|
+
"recompute_acceptance_target_status",
|
|
32
|
+
"recompute_product_goal_complete",
|
|
33
|
+
"regenerate_derived",
|
|
34
|
+
"append_final_gate_event"
|
|
35
|
+
];
|
|
10
36
|
export async function evaluateTrustedEvidenceKernel(workdir, providedState) {
|
|
11
37
|
const state = providedState ?? (await loadSuperpowersState(workdir));
|
|
12
38
|
const errors = [];
|
|
13
39
|
const acStatuses = {};
|
|
40
|
+
const acFindings = {};
|
|
14
41
|
const staleEvidenceIds = new Set();
|
|
42
|
+
const invalidatedEvidenceIds = new Set();
|
|
15
43
|
const attempt = currentAttempt(state);
|
|
16
44
|
const currentSources = await sourceRecords(workdir);
|
|
17
45
|
if (!attempt) {
|
|
@@ -21,7 +49,9 @@ export async function evaluateTrustedEvidenceKernel(workdir, providedState) {
|
|
|
21
49
|
validateAttemptAgainstSources(state, attempt, currentSources, errors);
|
|
22
50
|
}
|
|
23
51
|
const expectedSpecs = deriveRequiredCommandSpecs(state);
|
|
24
|
-
|
|
52
|
+
const commandCorrelation = validateRequiredCommandCorrelation(state, attempt, expectedSpecs);
|
|
53
|
+
errors.push(...commandCorrelation.errors);
|
|
54
|
+
commandCorrelation.invalidated_evidence_ids.forEach((id) => invalidatedEvidenceIds.add(id));
|
|
25
55
|
const underSpecified = new Map(findUnderSpecifiedAcs(state).map((item) => [item.ac_id, item.reasons]));
|
|
26
56
|
for (const reasons of underSpecified.values()) {
|
|
27
57
|
errors.push(...reasons);
|
|
@@ -32,11 +62,15 @@ export async function evaluateTrustedEvidenceKernel(workdir, providedState) {
|
|
|
32
62
|
errors.push(...drift.errors);
|
|
33
63
|
const baseline = evaluateProtectedBaseline(state);
|
|
34
64
|
errors.push(...baseline.errors);
|
|
65
|
+
validateScopeConflicts(state, errors);
|
|
66
|
+
const unregistered = await scanUnregisteredAssertionEvidence(workdir, state);
|
|
67
|
+
errors.push(...unregistered.errors);
|
|
35
68
|
const evidenceById = new Map((state.evidence ?? []).map((evidence) => [evidence.evidence_id, evidence]));
|
|
36
69
|
for (const [acId, ac] of Object.entries(state.graph?.acceptance_criteria ?? {})) {
|
|
37
70
|
const acErrors = [];
|
|
38
71
|
if (underSpecified.has(acId)) {
|
|
39
72
|
acStatuses[acId] = "under_specified";
|
|
73
|
+
acFindings[acId] = underSpecified.get(acId) ?? [];
|
|
40
74
|
continue;
|
|
41
75
|
}
|
|
42
76
|
const requiredLayers = ac.required_proof_layers ?? [];
|
|
@@ -65,6 +99,9 @@ export async function evaluateTrustedEvidenceKernel(workdir, providedState) {
|
|
|
65
99
|
}
|
|
66
100
|
acErrors.push(...validateEvidenceAgainstSpec(state, evidence, spec, layerId));
|
|
67
101
|
acErrors.push(...(await evaluateCurrentAttemptArtifact(workdir, evidence, layerId)));
|
|
102
|
+
if (invalidatedEvidenceIds.has(evidence.evidence_id)) {
|
|
103
|
+
acErrors.push(`${layerId} evidence ${evidence.evidence_id} invalidated by newer failed command`);
|
|
104
|
+
}
|
|
68
105
|
if (isStaleEvidenceError(acErrors)) {
|
|
69
106
|
staleEvidenceIds.add(evidence.evidence_id);
|
|
70
107
|
}
|
|
@@ -73,6 +110,7 @@ export async function evaluateTrustedEvidenceKernel(workdir, providedState) {
|
|
|
73
110
|
}
|
|
74
111
|
errors.push(...acErrors);
|
|
75
112
|
acStatuses[acId] = statusForAcErrors(acErrors, requiredLayers.length);
|
|
113
|
+
acFindings[acId] = acErrors;
|
|
76
114
|
}
|
|
77
115
|
const ac010 = evaluateAc010Bootstrap(state, acStatuses);
|
|
78
116
|
for (const acId of ac010.invalidated_ac_ids) {
|
|
@@ -80,6 +118,7 @@ export async function evaluateTrustedEvidenceKernel(workdir, providedState) {
|
|
|
80
118
|
}
|
|
81
119
|
errors.push(...ac010.errors);
|
|
82
120
|
const piStatuses = recomputePlanStatuses(state, acStatuses);
|
|
121
|
+
const piFindings = planFindings(state, acStatuses, piStatuses);
|
|
83
122
|
const allAcsComplete = Object.keys(state.graph?.acceptance_criteria ?? {}).length > 0 &&
|
|
84
123
|
Object.values(acStatuses).every((status) => status === "complete" || status === "out_of_scope_NA");
|
|
85
124
|
const allPisComplete = Object.keys(state.graph?.plan_items ?? {}).length > 0 &&
|
|
@@ -93,6 +132,11 @@ export async function evaluateTrustedEvidenceKernel(workdir, providedState) {
|
|
|
93
132
|
ac_statuses: acStatuses,
|
|
94
133
|
pi_statuses: piStatuses,
|
|
95
134
|
stale_evidence_ids: [...staleEvidenceIds],
|
|
135
|
+
ignored_unregistered_evidence: unregistered.ignored,
|
|
136
|
+
invalidated_evidence_ids: [...invalidatedEvidenceIds],
|
|
137
|
+
kernel_order: [...TRUSTED_EVIDENCE_KERNEL_ORDER],
|
|
138
|
+
ac_findings: acFindings,
|
|
139
|
+
pi_findings: piFindings,
|
|
96
140
|
harness_task_final_verdict: drift.harness_task_final_verdict
|
|
97
141
|
};
|
|
98
142
|
}
|
|
@@ -122,24 +166,13 @@ export function applyTrustedEvidenceKernelResult(state, result) {
|
|
|
122
166
|
state.gates.final_gate = {
|
|
123
167
|
status: result.product_goal_complete ? "pass" : result.acceptance_target_status,
|
|
124
168
|
kernel: "trusted_evidence_kernel",
|
|
125
|
-
order:
|
|
126
|
-
"load_three_inputs",
|
|
127
|
-
"recompute_source_hashes",
|
|
128
|
-
"load_task_state",
|
|
129
|
-
"load_current_attempt",
|
|
130
|
-
"load_command_run_records",
|
|
131
|
-
"load_registered_evidence_records",
|
|
132
|
-
"discard_stale_evidence",
|
|
133
|
-
"contradiction_scan",
|
|
134
|
-
"recompute_every_ac",
|
|
135
|
-
"recompute_every_pi",
|
|
136
|
-
"recompute_acceptance_target_status",
|
|
137
|
-
"recompute_product_goal_complete",
|
|
138
|
-
"regenerate_derived",
|
|
139
|
-
"append_event"
|
|
140
|
-
],
|
|
169
|
+
order: result.kernel_order,
|
|
141
170
|
errors: result.errors,
|
|
142
171
|
stale_evidence_ids: result.stale_evidence_ids,
|
|
172
|
+
ignored_unregistered_evidence: result.ignored_unregistered_evidence,
|
|
173
|
+
invalidated_evidence_ids: result.invalidated_evidence_ids,
|
|
174
|
+
ac_findings: result.ac_findings,
|
|
175
|
+
pi_findings: result.pi_findings,
|
|
143
176
|
harness_task_final_verdict: result.harness_task_final_verdict,
|
|
144
177
|
next_required_actions: state.final.next_required_actions
|
|
145
178
|
};
|
|
@@ -183,57 +216,6 @@ function validateAttemptAgainstSources(state, attempt, currentSources, errors) {
|
|
|
183
216
|
}
|
|
184
217
|
}
|
|
185
218
|
}
|
|
186
|
-
function validateRequiredSpecs(state, attempt, expectedSpecs, errors) {
|
|
187
|
-
const expectedByAc = new Map(expectedSpecs.map((spec) => [spec.ac_id, spec]));
|
|
188
|
-
for (const [acId, ac] of Object.entries(state.graph?.acceptance_criteria ?? {})) {
|
|
189
|
-
if (ac.machine_blocking !== true && ac.assertion_result_required !== true) {
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
const expected = expectedByAc.get(acId);
|
|
193
|
-
const actual = (state.required_command_specs ?? []).find((spec) => spec.ac_id === acId);
|
|
194
|
-
if (!expected || !actual) {
|
|
195
|
-
errors.push(`${acId} missing required_command_spec`);
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
if (actual.command_spec_id !== expected.command_spec_id) {
|
|
199
|
-
errors.push(`${acId} command_spec_id mismatch; required command specs must be recompiled from Acceptance Checklist`);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
if (attempt) {
|
|
203
|
-
const specsHash = requiredCommandSpecsHash(state.required_command_specs ?? []);
|
|
204
|
-
if (attempt.required_command_specs_hash !== specsHash) {
|
|
205
|
-
errors.push("required_command_specs_hash mismatch for current attempt");
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
function validateCommandRunsForSpec(state, attempt, spec) {
|
|
210
|
-
const errors = [];
|
|
211
|
-
for (const proofLayer of spec.proof_layers.filter((layer) => isMachineVerifiableLayer(`${spec.ac_id}.${layer}`))) {
|
|
212
|
-
const run = (state.command_runs ?? []).find((item) => item.task_attempt_id === state.current_attempt_id &&
|
|
213
|
-
item.command_spec_id === spec.command_spec_id &&
|
|
214
|
-
item.ac_id === spec.ac_id &&
|
|
215
|
-
item.proof_layer === proofLayer);
|
|
216
|
-
if (!run) {
|
|
217
|
-
errors.push(`${spec.ac_id}.${proofLayer} missing current attempt command-run record for command_spec_id ${spec.command_spec_id}`);
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
errors.push(...validateCommandRun(run, attempt));
|
|
221
|
-
}
|
|
222
|
-
return errors;
|
|
223
|
-
}
|
|
224
|
-
function validateCommandRun(run, attempt) {
|
|
225
|
-
const errors = [];
|
|
226
|
-
if (attempt && run.task_attempt_id !== attempt.task_attempt_id) {
|
|
227
|
-
errors.push(`${run.command_run_id} stale command run from ${run.task_attempt_id}; expected ${attempt.task_attempt_id}`);
|
|
228
|
-
}
|
|
229
|
-
if (run.exit_code !== 0) {
|
|
230
|
-
errors.push(`${run.command_run_id} command_exit_code=${run.exit_code}; expected 0`);
|
|
231
|
-
}
|
|
232
|
-
if (!run.command_line.trim()) {
|
|
233
|
-
errors.push(`${run.command_run_id} missing command_line`);
|
|
234
|
-
}
|
|
235
|
-
return errors;
|
|
236
|
-
}
|
|
237
219
|
function validateEvidenceAgainstSpec(state, evidence, spec, layerId) {
|
|
238
220
|
if (!spec) {
|
|
239
221
|
return [];
|
|
@@ -327,12 +309,23 @@ function recomputePlanStatuses(state, acStatuses) {
|
|
|
327
309
|
}
|
|
328
310
|
return statuses;
|
|
329
311
|
}
|
|
312
|
+
function planFindings(state, acStatuses, piStatuses) {
|
|
313
|
+
const findings = {};
|
|
314
|
+
for (const [planId, item] of Object.entries(state.graph?.plan_items ?? {})) {
|
|
315
|
+
const related = item.related_acs ?? [];
|
|
316
|
+
findings[planId] = related
|
|
317
|
+
.filter((acId) => acStatuses[acId] && acStatuses[acId] !== "complete" && acStatuses[acId] !== "out_of_scope_NA")
|
|
318
|
+
.map((acId) => `${acId} status=${acStatuses[acId]}`)
|
|
319
|
+
.concat(piStatuses[planId] ? [`${planId} status=${piStatuses[planId]}`] : []);
|
|
320
|
+
}
|
|
321
|
+
return findings;
|
|
322
|
+
}
|
|
330
323
|
function statusForGlobalErrors(errors, acStatuses) {
|
|
331
324
|
const text = errors.join("\n");
|
|
332
325
|
if (Object.values(acStatuses).includes("under_specified") || /under_specified/i.test(text)) {
|
|
333
326
|
return "under_specified";
|
|
334
327
|
}
|
|
335
|
-
if (/harness_drift|protected_baseline|source hash mismatch|missing current attempt|required_command_specs_hash|harness_task_missing/i.test(text)) {
|
|
328
|
+
if (/harness_drift|protected_baseline|source hash mismatch|missing current attempt|required_command_specs_hash|harness_task_missing|scope_conflict_requires_decision/i.test(text)) {
|
|
336
329
|
return "blocked";
|
|
337
330
|
}
|
|
338
331
|
if (/stale|failed|invalid|contradiction|negative evidence|forbidden|bootstrap/i.test(text)) {
|
|
@@ -341,7 +334,10 @@ function statusForGlobalErrors(errors, acStatuses) {
|
|
|
341
334
|
return "partial";
|
|
342
335
|
}
|
|
343
336
|
function currentAttempt(state) {
|
|
344
|
-
|
|
337
|
+
if (!state.current_attempt_id) {
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
return (state.attempts ?? []).find((item) => item.task_attempt_id === state.current_attempt_id);
|
|
345
341
|
}
|
|
346
342
|
function isStaleEvidenceError(errors) {
|
|
347
343
|
return errors.some((error) => /stale evidence|source_bundle_hash mismatch|artifact_sha256 mismatch|artifact_mtime/i.test(error));
|
|
@@ -35,6 +35,7 @@ export async function runSuperpowersAssertion(workdir, options) {
|
|
|
35
35
|
command_line: commandLine,
|
|
36
36
|
exit_code: exitCode,
|
|
37
37
|
started_at: startedAt,
|
|
38
|
+
completed_at: endedAt,
|
|
38
39
|
ended_at: endedAt,
|
|
39
40
|
artifact_paths: []
|
|
40
41
|
};
|
|
@@ -81,11 +82,16 @@ export async function recordSuperpowersEvidence(workdir, options) {
|
|
|
81
82
|
schema_version: "evidence-record-v2",
|
|
82
83
|
evidence_id: `EV2-${compactDate(new Date().toISOString())}-${sha256(options.commandRunId + artifactText).slice(0, 8)}`,
|
|
83
84
|
task_attempt_id: commandRun.task_attempt_id,
|
|
85
|
+
generated_at: new Date().toISOString(),
|
|
84
86
|
source_bundle_hash: attempt?.source_bundle_hash ?? computeSourceBundleHash(state),
|
|
85
87
|
product_source_hash: attempt?.product_source_hash ?? state.sources.product_architecture_source?.sha256 ?? "",
|
|
86
88
|
technical_plan_hash: attempt?.technical_plan_hash ?? state.sources.technical_realization_plan?.sha256 ?? "",
|
|
87
89
|
acceptance_checklist_hash: attempt?.acceptance_checklist_hash ?? state.sources.acceptance_checklist?.sha256 ?? "",
|
|
88
90
|
git_head: attempt?.git_head ?? "",
|
|
91
|
+
git_status_short: attempt?.git_status_short ?? "",
|
|
92
|
+
tracked_diff_hash: attempt?.tracked_diff_hash ?? "",
|
|
93
|
+
relevant_untracked_hash: attempt?.relevant_untracked_hash ?? "",
|
|
94
|
+
covers_dirty_worktree: Boolean(attempt?.git_status_short?.trim()),
|
|
89
95
|
worktree_fingerprint: attempt?.worktree_fingerprint ?? "",
|
|
90
96
|
command_spec_id: commandRun.command_spec_id,
|
|
91
97
|
command_run_id: commandRun.command_run_id,
|
|
@@ -99,7 +105,7 @@ export async function recordSuperpowersEvidence(workdir, options) {
|
|
|
99
105
|
target_proof_layers: [layerId],
|
|
100
106
|
slice_id: String(artifactRecord.slice_id ?? "attempt-evidence"),
|
|
101
107
|
type: String(artifactRecord.type ?? `${commandRun.proof_layer}_assertion`),
|
|
102
|
-
freshness: { created_at: commandRun.ended_at, valid_for: "current_attempt", stale_after: null },
|
|
108
|
+
freshness: { created_at: commandRun.completed_at ?? commandRun.ended_at, valid_for: "current_attempt", stale_after: null },
|
|
103
109
|
command: commandRun.command_line,
|
|
104
110
|
artifact_paths: [relativeArtifactPath],
|
|
105
111
|
proves: [layerId],
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type CompletionOutputContract } from "./superpowers-task-completion-output.js";
|
|
2
|
+
import { type SuperpowersTaskState } from "./superpowers-task-state-schema.js";
|
|
3
|
+
export declare function renderFinalCard(contract: CompletionOutputContract, state: SuperpowersTaskState): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function renderFinalCard(contract, state) {
|
|
2
|
+
const reasons = contract.completion_output_status === "blocked" ? contract.blocked_reasons : contract.rejection_reasons;
|
|
3
|
+
const reasonBlock = reasons.length > 0 ? reasons.map((reason) => `- ${reason}`).join("\n") : "- none";
|
|
4
|
+
const triage = contract.blocker_triage;
|
|
5
|
+
const triageBlock = triage
|
|
6
|
+
? `blocker_triage_category: ${triage.category}
|
|
7
|
+
blocker_triage_self_recoverable: ${triage.self_recoverable}
|
|
8
|
+
blocker_triage_recovery_attempted: ${triage.recovery_attempted}
|
|
9
|
+
blocker_triage_next_action: ${triage.next_action}`
|
|
10
|
+
: `blocker_triage_category: none
|
|
11
|
+
blocker_triage_self_recoverable: false
|
|
12
|
+
blocker_triage_recovery_attempted: false
|
|
13
|
+
blocker_triage_next_action: no blocker remains`;
|
|
14
|
+
const gate = contract.completion_output_status === "accept" ? "Final answer: accept" : `Final answer: ${contract.completion_output_status}`;
|
|
15
|
+
const auditLine = contract.completion_output_status === "accept"
|
|
16
|
+
? "Final-gate accepted the current attempt."
|
|
17
|
+
: "Audit workflow completed; acceptance target not complete.";
|
|
18
|
+
return `# Final Card
|
|
19
|
+
|
|
20
|
+
completion_output_status: ${contract.completion_output_status}
|
|
21
|
+
${gate}
|
|
22
|
+
required_user_visible_status: ${contract.required_user_visible_status}
|
|
23
|
+
final_answer_allowed: ${contract.final_answer_allowed}
|
|
24
|
+
exit_code: ${contract.exit_code}
|
|
25
|
+
product_goal_complete: ${contract.product_goal_complete}
|
|
26
|
+
acceptance_target_status: ${contract.acceptance_target_status}
|
|
27
|
+
audit_task_complete: ${state.final.audit_task_complete}
|
|
28
|
+
${triageBlock}
|
|
29
|
+
|
|
30
|
+
${auditLine}
|
|
31
|
+
|
|
32
|
+
Reasons:
|
|
33
|
+
${reasonBlock}
|
|
34
|
+
`;
|
|
35
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CompletionOutputContract } from "./superpowers-task-completion-output.js";
|
|
1
2
|
export declare function runSliceGate(workdir: string, sliceId: string): Promise<{
|
|
2
3
|
passed: boolean;
|
|
3
4
|
messages: string[];
|
|
@@ -6,7 +7,6 @@ export declare function runEpochGate(workdir: string, epochId: string): Promise<
|
|
|
6
7
|
passed: boolean;
|
|
7
8
|
messages: string[];
|
|
8
9
|
}>;
|
|
9
|
-
export declare function runFinalGate(workdir: string): Promise<{
|
|
10
|
-
product_goal_complete: boolean;
|
|
10
|
+
export declare function runFinalGate(workdir: string): Promise<CompletionOutputContract & {
|
|
11
11
|
errors: string[];
|
|
12
12
|
}>;
|