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.
Files changed (55) hide show
  1. package/README.md +10 -4
  2. package/assets/README.md +13 -7
  3. package/assets/README.zh-CN.md +8 -2
  4. package/assets/protected-harness-baseline.json +20 -0
  5. package/assets/skills/composite-long-task-workflow/SKILL.md +27 -3
  6. package/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md +24 -0
  7. package/assets/skills/composite-long-task-workflow/assets/goal-objective.template.md +6 -15
  8. package/assets/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md +51 -25
  9. package/dist/commands/composite-long-task.js +60 -5
  10. package/dist/lib/superpowers-task-ac010.d.ts +6 -0
  11. package/dist/lib/superpowers-task-ac010.js +26 -0
  12. package/dist/lib/superpowers-task-assertion-normalizers.js +4 -0
  13. package/dist/lib/superpowers-task-assertions.js +18 -4
  14. package/dist/lib/superpowers-task-attempt.d.ts +4 -0
  15. package/dist/lib/superpowers-task-attempt.js +102 -0
  16. package/dist/lib/superpowers-task-command-run-correlation.d.ts +8 -0
  17. package/dist/lib/superpowers-task-command-run-correlation.js +103 -0
  18. package/dist/lib/superpowers-task-command-specs.d.ts +3 -0
  19. package/dist/lib/superpowers-task-command-specs.js +52 -0
  20. package/dist/lib/superpowers-task-compile.d.ts +4 -1
  21. package/dist/lib/superpowers-task-compile.js +7 -1
  22. package/dist/lib/superpowers-task-completion-output.d.ts +52 -0
  23. package/dist/lib/superpowers-task-completion-output.js +228 -0
  24. package/dist/lib/superpowers-task-contradictions.d.ts +6 -0
  25. package/dist/lib/superpowers-task-contradictions.js +126 -0
  26. package/dist/lib/superpowers-task-current-evidence.d.ts +3 -0
  27. package/dist/lib/superpowers-task-current-evidence.js +176 -0
  28. package/dist/lib/superpowers-task-derive.js +69 -8
  29. package/dist/lib/superpowers-task-evidence-kernel.d.ts +19 -0
  30. package/dist/lib/superpowers-task-evidence-kernel.js +347 -0
  31. package/dist/lib/superpowers-task-evidence-records.d.ts +2 -0
  32. package/dist/lib/superpowers-task-evidence-records.js +55 -0
  33. package/dist/lib/superpowers-task-evidence.d.ts +10 -0
  34. package/dist/lib/superpowers-task-evidence.js +147 -0
  35. package/dist/lib/superpowers-task-final-card.d.ts +3 -0
  36. package/dist/lib/superpowers-task-final-card.js +24 -0
  37. package/dist/lib/superpowers-task-gates.d.ts +2 -2
  38. package/dist/lib/superpowers-task-gates.js +87 -35
  39. package/dist/lib/superpowers-task-harness-drift.d.ts +11 -0
  40. package/dist/lib/superpowers-task-harness-drift.js +90 -0
  41. package/dist/lib/superpowers-task-protected-baseline.d.ts +10 -0
  42. package/dist/lib/superpowers-task-protected-baseline.js +66 -0
  43. package/dist/lib/superpowers-task-state-schema.d.ts +116 -3
  44. package/dist/lib/superpowers-task-state-schema.js +23 -1
  45. package/dist/lib/superpowers-task-state-shape.d.ts +3 -0
  46. package/dist/lib/superpowers-task-state-shape.js +50 -0
  47. package/dist/lib/superpowers-task-state.js +17 -37
  48. package/dist/lib/superpowers-task-status.js +11 -1
  49. package/dist/lib/superpowers-task-under-specified.d.ts +7 -0
  50. package/dist/lib/superpowers-task-under-specified.js +61 -0
  51. package/dist/lib/superpowers-task-unregistered-evidence.d.ts +11 -0
  52. package/dist/lib/superpowers-task-unregistered-evidence.js +72 -0
  53. package/dist/lib/superpowers-task-validator.js +43 -27
  54. package/package.json +69 -69
  55. 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, recomputeStatuses, saveSuperpowersState } from "./superpowers-task-state.js";
5
- import { completionConditionErrors, validateSuperpowersState } from "./superpowers-task-validator.js";
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
- recomputeStatuses(state);
28
- state.final.audit_task_complete = true;
29
- state.meta.audit_task_complete = true;
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
- const completionErrors = completionConditionErrors(latest);
37
- const errors = [...new Set([...report.errors, ...acceptanceReport.errors, ...completionErrors])];
38
- const complete = errors.length === 0;
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
- latest.final.product_goal_complete = complete;
42
- latest.meta.product_goal_complete = complete;
43
- latest.final.acceptance_target_status = acceptanceStatus;
44
- latest.meta.acceptance_target_status = latest.final.acceptance_target_status;
45
- latest.final.audit_task_complete = true;
46
- latest.meta.audit_task_complete = true;
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
- ? ["all_required_acs_complete", "validator_passed", "assertion_evidence_passed", "negative_evidence_scan_passed", "auditor_no_blocker"]
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
- order: [
55
- "derive",
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
- next_required_actions: nextRequiredActions
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 appendSuperpowersEvent(workdir, "final_gate", { product_goal_complete: complete });
72
- return { product_goal_complete: complete, errors };
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 (/source hash mismatch|source_changed_requires_recompile|scope_conflict_requires_decision|auditor blocker|Context Delta coverage is unresolved/i.test(text)) {
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: ["meta", "sources", "context", "delivery", "graph", "slices", "evidence", "gates", "progress", "blockers", "final"],
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,3 @@
1
+ import { type SuperpowersTaskState } from "./superpowers-task-state-schema.js";
2
+ export declare function validateShape(state: SuperpowersTaskState, errors: string[]): void;
3
+ export declare function hasUsableShape(state: SuperpowersTaskState): boolean;
@@ -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
+ }