project-tiny-context-harness 0.2.81 → 0.2.83

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 (52) hide show
  1. package/README.md +11 -3
  2. package/assets/README.md +11 -3
  3. package/assets/README.zh-CN.md +9 -1
  4. package/assets/protected-harness-baseline.json +18 -0
  5. package/assets/skills/composite-long-task-workflow/SKILL.md +21 -1
  6. package/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md +10 -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 +32 -15
  9. package/dist/commands/composite-long-task.js +45 -2
  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 +8 -1
  13. package/dist/lib/superpowers-task-assertions.js +35 -20
  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-specs.d.ts +3 -0
  17. package/dist/lib/superpowers-task-command-specs.js +52 -0
  18. package/dist/lib/superpowers-task-compile.d.ts +4 -1
  19. package/dist/lib/superpowers-task-compile.js +7 -1
  20. package/dist/lib/superpowers-task-contradictions.d.ts +6 -0
  21. package/dist/lib/superpowers-task-contradictions.js +126 -0
  22. package/dist/lib/superpowers-task-current-evidence.d.ts +3 -0
  23. package/dist/lib/superpowers-task-current-evidence.js +154 -0
  24. package/dist/lib/superpowers-task-delivery.js +30 -18
  25. package/dist/lib/superpowers-task-derive.d.ts +1 -0
  26. package/dist/lib/superpowers-task-derive.js +76 -1
  27. package/dist/lib/superpowers-task-evidence-kernel.d.ts +12 -0
  28. package/dist/lib/superpowers-task-evidence-kernel.js +351 -0
  29. package/dist/lib/superpowers-task-evidence-records.d.ts +2 -0
  30. package/dist/lib/superpowers-task-evidence-records.js +55 -0
  31. package/dist/lib/superpowers-task-evidence.d.ts +10 -0
  32. package/dist/lib/superpowers-task-evidence.js +141 -0
  33. package/dist/lib/superpowers-task-fields.d.ts +22 -0
  34. package/dist/lib/superpowers-task-fields.js +276 -0
  35. package/dist/lib/superpowers-task-gates.js +36 -24
  36. package/dist/lib/superpowers-task-harness-drift.d.ts +11 -0
  37. package/dist/lib/superpowers-task-harness-drift.js +86 -0
  38. package/dist/lib/superpowers-task-protected-baseline.d.ts +10 -0
  39. package/dist/lib/superpowers-task-protected-baseline.js +47 -0
  40. package/dist/lib/superpowers-task-source-compile.js +93 -95
  41. package/dist/lib/superpowers-task-source-parser.js +1 -3
  42. package/dist/lib/superpowers-task-state-schema.d.ts +158 -3
  43. package/dist/lib/superpowers-task-state-schema.js +23 -1
  44. package/dist/lib/superpowers-task-state-shape.d.ts +3 -0
  45. package/dist/lib/superpowers-task-state-shape.js +50 -0
  46. package/dist/lib/superpowers-task-state.js +19 -39
  47. package/dist/lib/superpowers-task-status.js +11 -1
  48. package/dist/lib/superpowers-task-under-specified.d.ts +7 -0
  49. package/dist/lib/superpowers-task-under-specified.js +61 -0
  50. package/dist/lib/superpowers-task-validator.js +11 -37
  51. package/package.json +69 -69
  52. package/source-mappings.yaml +3 -0
@@ -0,0 +1,141 @@
1
+ import { createHash } from "node:crypto";
2
+ import { spawn } from "node:child_process";
3
+ import { promises as fs } from "node:fs";
4
+ import path from "node:path";
5
+ import { pathExists, readText } from "./fs.js";
6
+ import { appendSuperpowersEvent } from "./superpowers-task-events.js";
7
+ import { computeSourceBundleHash } from "./superpowers-task-attempt.js";
8
+ import { normalizeAssertionResult, normalizeNegativeEvidenceScan } from "./superpowers-task-assertions.js";
9
+ import { normalizeProofLayerId, normalizeProofLayerName } from "./superpowers-task-fields.js";
10
+ import { loadSuperpowersState, saveSuperpowersState, sha256 } from "./superpowers-task-state.js";
11
+ import { asStringArray, isRecord } from "./superpowers-task-state-schema.js";
12
+ export async function runSuperpowersAssertion(workdir, options) {
13
+ if (!options.acId || !options.proofLayer) {
14
+ throw new Error("run-assertion requires --ac <AC-ID> and --proof-layer <layer>");
15
+ }
16
+ if (options.commandArgs.length === 0) {
17
+ throw new Error("run-assertion requires a command after --");
18
+ }
19
+ const state = await loadSuperpowersState(workdir);
20
+ const proofLayer = normalizeProofLayerName(options.proofLayer);
21
+ const spec = (state.required_command_specs ?? []).find((item) => item.ac_id === options.acId && item.proof_layers.map(normalizeProofLayerName).includes(proofLayer));
22
+ if (!spec) {
23
+ throw new Error(`no required command spec for ${options.acId}.${proofLayer}`);
24
+ }
25
+ const startedAt = new Date().toISOString();
26
+ const exitCode = await runCommand(options.commandArgs);
27
+ const endedAt = new Date().toISOString();
28
+ const commandLine = options.commandArgs.join(" ");
29
+ const commandRun = {
30
+ command_run_id: `CR-${compactDate(startedAt)}-${sha256(commandLine).slice(0, 8)}`,
31
+ task_attempt_id: state.current_attempt_id,
32
+ command_spec_id: spec.command_spec_id,
33
+ ac_id: options.acId,
34
+ proof_layer: proofLayer,
35
+ command_line: commandLine,
36
+ exit_code: exitCode,
37
+ started_at: startedAt,
38
+ ended_at: endedAt,
39
+ artifact_paths: []
40
+ };
41
+ state.command_runs = [...(state.command_runs ?? []), commandRun];
42
+ await saveSuperpowersState(workdir, state);
43
+ await appendSuperpowersEvent(workdir, "assertion_command_run", {
44
+ command_run_id: commandRun.command_run_id,
45
+ command_spec_id: commandRun.command_spec_id,
46
+ exit_code: commandRun.exit_code
47
+ });
48
+ return commandRun;
49
+ }
50
+ export async function recordSuperpowersEvidence(workdir, options) {
51
+ const state = await loadSuperpowersState(workdir);
52
+ const commandRun = (state.command_runs ?? []).find((item) => item.command_run_id === options.commandRunId);
53
+ if (!commandRun) {
54
+ throw new Error(`command run not found: ${options.commandRunId}`);
55
+ }
56
+ const artifactPath = path.resolve(options.artifactPath);
57
+ if (!(await pathExists(artifactPath))) {
58
+ throw new Error(`evidence artifact not found: ${options.artifactPath}`);
59
+ }
60
+ const artifactText = await readText(artifactPath);
61
+ const artifact = JSON.parse(artifactText);
62
+ const artifactRecord = isRecord(artifact) ? artifact : {};
63
+ const assertion = normalizeAssertionResult(artifactRecord.assertion_result ?? artifactRecord);
64
+ if (!assertion) {
65
+ throw new Error(`evidence artifact is missing assertion_result: ${options.artifactPath}`);
66
+ }
67
+ const stats = await fs.stat(artifactPath);
68
+ const attempt = (state.attempts ?? []).find((item) => item.task_attempt_id === commandRun.task_attempt_id);
69
+ const commandSpec = (state.required_command_specs ?? []).find((item) => item.command_spec_id === commandRun.command_spec_id);
70
+ const layerId = normalizeProofLayerId(`${commandRun.ac_id}.${commandRun.proof_layer}`);
71
+ const relativeArtifactPath = slash(path.relative(workdir, artifactPath));
72
+ const targetPiIds = commandSpec ? (state.graph.acceptance_criteria[commandSpec.ac_id]?.related_plan_items ?? []) : [];
73
+ const negativeScan = normalizeNegativeEvidenceScan(artifactRecord.negative_evidence_scan);
74
+ if (assertion.schema_version === "assertion-result-v2") {
75
+ assertion.target_pi_ids = assertion.target_pi_ids ?? targetPiIds;
76
+ assertion.invalid_completion_signals = assertion.invalid_completion_signals ?? [];
77
+ assertion.required_test_ids = assertion.required_test_ids ?? commandSpec?.required_test_ids ?? [];
78
+ assertion.negative_evidence_scan = assertion.negative_evidence_scan ?? negativeScan;
79
+ }
80
+ const evidence = {
81
+ schema_version: "evidence-record-v2",
82
+ evidence_id: `EV2-${compactDate(new Date().toISOString())}-${sha256(options.commandRunId + artifactText).slice(0, 8)}`,
83
+ task_attempt_id: commandRun.task_attempt_id,
84
+ source_bundle_hash: attempt?.source_bundle_hash ?? computeSourceBundleHash(state),
85
+ product_source_hash: attempt?.product_source_hash ?? state.sources.product_architecture_source?.sha256 ?? "",
86
+ technical_plan_hash: attempt?.technical_plan_hash ?? state.sources.technical_realization_plan?.sha256 ?? "",
87
+ acceptance_checklist_hash: attempt?.acceptance_checklist_hash ?? state.sources.acceptance_checklist?.sha256 ?? "",
88
+ git_head: attempt?.git_head ?? "",
89
+ worktree_fingerprint: attempt?.worktree_fingerprint ?? "",
90
+ command_spec_id: commandRun.command_spec_id,
91
+ command_run_id: commandRun.command_run_id,
92
+ command_line: commandRun.command_line,
93
+ command_exit_code: commandRun.exit_code,
94
+ artifact_path: relativeArtifactPath,
95
+ artifact_sha256: createHash("sha256").update(artifactText).digest("hex"),
96
+ artifact_mtime: stats.mtime.toISOString(),
97
+ target_ac_ids: [commandRun.ac_id],
98
+ target_pi_ids: targetPiIds,
99
+ target_proof_layers: [layerId],
100
+ slice_id: String(artifactRecord.slice_id ?? "attempt-evidence"),
101
+ type: String(artifactRecord.type ?? `${commandRun.proof_layer}_assertion`),
102
+ freshness: { created_at: commandRun.ended_at, valid_for: "current_attempt", stale_after: null },
103
+ command: commandRun.command_line,
104
+ artifact_paths: [relativeArtifactPath],
105
+ proves: [layerId],
106
+ does_not_prove: asStringArray(artifactRecord.does_not_prove).length > 0 ? asStringArray(artifactRecord.does_not_prove) : ["unrelated proof layer"],
107
+ redaction: isRecord(artifactRecord.redaction)
108
+ ? { checked: artifactRecord.redaction.checked === true, contains_secret: artifactRecord.redaction.contains_secret === true }
109
+ : { checked: true, contains_secret: false },
110
+ reviewability: isRecord(artifactRecord.reviewability)
111
+ ? {
112
+ external_reviewer_can_reproduce: artifactRecord.reviewability.external_reviewer_can_reproduce === true,
113
+ reproduction_steps: String(artifactRecord.reviewability.reproduction_steps ?? commandRun.command_line)
114
+ }
115
+ : { external_reviewer_can_reproduce: true, reproduction_steps: commandRun.command_line },
116
+ assertion_result: assertion,
117
+ negative_evidence_scan: negativeScan
118
+ };
119
+ state.evidence = [...(state.evidence ?? []), evidence];
120
+ const proofLayer = state.graph.proof_layers[layerId];
121
+ if (proofLayer && commandRun.exit_code === 0 && assertion.status === "passed") {
122
+ proofLayer.status = "satisfied";
123
+ proofLayer.evidence_ids = [...new Set([...(proofLayer.evidence_ids ?? []), evidence.evidence_id])];
124
+ }
125
+ await saveSuperpowersState(workdir, state);
126
+ await appendSuperpowersEvent(workdir, "evidence_recorded", { evidence_id: evidence.evidence_id, command_run_id: commandRun.command_run_id });
127
+ return evidence;
128
+ }
129
+ function runCommand(args) {
130
+ return new Promise((resolve) => {
131
+ const child = spawn(args[0], args.slice(1), { cwd: process.cwd(), stdio: "ignore", windowsHide: true });
132
+ child.on("error", () => resolve(1));
133
+ child.on("exit", (code) => resolve(code ?? 1));
134
+ });
135
+ }
136
+ function compactDate(value) {
137
+ return value.replace(/[-:.TZ]/g, "").slice(0, 17);
138
+ }
139
+ function slash(value) {
140
+ return value.split(path.sep).join("/");
141
+ }
@@ -0,0 +1,22 @@
1
+ export declare const PRODUCT_FIELDS: readonly ["delivery_scope", "full_population_required", "representative_samples_validate", "representative_samples_do_not_validate", "out_of_scope_backlog", "scope_fit_decision", "selected_scope_fit_slice", "owner_boundary", "primary_capability_path", "non_completing_outcomes", "assertion_policy", "source_authority", "product_goal", "surface_ia_lock", "decision_lock", "context_delta", "source_to_context_coverage", "acceptance_semantics", "impact"];
2
+ export declare const PLAN_FIELDS: readonly ["delivery_scope", "capability_target", "representative_samples", "full_population_boundary", "non_required_population", "owner_surfaces", "forbidden_surfaces", "owner_boundary", "primary_capability_path", "trigger_contract", "state_transition_contract", "observable_result_contract", "assertion_support", "required_assertion_commands", "invalid_implementation_shortcuts", "implementation_paths", "required_tests", "related_acs", "requirement_ref", "decision_id", "proof_layer_ids", "api_schema_changes", "state_machine", "data_flow", "worker_runtime_behavior", "ui_ia_changes", "migration_plan", "evidence_artifacts", "explicit_no_test_scope", "non_completing_shortcuts", "substitution_policy", "drift_severity", "partial_conditions", "blockers", "context_fact_refs"];
3
+ export declare const ACCEPTANCE_FIELDS: readonly ["acceptance_scope", "ac_validates", "ac_does_not_validate", "sample_boundary", "full_population_required", "related_plan_items", "required_proof_layers", "assertion_command", "assertion_artifacts", "positive_assertions", "negative_assertions", "machine_blocking", "invalid_completion_signals", "assertion_result_required", "ac_type", "proof_chain", "verification_method", "fail_conditions", "invalid_evidence", "substitution_policy", "missing_layer_downgrade", "auditor_expectation", "out_of_scope_na_approval_source", "required_test_ids", "explicit_no_test_scope", "hard_blockers", "validates_explanation", "does_not_validate_explanation", "final_evidence_expected", "test_cases"];
4
+ export declare const PRODUCT_REQUIRED_FIELDS: readonly ["delivery_scope", "full_population_required", "representative_samples_validate", "representative_samples_do_not_validate", "out_of_scope_backlog", "scope_fit_decision", "selected_scope_fit_slice", "owner_boundary", "primary_capability_path", "non_completing_outcomes", "assertion_policy", "source_authority", "product_goal"];
5
+ export declare const PLAN_REQUIRED_FIELDS: readonly ["delivery_scope", "capability_target", "representative_samples", "full_population_boundary", "non_required_population", "owner_boundary", "primary_capability_path", "trigger_contract", "state_transition_contract", "observable_result_contract", "assertion_support", "required_assertion_commands", "invalid_implementation_shortcuts", "implementation_paths"];
6
+ export declare const ACCEPTANCE_REQUIRED_FIELDS: readonly ["acceptance_scope", "ac_validates", "ac_does_not_validate", "sample_boundary", "full_population_required", "related_plan_items", "required_proof_layers", "assertion_command", "assertion_artifacts", "positive_assertions", "negative_assertions", "machine_blocking", "invalid_completion_signals", "assertion_result_required"];
7
+ type FieldType = "array" | "boolean" | "enum" | "text";
8
+ export declare const PRODUCT_FIELD_TYPES: Record<(typeof PRODUCT_FIELDS)[number], FieldType>;
9
+ export declare const PLAN_FIELD_TYPES: Record<(typeof PLAN_FIELDS)[number], FieldType>;
10
+ export declare const ACCEPTANCE_FIELD_TYPES: Record<(typeof ACCEPTANCE_FIELDS)[number], FieldType>;
11
+ export declare const PRODUCT_DELIVERY_SCOPES: Set<string>;
12
+ export declare const PLAN_DELIVERY_SCOPES: Set<string>;
13
+ export declare const ACCEPTANCE_SCOPES: Set<string>;
14
+ export declare const SCOPE_FIT_DECISIONS: Set<string>;
15
+ export declare const CANONICAL_PROOF_LAYERS: readonly ["code", "api_schema", "worker_runtime", "data_artifact", "integration", "ui_browser", "security_redaction", "all_provider_all_runner", "cleanup_stale_scan", "test"];
16
+ export declare const LEGACY_PROOF_LAYER_ALIASES: Record<string, string>;
17
+ export declare const MACHINE_VERIFIABLE_LAYER_NAMES: Set<string>;
18
+ export declare function fieldSet(fields: readonly string[]): Set<string>;
19
+ export declare function normalizeProofLayerName(layer: string): string;
20
+ export declare function normalizeProofLayerId(layerId: string): string;
21
+ export declare function isSelectedScopeFitSlice(value: string): boolean;
22
+ export {};
@@ -0,0 +1,276 @@
1
+ export const PRODUCT_FIELDS = [
2
+ "delivery_scope",
3
+ "full_population_required",
4
+ "representative_samples_validate",
5
+ "representative_samples_do_not_validate",
6
+ "out_of_scope_backlog",
7
+ "scope_fit_decision",
8
+ "selected_scope_fit_slice",
9
+ "owner_boundary",
10
+ "primary_capability_path",
11
+ "non_completing_outcomes",
12
+ "assertion_policy",
13
+ "source_authority",
14
+ "product_goal",
15
+ "surface_ia_lock",
16
+ "decision_lock",
17
+ "context_delta",
18
+ "source_to_context_coverage",
19
+ "acceptance_semantics",
20
+ "impact"
21
+ ];
22
+ export const PLAN_FIELDS = [
23
+ "delivery_scope",
24
+ "capability_target",
25
+ "representative_samples",
26
+ "full_population_boundary",
27
+ "non_required_population",
28
+ "owner_surfaces",
29
+ "forbidden_surfaces",
30
+ "owner_boundary",
31
+ "primary_capability_path",
32
+ "trigger_contract",
33
+ "state_transition_contract",
34
+ "observable_result_contract",
35
+ "assertion_support",
36
+ "required_assertion_commands",
37
+ "invalid_implementation_shortcuts",
38
+ "implementation_paths",
39
+ "required_tests",
40
+ "related_acs",
41
+ "requirement_ref",
42
+ "decision_id",
43
+ "proof_layer_ids",
44
+ "api_schema_changes",
45
+ "state_machine",
46
+ "data_flow",
47
+ "worker_runtime_behavior",
48
+ "ui_ia_changes",
49
+ "migration_plan",
50
+ "evidence_artifacts",
51
+ "explicit_no_test_scope",
52
+ "non_completing_shortcuts",
53
+ "substitution_policy",
54
+ "drift_severity",
55
+ "partial_conditions",
56
+ "blockers",
57
+ "context_fact_refs"
58
+ ];
59
+ export const ACCEPTANCE_FIELDS = [
60
+ "acceptance_scope",
61
+ "ac_validates",
62
+ "ac_does_not_validate",
63
+ "sample_boundary",
64
+ "full_population_required",
65
+ "related_plan_items",
66
+ "required_proof_layers",
67
+ "assertion_command",
68
+ "assertion_artifacts",
69
+ "positive_assertions",
70
+ "negative_assertions",
71
+ "machine_blocking",
72
+ "invalid_completion_signals",
73
+ "assertion_result_required",
74
+ "ac_type",
75
+ "proof_chain",
76
+ "verification_method",
77
+ "fail_conditions",
78
+ "invalid_evidence",
79
+ "substitution_policy",
80
+ "missing_layer_downgrade",
81
+ "auditor_expectation",
82
+ "out_of_scope_na_approval_source",
83
+ "required_test_ids",
84
+ "explicit_no_test_scope",
85
+ "hard_blockers",
86
+ "validates_explanation",
87
+ "does_not_validate_explanation",
88
+ "final_evidence_expected",
89
+ "test_cases"
90
+ ];
91
+ export const PRODUCT_REQUIRED_FIELDS = [
92
+ "delivery_scope",
93
+ "full_population_required",
94
+ "representative_samples_validate",
95
+ "representative_samples_do_not_validate",
96
+ "out_of_scope_backlog",
97
+ "scope_fit_decision",
98
+ "selected_scope_fit_slice",
99
+ "owner_boundary",
100
+ "primary_capability_path",
101
+ "non_completing_outcomes",
102
+ "assertion_policy",
103
+ "source_authority",
104
+ "product_goal"
105
+ ];
106
+ export const PLAN_REQUIRED_FIELDS = [
107
+ "delivery_scope",
108
+ "capability_target",
109
+ "representative_samples",
110
+ "full_population_boundary",
111
+ "non_required_population",
112
+ "owner_boundary",
113
+ "primary_capability_path",
114
+ "trigger_contract",
115
+ "state_transition_contract",
116
+ "observable_result_contract",
117
+ "assertion_support",
118
+ "required_assertion_commands",
119
+ "invalid_implementation_shortcuts",
120
+ "implementation_paths"
121
+ ];
122
+ export const ACCEPTANCE_REQUIRED_FIELDS = [
123
+ "acceptance_scope",
124
+ "ac_validates",
125
+ "ac_does_not_validate",
126
+ "sample_boundary",
127
+ "full_population_required",
128
+ "related_plan_items",
129
+ "required_proof_layers",
130
+ "assertion_command",
131
+ "assertion_artifacts",
132
+ "positive_assertions",
133
+ "negative_assertions",
134
+ "machine_blocking",
135
+ "invalid_completion_signals",
136
+ "assertion_result_required"
137
+ ];
138
+ export const PRODUCT_FIELD_TYPES = {
139
+ delivery_scope: "enum",
140
+ full_population_required: "boolean",
141
+ representative_samples_validate: "array",
142
+ representative_samples_do_not_validate: "array",
143
+ out_of_scope_backlog: "array",
144
+ scope_fit_decision: "enum",
145
+ selected_scope_fit_slice: "text",
146
+ owner_boundary: "text",
147
+ primary_capability_path: "text",
148
+ non_completing_outcomes: "array",
149
+ assertion_policy: "text",
150
+ source_authority: "text",
151
+ product_goal: "text",
152
+ surface_ia_lock: "text",
153
+ decision_lock: "text",
154
+ context_delta: "text",
155
+ source_to_context_coverage: "text",
156
+ acceptance_semantics: "text",
157
+ impact: "text"
158
+ };
159
+ export const PLAN_FIELD_TYPES = {
160
+ delivery_scope: "enum",
161
+ capability_target: "text",
162
+ representative_samples: "array",
163
+ full_population_boundary: "text",
164
+ non_required_population: "array",
165
+ owner_surfaces: "array",
166
+ forbidden_surfaces: "array",
167
+ owner_boundary: "text",
168
+ primary_capability_path: "text",
169
+ trigger_contract: "text",
170
+ state_transition_contract: "text",
171
+ observable_result_contract: "text",
172
+ assertion_support: "text",
173
+ required_assertion_commands: "array",
174
+ invalid_implementation_shortcuts: "array",
175
+ implementation_paths: "array",
176
+ required_tests: "array",
177
+ related_acs: "array",
178
+ requirement_ref: "text",
179
+ decision_id: "text",
180
+ proof_layer_ids: "array",
181
+ api_schema_changes: "text",
182
+ state_machine: "text",
183
+ data_flow: "text",
184
+ worker_runtime_behavior: "text",
185
+ ui_ia_changes: "text",
186
+ migration_plan: "text",
187
+ evidence_artifacts: "array",
188
+ explicit_no_test_scope: "boolean",
189
+ non_completing_shortcuts: "array",
190
+ substitution_policy: "array",
191
+ drift_severity: "text",
192
+ partial_conditions: "array",
193
+ blockers: "array",
194
+ context_fact_refs: "array"
195
+ };
196
+ export const ACCEPTANCE_FIELD_TYPES = {
197
+ acceptance_scope: "enum",
198
+ ac_validates: "array",
199
+ ac_does_not_validate: "array",
200
+ sample_boundary: "text",
201
+ full_population_required: "boolean",
202
+ related_plan_items: "array",
203
+ required_proof_layers: "array",
204
+ assertion_command: "text",
205
+ assertion_artifacts: "array",
206
+ positive_assertions: "array",
207
+ negative_assertions: "array",
208
+ machine_blocking: "boolean",
209
+ invalid_completion_signals: "array",
210
+ assertion_result_required: "boolean",
211
+ ac_type: "text",
212
+ proof_chain: "array",
213
+ verification_method: "array",
214
+ fail_conditions: "array",
215
+ invalid_evidence: "array",
216
+ substitution_policy: "array",
217
+ missing_layer_downgrade: "text",
218
+ auditor_expectation: "text",
219
+ out_of_scope_na_approval_source: "text",
220
+ required_test_ids: "array",
221
+ explicit_no_test_scope: "boolean",
222
+ hard_blockers: "array",
223
+ validates_explanation: "text",
224
+ does_not_validate_explanation: "text",
225
+ final_evidence_expected: "array",
226
+ test_cases: "array"
227
+ };
228
+ export const PRODUCT_DELIVERY_SCOPES = new Set([
229
+ "system_capability_build", "representative_sample_validation", "full_population_operation", "mixed_scope_requires_boundary"
230
+ ]);
231
+ export const PLAN_DELIVERY_SCOPES = new Set([
232
+ "system_capability_build", "representative_sample_validation", "full_population_operation", "out_of_scope_backlog"
233
+ ]);
234
+ export const ACCEPTANCE_SCOPES = new Set([
235
+ "system_capability_build", "representative_sample_validation", "full_population_operation", "full_population_not_required"
236
+ ]);
237
+ export const SCOPE_FIT_DECISIONS = new Set(["fit_for_three_inputs", "selected_from_split", "blocked_for_decision"]);
238
+ export const CANONICAL_PROOF_LAYERS = [
239
+ "code",
240
+ "api_schema",
241
+ "worker_runtime",
242
+ "data_artifact",
243
+ "integration",
244
+ "ui_browser",
245
+ "security_redaction",
246
+ "all_provider_all_runner",
247
+ "cleanup_stale_scan",
248
+ "test"
249
+ ];
250
+ export const LEGACY_PROOF_LAYER_ALIASES = {
251
+ runtime: "worker_runtime",
252
+ browser: "ui_browser",
253
+ api: "api_schema",
254
+ data: "data_artifact",
255
+ security: "security_redaction"
256
+ };
257
+ export const MACHINE_VERIFIABLE_LAYER_NAMES = new Set(CANONICAL_PROOF_LAYERS.filter((layer) => layer !== "code"));
258
+ export function fieldSet(fields) {
259
+ return new Set(fields);
260
+ }
261
+ export function normalizeProofLayerName(layer) {
262
+ const normalized = layer.trim().toLowerCase().replace(/[- ]+/g, "_");
263
+ return LEGACY_PROOF_LAYER_ALIASES[normalized] ?? normalized;
264
+ }
265
+ export function normalizeProofLayerId(layerId) {
266
+ const value = layerId.trim();
267
+ if (!value.includes(".")) {
268
+ return normalizeProofLayerName(value);
269
+ }
270
+ const acId = value.slice(0, value.lastIndexOf("."));
271
+ const layer = value.slice(value.lastIndexOf(".") + 1);
272
+ return `${acId}.${normalizeProofLayerName(layer)}`;
273
+ }
274
+ export function isSelectedScopeFitSlice(value) {
275
+ return value === "none" || /^SFC-\d{3,}$/i.test(value);
276
+ }
@@ -1,8 +1,9 @@
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 { applyTrustedEvidenceKernelResult, evaluateTrustedEvidenceKernel } from "./superpowers-task-evidence-kernel.js";
6
+ import { validateSuperpowersState } from "./superpowers-task-validator.js";
6
7
  export async function runSliceGate(workdir, sliceId) {
7
8
  const state = await loadSuperpowersState(workdir);
8
9
  const slice = state.slices.find((item) => item.slice_id === sliceId);
@@ -24,19 +25,17 @@ export async function runEpochGate(workdir, epochId) {
24
25
  }
25
26
  export async function runFinalGate(workdir) {
26
27
  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" };
28
+ const kernel = await evaluateTrustedEvidenceKernel(workdir, state);
29
+ applyTrustedEvidenceKernelResult(state, kernel);
30
+ state.gates.validator = { status: "not_run", kernel: "trusted_evidence_kernel" };
31
31
  await saveSuperpowersState(workdir, state);
32
32
  await deriveSuperpowersArtifacts(workdir);
33
33
  const report = await validateSuperpowersState(workdir, [workdir]);
34
34
  const acceptanceReport = await validatePlanAcceptance(workdir, [workdir]);
35
35
  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);
36
+ const errors = [...new Set([...kernel.errors, ...report.errors, ...acceptanceReport.errors])];
37
+ const complete = kernel.product_goal_complete && errors.length === 0;
38
+ const acceptanceStatus = complete ? "complete" : acceptanceStatusForErrors(errors, kernel);
40
39
  const nextRequiredActions = complete ? [] : nextActionsForErrors(errors);
41
40
  latest.final.product_goal_complete = complete;
42
41
  latest.meta.product_goal_complete = complete;
@@ -45,25 +44,32 @@ export async function runFinalGate(workdir) {
45
44
  latest.final.audit_task_complete = true;
46
45
  latest.meta.audit_task_complete = true;
47
46
  latest.final.completion_basis = complete
48
- ? ["all_required_acs_complete", "validator_passed", "assertion_evidence_passed", "negative_evidence_scan_passed", "auditor_no_blocker"]
47
+ ? ["trusted_evidence_kernel", "current_attempt_evidence", "negative_evidence_scan_passed", "harness_drift_lock_passed"]
49
48
  : [];
50
49
  latest.final.next_required_actions = nextRequiredActions;
51
50
  latest.gates.validator = { status: errors.length === 0 ? "pass" : "blocked", errors };
52
51
  latest.gates.final_gate = {
53
52
  status: complete ? "pass" : acceptanceStatus,
53
+ kernel: "trusted_evidence_kernel",
54
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"
55
+ "load_three_inputs",
56
+ "recompute_source_hashes",
57
+ "load_task_state",
58
+ "load_current_attempt",
59
+ "load_command_run_records",
60
+ "load_registered_evidence_records",
61
+ "discard_stale_evidence",
62
+ "contradiction_scan",
63
+ "recompute_every_ac",
64
+ "recompute_every_pi",
65
+ "recompute_acceptance_target_status",
66
+ "recompute_product_goal_complete",
67
+ "regenerate_derived",
68
+ "append_event"
65
69
  ],
66
70
  errors,
71
+ stale_evidence_ids: kernel.stale_evidence_ids,
72
+ harness_task_final_verdict: kernel.harness_task_final_verdict,
67
73
  next_required_actions: nextRequiredActions
68
74
  };
69
75
  await saveSuperpowersState(workdir, latest);
@@ -71,12 +77,15 @@ export async function runFinalGate(workdir) {
71
77
  await appendSuperpowersEvent(workdir, "final_gate", { product_goal_complete: complete });
72
78
  return { product_goal_complete: complete, errors };
73
79
  }
74
- function acceptanceStatusForErrors(errors) {
80
+ function acceptanceStatusForErrors(errors, kernel) {
75
81
  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)) {
82
+ if (kernel?.acceptance_target_status === "under_specified" || /under_specified/i.test(text)) {
83
+ return "under_specified";
84
+ }
85
+ 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
86
  return "blocked";
78
87
  }
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)) {
88
+ 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
89
  return "invalidated";
81
90
  }
82
91
  return "partial";
@@ -95,6 +104,9 @@ function nextActionsForErrors(errors) {
95
104
  if (/negative evidence|forbidden/i.test(error)) {
96
105
  return `${error}; rerun the negative evidence scan and fix the contradictory owner-surface state`;
97
106
  }
107
+ if (/current contradiction|playwright_last_run_failed|owner_dom_forbidden_state|failed_test_result_artifact/i.test(error)) {
108
+ return `${error}; rerun the required current assertion command after fixing the current failure`;
109
+ }
98
110
  if (/derived\/.*does not match/i.test(error)) {
99
111
  return `${error}; rerun ty-context composite-long-task derive <workdir>`;
100
112
  }
@@ -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,86 @@
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
+ /superpowers-task-(assertions|evidence|evidence-kernel|current-evidence|command-specs|ac010|gates|validator|derive|state|state-schema|state-shape|under-specified|protected-baseline|harness-drift)\.ts$/i,
8
+ /(^|\/)(\.codex\/ty-context-managed|packages\/ty-context\/assets)\/skills\/composite-long-task-workflow\//i,
9
+ /composite-long-task-workflow-protocol\.md$/i,
10
+ /(^|\/)Makefile$/i,
11
+ /(^|\/)package\.json$/i
12
+ ];
13
+ const REQUIRED_NEGATIVE_FIXTURES = [
14
+ "stale_evidence",
15
+ "historical_complete",
16
+ "derived_contradiction",
17
+ "ac010_summary_only",
18
+ "target_mismatch",
19
+ "api_only_for_ui",
20
+ "negative_evidence_after_pass",
21
+ "source_hash_mismatch",
22
+ "dirty_worktree_mismatch",
23
+ "missing_assertion_result",
24
+ "test_weakening"
25
+ ];
26
+ export function detectHarnessDrift(state) {
27
+ const attempt = currentAttempt(state);
28
+ const changedFiles = changedFilesFor(attempt);
29
+ const harnessFiles = changedFiles.filter(isHarnessPath);
30
+ const mode = attempt?.mode ?? "product_task";
31
+ if (mode === "product_task" && harnessFiles.length > 0) {
32
+ return {
33
+ harness_drift_detected: true,
34
+ acceptance_target_status: "blocked",
35
+ product_goal_complete: false,
36
+ changed_files: harnessFiles,
37
+ errors: [
38
+ `harness_drift_detected: ${harnessFiles.join(", ")}`,
39
+ "本轮修改了验收工具链或测试本身,不能用被修改后的验收证明同一轮产品完成。请拆成独立 harness_task。"
40
+ ]
41
+ };
42
+ }
43
+ if (mode === "harness_task") {
44
+ return harnessTaskFixtureVerdict(state, changedFiles);
45
+ }
46
+ return { harness_drift_detected: false, acceptance_target_status: "complete", product_goal_complete: true, changed_files: [], errors: [] };
47
+ }
48
+ export function isHarnessPath(file) {
49
+ const normalized = file.replace(/\\/g, "/");
50
+ return HARNESS_PATH_PATTERNS.some((pattern) => pattern.test(normalized));
51
+ }
52
+ function harnessTaskFixtureVerdict(state, changedFiles) {
53
+ const fixtureState = isRecord(state.gates?.harness_task_fixtures) ? state.gates.harness_task_fixtures : undefined;
54
+ const errors = [];
55
+ if (!fixtureState) {
56
+ errors.push("harness_task_missing_adversarial_fixtures: harness_task must declare adversarial fixture outcomes");
57
+ }
58
+ else {
59
+ for (const fixture of REQUIRED_NEGATIVE_FIXTURES) {
60
+ if (fixtureState[fixture] !== false) {
61
+ errors.push(`harness_task_missing_adversarial_fixtures: ${fixture} must have expected product_goal_complete=false`);
62
+ }
63
+ }
64
+ if (fixtureState.happy_path !== true) {
65
+ errors.push("harness_task_missing_happy_path_fixture: happy_path must have expected product_goal_complete=true");
66
+ }
67
+ }
68
+ const fixtureChanges = changedFiles.filter((file) => /(^|\/)tests\/ty-context\/.*fixture/i.test(file.replace(/\\/g, "/")));
69
+ if (fixtureChanges.length > 0) {
70
+ errors.push(`fixture_changed_requires_review: ${fixtureChanges.join(", ")}`);
71
+ }
72
+ return {
73
+ harness_drift_detected: false,
74
+ acceptance_target_status: errors.length > 0 ? "blocked" : "complete",
75
+ product_goal_complete: false,
76
+ harness_task_final_verdict: errors.length > 0 ? "failed" : "passed",
77
+ changed_files: changedFiles,
78
+ errors
79
+ };
80
+ }
81
+ function currentAttempt(state) {
82
+ return (state.attempts ?? []).find((item) => item.task_attempt_id === state.current_attempt_id) ?? (state.attempts ?? []).at(-1);
83
+ }
84
+ function changedFilesFor(attempt) {
85
+ return [...new Set((attempt?.changed_files ?? []).map((file) => file.replace(/\\/g, "/")).filter(Boolean))];
86
+ }
@@ -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;