project-tiny-context-harness 0.7.2 → 0.7.4
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 +7 -5
- package/assets/README.md +49 -14
- package/assets/README.zh-CN.md +26 -13
- package/assets/agents/AGENTS_CORE.md +5 -5
- package/assets/context_templates/global.md +1 -0
- package/assets/skills/context_development_engineer/SKILL.md +4 -1
- package/assets/skills/context_uiux_design/SKILL.md +19 -6
- package/assets/skills/long-task-workflow/SKILL.md +10 -9
- package/assets/skills/long-task-workflow/references/authority-lifecycle.md +7 -5
- package/assets/skills/long-task-workflow/references/contract-authoring.md +24 -6
- package/assets/skills/long-task-workflow/references/evidence-design.md +14 -1
- package/assets/skills/source-plan-authoring/SKILL.md +2 -2
- package/dist/commands/long-task-authoring.js +25 -0
- package/dist/commands/long-task.js +3 -0
- package/dist/lib/design-md.d.ts +3 -0
- package/dist/lib/design-md.js +48 -55
- package/dist/lib/doctor.js +8 -0
- package/dist/lib/long-task-activation-validation.js +13 -2
- package/dist/lib/long-task-authoring-authority-preview.js +1 -0
- package/dist/lib/long-task-authority-material-diff.js +26 -0
- package/dist/lib/long-task-authority-materials.d.ts +2 -2
- package/dist/lib/long-task-authority-materials.js +6 -0
- package/dist/lib/long-task-authority-policy.d.ts +12 -0
- package/dist/lib/long-task-authority-policy.js +9 -0
- package/dist/lib/long-task-authority-revision-analysis.d.ts +33 -0
- package/dist/lib/long-task-authority-revision-analysis.js +91 -0
- package/dist/lib/long-task-authority-revision.js +93 -158
- package/dist/lib/long-task-authority-types.d.ts +7 -0
- package/dist/lib/long-task-authority.js +32 -3
- package/dist/lib/long-task-check-evidence-decoder.d.ts +2 -2
- package/dist/lib/long-task-check-evidence-decoder.js +11 -1
- package/dist/lib/long-task-check-execution-policy.d.ts +3 -0
- package/dist/lib/long-task-check-execution-policy.js +5 -0
- package/dist/lib/long-task-check-runner.js +2 -0
- package/dist/lib/long-task-check-shape.js +33 -3
- package/dist/lib/long-task-conformance-policy.d.ts +5 -0
- package/dist/lib/long-task-conformance-policy.js +35 -0
- package/dist/lib/long-task-contract-types.d.ts +14 -4
- package/dist/lib/long-task-delivery-compiler.js +1 -0
- package/dist/lib/long-task-delivery-parser.js +5 -2
- package/dist/lib/long-task-delivery-types.d.ts +2 -0
- package/dist/lib/long-task-delivery-types.js +2 -0
- package/dist/lib/long-task-delivery-validation.js +11 -0
- package/dist/lib/long-task-evidence-capability-codec.d.ts +2 -0
- package/dist/lib/long-task-evidence-capability-codec.js +231 -0
- package/dist/lib/long-task-evidence-capability-policy.d.ts +8 -0
- package/dist/lib/long-task-evidence-capability-policy.js +147 -0
- package/dist/lib/long-task-evidence-capability-runtime.d.ts +2 -0
- package/dist/lib/long-task-evidence-capability-runtime.js +86 -0
- package/dist/lib/long-task-evidence-capability-types.d.ts +74 -0
- package/dist/lib/long-task-evidence-capability-types.js +1 -0
- package/dist/lib/long-task-evidence-findings.js +5 -1
- package/dist/lib/long-task-evidence-v2.js +38 -23
- package/dist/lib/long-task-final-v2.js +42 -1
- package/dist/lib/long-task-outcome-parser.js +14 -3
- package/dist/lib/long-task-playwright-evidence.d.ts +2 -1
- package/dist/lib/long-task-playwright-evidence.js +70 -2
- package/dist/lib/long-task-progress.js +4 -0
- package/dist/lib/long-task-root-shape.d.ts +1 -0
- package/dist/lib/long-task-root-shape.js +84 -3
- package/dist/lib/long-task-runner-environment.js +1 -1
- package/dist/lib/long-task-runner-freeze.d.ts +2 -2
- package/dist/lib/long-task-runner-freeze.js +3 -1
- package/dist/lib/long-task-runtime-types.d.ts +13 -0
- package/dist/lib/long-task-semantic-contract-types.d.ts +36 -0
- package/dist/lib/long-task-semantic-contract-types.js +1 -0
- package/dist/lib/long-task-semantic-drift-migration.d.ts +3 -0
- package/dist/lib/long-task-semantic-drift-migration.js +68 -0
- package/dist/lib/long-task-shape-primitives.d.ts +3 -0
- package/dist/lib/long-task-shape-primitives.js +25 -0
- package/dist/lib/long-task-stage-policy.d.ts +4 -0
- package/dist/lib/long-task-stage-policy.js +120 -0
- package/dist/lib/long-task-status-projection.d.ts +4 -1
- package/dist/lib/long-task-status-projection.js +63 -3
- package/dist/lib/long-task-status-v2.d.ts +11 -1
- package/dist/lib/long-task-status-v2.js +25 -3
- package/dist/lib/long-task-target-policy.d.ts +6 -0
- package/dist/lib/long-task-target-policy.js +127 -0
- package/dist/lib/migrations.js +29 -0
- package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +61 -7
- package/migrations/README.md +7 -0
- package/package.json +2 -2
|
@@ -74,18 +74,28 @@ export async function runDeliveryFinalGate(workdirInput) {
|
|
|
74
74
|
const blocked = globalBlocked ||
|
|
75
75
|
Object.values(outcomeResults).includes("blocked_external") ||
|
|
76
76
|
run.findings.some((finding) => finding.code === "blocked_external");
|
|
77
|
+
const targetBlocked = compiled.global.acceptance.external_confirmations.some((confirmation) => confirmation.blocks_target);
|
|
77
78
|
const workflowStatus = failed
|
|
78
79
|
? "needs_work"
|
|
79
|
-
: blocked
|
|
80
|
+
: blocked || targetBlocked
|
|
80
81
|
? "blocked_external"
|
|
81
82
|
: compiled.global.acceptance.external_confirmations.length
|
|
82
83
|
? "machine_accepted_external_pending"
|
|
83
84
|
: "machine_accepted";
|
|
85
|
+
const targetState = workflowStatus === "machine_accepted" ||
|
|
86
|
+
workflowStatus === "machine_accepted_external_pending"
|
|
87
|
+
? compiled.task.target_profile.required_state
|
|
88
|
+
: workflowStatus === "blocked_external"
|
|
89
|
+
? "blocked_external"
|
|
90
|
+
: "not_accepted";
|
|
84
91
|
return writeFinalReceipt(repository, active.workdir, {
|
|
85
92
|
schema_version: "long-task-final-receipt-v2",
|
|
86
93
|
authority_scope: "audit_only",
|
|
87
94
|
reusable_for_acceptance: false,
|
|
88
95
|
workflow_status: workflowStatus,
|
|
96
|
+
target_profile: compiled.task.target_profile,
|
|
97
|
+
target_state: targetState,
|
|
98
|
+
stage_results: projectStages(compiled, outcomeResults),
|
|
89
99
|
compiled_identity: compiled.compiled_identity,
|
|
90
100
|
contract_sha256: compiled.contract_sha256,
|
|
91
101
|
snapshot_sha256: snapshot.manifest.snapshot_sha256,
|
|
@@ -107,6 +117,37 @@ export async function runDeliveryFinalGate(workdirInput) {
|
|
|
107
117
|
await snapshot.dispose();
|
|
108
118
|
}
|
|
109
119
|
}
|
|
120
|
+
function projectStages(compiled, outcomes) {
|
|
121
|
+
const result = {};
|
|
122
|
+
const remaining = new Set(compiled.stages.map((stage) => stage.key));
|
|
123
|
+
while (remaining.size) {
|
|
124
|
+
let advanced = false;
|
|
125
|
+
for (const stage of compiled.stages) {
|
|
126
|
+
if (!remaining.has(stage.key))
|
|
127
|
+
continue;
|
|
128
|
+
if (stage.depends_on.some((dependency) => remaining.has(dependency)))
|
|
129
|
+
continue;
|
|
130
|
+
const owned = compiled.outcomes
|
|
131
|
+
.filter((outcome) => outcome.stage === stage.key)
|
|
132
|
+
.map((outcome) => outcomes[outcome.key]);
|
|
133
|
+
result[stage.key] = owned.includes("failed")
|
|
134
|
+
? "failed"
|
|
135
|
+
: owned.includes("blocked_external")
|
|
136
|
+
? "blocked_external"
|
|
137
|
+
: stage.depends_on.some((dependency) => result[dependency] !== "passed")
|
|
138
|
+
? "blocked_dependency"
|
|
139
|
+
: "passed";
|
|
140
|
+
remaining.delete(stage.key);
|
|
141
|
+
advanced = true;
|
|
142
|
+
}
|
|
143
|
+
if (!advanced) {
|
|
144
|
+
for (const stage of remaining)
|
|
145
|
+
result[stage] = "blocked_dependency";
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
110
151
|
function projectOutcomes(outcomeKeys, checks, findings) {
|
|
111
152
|
return Object.fromEntries(outcomeKeys.map((outcomeKey) => {
|
|
112
153
|
const owned = checks.filter((check) => check.outcome_key === outcomeKey);
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { parseCheck } from "./long-task-check-shape.js";
|
|
2
2
|
import { normalizeRepositoryFile } from "./long-task-paths.js";
|
|
3
3
|
import { parseRequirements } from "./long-task-requirement-shape.js";
|
|
4
|
-
import { array, key, literal, nullable, object, parseBindings, parseControls, parseCounterfactuals, parseKeyedStatements, parseObligations, parseOwner, parsePopulation, parseRollback, repositoryPatterns, string, strings, } from "./long-task-delivery-shape.js";
|
|
4
|
+
import { array, boolean, key, literal, nullable, object, parseBindings, parseControls, parseCounterfactuals, parseKeyedStatements, parseObligations, parseOwner, parsePopulation, parseRollback, repositoryPatterns, string, strings, } from "./long-task-delivery-shape.js";
|
|
5
5
|
import { parseStrictYaml } from "./strict-codec.js";
|
|
6
|
+
import { assertNoSemanticDriftMigration, semanticDriftOutcomeMigrationFields, } from "./long-task-semantic-drift-migration.js";
|
|
6
7
|
export function parseOutcome(value, label) {
|
|
7
|
-
|
|
8
|
-
const
|
|
8
|
+
assertNoSemanticDriftMigration(semanticDriftOutcomeMigrationFields(value, label));
|
|
9
|
+
const row = object(value, label, ["key", "title", "stage", "product", "technical", "acceptance"], ["depends_on"]);
|
|
10
|
+
const product = object(row.product, `${label}.product`, [
|
|
11
|
+
"observable_result",
|
|
12
|
+
"success_path_required",
|
|
13
|
+
"degradation_path_required",
|
|
14
|
+
"owner",
|
|
15
|
+
], ["requirements", "owner_surfaces", "controls", "non_completing_outcomes"]);
|
|
9
16
|
const technical = object(row.technical, `${label}.technical`, ["expected_change_paths"], [
|
|
10
17
|
"obligations",
|
|
11
18
|
"allowed_support_paths",
|
|
@@ -18,11 +25,14 @@ export function parseOutcome(value, label) {
|
|
|
18
25
|
return {
|
|
19
26
|
key: key(row.key, `${label}.key`),
|
|
20
27
|
title: string(row.title, `${label}.title`),
|
|
28
|
+
stage: key(row.stage, `${label}.stage`),
|
|
21
29
|
depends_on: Object.hasOwn(row, "depends_on")
|
|
22
30
|
? strings(row.depends_on, `${label}.depends_on`).map((item, index) => key(item, `${label}.depends_on[${index}]`))
|
|
23
31
|
: [],
|
|
24
32
|
product: {
|
|
25
33
|
observable_result: string(product.observable_result, `${label}.product.observable_result`),
|
|
34
|
+
success_path_required: boolean(product.success_path_required, `${label}.product.success_path_required`),
|
|
35
|
+
degradation_path_required: boolean(product.degradation_path_required, `${label}.product.degradation_path_required`),
|
|
26
36
|
owner: parseOwner(product.owner, `${label}.product.owner`),
|
|
27
37
|
requirements: Object.hasOwn(product, "requirements")
|
|
28
38
|
? parseRequirements(product.requirements, `${label}.product.requirements`)
|
|
@@ -78,6 +88,7 @@ export function parseOutcomeFragment(raw, relative) {
|
|
|
78
88
|
"outcomes",
|
|
79
89
|
"key",
|
|
80
90
|
"title",
|
|
91
|
+
"stage",
|
|
81
92
|
"depends_on",
|
|
82
93
|
"product",
|
|
83
94
|
"technical",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { CompiledCheckV2 } from "./long-task-delivery-types.js";
|
|
1
|
+
import type { CompiledCheckV2, EvidenceCapabilityRecordV2 } from "./long-task-delivery-types.js";
|
|
2
2
|
export interface PlaywrightEvidence {
|
|
3
3
|
observations: Record<string, unknown>;
|
|
4
|
+
evidence_records: EvidenceCapabilityRecordV2[];
|
|
4
5
|
error: string | null;
|
|
5
6
|
}
|
|
6
7
|
export declare function extractPlaywrightEvidence(check: CompiledCheckV2, report: Record<string, unknown>, exitCode: number): PlaywrightEvidence;
|
|
@@ -61,7 +61,33 @@ export function extractPlaywrightEvidence(check, report, exitCode) {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
observations["playwright.case_ids"] = cases.map((item) => item.id).sort();
|
|
64
|
-
|
|
64
|
+
const evidenceRecords = [];
|
|
65
|
+
for (const item of cases) {
|
|
66
|
+
const assertion = [
|
|
67
|
+
...check.positive_assertions,
|
|
68
|
+
...check.negative_assertions,
|
|
69
|
+
].find((candidate) => candidate.key === item.id);
|
|
70
|
+
if (assertion?.evidence_capabilities.includes("interaction_trace") &&
|
|
71
|
+
item.executed)
|
|
72
|
+
evidenceRecords.push({
|
|
73
|
+
assertion_key: assertion.key,
|
|
74
|
+
capability: "interaction_trace",
|
|
75
|
+
target_ref: check.execution_target.target_ref,
|
|
76
|
+
given_keys: item.given_keys,
|
|
77
|
+
action_keys: item.action_keys,
|
|
78
|
+
});
|
|
79
|
+
if (assertion?.evidence_capabilities.includes("target_runtime") &&
|
|
80
|
+
item.executed)
|
|
81
|
+
evidenceRecords.push({
|
|
82
|
+
assertion_key: assertion.key,
|
|
83
|
+
capability: "target_runtime",
|
|
84
|
+
target_ref: check.execution_target.target_ref,
|
|
85
|
+
root_entrypoint: check.execution_target_definition.root_entrypoint,
|
|
86
|
+
session_id: `playwright:${item.id}:${item.project_ids.join(",")}`,
|
|
87
|
+
cold_start: check.execution_target.entrypoint === "root",
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return { observations, evidence_records: evidenceRecords, error: null };
|
|
65
91
|
}
|
|
66
92
|
function collectCases(report, declaredIds) {
|
|
67
93
|
const cases = [];
|
|
@@ -139,6 +165,10 @@ function playwrightCase(id, test) {
|
|
|
139
165
|
status === "expected" &&
|
|
140
166
|
resultStatuses.length > 0 &&
|
|
141
167
|
resultStatuses.at(-1) === "passed";
|
|
168
|
+
const traces = results.map((result) => scenarioTrace(result));
|
|
169
|
+
const givenKeys = traces[0]?.given_keys ?? [];
|
|
170
|
+
const actionKeys = traces[0]?.action_keys ?? [];
|
|
171
|
+
const traceConsistent = traces.every((trace) => same(trace.given_keys, givenKeys) && same(trace.action_keys, actionKeys));
|
|
142
172
|
return {
|
|
143
173
|
id,
|
|
144
174
|
project_id: projectId,
|
|
@@ -150,6 +180,8 @@ function playwrightCase(id, test) {
|
|
|
150
180
|
timed_out: timedOut,
|
|
151
181
|
interrupted,
|
|
152
182
|
status,
|
|
183
|
+
given_keys: traceConsistent ? givenKeys : [],
|
|
184
|
+
action_keys: traceConsistent ? actionKeys : [],
|
|
153
185
|
};
|
|
154
186
|
}
|
|
155
187
|
function declaredCaseIds(check) {
|
|
@@ -227,6 +259,14 @@ function aggregateCases(instances) {
|
|
|
227
259
|
unexpected_instances: unexpectedInstances,
|
|
228
260
|
timed_out_instances: timedOutInstances,
|
|
229
261
|
interrupted_instances: interruptedInstances,
|
|
262
|
+
given_keys: rows.length &&
|
|
263
|
+
rows.every((row) => same(row.given_keys, rows[0].given_keys))
|
|
264
|
+
? rows[0].given_keys
|
|
265
|
+
: [],
|
|
266
|
+
action_keys: rows.length &&
|
|
267
|
+
rows.every((row) => same(row.action_keys, rows[0].action_keys))
|
|
268
|
+
? rows[0].action_keys
|
|
269
|
+
: [],
|
|
230
270
|
};
|
|
231
271
|
})
|
|
232
272
|
.sort((left, right) => left.id.localeCompare(right.id));
|
|
@@ -239,6 +279,34 @@ function record(value) {
|
|
|
239
279
|
function integer(value) {
|
|
240
280
|
return Number.isInteger(value) && Number(value) >= 0 ? Number(value) : null;
|
|
241
281
|
}
|
|
282
|
+
function scenarioTrace(result) {
|
|
283
|
+
const givenKeys = [];
|
|
284
|
+
const actionKeys = [];
|
|
285
|
+
const visit = (value) => {
|
|
286
|
+
if (!Array.isArray(value))
|
|
287
|
+
return;
|
|
288
|
+
for (const item of value) {
|
|
289
|
+
const step = record(item);
|
|
290
|
+
if (!step)
|
|
291
|
+
continue;
|
|
292
|
+
if (typeof step.title === "string") {
|
|
293
|
+
const given = /^\[given:([a-z0-9][a-z0-9-]*)\]$/u.exec(step.title);
|
|
294
|
+
const action = /^\[action:([a-z0-9][a-z0-9-]*)\]$/u.exec(step.title);
|
|
295
|
+
if (given)
|
|
296
|
+
givenKeys.push(given[1]);
|
|
297
|
+
if (action)
|
|
298
|
+
actionKeys.push(action[1]);
|
|
299
|
+
}
|
|
300
|
+
visit(step.steps);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
visit(result.steps);
|
|
304
|
+
return { given_keys: givenKeys, action_keys: actionKeys };
|
|
305
|
+
}
|
|
306
|
+
function same(left, right) {
|
|
307
|
+
return (left.length === right.length &&
|
|
308
|
+
left.every((value, index) => value === right[index]));
|
|
309
|
+
}
|
|
242
310
|
function invalid(error) {
|
|
243
|
-
return { observations: {}, error };
|
|
311
|
+
return { observations: {}, evidence_records: [], error };
|
|
244
312
|
}
|
|
@@ -76,6 +76,10 @@ export function checkIdentity(check) {
|
|
|
76
76
|
artifact_globs: check.artifact_globs,
|
|
77
77
|
positive_assertions: check.positive_assertions,
|
|
78
78
|
negative_assertions: check.negative_assertions,
|
|
79
|
+
journey_roles: check.journey_roles,
|
|
80
|
+
execution_target: check.execution_target,
|
|
81
|
+
execution_target_definition: check.execution_target_definition,
|
|
82
|
+
scenario: check.scenario,
|
|
79
83
|
environment_requirements: check.environment_requirements,
|
|
80
84
|
}));
|
|
81
85
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { DeliveryContractV2 } from "./long-task-delivery-types.js";
|
|
2
2
|
export declare function parseTask(value: unknown): DeliveryContractV2["task"];
|
|
3
|
+
export declare function parseStages(value: unknown): DeliveryContractV2["stages"];
|
|
3
4
|
export declare function parseRisk(value: unknown): DeliveryContractV2["risk"];
|
|
4
5
|
export declare function parseGlobal(value: unknown): DeliveryContractV2["global"];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parseGlobalCounterfactuals } from "./long-task-acceptance-shape.js";
|
|
2
2
|
import { parseCheck } from "./long-task-check-shape.js";
|
|
3
|
-
import { array, key, literal, object, parseKeyedPaths, parseKeyedStatements, repositoryFiles, string, strings, } from "./long-task-delivery-shape.js";
|
|
3
|
+
import { array, boolean, key, literal, object, parseKeyedPaths, parseKeyedStatements, repositoryFiles, string, strings, } from "./long-task-delivery-shape.js";
|
|
4
4
|
const RISK_FACTS = [
|
|
5
5
|
"public_api_or_schema_change",
|
|
6
6
|
"persistent_data_change",
|
|
@@ -14,11 +14,59 @@ const RISK_FACTS = [
|
|
|
14
14
|
"weak_observability",
|
|
15
15
|
];
|
|
16
16
|
export function parseTask(value) {
|
|
17
|
-
const row = object(value, "task", [
|
|
17
|
+
const row = object(value, "task", [
|
|
18
|
+
"id",
|
|
19
|
+
"title",
|
|
20
|
+
"goal",
|
|
21
|
+
"target_profile",
|
|
22
|
+
"execution_targets",
|
|
23
|
+
"source_paths",
|
|
24
|
+
"context_refs",
|
|
25
|
+
], ["context_snapshot_mode"]);
|
|
26
|
+
const targetProfile = object(row.target_profile, "task.target_profile", [
|
|
27
|
+
"key",
|
|
28
|
+
"description",
|
|
29
|
+
"required_state",
|
|
30
|
+
"required_target_refs",
|
|
31
|
+
]);
|
|
18
32
|
return {
|
|
19
33
|
id: key(row.id, "task.id"),
|
|
20
34
|
title: string(row.title, "task.title"),
|
|
21
35
|
goal: string(row.goal, "task.goal"),
|
|
36
|
+
target_profile: {
|
|
37
|
+
key: key(targetProfile.key, "task.target_profile.key"),
|
|
38
|
+
description: string(targetProfile.description, "task.target_profile.description"),
|
|
39
|
+
required_state: literal(targetProfile.required_state, [
|
|
40
|
+
"implementation_complete",
|
|
41
|
+
"target_profile_usable",
|
|
42
|
+
"production_release_ready",
|
|
43
|
+
], "task.target_profile.required_state"),
|
|
44
|
+
required_target_refs: strings(targetProfile.required_target_refs, "task.target_profile.required_target_refs").map((item, index) => key(item, `task.target_profile.required_target_refs[${index}]`)),
|
|
45
|
+
},
|
|
46
|
+
execution_targets: array(row.execution_targets, "task.execution_targets").map((item, index) => {
|
|
47
|
+
const label = `task.execution_targets[${index}]`;
|
|
48
|
+
const target = object(item, label, [
|
|
49
|
+
"key",
|
|
50
|
+
"description",
|
|
51
|
+
"role",
|
|
52
|
+
"runtime_family",
|
|
53
|
+
"root_entrypoint",
|
|
54
|
+
]);
|
|
55
|
+
return {
|
|
56
|
+
key: key(target.key, `${label}.key`),
|
|
57
|
+
description: string(target.description, `${label}.description`),
|
|
58
|
+
role: literal(target.role, ["product", "support", "observer"], `${label}.role`),
|
|
59
|
+
runtime_family: literal(target.runtime_family, [
|
|
60
|
+
"browser",
|
|
61
|
+
"native",
|
|
62
|
+
"desktop",
|
|
63
|
+
"service",
|
|
64
|
+
"process",
|
|
65
|
+
"external",
|
|
66
|
+
], `${label}.runtime_family`),
|
|
67
|
+
root_entrypoint: string(target.root_entrypoint, `${label}.root_entrypoint`),
|
|
68
|
+
};
|
|
69
|
+
}),
|
|
22
70
|
source_paths: repositoryFiles(row.source_paths, "task.source_paths"),
|
|
23
71
|
context_refs: repositoryFiles(row.context_refs, "task.context_refs"),
|
|
24
72
|
context_snapshot_mode: Object.hasOwn(row, "context_snapshot_mode")
|
|
@@ -26,6 +74,23 @@ export function parseTask(value) {
|
|
|
26
74
|
: "referenced",
|
|
27
75
|
};
|
|
28
76
|
}
|
|
77
|
+
export function parseStages(value) {
|
|
78
|
+
return array(value, "stages").map((item, index) => {
|
|
79
|
+
const label = `stages[${index}]`;
|
|
80
|
+
const row = object(item, label, [
|
|
81
|
+
"key",
|
|
82
|
+
"title",
|
|
83
|
+
"depends_on",
|
|
84
|
+
"gate_outcome",
|
|
85
|
+
]);
|
|
86
|
+
return {
|
|
87
|
+
key: key(row.key, `${label}.key`),
|
|
88
|
+
title: string(row.title, `${label}.title`),
|
|
89
|
+
depends_on: strings(row.depends_on, `${label}.depends_on`).map((entry, dependencyIndex) => key(entry, `${label}.depends_on[${dependencyIndex}]`)),
|
|
90
|
+
gate_outcome: key(row.gate_outcome, `${label}.gate_outcome`),
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
}
|
|
29
94
|
export function parseRisk(value) {
|
|
30
95
|
const row = object(value, "risk", ["facts"], ["requested_level"]);
|
|
31
96
|
const facts = object(row.facts, "risk.facts", [], [...RISK_FACTS]);
|
|
@@ -73,11 +138,27 @@ export function parseGlobal(value) {
|
|
|
73
138
|
external_confirmations: Object.hasOwn(acceptance, "external_confirmations")
|
|
74
139
|
? array(acceptance.external_confirmations, "global.acceptance.external_confirmations").map((item, index) => {
|
|
75
140
|
const label = `global.acceptance.external_confirmations[${index}]`;
|
|
76
|
-
const entry = object(item, label, [
|
|
141
|
+
const entry = object(item, label, [
|
|
142
|
+
"key",
|
|
143
|
+
"description",
|
|
144
|
+
"owner",
|
|
145
|
+
"kind",
|
|
146
|
+
"impact_claims",
|
|
147
|
+
"blocks_target",
|
|
148
|
+
]);
|
|
77
149
|
return {
|
|
78
150
|
key: key(entry.key, `${label}.key`),
|
|
79
151
|
description: string(entry.description, `${label}.description`),
|
|
80
152
|
owner: string(entry.owner, `${label}.owner`),
|
|
153
|
+
kind: literal(entry.kind, [
|
|
154
|
+
"functional_prerequisite",
|
|
155
|
+
"field_validation",
|
|
156
|
+
"production_release_gate",
|
|
157
|
+
"commercial_activation",
|
|
158
|
+
"expert_authority",
|
|
159
|
+
], `${label}.kind`),
|
|
160
|
+
impact_claims: strings(entry.impact_claims, `${label}.impact_claims`),
|
|
161
|
+
blocks_target: boolean(entry.blocks_target, `${label}.blocks_target`),
|
|
81
162
|
};
|
|
82
163
|
})
|
|
83
164
|
: [],
|
|
@@ -23,7 +23,7 @@ const BASE_ENVIRONMENT_KEYS = [
|
|
|
23
23
|
];
|
|
24
24
|
export function runnerEnvironment(requirements) {
|
|
25
25
|
const environment = {
|
|
26
|
-
TY_CONTEXT_CHECK_PROTOCOL: "long-task-check-result-
|
|
26
|
+
TY_CONTEXT_CHECK_PROTOCOL: "long-task-check-result-v3",
|
|
27
27
|
};
|
|
28
28
|
for (const key of BASE_ENVIRONMENT_KEYS)
|
|
29
29
|
copyEnvironmentValue(environment, key);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { CompiledCheckV2, DeliveryCheckV2, WorkspaceManifestV2 } from "./long-task-delivery-types.js";
|
|
2
|
-
export declare function freezeDeliveryCheck(check: DeliveryCheckV2, outcomeKey: string | null, repository: string, baseline: WorkspaceManifestV2): Promise<CompiledCheckV2>;
|
|
1
|
+
import type { CompiledCheckV2, DeliveryCheckV2, ExecutionTargetV2, WorkspaceManifestV2 } from "./long-task-delivery-types.js";
|
|
2
|
+
export declare function freezeDeliveryCheck(check: DeliveryCheckV2, outcomeKey: string | null, repository: string, baseline: WorkspaceManifestV2, executionTarget: ExecutionTargetV2, knownExecutionTargets: ExecutionTargetV2[]): Promise<CompiledCheckV2>;
|
|
@@ -7,7 +7,7 @@ import { nearestRunnerFile, npmCliPath, npxCliPath, } from "./long-task-runner-f
|
|
|
7
7
|
import { canonicalValueJson, sha256Hex } from "./strict-codec.js";
|
|
8
8
|
import { repoRelative, resolveInsideRepository, } from "./long-task-workspace.js";
|
|
9
9
|
import { evidenceAdapterForRunner } from "./long-task-evidence-adapter-policy.js";
|
|
10
|
-
export async function freezeDeliveryCheck(check, outcomeKey, repository, baseline) {
|
|
10
|
+
export async function freezeDeliveryCheck(check, outcomeKey, repository, baseline, executionTarget, knownExecutionTargets) {
|
|
11
11
|
const prefix = outcomeKey ? `CHECK.${outcomeKey}` : "CHECK.GLOBAL";
|
|
12
12
|
const expectedOutputs = check.expected_output_paths.map((pattern, index) => assertRepositoryPattern(repository, pattern, `${check.key}.expected_output_paths[${index}]`));
|
|
13
13
|
for (const [index, pattern] of check.input_paths.entries()) {
|
|
@@ -30,6 +30,8 @@ export async function freezeDeliveryCheck(check, outcomeKey, repository, baselin
|
|
|
30
30
|
evidence_adapter: evidenceAdapterForRunner(check.runner.type),
|
|
31
31
|
runner: await freezeRunner(check, repository, cwd, target, verificationInputHashes),
|
|
32
32
|
verification_input_hashes: verificationInputHashes,
|
|
33
|
+
execution_target_definition: executionTarget,
|
|
34
|
+
known_execution_targets: knownExecutionTargets,
|
|
33
35
|
};
|
|
34
36
|
return {
|
|
35
37
|
...compiled,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { DeliveryCheckV2, DeliveryContractV2, DeliveryOutcomeV2, DeliveryRunnerV2, ProofSurface } from "./long-task-contract-types.js";
|
|
2
|
+
import type { EvidenceCapabilityV2, ExecutionTargetV2 } from "./long-task-semantic-contract-types.js";
|
|
2
3
|
import type { AuthorityHashesV2, ContextAuthoritySnapshotV2, InitialTaskBaseV2, NextAuthorityMaterialsV2, VerifierIdentityV2 } from "./long-task-authority-types.js";
|
|
3
4
|
import type { EvidenceAdapter } from "./long-task-evidence-adapter-types.js";
|
|
5
|
+
import type { EvidenceCapabilityRecordV2 } from "./long-task-evidence-capability-types.js";
|
|
4
6
|
import type { EffectiveRiskLevel, RiskFactName } from "./long-task-risk-types.js";
|
|
5
7
|
import type { CompiledSourceItemV2 } from "./long-task-source-authority-types.js";
|
|
6
8
|
import type { WorkspaceManifestV2 } from "./long-task-workspace-runtime-types.js";
|
|
@@ -21,6 +23,8 @@ export interface CompiledCheckV2 extends Omit<DeliveryCheckV2, "runner"> {
|
|
|
21
23
|
evidence_adapter: EvidenceAdapter;
|
|
22
24
|
verification_input_hashes: Record<string, string>;
|
|
23
25
|
raw_execution_identity: string;
|
|
26
|
+
execution_target_definition: ExecutionTargetV2;
|
|
27
|
+
known_execution_targets: ExecutionTargetV2[];
|
|
24
28
|
}
|
|
25
29
|
export interface ProductClaimV2 {
|
|
26
30
|
id: string;
|
|
@@ -88,6 +92,7 @@ export interface CompiledDeliveryContractV2 {
|
|
|
88
92
|
task: DeliveryContractV2["task"];
|
|
89
93
|
risk: DeliveryContractV2["risk"];
|
|
90
94
|
source_claims: DeliveryContractV2["source_claims"];
|
|
95
|
+
stages: DeliveryContractV2["stages"];
|
|
91
96
|
global: Omit<DeliveryContractV2["global"], "acceptance"> & {
|
|
92
97
|
acceptance: {
|
|
93
98
|
checks: CompiledCheckV2[];
|
|
@@ -123,6 +128,8 @@ export interface AssertionResultV2 {
|
|
|
123
128
|
passed: boolean;
|
|
124
129
|
claims: string[];
|
|
125
130
|
observation: string;
|
|
131
|
+
evidence_capabilities: EvidenceCapabilityV2[];
|
|
132
|
+
evidence_complete: boolean;
|
|
126
133
|
status: "passed" | "observation_missing" | "observation_type_mismatch" | "assertion_value_mismatch";
|
|
127
134
|
expected?: unknown;
|
|
128
135
|
actual?: unknown;
|
|
@@ -133,6 +140,7 @@ export interface RawCommandExecutionV2 {
|
|
|
133
140
|
execution_status: "completed" | "blocked_external" | "infrastructure_error" | "invalid_evidence";
|
|
134
141
|
exit_code: number;
|
|
135
142
|
observations: Record<string, unknown>;
|
|
143
|
+
evidence_records: EvidenceCapabilityRecordV2[];
|
|
136
144
|
stdout_sha256: string;
|
|
137
145
|
stderr_sha256: string;
|
|
138
146
|
attempts: number;
|
|
@@ -148,6 +156,7 @@ export interface CheckExecutionResultV2 {
|
|
|
148
156
|
execution_identity: string;
|
|
149
157
|
assertion_results: AssertionResultV2[];
|
|
150
158
|
observations: Record<string, unknown>;
|
|
159
|
+
evidence_records: EvidenceCapabilityRecordV2[];
|
|
151
160
|
artifact_hashes: Record<string, string>;
|
|
152
161
|
claim_proofs: ClaimProofV2[];
|
|
153
162
|
findings: LongTaskFindingV2[];
|
|
@@ -185,12 +194,16 @@ export interface TargetedVerificationResultV2 {
|
|
|
185
194
|
completed_at: string;
|
|
186
195
|
}
|
|
187
196
|
export type OutcomeStatusV2 = "unverified" | "progress_passing" | "progress_failing" | "progress_stale" | "blocked_external";
|
|
197
|
+
export type StageStatusV2 = "locked" | "ready" | "unverified" | "progress_passing" | "progress_failing" | "progress_stale" | "blocked_external";
|
|
188
198
|
export interface FinalReceiptV2 {
|
|
189
199
|
schema_version: "long-task-final-receipt-v2";
|
|
190
200
|
receipt_sha256: string;
|
|
191
201
|
authority_scope: "audit_only";
|
|
192
202
|
reusable_for_acceptance: false;
|
|
193
203
|
workflow_status: "machine_accepted" | "machine_accepted_external_pending" | "needs_work" | "blocked_external";
|
|
204
|
+
target_profile: DeliveryContractV2["task"]["target_profile"];
|
|
205
|
+
target_state: DeliveryContractV2["task"]["target_profile"]["required_state"] | "not_accepted" | "blocked_external";
|
|
206
|
+
stage_results: Record<string, "passed" | "failed" | "blocked_external" | "blocked_dependency">;
|
|
194
207
|
compiled_identity: string;
|
|
195
208
|
contract_sha256: string;
|
|
196
209
|
snapshot_sha256: string;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface KeyedStatementV2 {
|
|
2
|
+
key: string;
|
|
3
|
+
statement: string;
|
|
4
|
+
}
|
|
5
|
+
export type ExternalConfirmationKindV2 = "functional_prerequisite" | "field_validation" | "production_release_gate" | "commercial_activation" | "expert_authority";
|
|
6
|
+
export type TargetCompletionStateV2 = "implementation_complete" | "target_profile_usable" | "production_release_ready";
|
|
7
|
+
export type ExecutionTargetRuntimeFamilyV2 = "browser" | "native" | "desktop" | "service" | "process" | "external";
|
|
8
|
+
export interface TargetProfileV2 {
|
|
9
|
+
key: string;
|
|
10
|
+
description: string;
|
|
11
|
+
required_state: TargetCompletionStateV2;
|
|
12
|
+
required_target_refs: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface ExecutionTargetV2 {
|
|
15
|
+
key: string;
|
|
16
|
+
description: string;
|
|
17
|
+
role: "product" | "support" | "observer";
|
|
18
|
+
runtime_family: ExecutionTargetRuntimeFamilyV2;
|
|
19
|
+
root_entrypoint: string;
|
|
20
|
+
}
|
|
21
|
+
export interface DeliveryStageV2 {
|
|
22
|
+
key: string;
|
|
23
|
+
title: string;
|
|
24
|
+
depends_on: string[];
|
|
25
|
+
gate_outcome: string;
|
|
26
|
+
}
|
|
27
|
+
export type DeliveryJourneyRoleV2 = "success" | "degradation" | "recovery" | "stage_gate" | "conformance";
|
|
28
|
+
export type EvidenceCapabilityV2 = "presence" | "interaction_trace" | "state_delta" | "cross_surface_consistency" | "durable_readback" | "boundary_invocation" | "external_side_effect" | "failure_injection" | "visual_render" | "target_runtime" | "input_variation";
|
|
29
|
+
export interface CheckExecutionTargetV2 {
|
|
30
|
+
target_ref: string;
|
|
31
|
+
entrypoint: "root" | "internal";
|
|
32
|
+
}
|
|
33
|
+
export interface DeliveryScenarioV2 {
|
|
34
|
+
given: KeyedStatementV2[];
|
|
35
|
+
when: KeyedStatementV2[];
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export function semanticDriftMigrationFields(value) {
|
|
2
|
+
const root = row(value);
|
|
3
|
+
if (!root || root.schema_version !== "long-task-delivery-v2")
|
|
4
|
+
return [];
|
|
5
|
+
const missing = [];
|
|
6
|
+
required(root, "$", ["stages"], missing);
|
|
7
|
+
const task = row(root.task);
|
|
8
|
+
if (task) {
|
|
9
|
+
required(task, "task", ["target_profile", "execution_targets"], missing);
|
|
10
|
+
const profile = row(task.target_profile);
|
|
11
|
+
if (profile)
|
|
12
|
+
required(profile, "task.target_profile", ["required_target_refs"], missing);
|
|
13
|
+
}
|
|
14
|
+
for (const [index, outcome] of rows(root.outcomes).entries())
|
|
15
|
+
collectOutcome(outcome, `outcomes[${index}]`, missing);
|
|
16
|
+
const global = row(root.global);
|
|
17
|
+
const globalAcceptance = row(global?.acceptance);
|
|
18
|
+
collectChecks(globalAcceptance?.checks, "global.acceptance.checks", missing);
|
|
19
|
+
for (const [index, confirmation] of rows(globalAcceptance?.external_confirmations).entries())
|
|
20
|
+
required(confirmation, `global.acceptance.external_confirmations[${index}]`, ["kind", "impact_claims", "blocks_target"], missing);
|
|
21
|
+
return [...new Set(missing)].sort();
|
|
22
|
+
}
|
|
23
|
+
export function semanticDriftOutcomeMigrationFields(value, label) {
|
|
24
|
+
const missing = [];
|
|
25
|
+
const outcome = row(value);
|
|
26
|
+
if (outcome)
|
|
27
|
+
collectOutcome(outcome, label, missing);
|
|
28
|
+
return [...new Set(missing)].sort();
|
|
29
|
+
}
|
|
30
|
+
export function assertNoSemanticDriftMigration(fields) {
|
|
31
|
+
if (!fields.length)
|
|
32
|
+
return;
|
|
33
|
+
const detail = fields.slice(0, 24).join(",");
|
|
34
|
+
const suffix = fields.length > 24 ? `,+${fields.length - 24}-more` : "";
|
|
35
|
+
throw new Error(`long_task_delivery_v2_semantic_drift_migration_required:${detail}${suffix}`);
|
|
36
|
+
}
|
|
37
|
+
function collectOutcome(outcome, label, missing) {
|
|
38
|
+
required(outcome, label, ["stage"], missing);
|
|
39
|
+
const product = row(outcome.product);
|
|
40
|
+
if (product)
|
|
41
|
+
required(product, `${label}.product`, ["success_path_required", "degradation_path_required"], missing);
|
|
42
|
+
const acceptance = row(outcome.acceptance);
|
|
43
|
+
collectChecks(acceptance?.checks, `${label}.acceptance.checks`, missing);
|
|
44
|
+
}
|
|
45
|
+
function collectChecks(value, label, missing) {
|
|
46
|
+
for (const [index, check] of rows(value).entries()) {
|
|
47
|
+
const checkLabel = `${label}[${index}]`;
|
|
48
|
+
required(check, checkLabel, ["journey_roles", "execution_target", "scenario"], missing);
|
|
49
|
+
for (const polarity of ["positive_assertions", "negative_assertions"])
|
|
50
|
+
for (const [assertionIndex, assertion] of rows(check[polarity]).entries())
|
|
51
|
+
required(assertion, `${checkLabel}.${polarity}[${assertionIndex}]`, ["evidence_capabilities"], missing);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function required(value, label, keys, missing) {
|
|
55
|
+
for (const key of keys)
|
|
56
|
+
if (!Object.hasOwn(value, key))
|
|
57
|
+
missing.push(`${label}.${key}`);
|
|
58
|
+
}
|
|
59
|
+
function rows(value) {
|
|
60
|
+
return Array.isArray(value)
|
|
61
|
+
? value.map(row).filter((item) => item !== null)
|
|
62
|
+
: [];
|
|
63
|
+
}
|
|
64
|
+
function row(value) {
|
|
65
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
66
|
+
? value
|
|
67
|
+
: null;
|
|
68
|
+
}
|
|
@@ -3,6 +3,7 @@ export declare function object(value: unknown, label: string, required: string[]
|
|
|
3
3
|
export declare function array(value: unknown, label: string): unknown[];
|
|
4
4
|
export declare function string(value: unknown, label: string): string;
|
|
5
5
|
export declare function text(value: unknown, label: string): string;
|
|
6
|
+
export declare function boolean(value: unknown, label: string): boolean;
|
|
6
7
|
export declare function strings(value: unknown, label: string): string[];
|
|
7
8
|
export declare function repositoryFile(value: unknown, label: string): string;
|
|
8
9
|
export declare function repositoryFiles(value: unknown, label: string): string[];
|
|
@@ -14,4 +15,6 @@ export declare function literal<T extends string>(value: unknown, allowed: reado
|
|
|
14
15
|
export declare function key(value: unknown, label: string): string;
|
|
15
16
|
export declare function nullable<T>(value: unknown, parser: (value: unknown) => T): T | null;
|
|
16
17
|
export declare const PROOF_SURFACES: readonly ["ui_browser", "runtime_behavior", "api_contract", "data_state", "security_boundary", "population_coverage", "implementation_structure"];
|
|
18
|
+
export declare const JOURNEY_ROLES: readonly ["success", "degradation", "recovery", "stage_gate", "conformance"];
|
|
19
|
+
export declare const EVIDENCE_CAPABILITIES: readonly ["presence", "interaction_trace", "state_delta", "cross_surface_consistency", "durable_readback", "boundary_invocation", "external_side_effect", "failure_injection", "visual_render", "target_runtime", "input_variation"];
|
|
17
20
|
export declare function fail(label: string, message: string): never;
|
|
@@ -27,6 +27,11 @@ export function text(value, label) {
|
|
|
27
27
|
fail(label, "must be a string");
|
|
28
28
|
return value;
|
|
29
29
|
}
|
|
30
|
+
export function boolean(value, label) {
|
|
31
|
+
if (typeof value !== "boolean")
|
|
32
|
+
fail(label, "must be a boolean");
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
30
35
|
export function strings(value, label) {
|
|
31
36
|
return array(value, label).map((item, index) => string(item, `${label}[${index}]`));
|
|
32
37
|
}
|
|
@@ -76,6 +81,26 @@ export const PROOF_SURFACES = [
|
|
|
76
81
|
"population_coverage",
|
|
77
82
|
"implementation_structure",
|
|
78
83
|
];
|
|
84
|
+
export const JOURNEY_ROLES = [
|
|
85
|
+
"success",
|
|
86
|
+
"degradation",
|
|
87
|
+
"recovery",
|
|
88
|
+
"stage_gate",
|
|
89
|
+
"conformance",
|
|
90
|
+
];
|
|
91
|
+
export const EVIDENCE_CAPABILITIES = [
|
|
92
|
+
"presence",
|
|
93
|
+
"interaction_trace",
|
|
94
|
+
"state_delta",
|
|
95
|
+
"cross_surface_consistency",
|
|
96
|
+
"durable_readback",
|
|
97
|
+
"boundary_invocation",
|
|
98
|
+
"external_side_effect",
|
|
99
|
+
"failure_injection",
|
|
100
|
+
"visual_render",
|
|
101
|
+
"target_runtime",
|
|
102
|
+
"input_variation",
|
|
103
|
+
];
|
|
79
104
|
export function fail(label, message) {
|
|
80
105
|
throw new Error(`delivery_contract_invalid:${label}:${message}`);
|
|
81
106
|
}
|