project-tiny-context-harness 0.7.9 → 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 -380
  3. package/assets/README.md +567 -557
  4. package/assets/README.zh-CN.md +339 -326
  5. package/assets/agents/.gitkeep +1 -1
  6. package/assets/agents/AGENTS_CORE.md +66 -64
  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 -131
  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 -92
  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 -105
  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
@@ -0,0 +1 @@
1
+ export declare function designResource(args: string[]): Promise<void>;
@@ -0,0 +1,32 @@
1
+ import { preflightDesignResourceHandoff } from "../lib/design-resource-handoff-validation.js";
2
+ import { normalizeRepositoryFile } from "../lib/long-task-paths.js";
3
+ import { canonicalJson } from "../lib/strict-codec.js";
4
+ export async function designResource(args) {
5
+ const [subcommand = "help", ...rest] = args;
6
+ if (subcommand === "help") {
7
+ help();
8
+ return;
9
+ }
10
+ if (subcommand !== "preflight")
11
+ throw new Error(`Unknown design-resource subcommand: ${subcommand}`);
12
+ const json = rest.includes("--json");
13
+ const positional = rest.filter((item) => item !== "--json");
14
+ if (positional.length !== 1)
15
+ throw new Error("usage: ty-context design-resource preflight <handoff.md> [--json]");
16
+ const handoffPath = normalizeRepositoryFile(positional[0], "design_resource_handoff");
17
+ const result = await preflightDesignResourceHandoff(process.cwd(), handoffPath);
18
+ if (json) {
19
+ process.stdout.write(canonicalJson(result));
20
+ return;
21
+ }
22
+ console.log(`Design resource handoff ready: ${result.handoff_path}`);
23
+ console.log(`Scope: ${result.handoff.scope.key}`);
24
+ console.log(`Targets: ${result.handoff.targets.map((item) => item.key).join(", ")}`);
25
+ console.log(`Coverage: ${result.counts.subjects} subjects x 8 dimensions (${result.counts.coverage} grouped rows)`);
26
+ console.log(`Acceptance blockers: ${result.counts.acceptance_blockers}`);
27
+ }
28
+ function help() {
29
+ console.log(`ty-context design-resource commands:
30
+ preflight <handoff.md> [--json]
31
+ Validate one selected implementation handoff`);
32
+ }
@@ -3,6 +3,7 @@ import { compositeLongTask } from "./composite-long-task.js";
3
3
  import { compositeCampaign } from "./composite-campaign.js";
4
4
  import { longTask } from "./long-task.js";
5
5
  import { deliverySet } from "./delivery-set.js";
6
+ import { designResource } from "./design-resource.js";
6
7
  import { doctor } from "./doctor.js";
7
8
  import { exportContext } from "./export-context.js";
8
9
  import { enable } from "./enable.js";
@@ -28,6 +29,7 @@ export const commands = {
28
29
  "validate-code-modularity": (args) => validate(["validate-code-modularity", ...args]),
29
30
  "validate-harness": (args) => validate(["validate-harness", ...args]),
30
31
  "long-task": (args) => withLongTaskProfile(args, longTask),
32
+ "design-resource": designResource,
31
33
  "delivery-set": deliverySet,
32
34
  "composite-long-task": compositeLongTask,
33
35
  "composite-campaign": compositeCampaign,
@@ -52,6 +54,8 @@ export function help() {
52
54
  validate-code-modularity
53
55
  Enforce touched handwritten source file modularity
54
56
  validate-harness Run validate-context and validate-code-modularity
57
+ design-resource <subcommand>
58
+ Validate a selected design-resource implementation handoff
55
59
  long-task <subcommand>
56
60
  Manage one Canonical Delivery Contract in the current workspace
57
61
  delivery-set <subcommand>
@@ -1,3 +1,4 @@
1
1
  export declare function option(args: string[], name: string): string | undefined;
2
+ export declare function flag(args: string[], name: string): boolean;
2
3
  export declare function rejectOptions(args: string[], allowed: string[]): void;
3
4
  export declare function rejectUnknown(actual: string[], allowed: string[]): void;
@@ -9,6 +9,12 @@ export function option(args, name) {
9
9
  throw new Error(`${name} requires a value`);
10
10
  return value;
11
11
  }
12
+ export function flag(args, name) {
13
+ const count = args.filter((value) => value === name).length;
14
+ if (count > 1)
15
+ throw new Error(`duplicate option: ${name}`);
16
+ return count === 1;
17
+ }
12
18
  export function rejectOptions(args, allowed) {
13
19
  for (let index = 0; index < args.length; index += 2)
14
20
  if (!allowed.includes(args[index]) || !args[index + 1])
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { diagnoseAuthorityRevision } from "../lib/long-task-authority-revision-diagnosis.js";
3
+ import { authorityRevisionDecisionNextAction } from "../lib/long-task-authority-revision-brief.js";
3
4
  import { projectAuthorityRevisionDecision } from "../lib/long-task-authority-revision-summary.js";
4
5
  import { canRetainProgressForSupportingContextRevision } from "../lib/long-task-context-authority.js";
5
6
  import { compileDeliveryContract } from "../lib/long-task-delivery-compiler.js";
@@ -89,7 +90,7 @@ function printCompileResult(compiled, previous, preserveProgress, revisionPropos
89
90
  ? projectAuthorityRevisionDecision(revisionProposal)
90
91
  : null,
91
92
  next_action: firstAuthorityLock
92
- ? "Complete the one-time model choice, then begin rolling implementation."
93
+ ? "End this turn now and ask the user to choose continue_current_model or switch_model_then_resume. Do not implement, edit files, build, or test until an explicit task-specific model strategy is received; generic continue or resume language does not satisfy this checkpoint."
93
94
  : authorityChanged
94
95
  ? "Run status or resume, then continue rolling implementation or repair under the adopted Authority Revision."
95
96
  : "Continue rolling implementation or repair under the active Authority.",
@@ -116,14 +117,15 @@ async function printPendingDecision(workdir, previous) {
116
117
  const pending = await readPendingAuthorityRevision(workdir);
117
118
  if (!pending)
118
119
  throw new Error("authority_revision_pending_state_missing");
120
+ const decision = projectAuthorityRevisionDecision(pending);
119
121
  console.log(JSON.stringify({
120
122
  status: "authority_revision_pending",
121
123
  acceptance_authorized: false,
122
124
  delivery_completed_by_this_event: false,
123
125
  native_goal_effect: "none",
124
126
  active_compiled_identity: previous?.compiled_identity ?? null,
125
- pending_authority_revision: projectAuthorityRevisionDecision(pending),
126
- next_action: "Ask the user to approve or reject this exact material revision; keep the previous Authority active.",
127
+ pending_authority_revision: decision,
128
+ next_action: authorityRevisionDecisionNextAction(decision),
127
129
  }));
128
130
  }
129
131
  async function diagnoseRevision(workdir, args) {
@@ -156,7 +158,17 @@ function executionModelCheckpoint(firstAuthorityLock) {
156
158
  required: true,
157
159
  phase: "post_authority_lock_pre_implementation",
158
160
  options: ["continue_current_model", "switch_model_then_resume"],
159
- message: "Authority Lock created. Pause before implementation and ask the user whether to continue with the current model or switch models, then resume this active Long-Task.",
161
+ turn_boundary: "end_current_turn",
162
+ blocked_until_explicit_choice: [
163
+ "product_implementation",
164
+ "file_edits",
165
+ "build",
166
+ "test_execution",
167
+ ],
168
+ explicit_task_specific_choice_required: true,
169
+ prior_explicit_task_specific_choice_satisfies: true,
170
+ generic_continue_satisfies: false,
171
+ message: "Authority Lock created. End the current turn before product implementation, file edits, builds, or tests. Ask the user to explicitly choose continue_current_model or switch_model_then_resume. Generic continue, resume, finish, or continue-goal language does not satisfy this checkpoint.",
160
172
  };
161
173
  }
162
174
  function message(error) {
@@ -5,9 +5,10 @@ import { abandonLongTaskState, forceClearCorruptActiveState, } from "../lib/long
5
5
  import { verifyDeliveryContract } from "../lib/long-task-verifier-v2.js";
6
6
  import { repositoryRoot } from "../lib/long-task-workspace.js";
7
7
  import { initializeLongTask, preflightLongTask, } from "./long-task-authoring.js";
8
- import { option, rejectOptions, rejectUnknown, } from "./long-task-command-args.js";
8
+ import { flag, option, rejectOptions, rejectUnknown, } from "./long-task-command-args.js";
9
9
  import { explainLongTask } from "./long-task-explain.js";
10
10
  import { handleLongTaskRevisionCommand } from "./long-task-revision.js";
11
+ import { previewVerificationExecution } from "../lib/long-task-verification-preview.js";
11
12
  export async function longTask(args) {
12
13
  const subcommand = args[0] ?? "help";
13
14
  if (subcommand === "help")
@@ -96,9 +97,15 @@ export async function longTask(args) {
96
97
  throw new Error(`Unknown long-task subcommand: ${subcommand}`);
97
98
  }
98
99
  async function verify(workdir, args) {
99
- const outcome = option(args, "--outcome");
100
- const check = option(args, "--check");
101
- rejectOptions(args, ["--outcome", "--check"]);
100
+ const explain = flag(args, "--explain");
101
+ const optionArgs = args.filter((value) => value !== "--explain");
102
+ const outcome = option(optionArgs, "--outcome");
103
+ const check = option(optionArgs, "--check");
104
+ rejectOptions(optionArgs, ["--outcome", "--check"]);
105
+ if (explain) {
106
+ console.log(JSON.stringify(await previewVerificationExecution(workdir, { outcome, check })));
107
+ return;
108
+ }
102
109
  const result = await verifyDeliveryContract(workdir, { outcome, check });
103
110
  console.log(JSON.stringify(result));
104
111
  if (result.findings.length)
@@ -125,7 +132,7 @@ function help() {
125
132
  diagnose-revision <workdir> [--outcome <key>] [--check <key>]
126
133
  approve-authority-revision <workdir> --revision <sha>
127
134
  explain <workdir>
128
- verify <workdir> [--outcome <key>] [--check <key>]
135
+ verify <workdir> [--outcome <key>] [--check <key>] [--explain]
129
136
  status <workdir>
130
137
  resume <workdir>
131
138
  doctor <workdir>
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export { commands } from "./commands/index.js";
2
2
  export { compileDeliveryContract, type CompileDeliveryOptionsV2, } from "./lib/long-task-delivery-compiler.js";
3
3
  export { DELIVERY_CONTRACT_FILE, parseDeliveryContract, parseDeliveryContractBundle, } from "./lib/long-task-delivery-parser.js";
4
4
  export { evaluateContractBoundary } from "./lib/long-task-boundary-check.js";
5
+ export { preflightDesignResourceHandoff } from "./lib/design-resource-handoff-validation.js";
5
6
  export { classifyLongTaskRisk, validateRiskProof, } from "./lib/long-task-risk.js";
6
7
  export { verifyDeliveryContract } from "./lib/long-task-verifier-v2.js";
7
8
  export { runDeliveryFinalGate } from "./lib/long-task-final-v2.js";
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@ export { commands } from "./commands/index.js";
2
2
  export { compileDeliveryContract, } from "./lib/long-task-delivery-compiler.js";
3
3
  export { DELIVERY_CONTRACT_FILE, parseDeliveryContract, parseDeliveryContractBundle, } from "./lib/long-task-delivery-parser.js";
4
4
  export { evaluateContractBoundary } from "./lib/long-task-boundary-check.js";
5
+ export { preflightDesignResourceHandoff } from "./lib/design-resource-handoff-validation.js";
5
6
  export { classifyLongTaskRisk, validateRiskProof, } from "./lib/long-task-risk.js";
6
7
  export { verifyDeliveryContract } from "./lib/long-task-verifier-v2.js";
7
8
  export { runDeliveryFinalGate } from "./lib/long-task-final-v2.js";
@@ -125,7 +125,8 @@ function designMdTemplate() {
125
125
  "- Authored exact-value token source: not selected.",
126
126
  "- Generation direction and generated token targets: not selected.",
127
127
  "- Durable design references: none selected.",
128
- "- For each selected reference, record a stable id, surface/route/component, project path or URI, `exact-target` / `constraint` / `inspiration` interpretation, and the viewport/theme/state conditions it covers.",
128
+ "- For each selected reference, record a stable id, surface/route/component, `exact-target` / `constraint` / `inspiration` interpretation, viewport/theme/state coverage, a readable immutable adopted path/URI plus digest, and the editable upstream owner/locator/update route (or an explicit manual/external-update boundary).",
129
+ "- Relevant product/design/development work must open affected selected exact targets/constraints; an index entry alone is not resource consumption.",
129
130
  "",
130
131
  "## Colors",
131
132
  "",
@@ -161,6 +162,7 @@ function designMdTemplate() {
161
162
  "- When there is a scan target such as UI source, page files, build output or a local/remote URL, run `npx impeccable detect <target>` before finalizing design changes.",
162
163
  "- Treat Impeccable findings as design-review signals: fix issues that affect clarity, consistency, accessibility or trust, and note when there is no suitable scan target.",
163
164
  "- After design decisions stabilize, replace the unconfigured marker, index the selected targets and authored token source/generation direction, and record durable tokens, component rules and do/don't guidance.",
165
+ "- Change an adopted target through its recorded upstream route, create or approve a new immutable version/digest, and update its index entry; never overwrite the adopted baseline in place.",
164
166
  "",
165
167
  ].join("\n");
166
168
  }
@@ -0,0 +1,4 @@
1
+ import type { ParsedDesignResourceHandoffV1 } from "./design-resource-handoff-types.js";
2
+ export type DesignResource = ParsedDesignResourceHandoffV1["handoff"]["resources"][number];
3
+ export declare function htmlAttributes(source: string): Map<string, string>;
4
+ export declare function isTextResource(resource: DesignResource): boolean;
@@ -0,0 +1,14 @@
1
+ export function htmlAttributes(source) {
2
+ const attributes = new Map();
3
+ for (const match of source.matchAll(/([A-Za-z_:][A-Za-z0-9_.:-]*)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/gu))
4
+ attributes.set(match[1].toLowerCase(), match[2] ?? match[3] ?? match[4] ?? "");
5
+ return attributes;
6
+ }
7
+ export function isTextResource(resource) {
8
+ return (resource.media_type.startsWith("text/") ||
9
+ [
10
+ "application/javascript",
11
+ "application/json",
12
+ "application/xhtml+xml",
13
+ ].includes(resource.media_type));
14
+ }
@@ -0,0 +1,2 @@
1
+ import type { ParsedDesignResourceHandoffV1 } from "./design-resource-handoff-types.js";
2
+ export declare function validateDesignResourceFiles(repository: string, parsed: ParsedDesignResourceHandoffV1): Promise<void>;
@@ -0,0 +1,142 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { htmlAttributes, isTextResource, } from "./design-resource-handoff-file-primitives.js";
4
+ import { invalidDesignResourceHandoff } from "./design-resource-handoff-validation-primitives.js";
5
+ import { validateDesignResourceWebDependencyClosure } from "./design-resource-handoff-web-dependency-validation.js";
6
+ import { assertProtectedRepositoryFile } from "./long-task-protected-files.js";
7
+ import { markdownAnchors } from "./long-task-source-validation.js";
8
+ export async function validateDesignResourceFiles(repository, parsed) {
9
+ const resources = new Map(parsed.handoff.resources.map((resource) => [resource.key, resource]));
10
+ const contents = new Map();
11
+ for (const resource of parsed.handoff.resources) {
12
+ const file = await assertProtectedRepositoryFile(repository, path.resolve(repository, ...resource.path.split("/")), `design_resource:${resource.key}`);
13
+ contents.set(resource.key, await readFile(file));
14
+ }
15
+ for (const evidence of parsed.handoff.evidence)
16
+ validateLocator(evidence, resources.get(evidence.resource_ref), contents.get(evidence.resource_ref));
17
+ for (const target of parsed.handoff.targets)
18
+ if (target.source_profile.kind === "implementation_web")
19
+ validateDesignResourceWebDependencyClosure(target, resources, contents);
20
+ }
21
+ function validateLocator(evidence, resource, bytes) {
22
+ const { kind, value } = evidence.locator;
23
+ const label = `${evidence.key}:${resource.key}:${kind}:${value}`;
24
+ validateEvidenceResourceCompatibility(evidence, resource);
25
+ if (kind === "whole_resource") {
26
+ if (value !== ".")
27
+ invalidDesignResourceHandoff("locator_whole_resource_value", label);
28
+ if (resource.media_type.startsWith("text/") &&
29
+ !["asset", "motion_capture", "frame"].includes(evidence.kind))
30
+ invalidDesignResourceHandoff("locator_whole_resource_too_broad", label);
31
+ return;
32
+ }
33
+ const content = bytes.toString("utf8");
34
+ if (kind === "html_selector") {
35
+ requireMediaType(resource, ["text/html", "application/xhtml+xml"], label);
36
+ if (!htmlSelectorExists(content, value))
37
+ invalidDesignResourceHandoff("locator_not_found", label);
38
+ return;
39
+ }
40
+ if (kind === "markdown_anchor") {
41
+ requireMediaType(resource, ["text/markdown"], label);
42
+ if (value.startsWith("#") || !markdownAnchors(content).has(value))
43
+ invalidDesignResourceHandoff("locator_not_found", label);
44
+ return;
45
+ }
46
+ if (kind === "json_pointer") {
47
+ requireMediaType(resource, ["application/json"], label);
48
+ if (!jsonPointerExists(content, value))
49
+ invalidDesignResourceHandoff("locator_not_found", label);
50
+ return;
51
+ }
52
+ if (kind === "css_selector") {
53
+ requireMediaType(resource, ["text/css"], label);
54
+ if (!cssSelectorExists(content, value))
55
+ invalidDesignResourceHandoff("locator_not_found", label);
56
+ return;
57
+ }
58
+ requireMediaType(resource, ["text/css"], label);
59
+ if (!/^--[A-Za-z0-9_-]+$/u.test(value))
60
+ invalidDesignResourceHandoff("locator_css_custom_property_invalid", label);
61
+ const customProperty = new RegExp(`${escapeRegExp(value)}\\s*:`, "u");
62
+ if (!customProperty.test(content))
63
+ invalidDesignResourceHandoff("locator_not_found", label);
64
+ }
65
+ function validateEvidenceResourceCompatibility(evidence, resource) {
66
+ const machineReadableKinds = new Set([
67
+ "prototype_transition",
68
+ "motion_spec",
69
+ "responsive_spec",
70
+ "input_spec",
71
+ "accessibility_spec",
72
+ "semantic_tree",
73
+ "token_spec",
74
+ "annotation",
75
+ ]);
76
+ if (machineReadableKinds.has(evidence.kind) && !isTextResource(resource))
77
+ invalidDesignResourceHandoff("evidence_resource_media_type_incompatible", `${evidence.key}:${evidence.kind}:${resource.media_type}`);
78
+ }
79
+ function requireMediaType(resource, expected, detail) {
80
+ if (!expected.includes(resource.media_type))
81
+ invalidDesignResourceHandoff("locator_media_type_incompatible", `${detail}:${resource.media_type}`);
82
+ }
83
+ function htmlSelectorExists(content, selector) {
84
+ const parsed = /^(?:([A-Za-z][A-Za-z0-9:-]*))?(?:#([A-Za-z][A-Za-z0-9_.:-]*))?(?:\[([A-Za-z_:][A-Za-z0-9_.:-]*)(?:=(?:"([^"]*)"|'([^']*)'|([^\]\s]+)))?\])?$/u.exec(selector);
85
+ if (!parsed || (!parsed[1] && !parsed[2] && !parsed[3]))
86
+ invalidDesignResourceHandoff("locator_html_selector_unsupported", selector);
87
+ const [, expectedTag, expectedId, expectedAttribute] = parsed;
88
+ const expectedValue = parsed[4] ?? parsed[5] ?? parsed[6];
89
+ for (const match of content.matchAll(/<([A-Za-z][A-Za-z0-9:-]*)\b([^<>]*)>/gu)) {
90
+ if (expectedTag && match[1].toLowerCase() !== expectedTag.toLowerCase())
91
+ continue;
92
+ const attributes = htmlAttributes(match[2]);
93
+ if (expectedId && attributes.get("id") !== expectedId)
94
+ continue;
95
+ if (expectedAttribute) {
96
+ const attributeName = expectedAttribute.toLowerCase();
97
+ if (!attributes.has(attributeName))
98
+ continue;
99
+ if (expectedValue !== undefined &&
100
+ attributes.get(attributeName) !== expectedValue)
101
+ continue;
102
+ }
103
+ return true;
104
+ }
105
+ return false;
106
+ }
107
+ function jsonPointerExists(content, pointer) {
108
+ if (!pointer.startsWith("/"))
109
+ return false;
110
+ let current;
111
+ try {
112
+ current = JSON.parse(content);
113
+ }
114
+ catch {
115
+ return false;
116
+ }
117
+ for (const token of pointer
118
+ .slice(1)
119
+ .split("/")
120
+ .map((part) => part.replace(/~1/gu, "/").replace(/~0/gu, "~"))) {
121
+ if (current === null ||
122
+ typeof current !== "object" ||
123
+ !Object.hasOwn(current, token))
124
+ return false;
125
+ current = current[token];
126
+ }
127
+ return true;
128
+ }
129
+ function cssSelectorExists(content, selector) {
130
+ if (!selector.trim() || selector.includes("{") || selector.includes("}"))
131
+ invalidDesignResourceHandoff("locator_css_selector_unsupported", selector);
132
+ for (const match of content.matchAll(/(?:^|\})\s*([^@{}][^{}]*)\{/gmu))
133
+ if (match[1]
134
+ .split(",")
135
+ .map((item) => item.trim())
136
+ .includes(selector))
137
+ return true;
138
+ return false;
139
+ }
140
+ function escapeRegExp(value) {
141
+ return value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
142
+ }
@@ -0,0 +1,3 @@
1
+ import type { ParsedDesignResourceHandoffV1 } from "./design-resource-handoff-types.js";
2
+ export declare function containsDesignResourceHandoff(content: string): boolean;
3
+ export declare function parseDesignResourceHandoffMarkdown(handoffPath: string, content: string): ParsedDesignResourceHandoffV1;
@@ -0,0 +1,29 @@
1
+ import { parseDesignResourceHandoffShape } from "./design-resource-handoff-shape.js";
2
+ import { parseSourceItems } from "./long-task-source-item-parser.js";
3
+ import { parseStrictYaml } from "./strict-codec.js";
4
+ const FENCE = /^```yaml[ \t]+design-resource-handoff-v1[ \t]*\r?\n([\s\S]*?)^```[ \t]*$/gmu;
5
+ export function containsDesignResourceHandoff(content) {
6
+ FENCE.lastIndex = 0;
7
+ return FENCE.test(content);
8
+ }
9
+ export function parseDesignResourceHandoffMarkdown(handoffPath, content) {
10
+ FENCE.lastIndex = 0;
11
+ const blocks = [...content.matchAll(FENCE)];
12
+ if (blocks.length !== 1)
13
+ throw new Error(`design_resource_handoff_invalid:block_count:${handoffPath}:${blocks.length}`);
14
+ try {
15
+ const sourceItems = parseSourceItems(handoffPath, content);
16
+ return {
17
+ handoff_path: handoffPath,
18
+ handoff: parseDesignResourceHandoffShape(parseStrictYaml(blocks[0][1])),
19
+ source_item_keys: sourceItems.map((item) => item.key),
20
+ source_item_kinds: Object.fromEntries(sourceItems.map((item) => [item.key, item.kind])),
21
+ };
22
+ }
23
+ catch (error) {
24
+ const message = error instanceof Error ? error.message : String(error);
25
+ if (message.startsWith("design_resource_handoff_invalid:"))
26
+ throw error;
27
+ throw new Error(`design_resource_handoff_invalid:shape:${message}`);
28
+ }
29
+ }
@@ -0,0 +1,3 @@
1
+ import type { DesignResourceDimension, DesignResourceEvidenceKind, DesignResourceVerificationMethod } from "./design-resource-handoff-types.js";
2
+ export declare const DESIGN_RESOURCE_EVIDENCE_BY_DIMENSION: Record<DesignResourceDimension, readonly DesignResourceEvidenceKind[]>;
3
+ export declare const DESIGN_RESOURCE_METHODS_BY_DIMENSION: Record<DesignResourceDimension, readonly DesignResourceVerificationMethod[]>;
@@ -0,0 +1,36 @@
1
+ export const DESIGN_RESOURCE_EVIDENCE_BY_DIMENSION = {
2
+ surface_flow: [
3
+ "frame",
4
+ "prototype_state",
5
+ "prototype_transition",
6
+ "annotation",
7
+ ],
8
+ visual_content: ["frame", "component_variant", "token_spec", "annotation"],
9
+ component_control: [
10
+ "frame",
11
+ "component_variant",
12
+ "prototype_state",
13
+ "token_spec",
14
+ "annotation",
15
+ ],
16
+ state_interaction: [
17
+ "component_variant",
18
+ "prototype_state",
19
+ "prototype_transition",
20
+ "input_spec",
21
+ ],
22
+ motion: ["prototype_transition", "motion_spec", "motion_capture"],
23
+ adaptation_input: ["responsive_spec", "input_spec"],
24
+ accessibility: ["accessibility_spec", "semantic_tree", "input_spec"],
25
+ assets: ["asset", "token_spec", "annotation"],
26
+ };
27
+ export const DESIGN_RESOURCE_METHODS_BY_DIMENSION = {
28
+ surface_flow: ["layout_geometry", "interaction_trace"],
29
+ visual_content: ["visual_pixel", "design_token", "content"],
30
+ component_control: ["visual_pixel", "design_token", "component_state"],
31
+ state_interaction: ["component_state", "interaction_trace"],
32
+ motion: ["motion_timeline"],
33
+ adaptation_input: ["responsive_reflow", "input_method"],
34
+ accessibility: ["accessibility_semantics"],
35
+ assets: ["asset_integrity"],
36
+ };
@@ -0,0 +1,4 @@
1
+ import type { DesignResourceHandoffBlockerV1, DesignResourceHandoffCoverageV1, DesignResourceHandoffEvidenceV1 } from "./design-resource-handoff-types.js";
2
+ export declare function parseDesignResourceHandoffEvidence(value: unknown): DesignResourceHandoffEvidenceV1[];
3
+ export declare function parseDesignResourceHandoffCoverage(value: unknown): DesignResourceHandoffCoverageV1[];
4
+ export declare function parseDesignResourceHandoffBlockers(value: unknown): DesignResourceHandoffBlockerV1[];
@@ -0,0 +1,84 @@
1
+ import { DESIGN_RESOURCE_DIMENSIONS, DESIGN_RESOURCE_EVIDENCE_KINDS, DESIGN_RESOURCE_LOCATOR_KINDS, } from "./design-resource-handoff-types.js";
2
+ import { contractKey, contractKeys, sourceItemKeys, stableKey, stableKeys, verificationMethods, } from "./design-resource-handoff-shape-primitives.js";
3
+ import { array, literal, object, string, } from "./long-task-shape-primitives.js";
4
+ export function parseDesignResourceHandoffEvidence(value) {
5
+ return array(value, "design_resource_handoff.evidence").map((item, index) => {
6
+ const label = `design_resource_handoff.evidence[${index}]`;
7
+ const row = object(item, label, [
8
+ "key",
9
+ "resource_ref",
10
+ "kind",
11
+ "locator",
12
+ "condition_refs",
13
+ ]);
14
+ const locator = object(row.locator, `${label}.locator`, ["kind", "value"]);
15
+ return {
16
+ key: stableKey(row.key, `${label}.key`),
17
+ resource_ref: stableKey(row.resource_ref, `${label}.resource_ref`),
18
+ kind: literal(row.kind, DESIGN_RESOURCE_EVIDENCE_KINDS, `${label}.kind`),
19
+ locator: {
20
+ kind: literal(locator.kind, DESIGN_RESOURCE_LOCATOR_KINDS, `${label}.locator.kind`),
21
+ value: string(locator.value, `${label}.locator.value`),
22
+ },
23
+ condition_refs: contractKeys(row.condition_refs, `${label}.condition_refs`),
24
+ };
25
+ });
26
+ }
27
+ export function parseDesignResourceHandoffCoverage(value) {
28
+ return array(value, "design_resource_handoff.coverage").map((item, index) => {
29
+ const label = `design_resource_handoff.coverage[${index}]`;
30
+ const row = object(item, label, [
31
+ "key",
32
+ "subject_refs",
33
+ "dimension",
34
+ "disposition",
35
+ "target_refs",
36
+ "condition_refs",
37
+ "evidence_refs",
38
+ "source_item_refs",
39
+ "verification_methods",
40
+ "rationale",
41
+ ]);
42
+ return {
43
+ key: stableKey(row.key, `${label}.key`),
44
+ subject_refs: stableKeys(row.subject_refs, `${label}.subject_refs`),
45
+ dimension: literal(row.dimension, DESIGN_RESOURCE_DIMENSIONS, `${label}.dimension`),
46
+ disposition: literal(row.disposition, [
47
+ "covered",
48
+ "not_applicable",
49
+ "excluded_by_scope",
50
+ "decision_required",
51
+ "unavailable",
52
+ ], `${label}.disposition`),
53
+ target_refs: contractKeys(row.target_refs, `${label}.target_refs`),
54
+ condition_refs: contractKeys(row.condition_refs, `${label}.condition_refs`),
55
+ evidence_refs: stableKeys(row.evidence_refs, `${label}.evidence_refs`),
56
+ source_item_refs: sourceItemKeys(row.source_item_refs, `${label}.source_item_refs`),
57
+ verification_methods: verificationMethods(row.verification_methods, `${label}.verification_methods`),
58
+ rationale: string(row.rationale, `${label}.rationale`),
59
+ };
60
+ });
61
+ }
62
+ export function parseDesignResourceHandoffBlockers(value) {
63
+ return array(value, "design_resource_handoff.acceptance_blockers").map((item, index) => {
64
+ const label = `design_resource_handoff.acceptance_blockers[${index}]`;
65
+ const row = object(item, label, [
66
+ "key",
67
+ "target_refs",
68
+ "subject_refs",
69
+ "dimensions",
70
+ "source_item_refs",
71
+ "verification_methods",
72
+ "description",
73
+ ]);
74
+ return {
75
+ key: contractKey(row.key, `${label}.key`),
76
+ target_refs: contractKeys(row.target_refs, `${label}.target_refs`),
77
+ subject_refs: stableKeys(row.subject_refs, `${label}.subject_refs`),
78
+ dimensions: array(row.dimensions, `${label}.dimensions`).map((dimension, itemIndex) => literal(dimension, DESIGN_RESOURCE_DIMENSIONS, `${label}.dimensions[${itemIndex}]`)),
79
+ source_item_refs: sourceItemKeys(row.source_item_refs, `${label}.source_item_refs`),
80
+ verification_methods: verificationMethods(row.verification_methods, `${label}.verification_methods`),
81
+ description: string(row.description, `${label}.description`),
82
+ };
83
+ });
84
+ }
@@ -0,0 +1,9 @@
1
+ import type { DesignResourceHandoffCoverageV1 } from "./design-resource-handoff-types.js";
2
+ export declare function stableKey(value: unknown, label: string): string;
3
+ export declare function stableKeys(value: unknown, label: string): string[];
4
+ export declare function contractKey(value: unknown, label: string): string;
5
+ export declare function contractKeys(value: unknown, label: string): string[];
6
+ export declare function sourceItemKeys(value: unknown, label: string): string[];
7
+ export declare function verificationMethods(value: unknown, label: string): DesignResourceHandoffCoverageV1["verification_methods"];
8
+ export declare function positiveInteger(value: unknown, label: string): number;
9
+ export declare function designResourceShapeFail(label: string, message: string): never;
@@ -0,0 +1,44 @@
1
+ import { DESIGN_RESOURCE_VERIFICATION_METHODS } from "./design-resource-handoff-types.js";
2
+ import { array, literal, string } from "./long-task-shape-primitives.js";
3
+ const KEY = /^[a-z0-9][a-z0-9._-]*$/u;
4
+ const SOURCE_ITEM_KEY = /^[a-z0-9][a-z0-9-]*$/u;
5
+ export function stableKey(value, label) {
6
+ const result = string(value, label);
7
+ if (!KEY.test(result))
8
+ designResourceShapeFail(label, "must match ^[a-z0-9][a-z0-9._-]*$");
9
+ return result;
10
+ }
11
+ export function stableKeys(value, label) {
12
+ return array(value, label).map((item, index) => stableKey(item, `${label}[${index}]`));
13
+ }
14
+ export function contractKey(value, label) {
15
+ const result = string(value, label);
16
+ if (!SOURCE_ITEM_KEY.test(result))
17
+ designResourceShapeFail(label, "must match ^[a-z0-9][a-z0-9-]*$");
18
+ return result;
19
+ }
20
+ export function contractKeys(value, label) {
21
+ return array(value, label).map((item, index) => contractKey(item, `${label}[${index}]`));
22
+ }
23
+ export function sourceItemKeys(value, label) {
24
+ return array(value, label).map((item, index) => {
25
+ const itemLabel = `${label}[${index}]`;
26
+ const result = string(item, itemLabel);
27
+ if (!SOURCE_ITEM_KEY.test(result))
28
+ designResourceShapeFail(itemLabel, "must be a valid ty-source-item key");
29
+ return result;
30
+ });
31
+ }
32
+ export function verificationMethods(value, label) {
33
+ return array(value, label).map((item, index) => literal(item, DESIGN_RESOURCE_VERIFICATION_METHODS, `${label}[${index}]`));
34
+ }
35
+ export function positiveInteger(value, label) {
36
+ if (typeof value !== "number" ||
37
+ !Number.isSafeInteger(value) ||
38
+ value <= 0)
39
+ designResourceShapeFail(label, "must be a positive safe integer");
40
+ return value;
41
+ }
42
+ export function designResourceShapeFail(label, message) {
43
+ throw new Error(`design_resource_handoff_invalid:${label}:${message}`);
44
+ }
@@ -0,0 +1,5 @@
1
+ import type { DesignResourceHandoffConditionV1, DesignResourceHandoffResourceV1, DesignResourceHandoffSubjectV1, DesignResourceHandoffTargetV1 } from "./design-resource-handoff-types.js";
2
+ export declare function parseDesignResourceHandoffResources(value: unknown): DesignResourceHandoffResourceV1[];
3
+ export declare function parseDesignResourceHandoffConditions(value: unknown): DesignResourceHandoffConditionV1[];
4
+ export declare function parseDesignResourceHandoffSubjects(value: unknown): DesignResourceHandoffSubjectV1[];
5
+ export declare function parseDesignResourceHandoffTargets(value: unknown): DesignResourceHandoffTargetV1[];