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.
Files changed (82) hide show
  1. package/README.md +7 -5
  2. package/assets/README.md +49 -14
  3. package/assets/README.zh-CN.md +26 -13
  4. package/assets/agents/AGENTS_CORE.md +5 -5
  5. package/assets/context_templates/global.md +1 -0
  6. package/assets/skills/context_development_engineer/SKILL.md +4 -1
  7. package/assets/skills/context_uiux_design/SKILL.md +19 -6
  8. package/assets/skills/long-task-workflow/SKILL.md +10 -9
  9. package/assets/skills/long-task-workflow/references/authority-lifecycle.md +7 -5
  10. package/assets/skills/long-task-workflow/references/contract-authoring.md +24 -6
  11. package/assets/skills/long-task-workflow/references/evidence-design.md +14 -1
  12. package/assets/skills/source-plan-authoring/SKILL.md +2 -2
  13. package/dist/commands/long-task-authoring.js +25 -0
  14. package/dist/commands/long-task.js +3 -0
  15. package/dist/lib/design-md.d.ts +3 -0
  16. package/dist/lib/design-md.js +48 -55
  17. package/dist/lib/doctor.js +8 -0
  18. package/dist/lib/long-task-activation-validation.js +13 -2
  19. package/dist/lib/long-task-authoring-authority-preview.js +1 -0
  20. package/dist/lib/long-task-authority-material-diff.js +26 -0
  21. package/dist/lib/long-task-authority-materials.d.ts +2 -2
  22. package/dist/lib/long-task-authority-materials.js +6 -0
  23. package/dist/lib/long-task-authority-policy.d.ts +12 -0
  24. package/dist/lib/long-task-authority-policy.js +9 -0
  25. package/dist/lib/long-task-authority-revision-analysis.d.ts +33 -0
  26. package/dist/lib/long-task-authority-revision-analysis.js +91 -0
  27. package/dist/lib/long-task-authority-revision.js +93 -158
  28. package/dist/lib/long-task-authority-types.d.ts +7 -0
  29. package/dist/lib/long-task-authority.js +32 -3
  30. package/dist/lib/long-task-check-evidence-decoder.d.ts +2 -2
  31. package/dist/lib/long-task-check-evidence-decoder.js +11 -1
  32. package/dist/lib/long-task-check-execution-policy.d.ts +3 -0
  33. package/dist/lib/long-task-check-execution-policy.js +5 -0
  34. package/dist/lib/long-task-check-runner.js +2 -0
  35. package/dist/lib/long-task-check-shape.js +33 -3
  36. package/dist/lib/long-task-conformance-policy.d.ts +5 -0
  37. package/dist/lib/long-task-conformance-policy.js +35 -0
  38. package/dist/lib/long-task-contract-types.d.ts +14 -4
  39. package/dist/lib/long-task-delivery-compiler.js +1 -0
  40. package/dist/lib/long-task-delivery-parser.js +5 -2
  41. package/dist/lib/long-task-delivery-types.d.ts +2 -0
  42. package/dist/lib/long-task-delivery-types.js +2 -0
  43. package/dist/lib/long-task-delivery-validation.js +11 -0
  44. package/dist/lib/long-task-evidence-capability-codec.d.ts +2 -0
  45. package/dist/lib/long-task-evidence-capability-codec.js +231 -0
  46. package/dist/lib/long-task-evidence-capability-policy.d.ts +8 -0
  47. package/dist/lib/long-task-evidence-capability-policy.js +147 -0
  48. package/dist/lib/long-task-evidence-capability-runtime.d.ts +2 -0
  49. package/dist/lib/long-task-evidence-capability-runtime.js +86 -0
  50. package/dist/lib/long-task-evidence-capability-types.d.ts +74 -0
  51. package/dist/lib/long-task-evidence-capability-types.js +1 -0
  52. package/dist/lib/long-task-evidence-findings.js +5 -1
  53. package/dist/lib/long-task-evidence-v2.js +38 -23
  54. package/dist/lib/long-task-final-v2.js +42 -1
  55. package/dist/lib/long-task-outcome-parser.js +14 -3
  56. package/dist/lib/long-task-playwright-evidence.d.ts +2 -1
  57. package/dist/lib/long-task-playwright-evidence.js +70 -2
  58. package/dist/lib/long-task-progress.js +4 -0
  59. package/dist/lib/long-task-root-shape.d.ts +1 -0
  60. package/dist/lib/long-task-root-shape.js +84 -3
  61. package/dist/lib/long-task-runner-environment.js +1 -1
  62. package/dist/lib/long-task-runner-freeze.d.ts +2 -2
  63. package/dist/lib/long-task-runner-freeze.js +3 -1
  64. package/dist/lib/long-task-runtime-types.d.ts +13 -0
  65. package/dist/lib/long-task-semantic-contract-types.d.ts +36 -0
  66. package/dist/lib/long-task-semantic-contract-types.js +1 -0
  67. package/dist/lib/long-task-semantic-drift-migration.d.ts +3 -0
  68. package/dist/lib/long-task-semantic-drift-migration.js +68 -0
  69. package/dist/lib/long-task-shape-primitives.d.ts +3 -0
  70. package/dist/lib/long-task-shape-primitives.js +25 -0
  71. package/dist/lib/long-task-stage-policy.d.ts +4 -0
  72. package/dist/lib/long-task-stage-policy.js +120 -0
  73. package/dist/lib/long-task-status-projection.d.ts +4 -1
  74. package/dist/lib/long-task-status-projection.js +63 -3
  75. package/dist/lib/long-task-status-v2.d.ts +11 -1
  76. package/dist/lib/long-task-status-v2.js +25 -3
  77. package/dist/lib/long-task-target-policy.d.ts +6 -0
  78. package/dist/lib/long-task-target-policy.js +127 -0
  79. package/dist/lib/migrations.js +29 -0
  80. package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +61 -7
  81. package/migrations/README.md +7 -0
  82. 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
- readyOutcomes: readyOutcomes(input.compiled, outcomes),
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) => outcomes[outcome.key] !== "progress_passing" &&
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 = "Declared machine Authority accepted 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.";
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
+ }
@@ -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");