project-tiny-context-harness 0.2.80 → 0.2.82
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 +10 -8
- package/assets/README.md +9 -7
- package/assets/README.zh-CN.md +10 -0
- package/assets/github/harness.yml +1 -1
- package/assets/skills/composite-long-task-workflow/SKILL.md +10 -2
- package/assets/skills/composite-long-task-workflow/assets/execution-binding.template.md +2 -0
- package/assets/skills/composite-long-task-workflow/assets/goal-objective.template.md +7 -3
- package/assets/skills/composite-long-task-workflow/references/composite-long-task-workflow-protocol.md +27 -11
- package/dist/lib/composite-long-task-renderer.js +18 -62
- package/dist/lib/superpowers-task-assertion-normalizers.d.ts +3 -0
- package/dist/lib/superpowers-task-assertion-normalizers.js +70 -0
- package/dist/lib/superpowers-task-assertions.d.ts +20 -0
- package/dist/lib/superpowers-task-assertions.js +243 -0
- package/dist/lib/superpowers-task-compile-diagnostics.d.ts +5 -0
- package/dist/lib/superpowers-task-compile-diagnostics.js +20 -0
- package/dist/lib/superpowers-task-compile-guards.d.ts +2 -0
- package/dist/lib/superpowers-task-compile-guards.js +66 -0
- package/dist/lib/superpowers-task-compile.js +25 -5
- package/dist/lib/superpowers-task-conformance.d.ts +2 -0
- package/dist/lib/superpowers-task-conformance.js +24 -0
- package/dist/lib/superpowers-task-delivery.js +30 -18
- package/dist/lib/superpowers-task-derive.d.ts +1 -0
- package/dist/lib/superpowers-task-derive.js +121 -4
- package/dist/lib/superpowers-task-fields.d.ts +22 -0
- package/dist/lib/superpowers-task-fields.js +276 -0
- package/dist/lib/superpowers-task-gates.js +57 -3
- package/dist/lib/superpowers-task-source-compile.js +128 -92
- package/dist/lib/superpowers-task-source-parser.js +12 -18
- package/dist/lib/superpowers-task-state-schema.d.ts +120 -1
- package/dist/lib/superpowers-task-state.js +33 -8
- package/dist/lib/superpowers-task-status.d.ts +2 -0
- package/dist/lib/superpowers-task-status.js +14 -0
- package/dist/lib/superpowers-task-validator.js +18 -9
- package/package.json +69 -69
|
@@ -1,87 +1,38 @@
|
|
|
1
|
+
import { MACHINE_VERIFIABLE_PROOF_LAYERS, proofLayerName } from "./superpowers-task-assertions.js";
|
|
2
|
+
import { compileReportSuffix, missingCategory, throwCompileErrors } from "./superpowers-task-compile-diagnostics.js";
|
|
3
|
+
import { ACCEPTANCE_FIELDS, ACCEPTANCE_SCOPES, fieldSet, isSelectedScopeFitSlice, normalizeProofLayerName, PLAN_DELIVERY_SCOPES, PLAN_FIELDS, PRODUCT_DELIVERY_SCOPES, PRODUCT_FIELDS, SCOPE_FIT_DECISIONS } from "./superpowers-task-fields.js";
|
|
1
4
|
import { fieldArray, fieldBoolean, fieldLine, fieldText, parseDocumentFields, parseHeadingDefinitions } from "./superpowers-task-source-parser.js";
|
|
2
5
|
export const DEFAULT_LAYERS = ["code", "test"];
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const PRODUCT_FIELDS = new Set([
|
|
7
|
-
"delivery_scope",
|
|
8
|
-
"full_population_required",
|
|
9
|
-
"representative_samples_validate",
|
|
10
|
-
"representative_samples_do_not_validate",
|
|
11
|
-
"out_of_scope_backlog",
|
|
12
|
-
"source_authority",
|
|
13
|
-
"product_goal",
|
|
14
|
-
"surface_ia_lock",
|
|
15
|
-
"decision_lock",
|
|
16
|
-
"context_delta",
|
|
17
|
-
"source_to_context_coverage",
|
|
18
|
-
"acceptance_semantics",
|
|
19
|
-
"impact"
|
|
20
|
-
]);
|
|
21
|
-
const PLAN_FIELDS = new Set([
|
|
22
|
-
"delivery_scope",
|
|
23
|
-
"capability_target",
|
|
24
|
-
"representative_samples",
|
|
25
|
-
"full_population_boundary",
|
|
26
|
-
"non_required_population",
|
|
27
|
-
"owner_surfaces",
|
|
28
|
-
"forbidden_surfaces",
|
|
29
|
-
"implementation_paths",
|
|
30
|
-
"required_tests",
|
|
31
|
-
"related_acs",
|
|
32
|
-
"requirement_ref",
|
|
33
|
-
"decision_id",
|
|
34
|
-
"proof_layer_ids",
|
|
35
|
-
"api_schema_changes",
|
|
36
|
-
"state_machine",
|
|
37
|
-
"data_flow",
|
|
38
|
-
"worker_runtime_behavior",
|
|
39
|
-
"ui_ia_changes",
|
|
40
|
-
"migration_plan",
|
|
41
|
-
"evidence_artifacts",
|
|
42
|
-
"explicit_no_test_scope",
|
|
43
|
-
"non_completing_shortcuts",
|
|
44
|
-
"substitution_policy",
|
|
45
|
-
"drift_severity",
|
|
46
|
-
"partial_conditions",
|
|
47
|
-
"blockers",
|
|
48
|
-
"context_fact_refs"
|
|
49
|
-
]);
|
|
50
|
-
const ACCEPTANCE_FIELDS = new Set([
|
|
51
|
-
"checklist_source",
|
|
52
|
-
"acceptance_scope",
|
|
53
|
-
"ac_validates",
|
|
54
|
-
"ac_does_not_validate",
|
|
55
|
-
"sample_boundary",
|
|
56
|
-
"full_population_required",
|
|
57
|
-
"related_plan_items",
|
|
58
|
-
"required_proof_layers",
|
|
59
|
-
"ac_type",
|
|
60
|
-
"proof_chain",
|
|
61
|
-
"verification_method",
|
|
62
|
-
"fail_conditions",
|
|
63
|
-
"invalid_evidence",
|
|
64
|
-
"substitution_policy",
|
|
65
|
-
"missing_layer_downgrade",
|
|
66
|
-
"auditor_expectation",
|
|
67
|
-
"out_of_scope_na_approval_source",
|
|
68
|
-
"required_test_ids",
|
|
69
|
-
"explicit_no_test_scope",
|
|
70
|
-
"hard_blockers",
|
|
71
|
-
"validates_explanation",
|
|
72
|
-
"does_not_validate_explanation",
|
|
73
|
-
"final_evidence_expected",
|
|
74
|
-
"test_cases"
|
|
75
|
-
]);
|
|
6
|
+
const PRODUCT_FIELD_SET = fieldSet(PRODUCT_FIELDS);
|
|
7
|
+
const PLAN_FIELD_SET = fieldSet(PLAN_FIELDS);
|
|
8
|
+
const ACCEPTANCE_FIELD_SET = fieldSet(ACCEPTANCE_FIELDS);
|
|
76
9
|
export function parseProductArchitectureScope(content, sourceFile) {
|
|
77
|
-
const fields = parseDocumentFields(content, sourceFile,
|
|
10
|
+
const fields = parseDocumentFields(content, sourceFile, PRODUCT_FIELD_SET);
|
|
78
11
|
const errors = [];
|
|
12
|
+
const selectedScopeFitSlice = requireText(errors, "Product / Architecture Source", "selected_scope_fit_slice", fields, sourceFile, 1);
|
|
13
|
+
if (selectedScopeFitSlice && !isSelectedScopeFitSlice(selectedScopeFitSlice)) {
|
|
14
|
+
errors.push(`Product / Architecture Source invalid selected_scope_fit_slice: ${selectedScopeFitSlice} at ${sourceFile}:${fieldLine(fields, "selected_scope_fit_slice") ?? 1}; allowed: none or SFC-###${compileReportSuffix("blocking_unparseable_object", sourceFile, fieldLine(fields, "selected_scope_fit_slice") ?? 1, "selected_scope_fit_slice", "value must be none or an SFC-### id", "fix selected_scope_fit_slice and rerun compile")}`);
|
|
15
|
+
}
|
|
79
16
|
const scope = {
|
|
80
17
|
delivery_scope: requireEnum(errors, "Product / Architecture Source", "delivery_scope", fields, PRODUCT_DELIVERY_SCOPES, sourceFile, 1),
|
|
81
18
|
full_population_required: requireBoolean(errors, "Product / Architecture Source", "full_population_required", fields, sourceFile, 1),
|
|
82
19
|
representative_samples_validate: requireArray(errors, "Product / Architecture Source", "representative_samples_validate", fields, sourceFile, 1),
|
|
83
20
|
representative_samples_do_not_validate: requireArray(errors, "Product / Architecture Source", "representative_samples_do_not_validate", fields, sourceFile, 1),
|
|
84
|
-
out_of_scope_backlog: requireArray(errors, "Product / Architecture Source", "out_of_scope_backlog", fields, sourceFile, 1)
|
|
21
|
+
out_of_scope_backlog: requireArray(errors, "Product / Architecture Source", "out_of_scope_backlog", fields, sourceFile, 1),
|
|
22
|
+
scope_fit_decision: requireEnum(errors, "Product / Architecture Source", "scope_fit_decision", fields, SCOPE_FIT_DECISIONS, sourceFile, 1),
|
|
23
|
+
selected_scope_fit_slice: selectedScopeFitSlice,
|
|
24
|
+
owner_boundary: requireText(errors, "Product / Architecture Source", "owner_boundary", fields, sourceFile, 1),
|
|
25
|
+
primary_capability_path: requireText(errors, "Product / Architecture Source", "primary_capability_path", fields, sourceFile, 1),
|
|
26
|
+
non_completing_outcomes: requireArray(errors, "Product / Architecture Source", "non_completing_outcomes", fields, sourceFile, 1),
|
|
27
|
+
assertion_policy: requireText(errors, "Product / Architecture Source", "assertion_policy", fields, sourceFile, 1),
|
|
28
|
+
source_authority: requireText(errors, "Product / Architecture Source", "source_authority", fields, sourceFile, 1),
|
|
29
|
+
product_goal: requireText(errors, "Product / Architecture Source", "product_goal", fields, sourceFile, 1),
|
|
30
|
+
surface_ia_lock: optionalText(fields, "surface_ia_lock"),
|
|
31
|
+
decision_lock: optionalText(fields, "decision_lock"),
|
|
32
|
+
context_delta: optionalText(fields, "context_delta"),
|
|
33
|
+
source_to_context_coverage: optionalText(fields, "source_to_context_coverage"),
|
|
34
|
+
acceptance_semantics: optionalText(fields, "acceptance_semantics"),
|
|
35
|
+
impact: optionalText(fields, "impact")
|
|
85
36
|
};
|
|
86
37
|
throwCompileErrors(errors);
|
|
87
38
|
return scope;
|
|
@@ -89,7 +40,7 @@ export function parseProductArchitectureScope(content, sourceFile) {
|
|
|
89
40
|
export function parsePlanItems(content, sourceFile) {
|
|
90
41
|
const items = {};
|
|
91
42
|
const errors = [];
|
|
92
|
-
for (const definition of parseHeadingDefinitions(content, { kind: "PI", sourceFile, allowedFields:
|
|
43
|
+
for (const definition of parseHeadingDefinitions(content, { kind: "PI", sourceFile, allowedFields: PLAN_FIELD_SET })) {
|
|
93
44
|
const fields = definition.fields;
|
|
94
45
|
items[definition.id] = {
|
|
95
46
|
requirement: definition.title,
|
|
@@ -101,10 +52,35 @@ export function parsePlanItems(content, sourceFile) {
|
|
|
101
52
|
representative_samples: requireArray(errors, definition.id, "representative_samples", fields, sourceFile, definition.source_start_line),
|
|
102
53
|
full_population_boundary: requireText(errors, definition.id, "full_population_boundary", fields, sourceFile, definition.source_start_line),
|
|
103
54
|
non_required_population: requireArray(errors, definition.id, "non_required_population", fields, sourceFile, definition.source_start_line),
|
|
55
|
+
owner_boundary: requireText(errors, definition.id, "owner_boundary", fields, sourceFile, definition.source_start_line),
|
|
56
|
+
primary_capability_path: requireText(errors, definition.id, "primary_capability_path", fields, sourceFile, definition.source_start_line),
|
|
57
|
+
trigger_contract: requireText(errors, definition.id, "trigger_contract", fields, sourceFile, definition.source_start_line),
|
|
58
|
+
state_transition_contract: requireText(errors, definition.id, "state_transition_contract", fields, sourceFile, definition.source_start_line),
|
|
59
|
+
observable_result_contract: requireText(errors, definition.id, "observable_result_contract", fields, sourceFile, definition.source_start_line),
|
|
60
|
+
assertion_support: requireText(errors, definition.id, "assertion_support", fields, sourceFile, definition.source_start_line),
|
|
61
|
+
required_assertion_commands: requireArray(errors, definition.id, "required_assertion_commands", fields, sourceFile, definition.source_start_line),
|
|
62
|
+
invalid_implementation_shortcuts: requireArray(errors, definition.id, "invalid_implementation_shortcuts", fields, sourceFile, definition.source_start_line),
|
|
104
63
|
owner_surfaces: optionalArray(fields, "owner_surfaces"),
|
|
105
64
|
forbidden_surfaces: optionalArray(fields, "forbidden_surfaces"),
|
|
106
|
-
implementation_paths:
|
|
65
|
+
implementation_paths: requireArray(errors, definition.id, "implementation_paths", fields, sourceFile, definition.source_start_line),
|
|
107
66
|
required_tests: optionalArray(fields, "required_tests"),
|
|
67
|
+
proof_layer_ids: optionalArray(fields, "proof_layer_ids").map(normalizeLayerId),
|
|
68
|
+
requirement_ref: optionalText(fields, "requirement_ref"),
|
|
69
|
+
decision_id: optionalText(fields, "decision_id"),
|
|
70
|
+
api_schema_changes: optionalText(fields, "api_schema_changes"),
|
|
71
|
+
state_machine: optionalText(fields, "state_machine"),
|
|
72
|
+
data_flow: optionalText(fields, "data_flow"),
|
|
73
|
+
worker_runtime_behavior: optionalText(fields, "worker_runtime_behavior"),
|
|
74
|
+
ui_ia_changes: optionalText(fields, "ui_ia_changes"),
|
|
75
|
+
migration_plan: optionalText(fields, "migration_plan"),
|
|
76
|
+
evidence_artifacts: optionalArray(fields, "evidence_artifacts"),
|
|
77
|
+
non_completing_shortcuts: optionalArray(fields, "non_completing_shortcuts"),
|
|
78
|
+
substitution_policy: optionalArray(fields, "substitution_policy"),
|
|
79
|
+
drift_severity: optionalText(fields, "drift_severity"),
|
|
80
|
+
partial_conditions: optionalArray(fields, "partial_conditions"),
|
|
81
|
+
blockers: optionalArray(fields, "blockers"),
|
|
82
|
+
explicit_no_test_scope: fieldBoolean(fields, "explicit_no_test_scope") === true,
|
|
83
|
+
context_fact_refs: optionalArray(fields, "context_fact_refs"),
|
|
108
84
|
status: "not_started",
|
|
109
85
|
related_acs: optionalArray(fields, "related_acs").map((item) => item.toUpperCase()),
|
|
110
86
|
required_proof_layers: []
|
|
@@ -116,9 +92,10 @@ export function parsePlanItems(content, sourceFile) {
|
|
|
116
92
|
export function parseAcceptanceCriteria(content, sourceFile) {
|
|
117
93
|
const items = {};
|
|
118
94
|
const errors = [];
|
|
119
|
-
for (const definition of parseHeadingDefinitions(content, { kind: "AC", sourceFile, allowedFields:
|
|
95
|
+
for (const definition of parseHeadingDefinitions(content, { kind: "AC", sourceFile, allowedFields: ACCEPTANCE_FIELD_SET })) {
|
|
120
96
|
const fields = definition.fields;
|
|
121
|
-
const layers =
|
|
97
|
+
const layers = requireArray(errors, definition.id, "required_proof_layers", fields, sourceFile, definition.source_start_line).map(normalizeLayer).filter(Boolean);
|
|
98
|
+
const requiredProofLayers = layers.length > 0 ? layers : DEFAULT_LAYERS;
|
|
122
99
|
items[definition.id] = {
|
|
123
100
|
scope: definition.title,
|
|
124
101
|
source_file: definition.source_file,
|
|
@@ -129,8 +106,32 @@ export function parseAcceptanceCriteria(content, sourceFile) {
|
|
|
129
106
|
ac_does_not_validate: requireArray(errors, definition.id, "ac_does_not_validate", fields, sourceFile, definition.source_start_line),
|
|
130
107
|
sample_boundary: requireText(errors, definition.id, "sample_boundary", fields, sourceFile, definition.source_start_line),
|
|
131
108
|
full_population_required: requireBoolean(errors, definition.id, "full_population_required", fields, sourceFile, definition.source_start_line),
|
|
132
|
-
related_plan_items:
|
|
133
|
-
required_proof_layers:
|
|
109
|
+
related_plan_items: requireArray(errors, definition.id, "related_plan_items", fields, sourceFile, definition.source_start_line).map((item) => item.toUpperCase()),
|
|
110
|
+
required_proof_layers: requiredProofLayers,
|
|
111
|
+
assertion_requirements: assertionRequirements(fields, requiredProofLayers),
|
|
112
|
+
assertion_command: requireText(errors, definition.id, "assertion_command", fields, sourceFile, definition.source_start_line),
|
|
113
|
+
assertion_artifacts: requireArray(errors, definition.id, "assertion_artifacts", fields, sourceFile, definition.source_start_line),
|
|
114
|
+
positive_assertions: requireArray(errors, definition.id, "positive_assertions", fields, sourceFile, definition.source_start_line),
|
|
115
|
+
negative_assertions: requireArray(errors, definition.id, "negative_assertions", fields, sourceFile, definition.source_start_line),
|
|
116
|
+
machine_blocking: requireBoolean(errors, definition.id, "machine_blocking", fields, sourceFile, definition.source_start_line),
|
|
117
|
+
invalid_completion_signals: requireArray(errors, definition.id, "invalid_completion_signals", fields, sourceFile, definition.source_start_line),
|
|
118
|
+
assertion_result_required: requireBoolean(errors, definition.id, "assertion_result_required", fields, sourceFile, definition.source_start_line),
|
|
119
|
+
ac_type: optionalText(fields, "ac_type"),
|
|
120
|
+
proof_chain: optionalArray(fields, "proof_chain").map(normalizeLayerId),
|
|
121
|
+
required_test_ids: optionalArray(fields, "required_test_ids"),
|
|
122
|
+
fail_conditions: optionalArray(fields, "fail_conditions"),
|
|
123
|
+
invalid_evidence: optionalArray(fields, "invalid_evidence"),
|
|
124
|
+
substitution_policy: optionalArray(fields, "substitution_policy"),
|
|
125
|
+
missing_layer_downgrade: optionalText(fields, "missing_layer_downgrade"),
|
|
126
|
+
auditor_expectation: optionalText(fields, "auditor_expectation"),
|
|
127
|
+
out_of_scope_na_approval_source: optionalText(fields, "out_of_scope_na_approval_source"),
|
|
128
|
+
hard_blockers: optionalArray(fields, "hard_blockers"),
|
|
129
|
+
validates_explanation: optionalText(fields, "validates_explanation"),
|
|
130
|
+
does_not_validate_explanation: optionalText(fields, "does_not_validate_explanation"),
|
|
131
|
+
final_evidence_expected: optionalArray(fields, "final_evidence_expected"),
|
|
132
|
+
verification_method: optionalArray(fields, "verification_method"),
|
|
133
|
+
test_cases: optionalArray(fields, "test_cases"),
|
|
134
|
+
explicit_no_test_scope: fieldBoolean(fields, "explicit_no_test_scope") === true,
|
|
134
135
|
status: "not_run"
|
|
135
136
|
};
|
|
136
137
|
}
|
|
@@ -138,17 +139,51 @@ export function parseAcceptanceCriteria(content, sourceFile) {
|
|
|
138
139
|
return items;
|
|
139
140
|
}
|
|
140
141
|
function normalizeLayer(value) {
|
|
141
|
-
return value
|
|
142
|
+
return normalizeProofLayerName(value);
|
|
143
|
+
}
|
|
144
|
+
function normalizeLayerId(value) {
|
|
145
|
+
const trimmed = value.trim();
|
|
146
|
+
if (!trimmed.includes(".")) {
|
|
147
|
+
return normalizeLayer(trimmed);
|
|
148
|
+
}
|
|
149
|
+
const acId = trimmed.slice(0, trimmed.lastIndexOf(".")).toUpperCase();
|
|
150
|
+
const layer = trimmed.slice(trimmed.lastIndexOf(".") + 1);
|
|
151
|
+
return `${acId}.${normalizeLayer(layer)}`;
|
|
152
|
+
}
|
|
153
|
+
function assertionRequirements(fields, layers) {
|
|
154
|
+
const requiredTestIds = optionalArray(fields, "required_test_ids");
|
|
155
|
+
const positiveAssertions = unique(optionalArray(fields, "positive_assertions"));
|
|
156
|
+
const negativeAssertions = unique(optionalArray(fields, "negative_assertions"));
|
|
157
|
+
const assertionCommand = fieldText(fields, "assertion_command");
|
|
158
|
+
const assertionArtifacts = optionalArray(fields, "assertion_artifacts");
|
|
159
|
+
const machineBlocking = fieldBoolean(fields, "machine_blocking");
|
|
160
|
+
const assertionResultRequired = fieldBoolean(fields, "assertion_result_required");
|
|
161
|
+
const invalidCompletionSignals = optionalArray(fields, "invalid_completion_signals");
|
|
162
|
+
return layers.map((layer) => {
|
|
163
|
+
const normalized = proofLayerName(normalizeLayer(layer));
|
|
164
|
+
return {
|
|
165
|
+
proof_layer: normalized,
|
|
166
|
+
required: true,
|
|
167
|
+
machine_blocking: machineBlocking ?? MACHINE_VERIFIABLE_PROOF_LAYERS.has(normalized),
|
|
168
|
+
assertion_result_required: assertionResultRequired ?? MACHINE_VERIFIABLE_PROOF_LAYERS.has(normalized),
|
|
169
|
+
assertion_command: assertionCommand,
|
|
170
|
+
assertion_artifacts: assertionArtifacts,
|
|
171
|
+
invalid_completion_signals: invalidCompletionSignals,
|
|
172
|
+
required_test_ids: requiredTestIds,
|
|
173
|
+
positive_assertions: positiveAssertions,
|
|
174
|
+
negative_assertions: negativeAssertions
|
|
175
|
+
};
|
|
176
|
+
});
|
|
142
177
|
}
|
|
143
178
|
function requireEnum(errors, label, name, fields, allowed, sourceFile, fallbackLine) {
|
|
144
179
|
const value = fieldText(fields, name);
|
|
145
180
|
const line = fieldLine(fields, name) ?? fallbackLine;
|
|
146
181
|
if (!value) {
|
|
147
|
-
errors.push(`${label} missing ${name} at ${sourceFile}:${line}`);
|
|
182
|
+
errors.push(`${label} missing ${name} at ${sourceFile}:${line}${compileReportSuffix(missingCategory(label), sourceFile, line, name, "field is required", "add the required field and rerun compile")}`);
|
|
148
183
|
return "";
|
|
149
184
|
}
|
|
150
185
|
if (!allowed.has(value)) {
|
|
151
|
-
errors.push(`${label} invalid ${name}: ${value} at ${sourceFile}:${line}; allowed: ${[...allowed].join(", ")}`);
|
|
186
|
+
errors.push(`${label} invalid ${name}: ${value} at ${sourceFile}:${line}; allowed: ${[...allowed].join(", ")}${compileReportSuffix("blocking_unparseable_object", sourceFile, line, name, `value must be one of ${[...allowed].join(", ")}`, "fix the field value and rerun compile")}`);
|
|
152
187
|
}
|
|
153
188
|
return value;
|
|
154
189
|
}
|
|
@@ -156,14 +191,14 @@ function requireText(errors, label, name, fields, sourceFile, fallbackLine) {
|
|
|
156
191
|
const value = fieldText(fields, name);
|
|
157
192
|
const line = fieldLine(fields, name) ?? fallbackLine;
|
|
158
193
|
if (!value) {
|
|
159
|
-
errors.push(`${label} missing ${name} at ${sourceFile}:${line}`);
|
|
194
|
+
errors.push(`${label} missing ${name} at ${sourceFile}:${line}${compileReportSuffix(missingCategory(label), sourceFile, line, name, "field is required", "add non-empty text and rerun compile")}`);
|
|
160
195
|
}
|
|
161
196
|
return value;
|
|
162
197
|
}
|
|
163
198
|
function requireArray(errors, label, name, fields, sourceFile, fallbackLine) {
|
|
164
199
|
const line = fieldLine(fields, name) ?? fallbackLine;
|
|
165
200
|
if (!fields[name]) {
|
|
166
|
-
errors.push(`${label} missing ${name} at ${sourceFile}:${line}`);
|
|
201
|
+
errors.push(`${label} missing ${name} at ${sourceFile}:${line}${compileReportSuffix(missingCategory(label), sourceFile, line, name, "field is required", "add the list field and rerun compile")}`);
|
|
167
202
|
return [];
|
|
168
203
|
}
|
|
169
204
|
return fieldArray(fields, name);
|
|
@@ -171,20 +206,21 @@ function requireArray(errors, label, name, fields, sourceFile, fallbackLine) {
|
|
|
171
206
|
function requireBoolean(errors, label, name, fields, sourceFile, fallbackLine) {
|
|
172
207
|
const line = fieldLine(fields, name) ?? fallbackLine;
|
|
173
208
|
if (!fields[name]) {
|
|
174
|
-
errors.push(`${label} missing ${name} at ${sourceFile}:${line}`);
|
|
209
|
+
errors.push(`${label} missing ${name} at ${sourceFile}:${line}${compileReportSuffix(missingCategory(label), sourceFile, line, name, "field is required", "add true or false and rerun compile")}`);
|
|
175
210
|
return null;
|
|
176
211
|
}
|
|
177
212
|
const value = fieldBoolean(fields, name);
|
|
178
213
|
if (value === null) {
|
|
179
|
-
errors.push(`${label} invalid ${name}: ${fieldText(fields, name)} at ${sourceFile}:${line}; must be true or false`);
|
|
214
|
+
errors.push(`${label} invalid ${name}: ${fieldText(fields, name)} at ${sourceFile}:${line}; must be true or false${compileReportSuffix("blocking_unparseable_object", sourceFile, line, name, "value must be true or false", "fix the boolean and rerun compile")}`);
|
|
180
215
|
}
|
|
181
216
|
return value;
|
|
182
217
|
}
|
|
183
218
|
function optionalArray(fields, name) {
|
|
184
219
|
return fields[name] ? fieldArray(fields, name) : [];
|
|
185
220
|
}
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
221
|
+
function optionalText(fields, name) {
|
|
222
|
+
return fields[name] ? fieldText(fields, name) : "";
|
|
223
|
+
}
|
|
224
|
+
function unique(values) {
|
|
225
|
+
return [...new Set(values.filter(Boolean))];
|
|
190
226
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { compileError, throwCompileErrors } from "./superpowers-task-compile-diagnostics.js";
|
|
1
2
|
export function parseHeadingDefinitions(content, options) {
|
|
2
3
|
const lines = splitLines(content);
|
|
3
4
|
const errors = [];
|
|
@@ -8,7 +9,7 @@ export function parseHeadingDefinitions(content, options) {
|
|
|
8
9
|
for (const heading of headings) {
|
|
9
10
|
const firstLine = seen.get(heading.id);
|
|
10
11
|
if (firstLine !== undefined) {
|
|
11
|
-
errors.push(`${heading.id} duplicate definition at ${options.sourceFile}:${firstLine} and ${options.sourceFile}:${heading.line}
|
|
12
|
+
errors.push(compileError(`${heading.id} duplicate definition at ${options.sourceFile}:${firstLine} and ${options.sourceFile}:${heading.line}`, "blocking_unparseable_object", options.sourceFile, heading.line, heading.id, "duplicate object ids make PI/AC graph references ambiguous", "keep exactly one heading definition for this id"));
|
|
12
13
|
continue;
|
|
13
14
|
}
|
|
14
15
|
seen.set(heading.id, heading.line);
|
|
@@ -24,15 +25,15 @@ export function parseHeadingDefinitions(content, options) {
|
|
|
24
25
|
});
|
|
25
26
|
}
|
|
26
27
|
if (definitions.length === 0) {
|
|
27
|
-
errors.push(`${options.sourceFile} must define ${options.kind} items with Markdown headings like "## ${options.kind}-001: ..."`);
|
|
28
|
+
errors.push(compileError(`${options.sourceFile} must define ${options.kind} items with Markdown headings like "## ${options.kind}-001: ..."`, options.kind === "PI" ? "blocking_missing_plan" : "blocking_missing_checklist", options.sourceFile, 1, `${options.kind}_heading`, "the source cannot compile without at least one object heading", `add headings like ## ${options.kind}-001: ...`));
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
+
throwCompileErrors(errors);
|
|
30
31
|
return definitions;
|
|
31
32
|
}
|
|
32
33
|
export function parseDocumentFields(content, sourceFile, allowedFields) {
|
|
33
34
|
const errors = [];
|
|
34
35
|
const fields = parseFields(splitLines(content), sourceFile, 1, allowedFields, errors);
|
|
35
|
-
|
|
36
|
+
throwCompileErrors(errors);
|
|
36
37
|
return fields;
|
|
37
38
|
}
|
|
38
39
|
export function fieldText(fields, name) {
|
|
@@ -72,11 +73,11 @@ function parseFields(lines, sourceFile, startLine, allowedFields, errors) {
|
|
|
72
73
|
const lineNumber = startLine + index;
|
|
73
74
|
const heading = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
|
|
74
75
|
if (heading && allowedFields.has(heading[2].trim())) {
|
|
75
|
-
errors.push(`${sourceFile}:${lineNumber} field headings are not supported; use "${heading[2].trim()}: ..."
|
|
76
|
+
errors.push(compileError(`${sourceFile}:${lineNumber} field headings are not supported; use "${heading[2].trim()}: ..."`, "blocking_unparseable_object", sourceFile, lineNumber, heading[2].trim(), "field heading syntax is ambiguous in compiled source", "use key: value fields"));
|
|
76
77
|
continue;
|
|
77
78
|
}
|
|
78
79
|
if (/^\s*\|/.test(line) && containsKnownField(line, allowedFields)) {
|
|
79
|
-
errors.push(`${sourceFile}:${lineNumber} table fields are not supported; use key: value fields
|
|
80
|
+
errors.push(compileError(`${sourceFile}:${lineNumber} table fields are not supported; use key: value fields`, "blocking_unparseable_object", sourceFile, lineNumber, "table_field", "table fields cannot be parsed into canonical state safely", "use key: value fields"));
|
|
80
81
|
continue;
|
|
81
82
|
}
|
|
82
83
|
const match = /^([a-z][a-z0-9_]*)\s*:\s*(.*)$/.exec(line);
|
|
@@ -85,13 +86,11 @@ function parseFields(lines, sourceFile, startLine, allowedFields, errors) {
|
|
|
85
86
|
}
|
|
86
87
|
const name = match[1];
|
|
87
88
|
if (!allowedFields.has(name)) {
|
|
88
|
-
|
|
89
|
-
errors.push(`${sourceFile}:${lineNumber} unknown field ${name}`);
|
|
90
|
-
}
|
|
89
|
+
errors.push(compileError(`${sourceFile}:${lineNumber} unknown field ${name}`, "blocking_unparseable_object", sourceFile, lineNumber, name, "unknown fields may hide required source semantics", "rename the field to a supported canonical key"));
|
|
91
90
|
continue;
|
|
92
91
|
}
|
|
93
92
|
if (fields[name]) {
|
|
94
|
-
errors.push(`${sourceFile}:${lineNumber} duplicate field ${name}
|
|
93
|
+
errors.push(compileError(`${sourceFile}:${lineNumber} duplicate field ${name}`, "blocking_unparseable_object", sourceFile, lineNumber, name, "duplicate fields make canonical source values ambiguous", "keep one field value"));
|
|
95
94
|
continue;
|
|
96
95
|
}
|
|
97
96
|
const parsed = parseFieldValue(lines, index, match[2], sourceFile, lineNumber, errors);
|
|
@@ -113,7 +112,7 @@ function parseFieldValue(lines, index, rest, sourceFile, lineNumber, errors) {
|
|
|
113
112
|
block.push(next.replace(/^\s{0,2}/, ""));
|
|
114
113
|
}
|
|
115
114
|
if (block.length === 0) {
|
|
116
|
-
errors.push(`${sourceFile}:${lineNumber} block field must include indented text
|
|
115
|
+
errors.push(compileError(`${sourceFile}:${lineNumber} block field must include indented text`, "blocking_unparseable_object", sourceFile, lineNumber, "block_field", "empty block field cannot prove source intent", "add indented text or remove the field"));
|
|
117
116
|
}
|
|
118
117
|
return { value: block.join("\n").trim(), endIndex: cursor - 1 };
|
|
119
118
|
}
|
|
@@ -132,7 +131,7 @@ function parseFieldValue(lines, index, rest, sourceFile, lineNumber, errors) {
|
|
|
132
131
|
}
|
|
133
132
|
const listItem = /^\s*[-*+]\s+(.+?)\s*$/.exec(next);
|
|
134
133
|
if (!listItem) {
|
|
135
|
-
errors.push(`${sourceFile}:${cursor + 1} field lists must use indented "- item" entries or key: | blocks
|
|
134
|
+
errors.push(compileError(`${sourceFile}:${cursor + 1} field lists must use indented "- item" entries or key: | blocks`, "blocking_unparseable_object", sourceFile, cursor + 1, "list_field", "list syntax cannot be parsed into canonical arrays", "use indented '- item' entries or key: | blocks"));
|
|
136
135
|
continue;
|
|
137
136
|
}
|
|
138
137
|
values.push(cleanValue(listItem[1]));
|
|
@@ -158,7 +157,7 @@ function rejectListStyleDefinitions(lines, options, errors) {
|
|
|
158
157
|
continue;
|
|
159
158
|
}
|
|
160
159
|
const id = match[1].toUpperCase();
|
|
161
|
-
errors.push(`${id} list-style definition is not allowed at ${options.sourceFile}:${index + 1}; use "## ${id}: ..."`);
|
|
160
|
+
errors.push(compileError(`${id} list-style definition is not allowed at ${options.sourceFile}:${index + 1}; use "## ${id}: ..."`, "blocking_unparseable_object", options.sourceFile, index + 1, id, "list-style object definitions are not canonical PI/AC headings", `use "## ${id}: ..."`));
|
|
162
161
|
}
|
|
163
162
|
}
|
|
164
163
|
function listItemBlock(lines, start) {
|
|
@@ -217,8 +216,3 @@ function splitLines(content) {
|
|
|
217
216
|
function cleanValue(value) {
|
|
218
217
|
return value.replace(/^[-#*\s]+/, "").trim();
|
|
219
218
|
}
|
|
220
|
-
function throwIfErrors(errors) {
|
|
221
|
-
if (errors.length > 0) {
|
|
222
|
-
throw new Error(`Superpowers source compile failed:\n- ${errors.join("\n- ")}`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
@@ -54,11 +54,12 @@ export declare const SUPERPOWERS_TASK_STATE_JSON_SCHEMA: {
|
|
|
54
54
|
};
|
|
55
55
|
};
|
|
56
56
|
export type SuperpowersProofLayerStatus = "missing" | "satisfied" | "invalidated" | "blocked";
|
|
57
|
-
export type SuperpowersPlanItemStatus = "not_started" | "
|
|
57
|
+
export type SuperpowersPlanItemStatus = "not_started" | "in_progress" | "complete" | "partial" | "blocked" | "invalidated" | "out_of_scope_NA";
|
|
58
58
|
export type SuperpowersAcceptanceStatus = "not_run" | "complete" | "partial" | "blocked" | "invalidated" | "out_of_scope_NA";
|
|
59
59
|
export type SuperpowersProductDeliveryScope = "system_capability_build" | "representative_sample_validation" | "full_population_operation" | "mixed_scope_requires_boundary";
|
|
60
60
|
export type SuperpowersPlanDeliveryScope = "system_capability_build" | "representative_sample_validation" | "full_population_operation" | "out_of_scope_backlog";
|
|
61
61
|
export type SuperpowersAcceptanceScope = "system_capability_build" | "representative_sample_validation" | "full_population_operation" | "full_population_not_required";
|
|
62
|
+
export type SuperpowersScopeFitDecision = "fit_for_three_inputs" | "selected_from_split" | "blocked_for_decision" | "";
|
|
62
63
|
export interface SuperpowersTaskState {
|
|
63
64
|
meta: {
|
|
64
65
|
task_id: string;
|
|
@@ -95,6 +96,7 @@ export interface SuperpowersTaskState {
|
|
|
95
96
|
acceptance_target_status: string;
|
|
96
97
|
audit_task_complete: boolean;
|
|
97
98
|
completion_basis: string[];
|
|
99
|
+
next_required_actions?: string[];
|
|
98
100
|
};
|
|
99
101
|
}
|
|
100
102
|
export interface SuperpowersSourceRecord {
|
|
@@ -112,6 +114,20 @@ export interface SuperpowersProductArchitectureScope {
|
|
|
112
114
|
representative_samples_validate: string[];
|
|
113
115
|
representative_samples_do_not_validate: string[];
|
|
114
116
|
out_of_scope_backlog: string[];
|
|
117
|
+
scope_fit_decision: SuperpowersScopeFitDecision;
|
|
118
|
+
selected_scope_fit_slice: string;
|
|
119
|
+
owner_boundary: string;
|
|
120
|
+
primary_capability_path: string;
|
|
121
|
+
non_completing_outcomes: string[];
|
|
122
|
+
assertion_policy: string;
|
|
123
|
+
source_authority: string;
|
|
124
|
+
product_goal: string;
|
|
125
|
+
surface_ia_lock?: string;
|
|
126
|
+
decision_lock?: string;
|
|
127
|
+
context_delta?: string;
|
|
128
|
+
source_to_context_coverage?: string;
|
|
129
|
+
acceptance_semantics?: string;
|
|
130
|
+
impact?: string;
|
|
115
131
|
}
|
|
116
132
|
export interface SuperpowersProgressState {
|
|
117
133
|
system_capability_progress: Record<string, unknown>;
|
|
@@ -130,10 +146,35 @@ export interface SuperpowersPlanItem {
|
|
|
130
146
|
representative_samples: string[];
|
|
131
147
|
full_population_boundary: string;
|
|
132
148
|
non_required_population: string[];
|
|
149
|
+
owner_boundary: string;
|
|
150
|
+
primary_capability_path: string;
|
|
151
|
+
trigger_contract: string;
|
|
152
|
+
state_transition_contract: string;
|
|
153
|
+
observable_result_contract: string;
|
|
154
|
+
assertion_support: string;
|
|
155
|
+
required_assertion_commands: string[];
|
|
156
|
+
invalid_implementation_shortcuts: string[];
|
|
133
157
|
owner_surfaces: string[];
|
|
134
158
|
forbidden_surfaces: string[];
|
|
135
159
|
implementation_paths: string[];
|
|
136
160
|
required_tests: string[];
|
|
161
|
+
proof_layer_ids?: string[];
|
|
162
|
+
requirement_ref?: string;
|
|
163
|
+
decision_id?: string;
|
|
164
|
+
api_schema_changes?: string;
|
|
165
|
+
state_machine?: string;
|
|
166
|
+
data_flow?: string;
|
|
167
|
+
worker_runtime_behavior?: string;
|
|
168
|
+
ui_ia_changes?: string;
|
|
169
|
+
migration_plan?: string;
|
|
170
|
+
evidence_artifacts?: string[];
|
|
171
|
+
non_completing_shortcuts?: string[];
|
|
172
|
+
substitution_policy?: string[];
|
|
173
|
+
drift_severity?: string;
|
|
174
|
+
partial_conditions?: string[];
|
|
175
|
+
blockers?: string[];
|
|
176
|
+
explicit_no_test_scope?: boolean;
|
|
177
|
+
context_fact_refs?: string[];
|
|
137
178
|
status: SuperpowersPlanItemStatus;
|
|
138
179
|
related_acs: string[];
|
|
139
180
|
required_proof_layers: string[];
|
|
@@ -150,8 +191,44 @@ export interface SuperpowersAcceptanceCriterion {
|
|
|
150
191
|
full_population_required: boolean | null;
|
|
151
192
|
related_plan_items: string[];
|
|
152
193
|
required_proof_layers: string[];
|
|
194
|
+
assertion_requirements?: AssertionRequirement[];
|
|
195
|
+
assertion_command?: string;
|
|
196
|
+
assertion_artifacts?: string[];
|
|
197
|
+
positive_assertions?: string[];
|
|
198
|
+
negative_assertions?: string[];
|
|
199
|
+
machine_blocking?: boolean | null;
|
|
200
|
+
invalid_completion_signals?: string[];
|
|
201
|
+
assertion_result_required?: boolean | null;
|
|
202
|
+
ac_type?: string;
|
|
203
|
+
proof_chain?: string[];
|
|
204
|
+
required_test_ids?: string[];
|
|
205
|
+
fail_conditions?: string[];
|
|
206
|
+
invalid_evidence?: string[];
|
|
207
|
+
substitution_policy?: string[];
|
|
208
|
+
missing_layer_downgrade?: string;
|
|
209
|
+
auditor_expectation?: string;
|
|
210
|
+
out_of_scope_na_approval_source?: string;
|
|
211
|
+
hard_blockers?: string[];
|
|
212
|
+
validates_explanation?: string;
|
|
213
|
+
does_not_validate_explanation?: string;
|
|
214
|
+
final_evidence_expected?: string[];
|
|
215
|
+
verification_method?: string[];
|
|
216
|
+
test_cases?: string[];
|
|
217
|
+
explicit_no_test_scope?: boolean;
|
|
153
218
|
status: SuperpowersAcceptanceStatus;
|
|
154
219
|
}
|
|
220
|
+
export interface AssertionRequirement {
|
|
221
|
+
proof_layer: string;
|
|
222
|
+
required: boolean;
|
|
223
|
+
machine_blocking: boolean;
|
|
224
|
+
assertion_result_required?: boolean;
|
|
225
|
+
assertion_command?: string;
|
|
226
|
+
assertion_artifacts?: string[];
|
|
227
|
+
invalid_completion_signals?: string[];
|
|
228
|
+
required_test_ids: string[];
|
|
229
|
+
positive_assertions: string[];
|
|
230
|
+
negative_assertions: string[];
|
|
231
|
+
}
|
|
155
232
|
export interface SuperpowersProofLayer {
|
|
156
233
|
required: boolean;
|
|
157
234
|
status: SuperpowersProofLayerStatus;
|
|
@@ -190,6 +267,7 @@ export interface SuperpowersEvidenceRecord {
|
|
|
190
267
|
stale_after: string | null;
|
|
191
268
|
};
|
|
192
269
|
command?: string;
|
|
270
|
+
command_exit_code?: number;
|
|
193
271
|
artifact_paths: string[];
|
|
194
272
|
proves: string[];
|
|
195
273
|
does_not_prove: string[];
|
|
@@ -201,8 +279,49 @@ export interface SuperpowersEvidenceRecord {
|
|
|
201
279
|
external_reviewer_can_reproduce: boolean;
|
|
202
280
|
reproduction_steps: string;
|
|
203
281
|
};
|
|
282
|
+
assertion_result?: AssertionResult;
|
|
283
|
+
negative_evidence_scan?: NegativeEvidenceScan;
|
|
204
284
|
sibling_substitution_used?: boolean;
|
|
205
285
|
sibling_substitution_approval_source?: string;
|
|
206
286
|
}
|
|
287
|
+
export interface AssertionResult {
|
|
288
|
+
schema_version: "assertion-result-v1";
|
|
289
|
+
status: "passed" | "failed" | "blocked" | "stale";
|
|
290
|
+
runner: string;
|
|
291
|
+
exit_code: number;
|
|
292
|
+
target_ac_ids: string[];
|
|
293
|
+
target_proof_layers: string[];
|
|
294
|
+
owner_surface?: string;
|
|
295
|
+
route?: string;
|
|
296
|
+
action?: string;
|
|
297
|
+
positive_assertions: AssertionCheck[];
|
|
298
|
+
negative_assertions: AssertionCheck[];
|
|
299
|
+
artifacts?: string[];
|
|
300
|
+
}
|
|
301
|
+
export interface AssertionCheck {
|
|
302
|
+
id: string;
|
|
303
|
+
status: "passed" | "failed" | "blocked" | "stale";
|
|
304
|
+
actual?: string;
|
|
305
|
+
expected?: string;
|
|
306
|
+
forbidden_text?: string;
|
|
307
|
+
}
|
|
308
|
+
export interface NegativeEvidenceScan {
|
|
309
|
+
schema_version: "negative-evidence-scan-v1";
|
|
310
|
+
status: "passed" | "failed" | "blocked" | "stale";
|
|
311
|
+
target_ac_ids: string[];
|
|
312
|
+
target_proof_layers?: string[];
|
|
313
|
+
invalid_completion_signals_checked?: string[];
|
|
314
|
+
owner_surface?: string;
|
|
315
|
+
route?: string;
|
|
316
|
+
forbidden_findings: NegativeFinding[];
|
|
317
|
+
required_findings: AssertionCheck[];
|
|
318
|
+
artifacts: string[];
|
|
319
|
+
}
|
|
320
|
+
export interface NegativeFinding {
|
|
321
|
+
id: string;
|
|
322
|
+
status: "found" | "not_found";
|
|
323
|
+
forbidden_text?: string;
|
|
324
|
+
actual?: string;
|
|
325
|
+
}
|
|
207
326
|
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
208
327
|
export declare function asStringArray(value: unknown): string[];
|