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
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export function validateDeliveryStages(contract, report) {
|
|
2
|
+
unique(contract.stages.map((stage) => stage.key), "stage_key_duplicate", report);
|
|
3
|
+
const stages = new Map(contract.stages.map((stage) => [stage.key, stage]));
|
|
4
|
+
const outcomes = new Map(contract.outcomes.map((outcome) => [outcome.key, outcome]));
|
|
5
|
+
for (const stage of contract.stages) {
|
|
6
|
+
for (const dependency of stage.depends_on) {
|
|
7
|
+
if (dependency === stage.key)
|
|
8
|
+
issue(report, "stage_dependency_self", stage.key);
|
|
9
|
+
else if (!stages.has(dependency))
|
|
10
|
+
issue(report, "stage_dependency_unknown", `${stage.key}:${dependency}`);
|
|
11
|
+
}
|
|
12
|
+
const gate = outcomes.get(stage.gate_outcome);
|
|
13
|
+
if (!gate)
|
|
14
|
+
issue(report, "stage_gate_outcome_unknown", `${stage.key}:${stage.gate_outcome}`);
|
|
15
|
+
else if (gate.stage !== stage.key)
|
|
16
|
+
issue(report, "stage_gate_outcome_mismatch", `${stage.key}:${gate.key}`);
|
|
17
|
+
}
|
|
18
|
+
for (const outcome of contract.outcomes)
|
|
19
|
+
if (!stages.has(outcome.stage))
|
|
20
|
+
issue(report, "outcome_stage_unknown", `${outcome.key}:${outcome.stage}`);
|
|
21
|
+
validateStageCycles(contract, report);
|
|
22
|
+
validateStageGateClosure(contract, report);
|
|
23
|
+
}
|
|
24
|
+
function validateStageCycles(contract, report) {
|
|
25
|
+
const stages = new Map(contract.stages.map((stage) => [stage.key, stage]));
|
|
26
|
+
const visiting = new Set();
|
|
27
|
+
const visited = new Set();
|
|
28
|
+
const visit = (key) => {
|
|
29
|
+
if (visiting.has(key)) {
|
|
30
|
+
issue(report, "stage_dependency_cycle", key);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (visited.has(key))
|
|
34
|
+
return;
|
|
35
|
+
visiting.add(key);
|
|
36
|
+
for (const dependency of stages.get(key)?.depends_on ?? [])
|
|
37
|
+
if (stages.has(dependency))
|
|
38
|
+
visit(dependency);
|
|
39
|
+
visiting.delete(key);
|
|
40
|
+
visited.add(key);
|
|
41
|
+
};
|
|
42
|
+
for (const stage of stages.keys())
|
|
43
|
+
visit(stage);
|
|
44
|
+
}
|
|
45
|
+
function validateStageGateClosure(contract, report) {
|
|
46
|
+
const outcomes = new Map(contract.outcomes.map((outcome) => [outcome.key, outcome]));
|
|
47
|
+
for (const stage of contract.stages) {
|
|
48
|
+
const gate = outcomes.get(stage.gate_outcome);
|
|
49
|
+
if (!gate || gate.stage !== stage.key)
|
|
50
|
+
continue;
|
|
51
|
+
for (const outcome of contract.outcomes.filter((candidate) => candidate.stage === stage.key && candidate.key !== gate.key))
|
|
52
|
+
if (!outcomeDependsOn(gate.key, outcome.key, outcomes))
|
|
53
|
+
issue(report, "stage_gate_missing_outcome_dependency", `${stage.key}:${gate.key}:${outcome.key}`);
|
|
54
|
+
for (const dependency of stage.depends_on) {
|
|
55
|
+
const dependencyGate = contract.stages.find((candidate) => candidate.key === dependency)?.gate_outcome;
|
|
56
|
+
if (!dependencyGate)
|
|
57
|
+
continue;
|
|
58
|
+
for (const outcome of contract.outcomes.filter((candidate) => candidate.stage === stage.key))
|
|
59
|
+
if (!outcomeDependsOn(outcome.key, dependencyGate, outcomes))
|
|
60
|
+
issue(report, "stage_outcome_missing_gate_dependency", `${stage.key}:${outcome.key}:${dependencyGate}`);
|
|
61
|
+
}
|
|
62
|
+
const stageGateChecks = gate.acceptance.checks.filter((check) => check.journey_roles.includes("stage_gate"));
|
|
63
|
+
if (!stageGateChecks.length)
|
|
64
|
+
issue(report, "stage_gate_check_required", `${stage.key}:${gate.key}`);
|
|
65
|
+
else if (!stageGateChecks.some((check) => provesGate(check, gate)))
|
|
66
|
+
issue(report, "stage_gate_target_runtime_result_required", `${stage.key}:${gate.key}`);
|
|
67
|
+
for (const targetRef of contract.task.target_profile.required_target_refs)
|
|
68
|
+
if (!stageGateChecks.some((check) => check.execution_target.target_ref === targetRef &&
|
|
69
|
+
check.execution_target.entrypoint === "root" &&
|
|
70
|
+
provesGate(check, gate)))
|
|
71
|
+
issue(report, "stage_gate_required_target_proof_missing", `${stage.key}:${targetRef}`);
|
|
72
|
+
const stageOutcomes = contract.outcomes.filter((candidate) => candidate.stage === stage.key);
|
|
73
|
+
if (stageOutcomes.length > 1 &&
|
|
74
|
+
!stageGateChecks.some((check) => [...check.positive_assertions, ...check.negative_assertions].some((assertion) => assertion.evidence_capabilities.includes("cross_surface_consistency"))))
|
|
75
|
+
issue(report, "stage_gate_cross_surface_consistency_required", `${stage.key}:${stageOutcomes.map((outcome) => outcome.key).join(",")}`);
|
|
76
|
+
}
|
|
77
|
+
for (const outcome of contract.outcomes)
|
|
78
|
+
for (const check of outcome.acceptance.checks)
|
|
79
|
+
if (check.journey_roles.includes("stage_gate") &&
|
|
80
|
+
!contract.stages.some((stage) => stage.gate_outcome === outcome.key))
|
|
81
|
+
issue(report, "stage_gate_role_on_non_gate", `${outcome.key}:${check.key}`);
|
|
82
|
+
}
|
|
83
|
+
function provesGate(check, gate) {
|
|
84
|
+
const assertions = [
|
|
85
|
+
...check.positive_assertions,
|
|
86
|
+
...check.negative_assertions,
|
|
87
|
+
];
|
|
88
|
+
const provesResult = assertions.some((assertion) => assertion.claims.includes("result")) ||
|
|
89
|
+
(gate.acceptance.population?.check_key === check.key &&
|
|
90
|
+
gate.acceptance.population.claims.includes("result"));
|
|
91
|
+
return (provesResult &&
|
|
92
|
+
assertions.some((assertion) => assertion.evidence_capabilities.includes("target_runtime")));
|
|
93
|
+
}
|
|
94
|
+
function outcomeDependsOn(start, target, outcomes) {
|
|
95
|
+
const seen = new Set();
|
|
96
|
+
const visit = (key) => {
|
|
97
|
+
if (key === target)
|
|
98
|
+
return true;
|
|
99
|
+
if (seen.has(key))
|
|
100
|
+
return false;
|
|
101
|
+
seen.add(key);
|
|
102
|
+
return (outcomes.get(key)?.depends_on ?? []).some(visit);
|
|
103
|
+
};
|
|
104
|
+
return visit(start);
|
|
105
|
+
}
|
|
106
|
+
function unique(values, code, report) {
|
|
107
|
+
const seen = new Set();
|
|
108
|
+
for (const value of values) {
|
|
109
|
+
if (seen.has(value))
|
|
110
|
+
issue(report, code, value);
|
|
111
|
+
seen.add(value);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function issue(report, code, detail) {
|
|
115
|
+
const message = `delivery_contract_invalid:${code}:${detail}`;
|
|
116
|
+
if (report)
|
|
117
|
+
report(message);
|
|
118
|
+
else
|
|
119
|
+
throw new Error(message);
|
|
120
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CompiledDeliveryContractV2, FinalReceiptV2, LongTaskFindingV2, OutcomeStatusV2, ProgressRecordV2, WorkspaceManifestV2 } from "./long-task-delivery-types.js";
|
|
1
|
+
import type { CompiledDeliveryContractV2, FinalReceiptV2, LongTaskFindingV2, OutcomeStatusV2, ProgressRecordV2, StageStatusV2, WorkspaceManifestV2 } from "./long-task-delivery-types.js";
|
|
2
2
|
interface StatusProjectionInputV2 {
|
|
3
3
|
compiled: CompiledDeliveryContractV2;
|
|
4
4
|
manifest: WorkspaceManifestV2;
|
|
@@ -11,7 +11,10 @@ export type AuditGateStatusV2 = "no_final_gate" | "last_gate_passed" | "last_gat
|
|
|
11
11
|
export declare function projectDeliveryStatus(input: StatusProjectionInputV2): {
|
|
12
12
|
finalResult: AuditGateStatusV2;
|
|
13
13
|
finalWorkflowStatus: FinalReceiptV2["workflow_status"] | null;
|
|
14
|
+
targetState: FinalReceiptV2["target_state"];
|
|
14
15
|
outcomes: Record<string, OutcomeStatusV2>;
|
|
16
|
+
stages: Record<string, StageStatusV2>;
|
|
17
|
+
readyStages: string[];
|
|
15
18
|
readyOutcomes: string[];
|
|
16
19
|
needsReverify: string[];
|
|
17
20
|
progressPassing: string[];
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { progressRecordFresh } from "./long-task-progress.js";
|
|
2
2
|
export function projectDeliveryStatus(input) {
|
|
3
3
|
const outcomes = projectOutcomes(input);
|
|
4
|
+
const stages = projectStages(input.compiled, outcomes);
|
|
4
5
|
return {
|
|
5
6
|
finalResult: projectFinalResult(input),
|
|
6
7
|
finalWorkflowStatus: receiptFresh(input)
|
|
7
8
|
? input.receipt.workflow_status
|
|
8
9
|
: null,
|
|
10
|
+
targetState: receiptFresh(input)
|
|
11
|
+
? (input.receipt.target_state ?? legacyTargetState(input.receipt))
|
|
12
|
+
: "not_accepted",
|
|
9
13
|
outcomes,
|
|
10
|
-
|
|
14
|
+
stages,
|
|
15
|
+
readyStages: Object.entries(stages)
|
|
16
|
+
.filter(([, status]) => status === "ready")
|
|
17
|
+
.map(([key]) => key),
|
|
18
|
+
readyOutcomes: readyOutcomes(input.compiled, outcomes, stages),
|
|
11
19
|
needsReverify: Object.entries(outcomes)
|
|
12
20
|
.filter(([, status]) => ["unverified", "progress_stale", "progress_failing"].includes(status))
|
|
13
21
|
.map(([key]) => key),
|
|
@@ -88,12 +96,64 @@ function projectFindings(input) {
|
|
|
88
96
|
}
|
|
89
97
|
return findings;
|
|
90
98
|
}
|
|
91
|
-
function readyOutcomes(compiled, outcomes) {
|
|
99
|
+
function readyOutcomes(compiled, outcomes, stages) {
|
|
92
100
|
return compiled.outcomes
|
|
93
|
-
.filter((outcome) =>
|
|
101
|
+
.filter((outcome) => stages[outcome.stage] !== "locked" &&
|
|
102
|
+
stages[outcome.stage] !== "progress_passing" &&
|
|
103
|
+
outcomes[outcome.key] !== "progress_passing" &&
|
|
94
104
|
outcome.depends_on.every((dependency) => outcomes[dependency] === "progress_passing"))
|
|
95
105
|
.map((outcome) => outcome.key);
|
|
96
106
|
}
|
|
107
|
+
function legacyTargetState(receipt) {
|
|
108
|
+
if (receipt.workflow_status === "machine_accepted" ||
|
|
109
|
+
receipt.workflow_status === "machine_accepted_external_pending")
|
|
110
|
+
return receipt.target_profile.required_state;
|
|
111
|
+
return receipt.workflow_status === "blocked_external"
|
|
112
|
+
? "blocked_external"
|
|
113
|
+
: "not_accepted";
|
|
114
|
+
}
|
|
115
|
+
function projectStages(compiled, outcomes) {
|
|
116
|
+
const result = {};
|
|
117
|
+
const remaining = new Set(compiled.stages.map((stage) => stage.key));
|
|
118
|
+
while (remaining.size) {
|
|
119
|
+
let advanced = false;
|
|
120
|
+
for (const stage of compiled.stages) {
|
|
121
|
+
if (!remaining.has(stage.key))
|
|
122
|
+
continue;
|
|
123
|
+
if (stage.depends_on.some((dependency) => remaining.has(dependency)))
|
|
124
|
+
continue;
|
|
125
|
+
const prerequisitesPassing = stage.depends_on.every((dependency) => result[dependency] === "progress_passing");
|
|
126
|
+
if (!prerequisitesPassing)
|
|
127
|
+
result[stage.key] = "locked";
|
|
128
|
+
else {
|
|
129
|
+
const owned = compiled.outcomes
|
|
130
|
+
.filter((outcome) => outcome.stage === stage.key)
|
|
131
|
+
.map((outcome) => outcomes[outcome.key]);
|
|
132
|
+
const gate = outcomes[stage.gate_outcome];
|
|
133
|
+
result[stage.key] = owned.includes("progress_failing")
|
|
134
|
+
? "progress_failing"
|
|
135
|
+
: owned.includes("blocked_external")
|
|
136
|
+
? "blocked_external"
|
|
137
|
+
: owned.includes("progress_stale")
|
|
138
|
+
? "progress_stale"
|
|
139
|
+
: gate === "progress_passing" &&
|
|
140
|
+
owned.every((status) => status === "progress_passing")
|
|
141
|
+
? "progress_passing"
|
|
142
|
+
: owned.every((status) => status === "unverified")
|
|
143
|
+
? "ready"
|
|
144
|
+
: "unverified";
|
|
145
|
+
}
|
|
146
|
+
remaining.delete(stage.key);
|
|
147
|
+
advanced = true;
|
|
148
|
+
}
|
|
149
|
+
if (!advanced) {
|
|
150
|
+
for (const stage of remaining)
|
|
151
|
+
result[stage] = "locked";
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
97
157
|
function projectFinalResult(input) {
|
|
98
158
|
if (!input.receipt)
|
|
99
159
|
return "no_final_gate";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AuthorityRevisionDecisionV2 } from "./long-task-authority-revision-types.js";
|
|
2
|
-
import type { ExternalConfirmationV2, FinalReceiptV2, LongTaskFindingV2, OutcomeStatusV2 } from "./long-task-delivery-types.js";
|
|
2
|
+
import type { ExternalConfirmationV2, FinalReceiptV2, LongTaskFindingV2, OutcomeStatusV2, StageStatusV2, TargetProfileV2 } from "./long-task-delivery-types.js";
|
|
3
3
|
import { type AuditGateStatusV2 } from "./long-task-status-projection.js";
|
|
4
4
|
export interface DeliveryStatusV2 {
|
|
5
5
|
schema_version: "long-task-status-v2";
|
|
@@ -13,7 +13,11 @@ export interface DeliveryStatusV2 {
|
|
|
13
13
|
final_result: AuditGateStatusV2;
|
|
14
14
|
final_workflow_status: FinalReceiptV2["workflow_status"] | null;
|
|
15
15
|
external_confirmations: ExternalConfirmationV2[];
|
|
16
|
+
target_profile: TargetProfileV2;
|
|
17
|
+
target_state: FinalReceiptV2["target_state"];
|
|
16
18
|
outcomes: Record<string, OutcomeStatusV2>;
|
|
19
|
+
stages: Record<string, StageStatusV2>;
|
|
20
|
+
ready_stages: string[];
|
|
17
21
|
ready_outcomes: string[];
|
|
18
22
|
ready_for_implementation: string[];
|
|
19
23
|
needs_reverify: string[];
|
|
@@ -31,6 +35,9 @@ export interface StopCheckDeliveryResultV2 {
|
|
|
31
35
|
reason: string;
|
|
32
36
|
workflow_status?: FinalReceiptV2["workflow_status"];
|
|
33
37
|
external_confirmations?: ExternalConfirmationV2[];
|
|
38
|
+
target_profile?: TargetProfileV2;
|
|
39
|
+
target_state?: FinalReceiptV2["target_state"];
|
|
40
|
+
stage_results?: FinalReceiptV2["stage_results"];
|
|
34
41
|
acceptance_scope?: "declared_machine_authority";
|
|
35
42
|
native_goal_effect?: "none";
|
|
36
43
|
message?: string;
|
|
@@ -39,6 +46,9 @@ export interface CloseDeliveryResultV2 {
|
|
|
39
46
|
status: "closed";
|
|
40
47
|
workflow_status: Extract<FinalReceiptV2["workflow_status"], "machine_accepted" | "machine_accepted_external_pending">;
|
|
41
48
|
external_confirmations: ExternalConfirmationV2[];
|
|
49
|
+
target_profile: TargetProfileV2;
|
|
50
|
+
target_state: FinalReceiptV2["target_state"];
|
|
51
|
+
stage_results: FinalReceiptV2["stage_results"];
|
|
42
52
|
acceptance_scope: "declared_machine_authority";
|
|
43
53
|
closed_scope: "machine_authority";
|
|
44
54
|
native_goal_effect: "none";
|
|
@@ -53,7 +53,11 @@ async function readDeliveryStatusForAuthority(active) {
|
|
|
53
53
|
final_result: projection.finalResult,
|
|
54
54
|
final_workflow_status: projection.finalWorkflowStatus,
|
|
55
55
|
external_confirmations: compiled.global.acceptance.external_confirmations,
|
|
56
|
+
target_profile: compiled.task.target_profile,
|
|
57
|
+
target_state: projection.targetState,
|
|
56
58
|
outcomes: projection.outcomes,
|
|
59
|
+
stages: projection.stages,
|
|
60
|
+
ready_stages: projection.readyStages,
|
|
57
61
|
ready_outcomes: projection.readyOutcomes,
|
|
58
62
|
ready_for_implementation: projection.readyOutcomes,
|
|
59
63
|
needs_reverify: projection.needsReverify,
|
|
@@ -89,6 +93,8 @@ export async function resumeDeliveryTask(workdir) {
|
|
|
89
93
|
id: compiled.task.id,
|
|
90
94
|
title: compiled.task.title,
|
|
91
95
|
goal: compiled.task.goal,
|
|
96
|
+
target_profile: compiled.task.target_profile,
|
|
97
|
+
execution_targets: compiled.task.execution_targets,
|
|
92
98
|
},
|
|
93
99
|
contract_identity: compiled.compiled_identity,
|
|
94
100
|
authority_revision: compiled.authority_revision,
|
|
@@ -101,7 +107,11 @@ export async function resumeDeliveryTask(workdir) {
|
|
|
101
107
|
last_gate: status.final_result,
|
|
102
108
|
final_workflow_status: status.final_workflow_status,
|
|
103
109
|
external_confirmations: status.external_confirmations,
|
|
110
|
+
target_profile: status.target_profile,
|
|
111
|
+
target_state: status.target_state,
|
|
104
112
|
outcomes: status.outcomes,
|
|
113
|
+
stages: status.stages,
|
|
114
|
+
ready_stages: status.ready_stages,
|
|
105
115
|
ready_outcomes: status.ready_outcomes,
|
|
106
116
|
ready_for_implementation: status.ready_for_implementation,
|
|
107
117
|
needs_reverify: status.needs_reverify,
|
|
@@ -216,6 +226,9 @@ export async function stopCheckDeliveryTask(workdirInput, messageText = "") {
|
|
|
216
226
|
reason: "active_authority_changed_after_final_gate",
|
|
217
227
|
workflow_status: result.workflow_status,
|
|
218
228
|
external_confirmations: result.external_confirmations,
|
|
229
|
+
target_profile: result.target_profile,
|
|
230
|
+
target_state: result.target_state,
|
|
231
|
+
stage_results: result.stage_results,
|
|
219
232
|
message: "Active Authority changed after the Live Final Gate; the accepted identity was not cleared.",
|
|
220
233
|
};
|
|
221
234
|
throw error;
|
|
@@ -225,9 +238,12 @@ export async function stopCheckDeliveryTask(workdirInput, messageText = "") {
|
|
|
225
238
|
reason: result.workflow_status,
|
|
226
239
|
workflow_status: result.workflow_status,
|
|
227
240
|
external_confirmations: result.external_confirmations,
|
|
241
|
+
target_profile: result.target_profile,
|
|
242
|
+
target_state: result.target_state,
|
|
243
|
+
stage_results: result.stage_results,
|
|
228
244
|
acceptance_scope: "declared_machine_authority",
|
|
229
245
|
native_goal_effect: "none",
|
|
230
|
-
message: acceptedScopeMessage(result.workflow_status, result.external_confirmations),
|
|
246
|
+
message: acceptedScopeMessage(result.workflow_status, result.external_confirmations, result.target_profile),
|
|
231
247
|
};
|
|
232
248
|
}
|
|
233
249
|
return {
|
|
@@ -235,6 +251,9 @@ export async function stopCheckDeliveryTask(workdirInput, messageText = "") {
|
|
|
235
251
|
reason: `live_final_gate_${result.workflow_status}`,
|
|
236
252
|
workflow_status: result.workflow_status,
|
|
237
253
|
external_confirmations: result.external_confirmations,
|
|
254
|
+
target_profile: result.target_profile,
|
|
255
|
+
target_state: result.target_state,
|
|
256
|
+
stage_results: result.stage_results,
|
|
238
257
|
message: messageText ||
|
|
239
258
|
result.findings.at(-1)?.next_action ||
|
|
240
259
|
"Repair the failing Check and rerun Stop.",
|
|
@@ -274,13 +293,16 @@ export async function closeDeliveryTask(workdir) {
|
|
|
274
293
|
status: "closed",
|
|
275
294
|
workflow_status: result.workflow_status,
|
|
276
295
|
external_confirmations: result.external_confirmations,
|
|
296
|
+
target_profile: result.target_profile,
|
|
297
|
+
target_state: result.target_state,
|
|
298
|
+
stage_results: result.stage_results,
|
|
277
299
|
acceptance_scope: "declared_machine_authority",
|
|
278
300
|
closed_scope: "machine_authority",
|
|
279
301
|
native_goal_effect: "none",
|
|
280
302
|
};
|
|
281
303
|
}
|
|
282
|
-
function acceptedScopeMessage(workflowStatus, confirmations) {
|
|
283
|
-
const scope =
|
|
304
|
+
function acceptedScopeMessage(workflowStatus, confirmations, targetProfile) {
|
|
305
|
+
const scope = `Declared machine Authority accepted for target ${targetProfile.key}:${targetProfile.required_state} and cleared. This result has no direct effect on the platform-native Goal; before completing it, confirm current Goal/user meaning is fully represented by accepted Source and no revision, blocker, or omitted requirement remains.`;
|
|
284
306
|
if (workflowStatus === "machine_accepted")
|
|
285
307
|
return scope;
|
|
286
308
|
const pending = confirmations
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CompiledClaimsV2 } from "./long-task-claims.js";
|
|
2
|
+
import type { DeliveryContractV2 } from "./long-task-delivery-types.js";
|
|
3
|
+
type Reporter = (message: string) => void;
|
|
4
|
+
export declare function validateExecutionTargets(contract: DeliveryContractV2, report?: Reporter): void;
|
|
5
|
+
export declare function validateExternalConfirmationImpacts(contract: DeliveryContractV2, claims: CompiledClaimsV2, report?: Reporter): void;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
export function validateExecutionTargets(contract, report) {
|
|
2
|
+
const targets = indexExecutionTargets(contract, report);
|
|
3
|
+
const requiredTargetRefs = contract.task.target_profile.required_target_refs;
|
|
4
|
+
validateRequiredTargetRefs(contract, targets, report);
|
|
5
|
+
for (const [outcomeKey, check] of allChecks(contract))
|
|
6
|
+
validateCheckTarget(outcomeKey, check, targets, report);
|
|
7
|
+
validateCriticalPathTargets(contract, requiredTargetRefs, report);
|
|
8
|
+
}
|
|
9
|
+
function indexExecutionTargets(contract, report) {
|
|
10
|
+
const targets = new Map();
|
|
11
|
+
for (const target of contract.task.execution_targets) {
|
|
12
|
+
if (targets.has(target.key))
|
|
13
|
+
issue(report, "execution_target_key_duplicate", target.key);
|
|
14
|
+
targets.set(target.key, target);
|
|
15
|
+
}
|
|
16
|
+
if (![...targets.values()].some((target) => target.role === "product"))
|
|
17
|
+
issue(report, "product_execution_target_required", contract.task.id);
|
|
18
|
+
return targets;
|
|
19
|
+
}
|
|
20
|
+
function validateRequiredTargetRefs(contract, targets, report) {
|
|
21
|
+
const required = contract.task.target_profile.required_target_refs;
|
|
22
|
+
if (!required.length)
|
|
23
|
+
issue(report, "target_profile_required_target_refs_required", contract.task.id);
|
|
24
|
+
if (new Set(required).size !== required.length)
|
|
25
|
+
issue(report, "target_profile_required_target_refs_duplicate", contract.task.id);
|
|
26
|
+
for (const targetRef of required) {
|
|
27
|
+
const target = targets.get(targetRef);
|
|
28
|
+
if (!target)
|
|
29
|
+
issue(report, "target_profile_required_target_ref_unknown", targetRef);
|
|
30
|
+
else if (target.role !== "product")
|
|
31
|
+
issue(report, "target_profile_required_target_must_be_product", targetRef);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function validateCheckTarget(outcomeKey, check, targets, report) {
|
|
35
|
+
const target = targets.get(check.execution_target.target_ref);
|
|
36
|
+
const label = `${outcomeKey ?? "GLOBAL"}:${check.key}`;
|
|
37
|
+
if (!target) {
|
|
38
|
+
issue(report, "check_execution_target_unknown", `${label}:${check.execution_target.target_ref}`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if ((check.runner.type === "playwright_test" ||
|
|
42
|
+
check.proof_surface === "ui_browser") &&
|
|
43
|
+
target.runtime_family !== "browser")
|
|
44
|
+
issue(report, "browser_check_target_mismatch", `${label}:${target.key}`);
|
|
45
|
+
if (provesTargetRuntime(check) &&
|
|
46
|
+
target.runtime_family === "browser" &&
|
|
47
|
+
check.runner.type !== "playwright_test")
|
|
48
|
+
issue(report, "browser_target_runtime_requires_playwright", `${label}:${target.key}`);
|
|
49
|
+
if (provesTargetRuntime(check) &&
|
|
50
|
+
["native", "desktop"].includes(target.runtime_family) &&
|
|
51
|
+
check.runner.type !== "project_binary")
|
|
52
|
+
issue(report, "native_target_runtime_requires_project_binary", `${label}:${target.key}`);
|
|
53
|
+
validateStageGateTarget(label, check, target, report);
|
|
54
|
+
validateConformanceTarget(outcomeKey, check, target, report);
|
|
55
|
+
}
|
|
56
|
+
function validateStageGateTarget(label, check, target, report) {
|
|
57
|
+
if (!check.journey_roles.includes("stage_gate"))
|
|
58
|
+
return;
|
|
59
|
+
if (target.role !== "product")
|
|
60
|
+
issue(report, "stage_gate_product_target_required", `${label}:${target.key}`);
|
|
61
|
+
if (check.execution_target.entrypoint !== "root")
|
|
62
|
+
issue(report, "stage_gate_root_entrypoint_required", label);
|
|
63
|
+
}
|
|
64
|
+
function validateConformanceTarget(outcomeKey, check, target, report) {
|
|
65
|
+
if (!check.journey_roles.includes("conformance"))
|
|
66
|
+
return;
|
|
67
|
+
if (outcomeKey !== null)
|
|
68
|
+
issue(report, "conformance_check_must_be_global", `${outcomeKey}:${check.key}`);
|
|
69
|
+
if (target.role !== "product" || check.execution_target.entrypoint !== "root")
|
|
70
|
+
issue(report, "conformance_root_product_target_required", check.key);
|
|
71
|
+
}
|
|
72
|
+
function validateCriticalPathTargets(contract, requiredTargetRefs, report) {
|
|
73
|
+
for (const outcomeKey of contract.risk.facts.critical_user_path) {
|
|
74
|
+
const outcome = contract.outcomes.find((candidate) => candidate.key === outcomeKey);
|
|
75
|
+
if (!outcome)
|
|
76
|
+
continue;
|
|
77
|
+
for (const targetRef of requiredTargetRefs)
|
|
78
|
+
if (!outcome.acceptance.checks.some((check) => provesSuccess(check, targetRef)))
|
|
79
|
+
issue(report, "critical_path_required_target_proof_missing", `${outcome.key}:${targetRef}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function provesSuccess(check, targetRef) {
|
|
83
|
+
return (check.execution_target.target_ref === targetRef &&
|
|
84
|
+
check.journey_roles.includes("success") &&
|
|
85
|
+
check.execution_target.entrypoint === "root" &&
|
|
86
|
+
provesTargetRuntime(check));
|
|
87
|
+
}
|
|
88
|
+
function provesTargetRuntime(check) {
|
|
89
|
+
return [...check.positive_assertions, ...check.negative_assertions].some((assertion) => assertion.evidence_capabilities.includes("target_runtime"));
|
|
90
|
+
}
|
|
91
|
+
export function validateExternalConfirmationImpacts(contract, claims, report) {
|
|
92
|
+
const claimRefs = new Set();
|
|
93
|
+
for (const [outcomeKey, values] of Object.entries(claims.by_outcome))
|
|
94
|
+
for (const claim of values)
|
|
95
|
+
claimRefs.add(`${outcomeKey}.${claim.local_key}`);
|
|
96
|
+
for (const claim of claims.by_global)
|
|
97
|
+
claimRefs.add(`GLOBAL.${claim.local_key}`);
|
|
98
|
+
for (const confirmation of contract.global.acceptance
|
|
99
|
+
.external_confirmations) {
|
|
100
|
+
if (!confirmation.impact_claims.length)
|
|
101
|
+
issue(report, "external_confirmation_impact_required", confirmation.key);
|
|
102
|
+
for (const claim of confirmation.impact_claims)
|
|
103
|
+
if (!claimRefs.has(claim))
|
|
104
|
+
issue(report, "external_confirmation_impact_unknown", `${confirmation.key}:${claim}`);
|
|
105
|
+
if (confirmation.kind === "functional_prerequisite" &&
|
|
106
|
+
!confirmation.blocks_target)
|
|
107
|
+
issue(report, "functional_prerequisite_must_block_target", confirmation.key);
|
|
108
|
+
if (confirmation.kind === "production_release_gate" &&
|
|
109
|
+
contract.task.target_profile.required_state ===
|
|
110
|
+
"production_release_ready" &&
|
|
111
|
+
!confirmation.blocks_target)
|
|
112
|
+
issue(report, "production_gate_must_block_release_target", confirmation.key);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function allChecks(contract) {
|
|
116
|
+
return [
|
|
117
|
+
...contract.global.acceptance.checks.map((check) => [null, check]),
|
|
118
|
+
...contract.outcomes.flatMap((outcome) => outcome.acceptance.checks.map((check) => [outcome.key, check])),
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
function issue(report, code, detail) {
|
|
122
|
+
const message = `delivery_contract_invalid:${code}:${detail}`;
|
|
123
|
+
if (report)
|
|
124
|
+
report(message);
|
|
125
|
+
else
|
|
126
|
+
throw new Error(message);
|
|
127
|
+
}
|
package/dist/lib/migrations.js
CHANGED
|
@@ -9,11 +9,22 @@ import { ensureDir, listFiles, pathExists, readText, writeTextIfChanged, } from
|
|
|
9
9
|
import { harnessConfigPath, harnessRoot } from "./harness-root.js";
|
|
10
10
|
import { legacySdlcHarnessMigration } from "./legacy-sdlc-migration.js";
|
|
11
11
|
import { parseYaml, stringifyYaml } from "./yaml.js";
|
|
12
|
+
import { semanticDriftMigrationFields } from "./long-task-semantic-drift-migration.js";
|
|
12
13
|
async function verifyNoop() {
|
|
13
14
|
return;
|
|
14
15
|
}
|
|
15
16
|
export const migrations = [
|
|
16
17
|
legacySdlcHarnessMigration,
|
|
18
|
+
{
|
|
19
|
+
id: "long-task-v2-semantic-drift-authority",
|
|
20
|
+
introducedIn: "0.7.2",
|
|
21
|
+
description: "Require explicit Stage, target profile/root runtime, journey scenario and evidence-capability authority in V2 Long-Task Contracts.",
|
|
22
|
+
scope: ".long-task/delivery-contract.yaml",
|
|
23
|
+
risk: "manual",
|
|
24
|
+
manualMessage: "These product and proof semantics cannot be inferred safely. Preserve Source, then explicitly author stages, vertical Outcome membership, target profile and execution targets, success/degradation requirements, Given/When scenarios, evidence capabilities and typed external-confirmation impact before recompiling. Historical Progress and Receipts remain audit-only and must not be imported as acceptance.",
|
|
25
|
+
detect: detectLongTaskSemanticDriftAuthority,
|
|
26
|
+
verify: verifyNoop,
|
|
27
|
+
},
|
|
17
28
|
{
|
|
18
29
|
id: "long-task-v1-retirement",
|
|
19
30
|
introducedIn: "0.6.0",
|
|
@@ -102,6 +113,24 @@ export const migrations = [
|
|
|
102
113
|
verify: verifyNoop,
|
|
103
114
|
},
|
|
104
115
|
];
|
|
116
|
+
async function detectLongTaskSemanticDriftAuthority(projectRoot, _root, migration) {
|
|
117
|
+
const contract = path.join(projectRoot, ".long-task", "delivery-contract.yaml");
|
|
118
|
+
if (!(await pathExists(contract)))
|
|
119
|
+
return [];
|
|
120
|
+
let decoded;
|
|
121
|
+
try {
|
|
122
|
+
decoded = parseYaml(await readText(contract));
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
const fields = semanticDriftMigrationFields(decoded);
|
|
128
|
+
if (!fields.length)
|
|
129
|
+
return [];
|
|
130
|
+
return [
|
|
131
|
+
item(migration, "manual_required", ".long-task/delivery-contract.yaml", `The active/draft V2 Contract predates semantic-drift authority and is missing: ${fields.slice(0, 12).join(", ")}${fields.length > 12 ? `, +${fields.length - 12} more` : ""}. Re-author these meanings from Source; do not synthesize them from old passing evidence.`),
|
|
132
|
+
];
|
|
133
|
+
}
|
|
105
134
|
async function detectLongTaskV1Retirement(projectRoot, root, migration) {
|
|
106
135
|
const items = [];
|
|
107
136
|
const hook = path.join(projectRoot, root, "hooks", "long-task-hook.mjs");
|