project-tiny-context-harness 0.8.0 → 0.8.2

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 (119) hide show
  1. package/README.md +40 -32
  2. package/assets/README.md +141 -60
  3. package/assets/README.zh-CN.md +48 -40
  4. package/assets/agents/AGENTS_CORE.md +16 -12
  5. package/assets/context_templates/architecture.md +5 -5
  6. package/assets/context_templates/area.md +5 -5
  7. package/assets/context_templates/global.md +6 -6
  8. package/assets/context_templates/screen-contract.md +18 -18
  9. package/assets/github/harness.yml +2 -2
  10. package/assets/skills/context_development_engineer/SKILL.md +7 -5
  11. package/assets/skills/context_full_project_export/SKILL.md +42 -42
  12. package/assets/skills/context_uiux_design/SKILL.md +27 -13
  13. package/assets/skills/design-resource-authoring/SKILL.md +7 -7
  14. package/assets/skills/design-resource-authoring/references/downstream-handoff.md +12 -12
  15. package/assets/skills/design-resource-authoring/references/open-design-provider.md +21 -21
  16. package/assets/skills/design-resource-authoring/references/resource-selection.md +3 -3
  17. package/assets/skills/long-task-workflow/SKILL.md +17 -17
  18. package/assets/skills/long-task-workflow/agents/openai.yaml +4 -4
  19. package/assets/skills/long-task-workflow/references/authority-lifecycle.md +11 -9
  20. package/assets/skills/long-task-workflow/references/contract-authoring.md +19 -15
  21. package/assets/skills/long-task-workflow/references/evidence-design.md +18 -17
  22. package/assets/skills/long-task-workflow/references/source-authoring.md +15 -12
  23. package/assets/skills/normal-long-task/SKILL.md +12 -12
  24. package/dist/commands/design-resource.js +2 -2
  25. package/dist/commands/index.js +26 -26
  26. package/dist/commands/long-task-authoring.js +83 -9
  27. package/dist/commands/long-task.js +15 -15
  28. package/dist/lib/context-graph-snapshot.js +7 -0
  29. package/dist/lib/design-md.js +5 -4
  30. package/dist/lib/design-resource-handoff-shape-evidence.js +3 -0
  31. package/dist/lib/design-resource-handoff-shape-primitives.js +1 -3
  32. package/dist/lib/design-resource-handoff-types.d.ts +2 -0
  33. package/dist/lib/design-resource-handoff-validation-coverage.js +1 -0
  34. package/dist/lib/design-resource-handoff-validation-structure.js +2 -0
  35. package/dist/lib/doctor.js +2 -2
  36. package/dist/lib/execution-target-capabilities.d.ts +2 -0
  37. package/dist/lib/execution-target-capabilities.js +31 -0
  38. package/dist/lib/long-task-acceptance-shape.js +81 -20
  39. package/dist/lib/long-task-applicability-shape.d.ts +4 -0
  40. package/dist/lib/long-task-applicability-shape.js +54 -0
  41. package/dist/lib/long-task-assertions-v2.js +8 -0
  42. package/dist/lib/long-task-authoring-preflight-diagnostics.js +5 -0
  43. package/dist/lib/long-task-authoring-preflight-repair-order.js +3 -0
  44. package/dist/lib/long-task-authoring-preflight.js +12 -8
  45. package/dist/lib/long-task-authority-materials.js +46 -4
  46. package/dist/lib/long-task-authority-policy.d.ts +54 -0
  47. package/dist/lib/long-task-authority-policy.js +61 -0
  48. package/dist/lib/long-task-authority-types.d.ts +12 -2
  49. package/dist/lib/long-task-authority.js +9 -2
  50. package/dist/lib/long-task-check-shape.js +6 -1
  51. package/dist/lib/long-task-claim-definitions.js +27 -42
  52. package/dist/lib/long-task-claims.js +275 -146
  53. package/dist/lib/long-task-contract-types.d.ts +42 -8
  54. package/dist/lib/long-task-control-fields.d.ts +12 -0
  55. package/dist/lib/long-task-control-fields.js +66 -0
  56. package/dist/lib/long-task-control-types.d.ts +1 -0
  57. package/dist/lib/long-task-control-types.js +1 -0
  58. package/dist/lib/long-task-counterfactual-claim-policy.js +7 -0
  59. package/dist/lib/long-task-counterfactual-sandbox.js +1 -2
  60. package/dist/lib/long-task-counterfactual-types.d.ts +12 -0
  61. package/dist/lib/long-task-delivery-compiler.js +1 -0
  62. package/dist/lib/long-task-delivery-preflight.js +24 -0
  63. package/dist/lib/long-task-delivery-shape.d.ts +1 -0
  64. package/dist/lib/long-task-delivery-shape.js +1 -0
  65. package/dist/lib/long-task-delivery-types.d.ts +1 -0
  66. package/dist/lib/long-task-delivery-types.js +1 -0
  67. package/dist/lib/long-task-delivery-validation.js +46 -0
  68. package/dist/lib/long-task-design-resource-handoff.js +48 -7
  69. package/dist/lib/long-task-evidence-capability-codec.js +37 -0
  70. package/dist/lib/long-task-evidence-capability-policy.js +80 -38
  71. package/dist/lib/long-task-evidence-capability-runtime.js +36 -0
  72. package/dist/lib/long-task-evidence-capability-types.d.ts +13 -1
  73. package/dist/lib/long-task-evidence-findings.js +3 -0
  74. package/dist/lib/long-task-evidence-sensitivity-policy.js +38 -0
  75. package/dist/lib/long-task-evidence-v2.js +7 -1
  76. package/dist/lib/long-task-final-integrity.d.ts +16 -0
  77. package/dist/lib/long-task-final-integrity.js +98 -0
  78. package/dist/lib/long-task-final-v2.js +17 -34
  79. package/dist/lib/long-task-outcome-parser.js +22 -4
  80. package/dist/lib/long-task-playwright-case-evidence.d.ts +2 -0
  81. package/dist/lib/long-task-playwright-case-evidence.js +10 -2
  82. package/dist/lib/long-task-playwright-evidence.d.ts +1 -0
  83. package/dist/lib/long-task-playwright-evidence.js +32 -0
  84. package/dist/lib/long-task-product-shape.d.ts +3 -1
  85. package/dist/lib/long-task-product-shape.js +100 -4
  86. package/dist/lib/long-task-requirement-shape.js +3 -0
  87. package/dist/lib/long-task-root-shape.js +12 -6
  88. package/dist/lib/long-task-runner-freeze.js +16 -0
  89. package/dist/lib/long-task-runtime-types.d.ts +11 -2
  90. package/dist/lib/long-task-semantic-assurance-policy.d.ts +4 -0
  91. package/dist/lib/long-task-semantic-assurance-policy.js +52 -0
  92. package/dist/lib/long-task-semantic-contract-types.d.ts +18 -1
  93. package/dist/lib/long-task-semantic-drift-migration.js +33 -2
  94. package/dist/lib/long-task-semantic-mutation.d.ts +7 -0
  95. package/dist/lib/long-task-semantic-mutation.js +81 -0
  96. package/dist/lib/long-task-shape-primitives.d.ts +1 -1
  97. package/dist/lib/long-task-shape-primitives.js +1 -0
  98. package/dist/lib/long-task-source-authority-types.d.ts +2 -0
  99. package/dist/lib/long-task-source-item-parser.d.ts +1 -1
  100. package/dist/lib/long-task-source-item-parser.js +150 -104
  101. package/dist/lib/long-task-source-markers.d.ts +12 -0
  102. package/dist/lib/long-task-source-markers.js +146 -0
  103. package/dist/lib/long-task-source-target-continuity.d.ts +1 -1
  104. package/dist/lib/long-task-source-target-continuity.js +80 -55
  105. package/dist/lib/long-task-source-target-index.js +7 -29
  106. package/dist/lib/long-task-status-v2.d.ts +1 -0
  107. package/dist/lib/long-task-status-v2.js +2 -2
  108. package/dist/lib/long-task-target-policy.js +20 -0
  109. package/dist/lib/long-task-ui-design-policy.js +27 -1
  110. package/dist/lib/long-task-ui-surface-shape.js +21 -1
  111. package/dist/lib/long-task-ui-surface-types.d.ts +7 -0
  112. package/dist/lib/long-task-verification-preview.d.ts +10 -0
  113. package/dist/lib/long-task-verifier-dependency-closure.d.ts +3 -0
  114. package/dist/lib/long-task-verifier-dependency-closure.js +179 -0
  115. package/dist/lib/long-task-verifier-v2.js +12 -2
  116. package/dist/lib/long-task-workspace.js +5 -1
  117. package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +99 -25
  118. package/migrations/README.md +8 -8
  119. package/package.json +84 -84
@@ -104,8 +104,7 @@ export async function removeCounterfactualSandboxRoot(root, removeTree = rm, wai
104
104
  const code = error && typeof error === "object" && "code" in error
105
105
  ? String(error.code)
106
106
  : "";
107
- if (!TRANSIENT_REMOVE_CODES.has(code) ||
108
- attempt >= REMOVE_RETRY_LIMIT)
107
+ if (!TRANSIENT_REMOVE_CODES.has(code) || attempt >= REMOVE_RETRY_LIMIT)
109
108
  throw error;
110
109
  await wait(REMOVE_RETRY_DELAY_MS * (attempt + 1));
111
110
  }
@@ -5,6 +5,16 @@ export type CounterfactualMutationV2 = {
5
5
  type: "replace_file";
6
6
  path: string;
7
7
  fixture_path: string;
8
+ } | {
9
+ type: "replace_json_value";
10
+ path: string;
11
+ pointer: string;
12
+ value: unknown;
13
+ } | {
14
+ type: "replace_text";
15
+ path: string;
16
+ match: string;
17
+ replacement: string;
8
18
  };
9
19
  export interface CounterfactualControlV2 {
10
20
  key: string;
@@ -13,6 +23,7 @@ export interface CounterfactualControlV2 {
13
23
  check_key: string;
14
24
  mutation: CounterfactualMutationV2;
15
25
  expected_assertion_failures: string[];
26
+ preserved_assertions: string[];
16
27
  }
17
28
  export interface GlobalCounterfactualControlV2 {
18
29
  key: string;
@@ -21,4 +32,5 @@ export interface GlobalCounterfactualControlV2 {
21
32
  check_key: string;
22
33
  mutation: CounterfactualMutationV2;
23
34
  expected_assertion_failures: string[];
35
+ preserved_assertions: string[];
24
36
  }
@@ -134,6 +134,7 @@ export async function compileDeliveryContract(workdirInput, projectRootInput = p
134
134
  source_claims: contract.source_claims,
135
135
  stages: contract.stages,
136
136
  global: {
137
+ applicability: contract.global.applicability,
137
138
  product: contract.global.product,
138
139
  technical: contract.global.technical,
139
140
  acceptance: {
@@ -4,6 +4,7 @@ import { assertRepositoryPattern, matchesRepoPattern, proveRepositoryPatternSubs
4
4
  import { assertProtectedRepositoryFile } from "./long-task-protected-files.js";
5
5
  import { sha256Hex } from "./strict-codec.js";
6
6
  import { repoRelative, resolveInsideRepository, } from "./long-task-workspace.js";
7
+ import { mutateSemanticText } from "./long-task-semantic-mutation.js";
7
8
  export function validateVerificationInputSeparation(contract, checks, workdirRelative) {
8
9
  const implementation = contract.outcomes.flatMap((outcome) => [
9
10
  ...outcome.technical.expected_change_paths,
@@ -133,12 +134,35 @@ async function validateScopeCounterfactualPaths(options) {
133
134
  !(await stat(target).catch(() => null)))
134
135
  throw new Error(`counterfactual_path_not_found:${options.scope}:${relative}`);
135
136
  }
137
+ if (options.mutation.type === "replace_json_value" ||
138
+ options.mutation.type === "replace_text") {
139
+ const replacementTarget = resolveInsideRepository(options.repository, options.mutation.path, `${options.scope}.counterfactual.path`);
140
+ if (!(await stat(replacementTarget).catch(() => null)))
141
+ return;
142
+ try {
143
+ mutateSemanticText(await readFile(replacementTarget, "utf8"), options.mutation, false);
144
+ }
145
+ catch (error) {
146
+ const message = error instanceof Error ? error.message : String(error);
147
+ throw new Error(`counterfactual_semantic_mutation_invalid:${options.scope}:${options.controlKey}:${message}`);
148
+ }
149
+ return;
150
+ }
136
151
  if (options.mutation.type !== "replace_file")
137
152
  return;
138
153
  const fixtureTarget = resolveInsideRepository(options.repository, options.mutation.fixture_path, `${options.scope}.counterfactual.fixture`);
139
154
  const fixture = repoRelative(options.repository, await assertProtectedRepositoryFile(options.repository, fixtureTarget, `${options.scope}.counterfactual.fixture`));
140
155
  if (!Object.hasOwn(options.check.verification_input_hashes, fixture))
141
156
  throw new Error(`counterfactual_fixture_must_be_verification_input:${options.scope}:${fixture}`);
157
+ const replacementTarget = resolveInsideRepository(options.repository, options.mutation.path, `${options.scope}.counterfactual.path`);
158
+ if (await stat(replacementTarget).catch(() => null)) {
159
+ const [fixtureBytes, targetBytes] = await Promise.all([
160
+ readFile(fixtureTarget),
161
+ readFile(replacementTarget),
162
+ ]);
163
+ if (fixtureBytes.equals(targetBytes))
164
+ throw new Error(`counterfactual_replacement_must_change_carrier:${options.scope}:${options.controlKey}`);
165
+ }
142
166
  }
143
167
  export async function hashDeclaredFiles(repository, files, label) {
144
168
  const result = {};
@@ -1,4 +1,5 @@
1
1
  export * from "./long-task-acceptance-shape.js";
2
+ export * from "./long-task-applicability-shape.js";
2
3
  export * from "./long-task-product-shape.js";
3
4
  export * from "./long-task-shape-primitives.js";
4
5
  export * from "./long-task-source-shape.js";
@@ -1,4 +1,5 @@
1
1
  export * from "./long-task-acceptance-shape.js";
2
+ export * from "./long-task-applicability-shape.js";
2
3
  export * from "./long-task-product-shape.js";
3
4
  export * from "./long-task-shape-primitives.js";
4
5
  export * from "./long-task-source-shape.js";
@@ -1,4 +1,5 @@
1
1
  export * from "./long-task-contract-types.js";
2
+ export * from "./long-task-control-types.js";
2
3
  export * from "./long-task-counterfactual-types.js";
3
4
  export * from "./long-task-authority-types.js";
4
5
  export * from "./long-task-evidence-adapter-types.js";
@@ -1,4 +1,5 @@
1
1
  export * from "./long-task-contract-types.js";
2
+ export * from "./long-task-control-types.js";
2
3
  export * from "./long-task-counterfactual-types.js";
3
4
  export * from "./long-task-authority-types.js";
4
5
  export * from "./long-task-evidence-adapter-types.js";
@@ -8,9 +8,12 @@ import { validateEvidenceCapabilityDeclarations } from "./long-task-evidence-cap
8
8
  import { validateDeliveryStages } from "./long-task-stage-policy.js";
9
9
  import { validateExecutionTargets, validateExternalConfirmationImpacts, } from "./long-task-target-policy.js";
10
10
  import { validateUiSurfaceBindings } from "./long-task-ui-surface-policy.js";
11
+ import { controlFieldFacts } from "./long-task-control-fields.js";
12
+ import { validateSemanticAssuranceShape } from "./long-task-semantic-assurance-policy.js";
11
13
  export function validateDeliveryContractStructure(contract) {
12
14
  validateUniqueKeys(contract);
13
15
  validateDependencies(contract);
16
+ validateControlClosure(contract);
14
17
  validateOwnerAndBindings(contract);
15
18
  validateDeclaredCheckSafety(contract);
16
19
  validateEvidenceAdapters(contract);
@@ -22,6 +25,7 @@ export function validateDeliveryContractStructure(contract) {
22
25
  assertCompiledClaimsCovered(claims);
23
26
  validateDeliveryStages(contract);
24
27
  validateExecutionTargets(contract);
28
+ validateSemanticAssuranceShape(contract);
25
29
  validateEvidenceCapabilityDeclarations(contract);
26
30
  validateExternalConfirmationImpacts(contract, claims);
27
31
  }
@@ -30,8 +34,10 @@ export function deliveryContractStructureDiagnostics(contract) {
30
34
  const report = (message) => diagnostics.push(message);
31
35
  validateUniqueKeys(contract, report);
32
36
  validateDependencies(contract, report);
37
+ validateControlClosure(contract, report);
33
38
  validateDeliveryStages(contract, report);
34
39
  validateExecutionTargets(contract, report);
40
+ validateSemanticAssuranceShape(contract, report);
35
41
  validateEvidenceCapabilityDeclarations(contract, report);
36
42
  validateOwnerAndBindings(contract, report);
37
43
  validateDeclaredCheckSafety(contract, report);
@@ -73,6 +79,7 @@ function validateUniqueKeys(contract, report) {
73
79
  unique(outcome.acceptance.checks.map((check) => check.key), `check_key_duplicate:${outcome.key}`, report);
74
80
  unique(outcome.product.requirements.map((item) => item.key), `requirement_key_duplicate:${outcome.key}`, report);
75
81
  unique(outcome.product.controls.map((control) => control.key), `control_key_duplicate:${outcome.key}`, report);
82
+ unique(outcome.product.control_relations.map((relation) => relation.key), `control_relation_key_duplicate:${outcome.key}`, report);
76
83
  unique(outcome.product.non_completing_outcomes.map((item) => item.key), `non_completing_key_duplicate:${outcome.key}`, report);
77
84
  unique(outcome.technical.obligations.map((item) => item.key), `obligation_key_duplicate:${outcome.key}`, report);
78
85
  unique(outcome.technical.forbidden_shortcuts.map((item) => item.key), `forbidden_shortcut_key_duplicate:${outcome.key}`, report);
@@ -87,6 +94,45 @@ function validateUniqueKeys(contract, report) {
87
94
  for (const check of contract.global.acceptance.checks)
88
95
  unique([...check.positive_assertions, ...check.negative_assertions].map((assertion) => assertion.observation), `assertion_observation_duplicate:GLOBAL:${check.key}`, report);
89
96
  }
97
+ function validateControlClosure(contract, report) {
98
+ for (const outcome of contract.outcomes) {
99
+ const controls = new Set(outcome.product.controls.map((control) => control.key));
100
+ const closure = outcome.product.control_relation_closure;
101
+ const relations = outcome.product.control_relations;
102
+ if (closure.state === "unresolved")
103
+ issue(report, "control_relation_closure_unresolved", outcome.key);
104
+ if (closure.state === "specified" && !relations.length)
105
+ issue(report, "control_relation_required", outcome.key);
106
+ if (closure.state === "not_applicable" && relations.length)
107
+ issue(report, "control_relation_forbidden_when_not_applicable", outcome.key);
108
+ if (!controls.size && closure.state !== "not_applicable")
109
+ issue(report, "control_relation_closure_without_controls", outcome.key);
110
+ for (const control of outcome.product.controls) {
111
+ const facts = controlFieldFacts(control);
112
+ const unresolved = facts
113
+ .filter((fact) => fact.state === "unresolved")
114
+ .map((fact) => fact.contract_field);
115
+ if (unresolved.length)
116
+ issue(report, "control_field_unresolved", `${outcome.key}:${control.key}:${unresolved.join(",")}`);
117
+ if (!facts.some((fact) => [
118
+ "navigation_result",
119
+ "interaction",
120
+ "trigger",
121
+ "location",
122
+ ].includes(fact.contract_field) && fact.state === "specified"))
123
+ issue(report, "control_root_behavior_required", `${outcome.key}:${control.key}`);
124
+ }
125
+ for (const relation of relations) {
126
+ if (relation.control_refs.length < 2)
127
+ issue(report, "control_relation_control_refs_required", `${outcome.key}:${relation.key}`);
128
+ if (new Set(relation.control_refs).size !== relation.control_refs.length)
129
+ issue(report, "control_relation_control_ref_duplicate", `${outcome.key}:${relation.key}`);
130
+ for (const reference of relation.control_refs)
131
+ if (!controls.has(reference))
132
+ issue(report, "control_relation_control_ref_unknown", `${outcome.key}:${relation.key}:${reference}`);
133
+ }
134
+ }
135
+ }
90
136
  function validateOwnerAndBindings(contract, report) {
91
137
  const contextRefs = new Set(contract.task.context_refs);
92
138
  for (const outcome of contract.outcomes) {
@@ -18,6 +18,7 @@ export async function validateLongTaskDesignResourceHandoffs(contract, repositor
18
18
  invalid("target_handoff_missing", contractTarget.target.key);
19
19
  consumed.add(contractTarget.target.key);
20
20
  validateTargetIdentity(contractTarget, indexedTarget);
21
+ validateTargetCapabilities(contract, contractTarget, indexedTarget);
21
22
  validateCoverageClaims(contract, contractTarget, indexedTarget);
22
23
  validateBlockerBindings(contract, contractTarget, indexedTarget);
23
24
  }
@@ -62,7 +63,6 @@ function validateCoverageClaims(contract, contractTarget, indexed) {
62
63
  const check = contract.outcomes
63
64
  .find((item) => item.key === contractTarget.outcome_key)
64
65
  .acceptance.checks.find((item) => item.key === target.conformance_check_ref);
65
- const assertion = check.positive_assertions.find((item) => item.key === target.conformance_assertion_ref);
66
66
  const claims = new Map(contract.source_claims.map((item) => [item.key, item]));
67
67
  const rows = indexed.preflight.handoff.coverage.filter((row) => row.disposition === "covered" && row.target_refs.includes(target.key));
68
68
  if (!rows.length)
@@ -72,9 +72,6 @@ function validateCoverageClaims(contract, contractTarget, indexed) {
72
72
  for (const sourceItemRef of row.source_item_refs) {
73
73
  const localClaims = designSourceItemClaims(contract, contractTarget, indexed, sourceItemRef, claims);
74
74
  claimsBySourceItem.set(sourceItemRef, localClaims);
75
- for (const localClaimRef of localClaims)
76
- if (!assertion.claims.includes(localClaimRef))
77
- invalid("coverage_claim_not_in_conformance_assertion", `${target.key}:${sourceItemRef}:${localClaimRef}`);
78
75
  }
79
76
  validateVerificationMethodBindings(target, check, rows, claimsBySourceItem);
80
77
  }
@@ -94,6 +91,7 @@ function validateBlockerBindings(contract, contractTarget, indexed) {
94
91
  invalid("acceptance_blocker_unbound", `${contractTarget.target.key}:${blocker.key}`);
95
92
  assertSameSet(contractBlocker.source_item_refs, blocker.source_item_refs, "acceptance_blocker_source_items_mismatch", blocker.key);
96
93
  assertSameSet(contractBlocker.verification_methods, blocker.verification_methods, "acceptance_blocker_methods_mismatch", blocker.key);
94
+ assertSameSet(contractBlocker.required_capabilities, blocker.required_capabilities, "acceptance_blocker_capabilities_mismatch", blocker.key);
97
95
  for (const method of blocker.verification_methods)
98
96
  if (!availableMethods.has(method))
99
97
  invalid("acceptance_blocker_method_unbound", `${blocker.key}:${method}`);
@@ -102,6 +100,12 @@ function validateBlockerBindings(contract, contractTarget, indexed) {
102
100
  return local.map((claimRef) => `${contractTarget.outcome_key}.${claimRef}`);
103
101
  });
104
102
  if (contractBlocker.status === "machine_claim") {
103
+ const executionTarget = contract.task.execution_targets.find((item) => item.key === contractTarget.binding.target_ref);
104
+ if (!executionTarget)
105
+ invalid("execution_target_unknown", `${contractTarget.target.key}:${contractTarget.binding.target_ref}`);
106
+ for (const capability of blocker.required_capabilities)
107
+ if (!executionTarget.capabilities.includes(capability))
108
+ invalid("acceptance_blocker_target_capability_missing", `${blocker.key}:${executionTarget.key}:${capability}`);
105
109
  const expectedLocal = fullClaimRefs.map((claimRef) => claimRef.slice(contractTarget.outcome_key.length + 1));
106
110
  for (const claimRef of expectedLocal)
107
111
  if (!contractBlocker.refs.includes(claimRef))
@@ -156,14 +160,51 @@ function validateVerificationMethodBindings(target, check, rows, claimsBySourceI
156
160
  for (const capability of requiredCapabilities(binding.method))
157
161
  if (!assertion.evidence_capabilities.includes(capability))
158
162
  invalid("verification_method_capability_required", `${target.key}:${binding.method}:${capability}`);
163
+ assertSameSet(binding.evidence_artifacts.map((item) => item.condition_key), target.condition_keys, "verification_method_evidence_conditions_mismatch", `${target.key}:${binding.method}`);
159
164
  }
160
165
  }
161
166
  function requiredCapabilities(method) {
162
167
  if (method === "interaction_trace")
163
- return ["interaction_trace", "target_runtime"];
168
+ return ["design_method", "interaction_trace", "target_runtime"];
164
169
  if (method === "component_state")
165
- return ["design_conformance", "interaction_trace", "target_runtime"];
166
- return ["design_conformance", "target_runtime"];
170
+ return [
171
+ "design_method",
172
+ "design_conformance",
173
+ "interaction_trace",
174
+ "target_runtime",
175
+ ];
176
+ return ["design_method", "design_conformance", "target_runtime"];
177
+ }
178
+ function validateTargetCapabilities(contract, contractTarget, indexed) {
179
+ const executionTarget = contract.task.execution_targets.find((item) => item.key === contractTarget.binding.target_ref);
180
+ if (!executionTarget)
181
+ invalid("execution_target_unknown", `${contractTarget.target.key}:${contractTarget.binding.target_ref}`);
182
+ const required = new Set();
183
+ for (const method of contractTarget.target.verification_method_bindings.map((item) => item.method)) {
184
+ if (method === "responsive_reflow")
185
+ required.add("viewport-control");
186
+ if (method === "motion_timeline")
187
+ required.add("motion-observation");
188
+ if (method === "accessibility_semantics")
189
+ required.add("assistive-technology");
190
+ }
191
+ for (const conditionRef of indexed.target.condition_refs) {
192
+ const condition = indexed.preflight.handoff.conditions.find((item) => item.key === conditionRef);
193
+ const input = condition.input_methods[0].toLowerCase();
194
+ if (["mouse", "pointer", "trackpad"].includes(input))
195
+ required.add("pointer-input");
196
+ if (input === "keyboard")
197
+ required.add("keyboard-input");
198
+ if (["touch", "pen", "stylus"].includes(input))
199
+ required.add("touch-input");
200
+ if (["screen-reader", "voice-control", "switch-control"].includes(input))
201
+ required.add("assistive-technology");
202
+ if (condition.motion === "reduced")
203
+ required.add("reduced-motion");
204
+ }
205
+ for (const capability of required)
206
+ if (!executionTarget.capabilities.includes(capability))
207
+ invalid("execution_target_capability_missing", `${contractTarget.target.key}:${executionTarget.key}:${capability}`);
167
208
  }
168
209
  function assertSameSet(actual, expected, code, detail) {
169
210
  const left = [...new Set(actual)].sort();
@@ -1,3 +1,4 @@
1
+ import { DESIGN_RESOURCE_VERIFICATION_METHODS } from "./design-resource-handoff-types.js";
1
2
  export function decodeEvidenceCapabilityRecords(value) {
2
3
  if (!Array.isArray(value))
3
4
  throw invalidRecord("must_be_array");
@@ -179,6 +180,36 @@ function decodeRecord(value, index) {
179
180
  session_id: nonEmpty(row.session_id, `${label}.session_id`),
180
181
  cold_start: row.cold_start,
181
182
  };
183
+ case "design_method":
184
+ exact(row, label, [
185
+ "assertion_key",
186
+ "capability",
187
+ "design_target_ref",
188
+ "target_ref",
189
+ "method",
190
+ "cells",
191
+ ]);
192
+ return {
193
+ ...base,
194
+ capability,
195
+ design_target_ref: key(row.design_target_ref, `${label}.design_target_ref`),
196
+ target_ref: key(row.target_ref, `${label}.target_ref`),
197
+ method: literal(row.method, DESIGN_RESOURCE_VERIFICATION_METHODS, `${label}.method`),
198
+ cells: array(row.cells, `${label}.cells`).map((item, cellIndex) => {
199
+ const cellLabel = `${label}.cells[${cellIndex}]`;
200
+ const cell = record(item, cellLabel);
201
+ exact(cell, cellLabel, [
202
+ "condition_key",
203
+ "artifact_path",
204
+ "observation_artifact_path",
205
+ ]);
206
+ return {
207
+ condition_key: key(cell.condition_key, `${cellLabel}.condition_key`),
208
+ artifact_path: nonEmpty(cell.artifact_path, `${cellLabel}.artifact_path`),
209
+ observation_artifact_path: nonEmpty(cell.observation_artifact_path, `${cellLabel}.observation_artifact_path`),
210
+ };
211
+ }),
212
+ };
182
213
  case "input_variation":
183
214
  exact(row, label, [
184
215
  "assertion_key",
@@ -248,3 +279,9 @@ function sha(value, label) {
248
279
  throw invalidRecord(label);
249
280
  return result;
250
281
  }
282
+ function literal(value, allowed, label) {
283
+ const result = nonEmpty(value, label);
284
+ if (!allowed.includes(result))
285
+ throw invalidRecord(label);
286
+ return result;
287
+ }
@@ -4,47 +4,56 @@ export { decodeEvidenceCapabilityRecords } from "./long-task-evidence-capability
4
4
  export function validateEvidenceCapabilityDeclarations(contract, report) {
5
5
  const targets = new Map(contract.task.execution_targets.map((target) => [target.key, target]));
6
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("design_conformance") &&
30
- !check.artifact_globs.length)
31
- issue(report, "design_conformance_artifact_required", label);
32
- if (assertion.evidence_capabilities.includes("interaction_trace") &&
33
- !check.scenario.when.length)
34
- issue(report, "interaction_trace_actions_required", label);
35
- if (check.runner.type === "playwright_test" &&
36
- assertion.evidence_capabilities.some((capability) => capability !== "presence" &&
37
- capability !== "interaction_trace" &&
38
- capability !== "design_conformance" &&
39
- capability !== "target_runtime"))
40
- issue(report, "playwright_evidence_capability_unsupported", label);
41
- if (["boundary_invocation", "external_side_effect"].some((capability) => assertion.evidence_capabilities.includes(capability)) &&
42
- targets.get(check.execution_target.target_ref)?.role !== "observer")
43
- issue(report, "observer_check_target_required", label);
44
- }
7
+ validateCheckEvidenceCapabilityDeclarations(check, outcomeKey, targets, report);
45
8
  }
46
9
  validateJourneySeparation(contract, report);
47
10
  }
11
+ function validateCheckEvidenceCapabilityDeclarations(check, outcomeKey, targets, report) {
12
+ const label = checkLabel(outcomeKey, check.key);
13
+ unique(check.journey_roles, "journey_role_duplicate", label, report);
14
+ if (!check.journey_roles.length)
15
+ issue(report, "journey_role_required", label);
16
+ validateScenario(check, outcomeKey, report);
17
+ for (const assertion of [
18
+ ...check.positive_assertions,
19
+ ...check.negative_assertions,
20
+ ])
21
+ validateAssertionEvidenceCapabilityDeclarations(assertion, check, targets, `${label}:${assertion.key}`, report);
22
+ }
23
+ function validateAssertionEvidenceCapabilityDeclarations(assertion, check, targets, label, report) {
24
+ unique(assertion.evidence_capabilities, "evidence_capability_duplicate", label, report);
25
+ if (!assertion.evidence_capabilities.length)
26
+ issue(report, "evidence_capability_required", label);
27
+ if (assertion.claims.length &&
28
+ check.proof_surface !== "implementation_structure" &&
29
+ assertion.evidence_capabilities.every((capability) => capability === "presence"))
30
+ issue(report, "presence_cannot_prove_behavior", label);
31
+ if (assertion.operator === "exists" &&
32
+ !assertion.evidence_capabilities.includes("presence"))
33
+ issue(report, "exists_requires_presence_capability", label);
34
+ if (assertion.evidence_capabilities.includes("visual_render") &&
35
+ !check.artifact_globs.length)
36
+ issue(report, "visual_render_artifact_required", label);
37
+ if (assertion.evidence_capabilities.includes("design_conformance") &&
38
+ !check.artifact_globs.length)
39
+ issue(report, "design_conformance_artifact_required", label);
40
+ if (assertion.evidence_capabilities.includes("design_method") &&
41
+ !check.artifact_globs.length)
42
+ issue(report, "design_method_artifact_required", label);
43
+ if (assertion.evidence_capabilities.includes("interaction_trace") &&
44
+ !check.scenario.when.length)
45
+ issue(report, "interaction_trace_actions_required", label);
46
+ if (check.runner.type === "playwright_test" &&
47
+ assertion.evidence_capabilities.some((capability) => capability !== "presence" &&
48
+ capability !== "interaction_trace" &&
49
+ capability !== "design_conformance" &&
50
+ capability !== "design_method" &&
51
+ capability !== "target_runtime"))
52
+ issue(report, "playwright_evidence_capability_unsupported", label);
53
+ if (["boundary_invocation", "external_side_effect"].some((capability) => assertion.evidence_capabilities.includes(capability)) &&
54
+ targets.get(check.execution_target.target_ref)?.role !== "observer")
55
+ issue(report, "observer_check_target_required", label);
56
+ }
48
57
  function validateScenario(check, outcomeKey, report) {
49
58
  const label = checkLabel(outcomeKey, check.key);
50
59
  if (!check.scenario.given.length)
@@ -127,8 +136,41 @@ export function evaluateEvidenceCapabilities(check, records, artifactHashes) {
127
136
  }
128
137
  complete[assertion.key] = assertionComplete;
129
138
  }
139
+ validateDistinctDesignMethodArtifacts(check, runtimeRecords, artifactHashes, findings);
130
140
  return { complete, findings };
131
141
  }
142
+ function validateDistinctDesignMethodArtifacts(check, records, artifactHashes, findings) {
143
+ const pathOwners = new Map();
144
+ const digestOwners = new Map();
145
+ for (const record of records) {
146
+ if (record.capability !== "design_method")
147
+ continue;
148
+ for (const cell of record.cells) {
149
+ const currentOwner = `${record.method}:${cell.condition_key}`;
150
+ const pathOwner = pathOwners.get(cell.observation_artifact_path);
151
+ if (pathOwner)
152
+ findings.push(reusedDesignMethodArtifactFinding(check, record.assertion_key, currentOwner, pathOwner, "path", cell.observation_artifact_path));
153
+ else
154
+ pathOwners.set(cell.observation_artifact_path, currentOwner);
155
+ const digest = artifactHashes[cell.observation_artifact_path];
156
+ if (!digest)
157
+ continue;
158
+ const digestOwner = digestOwners.get(digest);
159
+ if (digestOwner)
160
+ findings.push(reusedDesignMethodArtifactFinding(check, record.assertion_key, currentOwner, digestOwner, "sha256", digest));
161
+ else
162
+ digestOwners.set(digest, currentOwner);
163
+ }
164
+ }
165
+ }
166
+ function reusedDesignMethodArtifactFinding(check, assertionKey, currentOwner, previousOwner, identityKind, identity) {
167
+ return {
168
+ ...checkFinding(check, "design_method_evidence_reused", `Design-method evidence ${identityKind} ${identity} is reused by ${previousOwner} and ${currentOwner}.`, "Emit method-and-condition evidence whose path and current content digest are both distinct for every verification-method cell."),
169
+ assertion_key: assertionKey,
170
+ expected: "distinct_method_condition_artifact_path_and_content",
171
+ actual: { identity_kind: identityKind, identity },
172
+ };
173
+ }
132
174
  function allChecks(contract) {
133
175
  return [
134
176
  ...contract.global.acceptance.checks.map((check) => [null, check]),
@@ -27,12 +27,48 @@ export function validateRuntimeEvidenceRecord(check, record, artifactHashes) {
27
27
  : "artifact_hash_mismatch";
28
28
  case "design_conformance":
29
29
  return validateDesignConformance(check, record, artifactHashes);
30
+ case "design_method":
31
+ return validateDesignMethod(check, record, artifactHashes);
30
32
  case "target_runtime":
31
33
  return validateTargetRuntime(check, record);
32
34
  case "input_variation":
33
35
  return validateInputVariation(record);
34
36
  }
35
37
  }
38
+ function validateDesignMethod(check, record, artifactHashes) {
39
+ const target = (check.design_conformance_targets ?? []).find((item) => item.key === record.design_target_ref &&
40
+ item.verification_method_bindings.some((binding) => binding.assertion_ref === record.assertion_key &&
41
+ binding.method === record.method));
42
+ if (!target)
43
+ return "design_method_binding_unknown";
44
+ if (target.target_ref !== record.target_ref ||
45
+ target.target_ref !== check.execution_target.target_ref)
46
+ return "target_mismatch";
47
+ const binding = target.verification_method_bindings.find((item) => item.assertion_ref === record.assertion_key &&
48
+ item.method === record.method);
49
+ if (!same([...record.cells.map((item) => item.condition_key)].sort(), [...target.condition_keys].sort()))
50
+ return "design_method_conditions_mismatch";
51
+ const expected = new Map(binding.evidence_artifacts.map((item) => [
52
+ item.condition_key,
53
+ {
54
+ artifact_path: item.path,
55
+ observation_artifact_path: item.observation_path,
56
+ },
57
+ ]));
58
+ if (record.cells.length !== expected.size)
59
+ return "design_method_cell_count_mismatch";
60
+ for (const cell of record.cells) {
61
+ const expectedCell = expected.get(cell.condition_key);
62
+ if (expectedCell?.artifact_path !== cell.artifact_path ||
63
+ expectedCell?.observation_artifact_path !== cell.observation_artifact_path)
64
+ return "design_method_artifact_path_mismatch";
65
+ if (!artifactHashes[cell.artifact_path])
66
+ return "design_method_artifact_missing";
67
+ if (!artifactHashes[cell.observation_artifact_path])
68
+ return "design_method_observation_artifact_missing";
69
+ }
70
+ return null;
71
+ }
36
72
  function validateDesignConformance(check, record, artifactHashes) {
37
73
  const target = (check.design_conformance_targets ?? []).find((item) => item.key === record.design_target_ref &&
38
74
  designTargetUsesAssertion(item, record.assertion_key));
@@ -1,4 +1,5 @@
1
1
  import type { EvidenceCapabilityV2 } from "./long-task-semantic-contract-types.js";
2
+ import type { DesignResourceVerificationMethod } from "./design-resource-handoff-types.js";
2
3
  interface EvidenceRecordBaseV2 {
3
4
  assertion_key: string;
4
5
  capability: EvidenceCapabilityV2;
@@ -70,6 +71,17 @@ export interface TargetRuntimeEvidenceV2 extends EvidenceRecordBaseV2 {
70
71
  session_id: string;
71
72
  cold_start: boolean;
72
73
  }
74
+ export interface DesignMethodEvidenceV2 extends EvidenceRecordBaseV2 {
75
+ capability: "design_method";
76
+ design_target_ref: string;
77
+ target_ref: string;
78
+ method: DesignResourceVerificationMethod;
79
+ cells: Array<{
80
+ condition_key: string;
81
+ artifact_path: string;
82
+ observation_artifact_path: string;
83
+ }>;
84
+ }
73
85
  export interface InputVariationEvidenceV2 extends EvidenceRecordBaseV2 {
74
86
  capability: "input_variation";
75
87
  cases: Array<{
@@ -78,5 +90,5 @@ export interface InputVariationEvidenceV2 extends EvidenceRecordBaseV2 {
78
90
  }>;
79
91
  failure_case_observed: boolean;
80
92
  }
81
- export type EvidenceCapabilityRecordV2 = InteractionTraceEvidenceV2 | StateDeltaEvidenceV2 | CrossSurfaceConsistencyEvidenceV2 | DurableReadbackEvidenceV2 | BoundaryInvocationEvidenceV2 | ExternalSideEffectEvidenceV2 | FailureInjectionEvidenceV2 | VisualRenderEvidenceV2 | DesignConformanceEvidenceV2 | TargetRuntimeEvidenceV2 | InputVariationEvidenceV2;
93
+ export type EvidenceCapabilityRecordV2 = InteractionTraceEvidenceV2 | StateDeltaEvidenceV2 | CrossSurfaceConsistencyEvidenceV2 | DurableReadbackEvidenceV2 | BoundaryInvocationEvidenceV2 | ExternalSideEffectEvidenceV2 | FailureInjectionEvidenceV2 | VisualRenderEvidenceV2 | DesignConformanceEvidenceV2 | DesignMethodEvidenceV2 | TargetRuntimeEvidenceV2 | InputVariationEvidenceV2;
82
94
  export {};
@@ -18,6 +18,9 @@ export function evaluateAssertionResults(check, observations) {
18
18
  polarity,
19
19
  passed: evaluation.passed,
20
20
  claims: assertion.claims,
21
+ ...(assertion.applicability_ref
22
+ ? { applicability_ref: assertion.applicability_ref }
23
+ : {}),
21
24
  observation: assertion.observation,
22
25
  evidence_capabilities: assertion.evidence_capabilities,
23
26
  evidence_complete: false,