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
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
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
|
-
import { loadSuperpowersState,
|
|
5
|
-
import {
|
|
4
|
+
import { loadSuperpowersState, saveSuperpowersState } from "./superpowers-task-state.js";
|
|
5
|
+
import { applyCompletionOutputContract, completionPhraseFindingMessages, resolveCompletionOutputStatus, scanGeneratedCompletionOutputSurfaces } from "./superpowers-task-completion-output.js";
|
|
6
|
+
import { applyTrustedEvidenceKernelResult, evaluateTrustedEvidenceKernel } from "./superpowers-task-evidence-kernel.js";
|
|
7
|
+
import { isRecord } from "./superpowers-task-state-schema.js";
|
|
8
|
+
import { validateSuperpowersState } from "./superpowers-task-validator.js";
|
|
6
9
|
export async function runSliceGate(workdir, sliceId) {
|
|
7
10
|
const state = await loadSuperpowersState(workdir);
|
|
8
11
|
const slice = state.slices.find((item) => item.slice_id === sliceId);
|
|
@@ -24,59 +27,105 @@ export async function runEpochGate(workdir, epochId) {
|
|
|
24
27
|
}
|
|
25
28
|
export async function runFinalGate(workdir) {
|
|
26
29
|
const state = await loadSuperpowersState(workdir);
|
|
27
|
-
|
|
28
|
-
state
|
|
29
|
-
state.
|
|
30
|
-
state.gates.validator = { status: "not_run" };
|
|
30
|
+
const kernel = await evaluateTrustedEvidenceKernel(workdir, state);
|
|
31
|
+
applyTrustedEvidenceKernelResult(state, kernel);
|
|
32
|
+
state.gates.validator = { status: "not_run", kernel: "trusted_evidence_kernel" };
|
|
31
33
|
await saveSuperpowersState(workdir, state);
|
|
32
34
|
await deriveSuperpowersArtifacts(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
|
-
const
|
|
39
|
-
const acceptanceStatus = complete ? "complete" : acceptanceStatusForErrors(errors);
|
|
38
|
+
let errors = [...new Set([...kernel.errors, ...report.errors, ...acceptanceReport.errors])];
|
|
39
|
+
let complete = kernel.product_goal_complete && errors.length === 0;
|
|
40
|
+
const acceptanceStatus = complete ? "complete" : acceptanceStatusForErrors(errors, kernel);
|
|
40
41
|
const nextRequiredActions = complete ? [] : nextActionsForErrors(errors);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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);
|
|
47
52
|
latest.final.completion_basis = complete
|
|
48
|
-
? ["
|
|
53
|
+
? ["trusted_evidence_kernel", "current_attempt_evidence", "negative_evidence_scan_passed", "harness_drift_lock_passed"]
|
|
49
54
|
: [];
|
|
50
55
|
latest.final.next_required_actions = nextRequiredActions;
|
|
51
56
|
latest.gates.validator = { status: errors.length === 0 ? "pass" : "blocked", errors };
|
|
52
57
|
latest.gates.final_gate = {
|
|
53
58
|
status: complete ? "pass" : acceptanceStatus,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"verification_before_completion_expected",
|
|
57
|
-
"validate_state",
|
|
58
|
-
"validate_derived",
|
|
59
|
-
"validate_plan_acceptance",
|
|
60
|
-
"auditor_blocker_scan",
|
|
61
|
-
"stale_overclaim_scope_scan",
|
|
62
|
-
"ac_evidence_assertion_gate",
|
|
63
|
-
"negative_evidence_scan_gate",
|
|
64
|
-
"compute_completion"
|
|
65
|
-
],
|
|
59
|
+
kernel: "trusted_evidence_kernel",
|
|
60
|
+
order: kernel.kernel_order,
|
|
66
61
|
errors,
|
|
67
|
-
|
|
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,
|
|
67
|
+
harness_task_final_verdict: kernel.harness_task_final_verdict,
|
|
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
|
|
68
76
|
};
|
|
69
77
|
await saveSuperpowersState(workdir, latest);
|
|
70
78
|
await deriveSuperpowersArtifacts(workdir);
|
|
71
|
-
await
|
|
72
|
-
|
|
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 };
|
|
73
119
|
}
|
|
74
|
-
function acceptanceStatusForErrors(errors) {
|
|
120
|
+
function acceptanceStatusForErrors(errors, kernel) {
|
|
75
121
|
const text = errors.join("\n");
|
|
76
|
-
if (
|
|
122
|
+
if (kernel?.acceptance_target_status === "under_specified" || /under_specified/i.test(text)) {
|
|
123
|
+
return "under_specified";
|
|
124
|
+
}
|
|
125
|
+
if (/source hash mismatch|source_changed_requires_recompile|scope_conflict_requires_decision|auditor blocker|Context Delta coverage is unresolved|harness_drift|protected_baseline|harness_task_missing|required_command_specs_hash/i.test(text)) {
|
|
77
126
|
return "blocked";
|
|
78
127
|
}
|
|
79
|
-
if (/invalid evidence|forbidden shortcut|negative evidence|stale evidence|raw secret|contains_secret|sibling substitution|assertion_result\.status=failed|assertion exit_code=|command_exit_code=|forbidden text/i.test(text)) {
|
|
128
|
+
if (/invalid evidence|forbidden shortcut|negative evidence|stale evidence|raw secret|contains_secret|sibling substitution|assertion_result\.status=failed|assertion exit_code=|command_exit_code=|forbidden text|current contradiction|workflow_gate_bug_prevented|playwright_last_run_failed|owner_dom_forbidden_state|failed_test_result_artifact/i.test(text)) {
|
|
80
129
|
return "invalidated";
|
|
81
130
|
}
|
|
82
131
|
return "partial";
|
|
@@ -95,6 +144,9 @@ function nextActionsForErrors(errors) {
|
|
|
95
144
|
if (/negative evidence|forbidden/i.test(error)) {
|
|
96
145
|
return `${error}; rerun the negative evidence scan and fix the contradictory owner-surface state`;
|
|
97
146
|
}
|
|
147
|
+
if (/current contradiction|playwright_last_run_failed|owner_dom_forbidden_state|failed_test_result_artifact/i.test(error)) {
|
|
148
|
+
return `${error}; rerun the required current assertion command after fixing the current failure`;
|
|
149
|
+
}
|
|
98
150
|
if (/derived\/.*does not match/i.test(error)) {
|
|
99
151
|
return `${error}; rerun ty-context composite-long-task derive <workdir>`;
|
|
100
152
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type SuperpowersTaskState } from "./superpowers-task-state-schema.js";
|
|
2
|
+
export interface HarnessDriftResult {
|
|
3
|
+
harness_drift_detected: boolean;
|
|
4
|
+
acceptance_target_status: "complete" | "partial" | "blocked";
|
|
5
|
+
product_goal_complete: boolean;
|
|
6
|
+
harness_task_final_verdict?: "passed" | "failed";
|
|
7
|
+
changed_files: string[];
|
|
8
|
+
errors: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare function detectHarnessDrift(state: SuperpowersTaskState): HarnessDriftResult;
|
|
11
|
+
export declare function isHarnessPath(file: string): boolean;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { isRecord } from "./superpowers-task-state-schema.js";
|
|
2
|
+
const HARNESS_PATH_PATTERNS = [
|
|
3
|
+
/(^|\/)tests\/.*\.(spec|test)\.[cm]?[jt]sx?$/i,
|
|
4
|
+
/(^|\/)playwright\.config\./i,
|
|
5
|
+
/(^|\/).*\.spec\.[cm]?[jt]sx?$/i,
|
|
6
|
+
/(^|\/).*\.test\.[cm]?[jt]sx?$/i,
|
|
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,
|
|
9
|
+
/(^|\/)(\.codex\/ty-context-managed|packages\/ty-context\/assets)\/skills\/composite-long-task-workflow\//i,
|
|
10
|
+
/composite-long-task-workflow-protocol\.md$/i,
|
|
11
|
+
/(^|\/)Makefile$/i,
|
|
12
|
+
/(^|\/)package\.json$/i
|
|
13
|
+
];
|
|
14
|
+
const REQUIRED_NEGATIVE_FIXTURES = [
|
|
15
|
+
"stale_evidence",
|
|
16
|
+
"historical_complete",
|
|
17
|
+
"derived_contradiction",
|
|
18
|
+
"ac010_summary_only",
|
|
19
|
+
"target_mismatch",
|
|
20
|
+
"api_only_for_ui",
|
|
21
|
+
"negative_evidence_after_pass",
|
|
22
|
+
"source_hash_mismatch",
|
|
23
|
+
"dirty_worktree_mismatch",
|
|
24
|
+
"missing_assertion_result",
|
|
25
|
+
"test_weakening"
|
|
26
|
+
];
|
|
27
|
+
export function detectHarnessDrift(state) {
|
|
28
|
+
const attempt = currentAttempt(state);
|
|
29
|
+
const changedFiles = changedFilesFor(attempt);
|
|
30
|
+
const harnessFiles = changedFiles.filter(isHarnessPath);
|
|
31
|
+
const mode = attempt?.mode ?? "product_task";
|
|
32
|
+
if (mode === "product_task" && harnessFiles.length > 0) {
|
|
33
|
+
return {
|
|
34
|
+
harness_drift_detected: true,
|
|
35
|
+
acceptance_target_status: "blocked",
|
|
36
|
+
product_goal_complete: false,
|
|
37
|
+
changed_files: harnessFiles,
|
|
38
|
+
errors: [
|
|
39
|
+
`harness_drift_detected: ${harnessFiles.join(", ")}`,
|
|
40
|
+
"本轮修改了验收工具链或测试本身,不能用被修改后的验收证明同一轮产品完成。请拆成独立 harness_task。"
|
|
41
|
+
]
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (mode === "harness_task") {
|
|
45
|
+
return harnessTaskFixtureVerdict(state, changedFiles);
|
|
46
|
+
}
|
|
47
|
+
return { harness_drift_detected: false, acceptance_target_status: "complete", product_goal_complete: true, changed_files: [], errors: [] };
|
|
48
|
+
}
|
|
49
|
+
export function isHarnessPath(file) {
|
|
50
|
+
const normalized = file.replace(/\\/g, "/");
|
|
51
|
+
return HARNESS_PATH_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
52
|
+
}
|
|
53
|
+
function harnessTaskFixtureVerdict(state, changedFiles) {
|
|
54
|
+
const fixtureState = isRecord(state.gates?.harness_task_fixtures) ? state.gates.harness_task_fixtures : undefined;
|
|
55
|
+
const errors = [];
|
|
56
|
+
if (!fixtureState) {
|
|
57
|
+
errors.push("harness_task_missing_adversarial_fixtures: harness_task must declare adversarial fixture outcomes");
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
for (const fixture of REQUIRED_NEGATIVE_FIXTURES) {
|
|
61
|
+
if (fixtureState[fixture] !== false) {
|
|
62
|
+
errors.push(`harness_task_missing_adversarial_fixtures: ${fixture} must have expected product_goal_complete=false`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (fixtureState.happy_path !== true) {
|
|
66
|
+
errors.push("harness_task_missing_happy_path_fixture: happy_path must have expected product_goal_complete=true");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const fixtureChanges = changedFiles.filter((file) => /(^|\/)tests\/ty-context\/.*fixture/i.test(file.replace(/\\/g, "/")));
|
|
70
|
+
if (fixtureChanges.length > 0) {
|
|
71
|
+
errors.push(`fixture_changed_requires_review: ${fixtureChanges.join(", ")}`);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
harness_drift_detected: false,
|
|
75
|
+
acceptance_target_status: errors.length > 0 ? "blocked" : "complete",
|
|
76
|
+
product_goal_complete: false,
|
|
77
|
+
harness_task_final_verdict: errors.length > 0 ? "failed" : "passed",
|
|
78
|
+
changed_files: changedFiles,
|
|
79
|
+
errors
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function currentAttempt(state) {
|
|
83
|
+
if (!state.current_attempt_id) {
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
return (state.attempts ?? []).find((item) => item.task_attempt_id === state.current_attempt_id);
|
|
87
|
+
}
|
|
88
|
+
function changedFilesFor(attempt) {
|
|
89
|
+
return [...new Set((attempt?.changed_files ?? []).map((file) => file.replace(/\\/g, "/")).filter(Boolean))];
|
|
90
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type SuperpowersTaskState } from "./superpowers-task-state-schema.js";
|
|
2
|
+
export interface ProtectedBaselineResult {
|
|
3
|
+
protected_baseline_changed: boolean;
|
|
4
|
+
product_goal_complete: boolean;
|
|
5
|
+
changed_files: string[];
|
|
6
|
+
errors: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare const PROTECTED_BASELINE_PATHS: string[];
|
|
9
|
+
export declare function evaluateProtectedBaseline(state: SuperpowersTaskState): ProtectedBaselineResult;
|
|
10
|
+
export declare function isProtectedBaselinePath(file: string): boolean;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { isHarnessPath } from "./superpowers-task-harness-drift.js";
|
|
2
|
+
import { isRecord } from "./superpowers-task-state-schema.js";
|
|
3
|
+
export const PROTECTED_BASELINE_PATHS = [
|
|
4
|
+
"packages/ty-context/assets/protected-harness-baseline.json",
|
|
5
|
+
".codex/ty-context-managed/protected-harness-baseline.json",
|
|
6
|
+
"packages/ty-context/source-mappings.yaml",
|
|
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",
|
|
10
|
+
"packages/ty-context/src/lib/superpowers-task-validator.ts",
|
|
11
|
+
"packages/ty-context/src/lib/superpowers-task-derive.ts",
|
|
12
|
+
"packages/ty-context/src/lib/superpowers-task-evidence.ts",
|
|
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",
|
|
20
|
+
"packages/ty-context/src/lib/superpowers-task-state-schema.ts",
|
|
21
|
+
".codex/ty-context-managed/skills/composite-long-task-workflow/SKILL.md",
|
|
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",
|
|
25
|
+
"packages/ty-context/assets/skills/composite-long-task-workflow/SKILL.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"
|
|
33
|
+
];
|
|
34
|
+
export function evaluateProtectedBaseline(state) {
|
|
35
|
+
const attempt = currentAttempt(state);
|
|
36
|
+
const changedFiles = [...new Set((attempt?.changed_files ?? []).map((file) => file.replace(/\\/g, "/")).filter(Boolean))];
|
|
37
|
+
const protectedChanges = changedFiles.filter(isProtectedBaselinePath);
|
|
38
|
+
const mode = attempt?.mode ?? "product_task";
|
|
39
|
+
const errors = [];
|
|
40
|
+
if (mode === "product_task" && protectedChanges.length > 0) {
|
|
41
|
+
errors.push(`protected_baseline_changed: product_task cannot change protected harness baseline paths: ${protectedChanges.join(", ")}`);
|
|
42
|
+
}
|
|
43
|
+
if (mode === "harness_task" && protectedChanges.length > 0 && !protectedBaselineReason(state)) {
|
|
44
|
+
errors.push("protected_baseline_reason_required: harness_task baseline changes must record gates.protected_baseline.reason");
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
protected_baseline_changed: protectedChanges.length > 0,
|
|
48
|
+
product_goal_complete: errors.length === 0,
|
|
49
|
+
changed_files: protectedChanges,
|
|
50
|
+
errors
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function isProtectedBaselinePath(file) {
|
|
54
|
+
const normalized = file.replace(/\\/g, "/");
|
|
55
|
+
return PROTECTED_BASELINE_PATHS.includes(normalized) || isHarnessPath(normalized);
|
|
56
|
+
}
|
|
57
|
+
function protectedBaselineReason(state) {
|
|
58
|
+
const baseline = state.gates?.protected_baseline;
|
|
59
|
+
return isRecord(baseline) && typeof baseline.reason === "string" && baseline.reason.trim().length > 0;
|
|
60
|
+
}
|
|
61
|
+
function currentAttempt(state) {
|
|
62
|
+
if (!state.current_attempt_id) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
return (state.attempts ?? []).find((item) => item.task_attempt_id === state.current_attempt_id);
|
|
66
|
+
}
|
|
@@ -4,7 +4,7 @@ export declare const SUPERPOWERS_TASK_STATE_JSON_SCHEMA: {
|
|
|
4
4
|
readonly $id: "https://project-tiny-context-harness.local/superpowers-task-state.schema.json";
|
|
5
5
|
readonly title: "Superpowers Long-Task State";
|
|
6
6
|
readonly type: "object";
|
|
7
|
-
readonly required: readonly ["meta", "sources", "context", "delivery", "graph", "slices", "evidence", "gates", "progress", "blockers", "final"];
|
|
7
|
+
readonly required: readonly ["meta", "sources", "context", "delivery", "graph", "attempts", "current_attempt_id", "required_command_specs", "command_runs", "negative_evidence_records", "slices", "evidence", "gates", "progress", "blockers", "final"];
|
|
8
8
|
readonly properties: {
|
|
9
9
|
readonly meta: {
|
|
10
10
|
readonly type: "object";
|
|
@@ -33,6 +33,21 @@ export declare const SUPERPOWERS_TASK_STATE_JSON_SCHEMA: {
|
|
|
33
33
|
readonly graph: {
|
|
34
34
|
readonly type: "object";
|
|
35
35
|
};
|
|
36
|
+
readonly attempts: {
|
|
37
|
+
readonly type: "array";
|
|
38
|
+
};
|
|
39
|
+
readonly current_attempt_id: {
|
|
40
|
+
readonly type: "string";
|
|
41
|
+
};
|
|
42
|
+
readonly required_command_specs: {
|
|
43
|
+
readonly type: "array";
|
|
44
|
+
};
|
|
45
|
+
readonly command_runs: {
|
|
46
|
+
readonly type: "array";
|
|
47
|
+
};
|
|
48
|
+
readonly negative_evidence_records: {
|
|
49
|
+
readonly type: "array";
|
|
50
|
+
};
|
|
36
51
|
readonly slices: {
|
|
37
52
|
readonly type: "array";
|
|
38
53
|
};
|
|
@@ -55,11 +70,13 @@ export declare const SUPERPOWERS_TASK_STATE_JSON_SCHEMA: {
|
|
|
55
70
|
};
|
|
56
71
|
export type SuperpowersProofLayerStatus = "missing" | "satisfied" | "invalidated" | "blocked";
|
|
57
72
|
export type SuperpowersPlanItemStatus = "not_started" | "in_progress" | "complete" | "partial" | "blocked" | "invalidated" | "out_of_scope_NA";
|
|
58
|
-
export type SuperpowersAcceptanceStatus = "not_run" | "complete" | "partial" | "blocked" | "invalidated" | "out_of_scope_NA";
|
|
73
|
+
export type SuperpowersAcceptanceStatus = "not_run" | "complete" | "partial" | "blocked" | "invalidated" | "under_specified" | "out_of_scope_NA";
|
|
74
|
+
export type SuperpowersAttemptMode = "product_task" | "harness_task";
|
|
59
75
|
export type SuperpowersProductDeliveryScope = "system_capability_build" | "representative_sample_validation" | "full_population_operation" | "mixed_scope_requires_boundary";
|
|
60
76
|
export type SuperpowersPlanDeliveryScope = "system_capability_build" | "representative_sample_validation" | "full_population_operation" | "out_of_scope_backlog";
|
|
61
77
|
export type SuperpowersAcceptanceScope = "system_capability_build" | "representative_sample_validation" | "full_population_operation" | "full_population_not_required";
|
|
62
78
|
export type SuperpowersScopeFitDecision = "fit_for_three_inputs" | "selected_from_split" | "blocked_for_decision" | "";
|
|
79
|
+
export type CompletionOutputStatus = "accept" | "reject" | "blocked";
|
|
63
80
|
export interface SuperpowersTaskState {
|
|
64
81
|
meta: {
|
|
65
82
|
task_id: string;
|
|
@@ -71,6 +88,7 @@ export interface SuperpowersTaskState {
|
|
|
71
88
|
product_goal_complete: boolean;
|
|
72
89
|
acceptance_target_status: string;
|
|
73
90
|
audit_task_complete: boolean;
|
|
91
|
+
completion_output_status?: CompletionOutputStatus;
|
|
74
92
|
};
|
|
75
93
|
sources: Record<string, SuperpowersSourceRecord>;
|
|
76
94
|
context: {
|
|
@@ -86,6 +104,11 @@ export interface SuperpowersTaskState {
|
|
|
86
104
|
proof_layers: Record<string, SuperpowersProofLayer>;
|
|
87
105
|
edges: SuperpowersGraphEdge[];
|
|
88
106
|
};
|
|
107
|
+
attempts: ExecutionAttempt[];
|
|
108
|
+
current_attempt_id: string;
|
|
109
|
+
required_command_specs: RequiredCommandSpec[];
|
|
110
|
+
command_runs: CommandRunRecord[];
|
|
111
|
+
negative_evidence_records: NegativeEvidenceRecord[];
|
|
89
112
|
slices: SuperpowersSliceRecord[];
|
|
90
113
|
evidence: SuperpowersEvidenceRecord[];
|
|
91
114
|
gates: Record<string, unknown>;
|
|
@@ -95,10 +118,74 @@ export interface SuperpowersTaskState {
|
|
|
95
118
|
product_goal_complete: boolean;
|
|
96
119
|
acceptance_target_status: string;
|
|
97
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[];
|
|
98
130
|
completion_basis: string[];
|
|
99
131
|
next_required_actions?: string[];
|
|
100
132
|
};
|
|
101
133
|
}
|
|
134
|
+
export interface ExecutionAttempt {
|
|
135
|
+
task_attempt_id: string;
|
|
136
|
+
source_bundle_hash: string;
|
|
137
|
+
product_source_hash: string;
|
|
138
|
+
technical_plan_hash: string;
|
|
139
|
+
acceptance_checklist_hash: string;
|
|
140
|
+
git_head: string;
|
|
141
|
+
git_status_short: string;
|
|
142
|
+
tracked_diff_hash: string;
|
|
143
|
+
relevant_untracked_hash: string;
|
|
144
|
+
untracked_relevant_hash?: string;
|
|
145
|
+
worktree_fingerprint: string;
|
|
146
|
+
started_at: string;
|
|
147
|
+
ended_at: string | null;
|
|
148
|
+
finalized_at?: string | null;
|
|
149
|
+
required_command_specs_hash: string;
|
|
150
|
+
mode: SuperpowersAttemptMode;
|
|
151
|
+
changed_files?: string[];
|
|
152
|
+
}
|
|
153
|
+
export interface RequiredCommandSpec {
|
|
154
|
+
command_spec_id: string;
|
|
155
|
+
ac_id: string;
|
|
156
|
+
proof_layers: string[];
|
|
157
|
+
command: string;
|
|
158
|
+
assertion_artifacts: string[];
|
|
159
|
+
required_test_ids: string[];
|
|
160
|
+
machine_blocking: boolean;
|
|
161
|
+
assertion_result_required: boolean;
|
|
162
|
+
positive_assertions: string[];
|
|
163
|
+
negative_assertions: string[];
|
|
164
|
+
invalid_completion_signals: string[];
|
|
165
|
+
final_evidence_expected: string[];
|
|
166
|
+
}
|
|
167
|
+
export interface CommandRunRecord {
|
|
168
|
+
command_run_id: string;
|
|
169
|
+
task_attempt_id: string;
|
|
170
|
+
command_spec_id: string;
|
|
171
|
+
ac_id: string;
|
|
172
|
+
proof_layer: string;
|
|
173
|
+
command_line: string;
|
|
174
|
+
exit_code: number;
|
|
175
|
+
started_at: string;
|
|
176
|
+
completed_at?: string;
|
|
177
|
+
ended_at: string;
|
|
178
|
+
artifact_paths: string[];
|
|
179
|
+
}
|
|
180
|
+
export interface NegativeEvidenceRecord {
|
|
181
|
+
negative_evidence_id: string;
|
|
182
|
+
task_attempt_id: string;
|
|
183
|
+
target_ac_ids: string[];
|
|
184
|
+
target_proof_layers: string[];
|
|
185
|
+
artifact_path: string;
|
|
186
|
+
artifact_sha256?: string;
|
|
187
|
+
status: "passed" | "failed" | "blocked" | "stale";
|
|
188
|
+
}
|
|
102
189
|
export interface SuperpowersSourceRecord {
|
|
103
190
|
path: string;
|
|
104
191
|
sha256: string;
|
|
@@ -258,7 +345,29 @@ export interface SuperpowersSliceRecord {
|
|
|
258
345
|
};
|
|
259
346
|
}
|
|
260
347
|
export interface SuperpowersEvidenceRecord {
|
|
348
|
+
schema_version?: "evidence-record-v1" | "evidence-record-v2" | string;
|
|
261
349
|
evidence_id: string;
|
|
350
|
+
task_attempt_id?: string;
|
|
351
|
+
generated_at?: string;
|
|
352
|
+
source_bundle_hash?: string;
|
|
353
|
+
product_source_hash?: string;
|
|
354
|
+
technical_plan_hash?: string;
|
|
355
|
+
acceptance_checklist_hash?: string;
|
|
356
|
+
git_head?: string;
|
|
357
|
+
git_status_short?: string;
|
|
358
|
+
tracked_diff_hash?: string;
|
|
359
|
+
relevant_untracked_hash?: string;
|
|
360
|
+
covers_dirty_worktree?: boolean;
|
|
361
|
+
worktree_fingerprint?: string;
|
|
362
|
+
command_spec_id?: string;
|
|
363
|
+
command_run_id?: string;
|
|
364
|
+
command_line?: string;
|
|
365
|
+
artifact_path?: string;
|
|
366
|
+
artifact_sha256?: string;
|
|
367
|
+
artifact_mtime?: string;
|
|
368
|
+
target_ac_ids?: string[];
|
|
369
|
+
target_pi_ids?: string[];
|
|
370
|
+
target_proof_layers?: string[];
|
|
262
371
|
slice_id: string;
|
|
263
372
|
type: string;
|
|
264
373
|
freshness: {
|
|
@@ -285,17 +394,21 @@ export interface SuperpowersEvidenceRecord {
|
|
|
285
394
|
sibling_substitution_approval_source?: string;
|
|
286
395
|
}
|
|
287
396
|
export interface AssertionResult {
|
|
288
|
-
schema_version: "assertion-result-v1";
|
|
397
|
+
schema_version: "assertion-result-v1" | "assertion-result-v2";
|
|
289
398
|
status: "passed" | "failed" | "blocked" | "stale";
|
|
290
399
|
runner: string;
|
|
291
400
|
exit_code: number;
|
|
292
401
|
target_ac_ids: string[];
|
|
402
|
+
target_pi_ids?: string[];
|
|
293
403
|
target_proof_layers: string[];
|
|
294
404
|
owner_surface?: string;
|
|
295
405
|
route?: string;
|
|
296
406
|
action?: string;
|
|
297
407
|
positive_assertions: AssertionCheck[];
|
|
298
408
|
negative_assertions: AssertionCheck[];
|
|
409
|
+
invalid_completion_signals?: AssertionCheck[];
|
|
410
|
+
negative_evidence_scan?: NegativeEvidenceScan;
|
|
411
|
+
required_test_ids?: string[];
|
|
299
412
|
artifacts?: string[];
|
|
300
413
|
}
|
|
301
414
|
export interface AssertionCheck {
|
|
@@ -4,7 +4,24 @@ export const SUPERPOWERS_TASK_STATE_JSON_SCHEMA = {
|
|
|
4
4
|
$id: "https://project-tiny-context-harness.local/superpowers-task-state.schema.json",
|
|
5
5
|
title: "Superpowers Long-Task State",
|
|
6
6
|
type: "object",
|
|
7
|
-
required: [
|
|
7
|
+
required: [
|
|
8
|
+
"meta",
|
|
9
|
+
"sources",
|
|
10
|
+
"context",
|
|
11
|
+
"delivery",
|
|
12
|
+
"graph",
|
|
13
|
+
"attempts",
|
|
14
|
+
"current_attempt_id",
|
|
15
|
+
"required_command_specs",
|
|
16
|
+
"command_runs",
|
|
17
|
+
"negative_evidence_records",
|
|
18
|
+
"slices",
|
|
19
|
+
"evidence",
|
|
20
|
+
"gates",
|
|
21
|
+
"progress",
|
|
22
|
+
"blockers",
|
|
23
|
+
"final"
|
|
24
|
+
],
|
|
8
25
|
properties: {
|
|
9
26
|
meta: {
|
|
10
27
|
type: "object",
|
|
@@ -19,6 +36,11 @@ export const SUPERPOWERS_TASK_STATE_JSON_SCHEMA = {
|
|
|
19
36
|
context: { type: "object" },
|
|
20
37
|
delivery: { type: "object" },
|
|
21
38
|
graph: { type: "object" },
|
|
39
|
+
attempts: { type: "array" },
|
|
40
|
+
current_attempt_id: { type: "string" },
|
|
41
|
+
required_command_specs: { type: "array" },
|
|
42
|
+
command_runs: { type: "array" },
|
|
43
|
+
negative_evidence_records: { type: "array" },
|
|
22
44
|
slices: { type: "array" },
|
|
23
45
|
evidence: { type: "array" },
|
|
24
46
|
gates: { type: "object" },
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { isRecord } from "./superpowers-task-state-schema.js";
|
|
2
|
+
export function validateShape(state, errors) {
|
|
3
|
+
if (state.meta?.schema_version !== "superpowers-task-state-v1") {
|
|
4
|
+
errors.push("task-state.json schema_version must be superpowers-task-state-v1");
|
|
5
|
+
}
|
|
6
|
+
for (const key of [
|
|
7
|
+
"meta",
|
|
8
|
+
"sources",
|
|
9
|
+
"context",
|
|
10
|
+
"delivery",
|
|
11
|
+
"graph",
|
|
12
|
+
"attempts",
|
|
13
|
+
"current_attempt_id",
|
|
14
|
+
"required_command_specs",
|
|
15
|
+
"command_runs",
|
|
16
|
+
"negative_evidence_records",
|
|
17
|
+
"slices",
|
|
18
|
+
"evidence",
|
|
19
|
+
"gates",
|
|
20
|
+
"progress",
|
|
21
|
+
"blockers",
|
|
22
|
+
"final"
|
|
23
|
+
]) {
|
|
24
|
+
if (!(key in state)) {
|
|
25
|
+
errors.push(`task-state.json is missing section: ${key}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function hasUsableShape(state) {
|
|
30
|
+
const candidate = state;
|
|
31
|
+
return (isRecord(candidate.meta) &&
|
|
32
|
+
isRecord(candidate.sources) &&
|
|
33
|
+
isRecord(candidate.context) &&
|
|
34
|
+
isRecord(candidate.delivery) &&
|
|
35
|
+
isRecord(candidate.graph) &&
|
|
36
|
+
isRecord(candidate.graph.plan_items) &&
|
|
37
|
+
isRecord(candidate.graph.acceptance_criteria) &&
|
|
38
|
+
isRecord(candidate.graph.proof_layers) &&
|
|
39
|
+
Array.isArray(candidate.attempts) &&
|
|
40
|
+
typeof candidate.current_attempt_id === "string" &&
|
|
41
|
+
Array.isArray(candidate.required_command_specs) &&
|
|
42
|
+
Array.isArray(candidate.command_runs) &&
|
|
43
|
+
Array.isArray(candidate.negative_evidence_records) &&
|
|
44
|
+
Array.isArray(candidate.slices) &&
|
|
45
|
+
Array.isArray(candidate.evidence) &&
|
|
46
|
+
isRecord(candidate.gates) &&
|
|
47
|
+
isRecord(candidate.progress) &&
|
|
48
|
+
Array.isArray(candidate.blockers) &&
|
|
49
|
+
isRecord(candidate.final));
|
|
50
|
+
}
|