project-tiny-context-harness 0.7.8 → 0.8.0

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 (150) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +393 -375
  3. package/assets/README.md +567 -552
  4. package/assets/README.zh-CN.md +339 -321
  5. package/assets/agents/.gitkeep +1 -1
  6. package/assets/agents/AGENTS_CORE.md +66 -58
  7. package/assets/context_templates/architecture.md +33 -33
  8. package/assets/context_templates/area.md +39 -39
  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 +52 -51
  12. package/assets/context_templates/product-surface-contract.md +70 -70
  13. package/assets/context_templates/screen-contract.md +186 -177
  14. package/assets/context_templates/verification.md +32 -32
  15. package/assets/github/.gitkeep +1 -1
  16. package/assets/github/harness.yml +41 -41
  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 +135 -112
  20. package/assets/skills/context_full_project_export/SKILL.md +70 -70
  21. package/assets/skills/context_harness_upgrade/SKILL.md +60 -60
  22. package/assets/skills/context_product_plan/SKILL.md +88 -87
  23. package/assets/skills/context_surface_contract/SKILL.md +191 -191
  24. package/assets/skills/context_uiux_design/SKILL.md +158 -154
  25. package/assets/skills/design-resource-authoring/SKILL.md +84 -79
  26. package/assets/skills/design-resource-authoring/references/downstream-handoff.md +136 -108
  27. package/assets/skills/design-resource-authoring/references/open-design-provider.md +133 -127
  28. package/assets/skills/design-resource-authoring/references/resource-selection.md +173 -173
  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 -47
  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 +100 -87
  34. package/assets/skills/long-task-workflow/agents/openai.yaml +4 -4
  35. package/assets/skills/long-task-workflow/references/authority-lifecycle.md +76 -59
  36. package/assets/skills/long-task-workflow/references/contract-authoring.md +116 -103
  37. package/assets/skills/long-task-workflow/references/evidence-design.md +78 -75
  38. package/assets/skills/long-task-workflow/references/source-authoring.md +101 -98
  39. package/assets/skills/normal-long-task/SKILL.md +12 -12
  40. package/assets/skills/source-plan-authoring/SKILL.md +14 -14
  41. package/assets/tools/validate_context.py +442 -442
  42. package/dist/commands/design-resource.d.ts +1 -0
  43. package/dist/commands/design-resource.js +32 -0
  44. package/dist/commands/index.js +4 -0
  45. package/dist/commands/long-task-command-args.d.ts +1 -0
  46. package/dist/commands/long-task-command-args.js +6 -0
  47. package/dist/commands/long-task-revision.js +16 -4
  48. package/dist/commands/long-task.js +12 -5
  49. package/dist/index.d.ts +1 -0
  50. package/dist/index.js +1 -0
  51. package/dist/lib/design-md.js +3 -1
  52. package/dist/lib/design-resource-handoff-file-primitives.d.ts +4 -0
  53. package/dist/lib/design-resource-handoff-file-primitives.js +14 -0
  54. package/dist/lib/design-resource-handoff-file-validation.d.ts +2 -0
  55. package/dist/lib/design-resource-handoff-file-validation.js +142 -0
  56. package/dist/lib/design-resource-handoff-parser.d.ts +3 -0
  57. package/dist/lib/design-resource-handoff-parser.js +29 -0
  58. package/dist/lib/design-resource-handoff-policy.d.ts +3 -0
  59. package/dist/lib/design-resource-handoff-policy.js +36 -0
  60. package/dist/lib/design-resource-handoff-shape-evidence.d.ts +4 -0
  61. package/dist/lib/design-resource-handoff-shape-evidence.js +84 -0
  62. package/dist/lib/design-resource-handoff-shape-primitives.d.ts +9 -0
  63. package/dist/lib/design-resource-handoff-shape-primitives.js +44 -0
  64. package/dist/lib/design-resource-handoff-shape-structure.d.ts +5 -0
  65. package/dist/lib/design-resource-handoff-shape-structure.js +118 -0
  66. package/dist/lib/design-resource-handoff-shape.d.ts +2 -0
  67. package/dist/lib/design-resource-handoff-shape.js +75 -0
  68. package/dist/lib/design-resource-handoff-types.d.ts +139 -0
  69. package/dist/lib/design-resource-handoff-types.js +46 -0
  70. package/dist/lib/design-resource-handoff-validation-coverage.d.ts +4 -0
  71. package/dist/lib/design-resource-handoff-validation-coverage.js +168 -0
  72. package/dist/lib/design-resource-handoff-validation-primitives.d.ts +11 -0
  73. package/dist/lib/design-resource-handoff-validation-primitives.js +26 -0
  74. package/dist/lib/design-resource-handoff-validation-structure.d.ts +6 -0
  75. package/dist/lib/design-resource-handoff-validation-structure.js +93 -0
  76. package/dist/lib/design-resource-handoff-validation.d.ts +3 -0
  77. package/dist/lib/design-resource-handoff-validation.js +78 -0
  78. package/dist/lib/design-resource-handoff-web-dependency-validation.d.ts +3 -0
  79. package/dist/lib/design-resource-handoff-web-dependency-validation.js +70 -0
  80. package/dist/lib/long-task-activation-validation.js +14 -2
  81. package/dist/lib/long-task-authoring-preflight-diagnostics.js +7 -0
  82. package/dist/lib/long-task-authoring-preflight.js +39 -1
  83. package/dist/lib/long-task-authority-material-diff.js +14 -0
  84. package/dist/lib/long-task-authority-materials.js +21 -0
  85. package/dist/lib/long-task-authority-policy.d.ts +33 -0
  86. package/dist/lib/long-task-authority-policy.js +36 -0
  87. package/dist/lib/long-task-authority-revision-analysis.d.ts +1 -0
  88. package/dist/lib/long-task-authority-revision-analysis.js +30 -0
  89. package/dist/lib/long-task-authority-revision-brief.d.ts +3 -0
  90. package/dist/lib/long-task-authority-revision-brief.js +74 -0
  91. package/dist/lib/long-task-authority-revision-details.js +16 -16
  92. package/dist/lib/long-task-authority-revision-diagnosis.d.ts +1 -1
  93. package/dist/lib/long-task-authority-revision-diagnosis.js +8 -0
  94. package/dist/lib/long-task-authority-revision-enforcement.js +7 -3
  95. package/dist/lib/long-task-authority-revision-summary.d.ts +3 -0
  96. package/dist/lib/long-task-authority-revision-summary.js +133 -21
  97. package/dist/lib/long-task-authority-revision-types.d.ts +19 -1
  98. package/dist/lib/long-task-authority-revision.js +5 -0
  99. package/dist/lib/long-task-authority-types.d.ts +2 -0
  100. package/dist/lib/long-task-authority.js +10 -2
  101. package/dist/lib/long-task-check-execution-policy.js +2 -0
  102. package/dist/lib/long-task-claim-definitions.js +1 -5
  103. package/dist/lib/long-task-contract-types.d.ts +2 -0
  104. package/dist/lib/long-task-counterfactual-sandbox.d.ts +7 -0
  105. package/dist/lib/long-task-counterfactual-sandbox.js +27 -1
  106. package/dist/lib/long-task-delivery-compiler.js +26 -8
  107. package/dist/lib/long-task-delivery-types.d.ts +1 -0
  108. package/dist/lib/long-task-delivery-types.js +1 -0
  109. package/dist/lib/long-task-delivery-validation.js +3 -0
  110. package/dist/lib/long-task-design-resource-handoff.d.ts +2 -0
  111. package/dist/lib/long-task-design-resource-handoff.js +177 -0
  112. package/dist/lib/long-task-evidence-capability-codec.js +19 -0
  113. package/dist/lib/long-task-evidence-capability-policy.js +4 -0
  114. package/dist/lib/long-task-evidence-capability-runtime.js +25 -0
  115. package/dist/lib/long-task-evidence-capability-types.d.ts +9 -1
  116. package/dist/lib/long-task-outcome-parser.js +11 -1
  117. package/dist/lib/long-task-paths.d.ts +0 -1
  118. package/dist/lib/long-task-paths.js +0 -4
  119. package/dist/lib/long-task-playwright-case-evidence.d.ts +43 -0
  120. package/dist/lib/long-task-playwright-case-evidence.js +152 -0
  121. package/dist/lib/long-task-playwright-evidence.js +19 -152
  122. package/dist/lib/long-task-runner-freeze.d.ts +2 -2
  123. package/dist/lib/long-task-runner-freeze.js +2 -1
  124. package/dist/lib/long-task-runtime-types.d.ts +2 -0
  125. package/dist/lib/long-task-semantic-contract-types.d.ts +1 -1
  126. package/dist/lib/long-task-semantic-drift-migration.js +6 -1
  127. package/dist/lib/long-task-shape-primitives.d.ts +1 -1
  128. package/dist/lib/long-task-shape-primitives.js +1 -0
  129. package/dist/lib/long-task-status-v2.js +12 -7
  130. package/dist/lib/long-task-ui-design-policy.d.ts +17 -0
  131. package/dist/lib/long-task-ui-design-policy.js +128 -0
  132. package/dist/lib/long-task-ui-surface-policy.d.ts +4 -0
  133. package/dist/lib/long-task-ui-surface-policy.js +109 -0
  134. package/dist/lib/long-task-ui-surface-shape.d.ts +2 -0
  135. package/dist/lib/long-task-ui-surface-shape.js +94 -0
  136. package/dist/lib/long-task-ui-surface-types.d.ts +44 -0
  137. package/dist/lib/long-task-ui-surface-types.js +1 -0
  138. package/dist/lib/long-task-ui-surface-validation.d.ts +11 -0
  139. package/dist/lib/long-task-ui-surface-validation.js +62 -0
  140. package/dist/lib/long-task-verification-preview.d.ts +55 -0
  141. package/dist/lib/long-task-verification-preview.js +120 -0
  142. package/dist/lib/long-task-verifier-v2.js +11 -18
  143. package/dist/lib/long-task-workspace-scope.d.ts +38 -0
  144. package/dist/lib/long-task-workspace-scope.js +121 -0
  145. package/dist/lib/long-task-workspace.d.ts +1 -0
  146. package/dist/lib/long-task-workspace.js +73 -9
  147. package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +84 -2
  148. package/migrations/README.md +7 -7
  149. package/package.json +3 -3
  150. package/source-mappings.yaml +25 -25
@@ -9,7 +9,8 @@ import { assertRiskNotDowngraded, buildAuthorityRevisionProposal, enforceAuthori
9
9
  import { validateActualRiskSurfaces } from "./long-task-risk-surfaces.js";
10
10
  import { captureVerifierIdentity } from "./long-task-verifier-identity.js";
11
11
  import { verifierAuthorityDiff } from "./long-task-verifier-authority.js";
12
- import { changedWorkspacePaths, currentGitTree, repoRelative, repositoryRoot, } from "./long-task-workspace.js";
12
+ import { changedWorkspacePaths, changedWorkspacePathsFromHead, currentGitTree, repoRelative, repositoryRoot, } from "./long-task-workspace.js";
13
+ import { assertWorkspaceScope, classifyWorkspaceScope, firstLockManagedWorkspacePaths, protectedWorkspacePaths, } from "./long-task-workspace-scope.js";
13
14
  export async function compileDeliveryContract(workdirInput, projectRootInput = process.cwd(), options = {}) {
14
15
  const repository = await repositoryRoot(projectRootInput);
15
16
  const workdir = path.resolve(workdirInput);
@@ -35,18 +36,38 @@ export async function compileDeliveryContract(workdirInput, projectRootInput = p
35
36
  const current = validation.workspace;
36
37
  const globalChecks = validation.global_checks;
37
38
  const outcomes = validation.outcomes;
39
+ const contractFiles = Object.fromEntries(Object.entries(parsed.contract_files).map(([relative, hash]) => [
40
+ repoRelative(repository, path.join(workdir, ...relative.split("/"))),
41
+ hash,
42
+ ]));
38
43
  const contractHash = sha256Hex(canonicalValueJson(contract));
39
44
  const authorityMaterials = computeAuthorityMaterials(contract, sourceHashes, sourceItems, context);
40
45
  const previous = options.live_gate
41
46
  ? null
42
47
  : (options.previous_authority ?? null);
43
- const initialTaskBase = options.initial_task_base ??
44
- previous?.initial_task_base ?? {
48
+ const existingInitialTaskBase = options.initial_task_base ?? previous?.initial_task_base ?? null;
49
+ const changedPaths = existingInitialTaskBase
50
+ ? changedWorkspacePaths(existingInitialTaskBase.workspace_manifest, current)
51
+ : await changedWorkspacePathsFromHead(repository, workdir);
52
+ const firstLockManagedPaths = existingInitialTaskBase
53
+ ? []
54
+ : await firstLockManagedWorkspacePaths(repository, changedPaths);
55
+ assertWorkspaceScope(classifyWorkspaceScope(contract, changedPaths, protectedWorkspacePaths({
56
+ contract_files: contractFiles,
57
+ source_hashes: sourceHashes,
58
+ context_hashes: context.sha256,
59
+ checks: [
60
+ ...globalChecks,
61
+ ...outcomes.flatMap((outcome) => outcome.acceptance.checks),
62
+ ],
63
+ additional_files: firstLockManagedPaths,
64
+ })));
65
+ const initialTaskBase = existingInitialTaskBase ?? {
45
66
  git_commit: current.git_head,
46
67
  git_tree: await currentGitTree(repository),
47
68
  workspace_manifest: current,
48
69
  };
49
- await validateActualRiskSurfaces(repository, changedWorkspacePaths(initialTaskBase.workspace_manifest, current), contract);
70
+ await validateActualRiskSurfaces(repository, changedPaths, contract);
50
71
  const verifier = await captureVerifierIdentity(repository, options.require_completion_gate !== false);
51
72
  const allChecks = [
52
73
  ...globalChecks,
@@ -95,10 +116,7 @@ export async function compileDeliveryContract(workdirInput, projectRootInput = p
95
116
  workdir,
96
117
  contract_file: repoRelative(repository, contractFile),
97
118
  contract_sha256: contractHash,
98
- contract_files: Object.fromEntries(Object.entries(parsed.contract_files).map(([relative, hash]) => [
99
- repoRelative(repository, path.join(workdir, ...relative.split("/"))),
100
- hash,
101
- ])),
119
+ contract_files: contractFiles,
102
120
  source_hashes: sourceHashes,
103
121
  source_items: sourceItems,
104
122
  context_snapshot: normalizeContextAuthoritySnapshot(context),
@@ -6,5 +6,6 @@ export * from "./long-task-evidence-capability-types.js";
6
6
  export * from "./long-task-risk-types.js";
7
7
  export * from "./long-task-runtime-types.js";
8
8
  export * from "./long-task-semantic-contract-types.js";
9
+ export * from "./long-task-ui-surface-types.js";
9
10
  export * from "./long-task-source-authority-types.js";
10
11
  export * from "./long-task-workspace-runtime-types.js";
@@ -6,5 +6,6 @@ export * from "./long-task-evidence-capability-types.js";
6
6
  export * from "./long-task-risk-types.js";
7
7
  export * from "./long-task-runtime-types.js";
8
8
  export * from "./long-task-semantic-contract-types.js";
9
+ export * from "./long-task-ui-surface-types.js";
9
10
  export * from "./long-task-source-authority-types.js";
10
11
  export * from "./long-task-workspace-runtime-types.js";
@@ -7,6 +7,7 @@ import { validateEvidenceAdapterCompatibility } from "./long-task-evidence-adapt
7
7
  import { validateEvidenceCapabilityDeclarations } from "./long-task-evidence-capability-policy.js";
8
8
  import { validateDeliveryStages } from "./long-task-stage-policy.js";
9
9
  import { validateExecutionTargets, validateExternalConfirmationImpacts, } from "./long-task-target-policy.js";
10
+ import { validateUiSurfaceBindings } from "./long-task-ui-surface-policy.js";
10
11
  export function validateDeliveryContractStructure(contract) {
11
12
  validateUniqueKeys(contract);
12
13
  validateDependencies(contract);
@@ -17,6 +18,7 @@ export function validateDeliveryContractStructure(contract) {
17
18
  allow_uncovered: true,
18
19
  });
19
20
  validateSourceClaimMappings(contract, claims);
21
+ validateUiSurfaceBindings(contract, claims);
20
22
  assertCompiledClaimsCovered(claims);
21
23
  validateDeliveryStages(contract);
22
24
  validateExecutionTargets(contract);
@@ -40,6 +42,7 @@ export function deliveryContractStructureDiagnostics(contract) {
40
42
  });
41
43
  if (claims) {
42
44
  validateSourceClaimMappings(contract, claims, report);
45
+ validateUiSurfaceBindings(contract, claims, report);
43
46
  validateExternalConfirmationImpacts(contract, claims, report);
44
47
  capture(diagnostics, () => assertCompiledClaimsCovered(claims));
45
48
  }
@@ -0,0 +1,2 @@
1
+ import type { DeliveryContractV2 } from "./long-task-delivery-types.js";
2
+ export declare function validateLongTaskDesignResourceHandoffs(contract: DeliveryContractV2, repository: string): Promise<void>;
@@ -0,0 +1,177 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { containsDesignResourceHandoff } from "./design-resource-handoff-parser.js";
4
+ import { preflightDesignResourceHandoff } from "./design-resource-handoff-validation.js";
5
+ import { assertProtectedRepositoryFile } from "./long-task-protected-files.js";
6
+ export async function validateLongTaskDesignResourceHandoffs(contract, repository) {
7
+ const handoffs = await loadHandoffs(contract, repository);
8
+ const indexed = indexHandoffTargets(handoffs);
9
+ const contractTargets = contract.outcomes.flatMap((outcome) => outcome.product.surface_bindings.flatMap((binding) => binding.design_targets.map((target) => ({
10
+ outcome_key: outcome.key,
11
+ binding,
12
+ target,
13
+ }))));
14
+ const consumed = new Set();
15
+ for (const contractTarget of contractTargets) {
16
+ const indexedTarget = indexed.get(contractTarget.target.key);
17
+ if (!indexedTarget)
18
+ invalid("target_handoff_missing", contractTarget.target.key);
19
+ consumed.add(contractTarget.target.key);
20
+ validateTargetIdentity(contractTarget, indexedTarget);
21
+ validateCoverageClaims(contract, contractTarget, indexedTarget);
22
+ validateBlockerBindings(contract, contractTarget, indexedTarget);
23
+ }
24
+ for (const key of indexed.keys())
25
+ if (!consumed.has(key))
26
+ invalid("handoff_target_unbound", key);
27
+ }
28
+ async function loadHandoffs(contract, repository) {
29
+ const results = [];
30
+ for (const sourcePath of contract.task.source_paths) {
31
+ if (!sourcePath.toLowerCase().endsWith(".md"))
32
+ continue;
33
+ const file = await assertProtectedRepositoryFile(repository, path.resolve(repository, ...sourcePath.split("/")), `design_resource_handoff_source:${sourcePath}`);
34
+ const content = await readFile(file, "utf8");
35
+ if (containsDesignResourceHandoff(content))
36
+ results.push(await preflightDesignResourceHandoff(repository, sourcePath));
37
+ }
38
+ return results;
39
+ }
40
+ function indexHandoffTargets(handoffs) {
41
+ const indexed = new Map();
42
+ for (const preflight of handoffs)
43
+ for (const target of preflight.handoff.targets) {
44
+ if (indexed.has(target.key))
45
+ invalid("handoff_target_duplicate", target.key);
46
+ indexed.set(target.key, { preflight, target });
47
+ }
48
+ return indexed;
49
+ }
50
+ function validateTargetIdentity(contractTarget, indexed) {
51
+ const { target } = contractTarget;
52
+ const handoffTarget = indexed.target;
53
+ if (target.interpretation !== handoffTarget.interpretation)
54
+ invalid("target_interpretation_mismatch", `${target.key}:${target.interpretation}:${handoffTarget.interpretation}`);
55
+ assertSameSet(target.condition_keys, handoffTarget.condition_refs, "target_conditions_mismatch", target.key);
56
+ const resourcePaths = handoffTarget.resource_refs.map((ref) => indexed.preflight.handoff.resources.find((item) => item.key === ref)
57
+ .path);
58
+ assertSameSet(target.source_paths, [indexed.preflight.handoff_path, ...resourcePaths], "target_source_paths_mismatch", target.key);
59
+ }
60
+ function validateCoverageClaims(contract, contractTarget, indexed) {
61
+ const target = contractTarget.target;
62
+ const check = contract.outcomes
63
+ .find((item) => item.key === contractTarget.outcome_key)
64
+ .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
+ const claims = new Map(contract.source_claims.map((item) => [item.key, item]));
67
+ const rows = indexed.preflight.handoff.coverage.filter((row) => row.disposition === "covered" && row.target_refs.includes(target.key));
68
+ if (!rows.length)
69
+ invalid("target_covered_claims_required", target.key);
70
+ const claimsBySourceItem = new Map();
71
+ for (const row of rows)
72
+ for (const sourceItemRef of row.source_item_refs) {
73
+ const localClaims = designSourceItemClaims(contract, contractTarget, indexed, sourceItemRef, claims);
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
+ }
79
+ validateVerificationMethodBindings(target, check, rows, claimsBySourceItem);
80
+ }
81
+ function validateBlockerBindings(contract, contractTarget, indexed) {
82
+ const bound = new Map(contractTarget.binding.acceptance_blockers.map((item) => [item.key, item]));
83
+ const claims = new Map(contract.source_claims.map((item) => [item.key, item]));
84
+ const confirmations = new Map(contract.global.acceptance.external_confirmations.map((item) => [
85
+ item.key,
86
+ item,
87
+ ]));
88
+ const availableMethods = new Set(contractTarget.target.verification_method_bindings.map((item) => item.method));
89
+ for (const blocker of indexed.preflight.handoff.acceptance_blockers) {
90
+ if (!blocker.target_refs.includes(contractTarget.target.key))
91
+ continue;
92
+ const contractBlocker = bound.get(blocker.key);
93
+ if (!contractBlocker)
94
+ invalid("acceptance_blocker_unbound", `${contractTarget.target.key}:${blocker.key}`);
95
+ assertSameSet(contractBlocker.source_item_refs, blocker.source_item_refs, "acceptance_blocker_source_items_mismatch", blocker.key);
96
+ assertSameSet(contractBlocker.verification_methods, blocker.verification_methods, "acceptance_blocker_methods_mismatch", blocker.key);
97
+ for (const method of blocker.verification_methods)
98
+ if (!availableMethods.has(method))
99
+ invalid("acceptance_blocker_method_unbound", `${blocker.key}:${method}`);
100
+ const fullClaimRefs = blocker.source_item_refs.flatMap((sourceItemRef) => {
101
+ const local = designSourceItemClaims(contract, contractTarget, indexed, sourceItemRef, claims);
102
+ return local.map((claimRef) => `${contractTarget.outcome_key}.${claimRef}`);
103
+ });
104
+ if (contractBlocker.status === "machine_claim") {
105
+ const expectedLocal = fullClaimRefs.map((claimRef) => claimRef.slice(contractTarget.outcome_key.length + 1));
106
+ for (const claimRef of expectedLocal)
107
+ if (!contractBlocker.refs.includes(claimRef))
108
+ invalid("acceptance_blocker_machine_claim_lineage_missing", `${blocker.key}:${claimRef}`);
109
+ }
110
+ else
111
+ for (const confirmationRef of contractBlocker.refs) {
112
+ const confirmation = confirmations.get(confirmationRef);
113
+ if (!confirmation)
114
+ invalid("acceptance_blocker_confirmation_unknown", `${blocker.key}:${confirmationRef}`);
115
+ for (const claimRef of fullClaimRefs)
116
+ if (!confirmation.impact_claims.includes(claimRef))
117
+ invalid("acceptance_blocker_confirmation_lineage_missing", `${blocker.key}:${confirmationRef}:${claimRef}`);
118
+ }
119
+ }
120
+ }
121
+ function designSourceItemClaims(contract, contractTarget, indexed, sourceItemRef, claims = new Map(contract.source_claims.map((item) => [item.key, item]))) {
122
+ const claim = claims.get(sourceItemRef);
123
+ if (!claim)
124
+ invalid("coverage_source_claim_unknown", `${contractTarget.target.key}:${sourceItemRef}`);
125
+ if (claim.source_ref.split("#")[0] !== indexed.preflight.handoff_path)
126
+ invalid("coverage_source_claim_file_mismatch", `${contractTarget.target.key}:${sourceItemRef}`);
127
+ if (claim.disposition.type !== "claim")
128
+ invalid("coverage_source_claim_disposition_required", `${contractTarget.target.key}:${sourceItemRef}:${claim.disposition.type}`);
129
+ const prefix = `${contractTarget.outcome_key}.`;
130
+ return claim.disposition.refs.map((claimRef) => {
131
+ if (!claimRef.startsWith(prefix))
132
+ invalid("coverage_claim_outcome_mismatch", `${contractTarget.target.key}:${sourceItemRef}:${claimRef}`);
133
+ return claimRef.slice(prefix.length);
134
+ });
135
+ }
136
+ function validateVerificationMethodBindings(target, check, rows, claimsBySourceItem) {
137
+ const expectedMethods = new Set(rows.flatMap((row) => row.verification_methods));
138
+ const bindings = target.verification_method_bindings;
139
+ assertSameSet(bindings.map((item) => item.method), [...expectedMethods], "verification_methods_mismatch", target.key);
140
+ const assertionRefs = bindings.map((item) => item.assertion_ref);
141
+ if (new Set(assertionRefs).size !== assertionRefs.length)
142
+ invalid("verification_method_assertion_duplicate", target.key);
143
+ if (assertionRefs.includes(target.conformance_assertion_ref))
144
+ invalid("verification_method_assertion_must_be_independent", target.key);
145
+ for (const binding of bindings) {
146
+ const assertion = check.positive_assertions.find((item) => item.key === binding.assertion_ref);
147
+ if (!assertion)
148
+ invalid("verification_method_assertion_unknown", `${target.key}:${binding.method}:${binding.assertion_ref}`);
149
+ const sourceItems = new Set(rows
150
+ .filter((row) => row.verification_methods.includes(binding.method))
151
+ .flatMap((row) => row.source_item_refs));
152
+ for (const sourceItemRef of sourceItems)
153
+ for (const claimRef of claimsBySourceItem.get(sourceItemRef) ?? [])
154
+ if (!assertion.claims.includes(claimRef))
155
+ invalid("verification_method_claim_not_asserted", `${target.key}:${binding.method}:${sourceItemRef}:${claimRef}`);
156
+ for (const capability of requiredCapabilities(binding.method))
157
+ if (!assertion.evidence_capabilities.includes(capability))
158
+ invalid("verification_method_capability_required", `${target.key}:${binding.method}:${capability}`);
159
+ }
160
+ }
161
+ function requiredCapabilities(method) {
162
+ if (method === "interaction_trace")
163
+ return ["interaction_trace", "target_runtime"];
164
+ if (method === "component_state")
165
+ return ["design_conformance", "interaction_trace", "target_runtime"];
166
+ return ["design_conformance", "target_runtime"];
167
+ }
168
+ function assertSameSet(actual, expected, code, detail) {
169
+ const left = [...new Set(actual)].sort();
170
+ const right = [...new Set(expected)].sort();
171
+ if (left.length !== right.length ||
172
+ left.some((item, index) => item !== right[index]))
173
+ invalid(code, `${detail}:${left.join(",")}:${right.join(",")}`);
174
+ }
175
+ function invalid(code, detail) {
176
+ throw new Error(`delivery_contract_invalid:design_resource_${code}:${detail}`);
177
+ }
@@ -141,6 +141,25 @@ function decodeRecord(value, index) {
141
141
  artifact_path: nonEmpty(row.artifact_path, `${label}.artifact_path`),
142
142
  artifact_sha256: sha(row.artifact_sha256, `${label}.artifact_sha256`),
143
143
  };
144
+ case "design_conformance":
145
+ exact(row, label, [
146
+ "assertion_key",
147
+ "capability",
148
+ "design_target_ref",
149
+ "target_ref",
150
+ "condition_keys",
151
+ "actual_artifact_path",
152
+ "comparison_artifact_path",
153
+ ]);
154
+ return {
155
+ ...base,
156
+ capability,
157
+ design_target_ref: key(row.design_target_ref, `${label}.design_target_ref`),
158
+ target_ref: key(row.target_ref, `${label}.target_ref`),
159
+ condition_keys: keys(row.condition_keys, `${label}.condition_keys`),
160
+ actual_artifact_path: nonEmpty(row.actual_artifact_path, `${label}.actual_artifact_path`),
161
+ comparison_artifact_path: nonEmpty(row.comparison_artifact_path, `${label}.comparison_artifact_path`),
162
+ };
144
163
  case "target_runtime":
145
164
  exact(row, label, [
146
165
  "assertion_key",
@@ -26,12 +26,16 @@ export function validateEvidenceCapabilityDeclarations(contract, report) {
26
26
  if (assertion.evidence_capabilities.includes("visual_render") &&
27
27
  !check.artifact_globs.length)
28
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);
29
32
  if (assertion.evidence_capabilities.includes("interaction_trace") &&
30
33
  !check.scenario.when.length)
31
34
  issue(report, "interaction_trace_actions_required", label);
32
35
  if (check.runner.type === "playwright_test" &&
33
36
  assertion.evidence_capabilities.some((capability) => capability !== "presence" &&
34
37
  capability !== "interaction_trace" &&
38
+ capability !== "design_conformance" &&
35
39
  capability !== "target_runtime"))
36
40
  issue(report, "playwright_evidence_capability_unsupported", label);
37
41
  if (["boundary_invocation", "external_side_effect"].some((capability) => assertion.evidence_capabilities.includes(capability)) &&
@@ -25,12 +25,37 @@ export function validateRuntimeEvidenceRecord(check, record, artifactHashes) {
25
25
  return artifactHashes[record.artifact_path] === record.artifact_sha256
26
26
  ? null
27
27
  : "artifact_hash_mismatch";
28
+ case "design_conformance":
29
+ return validateDesignConformance(check, record, artifactHashes);
28
30
  case "target_runtime":
29
31
  return validateTargetRuntime(check, record);
30
32
  case "input_variation":
31
33
  return validateInputVariation(record);
32
34
  }
33
35
  }
36
+ function validateDesignConformance(check, record, artifactHashes) {
37
+ const target = (check.design_conformance_targets ?? []).find((item) => item.key === record.design_target_ref &&
38
+ designTargetUsesAssertion(item, record.assertion_key));
39
+ if (!target)
40
+ return "design_target_unknown";
41
+ if (target.target_ref !== record.target_ref ||
42
+ target.target_ref !== check.execution_target.target_ref)
43
+ return "target_mismatch";
44
+ if (!same([...target.condition_keys].sort(), [...record.condition_keys].sort()))
45
+ return "design_conditions_mismatch";
46
+ if (target.actual_artifact_path !== record.actual_artifact_path ||
47
+ target.comparison_artifact_path !== record.comparison_artifact_path)
48
+ return "design_artifact_path_mismatch";
49
+ if (!artifactHashes[record.actual_artifact_path])
50
+ return "actual_artifact_missing";
51
+ if (!artifactHashes[record.comparison_artifact_path])
52
+ return "comparison_artifact_missing";
53
+ return null;
54
+ }
55
+ function designTargetUsesAssertion(target, assertionKey) {
56
+ return (target.conformance_assertion_ref === assertionKey ||
57
+ target.verification_method_bindings.some((item) => item.assertion_ref === assertionKey));
58
+ }
34
59
  function validateInteractionTrace(check, record) {
35
60
  if (record.target_ref !== check.execution_target.target_ref)
36
61
  return "target_mismatch";
@@ -55,6 +55,14 @@ export interface VisualRenderEvidenceV2 extends EvidenceRecordBaseV2 {
55
55
  artifact_path: string;
56
56
  artifact_sha256: string;
57
57
  }
58
+ export interface DesignConformanceEvidenceV2 extends EvidenceRecordBaseV2 {
59
+ capability: "design_conformance";
60
+ design_target_ref: string;
61
+ target_ref: string;
62
+ condition_keys: string[];
63
+ actual_artifact_path: string;
64
+ comparison_artifact_path: string;
65
+ }
58
66
  export interface TargetRuntimeEvidenceV2 extends EvidenceRecordBaseV2 {
59
67
  capability: "target_runtime";
60
68
  target_ref: string;
@@ -70,5 +78,5 @@ export interface InputVariationEvidenceV2 extends EvidenceRecordBaseV2 {
70
78
  }>;
71
79
  failure_case_observed: boolean;
72
80
  }
73
- export type EvidenceCapabilityRecordV2 = InteractionTraceEvidenceV2 | StateDeltaEvidenceV2 | CrossSurfaceConsistencyEvidenceV2 | DurableReadbackEvidenceV2 | BoundaryInvocationEvidenceV2 | ExternalSideEffectEvidenceV2 | FailureInjectionEvidenceV2 | VisualRenderEvidenceV2 | TargetRuntimeEvidenceV2 | InputVariationEvidenceV2;
81
+ export type EvidenceCapabilityRecordV2 = InteractionTraceEvidenceV2 | StateDeltaEvidenceV2 | CrossSurfaceConsistencyEvidenceV2 | DurableReadbackEvidenceV2 | BoundaryInvocationEvidenceV2 | ExternalSideEffectEvidenceV2 | FailureInjectionEvidenceV2 | VisualRenderEvidenceV2 | DesignConformanceEvidenceV2 | TargetRuntimeEvidenceV2 | InputVariationEvidenceV2;
74
82
  export {};
@@ -4,6 +4,7 @@ import { parseRequirements } from "./long-task-requirement-shape.js";
4
4
  import { array, boolean, key, literal, nullable, object, parseBindings, parseControls, parseCounterfactuals, parseKeyedStatements, parseObligations, parseOwner, parsePopulation, parseRollback, repositoryPatterns, string, strings, } from "./long-task-delivery-shape.js";
5
5
  import { parseStrictYaml } from "./strict-codec.js";
6
6
  import { assertNoSemanticDriftMigration, semanticDriftOutcomeMigrationFields, } from "./long-task-semantic-drift-migration.js";
7
+ import { parseSurfaceBindings } from "./long-task-ui-surface-shape.js";
7
8
  export function parseOutcome(value, label) {
8
9
  assertNoSemanticDriftMigration(semanticDriftOutcomeMigrationFields(value, label));
9
10
  const row = object(value, label, ["key", "title", "stage", "product", "technical", "acceptance"], ["depends_on"]);
@@ -12,7 +13,13 @@ export function parseOutcome(value, label) {
12
13
  "success_path_required",
13
14
  "degradation_path_required",
14
15
  "owner",
15
- ], ["requirements", "owner_surfaces", "controls", "non_completing_outcomes"]);
16
+ ], [
17
+ "requirements",
18
+ "owner_surfaces",
19
+ "controls",
20
+ "surface_bindings",
21
+ "non_completing_outcomes",
22
+ ]);
16
23
  const technical = object(row.technical, `${label}.technical`, ["expected_change_paths"], [
17
24
  "obligations",
18
25
  "allowed_support_paths",
@@ -43,6 +50,9 @@ export function parseOutcome(value, label) {
43
50
  controls: Object.hasOwn(product, "controls")
44
51
  ? parseControls(product.controls, `${label}.product.controls`)
45
52
  : [],
53
+ surface_bindings: Object.hasOwn(product, "surface_bindings")
54
+ ? parseSurfaceBindings(product.surface_bindings, `${label}.product.surface_bindings`)
55
+ : [],
46
56
  non_completing_outcomes: Object.hasOwn(product, "non_completing_outcomes")
47
57
  ? parseKeyedStatements(product.non_completing_outcomes, `${label}.product.non_completing_outcomes`)
48
58
  : [],
@@ -46,4 +46,3 @@ export declare function matchesRepoPattern(fileInput: string, patternInput: stri
46
46
  export declare function proveRepositoryPatternSubset(candidateInput: string, ownerInput: string): PatternContainment;
47
47
  export declare function classifyRepositoryPatternOverlap(leftInput: string, rightInput: string): PatternOverlap;
48
48
  export declare function repositoryPatternsMayOverlap(left: string, right: string): boolean;
49
- export declare function findScopeEscapes(changedPaths: string[], allowedPatterns: string[], forbiddenPatterns: string[]): string[];
@@ -250,10 +250,6 @@ export function classifyRepositoryPatternOverlap(leftInput, rightInput) {
250
250
  export function repositoryPatternsMayOverlap(left, right) {
251
251
  return (classifyRepositoryPatternOverlap(left, right).status !== "proven_disjoint");
252
252
  }
253
- export function findScopeEscapes(changedPaths, allowedPatterns, forbiddenPatterns) {
254
- return changedPaths.filter((file) => forbiddenPatterns.some((pattern) => matchesRepoPattern(file, pattern)) ||
255
- !allowedPatterns.some((pattern) => matchesRepoPattern(file, pattern)));
256
- }
257
253
  function segmentSubset(candidate, owner) {
258
254
  if (candidate.raw === owner.raw)
259
255
  return { status: "proven_subset" };
@@ -0,0 +1,43 @@
1
+ import type { CompiledCheckV2 } from "./long-task-delivery-types.js";
2
+ export interface PlaywrightCaseInstance {
3
+ id: string;
4
+ project_id: string;
5
+ executed: boolean;
6
+ passed: boolean;
7
+ skipped: boolean;
8
+ flaky: boolean;
9
+ unexpected: boolean;
10
+ timed_out: boolean;
11
+ interrupted: boolean;
12
+ status: string;
13
+ given_keys: string[];
14
+ action_keys: string[];
15
+ }
16
+ export interface PlaywrightCase {
17
+ id: string;
18
+ executed: boolean;
19
+ passed: boolean;
20
+ skipped: boolean;
21
+ flaky: boolean;
22
+ unexpected: boolean;
23
+ status: string;
24
+ project_ids: string[];
25
+ executed_instances: number;
26
+ failed_instances: number;
27
+ skipped_instances: number;
28
+ flaky_instances: number;
29
+ unexpected_instances: number;
30
+ timed_out_instances: number;
31
+ interrupted_instances: number;
32
+ given_keys: string[];
33
+ action_keys: string[];
34
+ }
35
+ export declare function collectCases(report: Record<string, unknown>, declaredIds: Set<string>): {
36
+ cases: PlaywrightCaseInstance[];
37
+ unbound_unexpected_instances: number;
38
+ error: string | null;
39
+ };
40
+ export declare function declaredCaseIds(check: CompiledCheckV2): Set<string>;
41
+ export declare function duplicateCaseInstance(values: PlaywrightCaseInstance[]): PlaywrightCaseInstance | null;
42
+ export declare function record(value: unknown): Record<string, unknown> | null;
43
+ export declare function same(left: string[], right: string[]): boolean;
@@ -0,0 +1,152 @@
1
+ export function collectCases(report, declaredIds) {
2
+ const cases = [];
3
+ const collection = { unbound_unexpected_instances: 0 };
4
+ const error = visitSuites(report.suites, cases, declaredIds, collection);
5
+ return { cases, error, ...collection };
6
+ }
7
+ function visitSuites(value, cases, declaredIds, collection) {
8
+ if (!Array.isArray(value))
9
+ return null;
10
+ for (const suiteValue of value) {
11
+ const suite = record(suiteValue);
12
+ if (!suite)
13
+ continue;
14
+ const nestedError = visitSuites(suite.suites, cases, declaredIds, collection);
15
+ if (nestedError)
16
+ return nestedError;
17
+ if (!Array.isArray(suite.specs))
18
+ continue;
19
+ for (const specValue of suite.specs) {
20
+ const spec = record(specValue);
21
+ if (!spec || !Array.isArray(spec.tests))
22
+ continue;
23
+ for (const testValue of spec.tests) {
24
+ const test = record(testValue);
25
+ if (!test)
26
+ continue;
27
+ const title = [spec.title, test.title]
28
+ .filter((item) => typeof item === "string")
29
+ .join(" ");
30
+ const ids = declaredIdsInTitle(title, declaredIds);
31
+ if (ids.length > 1)
32
+ return `playwright_test_multiple_ac_ids:${ids.join(",")}`;
33
+ const instance = playwrightCase(ids[0] ?? "unbound", test);
34
+ if (ids.length === 1)
35
+ cases.push(instance);
36
+ else if (instance.unexpected)
37
+ collection.unbound_unexpected_instances += 1;
38
+ }
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+ function declaredIdsInTitle(title, declared) {
44
+ const explicit = [...title.matchAll(/\[ac:([a-z0-9][a-z0-9-]*)\]/gu)].map((match) => match[1]);
45
+ const legacy = [...title.matchAll(/\[([a-z0-9][a-z0-9-]*)\]/gu)].map((match) => match[1]);
46
+ return [
47
+ ...new Set([...explicit, ...legacy].filter((id) => declared.has(id))),
48
+ ].sort();
49
+ }
50
+ function playwrightCase(id, test) {
51
+ const status = typeof test.status === "string" ? test.status : "invalid";
52
+ const projectId = typeof test.projectId === "string"
53
+ ? test.projectId
54
+ : typeof test.projectName === "string"
55
+ ? test.projectName
56
+ : "default";
57
+ const results = Array.isArray(test.results)
58
+ ? test.results.map(record).filter(Boolean)
59
+ : [];
60
+ const resultStatuses = results
61
+ .map((result) => result.status)
62
+ .filter((value) => typeof value === "string");
63
+ const skipped = status === "skipped" ||
64
+ (resultStatuses.length > 0 &&
65
+ resultStatuses.every((value) => value === "skipped"));
66
+ const executed = !skipped && resultStatuses.length > 0;
67
+ const flaky = status === "flaky";
68
+ const unexpected = status === "unexpected";
69
+ const timedOut = resultStatuses.includes("timedOut");
70
+ const interrupted = resultStatuses.includes("interrupted");
71
+ const passed = executed &&
72
+ !flaky &&
73
+ !unexpected &&
74
+ status === "expected" &&
75
+ resultStatuses.length > 0 &&
76
+ resultStatuses.at(-1) === "passed";
77
+ const traces = results.map((result) => scenarioTrace(result));
78
+ const givenKeys = traces[0]?.given_keys ?? [];
79
+ const actionKeys = traces[0]?.action_keys ?? [];
80
+ const traceConsistent = traces.every((trace) => same(trace.given_keys, givenKeys) && same(trace.action_keys, actionKeys));
81
+ return {
82
+ id,
83
+ project_id: projectId,
84
+ executed,
85
+ passed,
86
+ skipped,
87
+ flaky,
88
+ unexpected,
89
+ timed_out: timedOut,
90
+ interrupted,
91
+ status,
92
+ given_keys: traceConsistent ? givenKeys : [],
93
+ action_keys: traceConsistent ? actionKeys : [],
94
+ };
95
+ }
96
+ export function declaredCaseIds(check) {
97
+ const result = new Set();
98
+ for (const assertion of [
99
+ ...check.positive_assertions,
100
+ ...check.negative_assertions,
101
+ ]) {
102
+ if (!assertion.claims.length)
103
+ continue;
104
+ const match = /^playwright\.case\.([a-z0-9][a-z0-9-]*)\.passed$/u.exec(assertion.observation);
105
+ if (match)
106
+ result.add(assertion.key);
107
+ }
108
+ return result;
109
+ }
110
+ export function duplicateCaseInstance(values) {
111
+ const seen = new Set();
112
+ for (const value of values) {
113
+ const identity = `${value.id}\0${value.project_id}`;
114
+ if (seen.has(identity))
115
+ return value;
116
+ seen.add(identity);
117
+ }
118
+ return null;
119
+ }
120
+ export function record(value) {
121
+ return value && typeof value === "object" && !Array.isArray(value)
122
+ ? value
123
+ : null;
124
+ }
125
+ function scenarioTrace(result) {
126
+ const givenKeys = [];
127
+ const actionKeys = [];
128
+ const visit = (value) => {
129
+ if (!Array.isArray(value))
130
+ return;
131
+ for (const item of value) {
132
+ const step = record(item);
133
+ if (!step)
134
+ continue;
135
+ if (typeof step.title === "string") {
136
+ const given = /^\[given:([a-z0-9][a-z0-9-]*)\]$/u.exec(step.title);
137
+ const action = /^\[action:([a-z0-9][a-z0-9-]*)\]$/u.exec(step.title);
138
+ if (given)
139
+ givenKeys.push(given[1]);
140
+ if (action)
141
+ actionKeys.push(action[1]);
142
+ }
143
+ visit(step.steps);
144
+ }
145
+ };
146
+ visit(result.steps);
147
+ return { given_keys: givenKeys, action_keys: actionKeys };
148
+ }
149
+ export function same(left, right) {
150
+ return (left.length === right.length &&
151
+ left.every((value, index) => value === right[index]));
152
+ }