project-tiny-context-harness 0.8.8 → 0.8.10

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 (40) hide show
  1. package/README.md +149 -123
  2. package/assets/README.md +279 -251
  3. package/assets/README.zh-CN.md +172 -144
  4. package/assets/skills/design-resource-authoring/SKILL.md +12 -12
  5. package/assets/skills/design-resource-authoring/references/downstream-handoff.md +57 -48
  6. package/dist/commands/design-resource.js +62 -2
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +1 -0
  9. package/dist/lib/design-resource-fact-manifest-validation.d.ts +3 -2
  10. package/dist/lib/design-resource-fact-manifest-validation.js +23 -18
  11. package/dist/lib/design-resource-fact-universe-catalog.js +18 -8
  12. package/dist/lib/design-resource-fact-universe-helpers.js +9 -5
  13. package/dist/lib/design-resource-handoff-bundle.d.ts +31 -0
  14. package/dist/lib/design-resource-handoff-bundle.js +171 -0
  15. package/dist/lib/design-resource-handoff-file-validation.d.ts +1 -1
  16. package/dist/lib/design-resource-handoff-file-validation.js +1 -9
  17. package/dist/lib/design-resource-handoff-manifest-projection.d.ts +3 -0
  18. package/dist/lib/design-resource-handoff-manifest-projection.js +37 -0
  19. package/dist/lib/design-resource-handoff-parser.d.ts +7 -2
  20. package/dist/lib/design-resource-handoff-parser.js +32 -12
  21. package/dist/lib/design-resource-handoff-set-integrity.d.ts +8 -0
  22. package/dist/lib/design-resource-handoff-set-integrity.js +93 -0
  23. package/dist/lib/design-resource-handoff-shape.d.ts +2 -1
  24. package/dist/lib/design-resource-handoff-shape.js +62 -0
  25. package/dist/lib/design-resource-handoff-snapshot.d.ts +6 -0
  26. package/dist/lib/design-resource-handoff-snapshot.js +21 -0
  27. package/dist/lib/design-resource-handoff-types.d.ts +23 -1
  28. package/dist/lib/design-resource-handoff-validation.d.ts +2 -1
  29. package/dist/lib/design-resource-handoff-validation.js +45 -22
  30. package/dist/lib/long-task-design-resource-handoff.d.ts +15 -0
  31. package/dist/lib/long-task-design-resource-handoff.js +63 -173
  32. package/dist/lib/long-task-design-resource-method-binding.d.ts +8 -0
  33. package/dist/lib/long-task-design-resource-method-binding.js +135 -0
  34. package/dist/lib/long-task-source-item-parser.d.ts +8 -0
  35. package/dist/lib/long-task-source-item-parser.js +29 -107
  36. package/dist/lib/long-task-source-owned-sections.d.ts +29 -0
  37. package/dist/lib/long-task-source-owned-sections.js +90 -0
  38. package/dist/lib/source-line-scanner.d.ts +3 -0
  39. package/dist/lib/source-line-scanner.js +42 -0
  40. package/package.json +84 -84
@@ -0,0 +1,171 @@
1
+ import { lstat, mkdtemp, readFile, readdir, realpath, rename, rm, writeFile, } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { parseDesignResourceHandoffMarkdown } from "./design-resource-handoff-parser.js";
4
+ import { createDesignResourceHandoffSetIntegrity } from "./design-resource-handoff-set-integrity.js";
5
+ import { preflightParsedDesignResourceHandoff } from "./design-resource-handoff-validation.js";
6
+ import { normalizeRepositoryCwd, normalizeRepositoryFile, } from "./long-task-paths.js";
7
+ import { repoRelative, resolveInsideRepository, } from "./long-task-workspace.js";
8
+ import { sha256Hex } from "./strict-codec.js";
9
+ export async function publishDesignResourceHandoffBundle(options) {
10
+ const repository = path.resolve(options.repository);
11
+ const draftDirectory = normalizeBundleDirectory(options.draft_directory, "design_resource_bundle_draft_directory");
12
+ const outputDirectory = normalizeBundleDirectory(options.output_directory, "design_resource_bundle_output_directory");
13
+ if (!Number.isSafeInteger(options.max_handoff_bytes) ||
14
+ options.max_handoff_bytes <= 0)
15
+ invalid("max_handoff_bytes", String(options.max_handoff_bytes));
16
+ const manifestPaths = normalizeManifestPaths(options.manifest_paths);
17
+ const declaredManifestPaths = new Set(manifestPaths);
18
+ const draftAbsolute = await assertSafeDirectory(repository, draftDirectory, "draft_directory");
19
+ const outputAbsolute = resolveInsideRepository(repository, outputDirectory, "design_resource_bundle_output_directory");
20
+ if (await lstat(outputAbsolute).catch(() => null))
21
+ invalid("output_directory_exists", outputDirectory);
22
+ const outputParentRelative = path.posix.dirname(outputDirectory);
23
+ const outputParent = await assertSafeDirectory(repository, outputParentRelative === "." ? "." : outputParentRelative, "output_parent");
24
+ const temporary = await mkdtemp(path.join(outputParent, `.${path.basename(outputAbsolute)}.tmp-`));
25
+ try {
26
+ const draftEntries = (await readdir(draftAbsolute, {
27
+ withFileTypes: true,
28
+ })).sort((left, right) => compareText(left.name, right.name));
29
+ if (!draftEntries.length)
30
+ invalid("draft_directory_empty", draftDirectory);
31
+ for (const entry of draftEntries)
32
+ if (!entry.isFile() ||
33
+ entry.isSymbolicLink() ||
34
+ path.extname(entry.name).toLowerCase() !== ".md")
35
+ invalid("draft_entry_not_markdown_file", entry.name);
36
+ const handoffs = [];
37
+ const seenTargets = new Set();
38
+ const seenManifestPaths = new Set();
39
+ const manifests = new Map();
40
+ const handoffSetIntegrity = createDesignResourceHandoffSetIntegrity((code, detail) => invalid(code, detail));
41
+ for (const entry of draftEntries) {
42
+ const draftFile = path.join(draftAbsolute, entry.name);
43
+ const info = await lstat(draftFile);
44
+ if (typeof info.nlink === "number" && info.nlink > 1)
45
+ invalid("draft_hardlink_not_allowed", entry.name);
46
+ if (info.size > options.max_handoff_bytes)
47
+ invalid("handoff_byte_limit_exceeded", `${entry.name}:${info.size}:${options.max_handoff_bytes}`);
48
+ const bytes = await readFile(draftFile);
49
+ if (bytes.length > options.max_handoff_bytes)
50
+ invalid("handoff_byte_limit_exceeded", `${entry.name}:${bytes.length}:${options.max_handoff_bytes}`);
51
+ const content = bytes.toString("utf8");
52
+ if (!Buffer.from(content, "utf8").equals(bytes))
53
+ invalid("handoff_utf8_invalid", entry.name);
54
+ const stagedFile = path.join(temporary, entry.name);
55
+ await writeFile(stagedFile, bytes, { flag: "wx" });
56
+ const stagedPath = repoRelative(repository, stagedFile);
57
+ const parsed = parseDesignResourceHandoffMarkdown(stagedPath, content);
58
+ if (!("representation" in parsed.handoff))
59
+ invalid("manifest_backed_representation_required", entry.name);
60
+ if (parsed.handoff.targets.length !== 1)
61
+ invalid("one_target_per_handoff_required", `${entry.name}:${parsed.handoff.targets.length}`);
62
+ const declaredTarget = parsed.handoff.targets[0];
63
+ const manifestResource = parsed.handoff.resources.find((resource) => resource.key ===
64
+ declaredTarget.source_profile.fact_manifest_resource_ref);
65
+ if (!manifestResource)
66
+ invalid("target_manifest_resource_missing", declaredTarget.key);
67
+ if (!declaredManifestPaths.has(manifestResource.path))
68
+ invalid("target_manifest_not_declared", `${declaredTarget.key}:${manifestResource.path}`);
69
+ const preflight = await preflightParsedDesignResourceHandoff(repository, parsed);
70
+ handoffSetIntegrity.consume(preflight);
71
+ const targetKey = preflight.handoff.targets[0].key;
72
+ if (seenTargets.has(targetKey))
73
+ invalid("target_duplicate", targetKey);
74
+ seenTargets.add(targetKey);
75
+ const manifestIdentity = preflight.manifest_identities[0];
76
+ if (manifestIdentity.target_key !== targetKey ||
77
+ manifestIdentity.path !== manifestResource.path ||
78
+ manifestIdentity.sha256 !== manifestResource.sha256)
79
+ invalid("target_manifest_identity_mismatch", targetKey);
80
+ seenManifestPaths.add(manifestIdentity.path);
81
+ manifests.set(targetKey, {
82
+ path: manifestIdentity.path,
83
+ sha256: manifestIdentity.sha256,
84
+ scope_key: manifestIdentity.scope_key,
85
+ target_key: manifestIdentity.target_key,
86
+ collections: manifestIdentity.collections.map((collection) => ({
87
+ ...collection,
88
+ })),
89
+ });
90
+ handoffs.push({
91
+ path: `${outputDirectory}/${entry.name}`,
92
+ sha256: sha256Hex(bytes),
93
+ bytes: bytes.length,
94
+ scope_key: preflight.handoff.scope.key,
95
+ target_key: targetKey,
96
+ });
97
+ }
98
+ handoffSetIntegrity.finish();
99
+ assertSameSet(seenManifestPaths, declaredManifestPaths, "manifest_path_set_mismatch");
100
+ const result = {
101
+ status: "published",
102
+ output_directory: outputDirectory,
103
+ handoffs,
104
+ manifests: [...manifests.values()]
105
+ .map((baseline) => ({
106
+ path: baseline.path,
107
+ sha256: baseline.sha256,
108
+ scope_key: baseline.scope_key,
109
+ target_key: baseline.target_key,
110
+ collections: baseline.collections.map((collection) => ({
111
+ ...collection,
112
+ })),
113
+ }))
114
+ .sort((left, right) => compareText(left.target_key, right.target_key)),
115
+ };
116
+ await rename(temporary, outputAbsolute);
117
+ return result;
118
+ }
119
+ catch (error) {
120
+ try {
121
+ await rm(temporary, { recursive: true, force: true });
122
+ }
123
+ catch (cleanupError) {
124
+ throw new AggregateError([error, cleanupError], "design_resource_handoff_bundle_cleanup_failed");
125
+ }
126
+ throw error;
127
+ }
128
+ }
129
+ function normalizeManifestPaths(values) {
130
+ if (!values.length)
131
+ invalid("manifest_paths_required", "");
132
+ const normalized = values.map((value) => normalizeRepositoryFile(value, "design_resource_bundle_manifest"));
133
+ if (new Set(normalized).size !== normalized.length)
134
+ invalid("manifest_path_duplicate", normalized.join(","));
135
+ return normalized.sort(compareText);
136
+ }
137
+ async function assertSafeDirectory(repository, relative, label) {
138
+ const absolute = resolveInsideRepository(repository, relative, label);
139
+ const info = await lstat(absolute).catch(() => null);
140
+ if (!info)
141
+ invalid(`${label}_not_found`, relative);
142
+ if (info.isSymbolicLink() || !info.isDirectory())
143
+ invalid(`${label}_not_directory`, relative);
144
+ const root = await realpath(repository);
145
+ const resolved = await realpath(absolute);
146
+ const outside = path.relative(root, resolved);
147
+ if (outside === ".." ||
148
+ outside.startsWith(`..${path.sep}`) ||
149
+ path.isAbsolute(outside))
150
+ invalid(`${label}_outside_repository`, relative);
151
+ return resolved;
152
+ }
153
+ function normalizeBundleDirectory(value, label) {
154
+ const normalized = normalizeRepositoryCwd(value, label);
155
+ if (normalized === ".")
156
+ invalid("repository_root_directory_forbidden", label);
157
+ return normalized;
158
+ }
159
+ function assertSameSet(actual, expected, code) {
160
+ const left = [...actual].sort(compareText);
161
+ const right = [...expected].sort(compareText);
162
+ if (left.length !== right.length ||
163
+ left.some((value, index) => value !== right[index]))
164
+ invalid(code, `${left.join(",")}:${right.join(",")}`);
165
+ }
166
+ function invalid(code, detail) {
167
+ throw new Error(`design_resource_handoff_bundle_invalid:${code}${detail ? `:${detail}` : ""}`);
168
+ }
169
+ function compareText(left, right) {
170
+ return left < right ? -1 : left > right ? 1 : 0;
171
+ }
@@ -1,2 +1,2 @@
1
1
  import type { ParsedDesignResourceHandoffV1 } from "./design-resource-handoff-types.js";
2
- export declare function validateDesignResourceFiles(repository: string, parsed: ParsedDesignResourceHandoffV1): Promise<void>;
2
+ export declare function validateDesignResourceFiles(parsed: ParsedDesignResourceHandoffV1, contents: Map<string, Buffer>): void;
@@ -1,17 +1,9 @@
1
- import { readFile } from "node:fs/promises";
2
- import path from "node:path";
3
1
  import { isTextResource, } from "./design-resource-handoff-file-primitives.js";
4
2
  import { invalidDesignResourceHandoff } from "./design-resource-handoff-validation-primitives.js";
5
3
  import { resolveDesignResourceLocatorValue } from "./design-resource-fact-locator-validation.js";
6
4
  import { validateDesignResourceImplementationDependencyClosure } from "./design-resource-handoff-web-dependency-validation.js";
7
- import { assertProtectedRepositoryFile } from "./long-task-protected-files.js";
8
- export async function validateDesignResourceFiles(repository, parsed) {
5
+ export function validateDesignResourceFiles(parsed, contents) {
9
6
  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
7
  for (const evidence of parsed.handoff.evidence)
16
8
  validateLocator(evidence, resources.get(evidence.resource_ref), contents.get(evidence.resource_ref));
17
9
  for (const target of parsed.handoff.targets)
@@ -0,0 +1,3 @@
1
+ import type { DesignResourceObservableFactManifestV1 } from "./design-resource-fact-manifest-types.js";
2
+ import type { DesignResourceHandoffManifestBackedV1, DesignResourceHandoffV1 } from "./design-resource-handoff-types.js";
3
+ export declare function hydrateManifestBackedDesignResourceHandoff(descriptor: DesignResourceHandoffManifestBackedV1, manifests: Map<string, DesignResourceObservableFactManifestV1>): DesignResourceHandoffV1;
@@ -0,0 +1,37 @@
1
+ import { invalidDesignResourceHandoff } from "./design-resource-handoff-validation-primitives.js";
2
+ export function hydrateManifestBackedDesignResourceHandoff(descriptor, manifests) {
3
+ if (descriptor.targets.length !== 1)
4
+ invalidDesignResourceHandoff("manifest_backed_one_target_required", String(descriptor.targets.length));
5
+ const target = descriptor.targets[0];
6
+ const manifest = manifests.get(target.key);
7
+ if (!manifest)
8
+ invalidDesignResourceHandoff("fact_manifest_target_missing", target.key);
9
+ return {
10
+ schema_version: descriptor.schema_version,
11
+ intent: descriptor.intent,
12
+ scope: descriptor.scope,
13
+ provenance: descriptor.provenance,
14
+ resources: descriptor.resources,
15
+ axis_dispositions: manifest.axis_dispositions,
16
+ condition_exclusions: manifest.condition_exclusions,
17
+ conditions: manifest.conditions,
18
+ subjects: manifest.subjects,
19
+ variation_axis_dispositions: manifest.variation_axis_dispositions,
20
+ variation_exclusions: manifest.variation_exclusions,
21
+ variations: manifest.variations,
22
+ properties: manifest.properties,
23
+ lineage_nodes: manifest.lineage_nodes,
24
+ targets: descriptor.targets,
25
+ evidence: manifest.evidence,
26
+ fact_cells: manifest.fact_cells,
27
+ facts: manifest.facts,
28
+ proof_obligations: manifest.proof_obligations,
29
+ oracles: manifest.oracles,
30
+ environments: manifest.environments,
31
+ asset_bindings: manifest.asset_bindings,
32
+ resource_fact_closure: descriptor.resource_fact_closure,
33
+ coverage: descriptor.coverage,
34
+ acceptance_blockers: manifest.acceptance_blockers,
35
+ proposal: descriptor.proposal,
36
+ };
37
+ }
@@ -1,3 +1,8 @@
1
- import type { ParsedDesignResourceHandoffV1 } from "./design-resource-handoff-types.js";
1
+ import type { ParsedDesignResourceHandoffInputV1 } from "./design-resource-handoff-types.js";
2
+ export interface DesignResourceHandoffBlockSpan {
3
+ bodyStartOffset: number;
4
+ bodyEndOffset: number;
5
+ }
2
6
  export declare function containsDesignResourceHandoff(content: string): boolean;
3
- export declare function parseDesignResourceHandoffMarkdown(handoffPath: string, content: string): ParsedDesignResourceHandoffV1;
7
+ export declare function scanDesignResourceHandoffBlocks(content: string): DesignResourceHandoffBlockSpan[];
8
+ export declare function parseDesignResourceHandoffMarkdown(handoffPath: string, content: string): ParsedDesignResourceHandoffInputV1;
@@ -1,23 +1,43 @@
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;
1
+ import { parseSourceDocument } from "./long-task-source-item-parser.js";
2
+ import { forEachSourceLine } from "./source-line-scanner.js";
3
+ const DESIGN_RESOURCE_START = /^```yaml[ \t]+design-resource-handoff-v1[ \t]*$/u;
4
+ const FORMAL_BLOCK_END = /^```[ \t]*$/u;
5
5
  export function containsDesignResourceHandoff(content) {
6
- FENCE.lastIndex = 0;
7
- return FENCE.test(content);
6
+ return scanDesignResourceHandoffBlocks(content).length > 0;
7
+ }
8
+ export function scanDesignResourceHandoffBlocks(content) {
9
+ const blocks = [];
10
+ let bodyStartOffset = null;
11
+ forEachSourceLine(content, (line, startOffset, _endOffset, nextOffset) => {
12
+ if (bodyStartOffset === null) {
13
+ if (DESIGN_RESOURCE_START.test(line))
14
+ bodyStartOffset = nextOffset;
15
+ return;
16
+ }
17
+ if (!FORMAL_BLOCK_END.test(line))
18
+ return;
19
+ blocks.push({
20
+ bodyStartOffset,
21
+ bodyEndOffset: startOffset,
22
+ });
23
+ bodyStartOffset = null;
24
+ });
25
+ return blocks;
8
26
  }
9
27
  export function parseDesignResourceHandoffMarkdown(handoffPath, content) {
10
- FENCE.lastIndex = 0;
11
- const blocks = [...content.matchAll(FENCE)];
28
+ const blocks = scanDesignResourceHandoffBlocks(content);
12
29
  if (blocks.length !== 1)
13
30
  throw new Error(`design_resource_handoff_invalid:block_count:${handoffPath}:${blocks.length}`);
14
31
  try {
15
- const sourceItems = parseSourceItems(handoffPath, content);
32
+ const parsedSource = parseSourceDocument(handoffPath, content);
33
+ const handoff = parsedSource.designResourceHandoff;
34
+ if (!handoff)
35
+ throw new Error("design-resource-handoff-v1 block was not decoded");
16
36
  return {
17
37
  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])),
38
+ handoff,
39
+ source_item_keys: parsedSource.items.map((item) => item.key),
40
+ source_item_kinds: Object.fromEntries(parsedSource.items.map((item) => [item.key, item.kind])),
21
41
  };
22
42
  }
23
43
  catch (error) {
@@ -0,0 +1,8 @@
1
+ import type { DesignResourceHandoffPreflightV1 } from "./design-resource-handoff-types.js";
2
+ type HandoffSetFailure = (code: string, detail: string) => never;
3
+ export interface DesignResourceHandoffSetIntegrity {
4
+ consume(preflight: DesignResourceHandoffPreflightV1): void;
5
+ finish(): void;
6
+ }
7
+ export declare function createDesignResourceHandoffSetIntegrity(fail: HandoffSetFailure): DesignResourceHandoffSetIntegrity;
8
+ export {};
@@ -0,0 +1,93 @@
1
+ import { canonicalValueJson, sha256Hex } from "./strict-codec.js";
2
+ export function createDesignResourceHandoffSetIntegrity(fail) {
3
+ const scopes = new Map();
4
+ const sourceItems = new Set();
5
+ let issue = null;
6
+ function record(code, detail) {
7
+ issue ??= { code, detail };
8
+ }
9
+ function sharedHeader(scope, name, value) {
10
+ const digest = canonicalDigest(value);
11
+ const previous = scope.headers.get(name);
12
+ if (previous && previous !== digest)
13
+ record("handoff_set_header_conflict", name);
14
+ else
15
+ scope.headers.set(name, digest);
16
+ }
17
+ function sharedCollection(scope, name, rows) {
18
+ const index = scope.sharedRows.get(name) ?? new Map();
19
+ scope.sharedRows.set(name, index);
20
+ for (const row of rows) {
21
+ const digest = canonicalDigest(row);
22
+ const previous = index.get(row.key);
23
+ if (previous && previous !== digest)
24
+ record("handoff_set_shared_row_conflict", `${name}:${row.key}`);
25
+ else
26
+ index.set(row.key, digest);
27
+ }
28
+ }
29
+ return {
30
+ consume(preflight) {
31
+ if (issue)
32
+ return;
33
+ const handoff = preflight.handoff;
34
+ const scope = scopeIndex(scopes, handoff.scope.key);
35
+ sharedHeader(scope, "provenance", handoff.provenance);
36
+ sharedHeader(scope, "proposal", handoff.proposal);
37
+ sharedCollection(scope, "resources", handoff.resources);
38
+ sharedCollection(scope, "conditions", handoff.conditions);
39
+ sharedCollection(scope, "properties", handoff.properties);
40
+ sharedCollection(scope, "resource_fact_closure", handoff.resource_fact_closure);
41
+ for (const closure of handoff.resource_fact_closure) {
42
+ const digest = canonicalDigest(closure);
43
+ const previous = scope.resourceClosures.get(closure.resource_ref);
44
+ if (previous &&
45
+ (previous.key !== closure.key || previous.digest !== digest))
46
+ record("handoff_set_resource_closure_conflict", `${closure.resource_ref}:${previous.key}:${closure.key}`);
47
+ else
48
+ scope.resourceClosures.set(closure.resource_ref, {
49
+ key: closure.key,
50
+ digest,
51
+ });
52
+ }
53
+ for (const resource of handoff.resources) {
54
+ const digest = canonicalDigest(resource);
55
+ const previous = scope.resourcePaths.get(resource.path);
56
+ if (previous &&
57
+ (previous.key !== resource.key || previous.digest !== digest))
58
+ record("handoff_set_resource_path_conflict", `${resource.path}:${previous.key}:${resource.key}`);
59
+ else
60
+ scope.resourcePaths.set(resource.path, {
61
+ key: resource.key,
62
+ digest,
63
+ });
64
+ }
65
+ for (const sourceItem of preflight.source_item_keys) {
66
+ if (sourceItems.has(sourceItem))
67
+ record("handoff_set_source_item_duplicate", sourceItem);
68
+ else
69
+ sourceItems.add(sourceItem);
70
+ }
71
+ },
72
+ finish() {
73
+ if (issue)
74
+ fail(issue.code, issue.detail);
75
+ },
76
+ };
77
+ }
78
+ function scopeIndex(scopes, key) {
79
+ const existing = scopes.get(key);
80
+ if (existing)
81
+ return existing;
82
+ const created = {
83
+ headers: new Map(),
84
+ sharedRows: new Map(),
85
+ resourcePaths: new Map(),
86
+ resourceClosures: new Map(),
87
+ };
88
+ scopes.set(key, created);
89
+ return created;
90
+ }
91
+ function canonicalDigest(value) {
92
+ return sha256Hex(canonicalValueJson(value));
93
+ }
@@ -1,2 +1,3 @@
1
- import type { DesignResourceHandoffV1 } from "./design-resource-handoff-types.js";
1
+ import type { DesignResourceHandoffInputV1, DesignResourceHandoffV1 } from "./design-resource-handoff-types.js";
2
+ export declare function parseDesignResourceHandoffInputShape(value: unknown): DesignResourceHandoffInputV1;
2
3
  export declare function parseDesignResourceHandoffShape(value: unknown): DesignResourceHandoffV1;
@@ -3,6 +3,14 @@ import { parseDesignResourceHandoffBlockers, parseDesignResourceHandoffCoverage,
3
3
  import { contractKey, stableKeys, } from "./design-resource-handoff-shape-primitives.js";
4
4
  import { parseDesignResourceHandoffConditions, parseDesignResourceHandoffResources, parseDesignResourceHandoffSubjects, parseDesignResourceHandoffTargets, } from "./design-resource-handoff-shape-structure.js";
5
5
  import { literal, object, repositoryFile, string, strings, } from "./long-task-shape-primitives.js";
6
+ export function parseDesignResourceHandoffInputShape(value) {
7
+ if (value &&
8
+ typeof value === "object" &&
9
+ !Array.isArray(value) &&
10
+ value.representation === "manifest_backed")
11
+ return parseManifestBackedDesignResourceHandoffShape(value);
12
+ return parseDesignResourceHandoffShape(value);
13
+ }
6
14
  export function parseDesignResourceHandoffShape(value) {
7
15
  const root = object(value, "design_resource_handoff", [
8
16
  "schema_version",
@@ -102,3 +110,57 @@ export function parseDesignResourceHandoffShape(value) {
102
110
  },
103
111
  };
104
112
  }
113
+ function parseManifestBackedDesignResourceHandoffShape(value) {
114
+ const root = object(value, "design_resource_handoff", [
115
+ "schema_version",
116
+ "representation",
117
+ "intent",
118
+ "scope",
119
+ "provenance",
120
+ "resources",
121
+ "targets",
122
+ "resource_fact_closure",
123
+ "coverage",
124
+ "proposal",
125
+ ]);
126
+ const parsed = parseDesignResourceHandoffShape({
127
+ schema_version: root.schema_version,
128
+ intent: root.intent,
129
+ scope: root.scope,
130
+ provenance: root.provenance,
131
+ resources: root.resources,
132
+ axis_dispositions: [],
133
+ condition_exclusions: [],
134
+ conditions: [],
135
+ subjects: [],
136
+ variation_axis_dispositions: [],
137
+ variation_exclusions: [],
138
+ variations: [],
139
+ properties: [],
140
+ lineage_nodes: [],
141
+ targets: root.targets,
142
+ evidence: [],
143
+ fact_cells: [],
144
+ facts: [],
145
+ proof_obligations: [],
146
+ oracles: [],
147
+ environments: [],
148
+ asset_bindings: [],
149
+ resource_fact_closure: root.resource_fact_closure,
150
+ coverage: root.coverage,
151
+ acceptance_blockers: [],
152
+ proposal: root.proposal,
153
+ });
154
+ return {
155
+ schema_version: parsed.schema_version,
156
+ representation: literal(root.representation, ["manifest_backed"], "design_resource_handoff.representation"),
157
+ intent: parsed.intent,
158
+ scope: parsed.scope,
159
+ provenance: parsed.provenance,
160
+ resources: parsed.resources,
161
+ targets: parsed.targets,
162
+ resource_fact_closure: parsed.resource_fact_closure,
163
+ coverage: parsed.coverage,
164
+ proposal: parsed.proposal,
165
+ };
166
+ }
@@ -0,0 +1,6 @@
1
+ import type { ParsedDesignResourceHandoffInputV1 } from "./design-resource-handoff-types.js";
2
+ export interface DesignResourceSnapshot {
3
+ contents: Map<string, Buffer>;
4
+ hashes: Record<string, string>;
5
+ }
6
+ export declare function readDesignResourceSnapshot(repository: string, parsed: ParsedDesignResourceHandoffInputV1): Promise<DesignResourceSnapshot>;
@@ -0,0 +1,21 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { invalidDesignResourceHandoff } from "./design-resource-handoff-validation-primitives.js";
4
+ import { assertProtectedRepositoryFile } from "./long-task-protected-files.js";
5
+ import { sha256Hex } from "./strict-codec.js";
6
+ export async function readDesignResourceSnapshot(repository, parsed) {
7
+ const contents = new Map();
8
+ const hashes = {};
9
+ for (const resource of parsed.handoff.resources) {
10
+ if (resource.path === parsed.handoff_path)
11
+ invalidDesignResourceHandoff("resource_must_not_be_handoff", resource.key);
12
+ const file = await assertProtectedRepositoryFile(repository, path.resolve(repository, ...resource.path.split("/")), `design_resource:${resource.key}`);
13
+ const bytes = await readFile(file);
14
+ const digest = sha256Hex(bytes);
15
+ if (digest !== resource.sha256)
16
+ invalidDesignResourceHandoff("resource_digest_mismatch", `${resource.key}:${resource.sha256}:${digest}`);
17
+ contents.set(resource.key, bytes);
18
+ hashes[resource.key] = digest;
19
+ }
20
+ return { contents, hashes };
21
+ }
@@ -1,5 +1,5 @@
1
1
  import type { ExecutionTargetCapabilityV2 } from "./execution-target-capabilities.js";
2
- import type { DesignResourceAssetBindingV1, DesignResourceAxisDispositionV1, DesignResourceConditionCombinationDispositionV1, DesignResourceEnvironmentV1, DesignResourceFactCellV1, DesignResourceFactV1, DesignResourceLineageNodeV1, DesignResourceOracleV1, DesignResourcePropertyDefinitionV1, DesignResourceProofObligationV1, DesignResourceRelationEndpointV1, DesignResourceSubjectKind, DesignResourceSubjectPresenceKind, DesignResourceSubjectVariationV1, DesignResourceVariationAxisDispositionV1, DesignResourceVariationCombinationDispositionV1 } from "./design-resource-fact-manifest-types.js";
2
+ import type { DesignResourceAssetBindingV1, DesignResourceAxisDispositionV1, DesignResourceConditionCombinationDispositionV1, DesignResourceEnvironmentV1, DesignResourceFactCellV1, DesignResourceFactV1, DesignResourceLineageNodeV1, DesignResourceManifestCollectionName, DesignResourceOracleV1, DesignResourcePropertyDefinitionV1, DesignResourceProofObligationV1, DesignResourceRelationEndpointV1, DesignResourceSubjectKind, DesignResourceSubjectPresenceKind, DesignResourceSubjectVariationV1, DesignResourceVariationAxisDispositionV1, DesignResourceVariationCombinationDispositionV1 } from "./design-resource-fact-manifest-types.js";
3
3
  export declare const DESIGN_RESOURCE_DIMENSIONS: readonly ["surface_flow", "visual_content", "component_control", "state_interaction", "motion", "adaptation_input", "accessibility", "assets"];
4
4
  export type DesignResourceDimension = (typeof DESIGN_RESOURCE_DIMENSIONS)[number];
5
5
  export declare const DESIGN_RESOURCE_EVIDENCE_KINDS: readonly ["frame", "component_variant", "prototype_state", "prototype_transition", "motion_spec", "motion_capture", "responsive_spec", "input_spec", "accessibility_spec", "semantic_tree", "token_spec", "asset", "annotation", "localization_spec", "system_ui_spec", "haptic_spec", "sound_spec", "sound_capture", "render_environment", "relation_spec"];
@@ -57,6 +57,10 @@ export interface DesignResourceHandoffV1 {
57
57
  revision: string;
58
58
  };
59
59
  }
60
+ export type DesignResourceHandoffManifestBackedV1 = Pick<DesignResourceHandoffV1, "schema_version" | "intent" | "scope" | "provenance" | "resources" | "targets" | "resource_fact_closure" | "coverage" | "proposal"> & {
61
+ representation: "manifest_backed";
62
+ };
63
+ export type DesignResourceHandoffInputV1 = DesignResourceHandoffV1 | DesignResourceHandoffManifestBackedV1;
60
64
  export interface DesignResourceHandoffResourceV1 {
61
65
  key: string;
62
66
  role: "exact_target" | "constraint" | "supporting";
@@ -216,10 +220,28 @@ export interface ParsedDesignResourceHandoffV1 {
216
220
  source_item_keys: string[];
217
221
  source_item_kinds: Record<string, string>;
218
222
  }
223
+ export interface ParsedDesignResourceHandoffInputV1 {
224
+ handoff_path: string;
225
+ handoff: DesignResourceHandoffInputV1;
226
+ source_item_keys: string[];
227
+ source_item_kinds: Record<string, string>;
228
+ }
219
229
  export interface DesignResourceHandoffPreflightV1 extends ParsedDesignResourceHandoffV1 {
220
230
  schema_version: "design-resource-handoff-preflight-v1";
221
231
  status: "ready";
222
232
  resource_hashes: Record<string, string>;
233
+ manifest_identities: Array<{
234
+ resource_ref: string;
235
+ path: string;
236
+ sha256: string;
237
+ scope_key: string;
238
+ target_key: string;
239
+ collections: Array<{
240
+ name: DesignResourceManifestCollectionName;
241
+ expected_count: number;
242
+ identity_sha256: string;
243
+ }>;
244
+ }>;
223
245
  counts: {
224
246
  resources: number;
225
247
  manifests: number;
@@ -1,3 +1,4 @@
1
- import type { DesignResourceHandoffPreflightV1, ParsedDesignResourceHandoffV1 } from "./design-resource-handoff-types.js";
1
+ import type { DesignResourceHandoffPreflightV1, ParsedDesignResourceHandoffInputV1, ParsedDesignResourceHandoffV1 } from "./design-resource-handoff-types.js";
2
2
  export declare function preflightDesignResourceHandoff(repository: string, handoffPath: string): Promise<DesignResourceHandoffPreflightV1>;
3
+ export declare function preflightParsedDesignResourceHandoff(repository: string, parsed: ParsedDesignResourceHandoffInputV1): Promise<DesignResourceHandoffPreflightV1>;
3
4
  export declare function validateDesignResourceHandoffSemantics(parsed: ParsedDesignResourceHandoffV1): void;