project-tiny-context-harness 0.2.82 → 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 +10 -4
- package/assets/README.md +13 -7
- package/assets/README.zh-CN.md +8 -2
- package/assets/protected-harness-baseline.json +20 -0
- package/assets/skills/composite-long-task-workflow/SKILL.md +27 -3
- package/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md +24 -0
- package/assets/skills/composite-long-task-workflow/assets/goal-objective.template.md +6 -15
- package/assets/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md +51 -25
- package/dist/commands/composite-long-task.js +60 -5
- package/dist/lib/superpowers-task-ac010.d.ts +6 -0
- package/dist/lib/superpowers-task-ac010.js +26 -0
- package/dist/lib/superpowers-task-assertion-normalizers.js +4 -0
- package/dist/lib/superpowers-task-assertions.js +18 -4
- package/dist/lib/superpowers-task-attempt.d.ts +4 -0
- package/dist/lib/superpowers-task-attempt.js +102 -0
- 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-command-specs.d.ts +3 -0
- package/dist/lib/superpowers-task-command-specs.js +52 -0
- package/dist/lib/superpowers-task-compile.d.ts +4 -1
- package/dist/lib/superpowers-task-compile.js +7 -1
- 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-contradictions.d.ts +6 -0
- package/dist/lib/superpowers-task-contradictions.js +126 -0
- package/dist/lib/superpowers-task-current-evidence.d.ts +3 -0
- package/dist/lib/superpowers-task-current-evidence.js +176 -0
- package/dist/lib/superpowers-task-derive.js +69 -8
- package/dist/lib/superpowers-task-evidence-kernel.d.ts +19 -0
- package/dist/lib/superpowers-task-evidence-kernel.js +347 -0
- package/dist/lib/superpowers-task-evidence-records.d.ts +2 -0
- package/dist/lib/superpowers-task-evidence-records.js +55 -0
- package/dist/lib/superpowers-task-evidence.d.ts +10 -0
- package/dist/lib/superpowers-task-evidence.js +147 -0
- 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 +87 -35
- package/dist/lib/superpowers-task-harness-drift.d.ts +11 -0
- package/dist/lib/superpowers-task-harness-drift.js +90 -0
- package/dist/lib/superpowers-task-protected-baseline.d.ts +10 -0
- package/dist/lib/superpowers-task-protected-baseline.js +66 -0
- package/dist/lib/superpowers-task-state-schema.d.ts +116 -3
- package/dist/lib/superpowers-task-state-schema.js +23 -1
- package/dist/lib/superpowers-task-state-shape.d.ts +3 -0
- package/dist/lib/superpowers-task-state-shape.js +50 -0
- package/dist/lib/superpowers-task-state.js +17 -37
- package/dist/lib/superpowers-task-status.js +11 -1
- package/dist/lib/superpowers-task-under-specified.d.ts +7 -0
- package/dist/lib/superpowers-task-under-specified.js +61 -0
- 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 +43 -27
- package/package.json +69 -69
- package/source-mappings.yaml +3 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { stableJson } from "./superpowers-task-state.js";
|
|
3
|
+
export function deriveRequiredCommandSpecs(state) {
|
|
4
|
+
return Object.entries(state.graph.acceptance_criteria).flatMap(([acId, ac]) => {
|
|
5
|
+
const proofLayers = [...new Set((ac.required_proof_layers ?? []).filter(Boolean))];
|
|
6
|
+
if (proofLayers.length === 0) {
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
const spec = {
|
|
10
|
+
ac_id: acId,
|
|
11
|
+
proof_layers: proofLayers,
|
|
12
|
+
command: ac.assertion_command ?? "",
|
|
13
|
+
assertion_artifacts: ac.assertion_artifacts ?? [],
|
|
14
|
+
required_test_ids: ac.required_test_ids ?? [],
|
|
15
|
+
machine_blocking: ac.machine_blocking !== false,
|
|
16
|
+
assertion_result_required: ac.assertion_result_required !== false,
|
|
17
|
+
positive_assertions: ac.positive_assertions ?? [],
|
|
18
|
+
negative_assertions: ac.negative_assertions ?? [],
|
|
19
|
+
invalid_completion_signals: ac.invalid_completion_signals ?? [],
|
|
20
|
+
final_evidence_expected: ac.final_evidence_expected ?? []
|
|
21
|
+
};
|
|
22
|
+
return [{ command_spec_id: commandSpecId(spec), ...spec }];
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export function requiredCommandSpecsHash(specs) {
|
|
26
|
+
return sha256(stableJson(specs.map((spec) => ({
|
|
27
|
+
...spec,
|
|
28
|
+
proof_layers: [...(spec.proof_layers ?? [])].sort(),
|
|
29
|
+
assertion_artifacts: [...(spec.assertion_artifacts ?? [])].sort(),
|
|
30
|
+
required_test_ids: [...(spec.required_test_ids ?? [])].sort(),
|
|
31
|
+
positive_assertions: [...(spec.positive_assertions ?? [])].sort(),
|
|
32
|
+
negative_assertions: [...(spec.negative_assertions ?? [])].sort(),
|
|
33
|
+
invalid_completion_signals: [...(spec.invalid_completion_signals ?? [])].sort(),
|
|
34
|
+
final_evidence_expected: [...(spec.final_evidence_expected ?? [])].sort()
|
|
35
|
+
}))));
|
|
36
|
+
}
|
|
37
|
+
function commandSpecId(spec) {
|
|
38
|
+
return sha256([
|
|
39
|
+
spec.ac_id,
|
|
40
|
+
spec.proof_layers.join(","),
|
|
41
|
+
spec.command,
|
|
42
|
+
spec.assertion_artifacts.join(","),
|
|
43
|
+
spec.required_test_ids.join(","),
|
|
44
|
+
spec.positive_assertions.join(","),
|
|
45
|
+
spec.negative_assertions.join(","),
|
|
46
|
+
spec.invalid_completion_signals.join(","),
|
|
47
|
+
spec.final_evidence_expected.join(",")
|
|
48
|
+
].join("\n"));
|
|
49
|
+
}
|
|
50
|
+
function sha256(value) {
|
|
51
|
+
return createHash("sha256").update(value).digest("hex");
|
|
52
|
+
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import { type SuperpowersTaskState } from "./superpowers-task-state-schema.js";
|
|
2
|
-
|
|
2
|
+
import type { SuperpowersAttemptMode } from "./superpowers-task-state-schema.js";
|
|
3
|
+
export declare function compileSuperpowersTask(workdir: string, options?: {
|
|
4
|
+
mode?: SuperpowersAttemptMode;
|
|
5
|
+
}): Promise<SuperpowersTaskState>;
|
|
3
6
|
export declare function computeScopeConflicts(state: SuperpowersTaskState): string[];
|
|
@@ -3,9 +3,11 @@ import { pathExists, readText } from "./fs.js";
|
|
|
3
3
|
import { appendSuperpowersEvent } from "./superpowers-task-events.js";
|
|
4
4
|
import { compileError, compileReportSuffix, throwCompileErrors } from "./superpowers-task-compile-diagnostics.js";
|
|
5
5
|
import { validateCompiledSources } from "./superpowers-task-compile-guards.js";
|
|
6
|
+
import { startSuperpowersAttempt } from "./superpowers-task-attempt.js";
|
|
7
|
+
import { deriveRequiredCommandSpecs } from "./superpowers-task-command-specs.js";
|
|
6
8
|
import { loadSuperpowersState, recomputeStatuses, saveSuperpowersState, refreshSourceHashes } from "./superpowers-task-state.js";
|
|
7
9
|
import { DEFAULT_LAYERS, parseAcceptanceCriteria, parsePlanItems, parseProductArchitectureScope } from "./superpowers-task-source-compile.js";
|
|
8
|
-
export async function compileSuperpowersTask(workdir) {
|
|
10
|
+
export async function compileSuperpowersTask(workdir, options = {}) {
|
|
9
11
|
const state = await loadSuperpowersState(workdir);
|
|
10
12
|
await refreshSourceHashes(workdir, state);
|
|
11
13
|
await assertRequiredSourcesExist(workdir, state);
|
|
@@ -40,9 +42,13 @@ export async function compileSuperpowersTask(workdir) {
|
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
state.graph.edges = Object.entries(planItems).flatMap(([planId, item]) => item.related_acs.map((acId) => ({ from: planId, to: acId, type: "supports" })));
|
|
45
|
+
state.required_command_specs = deriveRequiredCommandSpecs(state);
|
|
46
|
+
state.command_runs = [];
|
|
47
|
+
state.negative_evidence_records = [];
|
|
43
48
|
state.delivery.scope_conflicts = computeScopeConflicts(state);
|
|
44
49
|
state.progress = compileProgress(state);
|
|
45
50
|
recomputeStatuses(state);
|
|
51
|
+
await startSuperpowersAttempt(workdir, state, options.mode ?? "product_task");
|
|
46
52
|
await saveSuperpowersState(workdir, state);
|
|
47
53
|
await appendSuperpowersEvent(workdir, "graph_compiled", {
|
|
48
54
|
plan_items: Object.keys(planItems).length,
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SuperpowersTaskState } from "./superpowers-task-state-schema.js";
|
|
2
|
+
export interface SuperpowersContradictionScan {
|
|
3
|
+
errors: string[];
|
|
4
|
+
historicalCompleteIgnored: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function scanSuperpowersContradictions(workdir: string, state: SuperpowersTaskState): Promise<SuperpowersContradictionScan>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { listFiles, pathExists, readText } from "./fs.js";
|
|
3
|
+
const OWNER_SURFACE_FORBIDDEN_STATES = ["尚未运行自测", "运行未记录", "未验证", "不可用", "暂不可用", "页面无明显变化"];
|
|
4
|
+
export async function scanSuperpowersContradictions(workdir, state) {
|
|
5
|
+
const files = await listFiles(workdir);
|
|
6
|
+
const errors = [
|
|
7
|
+
...scanFinalStateContradictions(state),
|
|
8
|
+
...(await scanCurrentFailureArtifacts(workdir, files))
|
|
9
|
+
];
|
|
10
|
+
const historicalCompleteIgnored = errors.length > 0 && (await hasHistoricalCompleteEvent(workdir));
|
|
11
|
+
if (historicalCompleteIgnored) {
|
|
12
|
+
errors.push("historical_complete_ignored: Historical stale completion event detected and ignored. Current recomputed product_goal_complete=false.");
|
|
13
|
+
}
|
|
14
|
+
if (errors.length > 0) {
|
|
15
|
+
errors.push("workflow_gate_bug_prevented: current contradiction scan prevents product_goal_complete=true.");
|
|
16
|
+
}
|
|
17
|
+
return { errors: unique(errors), historicalCompleteIgnored };
|
|
18
|
+
}
|
|
19
|
+
function scanFinalStateContradictions(state) {
|
|
20
|
+
const errors = [];
|
|
21
|
+
if (state.meta?.product_goal_complete === false && state.final?.product_goal_complete === true) {
|
|
22
|
+
errors.push("current contradiction task_state_product_goal_mismatch: meta product_goal_complete=false but final product_goal_complete=true.");
|
|
23
|
+
}
|
|
24
|
+
if (state.meta?.acceptance_target_status && state.final?.acceptance_target_status && state.meta.acceptance_target_status !== state.final.acceptance_target_status) {
|
|
25
|
+
errors.push(`current contradiction task_state_acceptance_status_mismatch: meta=${state.meta.acceptance_target_status} final=${state.final.acceptance_target_status}.`);
|
|
26
|
+
}
|
|
27
|
+
for (const commandRun of state.command_runs ?? []) {
|
|
28
|
+
if (Number(commandRun.exit_code) !== 0) {
|
|
29
|
+
errors.push(`current contradiction command_run_failed: ${commandRun.command_run_id} ac=${commandRun.ac_id} proof_layer=${commandRun.proof_layer} exit_code=${commandRun.exit_code}.`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return errors;
|
|
33
|
+
}
|
|
34
|
+
async function scanCurrentFailureArtifacts(workdir, files) {
|
|
35
|
+
const errors = [];
|
|
36
|
+
for (const file of files) {
|
|
37
|
+
const relative = slash(path.relative(workdir, file));
|
|
38
|
+
if (relative.endsWith(".last-run.json")) {
|
|
39
|
+
errors.push(...(await scanPlaywrightLastRun(file, relative)));
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (/test-results\/.*error-context\.md$/i.test(relative)) {
|
|
43
|
+
errors.push(...(await scanOwnerDomErrorContext(file, relative)));
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (isFailureJsonCandidate(relative)) {
|
|
47
|
+
errors.push(...(await scanFailedJsonArtifact(file, relative)));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return errors;
|
|
51
|
+
}
|
|
52
|
+
async function scanPlaywrightLastRun(file, relative) {
|
|
53
|
+
const parsed = await readJsonRecord(file);
|
|
54
|
+
if (!parsed) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
const status = String(parsed.status ?? parsed.outcome ?? "").toLowerCase();
|
|
58
|
+
const failedTests = Array.isArray(parsed.failedTests) ? parsed.failedTests : Array.isArray(parsed.failed_tests) ? parsed.failed_tests : [];
|
|
59
|
+
if (status !== "failed" && failedTests.length === 0) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
return [
|
|
63
|
+
`current contradiction playwright_last_run_failed: ${relative} status=${status || "unknown"} failed_tests=${failedTests
|
|
64
|
+
.map(String)
|
|
65
|
+
.join(", ") || "(unspecified)"}.`
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
async function scanOwnerDomErrorContext(file, relative) {
|
|
69
|
+
const text = await readText(file);
|
|
70
|
+
const findings = OWNER_SURFACE_FORBIDDEN_STATES.filter((state) => text.includes(state));
|
|
71
|
+
if (findings.length === 0) {
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
return [`current contradiction owner_dom_forbidden_state: ${relative} contains ${findings.join(", ")}.`];
|
|
75
|
+
}
|
|
76
|
+
async function scanFailedJsonArtifact(file, relative) {
|
|
77
|
+
const parsed = await readJsonRecord(file);
|
|
78
|
+
if (!parsed) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const text = JSON.stringify(parsed);
|
|
82
|
+
const status = String(parsed.status ?? parsed.outcome ?? parsed.result ?? "").toLowerCase();
|
|
83
|
+
const failures = Number(parsed.failures ?? parsed.failed ?? parsed.failed_count ?? 0);
|
|
84
|
+
if (status !== "failed" && failures <= 0 && !/"failed"/i.test(text)) {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
return [`current contradiction failed_test_result_artifact: ${relative} reports failed status.`];
|
|
88
|
+
}
|
|
89
|
+
function isFailureJsonCandidate(relative) {
|
|
90
|
+
return (/\.json$/i.test(relative) &&
|
|
91
|
+
/(^|\/)(test-results|playwright-report|junit|command-runs|negative-evidence|negative_evidence)(\/|$)/i.test(relative));
|
|
92
|
+
}
|
|
93
|
+
async function hasHistoricalCompleteEvent(workdir) {
|
|
94
|
+
const eventsPath = path.join(workdir, "events.ndjson");
|
|
95
|
+
if (!(await pathExists(eventsPath))) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const content = await readText(eventsPath);
|
|
99
|
+
return content
|
|
100
|
+
.split(/\r?\n/)
|
|
101
|
+
.filter(Boolean)
|
|
102
|
+
.some((line) => {
|
|
103
|
+
try {
|
|
104
|
+
const event = JSON.parse(line);
|
|
105
|
+
return String(event.event ?? event.event_type ?? event.type ?? "") === "final_gate" && event.product_goal_complete === true;
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return /final_gate/.test(line) && /product_goal_complete["']?\s*:\s*true/.test(line);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
async function readJsonRecord(file) {
|
|
113
|
+
try {
|
|
114
|
+
const parsed = JSON.parse(await readText(file));
|
|
115
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function slash(value) {
|
|
122
|
+
return value.split(path.sep).join("/");
|
|
123
|
+
}
|
|
124
|
+
function unique(values) {
|
|
125
|
+
return [...new Set(values.filter(Boolean))];
|
|
126
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SuperpowersEvidenceRecord, SuperpowersTaskState } from "./superpowers-task-state-schema.js";
|
|
2
|
+
export declare function evaluateCurrentAttemptEvidence(state: SuperpowersTaskState, evidence: SuperpowersEvidenceRecord, layerId: string): string[];
|
|
3
|
+
export declare function evaluateCurrentAttemptArtifact(workdir: string, evidence: SuperpowersEvidenceRecord, layerId: string): Promise<string[]>;
|