project-tiny-context-harness 0.7.3 → 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 (75) hide show
  1. package/README.md +2 -2
  2. package/assets/README.md +44 -11
  3. package/assets/README.zh-CN.md +21 -10
  4. package/assets/agents/AGENTS_CORE.md +2 -2
  5. package/assets/skills/long-task-workflow/SKILL.md +9 -9
  6. package/assets/skills/long-task-workflow/references/authority-lifecycle.md +7 -5
  7. package/assets/skills/long-task-workflow/references/contract-authoring.md +17 -3
  8. package/assets/skills/long-task-workflow/references/evidence-design.md +12 -1
  9. package/dist/commands/long-task-authoring.js +25 -0
  10. package/dist/commands/long-task.js +3 -0
  11. package/dist/lib/long-task-activation-validation.js +13 -2
  12. package/dist/lib/long-task-authoring-authority-preview.js +1 -0
  13. package/dist/lib/long-task-authority-material-diff.js +26 -0
  14. package/dist/lib/long-task-authority-materials.d.ts +2 -2
  15. package/dist/lib/long-task-authority-materials.js +6 -0
  16. package/dist/lib/long-task-authority-policy.d.ts +12 -0
  17. package/dist/lib/long-task-authority-policy.js +9 -0
  18. package/dist/lib/long-task-authority-revision-analysis.d.ts +33 -0
  19. package/dist/lib/long-task-authority-revision-analysis.js +91 -0
  20. package/dist/lib/long-task-authority-revision.js +93 -158
  21. package/dist/lib/long-task-authority-types.d.ts +7 -0
  22. package/dist/lib/long-task-authority.js +32 -3
  23. package/dist/lib/long-task-check-evidence-decoder.d.ts +2 -2
  24. package/dist/lib/long-task-check-evidence-decoder.js +11 -1
  25. package/dist/lib/long-task-check-execution-policy.d.ts +3 -0
  26. package/dist/lib/long-task-check-execution-policy.js +5 -0
  27. package/dist/lib/long-task-check-runner.js +2 -0
  28. package/dist/lib/long-task-check-shape.js +33 -3
  29. package/dist/lib/long-task-conformance-policy.d.ts +5 -0
  30. package/dist/lib/long-task-conformance-policy.js +35 -0
  31. package/dist/lib/long-task-contract-types.d.ts +14 -4
  32. package/dist/lib/long-task-delivery-compiler.js +1 -0
  33. package/dist/lib/long-task-delivery-parser.js +5 -2
  34. package/dist/lib/long-task-delivery-types.d.ts +2 -0
  35. package/dist/lib/long-task-delivery-types.js +2 -0
  36. package/dist/lib/long-task-delivery-validation.js +11 -0
  37. package/dist/lib/long-task-evidence-capability-codec.d.ts +2 -0
  38. package/dist/lib/long-task-evidence-capability-codec.js +231 -0
  39. package/dist/lib/long-task-evidence-capability-policy.d.ts +8 -0
  40. package/dist/lib/long-task-evidence-capability-policy.js +147 -0
  41. package/dist/lib/long-task-evidence-capability-runtime.d.ts +2 -0
  42. package/dist/lib/long-task-evidence-capability-runtime.js +86 -0
  43. package/dist/lib/long-task-evidence-capability-types.d.ts +74 -0
  44. package/dist/lib/long-task-evidence-capability-types.js +1 -0
  45. package/dist/lib/long-task-evidence-findings.js +5 -1
  46. package/dist/lib/long-task-evidence-v2.js +38 -23
  47. package/dist/lib/long-task-final-v2.js +42 -1
  48. package/dist/lib/long-task-outcome-parser.js +14 -3
  49. package/dist/lib/long-task-playwright-evidence.d.ts +2 -1
  50. package/dist/lib/long-task-playwright-evidence.js +70 -2
  51. package/dist/lib/long-task-progress.js +4 -0
  52. package/dist/lib/long-task-root-shape.d.ts +1 -0
  53. package/dist/lib/long-task-root-shape.js +84 -3
  54. package/dist/lib/long-task-runner-environment.js +1 -1
  55. package/dist/lib/long-task-runner-freeze.d.ts +2 -2
  56. package/dist/lib/long-task-runner-freeze.js +3 -1
  57. package/dist/lib/long-task-runtime-types.d.ts +13 -0
  58. package/dist/lib/long-task-semantic-contract-types.d.ts +36 -0
  59. package/dist/lib/long-task-semantic-contract-types.js +1 -0
  60. package/dist/lib/long-task-semantic-drift-migration.d.ts +3 -0
  61. package/dist/lib/long-task-semantic-drift-migration.js +68 -0
  62. package/dist/lib/long-task-shape-primitives.d.ts +3 -0
  63. package/dist/lib/long-task-shape-primitives.js +25 -0
  64. package/dist/lib/long-task-stage-policy.d.ts +4 -0
  65. package/dist/lib/long-task-stage-policy.js +120 -0
  66. package/dist/lib/long-task-status-projection.d.ts +4 -1
  67. package/dist/lib/long-task-status-projection.js +63 -3
  68. package/dist/lib/long-task-status-v2.d.ts +11 -1
  69. package/dist/lib/long-task-status-v2.js +25 -3
  70. package/dist/lib/long-task-target-policy.d.ts +6 -0
  71. package/dist/lib/long-task-target-policy.js +127 -0
  72. package/dist/lib/migrations.js +29 -0
  73. package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +61 -7
  74. package/migrations/README.md +7 -0
  75. package/package.json +2 -2
@@ -1,5 +1,6 @@
1
1
  import type { LongTaskRiskFacts, RequestedRiskLevel } from "./long-task-risk-types.js";
2
2
  import type { CounterfactualControlV2, GlobalCounterfactualControlV2 } from "./long-task-counterfactual-types.js";
3
+ import type { CheckExecutionTargetV2, DeliveryJourneyRoleV2, DeliveryScenarioV2, DeliveryStageV2, EvidenceCapabilityV2, ExecutionTargetV2, ExternalConfirmationKindV2, KeyedStatementV2, TargetProfileV2 } from "./long-task-semantic-contract-types.js";
3
4
  export type SourceClaimDispositionV2 = {
4
5
  type: "claim";
5
6
  refs: string[];
@@ -28,10 +29,6 @@ export interface SourceClaimV2 {
28
29
  statement: string;
29
30
  disposition: SourceClaimDispositionV2;
30
31
  }
31
- export interface KeyedStatementV2 {
32
- key: string;
33
- statement: string;
34
- }
35
32
  export interface KeyedPathV2 {
36
33
  key: string;
37
34
  path: string;
@@ -40,6 +37,9 @@ export interface ExternalConfirmationV2 {
40
37
  key: string;
41
38
  description: string;
42
39
  owner: string;
40
+ kind: ExternalConfirmationKindV2;
41
+ impact_claims: string[];
42
+ blocks_target: boolean;
43
43
  }
44
44
  export type ProofSurface = "ui_browser" | "runtime_behavior" | "api_contract" | "data_state" | "security_boundary" | "population_coverage" | "implementation_structure";
45
45
  export type RunnerType = "package_script" | "project_binary" | "node_oracle" | "playwright_test";
@@ -51,6 +51,7 @@ interface DeliveryAssertionBaseV2 {
51
51
  criterion?: string;
52
52
  claims: string[];
53
53
  observation: string;
54
+ evidence_capabilities: EvidenceCapabilityV2[];
54
55
  }
55
56
  export type DeliveryAssertionV2 = (DeliveryAssertionBaseV2 & {
56
57
  operator: PresenceOrUnaryAssertionOperator;
@@ -86,6 +87,9 @@ export type EnvironmentRequirementV2 = {
86
87
  };
87
88
  export interface DeliveryCheckV2 {
88
89
  key: string;
90
+ journey_roles: DeliveryJourneyRoleV2[];
91
+ execution_target: CheckExecutionTargetV2;
92
+ scenario: DeliveryScenarioV2;
89
93
  proof_surface: ProofSurface;
90
94
  runner: DeliveryRunnerV2;
91
95
  verification_inputs: string[];
@@ -144,9 +148,12 @@ export interface PopulationRequirementV2 {
144
148
  export interface DeliveryOutcomeV2 {
145
149
  key: string;
146
150
  title: string;
151
+ stage: string;
147
152
  depends_on: string[];
148
153
  product: {
149
154
  observable_result: string;
155
+ success_path_required: boolean;
156
+ degradation_path_required: boolean;
150
157
  owner: DeliveryOwnerV2;
151
158
  requirements: DeliveryRequirementV2[];
152
159
  owner_surfaces: string[];
@@ -174,11 +181,14 @@ export interface DeliveryContractV2 {
174
181
  id: string;
175
182
  title: string;
176
183
  goal: string;
184
+ target_profile: TargetProfileV2;
185
+ execution_targets: ExecutionTargetV2[];
177
186
  source_paths: string[];
178
187
  context_refs: string[];
179
188
  context_snapshot_mode: "referenced" | "full";
180
189
  };
181
190
  source_claims: SourceClaimV2[];
191
+ stages: DeliveryStageV2[];
182
192
  risk: {
183
193
  requested_level: RequestedRiskLevel;
184
194
  facts: LongTaskRiskFacts;
@@ -114,6 +114,7 @@ export async function compileDeliveryContract(workdirInput, projectRootInput = p
114
114
  task: contract.task,
115
115
  risk: contract.risk,
116
116
  source_claims: contract.source_claims,
117
+ stages: contract.stages,
117
118
  global: {
118
119
  product: contract.global.product,
119
120
  technical: contract.global.technical,
@@ -5,8 +5,9 @@ import { validateDeliveryContractStructure } from "./long-task-delivery-validati
5
5
  import { parseOutcome, parseOutcomeFragment, safeFragmentPath, } from "./long-task-outcome-parser.js";
6
6
  import { assertProtectedRepositoryFile } from "./long-task-protected-files.js";
7
7
  import { array, literal, object, parseSourceClaims, strings, } from "./long-task-delivery-shape.js";
8
- import { parseGlobal, parseRisk, parseTask } from "./long-task-root-shape.js";
8
+ import { parseGlobal, parseRisk, parseStages, parseTask, } from "./long-task-root-shape.js";
9
9
  import { repositoryRoot } from "./long-task-workspace.js";
10
+ import { assertNoSemanticDriftMigration, semanticDriftMigrationFields, } from "./long-task-semantic-drift-migration.js";
10
11
  export const DELIVERY_CONTRACT_FILE = "delivery-contract.yaml";
11
12
  export async function parseDeliveryContract(workdir) {
12
13
  return (await parseDeliveryContractBundle(workdir)).contract;
@@ -75,7 +76,8 @@ function parseRoot(raw, bundle) {
75
76
  decoded.schema_version ===
76
77
  "long-task-delivery-v1")
77
78
  throw new Error("long_task_delivery_v1_retired_use_v2");
78
- return object(decoded, "$", ["schema_version", "task", "source_claims", "risk", "global"], bundle ? ["outcomes", "outcome_files"] : ["outcomes"]);
79
+ assertNoSemanticDriftMigration(semanticDriftMigrationFields(decoded));
80
+ return object(decoded, "$", ["schema_version", "task", "source_claims", "stages", "risk", "global"], bundle ? ["outcomes", "outcome_files"] : ["outcomes"]);
79
81
  }
80
82
  function parseContractRoot(root, outcomes) {
81
83
  literal(root.schema_version, ["long-task-delivery-v2"], "schema_version");
@@ -83,6 +85,7 @@ function parseContractRoot(root, outcomes) {
83
85
  schema_version: "long-task-delivery-v2",
84
86
  task: parseTask(root.task),
85
87
  source_claims: parseSourceClaims(root.source_claims, "source_claims"),
88
+ stages: parseStages(root.stages),
86
89
  risk: parseRisk(root.risk),
87
90
  global: parseGlobal(root.global),
88
91
  outcomes,
@@ -2,7 +2,9 @@ export * from "./long-task-contract-types.js";
2
2
  export * from "./long-task-counterfactual-types.js";
3
3
  export * from "./long-task-authority-types.js";
4
4
  export * from "./long-task-evidence-adapter-types.js";
5
+ export * from "./long-task-evidence-capability-types.js";
5
6
  export * from "./long-task-risk-types.js";
6
7
  export * from "./long-task-runtime-types.js";
8
+ export * from "./long-task-semantic-contract-types.js";
7
9
  export * from "./long-task-source-authority-types.js";
8
10
  export * from "./long-task-workspace-runtime-types.js";
@@ -2,7 +2,9 @@ export * from "./long-task-contract-types.js";
2
2
  export * from "./long-task-counterfactual-types.js";
3
3
  export * from "./long-task-authority-types.js";
4
4
  export * from "./long-task-evidence-adapter-types.js";
5
+ export * from "./long-task-evidence-capability-types.js";
5
6
  export * from "./long-task-risk-types.js";
6
7
  export * from "./long-task-runtime-types.js";
8
+ export * from "./long-task-semantic-contract-types.js";
7
9
  export * from "./long-task-source-authority-types.js";
8
10
  export * from "./long-task-workspace-runtime-types.js";
@@ -4,6 +4,9 @@ import { proveRepositoryPatternSubset } from "./long-task-paths.js";
4
4
  import { validateSourceClaimMappings } from "./long-task-source-claim-validation.js";
5
5
  import { validateDeclaredCheckSafety } from "./long-task-claim-proof-policy.js";
6
6
  import { validateEvidenceAdapterCompatibility } from "./long-task-evidence-adapter-policy.js";
7
+ import { validateEvidenceCapabilityDeclarations } from "./long-task-evidence-capability-policy.js";
8
+ import { validateDeliveryStages } from "./long-task-stage-policy.js";
9
+ import { validateExecutionTargets, validateExternalConfirmationImpacts, } from "./long-task-target-policy.js";
7
10
  export function validateDeliveryContractStructure(contract) {
8
11
  validateUniqueKeys(contract);
9
12
  validateDependencies(contract);
@@ -15,12 +18,19 @@ export function validateDeliveryContractStructure(contract) {
15
18
  });
16
19
  validateSourceClaimMappings(contract, claims);
17
20
  assertCompiledClaimsCovered(claims);
21
+ validateDeliveryStages(contract);
22
+ validateExecutionTargets(contract);
23
+ validateEvidenceCapabilityDeclarations(contract);
24
+ validateExternalConfirmationImpacts(contract, claims);
18
25
  }
19
26
  export function deliveryContractStructureDiagnostics(contract) {
20
27
  const diagnostics = [];
21
28
  const report = (message) => diagnostics.push(message);
22
29
  validateUniqueKeys(contract, report);
23
30
  validateDependencies(contract, report);
31
+ validateDeliveryStages(contract, report);
32
+ validateExecutionTargets(contract, report);
33
+ validateEvidenceCapabilityDeclarations(contract, report);
24
34
  validateOwnerAndBindings(contract, report);
25
35
  validateDeclaredCheckSafety(contract, report);
26
36
  validateEvidenceAdapters(contract, report);
@@ -30,6 +40,7 @@ export function deliveryContractStructureDiagnostics(contract) {
30
40
  });
31
41
  if (claims) {
32
42
  validateSourceClaimMappings(contract, claims, report);
43
+ validateExternalConfirmationImpacts(contract, claims, report);
33
44
  capture(diagnostics, () => assertCompiledClaimsCovered(claims));
34
45
  }
35
46
  return [...new Set(diagnostics)];
@@ -0,0 +1,2 @@
1
+ import type { EvidenceCapabilityRecordV2 } from "./long-task-delivery-types.js";
2
+ export declare function decodeEvidenceCapabilityRecords(value: unknown): EvidenceCapabilityRecordV2[];
@@ -0,0 +1,231 @@
1
+ export function decodeEvidenceCapabilityRecords(value) {
2
+ if (!Array.isArray(value))
3
+ throw invalidRecord("must_be_array");
4
+ return value.map((item, index) => decodeRecord(item, index));
5
+ }
6
+ function decodeRecord(value, index) {
7
+ const label = `evidence_records[${index}]`;
8
+ const row = record(value, label);
9
+ const assertionKey = key(row.assertion_key, `${label}.assertion_key`);
10
+ const capability = nonEmpty(row.capability, `${label}.capability`);
11
+ const base = { assertion_key: assertionKey };
12
+ switch (capability) {
13
+ case "interaction_trace":
14
+ exact(row, label, [
15
+ "assertion_key",
16
+ "capability",
17
+ "target_ref",
18
+ "given_keys",
19
+ "action_keys",
20
+ ]);
21
+ return {
22
+ ...base,
23
+ capability,
24
+ target_ref: key(row.target_ref, `${label}.target_ref`),
25
+ given_keys: keys(row.given_keys, `${label}.given_keys`),
26
+ action_keys: keys(row.action_keys, `${label}.action_keys`),
27
+ };
28
+ case "state_delta":
29
+ exact(row, label, [
30
+ "assertion_key",
31
+ "capability",
32
+ "before_sha256",
33
+ "after_sha256",
34
+ "changed_fields",
35
+ ]);
36
+ return {
37
+ ...base,
38
+ capability,
39
+ before_sha256: sha(row.before_sha256, `${label}.before_sha256`),
40
+ after_sha256: sha(row.after_sha256, `${label}.after_sha256`),
41
+ changed_fields: strings(row.changed_fields, `${label}.changed_fields`),
42
+ };
43
+ case "cross_surface_consistency":
44
+ exact(row, label, ["assertion_key", "capability", "surfaces"]);
45
+ return {
46
+ ...base,
47
+ capability,
48
+ surfaces: array(row.surfaces, `${label}.surfaces`).map((item, surfaceIndex) => {
49
+ const surfaceLabel = `${label}.surfaces[${surfaceIndex}]`;
50
+ const surface = record(item, surfaceLabel);
51
+ exact(surface, surfaceLabel, [
52
+ "surface_ref",
53
+ "target_ref",
54
+ "state_sha256",
55
+ ]);
56
+ return {
57
+ surface_ref: key(surface.surface_ref, `${surfaceLabel}.surface_ref`),
58
+ target_ref: key(surface.target_ref, `${surfaceLabel}.target_ref`),
59
+ state_sha256: sha(surface.state_sha256, `${surfaceLabel}.state_sha256`),
60
+ };
61
+ }),
62
+ };
63
+ case "durable_readback":
64
+ exact(row, label, [
65
+ "assertion_key",
66
+ "capability",
67
+ "write_session_id",
68
+ "read_session_id",
69
+ "written_sha256",
70
+ "read_sha256",
71
+ ]);
72
+ return {
73
+ ...base,
74
+ capability,
75
+ write_session_id: nonEmpty(row.write_session_id, `${label}.write_session_id`),
76
+ read_session_id: nonEmpty(row.read_session_id, `${label}.read_session_id`),
77
+ written_sha256: sha(row.written_sha256, `${label}.written_sha256`),
78
+ read_sha256: sha(row.read_sha256, `${label}.read_sha256`),
79
+ };
80
+ case "boundary_invocation":
81
+ exact(row, label, [
82
+ "assertion_key",
83
+ "capability",
84
+ "boundary",
85
+ "invocation_id",
86
+ "request_sha256",
87
+ "observer_target_ref",
88
+ ]);
89
+ return {
90
+ ...base,
91
+ capability,
92
+ boundary: nonEmpty(row.boundary, `${label}.boundary`),
93
+ invocation_id: nonEmpty(row.invocation_id, `${label}.invocation_id`),
94
+ request_sha256: sha(row.request_sha256, `${label}.request_sha256`),
95
+ observer_target_ref: key(row.observer_target_ref, `${label}.observer_target_ref`),
96
+ };
97
+ case "external_side_effect":
98
+ exact(row, label, [
99
+ "assertion_key",
100
+ "capability",
101
+ "boundary",
102
+ "effect_id",
103
+ "effect_sha256",
104
+ "observer_target_ref",
105
+ ]);
106
+ return {
107
+ ...base,
108
+ capability,
109
+ boundary: nonEmpty(row.boundary, `${label}.boundary`),
110
+ effect_id: nonEmpty(row.effect_id, `${label}.effect_id`),
111
+ effect_sha256: sha(row.effect_sha256, `${label}.effect_sha256`),
112
+ observer_target_ref: key(row.observer_target_ref, `${label}.observer_target_ref`),
113
+ };
114
+ case "failure_injection":
115
+ exact(row, label, [
116
+ "assertion_key",
117
+ "capability",
118
+ "fault",
119
+ "failure_observed",
120
+ "recovery_state_sha256",
121
+ ]);
122
+ if (row.failure_observed !== true)
123
+ throw invalidRecord(`${label}.failure_observed`);
124
+ return {
125
+ ...base,
126
+ capability,
127
+ fault: nonEmpty(row.fault, `${label}.fault`),
128
+ failure_observed: true,
129
+ recovery_state_sha256: sha(row.recovery_state_sha256, `${label}.recovery_state_sha256`),
130
+ };
131
+ case "visual_render":
132
+ exact(row, label, [
133
+ "assertion_key",
134
+ "capability",
135
+ "artifact_path",
136
+ "artifact_sha256",
137
+ ]);
138
+ return {
139
+ ...base,
140
+ capability,
141
+ artifact_path: nonEmpty(row.artifact_path, `${label}.artifact_path`),
142
+ artifact_sha256: sha(row.artifact_sha256, `${label}.artifact_sha256`),
143
+ };
144
+ case "target_runtime":
145
+ exact(row, label, [
146
+ "assertion_key",
147
+ "capability",
148
+ "target_ref",
149
+ "root_entrypoint",
150
+ "session_id",
151
+ "cold_start",
152
+ ]);
153
+ if (typeof row.cold_start !== "boolean")
154
+ throw invalidRecord(`${label}.cold_start`);
155
+ return {
156
+ ...base,
157
+ capability,
158
+ target_ref: key(row.target_ref, `${label}.target_ref`),
159
+ root_entrypoint: nonEmpty(row.root_entrypoint, `${label}.root_entrypoint`),
160
+ session_id: nonEmpty(row.session_id, `${label}.session_id`),
161
+ cold_start: row.cold_start,
162
+ };
163
+ case "input_variation":
164
+ exact(row, label, [
165
+ "assertion_key",
166
+ "capability",
167
+ "cases",
168
+ "failure_case_observed",
169
+ ]);
170
+ if (typeof row.failure_case_observed !== "boolean")
171
+ throw invalidRecord(`${label}.failure_case_observed`);
172
+ return {
173
+ ...base,
174
+ capability,
175
+ cases: array(row.cases, `${label}.cases`).map((item, caseIndex) => {
176
+ const caseLabel = `${label}.cases[${caseIndex}]`;
177
+ const entry = record(item, caseLabel);
178
+ exact(entry, caseLabel, ["input_sha256", "output_sha256"]);
179
+ return {
180
+ input_sha256: sha(entry.input_sha256, `${caseLabel}.input_sha256`),
181
+ output_sha256: sha(entry.output_sha256, `${caseLabel}.output_sha256`),
182
+ };
183
+ }),
184
+ failure_case_observed: row.failure_case_observed,
185
+ };
186
+ default:
187
+ throw invalidRecord(`${label}.capability_unsupported:${capability}`);
188
+ }
189
+ }
190
+ function invalidRecord(detail) {
191
+ return new Error(`check_evidence_records_invalid:${detail}`);
192
+ }
193
+ function record(value, label) {
194
+ if (!value || typeof value !== "object" || Array.isArray(value))
195
+ throw invalidRecord(label);
196
+ return value;
197
+ }
198
+ function exact(row, label, fields) {
199
+ const allowed = new Set(fields);
200
+ if (fields.some((field) => !Object.hasOwn(row, field)) ||
201
+ Object.keys(row).some((field) => !allowed.has(field)))
202
+ throw invalidRecord(`${label}.shape`);
203
+ }
204
+ function array(value, label) {
205
+ if (!Array.isArray(value))
206
+ throw invalidRecord(label);
207
+ return value;
208
+ }
209
+ function nonEmpty(value, label) {
210
+ if (typeof value !== "string" || !value.trim())
211
+ throw invalidRecord(label);
212
+ return value;
213
+ }
214
+ function key(value, label) {
215
+ const result = nonEmpty(value, label);
216
+ if (!/^[a-z0-9][a-z0-9-]*$/u.test(result))
217
+ throw invalidRecord(label);
218
+ return result;
219
+ }
220
+ function strings(value, label) {
221
+ return array(value, label).map((item, index) => nonEmpty(item, `${label}[${index}]`));
222
+ }
223
+ function keys(value, label) {
224
+ return array(value, label).map((item, index) => key(item, `${label}[${index}]`));
225
+ }
226
+ function sha(value, label) {
227
+ const result = nonEmpty(value, label);
228
+ if (!/^[a-f0-9]{64}$/u.test(result))
229
+ throw invalidRecord(label);
230
+ return result;
231
+ }
@@ -0,0 +1,8 @@
1
+ import type { CompiledCheckV2, DeliveryContractV2, EvidenceCapabilityRecordV2, LongTaskFindingV2 } from "./long-task-delivery-types.js";
2
+ export { decodeEvidenceCapabilityRecords } from "./long-task-evidence-capability-codec.js";
3
+ type Reporter = (message: string) => void;
4
+ export declare function validateEvidenceCapabilityDeclarations(contract: DeliveryContractV2, report?: Reporter): void;
5
+ export declare function evaluateEvidenceCapabilities(check: CompiledCheckV2, records: EvidenceCapabilityRecordV2[], artifactHashes: Record<string, string>): {
6
+ complete: Record<string, boolean>;
7
+ findings: LongTaskFindingV2[];
8
+ };
@@ -0,0 +1,147 @@
1
+ import { validateRuntimeEvidenceRecord } from "./long-task-evidence-capability-runtime.js";
2
+ import { checkFinding } from "./long-task-evidence-findings.js";
3
+ export { decodeEvidenceCapabilityRecords } from "./long-task-evidence-capability-codec.js";
4
+ export function validateEvidenceCapabilityDeclarations(contract, report) {
5
+ const targets = new Map(contract.task.execution_targets.map((target) => [target.key, target]));
6
+ for (const [outcomeKey, check] of allChecks(contract)) {
7
+ unique(check.journey_roles, "journey_role_duplicate", checkLabel(outcomeKey, check.key), report);
8
+ if (!check.journey_roles.length)
9
+ issue(report, "journey_role_required", checkLabel(outcomeKey, check.key));
10
+ validateScenario(check, outcomeKey, report);
11
+ for (const assertion of [
12
+ ...check.positive_assertions,
13
+ ...check.negative_assertions,
14
+ ]) {
15
+ const label = `${checkLabel(outcomeKey, check.key)}:${assertion.key}`;
16
+ unique(assertion.evidence_capabilities, "evidence_capability_duplicate", label, report);
17
+ if (!assertion.evidence_capabilities.length)
18
+ issue(report, "evidence_capability_required", label);
19
+ if (assertion.claims.length &&
20
+ check.proof_surface !== "implementation_structure" &&
21
+ assertion.evidence_capabilities.every((capability) => capability === "presence"))
22
+ issue(report, "presence_cannot_prove_behavior", label);
23
+ if (assertion.operator === "exists" &&
24
+ !assertion.evidence_capabilities.includes("presence"))
25
+ issue(report, "exists_requires_presence_capability", label);
26
+ if (assertion.evidence_capabilities.includes("visual_render") &&
27
+ !check.artifact_globs.length)
28
+ issue(report, "visual_render_artifact_required", label);
29
+ if (assertion.evidence_capabilities.includes("interaction_trace") &&
30
+ !check.scenario.when.length)
31
+ issue(report, "interaction_trace_actions_required", label);
32
+ if (check.runner.type === "playwright_test" &&
33
+ assertion.evidence_capabilities.some((capability) => capability !== "presence" &&
34
+ capability !== "interaction_trace" &&
35
+ capability !== "target_runtime"))
36
+ issue(report, "playwright_evidence_capability_unsupported", label);
37
+ if (["boundary_invocation", "external_side_effect"].some((capability) => assertion.evidence_capabilities.includes(capability)) &&
38
+ targets.get(check.execution_target.target_ref)?.role !== "observer")
39
+ issue(report, "observer_check_target_required", label);
40
+ }
41
+ }
42
+ validateJourneySeparation(contract, report);
43
+ }
44
+ function validateScenario(check, outcomeKey, report) {
45
+ const label = checkLabel(outcomeKey, check.key);
46
+ if (!check.scenario.given.length)
47
+ issue(report, "scenario_given_required", label);
48
+ if (!check.scenario.when.length)
49
+ issue(report, "scenario_when_required", label);
50
+ unique(check.scenario.given.map((step) => step.key), "scenario_given_key_duplicate", label, report);
51
+ unique(check.scenario.when.map((step) => step.key), "scenario_when_key_duplicate", label, report);
52
+ }
53
+ function validateJourneySeparation(contract, report) {
54
+ for (const outcome of contract.outcomes) {
55
+ for (const check of outcome.acceptance.checks)
56
+ if (check.journey_roles.includes("success") &&
57
+ check.journey_roles.includes("degradation"))
58
+ issue(report, "success_degradation_check_must_be_distinct", `${outcome.key}:${check.key}`);
59
+ if (outcome.product.success_path_required &&
60
+ !outcome.acceptance.checks.some((check) => check.journey_roles.includes("success")))
61
+ issue(report, "success_path_check_required", outcome.key);
62
+ if (outcome.product.degradation_path_required &&
63
+ !outcome.acceptance.checks.some((check) => check.journey_roles.includes("degradation")))
64
+ issue(report, "degradation_path_check_required", outcome.key);
65
+ for (const check of outcome.acceptance.checks) {
66
+ const provesResult = [
67
+ ...check.positive_assertions,
68
+ ...check.negative_assertions,
69
+ ].some((assertion) => assertion.claims.includes("result"));
70
+ if (provesResult && !check.journey_roles.includes("success"))
71
+ issue(report, "result_claim_requires_success_journey", `${outcome.key}:${check.key}`);
72
+ }
73
+ }
74
+ }
75
+ export function evaluateEvidenceCapabilities(check, records, artifactHashes) {
76
+ const runtimeRecords = records ?? [];
77
+ const complete = {};
78
+ const findings = [];
79
+ const assertions = [
80
+ ...check.positive_assertions,
81
+ ...check.negative_assertions,
82
+ ];
83
+ const assertionsByKey = new Map(assertions.map((assertion) => [assertion.key, assertion]));
84
+ for (const record of runtimeRecords) {
85
+ const assertion = assertionsByKey.get(record.assertion_key);
86
+ const reason = !assertion
87
+ ? "assertion_unknown"
88
+ : !assertion.evidence_capabilities.includes(record.capability)
89
+ ? "capability_undeclared"
90
+ : null;
91
+ if (!reason)
92
+ continue;
93
+ findings.push({
94
+ ...checkFinding(check, "evidence_capability_invalid", `Evidence record ${record.assertion_key}:${record.capability} is not bound to a declared Assertion capability: ${reason}.`, "Emit records only for capabilities declared by Assertions in this Check."),
95
+ assertion_key: record.assertion_key,
96
+ claim_keys: assertion?.claims ?? [],
97
+ expected: "declared_assertion_capability",
98
+ actual: reason,
99
+ });
100
+ }
101
+ for (const assertion of assertions) {
102
+ let assertionComplete = true;
103
+ for (const capability of assertion.evidence_capabilities) {
104
+ if (capability === "presence")
105
+ continue;
106
+ const matches = runtimeRecords.filter((record) => record.assertion_key === assertion.key &&
107
+ record.capability === capability);
108
+ const reason = matches.length === 1
109
+ ? validateRuntimeEvidenceRecord(check, matches[0], artifactHashes)
110
+ : matches.length === 0
111
+ ? "record_missing"
112
+ : "record_duplicate";
113
+ if (!reason)
114
+ continue;
115
+ assertionComplete = false;
116
+ findings.push({
117
+ ...checkFinding(check, "evidence_capability_invalid", `Assertion ${assertion.key} did not provide valid ${capability} evidence: ${reason}.`, "Make the current Check execution emit one valid typed evidence record for the declared capability."),
118
+ assertion_key: assertion.key,
119
+ claim_keys: assertion.claims,
120
+ expected: capability,
121
+ actual: reason,
122
+ });
123
+ }
124
+ complete[assertion.key] = assertionComplete;
125
+ }
126
+ return { complete, findings };
127
+ }
128
+ function allChecks(contract) {
129
+ return [
130
+ ...contract.global.acceptance.checks.map((check) => [null, check]),
131
+ ...contract.outcomes.flatMap((outcome) => outcome.acceptance.checks.map((check) => [outcome.key, check])),
132
+ ];
133
+ }
134
+ function checkLabel(outcomeKey, checkKey) {
135
+ return `${outcomeKey ?? "GLOBAL"}:${checkKey}`;
136
+ }
137
+ function unique(values, code, detail, report) {
138
+ if (new Set(values).size !== values.length)
139
+ issue(report, code, detail);
140
+ }
141
+ function issue(report, code, detail) {
142
+ const message = `delivery_contract_invalid:${code}:${detail}`;
143
+ if (report)
144
+ report(message);
145
+ else
146
+ throw new Error(message);
147
+ }
@@ -0,0 +1,2 @@
1
+ import type { CompiledCheckV2, EvidenceCapabilityRecordV2 } from "./long-task-delivery-types.js";
2
+ export declare function validateRuntimeEvidenceRecord(check: CompiledCheckV2, record: EvidenceCapabilityRecordV2, artifactHashes: Record<string, string>): string | null;
@@ -0,0 +1,86 @@
1
+ export function validateRuntimeEvidenceRecord(check, record, artifactHashes) {
2
+ switch (record.capability) {
3
+ case "interaction_trace":
4
+ return validateInteractionTrace(check, record);
5
+ case "state_delta":
6
+ if (record.before_sha256 === record.after_sha256)
7
+ return "state_unchanged";
8
+ if (!record.changed_fields.length)
9
+ return "changed_fields_empty";
10
+ return null;
11
+ case "cross_surface_consistency":
12
+ return validateCrossSurfaceConsistency(check, record);
13
+ case "durable_readback":
14
+ if (record.write_session_id === record.read_session_id)
15
+ return "independent_session_required";
16
+ if (record.written_sha256 !== record.read_sha256)
17
+ return "readback_mismatch";
18
+ return null;
19
+ case "boundary_invocation":
20
+ case "external_side_effect":
21
+ return validateObserverEvidence(check, record.observer_target_ref);
22
+ case "failure_injection":
23
+ return record.failure_observed ? null : "failure_not_observed";
24
+ case "visual_render":
25
+ return artifactHashes[record.artifact_path] === record.artifact_sha256
26
+ ? null
27
+ : "artifact_hash_mismatch";
28
+ case "target_runtime":
29
+ return validateTargetRuntime(check, record);
30
+ case "input_variation":
31
+ return validateInputVariation(record);
32
+ }
33
+ }
34
+ function validateInteractionTrace(check, record) {
35
+ if (record.target_ref !== check.execution_target.target_ref)
36
+ return "target_mismatch";
37
+ if (!same(record.given_keys, check.scenario.given.map((step) => step.key)))
38
+ return "given_trace_mismatch";
39
+ if (!same(record.action_keys, check.scenario.when.map((step) => step.key)))
40
+ return "action_trace_mismatch";
41
+ return null;
42
+ }
43
+ function validateCrossSurfaceConsistency(check, record) {
44
+ const surfaces = new Set(record.surfaces.map((surface) => surface.surface_ref));
45
+ const targets = new Set(record.surfaces.map((surface) => surface.target_ref));
46
+ const states = new Set(record.surfaces.map((surface) => surface.state_sha256));
47
+ if (surfaces.size < 2)
48
+ return "two_surfaces_required";
49
+ if (states.size !== 1)
50
+ return "state_hash_mismatch";
51
+ if ([...targets].some((target) => !check.known_execution_targets.some((item) => item.key === target)))
52
+ return "target_unknown";
53
+ return null;
54
+ }
55
+ function validateObserverEvidence(check, observerTargetRef) {
56
+ const observer = check.known_execution_targets.find((target) => target.key === observerTargetRef);
57
+ if (!observer || observer.role !== "observer")
58
+ return "observer_target_invalid";
59
+ if (observer.key !== check.execution_target.target_ref)
60
+ return "check_must_execute_on_observer";
61
+ return null;
62
+ }
63
+ function validateTargetRuntime(check, record) {
64
+ if (record.target_ref !== check.execution_target.target_ref)
65
+ return "target_mismatch";
66
+ if (record.root_entrypoint !== check.execution_target_definition.root_entrypoint)
67
+ return "root_entrypoint_mismatch";
68
+ if (check.execution_target.entrypoint === "root" && !record.cold_start)
69
+ return "cold_start_required";
70
+ return null;
71
+ }
72
+ function validateInputVariation(record) {
73
+ const inputs = new Set(record.cases.map((item) => item.input_sha256));
74
+ const outputs = new Set(record.cases.map((item) => item.output_sha256));
75
+ if (inputs.size < 2)
76
+ return "distinct_inputs_required";
77
+ if (outputs.size < 2)
78
+ return "input_must_reach_output";
79
+ if (!record.failure_case_observed)
80
+ return "failure_case_required";
81
+ return null;
82
+ }
83
+ function same(left, right) {
84
+ return (left.length === right.length &&
85
+ left.every((value, index) => value === right[index]));
86
+ }