project-tiny-context-harness 0.8.1 → 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 (132) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +401 -395
  3. package/assets/README.md +628 -549
  4. package/assets/README.zh-CN.md +345 -339
  5. package/assets/agents/.gitkeep +1 -1
  6. package/assets/agents/AGENTS_CORE.md +70 -70
  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 +137 -137
  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 +88 -88
  23. package/assets/skills/context_surface_contract/SKILL.md +191 -191
  24. package/assets/skills/context_uiux_design/SKILL.md +172 -172
  25. package/assets/skills/design-resource-authoring/SKILL.md +85 -85
  26. package/assets/skills/design-resource-authoring/references/downstream-handoff.md +136 -136
  27. package/assets/skills/design-resource-authoring/references/open-design-provider.md +135 -135
  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 -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 +100 -100
  34. package/assets/skills/long-task-workflow/references/authority-lifecycle.md +75 -73
  35. package/assets/skills/long-task-workflow/references/contract-authoring.md +119 -115
  36. package/assets/skills/long-task-workflow/references/evidence-design.md +77 -76
  37. package/assets/skills/long-task-workflow/references/source-authoring.md +102 -99
  38. package/assets/skills/source-plan-authoring/SKILL.md +14 -14
  39. package/assets/tools/validate_context.py +442 -442
  40. package/dist/commands/design-resource.js +2 -2
  41. package/dist/commands/index.js +26 -26
  42. package/dist/commands/long-task-authoring.js +83 -9
  43. package/dist/commands/long-task.js +15 -15
  44. package/dist/lib/context-graph-snapshot.js +7 -0
  45. package/dist/lib/design-resource-handoff-shape-evidence.js +3 -0
  46. package/dist/lib/design-resource-handoff-types.d.ts +2 -0
  47. package/dist/lib/design-resource-handoff-validation-coverage.js +1 -0
  48. package/dist/lib/design-resource-handoff-validation-structure.js +2 -0
  49. package/dist/lib/execution-target-capabilities.d.ts +2 -0
  50. package/dist/lib/execution-target-capabilities.js +31 -0
  51. package/dist/lib/long-task-acceptance-shape.js +81 -20
  52. package/dist/lib/long-task-applicability-shape.d.ts +4 -0
  53. package/dist/lib/long-task-applicability-shape.js +54 -0
  54. package/dist/lib/long-task-assertions-v2.js +8 -0
  55. package/dist/lib/long-task-authoring-preflight-diagnostics.js +5 -0
  56. package/dist/lib/long-task-authoring-preflight-repair-order.js +3 -0
  57. package/dist/lib/long-task-authoring-preflight.js +12 -8
  58. package/dist/lib/long-task-authority-materials.js +46 -4
  59. package/dist/lib/long-task-authority-policy.d.ts +54 -0
  60. package/dist/lib/long-task-authority-policy.js +61 -0
  61. package/dist/lib/long-task-authority-types.d.ts +12 -2
  62. package/dist/lib/long-task-authority.js +9 -2
  63. package/dist/lib/long-task-check-shape.js +6 -1
  64. package/dist/lib/long-task-claim-definitions.js +27 -42
  65. package/dist/lib/long-task-claims.js +275 -146
  66. package/dist/lib/long-task-contract-types.d.ts +42 -8
  67. package/dist/lib/long-task-control-fields.d.ts +12 -0
  68. package/dist/lib/long-task-control-fields.js +66 -0
  69. package/dist/lib/long-task-control-types.d.ts +1 -0
  70. package/dist/lib/long-task-control-types.js +1 -0
  71. package/dist/lib/long-task-counterfactual-claim-policy.js +7 -0
  72. package/dist/lib/long-task-counterfactual-types.d.ts +12 -0
  73. package/dist/lib/long-task-delivery-compiler.js +1 -0
  74. package/dist/lib/long-task-delivery-preflight.js +24 -0
  75. package/dist/lib/long-task-delivery-shape.d.ts +1 -0
  76. package/dist/lib/long-task-delivery-shape.js +1 -0
  77. package/dist/lib/long-task-delivery-types.d.ts +1 -0
  78. package/dist/lib/long-task-delivery-types.js +1 -0
  79. package/dist/lib/long-task-delivery-validation.js +46 -0
  80. package/dist/lib/long-task-design-resource-handoff.js +48 -7
  81. package/dist/lib/long-task-evidence-capability-codec.js +37 -0
  82. package/dist/lib/long-task-evidence-capability-policy.js +80 -38
  83. package/dist/lib/long-task-evidence-capability-runtime.js +36 -0
  84. package/dist/lib/long-task-evidence-capability-types.d.ts +13 -1
  85. package/dist/lib/long-task-evidence-findings.js +3 -0
  86. package/dist/lib/long-task-evidence-sensitivity-policy.js +38 -0
  87. package/dist/lib/long-task-evidence-v2.js +7 -1
  88. package/dist/lib/long-task-final-integrity.d.ts +16 -0
  89. package/dist/lib/long-task-final-integrity.js +98 -0
  90. package/dist/lib/long-task-final-v2.js +17 -34
  91. package/dist/lib/long-task-outcome-parser.js +22 -4
  92. package/dist/lib/long-task-playwright-case-evidence.d.ts +2 -0
  93. package/dist/lib/long-task-playwright-case-evidence.js +10 -2
  94. package/dist/lib/long-task-playwright-evidence.d.ts +1 -0
  95. package/dist/lib/long-task-playwright-evidence.js +32 -0
  96. package/dist/lib/long-task-product-shape.d.ts +3 -1
  97. package/dist/lib/long-task-product-shape.js +100 -4
  98. package/dist/lib/long-task-requirement-shape.js +3 -0
  99. package/dist/lib/long-task-root-shape.js +12 -6
  100. package/dist/lib/long-task-runner-freeze.js +16 -0
  101. package/dist/lib/long-task-runtime-types.d.ts +11 -2
  102. package/dist/lib/long-task-semantic-assurance-policy.d.ts +4 -0
  103. package/dist/lib/long-task-semantic-assurance-policy.js +52 -0
  104. package/dist/lib/long-task-semantic-contract-types.d.ts +18 -1
  105. package/dist/lib/long-task-semantic-drift-migration.js +33 -2
  106. package/dist/lib/long-task-semantic-mutation.d.ts +7 -0
  107. package/dist/lib/long-task-semantic-mutation.js +81 -0
  108. package/dist/lib/long-task-shape-primitives.d.ts +1 -1
  109. package/dist/lib/long-task-shape-primitives.js +1 -0
  110. package/dist/lib/long-task-source-authority-types.d.ts +2 -0
  111. package/dist/lib/long-task-source-item-parser.d.ts +1 -1
  112. package/dist/lib/long-task-source-item-parser.js +150 -104
  113. package/dist/lib/long-task-source-markers.d.ts +12 -0
  114. package/dist/lib/long-task-source-markers.js +146 -0
  115. package/dist/lib/long-task-source-target-continuity.d.ts +1 -1
  116. package/dist/lib/long-task-source-target-continuity.js +80 -55
  117. package/dist/lib/long-task-source-target-index.js +7 -29
  118. package/dist/lib/long-task-status-v2.d.ts +1 -0
  119. package/dist/lib/long-task-status-v2.js +2 -2
  120. package/dist/lib/long-task-target-policy.js +20 -0
  121. package/dist/lib/long-task-ui-design-policy.js +27 -1
  122. package/dist/lib/long-task-ui-surface-shape.js +21 -1
  123. package/dist/lib/long-task-ui-surface-types.d.ts +7 -0
  124. package/dist/lib/long-task-verification-preview.d.ts +10 -0
  125. package/dist/lib/long-task-verifier-dependency-closure.d.ts +3 -0
  126. package/dist/lib/long-task-verifier-dependency-closure.js +179 -0
  127. package/dist/lib/long-task-verifier-v2.js +12 -2
  128. package/dist/lib/long-task-workspace.js +5 -1
  129. package/dist/schemas/long-task-delivery-v2/long-task-delivery-v2.schema.json +99 -25
  130. package/migrations/README.md +15 -15
  131. package/package.json +84 -84
  132. package/source-mappings.yaml +25 -25
@@ -5,6 +5,7 @@ import { executeCheckRunner } from "./long-task-check-runner.js";
5
5
  import { createCounterfactualSandbox } from "./long-task-counterfactual-sandbox.js";
6
6
  import { assertionFinding, checkFinding, classifyCheckStatus, evaluateAssertionResults, } from "./long-task-evidence-findings.js";
7
7
  import { classifyPlaywrightCounterfactual } from "./long-task-playwright-counterfactual-policy.js";
8
+ import { applyNarrowSemanticMutation } from "./long-task-semantic-mutation.js";
8
9
  import { evaluateEvidenceCapabilities } from "./long-task-evidence-capability-policy.js";
9
10
  import { resolveInsideRepository } from "./long-task-workspace.js";
10
11
  export async function evaluateCheckEvidence(check, raw, snapshotRoot, outcome) {
@@ -33,6 +34,7 @@ export async function evaluateCheckEvidence(check, raw, snapshotRoot, outcome) {
33
34
  assertion_key: result.key,
34
35
  polarity: result.polarity,
35
36
  proof_surface: check.proof_surface,
37
+ applicability_ref: result.applicability_ref ?? null,
36
38
  }))
37
39
  : [])
38
40
  : [];
@@ -45,6 +47,7 @@ export async function evaluateCheckEvidence(check, raw, snapshotRoot, outcome) {
45
47
  assertion_key: null,
46
48
  polarity: "population",
47
49
  proof_surface: check.proof_surface,
50
+ applicability_ref: null,
48
51
  })));
49
52
  return {
50
53
  internal_id: check.internal_id,
@@ -141,9 +144,12 @@ async function evaluateCounterfactualSet(entries, snapshotRoot, manifest) {
141
144
  for (const target of control.mutation.paths)
142
145
  await rm(resolveInsideRepository(root, target, "counterfactual.remove_paths"), { recursive: true, force: true });
143
146
  }
144
- else {
147
+ else if (control.mutation.type === "replace_file") {
145
148
  await copyFile(resolveInsideRepository(root, control.mutation.fixture_path, "counterfactual.fixture"), resolveInsideRepository(root, control.mutation.path, "counterfactual.path"));
146
149
  }
150
+ else {
151
+ await applyNarrowSemanticMutation(root, control.mutation);
152
+ }
147
153
  const raw = await executeCheckRunner(check, root);
148
154
  const result = await evaluateCheckEvidence(check, raw, root, entry.evidenceOutcome);
149
155
  const playwrightClassification = result.evidence_adapter === "playwright_json_v1"
@@ -0,0 +1,16 @@
1
+ import type { CompiledDeliveryContractV2, LongTaskFindingV2 } from "./long-task-delivery-types.js";
2
+ import { type ActiveAuthorityIdentityExpectation, type ActiveLongTaskAuthorityV3 } from "./long-task-state.js";
3
+ import { currentGitState } from "./long-task-workspace.js";
4
+ export interface FinalGateProtectedInputIdentity {
5
+ compiled_identity: string;
6
+ raw_contract_sha256: string;
7
+ }
8
+ export declare function captureFinalGateProtectedInputIdentity(repository: string, compiled: CompiledDeliveryContractV2): Promise<FinalGateProtectedInputIdentity>;
9
+ export declare function finalGateIntegrityFindings(input: {
10
+ repository: string;
11
+ active: ActiveLongTaskAuthorityV3;
12
+ accepted_authority: ActiveAuthorityIdentityExpectation;
13
+ candidate: Awaited<ReturnType<typeof currentGitState>>;
14
+ workspace_identity_before: string;
15
+ protected_inputs_before: FinalGateProtectedInputIdentity;
16
+ }): Promise<LongTaskFindingV2[]>;
@@ -0,0 +1,98 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { compileDeliveryContract } from "./long-task-delivery-compiler.js";
4
+ import { assertProtectedRepositoryFile } from "./long-task-protected-files.js";
5
+ import { activeAuthorityIdentityMatches, assertMatchingActiveBinding, loadActiveLongTaskAuthority, } from "./long-task-state.js";
6
+ import { captureWorkspaceFingerprint, currentGitState, } from "./long-task-workspace.js";
7
+ import { sha256Hex } from "./strict-codec.js";
8
+ export async function captureFinalGateProtectedInputIdentity(repository, compiled) {
9
+ const file = await assertProtectedRepositoryFile(repository, path.join(repository, ...compiled.contract_file.split("/")), "final_gate_delivery_contract");
10
+ return {
11
+ compiled_identity: compiled.compiled_identity,
12
+ raw_contract_sha256: sha256Hex(await readFile(file)),
13
+ };
14
+ }
15
+ export async function finalGateIntegrityFindings(input) {
16
+ const findings = [];
17
+ const after = await captureWorkspaceFingerprint(input.repository);
18
+ const gitAfter = await currentGitState(input.repository);
19
+ if (after.identity !== input.workspace_identity_before ||
20
+ gitAfter.head !== input.candidate.head ||
21
+ gitAfter.tree !== input.candidate.tree ||
22
+ gitAfter.dirty.length)
23
+ findings.push(workspaceChangedFinding());
24
+ if (await activeAuthorityChanged(input))
25
+ findings.push(activeAuthorityChangedFinding());
26
+ findings.push(...(await protectedInputFindings(input)));
27
+ return findings;
28
+ }
29
+ async function activeAuthorityChanged(input) {
30
+ try {
31
+ const currentActive = (await loadActiveLongTaskAuthority(input.repository))
32
+ .authority;
33
+ return (!currentActive ||
34
+ !activeAuthorityIdentityMatches(currentActive, input.accepted_authority));
35
+ }
36
+ catch {
37
+ return true;
38
+ }
39
+ }
40
+ async function protectedInputFindings(input) {
41
+ try {
42
+ const refreshed = await compileDeliveryContract(input.active.workdir, input.repository, {
43
+ live_gate: true,
44
+ initial_task_base: input.active.initial_task_base,
45
+ authority_revision: input.active.authority_revision,
46
+ require_completion_gate: true,
47
+ });
48
+ await assertMatchingActiveBinding(refreshed);
49
+ const after = await captureFinalGateProtectedInputIdentity(input.repository, refreshed);
50
+ return after.compiled_identity !==
51
+ input.protected_inputs_before.compiled_identity ||
52
+ after.raw_contract_sha256 !==
53
+ input.protected_inputs_before.raw_contract_sha256
54
+ ? [
55
+ protectedInputsChangedFinding({
56
+ before: input.protected_inputs_before,
57
+ after,
58
+ }),
59
+ ]
60
+ : [];
61
+ }
62
+ catch (error) {
63
+ return [
64
+ protectedInputsChangedFinding({
65
+ before: input.protected_inputs_before,
66
+ after_error: error instanceof Error ? error.message : String(error),
67
+ }),
68
+ ];
69
+ }
70
+ }
71
+ function workspaceChangedFinding() {
72
+ return {
73
+ code: "workspace_changed_during_final_gate",
74
+ outcome_key: null,
75
+ check_key: null,
76
+ message: "The workspace changed while Live Final Gate was running.",
77
+ next_action: "Stop concurrent mutation and rerun the complete Live Final Gate.",
78
+ };
79
+ }
80
+ function activeAuthorityChangedFinding() {
81
+ return {
82
+ code: "active_authority_changed_during_final_gate",
83
+ outcome_key: null,
84
+ check_key: null,
85
+ message: "Active Authority changed while Live Final Gate was running.",
86
+ next_action: "Review the new Authority Revision and rerun the complete Live Final Gate.",
87
+ };
88
+ }
89
+ function protectedInputsChangedFinding(actual) {
90
+ return {
91
+ code: "protected_inputs_changed_during_final_gate",
92
+ outcome_key: null,
93
+ check_key: null,
94
+ message: "Protected Contract, Source, controlling Context, verifier/runner, verification-input or workdir input identity changed while Final Gate was running.",
95
+ actual,
96
+ next_action: "Stop concurrent protected-input mutation, review any required Authority Revision, and rerun the complete Final Gate.",
97
+ };
98
+ }
@@ -1,6 +1,7 @@
1
1
  import { compileDeliveryContract } from "./long-task-delivery-compiler.js";
2
- import { activeAuthorityIdentityMatches, assertMatchingActiveBinding, loadActiveLongTaskAuthority, writeFinalReceipt, } from "./long-task-state.js";
3
- import { assertVerifierAuthorityCurrent } from "./long-task-freshness.js";
2
+ import { captureFinalGateProtectedInputIdentity, finalGateIntegrityFindings, } from "./long-task-final-integrity.js";
3
+ import { assertMatchingActiveBinding, loadActiveLongTaskAuthority, writeFinalReceipt, } from "./long-task-state.js";
4
+ import { assertVerifierAuthorityCurrent, deliveryCompileFreshness, } from "./long-task-freshness.js";
4
5
  import { allCompiledChecks, runDeliveryChecks, } from "./long-task-verifier-v2.js";
5
6
  import { captureWorkspaceFingerprint, createWorkspaceSnapshot, currentGitState, repositoryRoot, } from "./long-task-workspace.js";
6
7
  export async function runDeliveryFinalGate(workdirInput) {
@@ -21,6 +22,11 @@ export async function runDeliveryFinalGate(workdirInput) {
21
22
  const candidate = await currentGitState(repository);
22
23
  if (candidate.dirty.length)
23
24
  throw new Error(`final_gate_requires_clean_candidate_commit:${candidate.dirty.join(",")}`);
25
+ const acceptedCompiled = active.authority_snapshot;
26
+ await assertMatchingActiveBinding(acceptedCompiled);
27
+ const staleAuthorityInputs = await deliveryCompileFreshness(acceptedCompiled);
28
+ if (staleAuthorityInputs.length)
29
+ throw new Error(`final_gate_protected_input_stale:${staleAuthorityInputs.join(",")}`);
24
30
  const compiled = await compileDeliveryContract(active.workdir, repository, {
25
31
  live_gate: true,
26
32
  initial_task_base: active.initial_task_base,
@@ -28,42 +34,19 @@ export async function runDeliveryFinalGate(workdirInput) {
28
34
  require_completion_gate: true,
29
35
  });
30
36
  await assertMatchingActiveBinding(compiled);
37
+ const protectedInputsBefore = await captureFinalGateProtectedInputIdentity(repository, compiled);
31
38
  const before = await captureWorkspaceFingerprint(repository);
32
39
  const snapshot = await createWorkspaceSnapshot(repository, active.workdir, `final-${compiled.task.id}`);
33
40
  try {
34
41
  const run = await runDeliveryChecks(compiled, snapshot, allCompiledChecks(compiled), true, true);
35
- const after = await captureWorkspaceFingerprint(repository);
36
- const gitAfter = await currentGitState(repository);
37
- if (after.identity !== before.identity ||
38
- gitAfter.head !== candidate.head ||
39
- gitAfter.tree !== candidate.tree ||
40
- gitAfter.dirty.length)
41
- run.findings.push({
42
- code: "workspace_changed_during_final_gate",
43
- outcome_key: null,
44
- check_key: null,
45
- message: "The workspace changed while Live Final Gate was running.",
46
- next_action: "Stop concurrent mutation and rerun the complete Live Final Gate.",
47
- });
48
- let activeAuthorityChanged = false;
49
- try {
50
- const currentActive = (await loadActiveLongTaskAuthority(repository))
51
- .authority;
52
- activeAuthorityChanged =
53
- !currentActive ||
54
- !activeAuthorityIdentityMatches(currentActive, acceptedAuthority);
55
- }
56
- catch {
57
- activeAuthorityChanged = true;
58
- }
59
- if (activeAuthorityChanged)
60
- run.findings.push({
61
- code: "active_authority_changed_during_final_gate",
62
- outcome_key: null,
63
- check_key: null,
64
- message: "Active Authority changed while Live Final Gate was running.",
65
- next_action: "Review the new Authority Revision and rerun the complete Live Final Gate.",
66
- });
42
+ run.findings.push(...(await finalGateIntegrityFindings({
43
+ repository,
44
+ active,
45
+ accepted_authority: acceptedAuthority,
46
+ candidate,
47
+ workspace_identity_before: before.identity,
48
+ protected_inputs_before: protectedInputsBefore,
49
+ })));
67
50
  const outcomeResults = projectOutcomes(compiled.outcomes.map((outcome) => outcome.key), run.check_results, run.findings);
68
51
  const globalChecks = run.check_results.filter((check) => check.outcome_key === null);
69
52
  const globalHardFailure = globalChecks.some((check) => check.status !== "passed" && check.status !== "blocked_external");
@@ -1,22 +1,33 @@
1
1
  import { parseCheck } from "./long-task-check-shape.js";
2
2
  import { normalizeRepositoryFile } from "./long-task-paths.js";
3
3
  import { parseRequirements } from "./long-task-requirement-shape.js";
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";
4
+ import { array, boolean, key, literal, nullable, object, parseBindings, parseControls, parseCounterfactuals, parseApplicableKeyedStatements, parseClaimApplicability, parseControlRelationClosure, parseControlRelations, 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
7
  import { parseSurfaceBindings } from "./long-task-ui-surface-shape.js";
8
8
  export function parseOutcome(value, label) {
9
9
  assertNoSemanticDriftMigration(semanticDriftOutcomeMigrationFields(value, label));
10
- const row = object(value, label, ["key", "title", "stage", "product", "technical", "acceptance"], ["depends_on"]);
10
+ const row = object(value, label, [
11
+ "key",
12
+ "title",
13
+ "stage",
14
+ "applicability",
15
+ "product",
16
+ "technical",
17
+ "acceptance",
18
+ ], ["depends_on"]);
11
19
  const product = object(row.product, `${label}.product`, [
12
20
  "observable_result",
21
+ "result_applicability_refs",
13
22
  "success_path_required",
14
23
  "degradation_path_required",
15
24
  "owner",
25
+ "control_relation_closure",
16
26
  ], [
17
27
  "requirements",
18
28
  "owner_surfaces",
19
29
  "controls",
30
+ "control_relations",
20
31
  "surface_bindings",
21
32
  "non_completing_outcomes",
22
33
  ]);
@@ -36,8 +47,10 @@ export function parseOutcome(value, label) {
36
47
  depends_on: Object.hasOwn(row, "depends_on")
37
48
  ? strings(row.depends_on, `${label}.depends_on`).map((item, index) => key(item, `${label}.depends_on[${index}]`))
38
49
  : [],
50
+ applicability: parseClaimApplicability(row.applicability, `${label}.applicability`),
39
51
  product: {
40
52
  observable_result: string(product.observable_result, `${label}.product.observable_result`),
53
+ result_applicability_refs: strings(product.result_applicability_refs, `${label}.product.result_applicability_refs`).map((item, index) => key(item, `${label}.product.result_applicability_refs[${index}]`)),
41
54
  success_path_required: boolean(product.success_path_required, `${label}.product.success_path_required`),
42
55
  degradation_path_required: boolean(product.degradation_path_required, `${label}.product.degradation_path_required`),
43
56
  owner: parseOwner(product.owner, `${label}.product.owner`),
@@ -50,11 +63,15 @@ export function parseOutcome(value, label) {
50
63
  controls: Object.hasOwn(product, "controls")
51
64
  ? parseControls(product.controls, `${label}.product.controls`)
52
65
  : [],
66
+ control_relation_closure: parseControlRelationClosure(product.control_relation_closure, `${label}.product.control_relation_closure`),
67
+ control_relations: Object.hasOwn(product, "control_relations")
68
+ ? parseControlRelations(product.control_relations, `${label}.product.control_relations`)
69
+ : [],
53
70
  surface_bindings: Object.hasOwn(product, "surface_bindings")
54
71
  ? parseSurfaceBindings(product.surface_bindings, `${label}.product.surface_bindings`)
55
72
  : [],
56
73
  non_completing_outcomes: Object.hasOwn(product, "non_completing_outcomes")
57
- ? parseKeyedStatements(product.non_completing_outcomes, `${label}.product.non_completing_outcomes`)
74
+ ? parseApplicableKeyedStatements(product.non_completing_outcomes, `${label}.product.non_completing_outcomes`)
58
75
  : [],
59
76
  },
60
77
  technical: {
@@ -69,7 +86,7 @@ export function parseOutcome(value, label) {
69
86
  ? repositoryPatterns(technical.forbidden_paths, `${label}.technical.forbidden_paths`)
70
87
  : [],
71
88
  forbidden_shortcuts: Object.hasOwn(technical, "forbidden_shortcuts")
72
- ? parseKeyedStatements(technical.forbidden_shortcuts, `${label}.technical.forbidden_shortcuts`)
89
+ ? parseApplicableKeyedStatements(technical.forbidden_shortcuts, `${label}.technical.forbidden_shortcuts`)
73
90
  : [],
74
91
  bindings: Object.hasOwn(technical, "bindings")
75
92
  ? parseBindings(technical.bindings, `${label}.technical.bindings`)
@@ -100,6 +117,7 @@ export function parseOutcomeFragment(raw, relative) {
100
117
  "title",
101
118
  "stage",
102
119
  "depends_on",
120
+ "applicability",
103
121
  "product",
104
122
  "technical",
105
123
  "acceptance",
@@ -12,6 +12,7 @@ export interface PlaywrightCaseInstance {
12
12
  status: string;
13
13
  given_keys: string[];
14
14
  action_keys: string[];
15
+ attachment_names: string[];
15
16
  }
16
17
  export interface PlaywrightCase {
17
18
  id: string;
@@ -31,6 +32,7 @@ export interface PlaywrightCase {
31
32
  interrupted_instances: number;
32
33
  given_keys: string[];
33
34
  action_keys: string[];
35
+ attachment_names: string[];
34
36
  }
35
37
  export declare function collectCases(report: Record<string, unknown>, declaredIds: Set<string>): {
36
38
  cases: PlaywrightCaseInstance[];
@@ -75,6 +75,15 @@ function playwrightCase(id, test) {
75
75
  resultStatuses.length > 0 &&
76
76
  resultStatuses.at(-1) === "passed";
77
77
  const traces = results.map((result) => scenarioTrace(result));
78
+ const attachmentNames = [
79
+ ...new Set(results.flatMap((result) => Array.isArray(result.attachments)
80
+ ? result.attachments
81
+ .map(record)
82
+ .filter(Boolean)
83
+ .map((attachment) => attachment.name)
84
+ .filter((name) => typeof name === "string")
85
+ : [])),
86
+ ].sort();
78
87
  const givenKeys = traces[0]?.given_keys ?? [];
79
88
  const actionKeys = traces[0]?.action_keys ?? [];
80
89
  const traceConsistent = traces.every((trace) => same(trace.given_keys, givenKeys) && same(trace.action_keys, actionKeys));
@@ -91,6 +100,7 @@ function playwrightCase(id, test) {
91
100
  status,
92
101
  given_keys: traceConsistent ? givenKeys : [],
93
102
  action_keys: traceConsistent ? actionKeys : [],
103
+ attachment_names: attachmentNames,
94
104
  };
95
105
  }
96
106
  export function declaredCaseIds(check) {
@@ -99,8 +109,6 @@ export function declaredCaseIds(check) {
99
109
  ...check.positive_assertions,
100
110
  ...check.negative_assertions,
101
111
  ]) {
102
- if (!assertion.claims.length)
103
- continue;
104
112
  const match = /^playwright\.case\.([a-z0-9][a-z0-9-]*)\.passed$/u.exec(assertion.observation);
105
113
  if (match)
106
114
  result.add(assertion.key);
@@ -5,3 +5,4 @@ export interface PlaywrightEvidence {
5
5
  error: string | null;
6
6
  }
7
7
  export declare function extractPlaywrightEvidence(check: CompiledCheckV2, report: Record<string, unknown>, exitCode: number): PlaywrightEvidence;
8
+ export declare function designMethodAttachmentName(target: string, method: string, condition: string, kind: "record" | "observation"): string;
@@ -105,6 +105,32 @@ function evidenceRecordsForCases(check, cases) {
105
105
  comparison_artifact_path: target.comparison_artifact_path,
106
106
  });
107
107
  }
108
+ if (assertion?.evidence_capabilities.includes("design_method") &&
109
+ item.executed) {
110
+ for (const target of check.design_conformance_targets ?? []) {
111
+ const binding = target.verification_method_bindings.find((candidate) => candidate.assertion_ref === assertion.key);
112
+ if (!binding)
113
+ continue;
114
+ const requiredAttachments = binding.evidence_artifacts.flatMap((artifact) => [
115
+ designMethodAttachmentName(target.key, binding.method, artifact.condition_key, "record"),
116
+ designMethodAttachmentName(target.key, binding.method, artifact.condition_key, "observation"),
117
+ ]);
118
+ if (!requiredAttachments.every((name) => item.attachment_names.includes(name)))
119
+ continue;
120
+ evidenceRecords.push({
121
+ assertion_key: assertion.key,
122
+ capability: "design_method",
123
+ design_target_ref: target.key,
124
+ target_ref: target.target_ref,
125
+ method: binding.method,
126
+ cells: binding.evidence_artifacts.map((artifact) => ({
127
+ condition_key: artifact.condition_key,
128
+ artifact_path: artifact.path,
129
+ observation_artifact_path: artifact.observation_path,
130
+ })),
131
+ });
132
+ }
133
+ }
108
134
  }
109
135
  return evidenceRecords;
110
136
  }
@@ -167,10 +193,16 @@ function aggregateCases(instances) {
167
193
  rows.every((row) => same(row.action_keys, rows[0].action_keys))
168
194
  ? rows[0].action_keys
169
195
  : [],
196
+ attachment_names: rows.length === 0
197
+ ? []
198
+ : rows[0].attachment_names.filter((name) => rows.every((row) => row.attachment_names.includes(name))),
170
199
  };
171
200
  })
172
201
  .sort((left, right) => left.id.localeCompare(right.id));
173
202
  }
203
+ export function designMethodAttachmentName(target, method, condition, kind) {
204
+ return `ty-context-design-method:${target}:${method}:${condition}:${kind}`;
205
+ }
174
206
  function integer(value) {
175
207
  return Number.isInteger(value) && Number(value) >= 0 ? Number(value) : null;
176
208
  }
@@ -1,6 +1,8 @@
1
- import type { DeliveryControlV2, DeliveryObligationV2, DeliveryOwnerV2, KeyedPathV2, KeyedStatementV2 } from "./long-task-delivery-types.js";
1
+ import type { DeliveryControlRelationClosureV2, DeliveryControlRelationV2, DeliveryControlV2, DeliveryObligationV2, DeliveryOwnerV2, KeyedPathV2, KeyedStatementV2 } from "./long-task-delivery-types.js";
2
2
  export declare function parseKeyedStatements(value: unknown, label: string): KeyedStatementV2[];
3
3
  export declare function parseKeyedPaths(value: unknown, label: string): KeyedPathV2[];
4
4
  export declare function parseControls(value: unknown, label: string): DeliveryControlV2[];
5
+ export declare function parseControlRelations(value: unknown, label: string): DeliveryControlRelationV2[];
6
+ export declare function parseControlRelationClosure(value: unknown, label: string): DeliveryControlRelationClosureV2;
5
7
  export declare function parseOwner(value: unknown, label: string): DeliveryOwnerV2;
6
8
  export declare function parseObligations(value: unknown, label: string): DeliveryObligationV2[];
@@ -1,5 +1,7 @@
1
+ import { CONTROL_FIELD_NAMES } from "./long-task-control-fields.js";
2
+ import { parseKeyRefs } from "./long-task-applicability-shape.js";
1
3
  import { parseRequiredProofSurfaces } from "./long-task-required-proof-surfaces.js";
2
- import { array, key, object, repositoryFiles, repositoryPattern, repositoryPatterns, string, text, } from "./long-task-shape-primitives.js";
4
+ import { array, fail, key, literal, object, repositoryFiles, repositoryPattern, repositoryPatterns, string, text, } from "./long-task-shape-primitives.js";
3
5
  export function parseKeyedStatements(value, label) {
4
6
  return array(value, label).map((item, index) => {
5
7
  const itemLabel = `${label}[${index}]`;
@@ -23,9 +25,10 @@ export function parseKeyedPaths(value, label) {
23
25
  export function parseControls(value, label) {
24
26
  return array(value, label).map((item, index) => {
25
27
  const itemLabel = `${label}[${index}]`;
26
- const row = object(item, itemLabel, ["key", "location"], [
28
+ const row = object(item, itemLabel, ["key", "field_coverage"], [
27
29
  "surface",
28
30
  "region",
31
+ "location",
29
32
  "control_type",
30
33
  "label_content",
31
34
  "user_task",
@@ -46,11 +49,11 @@ export function parseControls(value, label) {
46
49
  "feedback",
47
50
  "accessibility",
48
51
  ]);
49
- return {
52
+ const control = {
50
53
  key: key(row.key, `${itemLabel}.key`),
51
54
  surface: optionalText(row, "surface", itemLabel),
52
55
  region: optionalText(row, "region", itemLabel),
53
- location: string(row.location, `${itemLabel}.location`),
56
+ location: optionalText(row, "location", itemLabel),
54
57
  control_type: optionalText(row, "control_type", itemLabel),
55
58
  label_content: optionalText(row, "label_content", itemLabel),
56
59
  user_task: optionalText(row, "user_task", itemLabel),
@@ -70,9 +73,44 @@ export function parseControls(value, label) {
70
73
  permission: optionalText(row, "permission", itemLabel),
71
74
  feedback: optionalText(row, "feedback", itemLabel),
72
75
  accessibility: optionalText(row, "accessibility", itemLabel),
76
+ field_coverage: parseControlFieldCoverage(row.field_coverage, `${itemLabel}.field_coverage`),
77
+ };
78
+ validateControlFieldCoverage(control, itemLabel);
79
+ return control;
80
+ });
81
+ }
82
+ export function parseControlRelations(value, label) {
83
+ return array(value, label).map((item, index) => {
84
+ const itemLabel = `${label}[${index}]`;
85
+ const row = object(item, itemLabel, [
86
+ "key",
87
+ "statement",
88
+ "control_refs",
89
+ "required_proof_surfaces",
90
+ "applicability_refs",
91
+ ]);
92
+ return {
93
+ key: key(row.key, `${itemLabel}.key`),
94
+ statement: string(row.statement, `${itemLabel}.statement`),
95
+ control_refs: parseKeyRefs(row.control_refs, `${itemLabel}.control_refs`),
96
+ required_proof_surfaces: parseRequiredProofSurfaces(row.required_proof_surfaces, `${itemLabel}.required_proof_surfaces`),
97
+ applicability_refs: parseKeyRefs(row.applicability_refs, `${itemLabel}.applicability_refs`),
73
98
  };
74
99
  });
75
100
  }
101
+ export function parseControlRelationClosure(value, label) {
102
+ const row = object(value, label, ["state", "statement"], ["applicability_refs"]);
103
+ const state = literal(row.state, ["specified", "not_applicable", "unresolved"], `${label}.state`);
104
+ return {
105
+ state,
106
+ statement: string(row.statement, `${label}.statement`),
107
+ applicability_refs: state === "unresolved"
108
+ ? Object.hasOwn(row, "applicability_refs")
109
+ ? parseKeyRefs(row.applicability_refs, `${label}.applicability_refs`)
110
+ : []
111
+ : parseKeyRefs(row.applicability_refs, `${label}.applicability_refs`),
112
+ };
113
+ }
76
114
  export function parseOwner(value, label) {
77
115
  const row = object(value, label, ["label", "context_refs", "path_globs"]);
78
116
  return {
@@ -88,14 +126,72 @@ export function parseObligations(value, label) {
88
126
  "key",
89
127
  "statement",
90
128
  "required_proof_surfaces",
129
+ "applicability_refs",
91
130
  ]);
92
131
  return {
93
132
  key: key(row.key, `${itemLabel}.key`),
94
133
  statement: string(row.statement, `${itemLabel}.statement`),
95
134
  required_proof_surfaces: parseRequiredProofSurfaces(row.required_proof_surfaces, `${itemLabel}.required_proof_surfaces`),
135
+ applicability_refs: parseKeyRefs(row.applicability_refs, `${itemLabel}.applicability_refs`),
96
136
  };
97
137
  });
98
138
  }
139
+ function parseControlFieldCoverage(value, label) {
140
+ return array(value, label).map((item, index) => {
141
+ const itemLabel = `${label}[${index}]`;
142
+ const row = object(item, itemLabel, ["fields", "state"], ["statement", "applicability_refs"]);
143
+ const fields = array(row.fields, `${itemLabel}.fields`).map((field, fieldIndex) => literal(field, CONTROL_FIELD_NAMES, `${itemLabel}.fields[${fieldIndex}]`));
144
+ if (!fields.length)
145
+ fail(`${itemLabel}.fields`, "must not be empty");
146
+ const state = literal(row.state, ["specified", "not_applicable", "unresolved"], `${itemLabel}.state`);
147
+ if (state === "specified") {
148
+ if (Object.hasOwn(row, "statement"))
149
+ fail(`${itemLabel}.statement`, "forbidden for specified fields");
150
+ return {
151
+ fields,
152
+ state,
153
+ applicability_refs: parseRequiredApplicabilityRefs(row, itemLabel),
154
+ };
155
+ }
156
+ const statement = string(row.statement, `${itemLabel}.statement`);
157
+ if (state === "not_applicable")
158
+ return {
159
+ fields,
160
+ state,
161
+ statement,
162
+ applicability_refs: parseRequiredApplicabilityRefs(row, itemLabel),
163
+ };
164
+ if (Object.hasOwn(row, "applicability_refs") &&
165
+ array(row.applicability_refs, `${itemLabel}.applicability_refs`).length)
166
+ fail(`${itemLabel}.applicability_refs`, "must be empty while unresolved");
167
+ return { fields, state, statement, applicability_refs: [] };
168
+ });
169
+ }
170
+ function parseRequiredApplicabilityRefs(row, label) {
171
+ const refs = parseKeyRefs(row.applicability_refs, `${label}.applicability_refs`);
172
+ if (!refs.length)
173
+ fail(`${label}.applicability_refs`, "must not be empty");
174
+ return refs;
175
+ }
176
+ function validateControlFieldCoverage(control, label) {
177
+ const owners = new Map();
178
+ for (const [index, entry] of control.field_coverage.entries()) {
179
+ for (const field of entry.fields) {
180
+ const previous = owners.get(field);
181
+ if (previous !== undefined)
182
+ fail(`${label}.field_coverage[${index}].fields`, `field ${field} already owned by entry ${previous}`);
183
+ owners.set(field, index);
184
+ const hasStatement = Boolean(control[field].trim());
185
+ if (entry.state === "specified" && !hasStatement)
186
+ fail(`${label}.${field}`, "must be non-empty when field state is specified");
187
+ if (entry.state !== "specified" && hasStatement)
188
+ fail(`${label}.${field}`, `must be empty when field state is ${entry.state}`);
189
+ }
190
+ }
191
+ const missing = CONTROL_FIELD_NAMES.filter((field) => !owners.has(field));
192
+ if (missing.length)
193
+ fail(`${label}.field_coverage`, `missing fields ${missing.join(",")}`);
194
+ }
99
195
  function optionalText(row, field, label) {
100
196
  return Object.hasOwn(row, field) ? text(row[field], `${label}.${field}`) : "";
101
197
  }
@@ -1,3 +1,4 @@
1
+ import { parseKeyRefs } from "./long-task-applicability-shape.js";
1
2
  import { parseRequiredProofSurfaces } from "./long-task-required-proof-surfaces.js";
2
3
  import { array, key, object, string } from "./long-task-shape-primitives.js";
3
4
  export function parseRequirements(value, label) {
@@ -7,12 +8,14 @@ export function parseRequirements(value, label) {
7
8
  "key",
8
9
  "statement",
9
10
  "required_proof_surfaces",
11
+ "applicability_refs",
10
12
  ]);
11
13
  const surfaces = parseRequiredProofSurfaces(row.required_proof_surfaces, `${itemLabel}.required_proof_surfaces`);
12
14
  return {
13
15
  key: key(row.key, `${itemLabel}.key`),
14
16
  statement: string(row.statement, `${itemLabel}.statement`),
15
17
  required_proof_surfaces: surfaces,
18
+ applicability_refs: parseKeyRefs(row.applicability_refs, `${itemLabel}.applicability_refs`),
16
19
  };
17
20
  });
18
21
  }