project-tiny-context-harness 0.8.8 → 0.8.11

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 (42) 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/lib/design-resource-fact-manifest-validation.d.ts +3 -1
  8. package/dist/lib/design-resource-fact-manifest-validation.js +23 -18
  9. package/dist/lib/design-resource-fact-universe-catalog.js +18 -8
  10. package/dist/lib/design-resource-fact-universe-helpers.js +9 -5
  11. package/dist/lib/design-resource-handoff-bundle-draft.d.ts +11 -0
  12. package/dist/lib/design-resource-handoff-bundle-draft.js +38 -0
  13. package/dist/lib/design-resource-handoff-bundle.d.ts +31 -0
  14. package/dist/lib/design-resource-handoff-bundle.js +147 -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-input-types.d.ts +11 -0
  18. package/dist/lib/design-resource-handoff-input-types.js +1 -0
  19. package/dist/lib/design-resource-handoff-manifest-projection.d.ts +4 -0
  20. package/dist/lib/design-resource-handoff-manifest-projection.js +37 -0
  21. package/dist/lib/design-resource-handoff-parser.d.ts +7 -2
  22. package/dist/lib/design-resource-handoff-parser.js +32 -12
  23. package/dist/lib/design-resource-handoff-set-integrity.d.ts +8 -0
  24. package/dist/lib/design-resource-handoff-set-integrity.js +93 -0
  25. package/dist/lib/design-resource-handoff-shape.d.ts +2 -0
  26. package/dist/lib/design-resource-handoff-shape.js +62 -0
  27. package/dist/lib/design-resource-handoff-snapshot.d.ts +6 -0
  28. package/dist/lib/design-resource-handoff-snapshot.js +21 -0
  29. package/dist/lib/design-resource-handoff-types.d.ts +13 -1
  30. package/dist/lib/design-resource-handoff-validation.d.ts +2 -0
  31. package/dist/lib/design-resource-handoff-validation.js +45 -22
  32. package/dist/lib/long-task-design-resource-handoff.d.ts +15 -0
  33. package/dist/lib/long-task-design-resource-handoff.js +63 -173
  34. package/dist/lib/long-task-design-resource-method-binding.d.ts +8 -0
  35. package/dist/lib/long-task-design-resource-method-binding.js +135 -0
  36. package/dist/lib/long-task-source-item-parser.d.ts +8 -0
  37. package/dist/lib/long-task-source-item-parser.js +29 -107
  38. package/dist/lib/long-task-source-owned-sections.d.ts +29 -0
  39. package/dist/lib/long-task-source-owned-sections.js +90 -0
  40. package/dist/lib/source-line-scanner.d.ts +3 -0
  41. package/dist/lib/source-line-scanner.js +42 -0
  42. package/package.json +84 -84
@@ -2,26 +2,59 @@ import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { parseDesignResourceHandoffMarkdown } from "./design-resource-handoff-parser.js";
4
4
  import { validateDesignResourceFiles } from "./design-resource-handoff-file-validation.js";
5
- import { loadAndValidateDesignResourceFactManifests } from "./design-resource-fact-manifest-validation.js";
5
+ import { parseDesignResourceFactManifests, validateDesignResourceFactManifests, } from "./design-resource-fact-manifest-validation.js";
6
+ import { hydrateManifestBackedDesignResourceHandoff } from "./design-resource-handoff-manifest-projection.js";
7
+ import { readDesignResourceSnapshot } from "./design-resource-handoff-snapshot.js";
6
8
  import { validateDesignResourceFacts } from "./design-resource-handoff-validation-facts.js";
7
9
  import { validateDesignResourceBlockers, validateDesignResourceCoverage, validateDesignResourceReachability, } from "./design-resource-handoff-validation-coverage.js";
8
10
  import { indexDesignResourceItems, invalidDesignResourceHandoff, requireNonemptyDesignResourceValues, requireUniqueDesignResourceObjects, requireUniqueDesignResourceValues, } from "./design-resource-handoff-validation-primitives.js";
9
11
  import { validateDesignResourceConditions, validateDesignResourceEvidence, validateDesignResourceScope, validateDesignResourceSubjects, validateDesignResourceTargets, } from "./design-resource-handoff-validation-structure.js";
10
12
  import { assertProtectedRepositoryFile } from "./long-task-protected-files.js";
11
- import { sha256Hex } from "./strict-codec.js";
12
13
  export async function preflightDesignResourceHandoff(repository, handoffPath) {
13
14
  const handoffFile = await assertProtectedRepositoryFile(repository, path.resolve(repository, ...handoffPath.split("/")), "design_resource_handoff");
14
15
  const parsed = parseDesignResourceHandoffMarkdown(handoffPath, await readFile(handoffFile, "utf8"));
15
- validateDesignResourceHandoffSemantics(parsed);
16
- const resourceHashes = await validateResourceIntegrity(repository, parsed);
17
- await validateDesignResourceFiles(repository, parsed);
18
- const manifests = await loadAndValidateDesignResourceFactManifests(repository, parsed);
19
- const handoff = parsed.handoff;
16
+ return preflightParsedDesignResourceHandoff(repository, parsed);
17
+ }
18
+ export async function preflightParsedDesignResourceHandoff(repository, parsed) {
19
+ const inputHandoff = parsed.handoff;
20
+ if (isManifestBacked(inputHandoff) && inputHandoff.targets.length !== 1)
21
+ invalidDesignResourceHandoff("manifest_backed_one_target_required", String(inputHandoff.targets.length));
22
+ if (!isManifestBacked(inputHandoff))
23
+ validateDesignResourceHandoffSemantics({
24
+ ...parsed,
25
+ handoff: inputHandoff,
26
+ });
27
+ const snapshot = await readDesignResourceSnapshot(repository, parsed);
28
+ const manifests = parseDesignResourceFactManifests(parsed, snapshot.contents);
29
+ const handoff = isManifestBacked(inputHandoff)
30
+ ? hydrateManifestBackedDesignResourceHandoff(inputHandoff, manifests)
31
+ : inputHandoff;
32
+ const normalized = { ...parsed, handoff };
33
+ if (isManifestBacked(inputHandoff))
34
+ validateDesignResourceHandoffSemantics(normalized);
35
+ validateDesignResourceFiles(normalized, snapshot.contents);
36
+ validateDesignResourceFactManifests(normalized, snapshot.contents, manifests, !isManifestBacked(inputHandoff));
37
+ const resources = new Map(handoff.resources.map((resource) => [resource.key, resource]));
20
38
  return {
21
39
  schema_version: "design-resource-handoff-preflight-v1",
22
40
  status: "ready",
23
- ...parsed,
24
- resource_hashes: resourceHashes,
41
+ ...normalized,
42
+ resource_hashes: snapshot.hashes,
43
+ manifest_identities: handoff.targets.map((target) => {
44
+ const resourceRef = target.source_profile.fact_manifest_resource_ref;
45
+ const resource = resources.get(resourceRef);
46
+ const manifest = manifests.get(target.key);
47
+ return {
48
+ resource_ref: resourceRef,
49
+ path: resource.path,
50
+ sha256: snapshot.hashes[resourceRef],
51
+ scope_key: manifest.scope_key,
52
+ target_key: manifest.target_key,
53
+ collections: manifest.generation.collections.map((collection) => ({
54
+ ...collection,
55
+ })),
56
+ };
57
+ }),
25
58
  counts: {
26
59
  resources: handoff.resources.length,
27
60
  manifests: manifests.size,
@@ -45,6 +78,9 @@ export async function preflightDesignResourceHandoff(repository, handoffPath) {
45
78
  },
46
79
  };
47
80
  }
81
+ function isManifestBacked(handoff) {
82
+ return "representation" in handoff;
83
+ }
48
84
  export function validateDesignResourceHandoffSemantics(parsed) {
49
85
  const { handoff } = parsed;
50
86
  requireNonemptyDesignResourceValues(handoff.scope.surface_keys, "scope_surface_keys_required");
@@ -104,16 +140,3 @@ export function validateDesignResourceHandoffSemantics(parsed) {
104
140
  validateDesignResourceBlockers(handoff, subjects, targets, sourceItems);
105
141
  validateDesignResourceReachability(handoff);
106
142
  }
107
- async function validateResourceIntegrity(repository, parsed) {
108
- const hashes = {};
109
- for (const resource of parsed.handoff.resources) {
110
- if (resource.path === parsed.handoff_path)
111
- invalidDesignResourceHandoff("resource_must_not_be_handoff", resource.key);
112
- const file = await assertProtectedRepositoryFile(repository, path.resolve(repository, ...resource.path.split("/")), `design_resource:${resource.key}`);
113
- const digest = sha256Hex(await readFile(file));
114
- if (digest !== resource.sha256)
115
- invalidDesignResourceHandoff("resource_digest_mismatch", `${resource.key}:${resource.sha256}:${digest}`);
116
- hashes[resource.key] = digest;
117
- }
118
- return hashes;
119
- }
@@ -1,2 +1,17 @@
1
+ import type { DesignResourceHandoffPreflightV1, DesignResourceHandoffTargetV1 } from "./design-resource-handoff-types.js";
1
2
  import type { DeliveryContractV2 } from "./long-task-delivery-types.js";
3
+ export interface ContractDesignTarget {
4
+ outcome_key: string;
5
+ binding: DeliveryContractV2["outcomes"][number]["product"]["surface_bindings"][number];
6
+ target: DeliveryContractV2["outcomes"][number]["product"]["surface_bindings"][number]["design_targets"][number];
7
+ }
8
+ export interface IndexedHandoffTarget {
9
+ preflight: DesignResourceHandoffPreflightV1;
10
+ target: DesignResourceHandoffTargetV1;
11
+ }
12
+ export interface LongTaskDesignHandoffConsumer {
13
+ consume(preflight: DesignResourceHandoffPreflightV1): void;
14
+ finish(): void;
15
+ }
2
16
  export declare function validateLongTaskDesignResourceHandoffs(contract: DeliveryContractV2, repository: string): Promise<void>;
17
+ export declare function createLongTaskDesignHandoffConsumer(contract: DeliveryContractV2): LongTaskDesignHandoffConsumer;
@@ -1,63 +1,78 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { containsDesignResourceHandoff } from "./design-resource-handoff-parser.js";
4
- import { preflightDesignResourceHandoff } from "./design-resource-handoff-validation.js";
3
+ import { containsDesignResourceHandoff, 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 { assertSameSet, designSourceItemClaims, invalid, validateTargetIdentity, validateVerificationMethodBindings, } from "./long-task-design-resource-method-binding.js";
5
7
  import { validateLongTaskDesignTargetCapabilities } from "./long-task-design-target-capabilities.js";
6
8
  import { assertProtectedRepositoryFile } from "./long-task-protected-files.js";
7
9
  export async function validateLongTaskDesignResourceHandoffs(contract, repository) {
8
- const handoffs = await loadHandoffs(contract, repository);
9
- const indexed = indexHandoffTargets(handoffs);
10
- const contractTargets = contract.outcomes.flatMap((outcome) => outcome.product.surface_bindings.flatMap((binding) => binding.design_targets.map((target) => ({
11
- outcome_key: outcome.key,
12
- binding,
13
- target,
14
- }))));
15
- const consumed = new Set();
16
- for (const contractTarget of contractTargets) {
17
- const indexedTarget = indexed.get(contractTarget.target.key);
18
- if (!indexedTarget)
19
- invalid("target_handoff_missing", contractTarget.target.key);
20
- consumed.add(contractTarget.target.key);
21
- validateTargetIdentity(contractTarget, indexedTarget);
22
- validateLongTaskDesignTargetCapabilities(contract, contractTarget, indexedTarget);
23
- validateCoverageClaims(contract, contractTarget, indexedTarget);
24
- validateBlockerBindings(contract, contractTarget, indexedTarget);
25
- }
26
- for (const key of indexed.keys())
27
- if (!consumed.has(key))
28
- invalid("handoff_target_unbound", key);
29
- }
30
- async function loadHandoffs(contract, repository) {
31
- const results = [];
10
+ const consumer = createLongTaskDesignHandoffConsumer(contract);
32
11
  for (const sourcePath of contract.task.source_paths) {
33
12
  if (!sourcePath.toLowerCase().endsWith(".md"))
34
13
  continue;
35
14
  const file = await assertProtectedRepositoryFile(repository, path.resolve(repository, ...sourcePath.split("/")), `design_resource_handoff_source:${sourcePath}`);
36
15
  const content = await readFile(file, "utf8");
37
- if (containsDesignResourceHandoff(content))
38
- results.push(await preflightDesignResourceHandoff(repository, sourcePath));
16
+ if (!containsDesignResourceHandoff(content))
17
+ continue;
18
+ consumer.consume(await preflightParsedDesignResourceHandoff(repository, parseDesignResourceHandoffMarkdown(sourcePath, content)));
39
19
  }
40
- return results;
20
+ consumer.finish();
41
21
  }
42
- function indexHandoffTargets(handoffs) {
43
- const indexed = new Map();
44
- for (const preflight of handoffs)
45
- for (const target of preflight.handoff.targets) {
46
- if (indexed.has(target.key))
47
- invalid("handoff_target_duplicate", target.key);
48
- indexed.set(target.key, { preflight, target });
49
- }
50
- return indexed;
51
- }
52
- function validateTargetIdentity(contractTarget, indexed) {
53
- const { target } = contractTarget;
54
- const handoffTarget = indexed.target;
55
- if (target.interpretation !== handoffTarget.interpretation)
56
- invalid("target_interpretation_mismatch", `${target.key}:${target.interpretation}:${handoffTarget.interpretation}`);
57
- assertSameSet(target.condition_keys, handoffTarget.condition_refs, "target_conditions_mismatch", target.key);
58
- const resourcePaths = handoffTarget.resource_refs.map((ref) => indexed.preflight.handoff.resources.find((item) => item.key === ref)
59
- .path);
60
- assertSameSet(target.source_paths, [indexed.preflight.handoff_path, ...resourcePaths], "target_source_paths_mismatch", target.key);
22
+ export function createLongTaskDesignHandoffConsumer(contract) {
23
+ const contractTargets = contract.outcomes.flatMap((outcome) => outcome.product.surface_bindings.flatMap((binding) => binding.design_targets.map((target) => ({
24
+ outcome_key: outcome.key,
25
+ binding,
26
+ target,
27
+ }))));
28
+ const contractTargetsByKey = new Map(contractTargets.map((item) => [item.target.key, item]));
29
+ const indexedTargetKeys = new Set();
30
+ const consumed = new Set();
31
+ const validationErrors = new Map();
32
+ let duplicate = null;
33
+ const unbound = [];
34
+ const handoffSetIntegrity = createDesignResourceHandoffSetIntegrity(invalid);
35
+ return {
36
+ consume(preflight) {
37
+ handoffSetIntegrity.consume(preflight);
38
+ for (const target of preflight.handoff.targets) {
39
+ if (indexedTargetKeys.has(target.key)) {
40
+ duplicate ??= target.key;
41
+ continue;
42
+ }
43
+ indexedTargetKeys.add(target.key);
44
+ const contractTarget = contractTargetsByKey.get(target.key);
45
+ if (!contractTarget) {
46
+ unbound.push(target.key);
47
+ continue;
48
+ }
49
+ const indexedTarget = { preflight, target };
50
+ consumed.add(target.key);
51
+ try {
52
+ validateTargetIdentity(contractTarget, indexedTarget);
53
+ validateLongTaskDesignTargetCapabilities(contract, contractTarget, indexedTarget);
54
+ validateCoverageClaims(contract, contractTarget, indexedTarget);
55
+ validateBlockerBindings(contract, contractTarget, indexedTarget);
56
+ }
57
+ catch (error) {
58
+ validationErrors.set(target.key, error);
59
+ }
60
+ }
61
+ },
62
+ finish() {
63
+ if (duplicate)
64
+ invalid("handoff_target_duplicate", duplicate);
65
+ handoffSetIntegrity.finish();
66
+ for (const contractTarget of contractTargets) {
67
+ if (!consumed.has(contractTarget.target.key))
68
+ invalid("target_handoff_missing", contractTarget.target.key);
69
+ if (validationErrors.has(contractTarget.target.key))
70
+ throw validationErrors.get(contractTarget.target.key);
71
+ }
72
+ if (unbound.length)
73
+ invalid("handoff_target_unbound", unbound[0]);
74
+ },
75
+ };
61
76
  }
62
77
  function validateCoverageClaims(contract, contractTarget, indexed) {
63
78
  const target = contractTarget.target;
@@ -128,128 +143,3 @@ function validateBlockerBindings(contract, contractTarget, indexed) {
128
143
  }
129
144
  }
130
145
  }
131
- function designSourceItemClaims(contract, contractTarget, indexed, sourceItemRef, claims = new Map(contract.source_claims.map((item) => [item.key, item]))) {
132
- const claim = claims.get(sourceItemRef);
133
- if (!claim)
134
- invalid("coverage_source_claim_unknown", `${contractTarget.target.key}:${sourceItemRef}`);
135
- if (claim.source_ref.split("#")[0] !== indexed.preflight.handoff_path)
136
- invalid("coverage_source_claim_file_mismatch", `${contractTarget.target.key}:${sourceItemRef}`);
137
- if (claim.disposition.type !== "claim")
138
- invalid("coverage_source_claim_disposition_required", `${contractTarget.target.key}:${sourceItemRef}:${claim.disposition.type}`);
139
- const prefix = `${contractTarget.outcome_key}.`;
140
- return claim.disposition.refs.map((claimRef) => {
141
- if (!claimRef.startsWith(prefix))
142
- invalid("coverage_claim_outcome_mismatch", `${contractTarget.target.key}:${sourceItemRef}:${claimRef}`);
143
- return claimRef.slice(prefix.length);
144
- });
145
- }
146
- function validateVerificationMethodBindings(target, check, facts, proofs, indexed, claimsBySourceItem) {
147
- const expectedMethods = new Set(proofs.map((proof) => proof.method));
148
- const bindings = target.verification_method_bindings;
149
- assertSameSet(bindings.map((item) => item.method), [...expectedMethods], "verification_methods_mismatch", target.key);
150
- const assertionRefs = bindings.map((item) => item.assertion_ref);
151
- if (new Set(assertionRefs).size !== assertionRefs.length)
152
- invalid("verification_method_assertion_duplicate", target.key);
153
- if (assertionRefs.includes(target.conformance_assertion_ref))
154
- invalid("verification_method_assertion_must_be_independent", target.key);
155
- const boundFactRefs = [];
156
- for (const binding of bindings) {
157
- const assertion = check.positive_assertions.find((item) => item.key === binding.assertion_ref);
158
- if (!assertion)
159
- invalid("verification_method_assertion_unknown", `${target.key}:${binding.method}:${binding.assertion_ref}`);
160
- const sourceItems = new Set(facts
161
- .filter((fact) => proofs.some((proof) => proof.fact_ref === fact.key && proof.method === binding.method))
162
- .flatMap((fact) => fact.source_item_refs));
163
- for (const sourceItemRef of sourceItems)
164
- for (const claimRef of claimsBySourceItem.get(sourceItemRef) ?? [])
165
- if (!assertion.claims.includes(claimRef))
166
- invalid("verification_method_claim_not_asserted", `${target.key}:${binding.method}:${sourceItemRef}:${claimRef}`);
167
- for (const capability of requiredCapabilities(binding.method))
168
- if (!assertion.evidence_capabilities.includes(capability))
169
- invalid("verification_method_capability_required", `${target.key}:${binding.method}:${capability}`);
170
- assertSameSet(binding.evidence_artifacts.map((item) => item.condition_key), target.condition_keys, "verification_method_evidence_conditions_mismatch", `${target.key}:${binding.method}`);
171
- for (const artifact of binding.evidence_artifacts) {
172
- const expectedFactRefs = facts
173
- .filter((fact) => proofs.some((proof) => proof.fact_ref === fact.key && proof.method === binding.method) && fact.condition_ref === artifact.condition_key)
174
- .map((fact) => fact.key);
175
- assertSameSet(artifact.fact_refs, expectedFactRefs, "design_method_fact_refs_mismatch", `${target.key}:${binding.method}:${artifact.condition_key}`);
176
- const expectedFactExpectations = proofs
177
- .filter((proof) => proof.method === binding.method &&
178
- facts.some((fact) => fact.key === proof.fact_ref &&
179
- fact.condition_ref === artifact.condition_key))
180
- .map((proof) => designFactExpectation(indexed, proof.fact_ref, proof.key));
181
- assertSameSet(artifact.fact_expectations.map((item) => item.fact_ref), expectedFactRefs, "design_method_fact_expectation_refs_mismatch", `${target.key}:${binding.method}:${artifact.condition_key}`);
182
- assertCanonicalRows(artifact.fact_expectations, expectedFactExpectations, "design_method_fact_expectations_mismatch", `${target.key}:${binding.method}:${artifact.condition_key}`);
183
- boundFactRefs.push(...artifact.fact_refs);
184
- }
185
- }
186
- assertSameSet(boundFactRefs, facts.map((fact) => fact.key), "design_method_fact_refs_mismatch", target.key);
187
- }
188
- function designFactExpectation(indexed, factRef, proofRef) {
189
- const handoff = indexed.preflight.handoff;
190
- const fact = handoff.facts.find((item) => item.key === factRef);
191
- const proof = handoff.proof_obligations.find((item) => item.key === proofRef);
192
- const oracle = handoff.oracles.find((item) => item.key === proof.oracle_ref);
193
- const environment = handoff.environments.find((item) => item.key === proof.environment_ref);
194
- return {
195
- fact_ref: fact.key,
196
- subject_ref: fact.subject_ref,
197
- variation_ref: fact.variation_ref,
198
- property_ref: fact.property_ref,
199
- observation_sensitivity: fact.observation_sensitivity,
200
- expected: fact.value,
201
- comparison: proof.comparison,
202
- oracle: {
203
- key: oracle.key,
204
- trust: oracle.trust,
205
- identity: oracle.identity,
206
- version: oracle.version,
207
- sha256: oracle.sha256,
208
- },
209
- environment: {
210
- key: environment.key,
211
- identity: environment.identity,
212
- definition: environment.definition,
213
- },
214
- };
215
- }
216
- function assertCanonicalRows(actual, expected, code, detail) {
217
- const left = new Map(actual.map((item) => [item.fact_ref, canonicalJson(item)]));
218
- const right = new Map(expected.map((item) => [item.fact_ref, canonicalJson(item)]));
219
- assertSameSet([...left.keys()], [...right.keys()], code, detail);
220
- for (const [key, value] of right)
221
- if (left.get(key) !== value)
222
- invalid(code, `${detail}:${key}`);
223
- }
224
- function canonicalJson(value) {
225
- if (Array.isArray(value))
226
- return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
227
- if (value && typeof value === "object")
228
- return `{${Object.entries(value)
229
- .sort(([left], [right]) => left.localeCompare(right))
230
- .map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`)
231
- .join(",")}}`;
232
- return JSON.stringify(value);
233
- }
234
- function requiredCapabilities(method) {
235
- if (method === "interaction_trace")
236
- return ["design_method", "interaction_trace", "target_runtime"];
237
- if (method === "component_state")
238
- return [
239
- "design_method",
240
- "design_conformance",
241
- "interaction_trace",
242
- "target_runtime",
243
- ];
244
- return ["design_method", "design_conformance", "target_runtime"];
245
- }
246
- function assertSameSet(actual, expected, code, detail) {
247
- const left = [...new Set(actual)].sort();
248
- const right = [...new Set(expected)].sort();
249
- if (left.length !== right.length ||
250
- left.some((item, index) => item !== right[index]))
251
- invalid(code, `${detail}:${left.join(",")}:${right.join(",")}`);
252
- }
253
- function invalid(code, detail) {
254
- throw new Error(`delivery_contract_invalid:design_resource_${code}:${detail}`);
255
- }
@@ -0,0 +1,8 @@
1
+ import type { DesignResourceHandoffPreflightV1 } from "./design-resource-handoff-types.js";
2
+ import type { DeliveryContractV2 } from "./long-task-delivery-types.js";
3
+ import type { ContractDesignTarget, IndexedHandoffTarget } from "./long-task-design-resource-handoff.js";
4
+ export declare function validateTargetIdentity(contractTarget: ContractDesignTarget, indexed: IndexedHandoffTarget): void;
5
+ export declare function designSourceItemClaims(contract: DeliveryContractV2, contractTarget: ContractDesignTarget, indexed: IndexedHandoffTarget, sourceItemRef: string, claims?: Map<string, import("./long-task-contract-types.js").SourceClaimV2>): string[];
6
+ export declare function validateVerificationMethodBindings(target: ContractDesignTarget["target"], check: DeliveryContractV2["outcomes"][number]["acceptance"]["checks"][number], facts: DesignResourceHandoffPreflightV1["handoff"]["facts"], proofs: DesignResourceHandoffPreflightV1["handoff"]["proof_obligations"], indexed: IndexedHandoffTarget, claimsBySourceItem: Map<string, string[]>): void;
7
+ export declare function assertSameSet(actual: string[], expected: string[], code: string, detail: string): void;
8
+ export declare function invalid(code: string, detail: string): never;
@@ -0,0 +1,135 @@
1
+ export function validateTargetIdentity(contractTarget, indexed) {
2
+ const { target } = contractTarget;
3
+ const handoffTarget = indexed.target;
4
+ if (target.interpretation !== handoffTarget.interpretation)
5
+ invalid("target_interpretation_mismatch", `${target.key}:${target.interpretation}:${handoffTarget.interpretation}`);
6
+ assertSameSet(target.condition_keys, handoffTarget.condition_refs, "target_conditions_mismatch", target.key);
7
+ const resourcePaths = handoffTarget.resource_refs.map((ref) => indexed.preflight.handoff.resources.find((item) => item.key === ref)
8
+ .path);
9
+ assertSameSet(target.source_paths, [indexed.preflight.handoff_path, ...resourcePaths], "target_source_paths_mismatch", target.key);
10
+ }
11
+ export function designSourceItemClaims(contract, contractTarget, indexed, sourceItemRef, claims = new Map(contract.source_claims.map((item) => [item.key, item]))) {
12
+ const claim = claims.get(sourceItemRef);
13
+ if (!claim)
14
+ invalid("coverage_source_claim_unknown", `${contractTarget.target.key}:${sourceItemRef}`);
15
+ if (claim.source_ref.split("#")[0] !== indexed.preflight.handoff_path)
16
+ invalid("coverage_source_claim_file_mismatch", `${contractTarget.target.key}:${sourceItemRef}`);
17
+ if (claim.disposition.type !== "claim")
18
+ invalid("coverage_source_claim_disposition_required", `${contractTarget.target.key}:${sourceItemRef}:${claim.disposition.type}`);
19
+ const prefix = `${contractTarget.outcome_key}.`;
20
+ return claim.disposition.refs.map((claimRef) => {
21
+ if (!claimRef.startsWith(prefix))
22
+ invalid("coverage_claim_outcome_mismatch", `${contractTarget.target.key}:${sourceItemRef}:${claimRef}`);
23
+ return claimRef.slice(prefix.length);
24
+ });
25
+ }
26
+ export function validateVerificationMethodBindings(target, check, facts, proofs, indexed, claimsBySourceItem) {
27
+ const expectedMethods = new Set(proofs.map((proof) => proof.method));
28
+ const bindings = target.verification_method_bindings;
29
+ assertSameSet(bindings.map((item) => item.method), [...expectedMethods], "verification_methods_mismatch", target.key);
30
+ const assertionRefs = bindings.map((item) => item.assertion_ref);
31
+ if (new Set(assertionRefs).size !== assertionRefs.length)
32
+ invalid("verification_method_assertion_duplicate", target.key);
33
+ if (assertionRefs.includes(target.conformance_assertion_ref))
34
+ invalid("verification_method_assertion_must_be_independent", target.key);
35
+ const boundFactRefs = [];
36
+ for (const binding of bindings) {
37
+ const assertion = check.positive_assertions.find((item) => item.key === binding.assertion_ref);
38
+ if (!assertion)
39
+ invalid("verification_method_assertion_unknown", `${target.key}:${binding.method}:${binding.assertion_ref}`);
40
+ const sourceItems = new Set(facts
41
+ .filter((fact) => proofs.some((proof) => proof.fact_ref === fact.key && proof.method === binding.method))
42
+ .flatMap((fact) => fact.source_item_refs));
43
+ for (const sourceItemRef of sourceItems)
44
+ for (const claimRef of claimsBySourceItem.get(sourceItemRef) ?? [])
45
+ if (!assertion.claims.includes(claimRef))
46
+ invalid("verification_method_claim_not_asserted", `${target.key}:${binding.method}:${sourceItemRef}:${claimRef}`);
47
+ for (const capability of requiredCapabilities(binding.method))
48
+ if (!assertion.evidence_capabilities.includes(capability))
49
+ invalid("verification_method_capability_required", `${target.key}:${binding.method}:${capability}`);
50
+ assertSameSet(binding.evidence_artifacts.map((item) => item.condition_key), target.condition_keys, "verification_method_evidence_conditions_mismatch", `${target.key}:${binding.method}`);
51
+ for (const artifact of binding.evidence_artifacts) {
52
+ const expectedFactRefs = facts
53
+ .filter((fact) => proofs.some((proof) => proof.fact_ref === fact.key && proof.method === binding.method) && fact.condition_ref === artifact.condition_key)
54
+ .map((fact) => fact.key);
55
+ assertSameSet(artifact.fact_refs, expectedFactRefs, "design_method_fact_refs_mismatch", `${target.key}:${binding.method}:${artifact.condition_key}`);
56
+ const expectedFactExpectations = proofs
57
+ .filter((proof) => proof.method === binding.method &&
58
+ facts.some((fact) => fact.key === proof.fact_ref &&
59
+ fact.condition_ref === artifact.condition_key))
60
+ .map((proof) => designFactExpectation(indexed, proof.fact_ref, proof.key));
61
+ assertSameSet(artifact.fact_expectations.map((item) => item.fact_ref), expectedFactRefs, "design_method_fact_expectation_refs_mismatch", `${target.key}:${binding.method}:${artifact.condition_key}`);
62
+ assertCanonicalRows(artifact.fact_expectations, expectedFactExpectations, "design_method_fact_expectations_mismatch", `${target.key}:${binding.method}:${artifact.condition_key}`);
63
+ boundFactRefs.push(...artifact.fact_refs);
64
+ }
65
+ }
66
+ assertSameSet(boundFactRefs, facts.map((fact) => fact.key), "design_method_fact_refs_mismatch", target.key);
67
+ }
68
+ function designFactExpectation(indexed, factRef, proofRef) {
69
+ const handoff = indexed.preflight.handoff;
70
+ const fact = handoff.facts.find((item) => item.key === factRef);
71
+ const proof = handoff.proof_obligations.find((item) => item.key === proofRef);
72
+ const oracle = handoff.oracles.find((item) => item.key === proof.oracle_ref);
73
+ const environment = handoff.environments.find((item) => item.key === proof.environment_ref);
74
+ return {
75
+ fact_ref: fact.key,
76
+ subject_ref: fact.subject_ref,
77
+ variation_ref: fact.variation_ref,
78
+ property_ref: fact.property_ref,
79
+ observation_sensitivity: fact.observation_sensitivity,
80
+ expected: fact.value,
81
+ comparison: proof.comparison,
82
+ oracle: {
83
+ key: oracle.key,
84
+ trust: oracle.trust,
85
+ identity: oracle.identity,
86
+ version: oracle.version,
87
+ sha256: oracle.sha256,
88
+ },
89
+ environment: {
90
+ key: environment.key,
91
+ identity: environment.identity,
92
+ definition: environment.definition,
93
+ },
94
+ };
95
+ }
96
+ function assertCanonicalRows(actual, expected, code, detail) {
97
+ const left = new Map(actual.map((item) => [item.fact_ref, canonicalJson(item)]));
98
+ const right = new Map(expected.map((item) => [item.fact_ref, canonicalJson(item)]));
99
+ assertSameSet([...left.keys()], [...right.keys()], code, detail);
100
+ for (const [key, value] of right)
101
+ if (left.get(key) !== value)
102
+ invalid(code, `${detail}:${key}`);
103
+ }
104
+ function canonicalJson(value) {
105
+ if (Array.isArray(value))
106
+ return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
107
+ if (value && typeof value === "object")
108
+ return `{${Object.entries(value)
109
+ .sort(([left], [right]) => left.localeCompare(right))
110
+ .map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`)
111
+ .join(",")}}`;
112
+ return JSON.stringify(value);
113
+ }
114
+ function requiredCapabilities(method) {
115
+ if (method === "interaction_trace")
116
+ return ["design_method", "interaction_trace", "target_runtime"];
117
+ if (method === "component_state")
118
+ return [
119
+ "design_method",
120
+ "design_conformance",
121
+ "interaction_trace",
122
+ "target_runtime",
123
+ ];
124
+ return ["design_method", "design_conformance", "target_runtime"];
125
+ }
126
+ export function assertSameSet(actual, expected, code, detail) {
127
+ const left = [...new Set(actual)].sort();
128
+ const right = [...new Set(expected)].sort();
129
+ if (left.length !== right.length ||
130
+ left.some((item, index) => item !== right[index]))
131
+ invalid(code, `${detail}:${left.join(",")}:${right.join(",")}`);
132
+ }
133
+ export function invalid(code, detail) {
134
+ throw new Error(`delivery_contract_invalid:design_resource_${code}:${detail}`);
135
+ }
@@ -1,3 +1,11 @@
1
1
  import type { CompiledSourceItemV2 } from "./long-task-delivery-types.js";
2
+ import type { DesignResourceHandoffInputV1 } from "./design-resource-handoff-input-types.js";
3
+ import type { SemanticFactManifestV1 } from "./semantic-fact-types.js";
4
+ export interface ParsedSourceDocument {
5
+ items: CompiledSourceItemV2[];
6
+ designResourceHandoff: DesignResourceHandoffInputV1 | null;
7
+ semanticFactManifest: SemanticFactManifestV1 | null;
8
+ }
2
9
  export declare function parseSourceItems(sourcePath: string, content: string): CompiledSourceItemV2[];
10
+ export declare function parseSourceDocument(sourcePath: string, content: string): ParsedSourceDocument;
3
11
  export { normalizeSourceItemText } from "./long-task-source-markers.js";