project-tiny-context-harness 0.2.83 → 0.2.84
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 +5 -3
- package/assets/skills/composite-long-task-workflow/SKILL.md +11 -5
- package/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md +14 -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 +39 -25
- package/dist/commands/composite-long-task.js +15 -3
- 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 +52 -0
- package/dist/lib/superpowers-task-completion-output.js +228 -0
- package/dist/lib/superpowers-task-current-evidence.js +22 -0
- package/dist/lib/superpowers-task-derive.js +55 -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 +24 -0
- package/dist/lib/superpowers-task-gates.d.ts +2 -2
- package/dist/lib/superpowers-task-gates.js +67 -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 +17 -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 +38 -0
- package/package.json +69 -69
|
@@ -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,24 @@
|
|
|
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 gate = contract.completion_output_status === "accept" ? "Final answer: accept" : `Final answer: ${contract.completion_output_status}`;
|
|
5
|
+
const auditLine = contract.completion_output_status === "accept"
|
|
6
|
+
? "Final-gate accepted the current attempt."
|
|
7
|
+
: "Audit workflow completed; acceptance target not complete.";
|
|
8
|
+
return `# Final Card
|
|
9
|
+
|
|
10
|
+
completion_output_status: ${contract.completion_output_status}
|
|
11
|
+
${gate}
|
|
12
|
+
required_user_visible_status: ${contract.required_user_visible_status}
|
|
13
|
+
final_answer_allowed: ${contract.final_answer_allowed}
|
|
14
|
+
exit_code: ${contract.exit_code}
|
|
15
|
+
product_goal_complete: ${contract.product_goal_complete}
|
|
16
|
+
acceptance_target_status: ${contract.acceptance_target_status}
|
|
17
|
+
audit_task_complete: ${state.final.audit_task_complete}
|
|
18
|
+
|
|
19
|
+
${auditLine}
|
|
20
|
+
|
|
21
|
+
Reasons:
|
|
22
|
+
${reasonBlock}
|
|
23
|
+
`;
|
|
24
|
+
}
|
|
@@ -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
|
}>;
|
|
@@ -2,7 +2,9 @@ import { appendSuperpowersEvent } from "./superpowers-task-events.js";
|
|
|
2
2
|
import { deriveSuperpowersArtifacts } from "./superpowers-task-derive.js";
|
|
3
3
|
import { validatePlanAcceptance } from "./plan-acceptance-validator.js";
|
|
4
4
|
import { loadSuperpowersState, saveSuperpowersState } from "./superpowers-task-state.js";
|
|
5
|
+
import { applyCompletionOutputContract, completionPhraseFindingMessages, resolveCompletionOutputStatus, scanGeneratedCompletionOutputSurfaces } from "./superpowers-task-completion-output.js";
|
|
5
6
|
import { applyTrustedEvidenceKernelResult, evaluateTrustedEvidenceKernel } from "./superpowers-task-evidence-kernel.js";
|
|
7
|
+
import { isRecord } from "./superpowers-task-state-schema.js";
|
|
6
8
|
import { validateSuperpowersState } from "./superpowers-task-validator.js";
|
|
7
9
|
export async function runSliceGate(workdir, sliceId) {
|
|
8
10
|
const state = await loadSuperpowersState(workdir);
|
|
@@ -33,16 +35,20 @@ export async function runFinalGate(workdir) {
|
|
|
33
35
|
const report = await validateSuperpowersState(workdir, [workdir]);
|
|
34
36
|
const acceptanceReport = await validatePlanAcceptance(workdir, [workdir]);
|
|
35
37
|
const latest = await loadSuperpowersState(workdir);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
let errors = [...new Set([...kernel.errors, ...report.errors, ...acceptanceReport.errors])];
|
|
39
|
+
let complete = kernel.product_goal_complete && errors.length === 0;
|
|
38
40
|
const acceptanceStatus = complete ? "complete" : acceptanceStatusForErrors(errors, kernel);
|
|
39
41
|
const nextRequiredActions = complete ? [] : nextActionsForErrors(errors);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
let contract = resolveCompletionOutputStatus({
|
|
43
|
+
final_gate_ran: true,
|
|
44
|
+
product_goal_complete: complete,
|
|
45
|
+
acceptance_target_status: acceptanceStatus,
|
|
46
|
+
audit_task_complete: true,
|
|
47
|
+
validator_errors: report.errors,
|
|
48
|
+
acceptance_validator_errors: acceptanceReport.errors,
|
|
49
|
+
rejection_reasons: complete ? [] : nextRequiredActions
|
|
50
|
+
});
|
|
51
|
+
applyCompletionOutputContract(latest, contract);
|
|
46
52
|
latest.final.completion_basis = complete
|
|
47
53
|
? ["trusted_evidence_kernel", "current_attempt_evidence", "negative_evidence_scan_passed", "harness_drift_lock_passed"]
|
|
48
54
|
: [];
|
|
@@ -51,31 +57,65 @@ export async function runFinalGate(workdir) {
|
|
|
51
57
|
latest.gates.final_gate = {
|
|
52
58
|
status: complete ? "pass" : acceptanceStatus,
|
|
53
59
|
kernel: "trusted_evidence_kernel",
|
|
54
|
-
order:
|
|
55
|
-
"load_three_inputs",
|
|
56
|
-
"recompute_source_hashes",
|
|
57
|
-
"load_task_state",
|
|
58
|
-
"load_current_attempt",
|
|
59
|
-
"load_command_run_records",
|
|
60
|
-
"load_registered_evidence_records",
|
|
61
|
-
"discard_stale_evidence",
|
|
62
|
-
"contradiction_scan",
|
|
63
|
-
"recompute_every_ac",
|
|
64
|
-
"recompute_every_pi",
|
|
65
|
-
"recompute_acceptance_target_status",
|
|
66
|
-
"recompute_product_goal_complete",
|
|
67
|
-
"regenerate_derived",
|
|
68
|
-
"append_event"
|
|
69
|
-
],
|
|
60
|
+
order: kernel.kernel_order,
|
|
70
61
|
errors,
|
|
71
62
|
stale_evidence_ids: kernel.stale_evidence_ids,
|
|
63
|
+
ignored_unregistered_evidence: kernel.ignored_unregistered_evidence,
|
|
64
|
+
invalidated_evidence_ids: kernel.invalidated_evidence_ids,
|
|
65
|
+
ac_findings: kernel.ac_findings,
|
|
66
|
+
pi_findings: kernel.pi_findings,
|
|
72
67
|
harness_task_final_verdict: kernel.harness_task_final_verdict,
|
|
73
|
-
next_required_actions: nextRequiredActions
|
|
68
|
+
next_required_actions: nextRequiredActions,
|
|
69
|
+
completion_output_status: contract.completion_output_status,
|
|
70
|
+
final_answer_allowed: contract.final_answer_allowed,
|
|
71
|
+
required_user_visible_status: contract.required_user_visible_status,
|
|
72
|
+
exit_code: contract.exit_code,
|
|
73
|
+
blocked_reasons: contract.blocked_reasons,
|
|
74
|
+
rejection_reasons: contract.rejection_reasons,
|
|
75
|
+
generated_output_mismatch: contract.generated_output_mismatch
|
|
74
76
|
};
|
|
75
77
|
await saveSuperpowersState(workdir, latest);
|
|
76
78
|
await deriveSuperpowersArtifacts(workdir);
|
|
77
|
-
await
|
|
78
|
-
|
|
79
|
+
const outputFindings = await scanGeneratedCompletionOutputSurfaces(workdir, contract);
|
|
80
|
+
if (outputFindings.length > 0) {
|
|
81
|
+
errors = [...new Set([...errors, ...completionPhraseFindingMessages(outputFindings)])];
|
|
82
|
+
complete = false;
|
|
83
|
+
contract = resolveCompletionOutputStatus({
|
|
84
|
+
final_gate_ran: true,
|
|
85
|
+
product_goal_complete: false,
|
|
86
|
+
acceptance_target_status: acceptanceStatusForErrors(errors, kernel),
|
|
87
|
+
audit_task_complete: true,
|
|
88
|
+
validator_errors: errors,
|
|
89
|
+
generated_output_mismatch: true
|
|
90
|
+
});
|
|
91
|
+
const blockedLatest = await loadSuperpowersState(workdir);
|
|
92
|
+
applyCompletionOutputContract(blockedLatest, contract);
|
|
93
|
+
blockedLatest.final.completion_basis = [];
|
|
94
|
+
blockedLatest.final.next_required_actions = nextActionsForErrors(errors);
|
|
95
|
+
blockedLatest.gates.validator = { status: "blocked", errors };
|
|
96
|
+
blockedLatest.gates.final_gate = {
|
|
97
|
+
...(isRecord(blockedLatest.gates.final_gate) ? blockedLatest.gates.final_gate : {}),
|
|
98
|
+
status: contract.completion_output_status,
|
|
99
|
+
errors,
|
|
100
|
+
next_required_actions: blockedLatest.final.next_required_actions,
|
|
101
|
+
completion_output_status: contract.completion_output_status,
|
|
102
|
+
final_answer_allowed: contract.final_answer_allowed,
|
|
103
|
+
required_user_visible_status: contract.required_user_visible_status,
|
|
104
|
+
exit_code: contract.exit_code,
|
|
105
|
+
blocked_reasons: contract.blocked_reasons,
|
|
106
|
+
rejection_reasons: contract.rejection_reasons,
|
|
107
|
+
generated_output_mismatch: contract.generated_output_mismatch,
|
|
108
|
+
false_completion_phrase_findings: outputFindings
|
|
109
|
+
};
|
|
110
|
+
blockedLatest.final.false_completion_phrase_findings = outputFindings;
|
|
111
|
+
await saveSuperpowersState(workdir, blockedLatest);
|
|
112
|
+
await deriveSuperpowersArtifacts(workdir);
|
|
113
|
+
}
|
|
114
|
+
await appendSuperpowersEvent(workdir, "final_gate", {
|
|
115
|
+
product_goal_complete: contract.product_goal_complete,
|
|
116
|
+
completion_output_status: contract.completion_output_status
|
|
117
|
+
});
|
|
118
|
+
return { ...contract, errors };
|
|
79
119
|
}
|
|
80
120
|
function acceptanceStatusForErrors(errors, kernel) {
|
|
81
121
|
const text = errors.join("\n");
|
|
@@ -4,7 +4,8 @@ const HARNESS_PATH_PATTERNS = [
|
|
|
4
4
|
/(^|\/)playwright\.config\./i,
|
|
5
5
|
/(^|\/).*\.spec\.[cm]?[jt]sx?$/i,
|
|
6
6
|
/(^|\/).*\.test\.[cm]?[jt]sx?$/i,
|
|
7
|
-
/
|
|
7
|
+
/(^|\/)tests\/ty-context\/fixtures\/composite-long-task\//i,
|
|
8
|
+
/superpowers-task-(assertions|evidence|evidence-kernel|current-evidence|command-specs|command-run-correlation|unregistered-evidence|completion-output|final-card|ac010|gates|validator|derive|state|state-schema|state-shape|under-specified|protected-baseline|harness-drift)\.ts$/i,
|
|
8
9
|
/(^|\/)(\.codex\/ty-context-managed|packages\/ty-context\/assets)\/skills\/composite-long-task-workflow\//i,
|
|
9
10
|
/composite-long-task-workflow-protocol\.md$/i,
|
|
10
11
|
/(^|\/)Makefile$/i,
|
|
@@ -79,7 +80,10 @@ function harnessTaskFixtureVerdict(state, changedFiles) {
|
|
|
79
80
|
};
|
|
80
81
|
}
|
|
81
82
|
function currentAttempt(state) {
|
|
82
|
-
|
|
83
|
+
if (!state.current_attempt_id) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
return (state.attempts ?? []).find((item) => item.task_attempt_id === state.current_attempt_id);
|
|
83
87
|
}
|
|
84
88
|
function changedFilesFor(attempt) {
|
|
85
89
|
return [...new Set((attempt?.changed_files ?? []).map((file) => file.replace(/\\/g, "/")).filter(Boolean))];
|
|
@@ -5,15 +5,31 @@ export const PROTECTED_BASELINE_PATHS = [
|
|
|
5
5
|
".codex/ty-context-managed/protected-harness-baseline.json",
|
|
6
6
|
"packages/ty-context/source-mappings.yaml",
|
|
7
7
|
"packages/ty-context/src/lib/superpowers-task-gates.ts",
|
|
8
|
+
"packages/ty-context/src/lib/superpowers-task-completion-output.ts",
|
|
9
|
+
"packages/ty-context/src/lib/superpowers-task-final-card.ts",
|
|
8
10
|
"packages/ty-context/src/lib/superpowers-task-validator.ts",
|
|
9
11
|
"packages/ty-context/src/lib/superpowers-task-derive.ts",
|
|
10
12
|
"packages/ty-context/src/lib/superpowers-task-evidence.ts",
|
|
11
13
|
"packages/ty-context/src/lib/superpowers-task-evidence-kernel.ts",
|
|
14
|
+
"packages/ty-context/src/lib/superpowers-task-current-evidence.ts",
|
|
15
|
+
"packages/ty-context/src/lib/superpowers-task-command-run-correlation.ts",
|
|
16
|
+
"packages/ty-context/src/lib/superpowers-task-unregistered-evidence.ts",
|
|
17
|
+
"packages/ty-context/src/lib/superpowers-task-ac010.ts",
|
|
18
|
+
"packages/ty-context/src/lib/superpowers-task-harness-drift.ts",
|
|
19
|
+
"packages/ty-context/src/lib/superpowers-task-protected-baseline.ts",
|
|
12
20
|
"packages/ty-context/src/lib/superpowers-task-state-schema.ts",
|
|
13
21
|
".codex/ty-context-managed/skills/composite-long-task-workflow/SKILL.md",
|
|
14
22
|
".codex/ty-context-managed/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md",
|
|
23
|
+
".codex/ty-context-managed/skills/composite-long-task-workflow/assets/goal-objective.template.md",
|
|
24
|
+
".codex/ty-context-managed/skills/composite-long-task-workflow/assets/execution-binding.template.md",
|
|
15
25
|
"packages/ty-context/assets/skills/composite-long-task-workflow/SKILL.md",
|
|
16
|
-
"packages/ty-context/assets/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md"
|
|
26
|
+
"packages/ty-context/assets/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md",
|
|
27
|
+
"packages/ty-context/assets/skills/composite-long-task-workflow/assets/goal-objective.template.md",
|
|
28
|
+
"packages/ty-context/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md",
|
|
29
|
+
"tests/ty-context/composite-long-task-completion-output-gate.test.mjs",
|
|
30
|
+
"tests/ty-context/fixtures/composite-long-task/completion-output-gate/expected-outcomes.json",
|
|
31
|
+
"tests/ty-context/composite-long-task-false-completion-regression.test.mjs",
|
|
32
|
+
"tests/ty-context/fixtures/composite-long-task/false-completion-regression/manifest.json"
|
|
17
33
|
];
|
|
18
34
|
export function evaluateProtectedBaseline(state) {
|
|
19
35
|
const attempt = currentAttempt(state);
|
|
@@ -43,5 +59,8 @@ function protectedBaselineReason(state) {
|
|
|
43
59
|
return isRecord(baseline) && typeof baseline.reason === "string" && baseline.reason.trim().length > 0;
|
|
44
60
|
}
|
|
45
61
|
function currentAttempt(state) {
|
|
46
|
-
|
|
62
|
+
if (!state.current_attempt_id) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
return (state.attempts ?? []).find((item) => item.task_attempt_id === state.current_attempt_id);
|
|
47
66
|
}
|
|
@@ -76,6 +76,7 @@ export type SuperpowersProductDeliveryScope = "system_capability_build" | "repre
|
|
|
76
76
|
export type SuperpowersPlanDeliveryScope = "system_capability_build" | "representative_sample_validation" | "full_population_operation" | "out_of_scope_backlog";
|
|
77
77
|
export type SuperpowersAcceptanceScope = "system_capability_build" | "representative_sample_validation" | "full_population_operation" | "full_population_not_required";
|
|
78
78
|
export type SuperpowersScopeFitDecision = "fit_for_three_inputs" | "selected_from_split" | "blocked_for_decision" | "";
|
|
79
|
+
export type CompletionOutputStatus = "accept" | "reject" | "blocked";
|
|
79
80
|
export interface SuperpowersTaskState {
|
|
80
81
|
meta: {
|
|
81
82
|
task_id: string;
|
|
@@ -87,6 +88,7 @@ export interface SuperpowersTaskState {
|
|
|
87
88
|
product_goal_complete: boolean;
|
|
88
89
|
acceptance_target_status: string;
|
|
89
90
|
audit_task_complete: boolean;
|
|
91
|
+
completion_output_status?: CompletionOutputStatus;
|
|
90
92
|
};
|
|
91
93
|
sources: Record<string, SuperpowersSourceRecord>;
|
|
92
94
|
context: {
|
|
@@ -116,6 +118,15 @@ export interface SuperpowersTaskState {
|
|
|
116
118
|
product_goal_complete: boolean;
|
|
117
119
|
acceptance_target_status: string;
|
|
118
120
|
audit_task_complete: boolean;
|
|
121
|
+
completion_output_status?: CompletionOutputStatus;
|
|
122
|
+
final_answer_allowed?: boolean;
|
|
123
|
+
required_user_visible_status?: "accepted" | "rejected" | "blocked";
|
|
124
|
+
final_answer?: CompletionOutputStatus;
|
|
125
|
+
exit_code?: 0 | 1 | 2;
|
|
126
|
+
blocked_reasons?: string[];
|
|
127
|
+
rejection_reasons?: string[];
|
|
128
|
+
generated_output_mismatch?: boolean;
|
|
129
|
+
false_completion_phrase_findings?: unknown[];
|
|
119
130
|
completion_basis: string[];
|
|
120
131
|
next_required_actions?: string[];
|
|
121
132
|
};
|
|
@@ -162,6 +173,7 @@ export interface CommandRunRecord {
|
|
|
162
173
|
command_line: string;
|
|
163
174
|
exit_code: number;
|
|
164
175
|
started_at: string;
|
|
176
|
+
completed_at?: string;
|
|
165
177
|
ended_at: string;
|
|
166
178
|
artifact_paths: string[];
|
|
167
179
|
}
|
|
@@ -336,11 +348,16 @@ export interface SuperpowersEvidenceRecord {
|
|
|
336
348
|
schema_version?: "evidence-record-v1" | "evidence-record-v2" | string;
|
|
337
349
|
evidence_id: string;
|
|
338
350
|
task_attempt_id?: string;
|
|
351
|
+
generated_at?: string;
|
|
339
352
|
source_bundle_hash?: string;
|
|
340
353
|
product_source_hash?: string;
|
|
341
354
|
technical_plan_hash?: string;
|
|
342
355
|
acceptance_checklist_hash?: string;
|
|
343
356
|
git_head?: string;
|
|
357
|
+
git_status_short?: string;
|
|
358
|
+
tracked_diff_hash?: string;
|
|
359
|
+
relevant_untracked_hash?: string;
|
|
360
|
+
covers_dirty_worktree?: boolean;
|
|
344
361
|
worktree_fingerprint?: string;
|
|
345
362
|
command_spec_id?: string;
|
|
346
363
|
command_run_id?: string;
|
|
@@ -41,7 +41,8 @@ export async function initializeSuperpowersTask(workdir, options = {}) {
|
|
|
41
41
|
goal_type: options.goalType ?? "implementation",
|
|
42
42
|
product_goal_complete: false,
|
|
43
43
|
acceptance_target_status: "not_run",
|
|
44
|
-
audit_task_complete: false
|
|
44
|
+
audit_task_complete: false,
|
|
45
|
+
completion_output_status: "blocked"
|
|
45
46
|
},
|
|
46
47
|
sources: await sourceRecords(workdir),
|
|
47
48
|
context: {
|
|
@@ -88,6 +89,14 @@ export async function initializeSuperpowersTask(workdir, options = {}) {
|
|
|
88
89
|
product_goal_complete: false,
|
|
89
90
|
acceptance_target_status: "not_run",
|
|
90
91
|
audit_task_complete: false,
|
|
92
|
+
completion_output_status: "blocked",
|
|
93
|
+
final_answer_allowed: false,
|
|
94
|
+
required_user_visible_status: "blocked",
|
|
95
|
+
final_answer: "blocked",
|
|
96
|
+
exit_code: 2,
|
|
97
|
+
blocked_reasons: ["final_gate_not_run"],
|
|
98
|
+
rejection_reasons: [],
|
|
99
|
+
generated_output_mismatch: false,
|
|
91
100
|
completion_basis: [],
|
|
92
101
|
next_required_actions: []
|
|
93
102
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type SuperpowersTaskState } from "./superpowers-task-state-schema.js";
|
|
2
|
+
export interface IgnoredUnregisteredEvidence {
|
|
3
|
+
path: string;
|
|
4
|
+
status: string;
|
|
5
|
+
target_ac_ids: string[];
|
|
6
|
+
target_proof_layers: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function scanUnregisteredAssertionEvidence(workdir: string, state: SuperpowersTaskState): Promise<{
|
|
9
|
+
ignored: IgnoredUnregisteredEvidence[];
|
|
10
|
+
errors: string[];
|
|
11
|
+
}>;
|