project-tiny-context-harness 0.8.4 → 0.8.5

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 (252) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +393 -371
  3. package/assets/README.md +573 -526
  4. package/assets/README.zh-CN.md +333 -311
  5. package/assets/agents/.gitkeep +1 -1
  6. package/assets/agents/AGENTS_CORE.md +44 -25
  7. package/assets/context_templates/architecture.md +28 -28
  8. package/assets/context_templates/area.md +34 -34
  9. package/assets/context_templates/context.toml +30 -30
  10. package/assets/context_templates/deployment.md +35 -35
  11. package/assets/context_templates/global.md +57 -57
  12. package/assets/context_templates/product-surface-contract.md +70 -70
  13. package/assets/context_templates/screen-contract.md +189 -189
  14. package/assets/context_templates/verification.md +32 -32
  15. package/assets/github/.gitkeep +1 -1
  16. package/assets/github/harness.yml +39 -39
  17. package/assets/make/.gitkeep +1 -1
  18. package/assets/make/ty-context.mk +48 -48
  19. package/assets/skills/context_development_engineer/SKILL.md +148 -125
  20. package/assets/skills/context_full_project_export/SKILL.md +28 -28
  21. package/assets/skills/context_harness_upgrade/SKILL.md +60 -60
  22. package/assets/skills/context_product_plan/SKILL.md +99 -79
  23. package/assets/skills/context_surface_contract/SKILL.md +191 -191
  24. package/assets/skills/context_uiux_design/SKILL.md +171 -171
  25. package/assets/skills/design-resource-authoring/SKILL.md +86 -84
  26. package/assets/skills/design-resource-authoring/references/downstream-handoff.md +138 -123
  27. package/assets/skills/design-resource-authoring/references/open-design-provider.md +132 -127
  28. package/assets/skills/design-resource-authoring/references/resource-selection.md +181 -154
  29. package/assets/skills/design-system-authoring/SKILL.md +57 -57
  30. package/assets/skills/design-system-authoring/agents/openai.yaml +6 -6
  31. package/assets/skills/design-system-authoring/references/authority-adoption.md +48 -48
  32. package/assets/skills/design-system-authoring/references/open-design-design-system-provider.md +110 -110
  33. package/assets/skills/long-task-workflow/SKILL.md +98 -92
  34. package/assets/skills/long-task-workflow/agents/openai.yaml +3 -1
  35. package/assets/skills/long-task-workflow/references/authority-lifecycle.md +72 -72
  36. package/assets/skills/long-task-workflow/references/contract-authoring.md +122 -101
  37. package/assets/skills/long-task-workflow/references/evidence-design.md +82 -61
  38. package/assets/skills/long-task-workflow/references/source-authoring.md +109 -90
  39. package/assets/skills/source-plan-authoring/SKILL.md +14 -14
  40. package/assets/tools/validate_context.py +442 -442
  41. package/dist/commands/long-task-authoring.js +27 -2
  42. package/dist/lib/context-templates.js +1 -1
  43. package/dist/lib/design-resource-fact-enums.d.ts +20 -0
  44. package/dist/lib/design-resource-fact-enums.js +170 -0
  45. package/dist/lib/design-resource-fact-locator-extractors.d.ts +8 -0
  46. package/dist/lib/design-resource-fact-locator-extractors.js +108 -0
  47. package/dist/lib/design-resource-fact-locator-resolver.d.ts +3 -0
  48. package/dist/lib/design-resource-fact-locator-resolver.js +112 -0
  49. package/dist/lib/design-resource-fact-locator-validation.d.ts +5 -0
  50. package/dist/lib/design-resource-fact-locator-validation.js +16 -0
  51. package/dist/lib/design-resource-fact-manifest-catalog.d.ts +4 -0
  52. package/dist/lib/design-resource-fact-manifest-catalog.js +249 -0
  53. package/dist/lib/design-resource-fact-manifest-model.d.ts +91 -0
  54. package/dist/lib/design-resource-fact-manifest-model.js +21 -0
  55. package/dist/lib/design-resource-fact-manifest-shape-axes.d.ts +6 -0
  56. package/dist/lib/design-resource-fact-manifest-shape-axes.js +159 -0
  57. package/dist/lib/design-resource-fact-manifest-shape-evidence.d.ts +4 -0
  58. package/dist/lib/design-resource-fact-manifest-shape-evidence.js +59 -0
  59. package/dist/lib/design-resource-fact-manifest-shape-facts.d.ts +4 -0
  60. package/dist/lib/design-resource-fact-manifest-shape-facts.js +109 -0
  61. package/dist/lib/design-resource-fact-manifest-shape-inspector.d.ts +5 -0
  62. package/dist/lib/design-resource-fact-manifest-shape-inspector.js +144 -0
  63. package/dist/lib/design-resource-fact-manifest-shape.d.ts +7 -0
  64. package/dist/lib/design-resource-fact-manifest-shape.js +75 -0
  65. package/dist/lib/design-resource-fact-manifest-types.d.ts +3 -0
  66. package/dist/lib/design-resource-fact-manifest-types.js +3 -0
  67. package/dist/lib/design-resource-fact-manifest-universe.d.ts +5 -0
  68. package/dist/lib/design-resource-fact-manifest-universe.js +50 -0
  69. package/dist/lib/design-resource-fact-manifest-validation.d.ts +3 -0
  70. package/dist/lib/design-resource-fact-manifest-validation.js +106 -0
  71. package/dist/lib/design-resource-fact-policy.d.ts +18 -0
  72. package/dist/lib/design-resource-fact-policy.js +72 -1
  73. package/dist/lib/design-resource-fact-property-methods.d.ts +3 -0
  74. package/dist/lib/design-resource-fact-property-methods.js +158 -0
  75. package/dist/lib/design-resource-fact-shape-primitives.d.ts +3 -0
  76. package/dist/lib/design-resource-fact-shape-primitives.js +18 -0
  77. package/dist/lib/design-resource-fact-types.d.ts +164 -0
  78. package/dist/lib/design-resource-fact-types.js +1 -0
  79. package/dist/lib/design-resource-fact-universe-assets.d.ts +4 -0
  80. package/dist/lib/design-resource-fact-universe-assets.js +71 -0
  81. package/dist/lib/design-resource-fact-universe-catalog.d.ts +4 -0
  82. package/dist/lib/design-resource-fact-universe-catalog.js +151 -0
  83. package/dist/lib/design-resource-fact-universe-conditions.d.ts +2 -0
  84. package/dist/lib/design-resource-fact-universe-conditions.js +92 -0
  85. package/dist/lib/design-resource-fact-universe-facts.d.ts +4 -0
  86. package/dist/lib/design-resource-fact-universe-facts.js +108 -0
  87. package/dist/lib/design-resource-fact-universe-helpers.d.ts +30 -0
  88. package/dist/lib/design-resource-fact-universe-helpers.js +167 -0
  89. package/dist/lib/design-resource-fact-universe-inspector.d.ts +7 -0
  90. package/dist/lib/design-resource-fact-universe-inspector.js +119 -0
  91. package/dist/lib/design-resource-fact-universe-proof.d.ts +4 -0
  92. package/dist/lib/design-resource-fact-universe-proof.js +113 -0
  93. package/dist/lib/design-resource-fact-universe-subjects.d.ts +5 -0
  94. package/dist/lib/design-resource-fact-universe-subjects.js +97 -0
  95. package/dist/lib/design-resource-fact-universe-variations.d.ts +2 -0
  96. package/dist/lib/design-resource-fact-universe-variations.js +75 -0
  97. package/dist/lib/design-resource-fact-value-validation.d.ts +2 -0
  98. package/dist/lib/design-resource-fact-value-validation.js +80 -0
  99. package/dist/lib/design-resource-handoff-file-primitives.js +2 -0
  100. package/dist/lib/design-resource-handoff-file-validation.js +13 -100
  101. package/dist/lib/design-resource-handoff-policy.d.ts +1 -0
  102. package/dist/lib/design-resource-handoff-policy.js +118 -11
  103. package/dist/lib/design-resource-handoff-shape-evidence.js +46 -3
  104. package/dist/lib/design-resource-handoff-shape-primitives.d.ts +5 -0
  105. package/dist/lib/design-resource-handoff-shape-primitives.js +28 -0
  106. package/dist/lib/design-resource-handoff-shape-structure.js +143 -22
  107. package/dist/lib/design-resource-handoff-shape.js +25 -0
  108. package/dist/lib/design-resource-handoff-types.d.ts +97 -21
  109. package/dist/lib/design-resource-handoff-types.js +22 -0
  110. package/dist/lib/design-resource-handoff-validation-coverage.d.ts +1 -1
  111. package/dist/lib/design-resource-handoff-validation-coverage.js +145 -118
  112. package/dist/lib/design-resource-handoff-validation-fact-cells.d.ts +2 -0
  113. package/dist/lib/design-resource-handoff-validation-fact-cells.js +60 -0
  114. package/dist/lib/design-resource-handoff-validation-fact-records.d.ts +2 -0
  115. package/dist/lib/design-resource-handoff-validation-fact-records.js +90 -0
  116. package/dist/lib/design-resource-handoff-validation-facts.js +10 -94
  117. package/dist/lib/design-resource-handoff-validation-proofs.d.ts +2 -0
  118. package/dist/lib/design-resource-handoff-validation-proofs.js +67 -0
  119. package/dist/lib/design-resource-handoff-validation-resource-closure.d.ts +3 -0
  120. package/dist/lib/design-resource-handoff-validation-resource-closure.js +88 -0
  121. package/dist/lib/design-resource-handoff-validation-structure.js +43 -14
  122. package/dist/lib/design-resource-handoff-validation.js +32 -0
  123. package/dist/lib/design-resource-handoff-web-dependency-validation.d.ts +2 -1
  124. package/dist/lib/design-resource-handoff-web-dependency-validation.js +67 -7
  125. package/dist/lib/execution-target-capabilities.d.ts +1 -1
  126. package/dist/lib/execution-target-capabilities.js +14 -0
  127. package/dist/lib/long-task-activation-validation.js +6 -2
  128. package/dist/lib/long-task-applicability-shape.js +3 -3
  129. package/dist/lib/long-task-authoring-authority-preview.js +1 -0
  130. package/dist/lib/long-task-authority-policy.d.ts +1 -0
  131. package/dist/lib/long-task-authority-policy.js +1 -0
  132. package/dist/lib/long-task-authority.js +13 -0
  133. package/dist/lib/long-task-claim-definitions.js +13 -1
  134. package/dist/lib/long-task-claims.js +23 -1
  135. package/dist/lib/long-task-contract-types.d.ts +3 -0
  136. package/dist/lib/long-task-delivery-compiler.js +1 -0
  137. package/dist/lib/long-task-delivery-parser.js +11 -1
  138. package/dist/lib/long-task-delivery-types.d.ts +1 -0
  139. package/dist/lib/long-task-delivery-types.js +1 -0
  140. package/dist/lib/long-task-delivery-validation.js +17 -0
  141. package/dist/lib/long-task-design-resource-handoff.js +62 -38
  142. package/dist/lib/long-task-design-target-capabilities.d.ts +14 -0
  143. package/dist/lib/long-task-design-target-capabilities.js +126 -0
  144. package/dist/lib/long-task-evidence-capability-codec.d.ts +94 -0
  145. package/dist/lib/long-task-evidence-capability-codec.js +395 -22
  146. package/dist/lib/long-task-evidence-capability-policy.js +18 -0
  147. package/dist/lib/long-task-evidence-capability-runtime.js +78 -0
  148. package/dist/lib/long-task-evidence-capability-types.d.ts +114 -1
  149. package/dist/lib/long-task-outcome-parser.js +4 -0
  150. package/dist/lib/long-task-playwright-capability-records.d.ts +5 -0
  151. package/dist/lib/long-task-playwright-capability-records.js +146 -0
  152. package/dist/lib/long-task-playwright-case-evidence.d.ts +3 -2
  153. package/dist/lib/long-task-playwright-case-evidence.js +10 -79
  154. package/dist/lib/long-task-playwright-case-primitives.d.ts +28 -0
  155. package/dist/lib/long-task-playwright-case-primitives.js +143 -0
  156. package/dist/lib/long-task-playwright-evidence.d.ts +1 -1
  157. package/dist/lib/long-task-playwright-evidence.js +10 -75
  158. package/dist/lib/long-task-runner-freeze.d.ts +2 -2
  159. package/dist/lib/long-task-runner-freeze.js +15 -1
  160. package/dist/lib/long-task-runtime-types.d.ts +4 -1
  161. package/dist/lib/long-task-semantic-contract-types.d.ts +1 -1
  162. package/dist/lib/long-task-semantic-drift-migration.js +2 -2
  163. package/dist/lib/long-task-semantic-fact-binding-types.d.ts +57 -0
  164. package/dist/lib/long-task-semantic-fact-binding-types.js +1 -0
  165. package/dist/lib/long-task-semantic-fact-closure-primitives.d.ts +5 -0
  166. package/dist/lib/long-task-semantic-fact-closure-primitives.js +43 -0
  167. package/dist/lib/long-task-semantic-fact-closure.d.ts +13 -0
  168. package/dist/lib/long-task-semantic-fact-closure.js +48 -0
  169. package/dist/lib/long-task-semantic-fact-contract-closure.d.ts +4 -0
  170. package/dist/lib/long-task-semantic-fact-contract-closure.js +24 -0
  171. package/dist/lib/long-task-semantic-fact-contract-facts.d.ts +4 -0
  172. package/dist/lib/long-task-semantic-fact-contract-facts.js +30 -0
  173. package/dist/lib/long-task-semantic-fact-contract-proofs.d.ts +4 -0
  174. package/dist/lib/long-task-semantic-fact-contract-proofs.js +97 -0
  175. package/dist/lib/long-task-semantic-fact-evidence.d.ts +21 -0
  176. package/dist/lib/long-task-semantic-fact-evidence.js +165 -0
  177. package/dist/lib/long-task-semantic-fact-input-closure.d.ts +4 -0
  178. package/dist/lib/long-task-semantic-fact-input-closure.js +101 -0
  179. package/dist/lib/long-task-semantic-fact-provenance-closure.d.ts +4 -0
  180. package/dist/lib/long-task-semantic-fact-provenance-closure.js +140 -0
  181. package/dist/lib/long-task-semantic-fact-shape.d.ts +3 -0
  182. package/dist/lib/long-task-semantic-fact-shape.js +89 -0
  183. package/dist/lib/long-task-semantic-fact-value-closure.d.ts +3 -0
  184. package/dist/lib/long-task-semantic-fact-value-closure.js +110 -0
  185. package/dist/lib/long-task-shape-primitives.d.ts +2 -1
  186. package/dist/lib/long-task-shape-primitives.js +7 -0
  187. package/dist/lib/long-task-source-item-parser.js +25 -6
  188. package/dist/lib/long-task-source-target-continuity.js +4 -1
  189. package/dist/lib/long-task-source-target-index.d.ts +1 -1
  190. package/dist/lib/long-task-source-target-index.js +5 -1
  191. package/dist/lib/long-task-ui-design-policy.js +8 -4
  192. package/dist/lib/long-task-ui-surface-shape.js +83 -1
  193. package/dist/lib/long-task-ui-surface-types.d.ts +29 -0
  194. package/dist/lib/modularity.js +7 -1
  195. package/dist/lib/semantic-fact-base-types.d.ts +14 -0
  196. package/dist/lib/semantic-fact-base-types.js +1 -0
  197. package/dist/lib/semantic-fact-catalog.d.ts +8 -0
  198. package/dist/lib/semantic-fact-catalog.js +191 -0
  199. package/dist/lib/semantic-fact-condition-shape.d.ts +48 -0
  200. package/dist/lib/semantic-fact-condition-shape.js +111 -0
  201. package/dist/lib/semantic-fact-input-shape.d.ts +34 -0
  202. package/dist/lib/semantic-fact-input-shape.js +111 -0
  203. package/dist/lib/semantic-fact-inventory-types.d.ts +147 -0
  204. package/dist/lib/semantic-fact-inventory-types.js +1 -0
  205. package/dist/lib/semantic-fact-manifest-shape.d.ts +4 -0
  206. package/dist/lib/semantic-fact-manifest-shape.js +135 -0
  207. package/dist/lib/semantic-fact-manifest-types.d.ts +58 -0
  208. package/dist/lib/semantic-fact-manifest-types.js +19 -0
  209. package/dist/lib/semantic-fact-policy-authority.d.ts +8 -0
  210. package/dist/lib/semantic-fact-policy-authority.js +79 -0
  211. package/dist/lib/semantic-fact-policy-census.d.ts +2 -0
  212. package/dist/lib/semantic-fact-policy-census.js +80 -0
  213. package/dist/lib/semantic-fact-policy-condition-references.d.ts +7 -0
  214. package/dist/lib/semantic-fact-policy-condition-references.js +19 -0
  215. package/dist/lib/semantic-fact-policy-conditions.d.ts +2 -0
  216. package/dist/lib/semantic-fact-policy-conditions.js +105 -0
  217. package/dist/lib/semantic-fact-policy-facts.d.ts +3 -0
  218. package/dist/lib/semantic-fact-policy-facts.js +73 -0
  219. package/dist/lib/semantic-fact-policy-primitives.d.ts +26 -0
  220. package/dist/lib/semantic-fact-policy-primitives.js +169 -0
  221. package/dist/lib/semantic-fact-policy-proofs.d.ts +2 -0
  222. package/dist/lib/semantic-fact-policy-proofs.js +75 -0
  223. package/dist/lib/semantic-fact-policy-properties.d.ts +3 -0
  224. package/dist/lib/semantic-fact-policy-properties.js +84 -0
  225. package/dist/lib/semantic-fact-policy-units.d.ts +4 -0
  226. package/dist/lib/semantic-fact-policy-units.js +103 -0
  227. package/dist/lib/semantic-fact-policy.d.ts +17 -0
  228. package/dist/lib/semantic-fact-policy.js +41 -0
  229. package/dist/lib/semantic-fact-proof-shape.d.ts +51 -0
  230. package/dist/lib/semantic-fact-proof-shape.js +121 -0
  231. package/dist/lib/semantic-fact-proof-types.d.ts +74 -0
  232. package/dist/lib/semantic-fact-proof-types.js +1 -0
  233. package/dist/lib/semantic-fact-property-shape.d.ts +29 -0
  234. package/dist/lib/semantic-fact-property-shape.js +77 -0
  235. package/dist/lib/semantic-fact-shape-constants.d.ts +3 -0
  236. package/dist/lib/semantic-fact-shape-constants.js +38 -0
  237. package/dist/lib/semantic-fact-shape-primitives.d.ts +15 -0
  238. package/dist/lib/semantic-fact-shape-primitives.js +68 -0
  239. package/dist/lib/semantic-fact-source-parser.d.ts +9 -0
  240. package/dist/lib/semantic-fact-source-parser.js +46 -0
  241. package/dist/lib/semantic-fact-support-shape.d.ts +21 -0
  242. package/dist/lib/semantic-fact-support-shape.js +59 -0
  243. package/dist/lib/semantic-fact-types.d.ts +5 -0
  244. package/dist/lib/semantic-fact-types.js +5 -0
  245. package/dist/lib/semantic-fact-unit-shape.d.ts +34 -0
  246. package/dist/lib/semantic-fact-unit-shape.js +89 -0
  247. package/dist/lib/semantic-fact-value-shape.d.ts +12 -0
  248. package/dist/lib/semantic-fact-value-shape.js +39 -0
  249. package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +1339 -131
  250. package/migrations/README.md +15 -15
  251. package/package.json +84 -84
  252. package/source-mappings.yaml +25 -25
@@ -1,4 +1,6 @@
1
1
  import { DESIGN_RESOURCE_VERIFICATION_METHODS } from "./design-resource-handoff-types.js";
2
+ import { DESIGN_RESOURCE_COMPARATORS } from "./design-resource-fact-manifest-types.js";
3
+ import { DESIGN_RESOURCE_LOCATOR_KINDS } from "./design-resource-handoff-types.js";
2
4
  export function decodeEvidenceCapabilityRecords(value) {
3
5
  if (!Array.isArray(value))
4
6
  throw invalidRecord("must_be_array");
@@ -203,42 +205,398 @@ function decodeRecord(value, index) {
203
205
  "artifact_path",
204
206
  "observation_artifact_path",
205
207
  "fact_refs",
208
+ "fact_results",
206
209
  ]);
207
210
  return {
208
211
  condition_key: key(cell.condition_key, `${cellLabel}.condition_key`),
209
212
  artifact_path: nonEmpty(cell.artifact_path, `${cellLabel}.artifact_path`),
210
213
  observation_artifact_path: nonEmpty(cell.observation_artifact_path, `${cellLabel}.observation_artifact_path`),
211
214
  fact_refs: designFactRefs(cell.fact_refs, `${cellLabel}.fact_refs`),
215
+ fact_results: array(cell.fact_results, `${cellLabel}.fact_results`).map((result, resultIndex) => decodeDesignFactResult(result, `${cellLabel}.fact_results[${resultIndex}]`)),
212
216
  };
213
217
  }),
214
218
  };
219
+ case "semantic_fact":
220
+ return decodeSemanticFactEvidence(row, label, base);
215
221
  case "input_variation":
216
- exact(row, label, [
217
- "assertion_key",
218
- "capability",
219
- "cases",
220
- "failure_case_observed",
221
- ]);
222
- if (typeof row.failure_case_observed !== "boolean")
223
- throw invalidRecord(`${label}.failure_case_observed`);
224
- return {
225
- ...base,
226
- capability,
227
- cases: array(row.cases, `${label}.cases`).map((item, caseIndex) => {
228
- const caseLabel = `${label}.cases[${caseIndex}]`;
229
- const entry = record(item, caseLabel);
230
- exact(entry, caseLabel, ["input_sha256", "output_sha256"]);
231
- return {
232
- input_sha256: sha(entry.input_sha256, `${caseLabel}.input_sha256`),
233
- output_sha256: sha(entry.output_sha256, `${caseLabel}.output_sha256`),
234
- };
235
- }),
236
- failure_case_observed: row.failure_case_observed,
237
- };
222
+ return decodeInputVariationEvidence(row, label, base);
238
223
  default:
239
224
  throw invalidRecord(`${label}.capability_unsupported:${capability}`);
240
225
  }
241
226
  }
227
+ function decodeInputVariationEvidence(row, label, base) {
228
+ exact(row, label, [
229
+ "assertion_key",
230
+ "capability",
231
+ "cases",
232
+ "failure_case_observed",
233
+ ]);
234
+ if (typeof row.failure_case_observed !== "boolean")
235
+ throw invalidRecord(`${label}.failure_case_observed`);
236
+ return {
237
+ ...base,
238
+ capability: "input_variation",
239
+ cases: array(row.cases, `${label}.cases`).map((item, caseIndex) => {
240
+ const caseLabel = `${label}.cases[${caseIndex}]`;
241
+ const entry = record(item, caseLabel);
242
+ exact(entry, caseLabel, ["input_sha256", "output_sha256"]);
243
+ return {
244
+ input_sha256: sha(entry.input_sha256, `${caseLabel}.input_sha256`),
245
+ output_sha256: sha(entry.output_sha256, `${caseLabel}.output_sha256`),
246
+ };
247
+ }),
248
+ failure_case_observed: row.failure_case_observed,
249
+ };
250
+ }
251
+ function decodeSemanticFactEvidence(row, label, base) {
252
+ exact(row, label, [
253
+ "assertion_key",
254
+ "capability",
255
+ "manifest_ref",
256
+ "manifest_sha256",
257
+ "outcome_ref",
258
+ "target_ref",
259
+ "fact_ref",
260
+ "proof_ref",
261
+ "method",
262
+ "subject_ref",
263
+ "condition_ref",
264
+ "property_ref",
265
+ "actual_observation",
266
+ "actual_environment",
267
+ "expected",
268
+ "comparison",
269
+ "verdict",
270
+ "oracle",
271
+ "environment",
272
+ "observer_results",
273
+ ]);
274
+ const actual = record(row.actual_observation, `${label}.actual_observation`);
275
+ exact(actual, `${label}.actual_observation`, [
276
+ "artifact_path",
277
+ "artifact_sha256",
278
+ "locator",
279
+ "value_sha256",
280
+ "sensitivity",
281
+ "redaction",
282
+ ]);
283
+ const actualEnvironment = record(row.actual_environment, `${label}.actual_environment`);
284
+ exact(actualEnvironment, `${label}.actual_environment`, [
285
+ "artifact_path",
286
+ "artifact_sha256",
287
+ "locator",
288
+ "value_sha256",
289
+ ]);
290
+ const comparison = record(row.comparison, `${label}.comparison`);
291
+ exact(comparison, `${label}.comparison`, [
292
+ "artifact_path",
293
+ "artifact_sha256",
294
+ "locator",
295
+ "result_sha256",
296
+ "comparator",
297
+ "mode",
298
+ "parameters",
299
+ "tolerance",
300
+ "mask",
301
+ "passed",
302
+ ]);
303
+ if (typeof comparison.passed !== "boolean")
304
+ throw invalidRecord(`${label}.comparison.passed`);
305
+ const oracle = record(row.oracle, `${label}.oracle`);
306
+ exact(oracle, `${label}.oracle`, [
307
+ "key",
308
+ "trust",
309
+ "identity",
310
+ "version",
311
+ "sha256",
312
+ "capabilities",
313
+ ]);
314
+ const environment = record(row.environment, `${label}.environment`);
315
+ exact(environment, `${label}.environment`, ["key", "identity", "definition"]);
316
+ return {
317
+ ...base,
318
+ capability: "semantic_fact",
319
+ manifest_ref: semanticFactRef(row.manifest_ref, `${label}.manifest_ref`),
320
+ manifest_sha256: sha(row.manifest_sha256, `${label}.manifest_sha256`),
321
+ outcome_ref: semanticFactRef(row.outcome_ref, `${label}.outcome_ref`),
322
+ target_ref: key(row.target_ref, `${label}.target_ref`),
323
+ fact_ref: semanticFactRef(row.fact_ref, `${label}.fact_ref`),
324
+ proof_ref: semanticFactRef(row.proof_ref, `${label}.proof_ref`),
325
+ method: semanticFactRef(row.method, `${label}.method`),
326
+ subject_ref: semanticFactRef(row.subject_ref, `${label}.subject_ref`),
327
+ condition_ref: semanticFactRef(row.condition_ref, `${label}.condition_ref`),
328
+ property_ref: semanticFactRef(row.property_ref, `${label}.property_ref`),
329
+ actual_observation: {
330
+ artifact_path: nonEmpty(actual.artifact_path, `${label}.actual_observation.artifact_path`),
331
+ artifact_sha256: sha(actual.artifact_sha256, `${label}.actual_observation.artifact_sha256`),
332
+ locator: decodeEvidenceLocator(actual.locator, `${label}.actual_observation.locator`),
333
+ value_sha256: sha(actual.value_sha256, `${label}.actual_observation.value_sha256`),
334
+ sensitivity: literal(actual.sensitivity, ["plain", "protected"], `${label}.actual_observation.sensitivity`),
335
+ redaction: nullable(actual.redaction, (value) => {
336
+ const entry = record(value, `${label}.actual_observation.redaction`);
337
+ exact(entry, `${label}.actual_observation.redaction`, [
338
+ "policy_ref",
339
+ "representation",
340
+ "raw_persisted",
341
+ ]);
342
+ if (entry.raw_persisted !== false)
343
+ throw invalidRecord(`${label}.actual_observation.redaction.raw_persisted`);
344
+ return {
345
+ policy_ref: semanticFactRef(entry.policy_ref, `${label}.actual_observation.redaction.policy_ref`),
346
+ representation: literal(entry.representation, ["digest_only", "redacted_structured"], `${label}.actual_observation.redaction.representation`),
347
+ raw_persisted: false,
348
+ };
349
+ }),
350
+ },
351
+ actual_environment: {
352
+ artifact_path: nonEmpty(actualEnvironment.artifact_path, `${label}.actual_environment.artifact_path`),
353
+ artifact_sha256: sha(actualEnvironment.artifact_sha256, `${label}.actual_environment.artifact_sha256`),
354
+ locator: decodeEvidenceLocator(actualEnvironment.locator, `${label}.actual_environment.locator`),
355
+ value_sha256: sha(actualEnvironment.value_sha256, `${label}.actual_environment.value_sha256`),
356
+ },
357
+ expected: decodeSemanticLocatedValue(row.expected, `${label}.expected`),
358
+ comparison: {
359
+ artifact_path: nonEmpty(comparison.artifact_path, `${label}.comparison.artifact_path`),
360
+ artifact_sha256: sha(comparison.artifact_sha256, `${label}.comparison.artifact_sha256`),
361
+ locator: decodeEvidenceLocator(comparison.locator, `${label}.comparison.locator`),
362
+ result_sha256: sha(comparison.result_sha256, `${label}.comparison.result_sha256`),
363
+ comparator: semanticFactRef(comparison.comparator, `${label}.comparison.comparator`),
364
+ mode: literal(comparison.mode, ["exact", "tolerance"], `${label}.comparison.mode`),
365
+ parameters: decodeSemanticLocatedValue(comparison.parameters, `${label}.comparison.parameters`),
366
+ tolerance: nullable(comparison.tolerance, (value) => decodeSemanticLocatedValue(value, `${label}.comparison.tolerance`)),
367
+ mask: nullable(comparison.mask, (value) => decodeSemanticLocatedValue(value, `${label}.comparison.mask`)),
368
+ passed: comparison.passed,
369
+ },
370
+ verdict: literal(row.verdict, ["passed", "failed"], `${label}.verdict`),
371
+ oracle: {
372
+ key: semanticFactRef(oracle.key, `${label}.oracle.key`),
373
+ trust: literal(oracle.trust, ["frozen_executable", "named_external_tcb"], `${label}.oracle.trust`),
374
+ identity: nonEmpty(oracle.identity, `${label}.oracle.identity`),
375
+ version: nonEmpty(oracle.version, `${label}.oracle.version`),
376
+ sha256: nullableSha(oracle.sha256, `${label}.oracle.sha256`),
377
+ capabilities: semanticFactRefs(oracle.capabilities, `${label}.oracle.capabilities`),
378
+ },
379
+ environment: {
380
+ key: semanticFactRef(environment.key, `${label}.environment.key`),
381
+ identity: nonEmpty(environment.identity, `${label}.environment.identity`),
382
+ definition: decodeSemanticLocatedValue(environment.definition, `${label}.environment.definition`),
383
+ },
384
+ observer_results: array(row.observer_results, `${label}.observer_results`).map((item, index) => {
385
+ const itemLabel = `${label}.observer_results[${index}]`;
386
+ const entry = record(item, itemLabel);
387
+ exact(entry, itemLabel, [
388
+ "target_ref",
389
+ "artifact_path",
390
+ "artifact_sha256",
391
+ "locator",
392
+ "value_sha256",
393
+ "comparison_result_sha256",
394
+ "passed",
395
+ ]);
396
+ if (typeof entry.passed !== "boolean")
397
+ throw invalidRecord(`${itemLabel}.passed`);
398
+ return {
399
+ target_ref: key(entry.target_ref, `${itemLabel}.target_ref`),
400
+ artifact_path: nonEmpty(entry.artifact_path, `${itemLabel}.artifact_path`),
401
+ artifact_sha256: sha(entry.artifact_sha256, `${itemLabel}.artifact_sha256`),
402
+ locator: decodeEvidenceLocator(entry.locator, `${itemLabel}.locator`),
403
+ value_sha256: sha(entry.value_sha256, `${itemLabel}.value_sha256`),
404
+ comparison_result_sha256: sha(entry.comparison_result_sha256, `${itemLabel}.comparison_result_sha256`),
405
+ passed: entry.passed,
406
+ };
407
+ }),
408
+ };
409
+ }
410
+ function decodeSemanticLocatedValue(value, label) {
411
+ const row = record(value, label);
412
+ const representation = literal(row.representation, ["inline", "located", "digest_only"], `${label}.representation`);
413
+ exact(row, label, representation === "inline"
414
+ ? ["representation", "locator", "sha256", "value"]
415
+ : ["representation", "locator", "sha256"]);
416
+ const locator = record(row.locator, `${label}.locator`);
417
+ exact(locator, `${label}.locator`, ["material_ref", "kind", "value"]);
418
+ return {
419
+ representation,
420
+ locator: {
421
+ material_ref: semanticFactRef(locator.material_ref, `${label}.locator.material_ref`),
422
+ kind: literal(locator.kind, [
423
+ "source_item",
424
+ "manifest_pointer",
425
+ "json_pointer",
426
+ "yaml_pointer",
427
+ "whole_resource",
428
+ "schema_pointer",
429
+ "api_operation",
430
+ "code_symbol",
431
+ "custom",
432
+ ], `${label}.locator.kind`),
433
+ value: nonEmpty(locator.value, `${label}.locator.value`),
434
+ },
435
+ sha256: sha(row.sha256, `${label}.sha256`),
436
+ ...(representation === "inline" ? { value: row.value } : {}),
437
+ };
438
+ }
439
+ function decodeDesignFactResult(value, label) {
440
+ const row = record(value, label);
441
+ exact(row, label, [
442
+ "fact_ref",
443
+ "subject_ref",
444
+ "variation_ref",
445
+ "property_ref",
446
+ "actual_observation",
447
+ "actual_environment",
448
+ "expected",
449
+ "comparison",
450
+ "verdict",
451
+ "oracle",
452
+ "environment",
453
+ ]);
454
+ const actual = record(row.actual_observation, `${label}.actual_observation`);
455
+ exact(actual, `${label}.actual_observation`, [
456
+ "artifact_path",
457
+ "artifact_sha256",
458
+ "locator",
459
+ "value_sha256",
460
+ "sensitivity",
461
+ "redaction",
462
+ ]);
463
+ const expected = decodeLocatedDigest(row.expected, `${label}.expected`);
464
+ const actualEnvironment = record(row.actual_environment, `${label}.actual_environment`);
465
+ exact(actualEnvironment, `${label}.actual_environment`, [
466
+ "artifact_path",
467
+ "artifact_sha256",
468
+ "locator",
469
+ "value_sha256",
470
+ ]);
471
+ const comparison = record(row.comparison, `${label}.comparison`);
472
+ exact(comparison, `${label}.comparison`, [
473
+ "artifact_path",
474
+ "artifact_sha256",
475
+ "locator",
476
+ "result_sha256",
477
+ "comparator",
478
+ "mode",
479
+ "parameters",
480
+ "tolerance",
481
+ "mask",
482
+ "passed",
483
+ ]);
484
+ const comparator = nonEmpty(comparison.comparator, `${label}.comparison.comparator`);
485
+ if (!DESIGN_RESOURCE_COMPARATORS.includes(comparator) &&
486
+ !/^custom\.[a-z0-9][a-z0-9._-]*$/u.test(comparator))
487
+ throw invalidRecord(`${label}.comparison.comparator`);
488
+ if (typeof comparison.passed !== "boolean")
489
+ throw invalidRecord(`${label}.comparison.passed`);
490
+ const oracle = record(row.oracle, `${label}.oracle`);
491
+ exact(oracle, `${label}.oracle`, [
492
+ "key",
493
+ "trust",
494
+ "identity",
495
+ "version",
496
+ "sha256",
497
+ ]);
498
+ const environment = record(row.environment, `${label}.environment`);
499
+ exact(environment, `${label}.environment`, ["key", "identity", "definition"]);
500
+ return {
501
+ fact_ref: designFactRef(row.fact_ref, `${label}.fact_ref`),
502
+ subject_ref: designFactRef(row.subject_ref, `${label}.subject_ref`),
503
+ variation_ref: designFactRef(row.variation_ref, `${label}.variation_ref`),
504
+ property_ref: designFactRef(row.property_ref, `${label}.property_ref`),
505
+ actual_observation: {
506
+ artifact_path: nonEmpty(actual.artifact_path, `${label}.actual_observation.artifact_path`),
507
+ artifact_sha256: sha(actual.artifact_sha256, `${label}.actual_observation.artifact_sha256`),
508
+ locator: decodeEvidenceLocator(actual.locator, `${label}.actual_observation.locator`),
509
+ value_sha256: sha(actual.value_sha256, `${label}.actual_observation.value_sha256`),
510
+ sensitivity: literal(actual.sensitivity, ["plain", "protected"], `${label}.actual_observation.sensitivity`),
511
+ redaction: nullable(actual.redaction, (value) => {
512
+ const redaction = record(value, `${label}.actual_observation.redaction`);
513
+ exact(redaction, `${label}.actual_observation.redaction`, [
514
+ "policy_ref",
515
+ "representation",
516
+ "raw_persisted",
517
+ ]);
518
+ if (redaction.raw_persisted !== false)
519
+ throw invalidRecord(`${label}.actual_observation.redaction.raw_persisted`);
520
+ return {
521
+ policy_ref: designFactRef(redaction.policy_ref, `${label}.actual_observation.redaction.policy_ref`),
522
+ representation: literal(redaction.representation, ["digest_only", "redacted_structured"], `${label}.actual_observation.redaction.representation`),
523
+ raw_persisted: false,
524
+ };
525
+ }),
526
+ },
527
+ actual_environment: {
528
+ artifact_path: nonEmpty(actualEnvironment.artifact_path, `${label}.actual_environment.artifact_path`),
529
+ artifact_sha256: sha(actualEnvironment.artifact_sha256, `${label}.actual_environment.artifact_sha256`),
530
+ locator: decodeEvidenceLocator(actualEnvironment.locator, `${label}.actual_environment.locator`),
531
+ value_sha256: sha(actualEnvironment.value_sha256, `${label}.actual_environment.value_sha256`),
532
+ },
533
+ expected,
534
+ comparison: {
535
+ artifact_path: nonEmpty(comparison.artifact_path, `${label}.comparison.artifact_path`),
536
+ artifact_sha256: sha(comparison.artifact_sha256, `${label}.comparison.artifact_sha256`),
537
+ locator: decodeEvidenceLocator(comparison.locator, `${label}.comparison.locator`),
538
+ result_sha256: sha(comparison.result_sha256, `${label}.comparison.result_sha256`),
539
+ comparator,
540
+ mode: literal(comparison.mode, ["exact", "tolerance"], `${label}.comparison.mode`),
541
+ parameters: decodeLocatedDigest(comparison.parameters, `${label}.comparison.parameters`),
542
+ tolerance: nullable(comparison.tolerance, (value) => decodeLocatedDigest(value, `${label}.comparison.tolerance`)),
543
+ mask: nullable(comparison.mask, (value) => decodeLocatedDigest(value, `${label}.comparison.mask`)),
544
+ passed: comparison.passed,
545
+ },
546
+ verdict: literal(row.verdict, ["passed", "failed"], `${label}.verdict`),
547
+ oracle: {
548
+ key: designFactRef(oracle.key, `${label}.oracle.key`),
549
+ trust: literal(oracle.trust, ["frozen_executable", "named_external_tcb"], `${label}.oracle.trust`),
550
+ identity: nonEmpty(oracle.identity, `${label}.oracle.identity`),
551
+ version: nonEmpty(oracle.version, `${label}.oracle.version`),
552
+ sha256: nullableSha(oracle.sha256, `${label}.oracle.sha256`),
553
+ },
554
+ environment: {
555
+ key: designFactRef(environment.key, `${label}.environment.key`),
556
+ identity: nonEmpty(environment.identity, `${label}.environment.identity`),
557
+ definition: decodeLocatedDigest(environment.definition, `${label}.environment.definition`),
558
+ },
559
+ };
560
+ }
561
+ export function decodeDesignFactResults(value, label) {
562
+ return array(value, label).map((item, index) => decodeDesignFactResult(item, `${label}[${index}]`));
563
+ }
564
+ function decodeLocatedDigest(value, label) {
565
+ const row = record(value, label);
566
+ exact(row, label, ["locator", "sha256"]);
567
+ const locator = record(row.locator, `${label}.locator`);
568
+ exact(locator, `${label}.locator`, ["resource_ref", "kind", "value"]);
569
+ return {
570
+ locator: {
571
+ resource_ref: designFactRef(locator.resource_ref, `${label}.locator.resource_ref`),
572
+ kind: literal(locator.kind, DESIGN_RESOURCE_LOCATOR_KINDS, `${label}.locator.kind`),
573
+ value: nonEmpty(locator.value, `${label}.locator.value`),
574
+ },
575
+ sha256: sha(row.sha256, `${label}.sha256`),
576
+ };
577
+ }
578
+ function decodeEvidenceLocator(value, label) {
579
+ const row = record(value, label);
580
+ exact(row, label, ["kind", "value"]);
581
+ return {
582
+ kind: literal(row.kind, [
583
+ "json_pointer",
584
+ "image_region",
585
+ "semantic_node",
586
+ "trace_event",
587
+ "timeline_sample",
588
+ "asset_ref",
589
+ "custom",
590
+ ], `${label}.kind`),
591
+ value: nonEmpty(row.value, `${label}.value`),
592
+ };
593
+ }
594
+ function nullableSha(value, label) {
595
+ return value === null ? null : sha(value, label);
596
+ }
597
+ function nullable(value, decode) {
598
+ return value === null ? null : decode(value);
599
+ }
242
600
  function invalidRecord(detail) {
243
601
  return new Error(`check_evidence_records_invalid:${detail}`);
244
602
  }
@@ -283,6 +641,21 @@ function designFactRefs(value, label) {
283
641
  return result;
284
642
  });
285
643
  }
644
+ function designFactRef(value, label) {
645
+ const result = nonEmpty(value, label);
646
+ if (!/^[a-z0-9][a-z0-9._-]*$/u.test(result))
647
+ throw invalidRecord(label);
648
+ return result;
649
+ }
650
+ function semanticFactRefs(value, label) {
651
+ return array(value, label).map((item, index) => semanticFactRef(item, `${label}[${index}]`));
652
+ }
653
+ function semanticFactRef(value, label) {
654
+ const result = nonEmpty(value, label);
655
+ if (!/^[a-z0-9][a-z0-9._:-]*$/u.test(result))
656
+ throw invalidRecord(label);
657
+ return result;
658
+ }
286
659
  function sha(value, label) {
287
660
  const result = nonEmpty(value, label);
288
661
  if (!/^[a-f0-9]{64}$/u.test(result))
@@ -1,4 +1,5 @@
1
1
  import { validateRuntimeEvidenceRecord } from "./long-task-evidence-capability-runtime.js";
2
+ import { validateDistinctSemanticFactEvidence } from "./long-task-semantic-fact-evidence.js";
2
3
  import { checkFinding } from "./long-task-evidence-findings.js";
3
4
  export { decodeEvidenceCapabilityRecords } from "./long-task-evidence-capability-codec.js";
4
5
  export function validateEvidenceCapabilityDeclarations(contract, report) {
@@ -31,6 +32,10 @@ function validateAssertionEvidenceCapabilityDeclarations(assertion, check, targe
31
32
  if (assertion.operator === "exists" &&
32
33
  !assertion.evidence_capabilities.includes("presence"))
33
34
  issue(report, "exists_requires_presence_capability", label);
35
+ validateArtifactCapabilityDeclarations(assertion, check, label, report);
36
+ validateRunnerCapabilityDeclarations(assertion, check, targets, label, report);
37
+ }
38
+ function validateArtifactCapabilityDeclarations(assertion, check, label, report) {
34
39
  if (assertion.evidence_capabilities.includes("visual_render") &&
35
40
  !check.artifact_globs.length)
36
41
  issue(report, "visual_render_artifact_required", label);
@@ -40,6 +45,11 @@ function validateAssertionEvidenceCapabilityDeclarations(assertion, check, targe
40
45
  if (assertion.evidence_capabilities.includes("design_method") &&
41
46
  !check.artifact_globs.length)
42
47
  issue(report, "design_method_artifact_required", label);
48
+ if (assertion.evidence_capabilities.includes("semantic_fact") &&
49
+ !check.artifact_globs.length)
50
+ issue(report, "semantic_fact_artifact_required", label);
51
+ }
52
+ function validateRunnerCapabilityDeclarations(assertion, check, targets, label, report) {
43
53
  if (assertion.evidence_capabilities.includes("interaction_trace") &&
44
54
  !check.scenario.when.length)
45
55
  issue(report, "interaction_trace_actions_required", label);
@@ -48,6 +58,7 @@ function validateAssertionEvidenceCapabilityDeclarations(assertion, check, targe
48
58
  capability !== "interaction_trace" &&
49
59
  capability !== "design_conformance" &&
50
60
  capability !== "design_method" &&
61
+ capability !== "semantic_fact" &&
51
62
  capability !== "target_runtime"))
52
63
  issue(report, "playwright_evidence_capability_unsupported", label);
53
64
  if (["boundary_invocation", "external_side_effect"].some((capability) => assertion.evidence_capabilities.includes(capability)) &&
@@ -137,6 +148,13 @@ export function evaluateEvidenceCapabilities(check, records, artifactHashes) {
137
148
  complete[assertion.key] = assertionComplete;
138
149
  }
139
150
  validateDistinctDesignMethodArtifacts(check, runtimeRecords, artifactHashes, findings);
151
+ const semanticDistinctness = validateDistinctSemanticFactEvidence(runtimeRecords);
152
+ if (semanticDistinctness)
153
+ findings.push({
154
+ ...checkFinding(check, "semantic_fact_evidence_reused", `Semantic Fact evidence is not independently attributable: ${semanticDistinctness}.`, "Emit exactly one independently located current observation and comparison result for every Fact-by-method obligation."),
155
+ expected: "unique_fact_proof_and_artifact_locator_identity",
156
+ actual: semanticDistinctness,
157
+ });
140
158
  return { complete, findings };
141
159
  }
142
160
  function validateDistinctDesignMethodArtifacts(check, records, artifactHashes, findings) {
@@ -1,3 +1,4 @@
1
+ import { validateSemanticFactEvidence } from "./long-task-semantic-fact-evidence.js";
1
2
  export function validateRuntimeEvidenceRecord(check, record, artifactHashes) {
2
3
  switch (record.capability) {
3
4
  case "interaction_trace":
@@ -29,6 +30,8 @@ export function validateRuntimeEvidenceRecord(check, record, artifactHashes) {
29
30
  return validateDesignConformance(check, record, artifactHashes);
30
31
  case "design_method":
31
32
  return validateDesignMethod(check, record, artifactHashes);
33
+ case "semantic_fact":
34
+ return validateSemanticFactEvidence(check, record, artifactHashes);
32
35
  case "target_runtime":
33
36
  return validateTargetRuntime(check, record);
34
37
  case "input_variation":
@@ -54,6 +57,7 @@ function validateDesignMethod(check, record, artifactHashes) {
54
57
  artifact_path: item.path,
55
58
  observation_artifact_path: item.observation_path,
56
59
  fact_refs: item.fact_refs,
60
+ fact_expectations: item.fact_expectations,
57
61
  },
58
62
  ]));
59
63
  if (record.cells.length !== expected.size)
@@ -65,13 +69,87 @@ function validateDesignMethod(check, record, artifactHashes) {
65
69
  return "design_method_artifact_path_mismatch";
66
70
  if (!same([...cell.fact_refs].sort(), [...(expectedCell?.fact_refs ?? [])].sort()))
67
71
  return "design_method_fact_refs_mismatch";
72
+ if (!same([...cell.fact_results.map((item) => item.fact_ref)].sort(), [...(expectedCell?.fact_refs ?? [])].sort()))
73
+ return "design_method_fact_results_mismatch";
68
74
  if (!artifactHashes[cell.artifact_path])
69
75
  return "design_method_artifact_missing";
70
76
  if (!artifactHashes[cell.observation_artifact_path])
71
77
  return "design_method_observation_artifact_missing";
78
+ const expectations = new Map((expectedCell?.fact_expectations ?? []).map((item) => [
79
+ item.fact_ref,
80
+ item,
81
+ ]));
82
+ const actualIdentities = new Set();
83
+ const comparisonIdentities = new Set();
84
+ for (const result of cell.fact_results) {
85
+ const expectation = expectations.get(result.fact_ref);
86
+ if (!expectation)
87
+ return "design_method_fact_expectation_missing";
88
+ if (result.subject_ref !== expectation.subject_ref ||
89
+ result.variation_ref !== expectation.variation_ref ||
90
+ result.property_ref !== expectation.property_ref ||
91
+ result.actual_observation.sensitivity !==
92
+ expectation.observation_sensitivity)
93
+ return "design_method_fact_identity_mismatch";
94
+ if ((result.actual_observation.sensitivity === "plain" &&
95
+ result.actual_observation.redaction !== null) ||
96
+ (result.actual_observation.sensitivity === "protected" &&
97
+ result.actual_observation.redaction === null))
98
+ return "design_method_observation_protection_mismatch";
99
+ if (canonicalJson(result.expected) !== canonicalJson(expectation.expected))
100
+ return "design_method_expected_value_mismatch";
101
+ if (result.comparison.comparator !== expectation.comparison.comparator ||
102
+ result.comparison.mode !== expectation.comparison.mode ||
103
+ canonicalJson(result.comparison.parameters) !==
104
+ canonicalJson(expectation.comparison.parameters) ||
105
+ canonicalJson(result.comparison.tolerance) !==
106
+ canonicalJson(expectation.comparison.tolerance) ||
107
+ canonicalJson(result.comparison.mask) !==
108
+ canonicalJson(expectation.comparison.mask))
109
+ return "design_method_comparison_authority_mismatch";
110
+ if (canonicalJson(result.oracle) !== canonicalJson(expectation.oracle) ||
111
+ canonicalJson(result.environment) !==
112
+ canonicalJson(expectation.environment))
113
+ return "design_method_oracle_environment_mismatch";
114
+ if (result.actual_observation.artifact_path !==
115
+ cell.observation_artifact_path ||
116
+ result.actual_observation.artifact_sha256 !==
117
+ artifactHashes[cell.observation_artifact_path])
118
+ return "design_method_actual_observation_mismatch";
119
+ if (result.actual_environment.artifact_path !==
120
+ cell.observation_artifact_path ||
121
+ result.actual_environment.artifact_sha256 !==
122
+ artifactHashes[cell.observation_artifact_path] ||
123
+ result.actual_environment.value_sha256 !==
124
+ expectation.environment.definition.sha256)
125
+ return "design_method_actual_environment_mismatch";
126
+ if (result.comparison.artifact_path !== cell.artifact_path ||
127
+ result.comparison.artifact_sha256 !== artifactHashes[cell.artifact_path])
128
+ return "design_method_comparison_artifact_mismatch";
129
+ const actualIdentity = `${result.actual_observation.artifact_path}\0${canonicalJson(result.actual_observation.locator)}`;
130
+ if (actualIdentities.has(actualIdentity))
131
+ return "design_method_actual_observation_reused";
132
+ actualIdentities.add(actualIdentity);
133
+ const comparisonIdentity = `${result.comparison.artifact_path}\0${canonicalJson(result.comparison.locator)}`;
134
+ if (comparisonIdentities.has(comparisonIdentity))
135
+ return "design_method_comparison_result_reused";
136
+ comparisonIdentities.add(comparisonIdentity);
137
+ if (result.verdict !== "passed" || result.comparison.passed !== true)
138
+ return "design_method_fact_failed";
139
+ }
72
140
  }
73
141
  return null;
74
142
  }
143
+ function canonicalJson(value) {
144
+ if (Array.isArray(value))
145
+ return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
146
+ if (value && typeof value === "object")
147
+ return `{${Object.entries(value)
148
+ .sort(([left], [right]) => left.localeCompare(right))
149
+ .map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`)
150
+ .join(",")}}`;
151
+ return JSON.stringify(value);
152
+ }
75
153
  function validateDesignConformance(check, record, artifactHashes) {
76
154
  const target = (check.design_conformance_targets ?? []).find((item) => item.key === record.design_target_ref &&
77
155
  designTargetUsesAssertion(item, record.assertion_key));