project-tiny-context-harness 0.2.75 → 0.2.77
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 +14 -10
- package/assets/README.md +15 -11
- package/assets/README.zh-CN.md +8 -6
- package/assets/skills/context_development_engineer/SKILL.md +44 -42
- package/assets/skills/superpowers-long-task/SKILL.md +135 -73
- package/dist/commands/index.js +16 -9
- package/dist/commands/superpowers.d.ts +1 -0
- package/dist/commands/superpowers.js +89 -0
- package/dist/lib/plan-acceptance-validator.js +15 -0
- package/dist/lib/superpowers-task-compile.d.ts +3 -0
- package/dist/lib/superpowers-task-compile.js +223 -0
- package/dist/lib/superpowers-task-delivery.d.ts +4 -0
- package/dist/lib/superpowers-task-delivery.js +84 -0
- package/dist/lib/superpowers-task-derive.d.ts +13 -0
- package/dist/lib/superpowers-task-derive.js +204 -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 +202 -0
- package/dist/lib/superpowers-task-state-schema.js +44 -0
- package/dist/lib/superpowers-task-state.d.ts +16 -0
- package/dist/lib/superpowers-task-state.js +241 -0
- package/dist/lib/superpowers-task-validator.d.ts +4 -0
- package/dist/lib/superpowers-task-validator.js +252 -0
- package/dist/lib/validators.js +4 -2
- package/package.json +1 -1
|
@@ -0,0 +1,202 @@
|
|
|
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", "delivery", "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 delivery: {
|
|
31
|
+
readonly type: "object";
|
|
32
|
+
};
|
|
33
|
+
readonly graph: {
|
|
34
|
+
readonly type: "object";
|
|
35
|
+
};
|
|
36
|
+
readonly slices: {
|
|
37
|
+
readonly type: "array";
|
|
38
|
+
};
|
|
39
|
+
readonly evidence: {
|
|
40
|
+
readonly type: "array";
|
|
41
|
+
};
|
|
42
|
+
readonly gates: {
|
|
43
|
+
readonly type: "object";
|
|
44
|
+
};
|
|
45
|
+
readonly progress: {
|
|
46
|
+
readonly type: "object";
|
|
47
|
+
};
|
|
48
|
+
readonly blockers: {
|
|
49
|
+
readonly type: "array";
|
|
50
|
+
};
|
|
51
|
+
readonly final: {
|
|
52
|
+
readonly type: "object";
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
export type SuperpowersProofLayerStatus = "missing" | "satisfied" | "invalidated" | "blocked";
|
|
57
|
+
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";
|
|
58
|
+
export type SuperpowersAcceptanceStatus = "not_run" | "complete" | "partial" | "blocked" | "invalidated" | "out_of_scope_NA";
|
|
59
|
+
export type SuperpowersProductDeliveryScope = "system_capability_build" | "representative_sample_validation" | "full_population_operation" | "mixed_scope_requires_boundary";
|
|
60
|
+
export type SuperpowersPlanDeliveryScope = "system_capability_build" | "representative_sample_validation" | "full_population_operation" | "out_of_scope_backlog";
|
|
61
|
+
export type SuperpowersAcceptanceScope = "system_capability_build" | "representative_sample_validation" | "full_population_operation" | "full_population_not_required";
|
|
62
|
+
export interface SuperpowersTaskState {
|
|
63
|
+
meta: {
|
|
64
|
+
task_id: string;
|
|
65
|
+
plan_slug: string;
|
|
66
|
+
created_at: string;
|
|
67
|
+
updated_at: string;
|
|
68
|
+
schema_version: typeof SUPERPOWERS_TASK_STATE_SCHEMA_VERSION;
|
|
69
|
+
goal_type: string;
|
|
70
|
+
product_goal_complete: boolean;
|
|
71
|
+
acceptance_target_status: string;
|
|
72
|
+
audit_task_complete: boolean;
|
|
73
|
+
};
|
|
74
|
+
sources: Record<string, SuperpowersSourceRecord>;
|
|
75
|
+
context: {
|
|
76
|
+
product_context_delta: "none" | "required";
|
|
77
|
+
technical_context_delta: "none" | "required";
|
|
78
|
+
source_to_context_coverage: Record<string, unknown>[];
|
|
79
|
+
context_to_implementation_binding: Record<string, unknown>[];
|
|
80
|
+
};
|
|
81
|
+
delivery: SuperpowersDeliveryState;
|
|
82
|
+
graph: {
|
|
83
|
+
plan_items: Record<string, SuperpowersPlanItem>;
|
|
84
|
+
acceptance_criteria: Record<string, SuperpowersAcceptanceCriterion>;
|
|
85
|
+
proof_layers: Record<string, SuperpowersProofLayer>;
|
|
86
|
+
edges: SuperpowersGraphEdge[];
|
|
87
|
+
};
|
|
88
|
+
slices: SuperpowersSliceRecord[];
|
|
89
|
+
evidence: SuperpowersEvidenceRecord[];
|
|
90
|
+
gates: Record<string, unknown>;
|
|
91
|
+
progress: SuperpowersProgressState;
|
|
92
|
+
blockers: unknown[];
|
|
93
|
+
final: {
|
|
94
|
+
product_goal_complete: boolean;
|
|
95
|
+
acceptance_target_status: string;
|
|
96
|
+
audit_task_complete: boolean;
|
|
97
|
+
completion_basis: string[];
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export interface SuperpowersSourceRecord {
|
|
101
|
+
path: string;
|
|
102
|
+
sha256: string;
|
|
103
|
+
authority: string;
|
|
104
|
+
}
|
|
105
|
+
export interface SuperpowersDeliveryState {
|
|
106
|
+
product_architecture_scope: SuperpowersProductArchitectureScope;
|
|
107
|
+
scope_conflicts: string[];
|
|
108
|
+
}
|
|
109
|
+
export interface SuperpowersProductArchitectureScope {
|
|
110
|
+
delivery_scope: SuperpowersProductDeliveryScope | "";
|
|
111
|
+
full_population_required: boolean | null;
|
|
112
|
+
representative_samples_validate: string[];
|
|
113
|
+
representative_samples_do_not_validate: string[];
|
|
114
|
+
out_of_scope_backlog: string[];
|
|
115
|
+
}
|
|
116
|
+
export interface SuperpowersProgressState {
|
|
117
|
+
system_capability_progress: Record<string, unknown>;
|
|
118
|
+
representative_sample_progress: Record<string, unknown>;
|
|
119
|
+
real_object_coverage: Record<string, unknown>;
|
|
120
|
+
full_population_operation_progress: Record<string, unknown>;
|
|
121
|
+
[key: string]: unknown;
|
|
122
|
+
}
|
|
123
|
+
export interface SuperpowersPlanItem {
|
|
124
|
+
requirement: string;
|
|
125
|
+
delivery_scope: SuperpowersPlanDeliveryScope | "";
|
|
126
|
+
capability_target: string;
|
|
127
|
+
representative_samples: string[];
|
|
128
|
+
full_population_boundary: string;
|
|
129
|
+
non_required_population: string[];
|
|
130
|
+
owner_surfaces: string[];
|
|
131
|
+
forbidden_surfaces: string[];
|
|
132
|
+
implementation_paths: string[];
|
|
133
|
+
required_tests: string[];
|
|
134
|
+
status: SuperpowersPlanItemStatus;
|
|
135
|
+
related_acs: string[];
|
|
136
|
+
required_proof_layers: string[];
|
|
137
|
+
}
|
|
138
|
+
export interface SuperpowersAcceptanceCriterion {
|
|
139
|
+
scope: string;
|
|
140
|
+
acceptance_scope: SuperpowersAcceptanceScope | "";
|
|
141
|
+
ac_validates: string[];
|
|
142
|
+
ac_does_not_validate: string[];
|
|
143
|
+
sample_boundary: string;
|
|
144
|
+
full_population_required: boolean | null;
|
|
145
|
+
related_plan_items: string[];
|
|
146
|
+
required_proof_layers: string[];
|
|
147
|
+
status: SuperpowersAcceptanceStatus;
|
|
148
|
+
}
|
|
149
|
+
export interface SuperpowersProofLayer {
|
|
150
|
+
required: boolean;
|
|
151
|
+
status: SuperpowersProofLayerStatus;
|
|
152
|
+
evidence_ids: string[];
|
|
153
|
+
}
|
|
154
|
+
export interface SuperpowersGraphEdge {
|
|
155
|
+
from: string;
|
|
156
|
+
to: string;
|
|
157
|
+
type: "supports" | string;
|
|
158
|
+
}
|
|
159
|
+
export interface SuperpowersSliceRecord {
|
|
160
|
+
slice_id: string;
|
|
161
|
+
slice_goal: string;
|
|
162
|
+
touched_plan_items: string[];
|
|
163
|
+
touched_acs: string[];
|
|
164
|
+
missing_layer_classes?: string[];
|
|
165
|
+
code_changes: string[];
|
|
166
|
+
evidence_records: string[];
|
|
167
|
+
closed_layers: string[];
|
|
168
|
+
remaining_layers: string[];
|
|
169
|
+
blockers: unknown[];
|
|
170
|
+
cleanup_assertions: string[];
|
|
171
|
+
progress_value: {
|
|
172
|
+
type: string;
|
|
173
|
+
closed_items: string[];
|
|
174
|
+
why_it_reduces_rework: string;
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
export interface SuperpowersEvidenceRecord {
|
|
178
|
+
evidence_id: string;
|
|
179
|
+
slice_id: string;
|
|
180
|
+
type: string;
|
|
181
|
+
freshness: {
|
|
182
|
+
created_at: string;
|
|
183
|
+
valid_for: string;
|
|
184
|
+
stale_after: string | null;
|
|
185
|
+
};
|
|
186
|
+
command?: string;
|
|
187
|
+
artifact_paths: string[];
|
|
188
|
+
proves: string[];
|
|
189
|
+
does_not_prove: string[];
|
|
190
|
+
redaction: {
|
|
191
|
+
checked: boolean;
|
|
192
|
+
contains_secret: boolean;
|
|
193
|
+
};
|
|
194
|
+
reviewability: {
|
|
195
|
+
external_reviewer_can_reproduce: boolean;
|
|
196
|
+
reproduction_steps: string;
|
|
197
|
+
};
|
|
198
|
+
sibling_substitution_used?: boolean;
|
|
199
|
+
sibling_substitution_approval_source?: string;
|
|
200
|
+
}
|
|
201
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
202
|
+
export declare function asStringArray(value: unknown): string[];
|
|
@@ -0,0 +1,44 @@
|
|
|
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", "delivery", "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
|
+
delivery: { type: "object" },
|
|
21
|
+
graph: { type: "object" },
|
|
22
|
+
slices: { type: "array" },
|
|
23
|
+
evidence: { type: "array" },
|
|
24
|
+
gates: { type: "object" },
|
|
25
|
+
progress: { type: "object" },
|
|
26
|
+
blockers: { type: "array" },
|
|
27
|
+
final: { type: "object" }
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
export function isRecord(value) {
|
|
31
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
32
|
+
}
|
|
33
|
+
export function asStringArray(value) {
|
|
34
|
+
if (Array.isArray(value)) {
|
|
35
|
+
return value.flatMap(asStringArray);
|
|
36
|
+
}
|
|
37
|
+
if (value === undefined || value === null) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
return String(value)
|
|
41
|
+
.split(/[,;\n]/)
|
|
42
|
+
.map((item) => item.trim())
|
|
43
|
+
.filter(Boolean);
|
|
44
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
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 emptyProgressState(): SuperpowersTaskState["progress"];
|
|
14
|
+
export declare function sourceRecords(workdir: string): Promise<SuperpowersTaskState["sources"]>;
|
|
15
|
+
export declare function sha256(value: string): string;
|
|
16
|
+
export declare function stableJson(value: unknown): string;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ensureDir, pathExists, readText, writeTextIfChanged } from "./fs.js";
|
|
4
|
+
import { appendSuperpowersEvent } from "./superpowers-task-events.js";
|
|
5
|
+
import { SUPERPOWERS_TASK_STATE_JSON_SCHEMA, SUPERPOWERS_TASK_STATE_SCHEMA_VERSION, asStringArray, isRecord } from "./superpowers-task-state-schema.js";
|
|
6
|
+
const SOURCE_FILES = {
|
|
7
|
+
product_architecture_source: {
|
|
8
|
+
path: "product-architecture-source.md",
|
|
9
|
+
authority: "intent_scope_boundaries"
|
|
10
|
+
},
|
|
11
|
+
technical_realization_plan: {
|
|
12
|
+
path: "technical-realization-plan.md",
|
|
13
|
+
authority: "plan_items_execution_blueprint_conformance"
|
|
14
|
+
},
|
|
15
|
+
acceptance_checklist: {
|
|
16
|
+
path: "acceptance-checklist.md",
|
|
17
|
+
authority: "acs_completion_semantics_proof_layers"
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
export async function initializeSuperpowersTask(workdir, options = {}) {
|
|
21
|
+
await ensureDir(path.join(workdir, "derived"));
|
|
22
|
+
await writeTextIfChanged(path.join(workdir, "task-state.schema.json"), `${stableJson(SUPERPOWERS_TASK_STATE_JSON_SCHEMA)}\n`);
|
|
23
|
+
for (const source of Object.values(SOURCE_FILES)) {
|
|
24
|
+
const target = path.join(workdir, source.path);
|
|
25
|
+
if (!(await pathExists(target))) {
|
|
26
|
+
await writeTextIfChanged(target, `# ${source.path.replace(/\.md$/, "").replace(/-/g, " ")}\n`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const now = new Date().toISOString();
|
|
30
|
+
const state = {
|
|
31
|
+
meta: {
|
|
32
|
+
task_id: options.taskId ?? `SP-${compactDate(now)}-001`,
|
|
33
|
+
plan_slug: options.planSlug ?? path.basename(workdir),
|
|
34
|
+
created_at: now,
|
|
35
|
+
updated_at: now,
|
|
36
|
+
schema_version: SUPERPOWERS_TASK_STATE_SCHEMA_VERSION,
|
|
37
|
+
goal_type: options.goalType ?? "implementation",
|
|
38
|
+
product_goal_complete: false,
|
|
39
|
+
acceptance_target_status: "not_run",
|
|
40
|
+
audit_task_complete: false
|
|
41
|
+
},
|
|
42
|
+
sources: await sourceRecords(workdir),
|
|
43
|
+
context: {
|
|
44
|
+
product_context_delta: "none",
|
|
45
|
+
technical_context_delta: "none",
|
|
46
|
+
source_to_context_coverage: [],
|
|
47
|
+
context_to_implementation_binding: []
|
|
48
|
+
},
|
|
49
|
+
delivery: {
|
|
50
|
+
product_architecture_scope: {
|
|
51
|
+
delivery_scope: "",
|
|
52
|
+
full_population_required: null,
|
|
53
|
+
representative_samples_validate: [],
|
|
54
|
+
representative_samples_do_not_validate: [],
|
|
55
|
+
out_of_scope_backlog: []
|
|
56
|
+
},
|
|
57
|
+
scope_conflicts: []
|
|
58
|
+
},
|
|
59
|
+
graph: {
|
|
60
|
+
plan_items: {},
|
|
61
|
+
acceptance_criteria: {},
|
|
62
|
+
proof_layers: {},
|
|
63
|
+
edges: []
|
|
64
|
+
},
|
|
65
|
+
slices: [],
|
|
66
|
+
evidence: [],
|
|
67
|
+
gates: {},
|
|
68
|
+
progress: emptyProgressState(),
|
|
69
|
+
blockers: [],
|
|
70
|
+
final: {
|
|
71
|
+
product_goal_complete: false,
|
|
72
|
+
acceptance_target_status: "not_run",
|
|
73
|
+
audit_task_complete: false,
|
|
74
|
+
completion_basis: []
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
await saveSuperpowersState(workdir, state);
|
|
78
|
+
await appendSuperpowersEvent(workdir, "task_initialized", { task_id: state.meta.task_id });
|
|
79
|
+
return state;
|
|
80
|
+
}
|
|
81
|
+
export async function loadSuperpowersState(workdir) {
|
|
82
|
+
return JSON.parse(await readText(path.join(workdir, "task-state.json")));
|
|
83
|
+
}
|
|
84
|
+
export async function saveSuperpowersState(workdir, state) {
|
|
85
|
+
state.meta.updated_at = new Date().toISOString();
|
|
86
|
+
await writeTextIfChanged(path.join(workdir, "task-state.json"), `${stableJson(state)}\n`);
|
|
87
|
+
}
|
|
88
|
+
export async function applySliceDelta(workdir, deltaFile) {
|
|
89
|
+
const state = await loadSuperpowersState(workdir);
|
|
90
|
+
const delta = JSON.parse(await readText(deltaFile));
|
|
91
|
+
const sliceId = String(delta.slice_id ?? "");
|
|
92
|
+
const progressValue = isRecord(delta.progress_value) ? delta.progress_value : undefined;
|
|
93
|
+
if (!sliceId) {
|
|
94
|
+
throw new Error("slice_delta must include slice_id");
|
|
95
|
+
}
|
|
96
|
+
if (!progressValue || !String(progressValue.type ?? "").trim() || asStringArray(progressValue.closed_items).length === 0) {
|
|
97
|
+
throw new Error("slice_delta must include progress_value with type and closed_items");
|
|
98
|
+
}
|
|
99
|
+
const evidenceRecords = readEvidenceRecords(delta.evidence_records);
|
|
100
|
+
for (const evidence of evidenceRecords) {
|
|
101
|
+
const existingIndex = state.evidence.findIndex((item) => item.evidence_id === evidence.evidence_id);
|
|
102
|
+
if (existingIndex >= 0) {
|
|
103
|
+
state.evidence[existingIndex] = evidence;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
state.evidence.push(evidence);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const slice = {
|
|
110
|
+
slice_id: sliceId,
|
|
111
|
+
slice_goal: String(delta.slice_goal ?? ""),
|
|
112
|
+
touched_plan_items: asStringArray(delta.touched_plan_items),
|
|
113
|
+
touched_acs: asStringArray(delta.touched_acs),
|
|
114
|
+
missing_layer_classes: asStringArray(delta.missing_layer_classes),
|
|
115
|
+
code_changes: asStringArray(delta.code_changes),
|
|
116
|
+
evidence_records: evidenceRecords.map((item) => item.evidence_id),
|
|
117
|
+
closed_layers: asStringArray(delta.closed_layers),
|
|
118
|
+
remaining_layers: asStringArray(delta.remaining_layers),
|
|
119
|
+
blockers: Array.isArray(delta.blockers) ? delta.blockers : [],
|
|
120
|
+
cleanup_assertions: asStringArray(delta.cleanup_assertions),
|
|
121
|
+
progress_value: {
|
|
122
|
+
type: String(progressValue.type),
|
|
123
|
+
closed_items: asStringArray(progressValue.closed_items),
|
|
124
|
+
why_it_reduces_rework: String(progressValue.why_it_reduces_rework ?? "")
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
state.slices.push(slice);
|
|
128
|
+
for (const layerId of slice.closed_layers) {
|
|
129
|
+
const layer = state.graph.proof_layers[layerId];
|
|
130
|
+
if (!layer) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
layer.status = "satisfied";
|
|
134
|
+
layer.evidence_ids = unique([...layer.evidence_ids, ...evidenceRecords.filter((item) => item.proves.includes(layerId)).map((item) => item.evidence_id)]);
|
|
135
|
+
}
|
|
136
|
+
recomputeStatuses(state);
|
|
137
|
+
await saveSuperpowersState(workdir, state);
|
|
138
|
+
await appendSuperpowersEvent(workdir, "slice_delta_applied", { slice_id: sliceId });
|
|
139
|
+
return state;
|
|
140
|
+
}
|
|
141
|
+
export async function refreshSourceHashes(workdir, state) {
|
|
142
|
+
state.sources = await sourceRecords(workdir);
|
|
143
|
+
}
|
|
144
|
+
export function recomputeStatuses(state) {
|
|
145
|
+
for (const [acId, ac] of Object.entries(state.graph.acceptance_criteria)) {
|
|
146
|
+
const layerIds = ac.required_proof_layers.map((layer) => `${acId}.${layer}`);
|
|
147
|
+
if (layerIds.length === 0) {
|
|
148
|
+
ac.status = "not_run";
|
|
149
|
+
}
|
|
150
|
+
else if (layerIds.every((layerId) => state.graph.proof_layers[layerId]?.status === "satisfied")) {
|
|
151
|
+
ac.status = "complete";
|
|
152
|
+
}
|
|
153
|
+
else if (layerIds.some((layerId) => state.graph.proof_layers[layerId]?.status === "satisfied")) {
|
|
154
|
+
ac.status = "partial";
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
ac.status = "not_run";
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
for (const item of Object.values(state.graph.plan_items)) {
|
|
161
|
+
if (item.related_acs.length > 0 && item.related_acs.every((acId) => state.graph.acceptance_criteria[acId]?.status === "complete")) {
|
|
162
|
+
item.status = "complete";
|
|
163
|
+
}
|
|
164
|
+
else if (item.related_acs.some((acId) => state.graph.acceptance_criteria[acId]?.status === "partial")) {
|
|
165
|
+
item.status = "partial";
|
|
166
|
+
}
|
|
167
|
+
else if (item.status === "complete") {
|
|
168
|
+
item.status = "not_started";
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
export function emptyProgressState() {
|
|
173
|
+
return {
|
|
174
|
+
system_capability_progress: { status: "not_started" },
|
|
175
|
+
representative_sample_progress: { status: "not_started" },
|
|
176
|
+
real_object_coverage: { status: "unknown" },
|
|
177
|
+
full_population_operation_progress: { status: "not_in_scope" }
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
export async function sourceRecords(workdir) {
|
|
181
|
+
const sources = {};
|
|
182
|
+
for (const [key, source] of Object.entries(SOURCE_FILES)) {
|
|
183
|
+
const file = path.join(workdir, source.path);
|
|
184
|
+
const content = (await pathExists(file)) ? await readText(file) : "";
|
|
185
|
+
sources[key] = { path: source.path, sha256: sha256(content), authority: source.authority };
|
|
186
|
+
}
|
|
187
|
+
return sources;
|
|
188
|
+
}
|
|
189
|
+
export function sha256(value) {
|
|
190
|
+
return createHash("sha256").update(value).digest("hex");
|
|
191
|
+
}
|
|
192
|
+
export function stableJson(value) {
|
|
193
|
+
return JSON.stringify(sortJson(value), null, 2);
|
|
194
|
+
}
|
|
195
|
+
function readEvidenceRecords(value) {
|
|
196
|
+
if (!Array.isArray(value)) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
return value.filter(isRecord).map((item) => ({
|
|
200
|
+
evidence_id: String(item.evidence_id ?? item.evidenceId ?? ""),
|
|
201
|
+
slice_id: String(item.slice_id ?? item.sliceId ?? ""),
|
|
202
|
+
type: String(item.type ?? ""),
|
|
203
|
+
freshness: isRecord(item.freshness)
|
|
204
|
+
? {
|
|
205
|
+
created_at: String(item.freshness.created_at ?? ""),
|
|
206
|
+
valid_for: String(item.freshness.valid_for ?? ""),
|
|
207
|
+
stale_after: item.freshness.stale_after === null ? null : item.freshness.stale_after === undefined ? null : String(item.freshness.stale_after)
|
|
208
|
+
}
|
|
209
|
+
: { created_at: "", valid_for: "", stale_after: null },
|
|
210
|
+
command: item.command === undefined ? undefined : String(item.command),
|
|
211
|
+
artifact_paths: asStringArray(item.artifact_paths),
|
|
212
|
+
proves: asStringArray(item.proves),
|
|
213
|
+
does_not_prove: asStringArray(item.does_not_prove),
|
|
214
|
+
redaction: isRecord(item.redaction)
|
|
215
|
+
? { checked: item.redaction.checked === true, contains_secret: item.redaction.contains_secret === true }
|
|
216
|
+
: { checked: false, contains_secret: false },
|
|
217
|
+
reviewability: isRecord(item.reviewability)
|
|
218
|
+
? {
|
|
219
|
+
external_reviewer_can_reproduce: item.reviewability.external_reviewer_can_reproduce === true,
|
|
220
|
+
reproduction_steps: String(item.reviewability.reproduction_steps ?? "")
|
|
221
|
+
}
|
|
222
|
+
: { external_reviewer_can_reproduce: false, reproduction_steps: "" },
|
|
223
|
+
sibling_substitution_used: item.sibling_substitution_used === true,
|
|
224
|
+
sibling_substitution_approval_source: item.sibling_substitution_approval_source === undefined ? undefined : String(item.sibling_substitution_approval_source)
|
|
225
|
+
}));
|
|
226
|
+
}
|
|
227
|
+
function sortJson(value) {
|
|
228
|
+
if (Array.isArray(value)) {
|
|
229
|
+
return value.map(sortJson);
|
|
230
|
+
}
|
|
231
|
+
if (!isRecord(value)) {
|
|
232
|
+
return value;
|
|
233
|
+
}
|
|
234
|
+
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, sortJson(value[key])]));
|
|
235
|
+
}
|
|
236
|
+
function unique(values) {
|
|
237
|
+
return [...new Set(values.filter(Boolean))];
|
|
238
|
+
}
|
|
239
|
+
function compactDate(value) {
|
|
240
|
+
return value.slice(0, 10).replace(/-/g, "");
|
|
241
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type SuperpowersTaskState } from "./superpowers-task-state-schema.js";
|
|
2
|
+
import type { ValidatorReport } from "./validators.js";
|
|
3
|
+
export declare function validateSuperpowersState(projectRoot: string, args?: string[]): Promise<ValidatorReport>;
|
|
4
|
+
export declare function allCompletionConditionsSatisfied(state: SuperpowersTaskState): boolean;
|