project-tiny-context-harness 0.2.74 → 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.
- package/README.md +48 -42
- package/assets/README.md +48 -42
- package/assets/README.zh-CN.md +56 -52
- package/assets/skills/superpowers-long-task/SKILL.md +586 -471
- package/dist/commands/index.js +16 -9
- package/dist/commands/superpowers.d.ts +1 -0
- package/dist/commands/superpowers.js +89 -0
- package/dist/commands/validate.js +6 -0
- package/dist/lib/plan-acceptance-artifacts.d.ts +1 -0
- package/dist/lib/plan-acceptance-artifacts.js +220 -0
- package/dist/lib/plan-acceptance-evidence.d.ts +1 -0
- package/dist/lib/plan-acceptance-evidence.js +40 -0
- package/dist/lib/plan-acceptance-json.js +4 -0
- package/dist/lib/plan-acceptance-validator.js +23 -4
- package/dist/lib/superpowers-task-compile.d.ts +1 -0
- package/dist/lib/superpowers-task-compile.js +114 -0
- package/dist/lib/superpowers-task-derive.d.ts +13 -0
- package/dist/lib/superpowers-task-derive.js +192 -0
- package/dist/lib/superpowers-task-events.d.ts +1 -0
- package/dist/lib/superpowers-task-events.js +13 -0
- package/dist/lib/superpowers-task-gates.d.ts +12 -0
- package/dist/lib/superpowers-task-gates.js +47 -0
- package/dist/lib/superpowers-task-next-slices.d.ts +1 -0
- package/dist/lib/superpowers-task-next-slices.js +12 -0
- package/dist/lib/superpowers-task-state-schema.d.ts +167 -0
- package/dist/lib/superpowers-task-state-schema.js +43 -0
- package/dist/lib/superpowers-task-state.d.ts +15 -0
- package/dist/lib/superpowers-task-state.js +223 -0
- package/dist/lib/superpowers-task-validator.d.ts +4 -0
- package/dist/lib/superpowers-task-validator.js +238 -0
- package/dist/lib/validators.d.ts +2 -0
- package/dist/lib/validators.js +6 -2
- package/package.json +69 -69
|
@@ -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;
|