project-tiny-context-harness 0.2.75 → 0.2.76

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.
@@ -0,0 +1,114 @@
1
+ import path from "node:path";
2
+ import { readText } from "./fs.js";
3
+ import { appendSuperpowersEvent } from "./superpowers-task-events.js";
4
+ import { loadSuperpowersState, recomputeStatuses, saveSuperpowersState, refreshSourceHashes } from "./superpowers-task-state.js";
5
+ import { asStringArray } from "./superpowers-task-state-schema.js";
6
+ const DEFAULT_LAYERS = ["code", "test"];
7
+ export async function compileSuperpowersTask(workdir) {
8
+ const state = await loadSuperpowersState(workdir);
9
+ await refreshSourceHashes(workdir, state);
10
+ const technicalPlan = await readText(path.join(workdir, state.sources.technical_realization_plan.path));
11
+ const checklist = await readText(path.join(workdir, state.sources.acceptance_checklist.path));
12
+ const planItems = parsePlanItems(technicalPlan);
13
+ const acceptanceCriteria = parseAcceptanceCriteria(checklist);
14
+ const acIds = Object.keys(acceptanceCriteria);
15
+ for (const [planId, item] of Object.entries(planItems)) {
16
+ if (item.related_acs.length === 0) {
17
+ item.related_acs = acIds;
18
+ }
19
+ item.required_proof_layers = item.related_acs.flatMap((acId) => (acceptanceCriteria[acId]?.required_proof_layers ?? DEFAULT_LAYERS).map((layer) => `${acId}.${layer}`));
20
+ }
21
+ for (const [acId, ac] of Object.entries(acceptanceCriteria)) {
22
+ if (ac.related_plan_items.length === 0) {
23
+ ac.related_plan_items = Object.keys(planItems);
24
+ }
25
+ }
26
+ state.graph.plan_items = planItems;
27
+ state.graph.acceptance_criteria = acceptanceCriteria;
28
+ state.graph.proof_layers = {};
29
+ for (const [acId, ac] of Object.entries(acceptanceCriteria)) {
30
+ for (const layer of ac.required_proof_layers) {
31
+ state.graph.proof_layers[`${acId}.${layer}`] = { required: true, status: "missing", evidence_ids: [] };
32
+ }
33
+ }
34
+ state.graph.edges = Object.entries(planItems).flatMap(([planId, item]) => item.related_acs.map((acId) => ({ from: planId, to: acId, type: "supports" })));
35
+ recomputeStatuses(state);
36
+ await saveSuperpowersState(workdir, state);
37
+ await appendSuperpowersEvent(workdir, "graph_compiled", {
38
+ plan_items: Object.keys(planItems).length,
39
+ acceptance_criteria: Object.keys(acceptanceCriteria).length
40
+ });
41
+ return state;
42
+ }
43
+ function parsePlanItems(content) {
44
+ const items = {};
45
+ const matches = [...content.matchAll(/\b(PI-\d{3,})\b\s*[:.-]?\s*([^\n]*)/gi)];
46
+ for (const [index, match] of matches.entries()) {
47
+ const id = match[1].toUpperCase();
48
+ const block = blockAfter(content, match.index ?? 0, matches[index + 1]?.index);
49
+ items[id] = {
50
+ requirement: cleanText(match[2]) || firstLine(block) || id,
51
+ owner_surfaces: field(block, "owner_surfaces"),
52
+ forbidden_surfaces: field(block, "forbidden_surfaces"),
53
+ implementation_paths: field(block, "implementation_paths"),
54
+ required_tests: field(block, "required_tests"),
55
+ status: "not_started",
56
+ related_acs: field(block, "related_acs").map((item) => item.toUpperCase()),
57
+ required_proof_layers: []
58
+ };
59
+ }
60
+ if (Object.keys(items).length === 0) {
61
+ items["PI-001"] = {
62
+ requirement: firstLine(content) || "Implement technical realization plan",
63
+ owner_surfaces: [],
64
+ forbidden_surfaces: [],
65
+ implementation_paths: [],
66
+ required_tests: [],
67
+ status: "not_started",
68
+ related_acs: [],
69
+ required_proof_layers: []
70
+ };
71
+ }
72
+ return items;
73
+ }
74
+ function parseAcceptanceCriteria(content) {
75
+ const items = {};
76
+ const matches = [...content.matchAll(/\b(AC-\d{3,})\b\s*[:.-]?\s*([^\n]*)/gi)];
77
+ for (const [index, match] of matches.entries()) {
78
+ const id = match[1].toUpperCase();
79
+ const block = blockAfter(content, match.index ?? 0, matches[index + 1]?.index);
80
+ const layers = field(block, "required_proof_layers").map(normalizeLayer).filter(Boolean);
81
+ items[id] = {
82
+ scope: cleanText(match[2]) || firstLine(block) || id,
83
+ related_plan_items: field(block, "related_plan_items").map((item) => item.toUpperCase()),
84
+ required_proof_layers: layers.length > 0 ? layers : DEFAULT_LAYERS,
85
+ status: "not_run"
86
+ };
87
+ }
88
+ if (Object.keys(items).length === 0) {
89
+ items["AC-001"] = {
90
+ scope: firstLine(content) || "Acceptance checklist item",
91
+ related_plan_items: [],
92
+ required_proof_layers: DEFAULT_LAYERS,
93
+ status: "not_run"
94
+ };
95
+ }
96
+ return items;
97
+ }
98
+ function blockAfter(content, start, end) {
99
+ return content.slice(start, end ?? content.length);
100
+ }
101
+ function field(block, name) {
102
+ const pattern = new RegExp(`${name}\\s*:\\s*([^\\n]+)`, "i");
103
+ const match = pattern.exec(block);
104
+ return match ? asStringArray(match[1]) : [];
105
+ }
106
+ function normalizeLayer(value) {
107
+ return value.trim().toLowerCase().replace(/[- ]+/g, "_");
108
+ }
109
+ function firstLine(content) {
110
+ return cleanText(content.split(/\r?\n/).find((line) => cleanText(line)) ?? "");
111
+ }
112
+ function cleanText(value) {
113
+ return value.replace(/^[-#*\s]+/, "").trim();
114
+ }
@@ -0,0 +1,13 @@
1
+ import { type SuperpowersTaskState } from "./superpowers-task-state-schema.js";
2
+ export interface DerivedSuperpowersArtifacts {
3
+ matrix: Record<string, unknown>;
4
+ verdict: Record<string, unknown>;
5
+ files: string[];
6
+ }
7
+ export declare function deriveSuperpowersArtifacts(workdir: string): Promise<DerivedSuperpowersArtifacts>;
8
+ export declare function deriveObjects(state: SuperpowersTaskState): {
9
+ matrix: Record<string, unknown>;
10
+ verdict: Record<string, unknown>;
11
+ progress: Record<string, unknown>;
12
+ };
13
+ export declare function derivedMatchesState(workdir: string, state: SuperpowersTaskState): Promise<string[]>;
@@ -0,0 +1,192 @@
1
+ import path from "node:path";
2
+ import { ensureDir, pathExists, readText, writeTextIfChanged } from "./fs.js";
3
+ import { stableJson, loadSuperpowersState } from "./superpowers-task-state.js";
4
+ export async function deriveSuperpowersArtifacts(workdir) {
5
+ const state = await loadSuperpowersState(workdir);
6
+ const derived = deriveObjects(state);
7
+ const derivedDir = path.join(workdir, "derived");
8
+ await ensureDir(derivedDir);
9
+ const files = [];
10
+ await writeDerived(files, path.join(derivedDir, "plan-conformance-matrix.json"), stableJson(derived.matrix));
11
+ await writeDerived(files, path.join(derivedDir, "final-acceptance-verdict.json"), stableJson(derived.verdict));
12
+ await writeDerived(files, path.join(derivedDir, "progress-ledger.json"), stableJson(derived.progress));
13
+ await writeDerived(files, path.join(derivedDir, "plan-conformance-matrix.md"), matrixMarkdown(derived.matrix));
14
+ await writeDerived(files, path.join(derivedDir, "final-acceptance-verdict.md"), verdictMarkdown(derived.verdict));
15
+ await writeDerived(files, path.join(derivedDir, "local-audit.md"), localAuditMarkdown(state));
16
+ await writeDerived(files, path.join(derivedDir, "progress-ledger.md"), progressMarkdown(derived.progress));
17
+ await writeDerived(files, path.join(derivedDir, "evidence-index.md"), evidenceMarkdown(state));
18
+ await writeDerived(files, path.join(derivedDir, "context-alignment.md"), contextMarkdown(state));
19
+ await writeDerived(files, path.join(derivedDir, "final-summary.md"), finalSummaryMarkdown(state, derived.verdict));
20
+ return { matrix: derived.matrix, verdict: derived.verdict, files };
21
+ }
22
+ export function deriveObjects(state) {
23
+ const matrixRows = Object.entries(state.graph.plan_items).map(([planItemId, item]) => {
24
+ const relatedAcs = item.related_acs;
25
+ const requiredLayers = item.required_proof_layers;
26
+ const missingLayers = requiredLayers.filter((layerId) => state.graph.proof_layers[layerId]?.status !== "satisfied");
27
+ const evidenceIds = evidenceForLayers(state, requiredLayers);
28
+ return {
29
+ plan_item_id: planItemId,
30
+ plan_requirement: item.requirement,
31
+ acceptance_ids: relatedAcs,
32
+ status: missingLayers.length === 0 && requiredLayers.length > 0 ? "complete" : evidenceIds.length > 0 ? "partial" : item.status,
33
+ conformance_type: item.owner_surfaces.length > 0 ? "product_surface" : "implementation",
34
+ owner_surface: item.owner_surfaces[0] ?? "",
35
+ forbidden_primary_surfaces: item.forbidden_surfaces,
36
+ negative_surface_checks: item.forbidden_surfaces.map((surface) => `confirmed ${surface} is not the owner surface`),
37
+ default_visibility_required: item.owner_surfaces.length > 0,
38
+ real_page_evidence: evidenceText(state, evidenceIds, "browser"),
39
+ context_fact_refs: [],
40
+ expected_surfaces: item.owner_surfaces.length > 0 ? ["ui", "runtime"] : ["code"],
41
+ implemented_paths: item.implementation_paths,
42
+ missing_paths: [],
43
+ tests: item.required_tests,
44
+ runtime_evidence: evidenceText(state, evidenceIds, "runtime"),
45
+ artifact_evidence: evidenceText(state, evidenceIds, "artifact"),
46
+ required_proof_layers: requiredLayers,
47
+ satisfied_proof_layers: requiredLayers.filter((layerId) => state.graph.proof_layers[layerId]?.status === "satisfied"),
48
+ missing_required_layers: missingLayers,
49
+ evidence_ids: evidenceIds,
50
+ scope_assessment: missingLayers.length === 0 ? "full" : "partial",
51
+ drift: missingLayers.length === 0 ? "no drift detected" : "missing required proof layers"
52
+ };
53
+ });
54
+ const verdictRows = Object.entries(state.graph.acceptance_criteria).map(([acId, ac]) => {
55
+ const requiredLayers = ac.required_proof_layers.map((layer) => `${acId}.${layer}`);
56
+ const missingLayers = requiredLayers.filter((layerId) => state.graph.proof_layers[layerId]?.status !== "satisfied");
57
+ const evidenceIds = evidenceForLayers(state, requiredLayers);
58
+ const status = missingLayers.length === 0 && requiredLayers.length > 0 ? "complete" : evidenceIds.length > 0 ? "partial" : ac.status;
59
+ return {
60
+ ac_id: acId,
61
+ related_plan_item_ids: ac.related_plan_items,
62
+ status,
63
+ required_evidence: requiredLayers,
64
+ required_proof_chain: requiredLayers,
65
+ fresh_evidence: evidenceText(state, evidenceIds),
66
+ missing_evidence: [],
67
+ missing_required_layers: missingLayers,
68
+ contradictions: [],
69
+ context_fact_refs: [],
70
+ evidence_ids: evidenceIds,
71
+ drift_severity: "none",
72
+ sibling_substitution_used: false,
73
+ auditor_status: auditorStatus(state),
74
+ decision: status === "complete" ? "accept" : "continue"
75
+ };
76
+ });
77
+ const allComplete = verdictRows.length > 0 && verdictRows.every((row) => row.status === "complete");
78
+ const progress = {
79
+ complete_count: verdictRows.filter((row) => row.status === "complete").length,
80
+ partial_count: verdictRows.filter((row) => row.status === "partial").length,
81
+ acceptance_required_count: verdictRows.filter((row) => row.status !== "out_of_scope_NA").length,
82
+ missing_layer_count: verdictRows.reduce((sum, row) => sum + row.missing_required_layers.length, 0),
83
+ next_clusters: Object.entries(state.graph.proof_layers)
84
+ .filter(([, layer]) => layer.status !== "satisfied")
85
+ .slice(0, 5)
86
+ .map(([layerId]) => layerId)
87
+ };
88
+ return {
89
+ matrix: { overall_status: allComplete ? "complete" : "partial", items: matrixRows },
90
+ verdict: { overall_status: allComplete ? "complete" : "partial", acceptance_items: verdictRows },
91
+ progress
92
+ };
93
+ }
94
+ export async function derivedMatchesState(workdir, state) {
95
+ const errors = [];
96
+ const expected = deriveObjects(state);
97
+ await assertDerivedJson(workdir, "plan-conformance-matrix", expected.matrix, errors);
98
+ await assertDerivedJson(workdir, "final-acceptance-verdict", expected.verdict, errors);
99
+ return errors;
100
+ }
101
+ async function assertDerivedJson(workdir, basename, expected, errors) {
102
+ const file = path.join(workdir, "derived", `${basename}.json`);
103
+ if (!(await pathExists(file))) {
104
+ return;
105
+ }
106
+ const actual = JSON.parse(await readText(file));
107
+ if (stableJson(actual) !== stableJson(expected)) {
108
+ errors.push(`derived/${basename}.json does not match task-state.json; rerun ty-context superpowers derive`);
109
+ }
110
+ }
111
+ function evidenceForLayers(state, layerIds) {
112
+ return [...new Set(layerIds.flatMap((layerId) => state.graph.proof_layers[layerId]?.evidence_ids ?? []))];
113
+ }
114
+ function evidenceText(state, evidenceIds, type) {
115
+ return evidenceIds
116
+ .map((evidenceId) => state.evidence.find((item) => item.evidence_id === evidenceId))
117
+ .filter((item) => item && (!type || item.type === type || (type === "artifact" && item.artifact_paths.length > 0)))
118
+ .map((item) => {
119
+ const artifacts = item?.artifact_paths.join(", ");
120
+ return `${item?.type} ${item?.command ?? ""} ${artifacts}`.trim();
121
+ });
122
+ }
123
+ function auditorStatus(state) {
124
+ const auditor = state.gates.auditor;
125
+ if (auditor && typeof auditor === "object" && !Array.isArray(auditor) && typeof auditor.auditor_status === "string") {
126
+ return String(auditor.auditor_status);
127
+ }
128
+ return "not_run";
129
+ }
130
+ async function writeDerived(files, file, content) {
131
+ await writeTextIfChanged(file, `${content.trimEnd()}\n`);
132
+ files.push(file.split(path.sep).join("/"));
133
+ }
134
+ function matrixMarkdown(matrix) {
135
+ const rows = matrix.items ?? [];
136
+ return ["# Plan Conformance Matrix", "", ...rows.map((row) => `- ${row.plan_item_id}: ${row.status}`)].join("\n");
137
+ }
138
+ function verdictMarkdown(verdict) {
139
+ const rows = verdict.acceptance_items ?? [];
140
+ const complete = rows.filter((row) => row.status === "complete").length;
141
+ const partial = rows.filter((row) => row.status === "partial").length;
142
+ const required = rows.filter((row) => row.status !== "out_of_scope_NA").length;
143
+ const missing = rows.reduce((sum, row) => sum + ((row.missing_required_layers ?? []).length), 0);
144
+ return `# Final Acceptance Verdict
145
+
146
+ <!-- generated:active-counts:start -->
147
+ complete_count: ${complete}
148
+ partial_count: ${partial}
149
+ acceptance_required_count: ${required}
150
+ missing_layer_count: ${missing}
151
+ <!-- generated:active-counts:end -->
152
+
153
+ ${rows.map((row) => `- ${row.ac_id}: ${row.status}`).join("\n")}
154
+ `;
155
+ }
156
+ function localAuditMarkdown(state) {
157
+ return `# Local Audit
158
+
159
+ audit_task_complete: ${state.final.audit_task_complete}
160
+ acceptance_target_status: ${state.final.acceptance_target_status}
161
+ product_goal_complete: ${state.final.product_goal_complete}
162
+ `;
163
+ }
164
+ function progressMarkdown(progress) {
165
+ return `# Progress Ledger
166
+
167
+ \`\`\`json
168
+ ${stableJson(progress)}
169
+ \`\`\`
170
+ `;
171
+ }
172
+ function evidenceMarkdown(state) {
173
+ return [
174
+ "# Evidence Index",
175
+ "",
176
+ ...state.evidence.map((item) => `- ${item.evidence_id}: proves ${item.proves.join(", ")}; does_not_prove ${item.does_not_prove.join(", ")}`)
177
+ ].join("\n");
178
+ }
179
+ function contextMarkdown(state) {
180
+ return `# Context Alignment
181
+
182
+ Product Context Delta: ${state.context.product_context_delta}
183
+ Technical Context Delta: ${state.context.technical_context_delta}
184
+ `;
185
+ }
186
+ function finalSummaryMarkdown(state, verdict) {
187
+ return `# Final Summary
188
+
189
+ overall_status: ${verdict.overall_status}
190
+ product_goal_complete: ${state.final.product_goal_complete}
191
+ `;
192
+ }
@@ -0,0 +1 @@
1
+ export declare function appendSuperpowersEvent(workdir: string, eventType: string, payload?: Record<string, unknown>): Promise<void>;
@@ -0,0 +1,13 @@
1
+ import path from "node:path";
2
+ import { ensureDir, readText, writeTextIfChanged, pathExists } from "./fs.js";
3
+ export async function appendSuperpowersEvent(workdir, eventType, payload = {}) {
4
+ const eventsPath = path.join(workdir, "events.ndjson");
5
+ await ensureDir(workdir);
6
+ const event = {
7
+ event_type: eventType,
8
+ created_at: new Date().toISOString(),
9
+ ...payload
10
+ };
11
+ const previous = (await pathExists(eventsPath)) ? await readText(eventsPath) : "";
12
+ await writeTextIfChanged(eventsPath, `${previous}${JSON.stringify(event)}\n`);
13
+ }
@@ -0,0 +1,12 @@
1
+ export declare function runSliceGate(workdir: string, sliceId: string): Promise<{
2
+ passed: boolean;
3
+ messages: string[];
4
+ }>;
5
+ export declare function runEpochGate(workdir: string, epochId: string): Promise<{
6
+ passed: boolean;
7
+ messages: string[];
8
+ }>;
9
+ export declare function runFinalGate(workdir: string): Promise<{
10
+ product_goal_complete: boolean;
11
+ errors: string[];
12
+ }>;
@@ -0,0 +1,47 @@
1
+ import { appendSuperpowersEvent } from "./superpowers-task-events.js";
2
+ import { deriveSuperpowersArtifacts } from "./superpowers-task-derive.js";
3
+ import { loadSuperpowersState, recomputeStatuses, saveSuperpowersState } from "./superpowers-task-state.js";
4
+ import { allCompletionConditionsSatisfied, validateSuperpowersState } from "./superpowers-task-validator.js";
5
+ export async function runSliceGate(workdir, sliceId) {
6
+ const state = await loadSuperpowersState(workdir);
7
+ const slice = state.slices.find((item) => item.slice_id === sliceId);
8
+ const messages = [];
9
+ if (!slice) {
10
+ messages.push(`slice not found: ${sliceId}`);
11
+ }
12
+ else if (!slice.progress_value?.type || slice.progress_value.closed_items.length === 0) {
13
+ messages.push(`slice ${sliceId} has no progress_value`);
14
+ }
15
+ await deriveSuperpowersArtifacts(workdir);
16
+ await appendSuperpowersEvent(workdir, "slice_gate", { slice_id: sliceId, passed: messages.length === 0 });
17
+ return { passed: messages.length === 0, messages };
18
+ }
19
+ export async function runEpochGate(workdir, epochId) {
20
+ await deriveSuperpowersArtifacts(workdir);
21
+ await appendSuperpowersEvent(workdir, "epoch_gate", { epoch_id: epochId, passed: true });
22
+ return { passed: true, messages: ["epoch derived artifacts refreshed"] };
23
+ }
24
+ export async function runFinalGate(workdir) {
25
+ const state = await loadSuperpowersState(workdir);
26
+ recomputeStatuses(state);
27
+ state.final.audit_task_complete = true;
28
+ state.meta.audit_task_complete = true;
29
+ state.gates.validator = { status: "not_run" };
30
+ await saveSuperpowersState(workdir, state);
31
+ await deriveSuperpowersArtifacts(workdir);
32
+ const report = await validateSuperpowersState(workdir, [workdir]);
33
+ const latest = await loadSuperpowersState(workdir);
34
+ const complete = report.errors.length === 0 && allCompletionConditionsSatisfied(latest);
35
+ latest.final.product_goal_complete = complete;
36
+ latest.meta.product_goal_complete = complete;
37
+ latest.final.acceptance_target_status = complete ? "complete" : "partial";
38
+ latest.meta.acceptance_target_status = latest.final.acceptance_target_status;
39
+ latest.final.audit_task_complete = true;
40
+ latest.meta.audit_task_complete = true;
41
+ latest.final.completion_basis = complete ? ["all_required_acs_complete", "validator_passed", "auditor_no_blocker"] : [];
42
+ latest.gates.validator = { status: report.errors.length === 0 ? "pass" : "blocked", errors: report.errors };
43
+ await saveSuperpowersState(workdir, latest);
44
+ await deriveSuperpowersArtifacts(workdir);
45
+ await appendSuperpowersEvent(workdir, "final_gate", { product_goal_complete: complete });
46
+ return { product_goal_complete: complete, errors: report.errors };
47
+ }
@@ -0,0 +1 @@
1
+ export declare function nextSuperpowersSlices(workdir: string, limit?: number): Promise<string[]>;
@@ -0,0 +1,12 @@
1
+ import { loadSuperpowersState } from "./superpowers-task-state.js";
2
+ export async function nextSuperpowersSlices(workdir, limit = 5) {
3
+ const state = await loadSuperpowersState(workdir);
4
+ return Object.entries(state.graph.proof_layers)
5
+ .filter(([, layer]) => layer.required && layer.status !== "satisfied")
6
+ .slice(0, limit)
7
+ .map(([layerId], index) => {
8
+ const acId = layerId.split(".")[0];
9
+ const planId = Object.entries(state.graph.plan_items).find(([, item]) => item.related_acs.includes(acId))?.[0] ?? "PI";
10
+ return `${index + 1}. ${planId}/${acId}: close ${layerId}`;
11
+ });
12
+ }
@@ -0,0 +1,167 @@
1
+ export declare const SUPERPOWERS_TASK_STATE_SCHEMA_VERSION = "superpowers-task-state-v1";
2
+ export declare const SUPERPOWERS_TASK_STATE_JSON_SCHEMA: {
3
+ readonly $schema: "https://json-schema.org/draft/2020-12/schema";
4
+ readonly $id: "https://project-tiny-context-harness.local/superpowers-task-state.schema.json";
5
+ readonly title: "Superpowers Long-Task State";
6
+ readonly type: "object";
7
+ readonly required: readonly ["meta", "sources", "context", "graph", "slices", "evidence", "gates", "progress", "blockers", "final"];
8
+ readonly properties: {
9
+ readonly meta: {
10
+ readonly type: "object";
11
+ readonly required: readonly ["task_id", "plan_slug", "created_at", "updated_at", "schema_version", "product_goal_complete", "acceptance_target_status"];
12
+ readonly properties: {
13
+ readonly schema_version: {
14
+ readonly const: "superpowers-task-state-v1";
15
+ };
16
+ readonly product_goal_complete: {
17
+ readonly type: "boolean";
18
+ };
19
+ readonly acceptance_target_status: {
20
+ readonly type: "string";
21
+ };
22
+ };
23
+ };
24
+ readonly sources: {
25
+ readonly type: "object";
26
+ };
27
+ readonly context: {
28
+ readonly type: "object";
29
+ };
30
+ readonly graph: {
31
+ readonly type: "object";
32
+ };
33
+ readonly slices: {
34
+ readonly type: "array";
35
+ };
36
+ readonly evidence: {
37
+ readonly type: "array";
38
+ };
39
+ readonly gates: {
40
+ readonly type: "object";
41
+ };
42
+ readonly progress: {
43
+ readonly type: "object";
44
+ };
45
+ readonly blockers: {
46
+ readonly type: "array";
47
+ };
48
+ readonly final: {
49
+ readonly type: "object";
50
+ };
51
+ };
52
+ };
53
+ export type SuperpowersProofLayerStatus = "missing" | "satisfied" | "invalidated" | "blocked";
54
+ export type SuperpowersPlanItemStatus = "not_started" | "complete" | "partial" | "sampled_only" | "not_implemented" | "blocked" | "scope_changed_requires_user_approval" | "contradicted_by_current_state" | "out_of_scope_NA";
55
+ export type SuperpowersAcceptanceStatus = "not_run" | "complete" | "partial" | "blocked" | "invalidated" | "out_of_scope_NA";
56
+ export interface SuperpowersTaskState {
57
+ meta: {
58
+ task_id: string;
59
+ plan_slug: string;
60
+ created_at: string;
61
+ updated_at: string;
62
+ schema_version: typeof SUPERPOWERS_TASK_STATE_SCHEMA_VERSION;
63
+ goal_type: string;
64
+ product_goal_complete: boolean;
65
+ acceptance_target_status: string;
66
+ audit_task_complete: boolean;
67
+ };
68
+ sources: Record<string, SuperpowersSourceRecord>;
69
+ context: {
70
+ product_context_delta: "none" | "required";
71
+ technical_context_delta: "none" | "required";
72
+ source_to_context_coverage: Record<string, unknown>[];
73
+ context_to_implementation_binding: Record<string, unknown>[];
74
+ };
75
+ graph: {
76
+ plan_items: Record<string, SuperpowersPlanItem>;
77
+ acceptance_criteria: Record<string, SuperpowersAcceptanceCriterion>;
78
+ proof_layers: Record<string, SuperpowersProofLayer>;
79
+ edges: SuperpowersGraphEdge[];
80
+ };
81
+ slices: SuperpowersSliceRecord[];
82
+ evidence: SuperpowersEvidenceRecord[];
83
+ gates: Record<string, unknown>;
84
+ progress: Record<string, unknown>;
85
+ blockers: unknown[];
86
+ final: {
87
+ product_goal_complete: boolean;
88
+ acceptance_target_status: string;
89
+ audit_task_complete: boolean;
90
+ completion_basis: string[];
91
+ };
92
+ }
93
+ export interface SuperpowersSourceRecord {
94
+ path: string;
95
+ sha256: string;
96
+ authority: string;
97
+ }
98
+ export interface SuperpowersPlanItem {
99
+ requirement: string;
100
+ owner_surfaces: string[];
101
+ forbidden_surfaces: string[];
102
+ implementation_paths: string[];
103
+ required_tests: string[];
104
+ status: SuperpowersPlanItemStatus;
105
+ related_acs: string[];
106
+ required_proof_layers: string[];
107
+ }
108
+ export interface SuperpowersAcceptanceCriterion {
109
+ scope: string;
110
+ related_plan_items: string[];
111
+ required_proof_layers: string[];
112
+ status: SuperpowersAcceptanceStatus;
113
+ }
114
+ export interface SuperpowersProofLayer {
115
+ required: boolean;
116
+ status: SuperpowersProofLayerStatus;
117
+ evidence_ids: string[];
118
+ }
119
+ export interface SuperpowersGraphEdge {
120
+ from: string;
121
+ to: string;
122
+ type: "supports" | string;
123
+ }
124
+ export interface SuperpowersSliceRecord {
125
+ slice_id: string;
126
+ slice_goal: string;
127
+ touched_plan_items: string[];
128
+ touched_acs: string[];
129
+ missing_layer_classes?: string[];
130
+ code_changes: string[];
131
+ evidence_records: string[];
132
+ closed_layers: string[];
133
+ remaining_layers: string[];
134
+ blockers: unknown[];
135
+ cleanup_assertions: string[];
136
+ progress_value: {
137
+ type: string;
138
+ closed_items: string[];
139
+ why_it_reduces_rework: string;
140
+ };
141
+ }
142
+ export interface SuperpowersEvidenceRecord {
143
+ evidence_id: string;
144
+ slice_id: string;
145
+ type: string;
146
+ freshness: {
147
+ created_at: string;
148
+ valid_for: string;
149
+ stale_after: string | null;
150
+ };
151
+ command?: string;
152
+ artifact_paths: string[];
153
+ proves: string[];
154
+ does_not_prove: string[];
155
+ redaction: {
156
+ checked: boolean;
157
+ contains_secret: boolean;
158
+ };
159
+ reviewability: {
160
+ external_reviewer_can_reproduce: boolean;
161
+ reproduction_steps: string;
162
+ };
163
+ sibling_substitution_used?: boolean;
164
+ sibling_substitution_approval_source?: string;
165
+ }
166
+ export declare function isRecord(value: unknown): value is Record<string, unknown>;
167
+ export declare function asStringArray(value: unknown): string[];
@@ -0,0 +1,43 @@
1
+ export const SUPERPOWERS_TASK_STATE_SCHEMA_VERSION = "superpowers-task-state-v1";
2
+ export const SUPERPOWERS_TASK_STATE_JSON_SCHEMA = {
3
+ $schema: "https://json-schema.org/draft/2020-12/schema",
4
+ $id: "https://project-tiny-context-harness.local/superpowers-task-state.schema.json",
5
+ title: "Superpowers Long-Task State",
6
+ type: "object",
7
+ required: ["meta", "sources", "context", "graph", "slices", "evidence", "gates", "progress", "blockers", "final"],
8
+ properties: {
9
+ meta: {
10
+ type: "object",
11
+ required: ["task_id", "plan_slug", "created_at", "updated_at", "schema_version", "product_goal_complete", "acceptance_target_status"],
12
+ properties: {
13
+ schema_version: { const: SUPERPOWERS_TASK_STATE_SCHEMA_VERSION },
14
+ product_goal_complete: { type: "boolean" },
15
+ acceptance_target_status: { type: "string" }
16
+ }
17
+ },
18
+ sources: { type: "object" },
19
+ context: { type: "object" },
20
+ graph: { type: "object" },
21
+ slices: { type: "array" },
22
+ evidence: { type: "array" },
23
+ gates: { type: "object" },
24
+ progress: { type: "object" },
25
+ blockers: { type: "array" },
26
+ final: { type: "object" }
27
+ }
28
+ };
29
+ export function isRecord(value) {
30
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
31
+ }
32
+ export function asStringArray(value) {
33
+ if (Array.isArray(value)) {
34
+ return value.flatMap(asStringArray);
35
+ }
36
+ if (value === undefined || value === null) {
37
+ return [];
38
+ }
39
+ return String(value)
40
+ .split(/[,;\n]/)
41
+ .map((item) => item.trim())
42
+ .filter(Boolean);
43
+ }
@@ -0,0 +1,15 @@
1
+ import { type SuperpowersTaskState } from "./superpowers-task-state-schema.js";
2
+ export interface InitializeSuperpowersTaskOptions {
3
+ taskId?: string;
4
+ planSlug?: string;
5
+ goalType?: string;
6
+ }
7
+ export declare function initializeSuperpowersTask(workdir: string, options?: InitializeSuperpowersTaskOptions): Promise<SuperpowersTaskState>;
8
+ export declare function loadSuperpowersState(workdir: string): Promise<SuperpowersTaskState>;
9
+ export declare function saveSuperpowersState(workdir: string, state: SuperpowersTaskState): Promise<void>;
10
+ export declare function applySliceDelta(workdir: string, deltaFile: string): Promise<SuperpowersTaskState>;
11
+ export declare function refreshSourceHashes(workdir: string, state: SuperpowersTaskState): Promise<void>;
12
+ export declare function recomputeStatuses(state: SuperpowersTaskState): void;
13
+ export declare function sourceRecords(workdir: string): Promise<SuperpowersTaskState["sources"]>;
14
+ export declare function sha256(value: string): string;
15
+ export declare function stableJson(value: unknown): string;