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
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { isMachineVerifiableLayer } from "./superpowers-task-assertions.js";
|
|
2
|
+
import { normalizeProofLayerName } from "./superpowers-task-fields.js";
|
|
3
|
+
import { requiredCommandSpecsHash } from "./superpowers-task-command-specs.js";
|
|
4
|
+
export function validateRequiredCommandCorrelation(state, attempt, expectedSpecs) {
|
|
5
|
+
const errors = [];
|
|
6
|
+
const invalidated = new Set();
|
|
7
|
+
const expectedByAc = new Map(expectedSpecs.map((spec) => [spec.ac_id, spec]));
|
|
8
|
+
for (const [acId, ac] of Object.entries(state.graph?.acceptance_criteria ?? {})) {
|
|
9
|
+
if (ac.machine_blocking !== true && ac.assertion_result_required !== true) {
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
const expected = expectedByAc.get(acId);
|
|
13
|
+
const actual = (state.required_command_specs ?? []).find((spec) => spec.ac_id === acId);
|
|
14
|
+
if (!expected || !actual) {
|
|
15
|
+
errors.push(`${acId} missing required_command_spec`);
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (actual.command_spec_id !== expected.command_spec_id) {
|
|
19
|
+
errors.push(`${acId} command_spec_id mismatch; required command specs must be recompiled from Acceptance Checklist`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (attempt) {
|
|
23
|
+
const specsHash = requiredCommandSpecsHash(state.required_command_specs ?? []);
|
|
24
|
+
if (attempt.required_command_specs_hash !== specsHash) {
|
|
25
|
+
errors.push("required_command_specs_hash mismatch for current attempt");
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
for (const spec of expectedSpecs) {
|
|
29
|
+
errors.push(...validateCommandRunsForSpec(state, attempt, spec));
|
|
30
|
+
}
|
|
31
|
+
for (const finding of findFailedCommandInvalidations(state)) {
|
|
32
|
+
invalidated.add(finding.evidence_id);
|
|
33
|
+
errors.push(`${finding.evidence_id} newer failed command invalidates older passed evidence for ${finding.ac_id}.${finding.proof_layer}: ${finding.command_run_id}`);
|
|
34
|
+
}
|
|
35
|
+
return { errors: unique(errors), invalidated_evidence_ids: [...invalidated] };
|
|
36
|
+
}
|
|
37
|
+
export function validateCommandRunsForSpec(state, attempt, spec) {
|
|
38
|
+
const errors = [];
|
|
39
|
+
for (const proofLayer of spec.proof_layers.filter((layer) => isMachineVerifiableLayer(`${spec.ac_id}.${layer}`))) {
|
|
40
|
+
const run = (state.command_runs ?? []).find((item) => item.task_attempt_id === state.current_attempt_id &&
|
|
41
|
+
item.command_spec_id === spec.command_spec_id &&
|
|
42
|
+
item.ac_id === spec.ac_id &&
|
|
43
|
+
normalizeProofLayerName(item.proof_layer) === normalizeProofLayerName(proofLayer));
|
|
44
|
+
if (!run) {
|
|
45
|
+
errors.push(`${spec.ac_id}.${proofLayer} missing current attempt command-run record for command_spec_id ${spec.command_spec_id}`);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
errors.push(...validateCommandRun(run, attempt));
|
|
49
|
+
}
|
|
50
|
+
return errors;
|
|
51
|
+
}
|
|
52
|
+
export function validateCommandRun(run, attempt) {
|
|
53
|
+
const errors = [];
|
|
54
|
+
if (attempt && run.task_attempt_id !== attempt.task_attempt_id) {
|
|
55
|
+
errors.push(`${run.command_run_id} stale command run from ${run.task_attempt_id}; expected ${attempt.task_attempt_id}`);
|
|
56
|
+
}
|
|
57
|
+
if (run.exit_code !== 0) {
|
|
58
|
+
errors.push(`${run.command_run_id} command_exit_code=${run.exit_code}; expected 0`);
|
|
59
|
+
}
|
|
60
|
+
if (!run.command_line.trim()) {
|
|
61
|
+
errors.push(`${run.command_run_id} missing command_line`);
|
|
62
|
+
}
|
|
63
|
+
if (!run.started_at) {
|
|
64
|
+
errors.push(`${run.command_run_id} missing started_at`);
|
|
65
|
+
}
|
|
66
|
+
if (!run.completed_at) {
|
|
67
|
+
errors.push(`${run.command_run_id} missing completed_at`);
|
|
68
|
+
}
|
|
69
|
+
if (!run.task_attempt_id) {
|
|
70
|
+
errors.push(`${run.command_run_id} missing attempt_id`);
|
|
71
|
+
}
|
|
72
|
+
return errors;
|
|
73
|
+
}
|
|
74
|
+
function findFailedCommandInvalidations(state) {
|
|
75
|
+
const evidence = state.evidence ?? [];
|
|
76
|
+
const failedRuns = (state.command_runs ?? []).filter((run) => run.task_attempt_id === state.current_attempt_id && Number(run.exit_code) !== 0);
|
|
77
|
+
return failedRuns.flatMap((run) => evidence
|
|
78
|
+
.filter((item) => evidenceTargetsRunLayer(item, run) && evidencePredatesRun(item, run))
|
|
79
|
+
.map((item) => ({
|
|
80
|
+
evidence_id: item.evidence_id,
|
|
81
|
+
command_run_id: run.command_run_id,
|
|
82
|
+
ac_id: run.ac_id,
|
|
83
|
+
proof_layer: run.proof_layer
|
|
84
|
+
})));
|
|
85
|
+
}
|
|
86
|
+
function evidenceTargetsRunLayer(evidence, run) {
|
|
87
|
+
const layerId = `${run.ac_id}.${normalizeProofLayerName(run.proof_layer)}`;
|
|
88
|
+
const targetLayers = (evidence.target_proof_layers ?? evidence.assertion_result?.target_proof_layers ?? []).map((item) => item.includes(".") ? item : `${run.ac_id}.${normalizeProofLayerName(item)}`);
|
|
89
|
+
return (evidence.task_attempt_id === run.task_attempt_id &&
|
|
90
|
+
(evidence.target_ac_ids ?? evidence.assertion_result?.target_ac_ids ?? []).includes(run.ac_id) &&
|
|
91
|
+
(targetLayers.includes(layerId) || evidence.proves.includes(layerId)));
|
|
92
|
+
}
|
|
93
|
+
function evidencePredatesRun(evidence, run) {
|
|
94
|
+
const evidenceTime = Date.parse(evidence.generated_at ?? evidence.freshness?.created_at ?? evidence.artifact_mtime ?? "");
|
|
95
|
+
const runTime = Date.parse(run.started_at || run.completed_at || run.ended_at || "");
|
|
96
|
+
if (Number.isNaN(evidenceTime) || Number.isNaN(runTime)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
return evidenceTime <= runTime;
|
|
100
|
+
}
|
|
101
|
+
function unique(values) {
|
|
102
|
+
return [...new Set(values.filter(Boolean))];
|
|
103
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type SuperpowersTaskState } from "./superpowers-task-state-schema.js";
|
|
2
|
+
export type CompletionOutputStatus = "accept" | "reject" | "blocked";
|
|
3
|
+
export interface CompletionOutputContract {
|
|
4
|
+
product_goal_complete: boolean;
|
|
5
|
+
acceptance_target_status: string;
|
|
6
|
+
completion_output_status: CompletionOutputStatus;
|
|
7
|
+
final_answer_allowed: boolean;
|
|
8
|
+
required_user_visible_status: "accepted" | "rejected" | "blocked";
|
|
9
|
+
final_answer: CompletionOutputStatus;
|
|
10
|
+
exit_code: 0 | 1 | 2;
|
|
11
|
+
blocked_reasons: string[];
|
|
12
|
+
rejection_reasons: string[];
|
|
13
|
+
audit_task_complete: boolean;
|
|
14
|
+
final_gate_ran: boolean;
|
|
15
|
+
generated_output_mismatch: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface CompletionOutputResolveInput {
|
|
18
|
+
final_gate_ran?: boolean;
|
|
19
|
+
product_goal_complete?: boolean;
|
|
20
|
+
acceptance_target_status?: string;
|
|
21
|
+
audit_task_complete?: boolean;
|
|
22
|
+
validator_errors?: string[];
|
|
23
|
+
acceptance_validator_errors?: string[];
|
|
24
|
+
blocked_reasons?: string[];
|
|
25
|
+
rejection_reasons?: string[];
|
|
26
|
+
generated_output_mismatch?: boolean;
|
|
27
|
+
required_command_not_run?: boolean;
|
|
28
|
+
environment_unknown?: boolean;
|
|
29
|
+
state_unreadable?: boolean;
|
|
30
|
+
source_unreadable?: boolean;
|
|
31
|
+
kernel_invalid?: boolean;
|
|
32
|
+
required_validator_unavailable?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface CompletionOutputSurface {
|
|
35
|
+
surface: string;
|
|
36
|
+
text: string;
|
|
37
|
+
}
|
|
38
|
+
export interface CompletionPhraseFinding {
|
|
39
|
+
surface: string;
|
|
40
|
+
phrase: string;
|
|
41
|
+
line: number;
|
|
42
|
+
text: string;
|
|
43
|
+
}
|
|
44
|
+
export declare function resolveCompletionOutputStatus(input: CompletionOutputResolveInput): CompletionOutputContract;
|
|
45
|
+
export declare function completionOutputContractFromState(state: SuperpowersTaskState): CompletionOutputContract;
|
|
46
|
+
export declare function applyCompletionOutputContract(state: SuperpowersTaskState, contract: CompletionOutputContract): void;
|
|
47
|
+
export declare function scanFalseCompletionPhrases(input: {
|
|
48
|
+
completion_output_status: CompletionOutputStatus;
|
|
49
|
+
surfaces: CompletionOutputSurface[] | string;
|
|
50
|
+
}): CompletionPhraseFinding[];
|
|
51
|
+
export declare function scanGeneratedCompletionOutputSurfaces(workdir: string, contract: CompletionOutputContract): Promise<CompletionPhraseFinding[]>;
|
|
52
|
+
export declare function completionPhraseFindingMessages(findings: CompletionPhraseFinding[]): string[];
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { pathExists, readText } from "./fs.js";
|
|
3
|
+
import { asStringArray, isRecord } from "./superpowers-task-state-schema.js";
|
|
4
|
+
const BLOCKING_TARGET_STATUSES = new Set(["not_run", "blocked", "under_specified", "unknown"]);
|
|
5
|
+
const REJECTING_TARGET_STATUSES = new Set(["partial", "failed", "rejected", "invalidated", "not_accepted"]);
|
|
6
|
+
const GENERATED_COMPLETION_SURFACES = [
|
|
7
|
+
"derived/final-summary.md",
|
|
8
|
+
"derived/final-card.md",
|
|
9
|
+
"derived/final-acceptance-verdict.json",
|
|
10
|
+
"derived/final-acceptance-verdict.md",
|
|
11
|
+
"derived/plan-conformance-matrix.json",
|
|
12
|
+
"derived/plan-conformance-matrix.md",
|
|
13
|
+
"derived/local-audit.md",
|
|
14
|
+
"goal-objective.txt",
|
|
15
|
+
"execution-binding.md",
|
|
16
|
+
"workflow-protocol.md"
|
|
17
|
+
];
|
|
18
|
+
const FORBIDDEN_PHRASES = [
|
|
19
|
+
{ phrase: "update_goal(status=\"complete\")", pattern: /update_goal\s*\(\s*status\s*=\s*["']complete["']\s*\)/i },
|
|
20
|
+
{ phrase: "goal achieved", pattern: /\bgoal achieved\b/i },
|
|
21
|
+
{ phrase: "ready to merge", pattern: /\bready to merge\b/i },
|
|
22
|
+
{ phrase: "implementation complete", pattern: /\bimplementation complete\b/i },
|
|
23
|
+
{ phrase: "all passed", pattern: /\ball (?:acs?|acceptance criteria|checks|tests)?\s*passed\b/i },
|
|
24
|
+
{ phrase: "product complete", pattern: /\bproduct complete\b/i },
|
|
25
|
+
{ phrase: "completed", pattern: /\bcompleted\b/i },
|
|
26
|
+
{ phrase: "complete", pattern: /\bcomplete\b/i },
|
|
27
|
+
{ phrase: "accepted", pattern: /\baccepted\b/i },
|
|
28
|
+
{ phrase: "accept", pattern: /\baccept\b/i },
|
|
29
|
+
{ phrase: "done", pattern: /\bdone\b/i },
|
|
30
|
+
{ phrase: "success", pattern: /\bsuccess(?:ful)?\b/i },
|
|
31
|
+
{ phrase: "任务完成", pattern: /任务完成/ },
|
|
32
|
+
{ phrase: "已完成", pattern: /已完成/ },
|
|
33
|
+
{ phrase: "验收通过", pattern: /验收通过/ },
|
|
34
|
+
{ phrase: "目标完成", pattern: /目标完成/ },
|
|
35
|
+
{ phrase: "全部通过", pattern: /全部通过/ },
|
|
36
|
+
{ phrase: "可以合并", pattern: /可以合并/ }
|
|
37
|
+
];
|
|
38
|
+
export function resolveCompletionOutputStatus(input) {
|
|
39
|
+
const finalGateRan = input.final_gate_ran === true;
|
|
40
|
+
const productGoalComplete = input.product_goal_complete === true;
|
|
41
|
+
const acceptanceTargetStatus = normalizeAcceptanceTargetStatus(input.acceptance_target_status);
|
|
42
|
+
const validatorErrors = [...(input.validator_errors ?? []), ...(input.acceptance_validator_errors ?? [])].filter(Boolean);
|
|
43
|
+
const blockedReasons = [...(input.blocked_reasons ?? [])];
|
|
44
|
+
const rejectionReasons = [...(input.rejection_reasons ?? [])];
|
|
45
|
+
if (!finalGateRan) {
|
|
46
|
+
blockedReasons.push("final_gate_not_run");
|
|
47
|
+
}
|
|
48
|
+
if (input.required_command_not_run === true) {
|
|
49
|
+
blockedReasons.push("required_command_not_run");
|
|
50
|
+
}
|
|
51
|
+
if (input.environment_unknown === true) {
|
|
52
|
+
blockedReasons.push("environment_unknown");
|
|
53
|
+
}
|
|
54
|
+
if (input.state_unreadable === true) {
|
|
55
|
+
blockedReasons.push("state_unreadable");
|
|
56
|
+
}
|
|
57
|
+
if (input.source_unreadable === true) {
|
|
58
|
+
blockedReasons.push("source_unreadable");
|
|
59
|
+
}
|
|
60
|
+
if (input.kernel_invalid === true) {
|
|
61
|
+
blockedReasons.push("kernel_invalid");
|
|
62
|
+
}
|
|
63
|
+
if (input.required_validator_unavailable === true) {
|
|
64
|
+
blockedReasons.push("required_validator_unavailable");
|
|
65
|
+
}
|
|
66
|
+
if (input.generated_output_mismatch === true) {
|
|
67
|
+
blockedReasons.push("generated_output_mismatch");
|
|
68
|
+
}
|
|
69
|
+
if (BLOCKING_TARGET_STATUSES.has(acceptanceTargetStatus)) {
|
|
70
|
+
blockedReasons.push(`acceptance_target_status=${acceptanceTargetStatus}`);
|
|
71
|
+
}
|
|
72
|
+
if (REJECTING_TARGET_STATUSES.has(acceptanceTargetStatus)) {
|
|
73
|
+
rejectionReasons.push(`acceptance_target_status=${acceptanceTargetStatus}`);
|
|
74
|
+
}
|
|
75
|
+
if (finalGateRan && !productGoalComplete && !BLOCKING_TARGET_STATUSES.has(acceptanceTargetStatus)) {
|
|
76
|
+
rejectionReasons.push("product_goal_complete=false");
|
|
77
|
+
}
|
|
78
|
+
if (validatorErrors.length > 0) {
|
|
79
|
+
rejectionReasons.push(...validatorErrors);
|
|
80
|
+
}
|
|
81
|
+
const accept = finalGateRan &&
|
|
82
|
+
productGoalComplete &&
|
|
83
|
+
isAcceptedStatus(acceptanceTargetStatus) &&
|
|
84
|
+
validatorErrors.length === 0 &&
|
|
85
|
+
blockedReasons.length === 0 &&
|
|
86
|
+
rejectionReasons.length === 0;
|
|
87
|
+
const completionOutputStatus = accept ? "accept" : blockedReasons.length > 0 ? "blocked" : "reject";
|
|
88
|
+
const exitCode = completionOutputStatus === "accept" ? 0 : completionOutputStatus === "reject" ? 1 : 2;
|
|
89
|
+
return {
|
|
90
|
+
product_goal_complete: productGoalComplete && accept,
|
|
91
|
+
acceptance_target_status: acceptanceTargetStatus,
|
|
92
|
+
completion_output_status: completionOutputStatus,
|
|
93
|
+
final_answer_allowed: completionOutputStatus === "accept",
|
|
94
|
+
required_user_visible_status: completionOutputStatus === "accept" ? "accepted" : completionOutputStatus === "reject" ? "rejected" : "blocked",
|
|
95
|
+
final_answer: completionOutputStatus,
|
|
96
|
+
exit_code: exitCode,
|
|
97
|
+
blocked_reasons: unique(blockedReasons),
|
|
98
|
+
rejection_reasons: unique(rejectionReasons),
|
|
99
|
+
audit_task_complete: input.audit_task_complete === true,
|
|
100
|
+
final_gate_ran: finalGateRan,
|
|
101
|
+
generated_output_mismatch: input.generated_output_mismatch === true
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export function completionOutputContractFromState(state) {
|
|
105
|
+
const finalRecord = state.final;
|
|
106
|
+
const gate = isRecord(state.gates?.final_gate) ? state.gates.final_gate : {};
|
|
107
|
+
const hasStoredOutput = typeof finalRecord.completion_output_status === "string" || typeof gate.completion_output_status === "string";
|
|
108
|
+
return resolveCompletionOutputStatus({
|
|
109
|
+
final_gate_ran: hasStoredOutput || isRecord(state.gates?.final_gate),
|
|
110
|
+
product_goal_complete: state.final.product_goal_complete,
|
|
111
|
+
acceptance_target_status: state.final.acceptance_target_status,
|
|
112
|
+
audit_task_complete: state.final.audit_task_complete,
|
|
113
|
+
blocked_reasons: asStringArray(finalRecord.blocked_reasons ?? gate.blocked_reasons),
|
|
114
|
+
rejection_reasons: asStringArray(finalRecord.rejection_reasons ?? gate.rejection_reasons),
|
|
115
|
+
generated_output_mismatch: finalRecord.generated_output_mismatch === true || gate.generated_output_mismatch === true
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
export function applyCompletionOutputContract(state, contract) {
|
|
119
|
+
const finalRecord = state.final;
|
|
120
|
+
const metaRecord = state.meta;
|
|
121
|
+
finalRecord.product_goal_complete = contract.product_goal_complete;
|
|
122
|
+
state.meta.product_goal_complete = contract.product_goal_complete;
|
|
123
|
+
finalRecord.acceptance_target_status = contract.acceptance_target_status;
|
|
124
|
+
state.meta.acceptance_target_status = contract.acceptance_target_status;
|
|
125
|
+
finalRecord.audit_task_complete = contract.audit_task_complete;
|
|
126
|
+
state.meta.audit_task_complete = contract.audit_task_complete;
|
|
127
|
+
finalRecord.completion_output_status = contract.completion_output_status;
|
|
128
|
+
finalRecord.final_answer_allowed = contract.final_answer_allowed;
|
|
129
|
+
finalRecord.required_user_visible_status = contract.required_user_visible_status;
|
|
130
|
+
finalRecord.final_answer = contract.final_answer;
|
|
131
|
+
finalRecord.exit_code = contract.exit_code;
|
|
132
|
+
finalRecord.blocked_reasons = contract.blocked_reasons;
|
|
133
|
+
finalRecord.rejection_reasons = contract.rejection_reasons;
|
|
134
|
+
finalRecord.generated_output_mismatch = contract.generated_output_mismatch;
|
|
135
|
+
metaRecord.completion_output_status = contract.completion_output_status;
|
|
136
|
+
}
|
|
137
|
+
export function scanFalseCompletionPhrases(input) {
|
|
138
|
+
if (input.completion_output_status === "accept") {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
const surfaces = typeof input.surfaces === "string" ? [{ surface: "inline", text: input.surfaces }] : input.surfaces;
|
|
142
|
+
const findings = [];
|
|
143
|
+
for (const surface of surfaces) {
|
|
144
|
+
const lines = surface.text.split(/\r?\n/);
|
|
145
|
+
for (const [index, line] of lines.entries()) {
|
|
146
|
+
if (lineAllowedForNonAccept(line)) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
for (const item of FORBIDDEN_PHRASES) {
|
|
150
|
+
if (item.pattern.test(line)) {
|
|
151
|
+
findings.push({ surface: surface.surface, phrase: item.phrase, line: index + 1, text: line.trim() });
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return findings;
|
|
158
|
+
}
|
|
159
|
+
export async function scanGeneratedCompletionOutputSurfaces(workdir, contract) {
|
|
160
|
+
const surfaces = [];
|
|
161
|
+
for (const relative of GENERATED_COMPLETION_SURFACES) {
|
|
162
|
+
const file = path.join(workdir, ...relative.split("/"));
|
|
163
|
+
if (await pathExists(file)) {
|
|
164
|
+
surfaces.push({ surface: relative, text: await readText(file) });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return scanFalseCompletionPhrases({ completion_output_status: contract.completion_output_status, surfaces });
|
|
168
|
+
}
|
|
169
|
+
export function completionPhraseFindingMessages(findings) {
|
|
170
|
+
return findings.map((finding) => `false completion phrase in ${finding.surface}:${finding.line}: ${finding.phrase}`);
|
|
171
|
+
}
|
|
172
|
+
function normalizeAcceptanceTargetStatus(value) {
|
|
173
|
+
const normalized = String(value ?? "not_run").trim().toLowerCase();
|
|
174
|
+
if (normalized === "accepted") {
|
|
175
|
+
return "complete";
|
|
176
|
+
}
|
|
177
|
+
if (normalized === "not accepted") {
|
|
178
|
+
return "not_accepted";
|
|
179
|
+
}
|
|
180
|
+
return normalized || "not_run";
|
|
181
|
+
}
|
|
182
|
+
function isAcceptedStatus(value) {
|
|
183
|
+
return value === "complete" || value === "accepted";
|
|
184
|
+
}
|
|
185
|
+
function lineAllowedForNonAccept(line) {
|
|
186
|
+
const text = line.trim();
|
|
187
|
+
if (!text) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
if (/Audit workflow completed; acceptance target not complete\./i.test(text)) {
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
if (/^Product goal complete:\s*false$/i.test(text)) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
if (/^(?:complete|partial|acceptance_required|missing_layer)_count:\s*\d+$/i.test(text)) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
if (/^-\s+[A-Z]+-\d+:\s*(?:complete|accepted|accept)\s*$/i.test(text)) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
const jsonField = /^"([^"]+)"\s*:\s*/.exec(text);
|
|
203
|
+
if (jsonField && !/^(final_answer|final_conclusion|conclusion|summary|message|required_user_visible_status)$/i.test(jsonField[1])) {
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
if (/\b(product_goal_complete|completion_output_status|acceptance_target_status|audit_task_complete)\b/i.test(text)) {
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
if (/["']?(overall_)?status["']?\s*:\s*["']?(complete|accepted|accept)["']?/i.test(text)) {
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
if (/diagnostic|row-level|row status|not[_ -]?in[_ -]?scope/i.test(text)) {
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
if (/\b(do not|must not|cannot|never|unless|only when|forbid|forbidden|invalid|false[- ]completion|does not mean|cannot authorize|cannot imply)\b/i.test(text)) {
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
if (/\bnot\s+(?:complete|completed|accepted|accept|done|successful)\b/i.test(text)) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
if (/不得|不能|禁止|仅当|不是|不等于/.test(text)) {
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
function unique(values) {
|
|
227
|
+
return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
|
|
228
|
+
}
|
|
@@ -15,11 +15,15 @@ export function evaluateCurrentAttemptEvidence(state, evidence, layerId) {
|
|
|
15
15
|
failures.push(`${label} stale evidence from old attempt ${evidence.task_attempt_id || "(missing)"}; expected current attempt ${state.current_attempt_id}`);
|
|
16
16
|
}
|
|
17
17
|
for (const field of [
|
|
18
|
+
"generated_at",
|
|
18
19
|
"source_bundle_hash",
|
|
19
20
|
"product_source_hash",
|
|
20
21
|
"technical_plan_hash",
|
|
21
22
|
"acceptance_checklist_hash",
|
|
22
23
|
"git_head",
|
|
24
|
+
"git_status_short",
|
|
25
|
+
"tracked_diff_hash",
|
|
26
|
+
"relevant_untracked_hash",
|
|
23
27
|
"worktree_fingerprint",
|
|
24
28
|
"command_spec_id",
|
|
25
29
|
"command_run_id",
|
|
@@ -48,9 +52,24 @@ export function evaluateCurrentAttemptEvidence(state, evidence, layerId) {
|
|
|
48
52
|
if (attempt && evidence.git_head && evidence.git_head !== attempt.git_head) {
|
|
49
53
|
failures.push(`${label} stale evidence git_head mismatch for current attempt`);
|
|
50
54
|
}
|
|
55
|
+
if (attempt && evidence.git_status_short !== undefined && evidence.git_status_short !== attempt.git_status_short) {
|
|
56
|
+
failures.push(`${label} stale evidence git_status_short mismatch for current attempt`);
|
|
57
|
+
}
|
|
58
|
+
if (attempt && evidence.tracked_diff_hash !== undefined && evidence.tracked_diff_hash !== attempt.tracked_diff_hash) {
|
|
59
|
+
failures.push(`${label} stale evidence tracked_diff_hash mismatch for current attempt`);
|
|
60
|
+
}
|
|
61
|
+
if (attempt && evidence.relevant_untracked_hash !== undefined && evidence.relevant_untracked_hash !== attempt.relevant_untracked_hash) {
|
|
62
|
+
failures.push(`${label} stale evidence relevant_untracked_hash mismatch for current attempt`);
|
|
63
|
+
}
|
|
51
64
|
if (attempt && evidence.worktree_fingerprint && evidence.worktree_fingerprint !== attempt.worktree_fingerprint) {
|
|
52
65
|
failures.push(`${label} stale evidence worktree_fingerprint mismatch for current attempt`);
|
|
53
66
|
}
|
|
67
|
+
if (attempt && (attempt.git_status_short || attempt.relevant_untracked_hash !== "none") && evidence.covers_dirty_worktree !== true) {
|
|
68
|
+
failures.push(`${label} dirty worktree evidence must set covers_dirty_worktree=true for current attempt`);
|
|
69
|
+
}
|
|
70
|
+
if (attempt && evidence.generated_at && Date.parse(evidence.generated_at) < Date.parse(attempt.started_at)) {
|
|
71
|
+
failures.push(`${label} stale evidence generated_at predates current attempt`);
|
|
72
|
+
}
|
|
54
73
|
if (attempt && evidence.artifact_mtime && Date.parse(evidence.artifact_mtime) < Date.parse(attempt.started_at)) {
|
|
55
74
|
failures.push(`${label} stale evidence artifact_mtime predates current attempt`);
|
|
56
75
|
}
|
|
@@ -65,6 +84,9 @@ export function evaluateCurrentAttemptEvidence(state, evidence, layerId) {
|
|
|
65
84
|
if (commandRun.exit_code !== 0) {
|
|
66
85
|
failures.push(`${label} command run ${commandRun.command_run_id} exit_code=${commandRun.exit_code}; expected 0`);
|
|
67
86
|
}
|
|
87
|
+
if (!commandRun.completed_at) {
|
|
88
|
+
failures.push(`${label} command run ${commandRun.command_run_id} missing completed_at`);
|
|
89
|
+
}
|
|
68
90
|
if (commandRun.command_spec_id !== evidence.command_spec_id) {
|
|
69
91
|
failures.push(`${label} command_spec_id mismatch between evidence and command run`);
|
|
70
92
|
}
|
|
@@ -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,27 @@ 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 auditLine = contract.completion_output_status === "accept"
|
|
363
|
+
? "Final-gate accepted the current attempt."
|
|
364
|
+
: "Audit workflow completed; acceptance target not complete.";
|
|
330
365
|
return `# Final Summary
|
|
331
366
|
|
|
332
|
-
|
|
333
|
-
product_goal_complete: ${
|
|
367
|
+
diagnostic_overall_status: ${verdict.overall_status}
|
|
368
|
+
product_goal_complete: ${contract.product_goal_complete}
|
|
369
|
+
acceptance_target_status: ${contract.acceptance_target_status}
|
|
370
|
+
completion_output_status: ${contract.completion_output_status}
|
|
371
|
+
final_answer_allowed: ${contract.final_answer_allowed}
|
|
372
|
+
required_user_visible_status: ${contract.required_user_visible_status}
|
|
373
|
+
exit_code: ${contract.exit_code}
|
|
374
|
+
|
|
375
|
+
Final answer: ${contract.final_answer}
|
|
376
|
+
|
|
377
|
+
${auditLine}
|
|
378
|
+
|
|
379
|
+
Reasons:
|
|
380
|
+
${reasonBlock}
|
|
334
381
|
`;
|
|
335
382
|
}
|
|
@@ -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;
|