@yakcc/cli 0.5.0-alpha.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 (115) hide show
  1. package/LICENSE +214 -0
  2. package/LICENSE-ATOMS +38 -0
  3. package/README.md +76 -0
  4. package/dist/bin.js +10884 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/blocks/ascii-char/impl.ts +16 -0
  7. package/dist/blocks/ascii-char/proof/manifest.json +8 -0
  8. package/dist/blocks/ascii-char/proof/tests.fast-check.ts +20 -0
  9. package/dist/blocks/ascii-char/spec.yak +80 -0
  10. package/dist/blocks/ascii-digit-set/impl.ts +9 -0
  11. package/dist/blocks/ascii-digit-set/proof/manifest.json +8 -0
  12. package/dist/blocks/ascii-digit-set/proof/tests.fast-check.ts +20 -0
  13. package/dist/blocks/ascii-digit-set/spec.yak +66 -0
  14. package/dist/blocks/base64-alphabet/impl.ts +98 -0
  15. package/dist/blocks/base64-alphabet/proof/manifest.json +8 -0
  16. package/dist/blocks/base64-alphabet/proof/tests.fast-check.ts +18 -0
  17. package/dist/blocks/base64-alphabet/spec.yak +110 -0
  18. package/dist/blocks/bracket/impl.ts +20 -0
  19. package/dist/blocks/bracket/proof/manifest.json +8 -0
  20. package/dist/blocks/bracket/proof/tests.fast-check.ts +20 -0
  21. package/dist/blocks/bracket/spec.yak +85 -0
  22. package/dist/blocks/char-code/impl.ts +15 -0
  23. package/dist/blocks/char-code/proof/manifest.json +8 -0
  24. package/dist/blocks/char-code/proof/tests.fast-check.ts +20 -0
  25. package/dist/blocks/char-code/spec.yak +80 -0
  26. package/dist/blocks/comma/impl.ts +20 -0
  27. package/dist/blocks/comma/proof/manifest.json +8 -0
  28. package/dist/blocks/comma/proof/tests.fast-check.ts +20 -0
  29. package/dist/blocks/comma/spec.yak +80 -0
  30. package/dist/blocks/comma-separated-integers/impl.ts +85 -0
  31. package/dist/blocks/comma-separated-integers/proof/manifest.json +8 -0
  32. package/dist/blocks/comma-separated-integers/proof/tests.fast-check.ts +20 -0
  33. package/dist/blocks/comma-separated-integers/spec.yak +84 -0
  34. package/dist/blocks/digit/impl.ts +12 -0
  35. package/dist/blocks/digit/proof/manifest.json +8 -0
  36. package/dist/blocks/digit/proof/tests.fast-check.ts +21 -0
  37. package/dist/blocks/digit/spec.yak +79 -0
  38. package/dist/blocks/digit-or-throw/impl.ts +20 -0
  39. package/dist/blocks/digit-or-throw/proof/manifest.json +8 -0
  40. package/dist/blocks/digit-or-throw/proof/tests.fast-check.ts +20 -0
  41. package/dist/blocks/digit-or-throw/spec.yak +80 -0
  42. package/dist/blocks/empty-list-content/impl.ts +20 -0
  43. package/dist/blocks/empty-list-content/proof/manifest.json +8 -0
  44. package/dist/blocks/empty-list-content/proof/tests.fast-check.ts +20 -0
  45. package/dist/blocks/empty-list-content/spec.yak +80 -0
  46. package/dist/blocks/eof-check/impl.ts +16 -0
  47. package/dist/blocks/eof-check/proof/manifest.json +8 -0
  48. package/dist/blocks/eof-check/proof/tests.fast-check.ts +20 -0
  49. package/dist/blocks/eof-check/spec.yak +76 -0
  50. package/dist/blocks/integer/impl.ts +29 -0
  51. package/dist/blocks/integer/proof/manifest.json +8 -0
  52. package/dist/blocks/integer/proof/tests.fast-check.ts +21 -0
  53. package/dist/blocks/integer/spec.yak +84 -0
  54. package/dist/blocks/list-of-ints/impl.ts +168 -0
  55. package/dist/blocks/list-of-ints/proof/manifest.json +8 -0
  56. package/dist/blocks/list-of-ints/proof/tests.fast-check.ts +23 -0
  57. package/dist/blocks/list-of-ints/spec.yak +103 -0
  58. package/dist/blocks/lru-node/impl.ts +50 -0
  59. package/dist/blocks/lru-node/proof/manifest.json +8 -0
  60. package/dist/blocks/lru-node/proof/tests.fast-check.ts +16 -0
  61. package/dist/blocks/lru-node/spec.yak +92 -0
  62. package/dist/blocks/memoize/impl.ts +60 -0
  63. package/dist/blocks/memoize/proof/manifest.json +8 -0
  64. package/dist/blocks/memoize/proof/tests.fast-check.ts +15 -0
  65. package/dist/blocks/memoize/spec.yak +91 -0
  66. package/dist/blocks/non-ascii-rejector/impl.ts +14 -0
  67. package/dist/blocks/non-ascii-rejector/proof/manifest.json +8 -0
  68. package/dist/blocks/non-ascii-rejector/proof/tests.fast-check.ts +20 -0
  69. package/dist/blocks/non-ascii-rejector/spec.yak +67 -0
  70. package/dist/blocks/nonempty-list-content/impl.ts +116 -0
  71. package/dist/blocks/nonempty-list-content/proof/manifest.json +8 -0
  72. package/dist/blocks/nonempty-list-content/proof/tests.fast-check.ts +20 -0
  73. package/dist/blocks/nonempty-list-content/spec.yak +88 -0
  74. package/dist/blocks/optional-whitespace/impl.ts +21 -0
  75. package/dist/blocks/optional-whitespace/proof/manifest.json +8 -0
  76. package/dist/blocks/optional-whitespace/proof/tests.fast-check.ts +20 -0
  77. package/dist/blocks/optional-whitespace/spec.yak +76 -0
  78. package/dist/blocks/peek-char/impl.ts +15 -0
  79. package/dist/blocks/peek-char/proof/manifest.json +8 -0
  80. package/dist/blocks/peek-char/proof/tests.fast-check.ts +20 -0
  81. package/dist/blocks/peek-char/spec.yak +76 -0
  82. package/dist/blocks/position-step/impl.ts +21 -0
  83. package/dist/blocks/position-step/proof/manifest.json +8 -0
  84. package/dist/blocks/position-step/proof/tests.fast-check.ts +20 -0
  85. package/dist/blocks/position-step/spec.yak +85 -0
  86. package/dist/blocks/queue-drain/impl.ts +74 -0
  87. package/dist/blocks/queue-drain/proof/manifest.json +8 -0
  88. package/dist/blocks/queue-drain/proof/tests.fast-check.ts +16 -0
  89. package/dist/blocks/queue-drain/spec.yak +119 -0
  90. package/dist/blocks/semver-component-parser/impl.ts +115 -0
  91. package/dist/blocks/semver-component-parser/proof/manifest.json +8 -0
  92. package/dist/blocks/semver-component-parser/proof/tests.fast-check.ts +19 -0
  93. package/dist/blocks/semver-component-parser/spec.yak +109 -0
  94. package/dist/blocks/signed-integer/impl.ts +40 -0
  95. package/dist/blocks/signed-integer/proof/manifest.json +8 -0
  96. package/dist/blocks/signed-integer/proof/tests.fast-check.ts +21 -0
  97. package/dist/blocks/signed-integer/spec.yak +84 -0
  98. package/dist/blocks/string-from-position/impl.ts +18 -0
  99. package/dist/blocks/string-from-position/proof/manifest.json +8 -0
  100. package/dist/blocks/string-from-position/proof/tests.fast-check.ts +20 -0
  101. package/dist/blocks/string-from-position/spec.yak +85 -0
  102. package/dist/blocks/timer-handle/impl.ts +66 -0
  103. package/dist/blocks/timer-handle/proof/manifest.json +8 -0
  104. package/dist/blocks/timer-handle/proof/tests.fast-check.ts +18 -0
  105. package/dist/blocks/timer-handle/spec.yak +67 -0
  106. package/dist/blocks/whitespace/impl.ts +20 -0
  107. package/dist/blocks/whitespace/proof/manifest.json +8 -0
  108. package/dist/blocks/whitespace/proof/tests.fast-check.ts +21 -0
  109. package/dist/blocks/whitespace/spec.yak +80 -0
  110. package/dist/bootstrap/expected-failures.json +4 -0
  111. package/dist/bootstrap/expected-roots.json +49890 -0
  112. package/dist/bootstrap/yakcc.registry.sqlite +0 -0
  113. package/dist/index.js +10826 -0
  114. package/dist/index.js.map +1 -0
  115. package/package.json +78 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../node_modules/.pnpm/@noble+hashes@2.2.0/node_modules/@noble/hashes/src/utils.ts","../../../node_modules/.pnpm/@noble+hashes@2.2.0/node_modules/@noble/hashes/src/_md.ts","../../../node_modules/.pnpm/@noble+hashes@2.2.0/node_modules/@noble/hashes/src/_u64.ts","../../../node_modules/.pnpm/@noble+hashes@2.2.0/node_modules/@noble/hashes/src/_blake.ts","../../../node_modules/.pnpm/@noble+hashes@2.2.0/node_modules/@noble/hashes/src/blake2.ts","../../../node_modules/.pnpm/@noble+hashes@2.2.0/node_modules/@noble/hashes/src/blake3.ts","../../contracts/src/canonicalize.ts","../../contracts/src/contract-id.ts","../../contracts/src/granularity.ts","../../contracts/src/embeddings.ts","../../contracts/src/spec-yak.ts","../../contracts/src/proof-manifest.ts","../../contracts/src/merkle.ts","../../contracts/src/canonical-ast.ts","../../contracts/src/source-extract.ts","../../contracts/src/source-pick.ts","../../contracts/src/query-from-source.ts","../../contracts/src/index.ts","../../registry/src/schema.ts","../../registry/src/search.ts","../../registry/src/rebuild.ts","../../registry/src/storage.ts","../../registry/src/select.ts","../../registry/src/index.ts","../../shave/src/intent/constants.ts","../../shave/src/intent/anthropic-client.ts","../../shave/src/intent/prompt.ts","../../federation/src/types.ts","../../federation/src/wire.ts","../../federation/src/transport.ts","../../federation/src/http-transport.ts","../../federation/src/pull.ts","../../federation/src/mirror.ts","../../federation/src/serve.ts","../../federation/src/index.ts","../src/commands/federation.ts","../src/bin.ts","../src/commands/bootstrap.ts","../../shave/src/types.ts","../../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/src/utils.ts","../../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/src/_md.ts","../../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/src/_u64.ts","../../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/src/_blake.ts","../../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/src/blake2.ts","../../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/src/blake3.ts","../../shave/src/cache/file-cache.ts","../../shave/src/cache/atomic-write.ts","../../shave/src/cache/normalize.ts","../../shave/src/cache/key.ts","../../shave/src/corpus/ai-derived.ts","../../shave/src/corpus/props-file.ts","../../shave/src/corpus/documented-usage.ts","../../shave/src/corpus/upstream-test.ts","../../shave/src/corpus/index.ts","../../shave/src/errors.ts","../../shave/src/intent/validate-intent-card.ts","../../shave/src/index.ts","../../shave/src/universalize/atom-test.ts","../../shave/src/universalize/recursion.ts","../../shave/src/universalize/stef.ts","../../ir/src/strict-subset.ts","../../ir/src/block-parser.ts","../../ir/src/strict-subset-project.ts","../../ir/src/ast-binding.ts","../../shave/src/universalize/slicer.ts","../../shave/src/universalize/variance-rank.ts","../../variance/src/index.ts","../../shave/src/persist/spec-from-intent.ts","../../shave/src/intent/extract.ts","../../shave/src/intent/static-extract.ts","../../shave/src/locate-root.ts","../../shave/src/persist/triplet.ts","../../shave/src/persist/atom-persist.ts","../src/commands/plumbing-globs.ts","../src/commands/compile-self.ts","../src/commands/compile.ts","../../compile/src/ts-backend.ts","../../compile/src/as-backend.ts","../../compile/src/assemble.ts","../../compile/src/manifest.ts","../../compile/src/resolve.ts","../../compile/src/import-gate.ts","../../seeds/src/seed.ts","../src/commands/registry-init.ts","../src/index.ts","../src/commands/hooks-aider-install.ts","../src/commands/hooks-cursor-install.ts","../src/commands/hooks-install.ts","../src/commands/hooks-windsurf-install.ts","../src/commands/init.ts","../src/lib/ide-detect.ts","../src/commands/hooks-cline-install.ts","../src/commands/hooks-continue-install.ts","../src/commands/seed-yakcc.ts","../src/commands/propose.ts","../src/commands/query.ts","../src/commands/registry-export.ts","../src/commands/registry-rebuild.ts","../src/commands/search.ts","../src/commands/seed.ts","../src/commands/shave.ts","../src/commands/uninstall.ts","../src/pkg-native-compat.ts"],"sourcesContent":["/**\n * Utilities for hex, bytes, CSPRNG.\n * @module\n */\n/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */\n/**\n * Bytes API type helpers for old + new TypeScript.\n *\n * TS 5.6 has `Uint8Array`, while TS 5.9+ made it generic `Uint8Array<ArrayBuffer>`.\n * We can't use specific return type, because TS 5.6 will error.\n * We can't use generic return type, because most TS 5.9 software will expect specific type.\n *\n * Maps typed-array input leaves to broad forms.\n * These are compatibility adapters, not ownership guarantees.\n *\n * - `TArg` keeps byte inputs broad.\n * - `TRet` marks byte outputs for TS 5.6 and TS 5.9+ compatibility.\n */\nexport type TypedArg<T> = T extends BigInt64Array\n ? BigInt64Array\n : T extends BigUint64Array\n ? BigUint64Array\n : T extends Float32Array\n ? Float32Array\n : T extends Float64Array\n ? Float64Array\n : T extends Int16Array\n ? Int16Array\n : T extends Int32Array\n ? Int32Array\n : T extends Int8Array\n ? Int8Array\n : T extends Uint16Array\n ? Uint16Array\n : T extends Uint32Array\n ? Uint32Array\n : T extends Uint8ClampedArray\n ? Uint8ClampedArray\n : T extends Uint8Array\n ? Uint8Array\n : never;\n/** Maps typed-array output leaves to narrow TS-compatible forms. */\nexport type TypedRet<T> = T extends BigInt64Array\n ? ReturnType<typeof BigInt64Array.of>\n : T extends BigUint64Array\n ? ReturnType<typeof BigUint64Array.of>\n : T extends Float32Array\n ? ReturnType<typeof Float32Array.of>\n : T extends Float64Array\n ? ReturnType<typeof Float64Array.of>\n : T extends Int16Array\n ? ReturnType<typeof Int16Array.of>\n : T extends Int32Array\n ? ReturnType<typeof Int32Array.of>\n : T extends Int8Array\n ? ReturnType<typeof Int8Array.of>\n : T extends Uint16Array\n ? ReturnType<typeof Uint16Array.of>\n : T extends Uint32Array\n ? ReturnType<typeof Uint32Array.of>\n : T extends Uint8ClampedArray\n ? ReturnType<typeof Uint8ClampedArray.of>\n : T extends Uint8Array\n ? ReturnType<typeof Uint8Array.of>\n : never;\n/** Recursively adapts byte-carrying API input types. See {@link TypedArg}. */\nexport type TArg<T> =\n | T\n | ([TypedArg<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TRet<A[K]> }) => TArg<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TArg<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TArg<A>, ...{ [K in keyof R]: TArg<R[K]> }]\n : T extends (infer A)[]\n ? TArg<A>[]\n : T extends readonly (infer A)[]\n ? readonly TArg<A>[]\n : T extends Promise<infer A>\n ? Promise<TArg<A>>\n : T extends object\n ? { [K in keyof T]: TArg<T[K]> }\n : T\n : TypedArg<T>);\n/** Recursively adapts byte-carrying API output types. See {@link TypedArg}. */\nexport type TRet<T> = T extends unknown\n ? T &\n ([TypedRet<T>] extends [never]\n ? T extends (...args: infer A) => infer R\n ? ((...args: { [K in keyof A]: TArg<A[K]> }) => TRet<R>) & {\n [K in keyof T]: T[K] extends (...args: any) => any ? T[K] : TRet<T[K]>;\n }\n : T extends [infer A, ...infer R]\n ? [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends readonly [infer A, ...infer R]\n ? readonly [TRet<A>, ...{ [K in keyof R]: TRet<R[K]> }]\n : T extends (infer A)[]\n ? TRet<A>[]\n : T extends readonly (infer A)[]\n ? readonly TRet<A>[]\n : T extends Promise<infer A>\n ? Promise<TRet<A>>\n : T extends object\n ? { [K in keyof T]: TRet<T[K]> }\n : T\n : TypedRet<T>)\n : never;\n/**\n * Checks if something is Uint8Array. Be careful: nodejs Buffer will return true.\n * @param a - value to test\n * @returns `true` when the value is a Uint8Array-compatible view.\n * @example\n * Check whether a value is a Uint8Array-compatible view.\n * ```ts\n * isBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function isBytes(a: unknown): a is Uint8Array {\n // Plain `instanceof Uint8Array` is too strict for some Buffer / proxy / cross-realm cases.\n // The fallback still requires a real ArrayBuffer view, so plain\n // JSON-deserialized `{ constructor: ... }` spoofing is rejected, and\n // `BYTES_PER_ELEMENT === 1` keeps the fallback on byte-oriented views.\n return (\n a instanceof Uint8Array ||\n (ArrayBuffer.isView(a) &&\n a.constructor.name === 'Uint8Array' &&\n 'BYTES_PER_ELEMENT' in a &&\n a.BYTES_PER_ELEMENT === 1)\n );\n}\n\n/**\n * Asserts something is a non-negative integer.\n * @param n - number to validate\n * @param title - label included in thrown errors\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a non-negative integer option.\n * ```ts\n * anumber(32, 'length');\n * ```\n */\nexport function anumber(n: number, title: string = ''): void {\n if (typeof n !== 'number') {\n const prefix = title && `\"${title}\" `;\n throw new TypeError(`${prefix}expected number, got ${typeof n}`);\n }\n if (!Number.isSafeInteger(n) || n < 0) {\n const prefix = title && `\"${title}\" `;\n throw new RangeError(`${prefix}expected integer >= 0, got ${n}`);\n }\n}\n\n/**\n * Asserts something is Uint8Array.\n * @param value - value to validate\n * @param length - optional exact length constraint\n * @param title - label included in thrown errors\n * @returns The validated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate that a value is a byte array.\n * ```ts\n * abytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function abytes(\n value: TArg<Uint8Array>,\n length?: number,\n title: string = ''\n): TRet<Uint8Array> {\n const bytes = isBytes(value);\n const len = value?.length;\n const needsLen = length !== undefined;\n if (!bytes || (needsLen && len !== length)) {\n const prefix = title && `\"${title}\" `;\n const ofLen = needsLen ? ` of length ${length}` : '';\n const got = bytes ? `length=${len}` : `type=${typeof value}`;\n const message = prefix + 'expected Uint8Array' + ofLen + ', got ' + got;\n if (!bytes) throw new TypeError(message);\n throw new RangeError(message);\n }\n return value as TRet<Uint8Array>;\n}\n\n/**\n * Copies bytes into a fresh Uint8Array.\n * Buffer-style slices can alias the same backing store, so callers that need ownership should copy.\n * @param bytes - source bytes to clone\n * @returns Freshly allocated copy of `bytes`.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Clone a byte array before mutating it.\n * ```ts\n * const copy = copyBytes(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function copyBytes(bytes: TArg<Uint8Array>): TRet<Uint8Array> {\n // `Uint8Array.from(...)` would also accept arrays / other typed arrays. Keep this helper strict\n // because callers use it at byte-validation boundaries before mutating the detached copy.\n return Uint8Array.from(abytes(bytes)) as TRet<Uint8Array>;\n}\n\n/**\n * Asserts something is a wrapped hash constructor.\n * @param h - hash constructor to validate\n * @throws On wrong argument types or invalid hash wrapper shape. {@link TypeError}\n * @throws On invalid hash metadata ranges or values. {@link RangeError}\n * @throws If the hash metadata allows empty outputs or block sizes. {@link Error}\n * @example\n * Validate a callable hash wrapper.\n * ```ts\n * import { ahash } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * ahash(sha256);\n * ```\n */\nexport function ahash(h: TArg<CHash>): void {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new TypeError('Hash must wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n // HMAC and KDF callers treat these as real byte lengths; allowing zero lets fake wrappers pass\n // validation and can produce empty outputs instead of failing fast.\n if (h.outputLen < 1) throw new Error('\"outputLen\" must be >= 1');\n if (h.blockLen < 1) throw new Error('\"blockLen\" must be >= 1');\n}\n\n/**\n * Asserts a hash instance has not been destroyed or finished.\n * @param instance - hash instance to validate\n * @param checkFinished - whether to reject finalized instances\n * @throws If the hash instance has already been destroyed or finalized. {@link Error}\n * @example\n * Validate that a hash instance is still usable.\n * ```ts\n * import { aexists } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aexists(hash);\n * ```\n */\nexport function aexists(instance: any, checkFinished = true): void {\n if (instance.destroyed) throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');\n}\n\n/**\n * Asserts output is a sufficiently-sized byte array.\n * @param out - destination buffer\n * @param instance - hash instance providing output length\n * Oversized buffers are allowed; downstream code only promises to fill the first `outputLen` bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Validate a caller-provided digest buffer.\n * ```ts\n * import { aoutput } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const hash = sha256.create();\n * aoutput(new Uint8Array(hash.outputLen), hash);\n * ```\n */\nexport function aoutput(out: any, instance: any): void {\n abytes(out, undefined, 'digestInto() output');\n const min = instance.outputLen;\n if (out.length < min) {\n throw new RangeError('\"digestInto() output\" expected to be of length >=' + min);\n }\n}\n\n/** Generic type encompassing 8/16/32-byte array views, but not 64-bit variants. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/**\n * Casts a typed array view to Uint8Array.\n * @param arr - source typed array\n * @returns Uint8Array view over the same buffer.\n * @example\n * Reinterpret a typed array as bytes.\n * ```ts\n * u8(new Uint32Array([1, 2]));\n * ```\n */\nexport function u8(arr: TArg<TypedArray>): TRet<Uint8Array> {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength) as TRet<Uint8Array>;\n}\n\n/**\n * Casts a typed array view to Uint32Array.\n * `arr.byteOffset` must already be 4-byte aligned or the platform\n * Uint32Array constructor will throw.\n * @param arr - source typed array\n * @returns Uint32Array view over the same buffer.\n * @example\n * Reinterpret a byte array as 32-bit words.\n * ```ts\n * u32(new Uint8Array(8));\n * ```\n */\nexport function u32(arr: TArg<TypedArray>): TRet<Uint32Array> {\n return new Uint32Array(\n arr.buffer,\n arr.byteOffset,\n Math.floor(arr.byteLength / 4)\n ) as TRet<Uint32Array>;\n}\n\n/**\n * Zeroizes typed arrays in place. Warning: JS provides no guarantees.\n * @param arrays - arrays to overwrite with zeros\n * @example\n * Zeroize sensitive buffers in place.\n * ```ts\n * clean(new Uint8Array([1, 2, 3]));\n * ```\n */\nexport function clean(...arrays: TArg<TypedArray[]>): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/**\n * Creates a DataView for byte-level manipulation.\n * @param arr - source typed array\n * @returns DataView over the same buffer region.\n * @example\n * Create a DataView over an existing buffer.\n * ```ts\n * createView(new Uint8Array(4));\n * ```\n */\nexport function createView(arr: TArg<TypedArray>): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/**\n * Rotate-right operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the right.\n * ```ts\n * rotr(0x12345678, 8);\n * ```\n */\nexport function rotr(word: number, shift: number): number {\n return (word << (32 - shift)) | (word >>> shift);\n}\n\n/**\n * Rotate-left operation for uint32 values.\n * @param word - source word\n * @param shift - shift amount in bits\n * @returns Rotated word.\n * @example\n * Rotate a 32-bit word to the left.\n * ```ts\n * rotl(0x12345678, 8);\n * ```\n */\nexport function rotl(word: number, shift: number): number {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n\n/** Whether the current platform is little-endian. */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/**\n * Byte-swap operation for uint32 values.\n * @param word - source word\n * @returns Word with reversed byte order.\n * @example\n * Reverse the byte order of a 32-bit word.\n * ```ts\n * byteSwap(0x11223344);\n * ```\n */\nexport function byteSwap(word: number): number {\n return (\n ((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff)\n );\n}\n/**\n * Conditionally byte-swaps one 32-bit word on big-endian platforms.\n * @param n - source word\n * @returns Original or byte-swapped word depending on platform endianness.\n * @example\n * Normalize a 32-bit word for host endianness.\n * ```ts\n * swap8IfBE(0x11223344);\n * ```\n */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n) >>> 0;\n\n/**\n * Byte-swaps every word of a Uint32Array in place.\n * @param arr - array to mutate\n * @returns The same array after mutation; callers pass live state arrays here.\n * @example\n * Reverse the byte order of every word in place.\n * ```ts\n * byteSwap32(new Uint32Array([0x11223344]));\n * ```\n */\nexport function byteSwap32(arr: TArg<Uint32Array>): TRet<Uint32Array> {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr as TRet<Uint32Array>;\n}\n\n/**\n * Conditionally byte-swaps a Uint32Array on big-endian platforms.\n * @param u - array to normalize for host endianness\n * @returns Original or byte-swapped array depending on platform endianness.\n * On big-endian runtimes this mutates `u` in place via `byteSwap32(...)`.\n * @example\n * Normalize a word array for host endianness.\n * ```ts\n * swap32IfBE(new Uint32Array([0x11223344]));\n * ```\n */\nexport const swap32IfBE: (u: TArg<Uint32Array>) => TRet<Uint32Array> = isLE\n ? (u: TArg<Uint32Array>) => u as TRet<Uint32Array>\n : byteSwap32;\n\n// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex\nconst hasHexBuiltin: boolean = /* @__PURE__ */ (() =>\n // @ts-ignore\n typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();\n\n// Array where index 0xf0 (240) is mapped to string 'f0'\nconst hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>\n i.toString(16).padStart(2, '0')\n);\n\n/**\n * Convert byte array to hex string.\n * Uses the built-in function when available and assumes it matches the tested\n * fallback semantics.\n * @param bytes - bytes to encode\n * @returns Lowercase hexadecimal string.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Convert bytes to lowercase hexadecimal.\n * ```ts\n * bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])); // 'cafe0123'\n * ```\n */\nexport function bytesToHex(bytes: TArg<Uint8Array>): string {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin) return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n\n// We use optimized technique to convert hex string to byte array\nconst asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const;\nfunction asciiToBase16(ch: number): number | undefined {\n if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48\n if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10)\n if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10)\n return;\n}\n\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @param hex - hexadecimal string to decode\n * @returns Decoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @example\n * Decode lowercase hexadecimal into bytes.\n * ```ts\n * hexToBytes('cafe0123'); // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n * ```\n */\nexport function hexToBytes(hex: string): TRet<Uint8Array> {\n if (typeof hex !== 'string') throw new TypeError('hex string expected, got ' + typeof hex);\n if (hasHexBuiltin) {\n try {\n return (Uint8Array as any).fromHex(hex);\n } catch (error) {\n if (error instanceof SyntaxError) throw new RangeError(error.message);\n throw error;\n }\n }\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new RangeError('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi));\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1));\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new RangeError(\n 'hex string expected, got non-hex character \"' + char + '\" at index ' + hi\n );\n }\n array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163\n }\n return array;\n}\n\n/**\n * There is no setImmediate in browser and setTimeout is slow.\n * This yields to the Promise/microtask scheduler queue, not to timers or the\n * full macrotask event loop.\n * @example\n * Yield to the next scheduler tick.\n * ```ts\n * await nextTick();\n * ```\n */\nexport const nextTick = async (): Promise<void> => {};\n\n/**\n * Returns control to the Promise/microtask scheduler every `tick`\n * milliseconds to avoid blocking long loops.\n * @param iters - number of loop iterations to run\n * @param tick - maximum time slice in milliseconds\n * @param cb - callback executed on each iteration\n * @example\n * Run a loop that periodically yields back to the event loop.\n * ```ts\n * await asyncLoop(2, 0, () => {});\n * ```\n */\nexport async function asyncLoop(\n iters: number,\n tick: number,\n cb: (i: number) => void\n): Promise<void> {\n let ts = Date.now();\n for (let i = 0; i < iters; i++) {\n cb(i);\n // Date.now() is not monotonic, so in case if clock goes backwards we return return control too\n const diff = Date.now() - ts;\n if (diff >= 0 && diff < tick) continue;\n await nextTick();\n ts += diff;\n }\n}\n\n// Global symbols, but ts doesn't see them: https://github.com/microsoft/TypeScript/issues/31535\ndeclare const TextEncoder: any;\n\n/**\n * Converts string to bytes using UTF8 encoding.\n * Built-in doesn't validate input to be string: we do the check.\n * Non-ASCII details are delegated to the platform `TextEncoder`.\n * @param str - string to encode\n * @returns UTF-8 encoded bytes.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Encode a string as UTF-8 bytes.\n * ```ts\n * utf8ToBytes('abc'); // Uint8Array.from([97, 98, 99])\n * ```\n */\nexport function utf8ToBytes(str: string): TRet<Uint8Array> {\n if (typeof str !== 'string') throw new TypeError('string expected');\n return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809\n}\n\n/** KDFs can accept string or Uint8Array for user convenience. */\nexport type KDFInput = string | Uint8Array;\n\n/**\n * Helper for KDFs: consumes Uint8Array or string.\n * String inputs are UTF-8 encoded; byte-array inputs stay aliased to the caller buffer.\n * @param data - user-provided KDF input\n * @param errorTitle - label included in thrown errors\n * @returns Byte representation of the input.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Normalize KDF input to bytes.\n * ```ts\n * kdfInputToBytes('password');\n * ```\n */\nexport function kdfInputToBytes(data: TArg<KDFInput>, errorTitle = ''): TRet<Uint8Array> {\n if (typeof data === 'string') return utf8ToBytes(data);\n return abytes(data, undefined, errorTitle);\n}\n\n/**\n * Copies several Uint8Arrays into one.\n * @param arrays - arrays to concatenate\n * @returns Concatenated byte array.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Concatenate multiple byte arrays.\n * ```ts\n * concatBytes(new Uint8Array([1]), new Uint8Array([2]));\n * ```\n */\nexport function concatBytes(...arrays: TArg<Uint8Array[]>): TRet<Uint8Array> {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res;\n}\n\ntype EmptyObj = {};\n/**\n * Merges default options and passed options.\n * @param defaults - base option object\n * @param opts - user overrides\n * @returns Merged option object. The merge mutates `defaults` in place.\n * @throws On wrong argument types. {@link TypeError}\n * @example\n * Merge user overrides onto default options.\n * ```ts\n * checkOpts({ dkLen: 32 }, { asyncTick: 10 });\n * ```\n */\nexport function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(\n defaults: T1,\n opts?: T2\n): T1 & T2 {\n if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')\n throw new TypeError('options must be object or undefined');\n const merged = Object.assign(defaults, opts);\n return merged as T1 & T2;\n}\n\n/** Common interface for all hash instances. */\nexport interface Hash<T> {\n /** Bytes processed per compression block. */\n blockLen: number;\n /** Bytes produced by `digest()`. */\n outputLen: number;\n /** Whether the instance supports XOF-style variable-length output via `xof()` / `xofInto()`. */\n canXOF: boolean;\n /**\n * Absorbs more message bytes into the running hash state.\n * @param buf - message chunk to absorb\n * @returns The same hash instance for chaining.\n */\n update(buf: TArg<Uint8Array>): this;\n /**\n * Finalizes the hash into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Nothing. Implementations write into `buf` in place.\n */\n digestInto(buf: TArg<Uint8Array>): void;\n /**\n * Finalizes the hash and returns a freshly allocated digest.\n * @returns Digest bytes.\n */\n digest(): TRet<Uint8Array>;\n /** Wipes internal state and makes the instance unusable. */\n destroy(): void;\n /**\n * Copies the current hash state into an existing or new instance.\n * @param to - Optional destination instance to reuse.\n * @returns Cloned hash state.\n */\n _cloneInto(to?: T): T;\n /**\n * Creates an independent copy of the current hash state.\n * @returns Cloned hash instance.\n */\n clone(): T;\n}\n\n/** Pseudorandom generator interface. */\nexport interface PRG {\n /**\n * Mixes more entropy into the generator state.\n * @param seed - fresh entropy bytes\n * @returns Nothing. Implementations update internal state in place.\n */\n addEntropy(seed: TArg<Uint8Array>): void;\n /**\n * Generates pseudorandom output bytes.\n * @param length - number of bytes to generate\n * @returns Generated pseudorandom bytes.\n */\n randomBytes(length: number): TRet<Uint8Array>;\n /** Wipes generator state and makes the instance unusable. */\n clean(): void;\n}\n\n/**\n * XOF: streaming API to read digest in chunks.\n * Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.\n * When hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot\n * destroy state, next call can require more bytes.\n */\nexport type HashXOF<T extends Hash<T>> = Hash<T> & {\n /**\n * Reads more bytes from the XOF stream.\n * @param bytes - number of bytes to read\n * @returns Requested digest bytes.\n */\n xof(bytes: number): TRet<Uint8Array>;\n /**\n * Reads more bytes from the XOF stream into a caller-provided buffer.\n * @param buf - destination buffer\n * @returns Filled output buffer.\n */\n xofInto(buf: TArg<Uint8Array>): TRet<Uint8Array>;\n};\n\n/** Hash constructor or factory type. */\nexport type HasherCons<T, Opts = undefined> = Opts extends undefined ? () => T : (opts?: Opts) => T;\n/** Optional hash metadata. */\nexport type HashInfo = {\n /** DER-encoded object identifier bytes for the hash algorithm. */\n oid?: TRet<Uint8Array>;\n};\n/** Callable hash function type. */\nexport type CHash<T extends Hash<T> = Hash<any>, Opts = undefined> = {\n /** Digest size in bytes. */\n outputLen: number;\n /** Input block size in bytes. */\n blockLen: number;\n /** Whether `.create()` returns a hash instance that can be used as an XOF stream. */\n canXOF: boolean;\n} & HashInfo &\n (Opts extends undefined\n ? {\n (msg: TArg<Uint8Array>): TRet<Uint8Array>;\n create(): T;\n }\n : {\n (msg: TArg<Uint8Array>, opts?: TArg<Opts>): TRet<Uint8Array>;\n create(opts?: Opts): T;\n });\n/** Callable extendable-output hash function type. */\nexport type CHashXOF<T extends HashXOF<T> = HashXOF<any>, Opts = undefined> = CHash<T, Opts>;\n\n/**\n * Creates a callable hash function from a stateful class constructor.\n * @param hashCons - hash constructor or factory\n * @param info - optional metadata such as DER OID\n * @returns Frozen callable hash wrapper with `.create()`.\n * Wrapper construction eagerly calls `hashCons(undefined)` once to read\n * `outputLen` / `blockLen`, so constructor side effects happen at module\n * init time.\n * @example\n * Wrap a stateful hash constructor into a callable helper.\n * ```ts\n * import { createHasher } from '@noble/hashes/utils.js';\n * import { sha256 } from '@noble/hashes/sha2.js';\n * const wrapped = createHasher(sha256.create, { oid: sha256.oid });\n * wrapped(new Uint8Array([1]));\n * ```\n */\nexport function createHasher<T extends Hash<T>, Opts = undefined>(\n hashCons: HasherCons<T, Opts>,\n info: TArg<HashInfo> = {}\n): TRet<CHash<T, Opts>> {\n const hashC: any = (msg: TArg<Uint8Array>, opts?: TArg<Opts>) =>\n hashCons(opts as Opts)\n .update(msg)\n .digest();\n const tmp = hashCons(undefined);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.canXOF = tmp.canXOF;\n hashC.create = (opts?: Opts) => hashCons(opts);\n Object.assign(hashC, info);\n return Object.freeze(hashC) as TRet<CHash<T, Opts>>;\n}\n\n/**\n * Cryptographically secure PRNG backed by `crypto.getRandomValues`.\n * @param bytesLength - number of random bytes to generate\n * @returns Random bytes.\n * The platform `getRandomValues()` implementation still defines any\n * single-call length cap, and this helper rejects oversize requests\n * with a stable library `RangeError` instead of host-specific errors.\n * @throws On wrong argument types. {@link TypeError}\n * @throws On wrong argument ranges or values. {@link RangeError}\n * @throws If the current runtime does not provide `crypto.getRandomValues`. {@link Error}\n * @example\n * Generate a fresh random key or nonce.\n * ```ts\n * const key = randomBytes(16);\n * ```\n */\nexport function randomBytes(bytesLength = 32): TRet<Uint8Array> {\n // Match the repo's other length-taking helpers instead of relying on Uint8Array coercion.\n anumber(bytesLength, 'bytesLength');\n const cr = typeof globalThis === 'object' ? (globalThis as any).crypto : null;\n if (typeof cr?.getRandomValues !== 'function')\n throw new Error('crypto.getRandomValues must be defined');\n // Web Cryptography API Level 2 §10.1.1:\n // if `byteLength > 65536`, throw `QuotaExceededError`.\n // Keep the guard explicit so callers can see the quota in code\n // instead of discovering it by reading the spec or host errors.\n // This wrapper surfaces the same quota as a stable library RangeError.\n if (bytesLength > 65536)\n throw new RangeError(`\"bytesLength\" expected <= 65536, got ${bytesLength}`);\n return cr.getRandomValues(new Uint8Array(bytesLength));\n}\n\n/**\n * Creates OID metadata for NIST hashes with prefix `06 09 60 86 48 01 65 03 04 02`.\n * @param suffix - final OID byte for the selected hash.\n * The helper accepts any byte even though only the documented NIST hash\n * suffixes are meaningful downstream.\n * @returns Object containing the DER-encoded OID.\n * @example\n * Build OID metadata for a NIST hash.\n * ```ts\n * oidNist(0x01);\n * ```\n */\nexport const oidNist = (suffix: number): TRet<Required<HashInfo>> => ({\n // Current NIST hashAlgs suffixes used here fit in one DER subidentifier octet.\n // Larger suffix values would need base-128 OID encoding and a different length byte.\n oid: Uint8Array.from([0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, suffix]),\n});\n","/**\n * Internal Merkle-Damgard hash utils.\n * @module\n */\nimport {\n abytes,\n aexists,\n aoutput,\n clean,\n createView,\n type Hash,\n type TArg,\n type TRet,\n} from './utils.ts';\n\n/**\n * Shared 32-bit conditional boolean primitive reused by SHA-256, SHA-1, and MD5 `F`.\n * Returns bits from `b` when `a` is set, otherwise from `c`.\n * The XOR form is equivalent to MD5's `F(X,Y,Z) = XY v not(X)Z` because the masked terms never\n * set the same bit.\n * @param a - selector word\n * @param b - word chosen when selector bit is set\n * @param c - word chosen when selector bit is clear\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit choice primitive.\n * ```ts\n * Chi(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Chi(a: number, b: number, c: number): number {\n return (a & b) ^ (~a & c);\n}\n\n/**\n * Shared 32-bit majority primitive reused by SHA-256 and SHA-1.\n * Returns bits shared by at least two inputs.\n * @param a - first input word\n * @param b - second input word\n * @param c - third input word\n * @returns Mixed 32-bit word.\n * @example\n * Combine three words with the shared 32-bit majority primitive.\n * ```ts\n * Maj(0xffffffff, 0x12345678, 0x87654321);\n * ```\n */\nexport function Maj(a: number, b: number, c: number): number {\n return (a & b) ^ (a & c) ^ (b & c);\n}\n\n/**\n * Merkle-Damgard hash construction base class.\n * Could be used to create MD5, RIPEMD, SHA1, SHA2.\n * Accepts only byte-aligned `Uint8Array` input, even when the underlying spec describes bit\n * strings with partial-byte tails.\n * @param blockLen - internal block size in bytes\n * @param outputLen - digest size in bytes\n * @param padOffset - trailing length field size in bytes\n * @param isLE - whether length and state words are encoded in little-endian\n * @example\n * Use a concrete subclass to get the shared Merkle-Damgard update/digest flow.\n * ```ts\n * import { _SHA1 } from '@noble/hashes/legacy.js';\n * const hash = new _SHA1();\n * hash.update(new Uint8Array([97, 98, 99]));\n * hash.digest();\n * ```\n */\nexport abstract class HashMD<T extends HashMD<T>> implements Hash<T> {\n // Subclasses must treat `buf` as read-only: `update()` may pass a direct view over caller input\n // when it can process whole blocks without buffering first.\n protected abstract process(buf: DataView, offset: number): void;\n protected abstract get(): number[];\n protected abstract set(...args: number[]): void;\n abstract destroy(): void;\n protected abstract roundClean(): void;\n\n readonly blockLen: number;\n readonly outputLen: number;\n readonly canXOF = false;\n readonly padOffset: number;\n readonly isLE: boolean;\n\n // For partial updates less than block size\n protected buffer: Uint8Array;\n protected view: DataView;\n protected finished = false;\n protected length = 0;\n protected pos = 0;\n protected destroyed = false;\n\n constructor(blockLen: number, outputLen: number, padOffset: number, isLE: boolean) {\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.padOffset = padOffset;\n this.isLE = isLE;\n this.buffer = new Uint8Array(blockLen);\n this.view = createView(this.buffer);\n }\n update(data: TArg<Uint8Array>): this {\n aexists(this);\n abytes(data);\n const { view, buffer, blockLen } = this;\n const len = data.length;\n for (let pos = 0; pos < len; ) {\n const take = Math.min(blockLen - this.pos, len - pos);\n // Fast path only when there is no buffered partial block: `take === blockLen` implies\n // `this.pos === 0`, so we can process full blocks directly from the input view.\n if (take === blockLen) {\n const dataView = createView(data);\n for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);\n continue;\n }\n buffer.set(data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n pos += take;\n if (this.pos === blockLen) {\n this.process(view, 0);\n this.pos = 0;\n }\n }\n this.length += data.length;\n this.roundClean();\n return this;\n }\n digestInto(out: TArg<Uint8Array>): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n // Padding\n // We can avoid allocation of buffer for padding completely if it\n // was previously not allocated here. But it won't change performance.\n const { buffer, view, blockLen, isLE } = this;\n let { pos } = this;\n // append the bit '1' to the message\n buffer[pos++] = 0b10000000;\n clean(this.buffer.subarray(pos));\n // we have less than padOffset left in buffer, so we cannot put length in\n // current block, need process it and pad again\n if (this.padOffset > blockLen - pos) {\n this.process(view, 0);\n pos = 0;\n }\n // Pad until full block byte with zeros\n for (let i = pos; i < blockLen; i++) buffer[i] = 0;\n // `padOffset` reserves the whole length field. For SHA-384/512 the high 64 bits stay zero from\n // the padding fill above, and JS will overflow before user input can make that half non-zero.\n // So we only need to write the low 64 bits here.\n view.setBigUint64(blockLen - 8, BigInt(this.length * 8), isLE);\n this.process(view, 0);\n const oview = createView(out);\n const len = this.outputLen;\n // NOTE: we do division by 4 later, which must be fused in single op with modulo by JIT\n if (len % 4) throw new Error('_sha2: outputLen must be aligned to 32bit');\n const outLen = len / 4;\n const state = this.get();\n if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state');\n for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);\n }\n digest(): TRet<Uint8Array> {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n // Copy before destroy(): subclasses wipe `buffer` during cleanup, but `digest()` must return\n // fresh bytes to the caller.\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res as TRet<Uint8Array>;\n }\n _cloneInto(to?: T): T {\n to ||= new (this.constructor as any)() as T;\n to.set(...this.get());\n const { blockLen, buffer, length, finished, destroyed, pos } = this;\n to.destroyed = destroyed;\n to.finished = finished;\n to.length = length;\n to.pos = pos;\n // Only partial-block bytes need copying: when `length % blockLen === 0`, `pos === 0` and\n // later `update()` / `digestInto()` overwrite `to.buffer` from the start before reading it.\n if (length % blockLen) to.buffer.set(buffer);\n return to as unknown as any;\n }\n clone(): T {\n return this._cloneInto();\n }\n}\n\n/**\n * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.\n * Check out `test/misc/sha2-gen-iv.js` for recomputation guide.\n */\n\n/** Initial SHA256 state from RFC 6234 §6.1: the first 32 bits of the fractional parts of the\n * square roots of the first eight prime numbers. Exported as a shared table; callers must treat\n * it as read-only because constructors copy words from it by index. */\nexport const SHA256_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Initial SHA224 state `H(0)` from RFC 6234 §6.1. Exported as a shared table; callers must\n * treat it as read-only because constructors copy words from it by index. */\nexport const SHA224_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,\n]);\n\n/** Initial SHA384 state from RFC 6234 §6.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the ninth\n * through sixteenth prime numbers. Exported as a shared table; callers must treat it as read-only\n * because constructors copy halves from it by index. */\nexport const SHA384_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0xcbbb9d5d, 0xc1059ed8, 0x629a292a, 0x367cd507, 0x9159015a, 0x3070dd17, 0x152fecd8, 0xf70e5939,\n 0x67332667, 0xffc00b31, 0x8eb44a87, 0x68581511, 0xdb0c2e0d, 0x64f98fa7, 0x47b5481d, 0xbefa4fa4,\n]);\n\n/** Initial SHA512 state from RFC 6234 §6.3: eight RFC 64-bit `H(0)` words stored as sixteen\n * big-endian 32-bit halves. Derived from the fractional parts of the square roots of the first\n * eight prime numbers. Exported as a shared table; callers must treat it as read-only because\n * constructors copy halves from it by index. */\nexport const SHA512_IV: TRet<Uint32Array> = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,\n 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,\n]);\n","/**\n * Internal helpers for u64.\n * BigUint64Array is too slow as per 2026, so we implement it using\n * Uint32Array.\n * @privateRemarks TODO: re-check {@link https://issues.chromium.org/issues/42212588}\n * @module\n */\nimport type { TRet } from './utils.ts';\n\nconst U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);\nconst _32n = /* @__PURE__ */ BigInt(32);\n\n// Split bigint into two 32-bit halves. With `le=true`, returned fields become `{ h: low, l: high\n// }` to match little-endian word order rather than the property names.\nfunction fromBig(\n n: bigint,\n le = false\n): {\n h: number;\n l: number;\n} {\n if (le) return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) };\n return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };\n}\n\n// Split bigint list into `[highWords, lowWords]` when `le=false`; with `le=true`, the first array\n// holds the low halves because `fromBig(...)` swaps the semantic meaning of `h` and `l`.\nfunction split(lst: bigint[], le = false): TRet<Uint32Array[]> {\n const len = lst.length;\n let Ah = new Uint32Array(len);\n let Al = new Uint32Array(len);\n for (let i = 0; i < len; i++) {\n const { h, l } = fromBig(lst[i], le);\n [Ah[i], Al[i]] = [h, l];\n }\n return [Ah, Al] as TRet<Uint32Array[]>;\n}\n\n// Combine explicit `(high, low)` 32-bit halves into a bigint; `>>> 0` normalizes signed JS\n// bitwise results back to uint32 first, and little-endian callers must swap.\nconst toBig = (h: number, l: number): bigint => (BigInt(h >>> 0) << _32n) | BigInt(l >>> 0);\n// High 32-bit half of a 64-bit logical right shift for `s` in `0..31`.\nconst shrSH = (h: number, _l: number, s: number): number => h >>> s;\n// Low 32-bit half of a 64-bit logical right shift, valid for `s` in `1..31`.\nconst shrSL = (h: number, l: number, s: number): number => (h << (32 - s)) | (l >>> s);\n// High 32-bit half of a 64-bit right rotate, valid for `s` in `1..31`.\nconst rotrSH = (h: number, l: number, s: number): number => (h >>> s) | (l << (32 - s));\n// Low 32-bit half of a 64-bit right rotate, valid for `s` in `1..31`.\nconst rotrSL = (h: number, l: number, s: number): number => (h << (32 - s)) | (l >>> s);\n// High 32-bit half of a 64-bit right rotate, valid for `s` in `33..63`; `32` uses `rotr32*`.\nconst rotrBH = (h: number, l: number, s: number): number => (h << (64 - s)) | (l >>> (s - 32));\n// Low 32-bit half of a 64-bit right rotate, valid for `s` in `33..63`; `32` uses `rotr32*`.\nconst rotrBL = (h: number, l: number, s: number): number => (h >>> (s - 32)) | (l << (64 - s));\n// High 32-bit half of a 64-bit right rotate for `s === 32`; this is just the swapped low half.\nconst rotr32H = (_h: number, l: number): number => l;\n// Low 32-bit half of a 64-bit right rotate for `s === 32`; this is just the swapped high half.\nconst rotr32L = (h: number, _l: number): number => h;\n// High 32-bit half of a 64-bit left rotate, valid for `s` in `1..31`.\nconst rotlSH = (h: number, l: number, s: number): number => (h << s) | (l >>> (32 - s));\n// Low 32-bit half of a 64-bit left rotate, valid for `s` in `1..31`.\nconst rotlSL = (h: number, l: number, s: number): number => (l << s) | (h >>> (32 - s));\n// High 32-bit half of a 64-bit left rotate, valid for `s` in `33..63`; `32` uses `rotr32*`.\nconst rotlBH = (h: number, l: number, s: number): number => (l << (s - 32)) | (h >>> (64 - s));\n// Low 32-bit half of a 64-bit left rotate, valid for `s` in `33..63`; `32` uses `rotr32*`.\nconst rotlBL = (h: number, l: number, s: number): number => (h << (s - 32)) | (l >>> (64 - s));\n\n// Add two split 64-bit words and return the split `{ h, l }` sum.\n// JS uses 32-bit signed integers for bitwise operations, so we cannot simply shift the carry out\n// of the low sum and instead use division.\nfunction add(\n Ah: number,\n Al: number,\n Bh: number,\n Bl: number\n): {\n h: number;\n l: number;\n} {\n const l = (Al >>> 0) + (Bl >>> 0);\n return { h: (Ah + Bh + ((l / 2 ** 32) | 0)) | 0, l: l | 0 };\n}\n// Addition with more than 2 elements\n// Unmasked low-word accumulator for 3-way addition; pass the raw result into `add3H(...)`.\nconst add3L = (Al: number, Bl: number, Cl: number): number => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0);\n// High-word finalize step for 3-way addition; `low` must be the untruncated output of `add3L(...)`.\nconst add3H = (low: number, Ah: number, Bh: number, Ch: number): number =>\n (Ah + Bh + Ch + ((low / 2 ** 32) | 0)) | 0;\n// Unmasked low-word accumulator for 4-way addition; pass the raw result into `add4H(...)`.\nconst add4L = (Al: number, Bl: number, Cl: number, Dl: number): number =>\n (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0);\n// High-word finalize step for 4-way addition; `low` must be the untruncated output of `add4L(...)`.\nconst add4H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number): number =>\n (Ah + Bh + Ch + Dh + ((low / 2 ** 32) | 0)) | 0;\n// Unmasked low-word accumulator for 5-way addition; pass the raw result into `add5H(...)`.\nconst add5L = (Al: number, Bl: number, Cl: number, Dl: number, El: number): number =>\n (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0);\n// High-word finalize step for 5-way addition; `low` must be the untruncated output of `add5L(...)`.\nconst add5H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number, Eh: number): number =>\n (Ah + Bh + Ch + Dh + Eh + ((low / 2 ** 32) | 0)) | 0;\n\n// prettier-ignore\nexport {\n add, add3H, add3L, add4H, add4L, add5H, add5L, fromBig, rotlBH, rotlBL, rotlSH, rotlSL, rotr32H, rotr32L, rotrBH, rotrBL, rotrSH, rotrSL, shrSH, shrSL, split, toBig\n};\n// Canonical grouped namespace for callers that prefer one object.\n// Named exports stay for direct imports.\n// prettier-ignore\nconst u64: { fromBig: typeof fromBig; split: typeof split; toBig: (h: number, l: number) => bigint; shrSH: (h: number, _l: number, s: number) => number; shrSL: (h: number, l: number, s: number) => number; rotrSH: (h: number, l: number, s: number) => number; rotrSL: (h: number, l: number, s: number) => number; rotrBH: (h: number, l: number, s: number) => number; rotrBL: (h: number, l: number, s: number) => number; rotr32H: (_h: number, l: number) => number; rotr32L: (h: number, _l: number) => number; rotlSH: (h: number, l: number, s: number) => number; rotlSL: (h: number, l: number, s: number) => number; rotlBH: (h: number, l: number, s: number) => number; rotlBL: (h: number, l: number, s: number) => number; add: typeof add; add3L: (Al: number, Bl: number, Cl: number) => number; add3H: (low: number, Ah: number, Bh: number, Ch: number) => number; add4L: (Al: number, Bl: number, Cl: number, Dl: number) => number; add4H: (low: number, Ah: number, Bh: number, Ch: number, Dh: number) => number; add5H: (low: number, Ah: number, Bh: number, Ch: number, Dh: number, Eh: number) => number; add5L: (Al: number, Bl: number, Cl: number, Dl: number, El: number) => number; } = {\n fromBig, split, toBig,\n shrSH, shrSL,\n rotrSH, rotrSL, rotrBH, rotrBL,\n rotr32H, rotr32L,\n rotlSH, rotlSL, rotlBH, rotlBL,\n add, add3L, add3H, add4L, add4H, add5H, add5L,\n};\n// Default export mirrors named `u64` for compatibility with object-style imports.\nexport default u64;\n","/**\n * Internal helpers for blake hash.\n * @module\n */\nimport { rotr, type TRet } from './utils.ts';\n\n/**\n * Internal blake permutation table.\n * Rows `0..9` serve BLAKE2s, rows `0..11` serve BLAKE2b with `10..11 = 0..1`, and Blake1 also\n * reuses the later rows shown below. Blake1 expands rounds `10..15` as `SIGMA[i % 10]`, so rows\n * `10..15` intentionally repeat rows `0..5` for the 14-round (256) and 16-round (512) variants.\n */\n// prettier-ignore\nexport const BSIGMA: TRet<Uint8Array> = /* @__PURE__ */ Uint8Array.from([\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\n 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,\n 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,\n 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,\n 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,\n 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,\n 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,\n 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,\n 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,\n 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\n 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,\n // Blake1, unused in others\n 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,\n 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,\n 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,\n 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,\n]);\n\n// prettier-ignore\nexport type Num4 = { a: number; b: number; c: number; d: number; };\n\n// 32-bit / BLAKE2s first half of G, with the fixed `(16, 12)` rotation pair.\n// Parameter `x` is the RFC 7693 first-half message word, or Blake1's pre-mixed\n// `m[sigma[r][2i]] ^ u[sigma[r][2i+1]]` addend in the 32-bit path.\nexport function G1s(a: number, b: number, c: number, d: number, x: number): Num4 {\n a = (a + b + x) | 0;\n d = rotr(d ^ a, 16);\n c = (c + d) | 0;\n b = rotr(b ^ c, 12);\n return { a, b, c, d };\n}\n\n// 32-bit / BLAKE2s second half of G.\n// Parameter `x` is the RFC 7693 second-half (`y`) message word, or Blake1's pre-mixed\n// `m[sigma[r][2i + 1]] ^ u[sigma[r][2i]]` addend in the 32-bit path.\nexport function G2s(a: number, b: number, c: number, d: number, x: number): Num4 {\n a = (a + b + x) | 0;\n d = rotr(d ^ a, 8);\n c = (c + d) | 0;\n b = rotr(b ^ c, 7);\n return { a, b, c, d };\n}\n","/**\n * blake2b (64-bit) & blake2s (8 to 32-bit) hash functions.\n * b could have been faster, but there is no fast u64 in js, so s is 1.5x faster.\n * @module\n */\nimport { BSIGMA, G1s, G2s } from './_blake.ts';\nimport { SHA256_IV } from './_md.ts';\nimport * as u64 from './_u64.ts';\n// prettier-ignore\nimport {\n abytes, aexists, anumber, aoutput,\n clean, createHasher,\n swap32IfBE, swap8IfBE,\n u32,\n type CHash,\n type Hash,\n type TArg,\n type TRet\n} from './utils.ts';\n\n/**\n * Blake hash options.\n * `dkLen` is output length. `key` is used in MAC mode. `salt` is used in\n * KDF mode.\n */\nexport type Blake2Opts = {\n /** Desired digest length in bytes. RFC 7693 uses 1..64 for blake2b and 1..32 for blake2s. */\n dkLen?: number;\n /** Optional MAC key. */\n key?: Uint8Array;\n /** Optional salt mixed into initialization. */\n salt?: Uint8Array;\n /** Optional personalization bytes. */\n personalization?: Uint8Array;\n};\n\n// Same IV words as `SHA512_IV`, but endian-swapped into LE u32 low/high halves\n// for the BLAKE2b u64 helpers below.\nconst B2B_IV = /* @__PURE__ */ Uint32Array.from([\n 0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372, 0x5f1d36f1, 0xa54ff53a,\n 0xade682d1, 0x510e527f, 0x2b3e6c1f, 0x9b05688c, 0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19,\n]);\n// Shared synchronous BLAKE2b work vector as LE u32 low/high halves.\nconst BBUF = /* @__PURE__ */ new Uint32Array(32);\n\n// BLAKE2b G mix split into two half-rounds over LE u32 low/high limbs.\nfunction G1b(a: number, b: number, c: number, d: number, msg: TArg<Uint32Array>, x: number) {\n // NOTE: V is LE here\n const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore\n let Al = BBUF[2 * a], Ah = BBUF[2 * a + 1]; // prettier-ignore\n let Bl = BBUF[2 * b], Bh = BBUF[2 * b + 1]; // prettier-ignore\n let Cl = BBUF[2 * c], Ch = BBUF[2 * c + 1]; // prettier-ignore\n let Dl = BBUF[2 * d], Dh = BBUF[2 * d + 1]; // prettier-ignore\n // v[a] = (v[a] + v[b] + x) | 0;\n let ll = u64.add3L(Al, Bl, Xl);\n Ah = u64.add3H(ll, Ah, Bh, Xh);\n Al = ll | 0;\n // v[d] = rotr(v[d] ^ v[a], 32)\n ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });\n ({ Dh, Dl } = { Dh: u64.rotr32H(Dh, Dl), Dl: u64.rotr32L(Dh, Dl) });\n // v[c] = (v[c] + v[d]) | 0;\n ({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));\n // v[b] = rotr(v[b] ^ v[c], 24)\n ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });\n ({ Bh, Bl } = { Bh: u64.rotrSH(Bh, Bl, 24), Bl: u64.rotrSL(Bh, Bl, 24) });\n ((BBUF[2 * a] = Al), (BBUF[2 * a + 1] = Ah));\n ((BBUF[2 * b] = Bl), (BBUF[2 * b + 1] = Bh));\n ((BBUF[2 * c] = Cl), (BBUF[2 * c + 1] = Ch));\n ((BBUF[2 * d] = Dl), (BBUF[2 * d + 1] = Dh));\n}\n\n// Second half-round of the same LE-limb BLAKE2b G mix; `x` is the message word offset.\nfunction G2b(a: number, b: number, c: number, d: number, msg: TArg<Uint32Array>, x: number) {\n // NOTE: V is LE here\n const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore\n let Al = BBUF[2 * a], Ah = BBUF[2 * a + 1]; // prettier-ignore\n let Bl = BBUF[2 * b], Bh = BBUF[2 * b + 1]; // prettier-ignore\n let Cl = BBUF[2 * c], Ch = BBUF[2 * c + 1]; // prettier-ignore\n let Dl = BBUF[2 * d], Dh = BBUF[2 * d + 1]; // prettier-ignore\n // v[a] = (v[a] + v[b] + x) | 0;\n let ll = u64.add3L(Al, Bl, Xl);\n Ah = u64.add3H(ll, Ah, Bh, Xh);\n Al = ll | 0;\n // v[d] = rotr(v[d] ^ v[a], 16)\n ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });\n ({ Dh, Dl } = { Dh: u64.rotrSH(Dh, Dl, 16), Dl: u64.rotrSL(Dh, Dl, 16) });\n // v[c] = (v[c] + v[d]) | 0;\n ({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));\n // v[b] = rotr(v[b] ^ v[c], 63)\n ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });\n ({ Bh, Bl } = { Bh: u64.rotrBH(Bh, Bl, 63), Bl: u64.rotrBL(Bh, Bl, 63) });\n ((BBUF[2 * a] = Al), (BBUF[2 * a + 1] = Ah));\n ((BBUF[2 * b] = Bl), (BBUF[2 * b + 1] = Bh));\n ((BBUF[2 * c] = Cl), (BBUF[2 * c + 1] = Ch));\n ((BBUF[2 * d] = Dl), (BBUF[2 * d + 1] = Dh));\n}\n\nfunction checkBlake2Opts(\n outputLen: number,\n opts: TArg<Blake2Opts | undefined> = {},\n keyLen: number,\n saltLen: number,\n persLen: number\n) {\n anumber(keyLen);\n // RFC 7693 §2.1 requires digest length nn in 1..keyLen.\n if (outputLen <= 0 || outputLen > keyLen) throw new Error('outputLen bigger than keyLen');\n const { key, salt, personalization } = opts;\n // This API uses `undefined` for the RFC 7693 `kk = 0` case, so a provided key must be non-empty.\n if (key !== undefined && (key.length < 1 || key.length > keyLen))\n throw new Error('\"key\" expected to be undefined or of length=1..' + keyLen);\n if (salt !== undefined) abytes(salt, saltLen, 'salt');\n if (personalization !== undefined) abytes(personalization, persLen, 'personalization');\n}\n\n/** Internal base class for BLAKE2. */\nexport abstract class _BLAKE2<T extends _BLAKE2<T>> implements Hash<T> {\n protected abstract compress(msg: Uint32Array, offset: number, isLast: boolean): void;\n protected abstract get(): number[];\n protected abstract set(...args: number[]): void;\n abstract destroy(): void;\n protected buffer: Uint8Array;\n protected buffer32: Uint32Array;\n protected finished = false;\n protected destroyed = false;\n protected length: number = 0;\n protected pos: number = 0;\n readonly blockLen: number;\n readonly outputLen: number;\n readonly canXOF: boolean = false;\n\n constructor(blockLen: number, outputLen: number) {\n anumber(blockLen);\n anumber(outputLen);\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.buffer = new Uint8Array(blockLen);\n this.buffer32 = u32(this.buffer);\n }\n update(data: TArg<Uint8Array>): this {\n aexists(this);\n abytes(data);\n // Main difference with other hashes: there is flag for last block,\n // so we cannot process current block before we know that there\n // is the next one. This significantly complicates logic and reduces ability\n // to do zero-copy processing\n const { blockLen, buffer, buffer32 } = this;\n const len = data.length;\n const offset = data.byteOffset;\n const buf = data.buffer;\n for (let pos = 0; pos < len; ) {\n // If buffer is full and we still have input (don't process last block, same as blake2s)\n if (this.pos === blockLen) {\n swap32IfBE(buffer32);\n this.compress(buffer32, 0, false);\n swap32IfBE(buffer32);\n this.pos = 0;\n }\n const take = Math.min(blockLen - this.pos, len - pos);\n const dataOffset = offset + pos;\n // Zero-copy only for full, 4-byte-aligned, non-final blocks.\n if (take === blockLen && !(dataOffset % 4) && pos + take < len) {\n const data32 = new Uint32Array(buf, dataOffset, Math.floor((len - pos) / 4));\n swap32IfBE(data32);\n for (let pos32 = 0; pos + blockLen < len; pos32 += buffer32.length, pos += blockLen) {\n this.length += blockLen;\n this.compress(data32, pos32, false);\n }\n swap32IfBE(data32);\n continue;\n }\n buffer.set(data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n this.length += take;\n pos += take;\n }\n return this;\n }\n digestInto(out: TArg<Uint8Array>): void {\n aexists(this);\n aoutput(out, this);\n const { pos, buffer32 } = this;\n this.finished = true;\n // Padding\n clean(this.buffer.subarray(pos));\n swap32IfBE(buffer32);\n this.compress(buffer32, 0, true);\n swap32IfBE(buffer32);\n // Reject unaligned views explicitly instead of hiding them behind a full scratch copy.\n if (out.byteOffset & 3)\n throw new RangeError(\n '\"digestInto() output\" expected 4-byte aligned byteOffset, got ' + out.byteOffset\n );\n const state = this.get();\n const out32 = u32(out);\n const full = Math.floor(this.outputLen / 4);\n for (let i = 0; i < full; i++) out32[i] = swap8IfBE(state[i]);\n const tail = this.outputLen % 4;\n if (!tail) return;\n const off = full * 4;\n const word = state[full];\n for (let i = 0; i < tail; i++) out[off + i] = word >>> (8 * i);\n }\n digest(): TRet<Uint8Array> {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n // Return a copy so callers do not alias the instance scratch buffer used during finalization.\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res as TRet<Uint8Array>;\n }\n _cloneInto(to?: T): T {\n const { buffer, length, finished, destroyed, outputLen, pos } = this;\n // Recreate only `dkLen`; key/salt/personalization are already absorbed into the copied state.\n to ||= new (this.constructor as any)({ dkLen: outputLen }) as T;\n to.set(...this.get());\n to.buffer.set(buffer);\n to.destroyed = destroyed;\n to.finished = finished;\n to.length = length;\n to.pos = pos;\n // @ts-ignore\n to.outputLen = outputLen;\n return to;\n }\n clone(): T {\n return this._cloneInto();\n }\n}\n\n/** Internal blake2b hash class with state stored as LE u32 low/high halves. */\nexport class _BLAKE2b extends _BLAKE2<_BLAKE2b> {\n // Same IV words as SHA-512 / BLAKE2b, encoded as LE u32 low/high halves.\n private v0l = B2B_IV[0] | 0;\n private v0h = B2B_IV[1] | 0;\n private v1l = B2B_IV[2] | 0;\n private v1h = B2B_IV[3] | 0;\n private v2l = B2B_IV[4] | 0;\n private v2h = B2B_IV[5] | 0;\n private v3l = B2B_IV[6] | 0;\n private v3h = B2B_IV[7] | 0;\n private v4l = B2B_IV[8] | 0;\n private v4h = B2B_IV[9] | 0;\n private v5l = B2B_IV[10] | 0;\n private v5h = B2B_IV[11] | 0;\n private v6l = B2B_IV[12] | 0;\n private v6h = B2B_IV[13] | 0;\n private v7l = B2B_IV[14] | 0;\n private v7h = B2B_IV[15] | 0;\n\n constructor(opts: Blake2Opts = {}) {\n const olen = opts.dkLen === undefined ? 64 : opts.dkLen;\n super(128, olen);\n checkBlake2Opts(olen, opts, 64, 16, 16);\n let { key, personalization, salt } = opts;\n let keyLength = 0;\n if (key !== undefined) {\n abytes(key, undefined, 'key');\n keyLength = key.length;\n }\n // RFC 7693 §2.5: xor `p[0] = 0x0101kknn` into the low 32 bits of `h[0]`;\n // the high 32 bits stay at `IV[0]`.\n this.v0l ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24);\n if (salt !== undefined) {\n abytes(salt, undefined, 'salt');\n const slt = u32(salt);\n this.v4l ^= swap8IfBE(slt[0]);\n this.v4h ^= swap8IfBE(slt[1]);\n this.v5l ^= swap8IfBE(slt[2]);\n this.v5h ^= swap8IfBE(slt[3]);\n }\n if (personalization !== undefined) {\n abytes(personalization, undefined, 'personalization');\n const pers = u32(personalization);\n this.v6l ^= swap8IfBE(pers[0]);\n this.v6h ^= swap8IfBE(pers[1]);\n this.v7l ^= swap8IfBE(pers[2]);\n this.v7h ^= swap8IfBE(pers[3]);\n }\n if (key !== undefined) {\n // Pad to blockLen and update\n const tmp = new Uint8Array(this.blockLen);\n tmp.set(key);\n this.update(tmp);\n }\n }\n // prettier-ignore\n protected get(): [\n number, number, number, number, number, number, number, number,\n number, number, number, number, number, number, number, number\n ] {\n let { v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h } = this;\n return [v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h];\n }\n // prettier-ignore\n protected set(\n v0l: number, v0h: number, v1l: number, v1h: number,\n v2l: number, v2h: number, v3l: number, v3h: number,\n v4l: number, v4h: number, v5l: number, v5h: number,\n v6l: number, v6h: number, v7l: number, v7h: number\n ): void {\n this.v0l = v0l | 0;\n this.v0h = v0h | 0;\n this.v1l = v1l | 0;\n this.v1h = v1h | 0;\n this.v2l = v2l | 0;\n this.v2h = v2h | 0;\n this.v3l = v3l | 0;\n this.v3h = v3h | 0;\n this.v4l = v4l | 0;\n this.v4h = v4h | 0;\n this.v5l = v5l | 0;\n this.v5h = v5h | 0;\n this.v6l = v6l | 0;\n this.v6h = v6h | 0;\n this.v7l = v7l | 0;\n this.v7h = v7h | 0;\n }\n protected compress(msg: Uint32Array, offset: number, isLast: boolean): void {\n this.get().forEach((v, i) => (BBUF[i] = v)); // First half from state.\n BBUF.set(B2B_IV, 16); // Second half from IV.\n let { h, l } = u64.fromBig(BigInt(this.length));\n BBUF[24] = B2B_IV[8] ^ l; // Low word of the offset.\n BBUF[25] = B2B_IV[9] ^ h; // High word.\n // Invert all bits for last block\n if (isLast) {\n BBUF[28] = ~BBUF[28];\n BBUF[29] = ~BBUF[29];\n }\n let j = 0;\n const s = BSIGMA;\n // SIGMA selects 64-bit message words; multiply by 2 because `msg` stores\n // each word as [low32, high32].\n for (let i = 0; i < 12; i++) {\n G1b(0, 4, 8, 12, msg, offset + 2 * s[j++]);\n G2b(0, 4, 8, 12, msg, offset + 2 * s[j++]);\n G1b(1, 5, 9, 13, msg, offset + 2 * s[j++]);\n G2b(1, 5, 9, 13, msg, offset + 2 * s[j++]);\n G1b(2, 6, 10, 14, msg, offset + 2 * s[j++]);\n G2b(2, 6, 10, 14, msg, offset + 2 * s[j++]);\n G1b(3, 7, 11, 15, msg, offset + 2 * s[j++]);\n G2b(3, 7, 11, 15, msg, offset + 2 * s[j++]);\n\n G1b(0, 5, 10, 15, msg, offset + 2 * s[j++]);\n G2b(0, 5, 10, 15, msg, offset + 2 * s[j++]);\n G1b(1, 6, 11, 12, msg, offset + 2 * s[j++]);\n G2b(1, 6, 11, 12, msg, offset + 2 * s[j++]);\n G1b(2, 7, 8, 13, msg, offset + 2 * s[j++]);\n G2b(2, 7, 8, 13, msg, offset + 2 * s[j++]);\n G1b(3, 4, 9, 14, msg, offset + 2 * s[j++]);\n G2b(3, 4, 9, 14, msg, offset + 2 * s[j++]);\n }\n this.v0l ^= BBUF[0] ^ BBUF[16];\n this.v0h ^= BBUF[1] ^ BBUF[17];\n this.v1l ^= BBUF[2] ^ BBUF[18];\n this.v1h ^= BBUF[3] ^ BBUF[19];\n this.v2l ^= BBUF[4] ^ BBUF[20];\n this.v2h ^= BBUF[5] ^ BBUF[21];\n this.v3l ^= BBUF[6] ^ BBUF[22];\n this.v3h ^= BBUF[7] ^ BBUF[23];\n this.v4l ^= BBUF[8] ^ BBUF[24];\n this.v4h ^= BBUF[9] ^ BBUF[25];\n this.v5l ^= BBUF[10] ^ BBUF[26];\n this.v5h ^= BBUF[11] ^ BBUF[27];\n this.v6l ^= BBUF[12] ^ BBUF[28];\n this.v6h ^= BBUF[13] ^ BBUF[29];\n this.v7l ^= BBUF[14] ^ BBUF[30];\n this.v7h ^= BBUF[15] ^ BBUF[31];\n clean(BBUF);\n }\n destroy(): void {\n this.destroyed = true;\n clean(this.buffer32);\n this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n }\n}\n\n/**\n * Blake2b hash function. 64-bit. 1.5x slower than blake2s in JS.\n * @param msg - message that would be hashed\n * @param opts - Optional output, MAC, salt, and personalization settings.\n * `dkLen` must be 1..64 bytes; `salt` and `personalization`, if present,\n * must be 16 bytes each. See {@link Blake2Opts}.\n * @returns Digest bytes.\n * @example\n * Hash a message with Blake2b.\n * ```ts\n * blake2b(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const blake2b: TRet<CHash<_BLAKE2b, Blake2Opts>> = /* @__PURE__ */ createHasher(\n (opts) => new _BLAKE2b(opts)\n);\n\n// =================\n// Blake2S\n// =================\n\n/** Internal type, 16 numbers. */\n// prettier-ignore\nexport type _Num16 = {\n v0: number; v1: number; v2: number; v3: number;\n v4: number; v5: number; v6: number; v7: number;\n v8: number; v9: number; v10: number; v11: number;\n v12: number; v13: number; v14: number; v15: number;\n};\n\n/**\n * BLAKE2-compress core method.\n * Runs only the round function over a caller-supplied local vector; callers initialize `v0..v15`\n * and apply the final `h[i] ^= v[i] ^ v[i + 8]` fold themselves.\n * @param s - flattened sigma schedule bytes\n * @param offset - starting word offset inside `msg`, not a byte offset\n * @param msg - message words\n * @param rounds - round count to execute\n * @param v0 - state word 0\n * @param v1 - state word 1\n * @param v2 - state word 2\n * @param v3 - state word 3\n * @param v4 - state word 4\n * @param v5 - state word 5\n * @param v6 - state word 6\n * @param v7 - state word 7\n * @param v8 - state word 8\n * @param v9 - state word 9\n * @param v10 - state word 10\n * @param v11 - state word 11\n * @param v12 - state word 12\n * @param v13 - state word 13\n * @param v14 - state word 14\n * @param v15 - state word 15\n * @returns Updated compression state words.\n * @example\n * Run the BLAKE2 compression core on zeroed state and message words.\n * ```ts\n * import { compress } from '@noble/hashes/blake2.js';\n * const state = compress(\n * new Uint8Array(16),\n * 0,\n * new Uint32Array(16),\n * 1,\n * 0, 0, 0, 0, 0, 0, 0, 0,\n * 0, 0, 0, 0, 0, 0, 0, 0\n * );\n * state.v0;\n * ```\n */\n// prettier-ignore\nexport function compress(s: TArg<Uint8Array>, offset: number, msg: TArg<Uint32Array>, rounds: number,\n v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number,\n v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number,\n): _Num16 {\n let j = 0;\n for (let i = 0; i < rounds; i++) {\n ({ a: v0, b: v4, c: v8, d: v12 } = G1s(v0, v4, v8, v12, msg[offset + s[j++]]));\n ({ a: v0, b: v4, c: v8, d: v12 } = G2s(v0, v4, v8, v12, msg[offset + s[j++]]));\n ({ a: v1, b: v5, c: v9, d: v13 } = G1s(v1, v5, v9, v13, msg[offset + s[j++]]));\n ({ a: v1, b: v5, c: v9, d: v13 } = G2s(v1, v5, v9, v13, msg[offset + s[j++]]));\n ({ a: v2, b: v6, c: v10, d: v14 } = G1s(v2, v6, v10, v14, msg[offset + s[j++]]));\n ({ a: v2, b: v6, c: v10, d: v14 } = G2s(v2, v6, v10, v14, msg[offset + s[j++]]));\n ({ a: v3, b: v7, c: v11, d: v15 } = G1s(v3, v7, v11, v15, msg[offset + s[j++]]));\n ({ a: v3, b: v7, c: v11, d: v15 } = G2s(v3, v7, v11, v15, msg[offset + s[j++]]));\n\n ({ a: v0, b: v5, c: v10, d: v15 } = G1s(v0, v5, v10, v15, msg[offset + s[j++]]));\n ({ a: v0, b: v5, c: v10, d: v15 } = G2s(v0, v5, v10, v15, msg[offset + s[j++]]));\n ({ a: v1, b: v6, c: v11, d: v12 } = G1s(v1, v6, v11, v12, msg[offset + s[j++]]));\n ({ a: v1, b: v6, c: v11, d: v12 } = G2s(v1, v6, v11, v12, msg[offset + s[j++]]));\n ({ a: v2, b: v7, c: v8, d: v13 } = G1s(v2, v7, v8, v13, msg[offset + s[j++]]));\n ({ a: v2, b: v7, c: v8, d: v13 } = G2s(v2, v7, v8, v13, msg[offset + s[j++]]));\n ({ a: v3, b: v4, c: v9, d: v14 } = G1s(v3, v4, v9, v14, msg[offset + s[j++]]));\n ({ a: v3, b: v4, c: v9, d: v14 } = G2s(v3, v4, v9, v14, msg[offset + s[j++]]));\n }\n return { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 };\n}\n\n// Blake2s reuses the SHA-256 IV words as-is.\nconst B2S_IV = /* @__PURE__ */ SHA256_IV.slice();\n\n/** Internal blake2s hash class. */\nexport class _BLAKE2s extends _BLAKE2<_BLAKE2s> {\n // Internal state, same as SHA-256\n private v0 = B2S_IV[0] | 0;\n private v1 = B2S_IV[1] | 0;\n private v2 = B2S_IV[2] | 0;\n private v3 = B2S_IV[3] | 0;\n private v4 = B2S_IV[4] | 0;\n private v5 = B2S_IV[5] | 0;\n private v6 = B2S_IV[6] | 0;\n private v7 = B2S_IV[7] | 0;\n\n constructor(opts: Blake2Opts = {}) {\n const olen = opts.dkLen === undefined ? 32 : opts.dkLen;\n super(64, olen);\n checkBlake2Opts(olen, opts, 32, 8, 8);\n let { key, personalization, salt } = opts;\n let keyLength = 0;\n if (key !== undefined) {\n abytes(key, undefined, 'key');\n keyLength = key.length;\n }\n // RFC 7693 §2.5: xor `p[0] = 0x0101kknn` directly into `h[0]`, since\n // BLAKE2s stores each state word as one `u32`.\n this.v0 ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24);\n if (salt !== undefined) {\n abytes(salt, undefined, 'salt');\n const slt = u32(salt as Uint8Array);\n this.v4 ^= swap8IfBE(slt[0]);\n this.v5 ^= swap8IfBE(slt[1]);\n }\n if (personalization !== undefined) {\n abytes(personalization, undefined, 'personalization');\n const pers = u32(personalization as Uint8Array);\n this.v6 ^= swap8IfBE(pers[0]);\n this.v7 ^= swap8IfBE(pers[1]);\n }\n if (key !== undefined) {\n // Pad to blockLen and update\n const tmp = new Uint8Array(this.blockLen);\n tmp.set(key);\n this.update(tmp);\n }\n }\n protected get(): [number, number, number, number, number, number, number, number] {\n const { v0, v1, v2, v3, v4, v5, v6, v7 } = this;\n return [v0, v1, v2, v3, v4, v5, v6, v7];\n }\n // prettier-ignore\n protected set(\n v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number\n ): void {\n this.v0 = v0 | 0;\n this.v1 = v1 | 0;\n this.v2 = v2 | 0;\n this.v3 = v3 | 0;\n this.v4 = v4 | 0;\n this.v5 = v5 | 0;\n this.v6 = v6 | 0;\n this.v7 = v7 | 0;\n }\n protected compress(msg: Uint32Array, offset: number, isLast: boolean): void {\n const { h, l } = u64.fromBig(BigInt(this.length));\n // Seed v8..v15 from the IV, xor the low/high 32-bit byte counter into\n // v12/v13, and invert v14 on the final block.\n // prettier-ignore\n const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =\n compress(\n BSIGMA, offset, msg, 10,\n this.v0, this.v1, this.v2, this.v3, this.v4, this.v5, this.v6, this.v7,\n B2S_IV[0], B2S_IV[1], B2S_IV[2], B2S_IV[3], l ^ B2S_IV[4], h ^ B2S_IV[5], isLast ? ~B2S_IV[6] : B2S_IV[6], B2S_IV[7]\n );\n this.v0 ^= v0 ^ v8;\n this.v1 ^= v1 ^ v9;\n this.v2 ^= v2 ^ v10;\n this.v3 ^= v3 ^ v11;\n this.v4 ^= v4 ^ v12;\n this.v5 ^= v5 ^ v13;\n this.v6 ^= v6 ^ v14;\n this.v7 ^= v7 ^ v15;\n }\n destroy(): void {\n this.destroyed = true;\n clean(this.buffer32);\n this.set(0, 0, 0, 0, 0, 0, 0, 0);\n }\n}\n\n/**\n * Blake2s hash function. Focuses on 8-bit to 32-bit platforms. 1.5x faster than blake2b in JS.\n * @param msg - message that would be hashed\n * @param opts - Optional output, MAC, salt, and personalization settings.\n * `dkLen` must be 1..32 bytes; `salt` and `personalization`, if present,\n * must be 8 bytes each. See {@link Blake2Opts}.\n * @returns Digest bytes.\n * @example\n * Hash a message with Blake2s.\n * ```ts\n * blake2s(new Uint8Array([97, 98, 99]));\n * ```\n */\nexport const blake2s: TRet<CHash<_BLAKE2s, Blake2Opts>> = /* @__PURE__ */ createHasher(\n (opts) => new _BLAKE2s(opts)\n);\n","/**\n * Blake3 fast hash is Blake2 with reduced security (round count). Can also be used as MAC & KDF.\n *\n * It is advertised as \"the fastest cryptographic hash\". However, it isn't true in JS.\n * Why is this so slow? While it must be 6x faster than blake2b, perf diff is only 20%:\n *\n * * There is only 30% reduction in number of rounds from blake2s\n * * Speed-up comes from tree structure, which is parallelized using SIMD & threading.\n * These features are not present in JS, so we only get overhead from trees.\n * * Parallelization only happens on 1024-byte chunks: there is no benefit for small inputs.\n * * It is still possible to make it faster using: a) loop unrolling b) web workers c) wasm\n * @module\n */\nimport { SHA256_IV } from './_md.ts';\nimport { fromBig } from './_u64.ts';\nimport { _BLAKE2, compress } from './blake2.ts';\n// prettier-ignore\nimport {\n abytes, aexists, anumber, aoutput,\n clean,\n copyBytes,\n createHasher, swap32IfBE,\n u32, u8,\n type CHashXOF,\n type HashXOF,\n type TArg,\n type TRet\n} from './utils.ts';\n\n// Constructor-time mode flags (`KEYED_HASH`, `DERIVE_*`) plus per-node tree\n// flags (`CHUNK_*`, `PARENT`, `ROOT`).\nconst B3_Flags = {\n CHUNK_START: 0b1,\n CHUNK_END: 0b10,\n PARENT: 0b100,\n ROOT: 0b1000,\n KEYED_HASH: 0b10000,\n DERIVE_KEY_CONTEXT: 0b100000,\n DERIVE_KEY_MATERIAL: 0b1000000,\n} as const;\n\n// Default BLAKE3 IV, cloned from the shared BLAKE2s / SHA-256 IV basis.\nconst B3_IV = /* @__PURE__ */ SHA256_IV.slice();\n\n// Seven 16-word rounds of BLAKE3 message schedule, generated by repeatedly\n// permuting the identity row.\nconst B3_SIGMA: TRet<Uint8Array> = /* @__PURE__ */ (() => {\n const Id = Array.from({ length: 16 }, (_, i) => i);\n const permute = (arr: number[]) =>\n [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8].map((i) => arr[i]);\n const res: number[] = [];\n for (let i = 0, v = Id; i < 7; i++, v = permute(v)) res.push(...v);\n return Uint8Array.from(res);\n})();\n\n/**\n * Ensure to use EITHER `key` OR `context`, not both.\n *\n * * `key`: 32-byte MAC key.\n * * `context`: caller-encoded KDF context bytes. If your protocol defines a\n * string context, encode it before passing it here.\n * A good default format for the original context string is\n * \"[application] [commit timestamp] [purpose]\".\n */\nexport type Blake3Opts = {\n /** Desired digest length in bytes. The BLAKE3 spec allows 0..2^64-1 bytes of output. */\n dkLen?: number;\n /** Optional 32-byte MAC key. */\n key?: Uint8Array;\n /** Optional KDF context bytes. */\n context?: Uint8Array;\n};\n\n/** Blake3 hash. Can be used as MAC and KDF with caller-encoded context bytes. */\nexport class _BLAKE3 extends _BLAKE2<_BLAKE3> implements HashXOF<_BLAKE3> {\n readonly canXOF = true;\n private chunkPos = 0; // Position of current block in chunk\n // How many chunks we already have; exact while this stays within\n // JS's safe-integer range.\n private chunksDone = 0;\n private flags = 0 | 0;\n private IV: Uint32Array;\n private state: Uint32Array;\n private stack: Uint32Array[] = [];\n // Output\n private posOut = 0;\n private bufferOut32 = new Uint32Array(16);\n private bufferOut: Uint8Array;\n // Index of output chunk; exact while this stays within JS's\n // safe-integer range.\n private chunkOut = 0;\n private enableXOF = true;\n\n constructor(opts: Blake3Opts = {}, flags = 0) {\n super(64, opts.dkLen === undefined ? 32 : opts.dkLen);\n const { key, context } = opts;\n const hasContext = context !== undefined;\n if (key !== undefined) {\n if (hasContext) throw new Error('Only \"key\" or \"context\" can be specified at same time');\n abytes(key, 32, 'key');\n const k = copyBytes(key);\n this.IV = u32(k);\n swap32IfBE(this.IV);\n this.flags = flags | B3_Flags.KEYED_HASH;\n } else if (hasContext) {\n abytes(context, undefined, 'context');\n const ctx = context;\n const contextKey = new _BLAKE3({ dkLen: 32 }, B3_Flags.DERIVE_KEY_CONTEXT)\n .update(ctx)\n .digest();\n this.IV = u32(contextKey);\n swap32IfBE(this.IV);\n this.flags = flags | B3_Flags.DERIVE_KEY_MATERIAL;\n } else {\n this.IV = B3_IV.slice();\n this.flags = flags;\n }\n this.state = this.IV.slice();\n this.bufferOut = u8(this.bufferOut32);\n }\n // _BLAKE2's scalar-state hooks are unused here: BLAKE3 keeps its tree/XOF state in arrays and\n // copies it directly in _cloneInto().\n protected get(): [] {\n return [];\n }\n protected set(): void {}\n // Truncated chunk/parent compression: seed v8..v15 as IV[0..3], t0, t1,\n // block length, and flags, then keep only the first 8 output words.\n private b2Compress(counter: number, flags: number, buf: Uint32Array, bufPos: number = 0) {\n const { state: s, pos } = this;\n const { h, l } = fromBig(BigInt(counter), true);\n // prettier-ignore\n const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =\n compress(\n B3_SIGMA, bufPos, buf, 7,\n s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7],\n B3_IV[0], B3_IV[1], B3_IV[2], B3_IV[3], h, l, pos, flags\n );\n s[0] = v0 ^ v8;\n s[1] = v1 ^ v9;\n s[2] = v2 ^ v10;\n s[3] = v3 ^ v11;\n s[4] = v4 ^ v12;\n s[5] = v5 ^ v13;\n s[6] = v6 ^ v14;\n s[7] = v7 ^ v15;\n }\n protected compress(buf: Uint32Array, bufPos: number = 0, isLast: boolean = false): void {\n // Compress last block\n let flags = this.flags;\n if (!this.chunkPos) flags |= B3_Flags.CHUNK_START;\n if (this.chunkPos === 15 || isLast) flags |= B3_Flags.CHUNK_END;\n if (!isLast) this.pos = this.blockLen;\n this.b2Compress(this.chunksDone, flags, buf, bufPos);\n this.chunkPos += 1;\n // If current block is last in chunk (16 blocks), then compress chunks\n if (this.chunkPos === 16 || isLast) {\n let chunk = this.state;\n this.state = this.IV.slice();\n // If not the last one, compress only when there are trailing zeros in chunk counter\n // Chunks are used as a binary tree where the current stack is the path.\n // Zero means the current leaf is finished and can be compressed.\n // 1 (001) - leaf not finished (just push current chunk to stack)\n // 2 (010) - leaf finished at depth=1 (merge with last elm on stack and push back)\n // 3 (011) - last leaf not finished\n // 4 (100) - leafs finished at depth=1 and depth=2\n for (let last, chunks = this.chunksDone + 1; isLast || !(chunks & 1); chunks >>= 1) {\n if (!(last = this.stack.pop())) break;\n this.buffer32.set(last, 0);\n this.buffer32.set(chunk, 8);\n this.pos = this.blockLen;\n this.b2Compress(0, this.flags | B3_Flags.PARENT, this.buffer32, 0);\n chunk = this.state;\n this.state = this.IV.slice();\n }\n this.chunksDone++;\n this.chunkPos = 0;\n this.stack.push(chunk);\n }\n this.pos = 0;\n }\n _cloneInto(to?: _BLAKE3): _BLAKE3 {\n to = super._cloneInto(to) as _BLAKE3;\n const { IV, flags, state, chunkPos, posOut, chunkOut, stack, chunksDone } = this;\n to.state.set(state.slice());\n // Clone each CV stack entry by value so extending or destroying the clone\n // cannot alias the source tree state.\n to.stack = stack.map((i) => Uint32Array.from(i));\n to.IV.set(IV);\n to.flags = flags;\n to.chunkPos = chunkPos;\n to.chunksDone = chunksDone;\n to.posOut = posOut;\n to.chunkOut = chunkOut;\n to.enableXOF = this.enableXOF;\n to.bufferOut32.set(this.bufferOut32);\n return to;\n }\n destroy(): void {\n this.destroyed = true;\n clean(this.state, this.buffer32, this.IV, this.bufferOut32);\n clean(...this.stack);\n }\n // Root/XOF compression: rerun the same ROOT inputs with incrementing output\n // counter `t` and materialize all 16 output words.\n // Same as b2Compress, but doesn't modify state and returns 16 u32 array (instead of 8)\n private b2CompressOut() {\n const { state: s, pos, flags, buffer32, bufferOut32: out32 } = this;\n const { h, l } = fromBig(BigInt(this.chunkOut++));\n swap32IfBE(buffer32);\n // prettier-ignore\n const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =\n compress(\n B3_SIGMA, 0, buffer32, 7,\n s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7],\n B3_IV[0], B3_IV[1], B3_IV[2], B3_IV[3], l, h, pos, flags\n );\n out32[0] = v0 ^ v8;\n out32[1] = v1 ^ v9;\n out32[2] = v2 ^ v10;\n out32[3] = v3 ^ v11;\n out32[4] = v4 ^ v12;\n out32[5] = v5 ^ v13;\n out32[6] = v6 ^ v14;\n out32[7] = v7 ^ v15;\n out32[8] = s[0] ^ v8;\n out32[9] = s[1] ^ v9;\n out32[10] = s[2] ^ v10;\n out32[11] = s[3] ^ v11;\n out32[12] = s[4] ^ v12;\n out32[13] = s[5] ^ v13;\n out32[14] = s[6] ^ v14;\n out32[15] = s[7] ^ v15;\n swap32IfBE(buffer32);\n swap32IfBE(out32);\n this.posOut = 0;\n }\n protected finish(): void {\n if (this.finished) return;\n this.finished = true;\n // Padding\n clean(this.buffer.subarray(this.pos));\n // Process last chunk\n let flags = this.flags | B3_Flags.ROOT;\n if (this.stack.length) {\n // Finalize the current chunk first, then rerun the last parent\n // compression as ROOT with t = 0 and b = 64.\n flags |= B3_Flags.PARENT;\n swap32IfBE(this.buffer32);\n this.compress(this.buffer32, 0, true);\n swap32IfBE(this.buffer32);\n this.chunksDone = 0;\n this.pos = this.blockLen;\n } else {\n flags |= (!this.chunkPos ? B3_Flags.CHUNK_START : 0) | B3_Flags.CHUNK_END;\n }\n this.flags = flags;\n this.b2CompressOut();\n }\n private writeInto(out: TArg<Uint8Array>): TRet<Uint8Array> {\n aexists(this, false);\n abytes(out);\n this.finish();\n const { blockLen, bufferOut } = this;\n for (let pos = 0, len = out.length; pos < len; ) {\n if (this.posOut >= blockLen) this.b2CompressOut();\n const take = Math.min(blockLen - this.posOut, len - pos);\n out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);\n this.posOut += take;\n pos += take;\n }\n return out as TRet<Uint8Array>;\n }\n xofInto(out: TArg<Uint8Array>): TRet<Uint8Array> {\n if (!this.enableXOF) throw new Error('XOF is not possible after digest call');\n return this.writeInto(out);\n }\n xof(bytes: number): TRet<Uint8Array> {\n anumber(bytes);\n return this.xofInto(new Uint8Array(bytes));\n }\n digestInto(out: TArg<Uint8Array>): void {\n aoutput(out, this);\n if (this.finished) throw new Error('digest() was already called');\n this.enableXOF = false;\n // `aoutput(...)` allows oversized buffers; digestInto() must fill only the configured digest.\n this.writeInto(out.subarray(0, this.outputLen));\n this.destroy();\n }\n digest(): TRet<Uint8Array> {\n const out = new Uint8Array(this.outputLen);\n this.digestInto(out);\n return out as TRet<Uint8Array>;\n }\n}\n\n/**\n * BLAKE3 hash function. Can be used as MAC and KDF.\n * @param msg - message that would be hashed\n * @param opts - Optional output, MAC, or KDF configuration. `key` must be\n * exactly 32 bytes, `context` is caller-encoded bytes, and `dkLen` can be\n * 0..2^64-1 via the XOF-backed output path. See {@link Blake3Opts}.\n * @returns Digest bytes.\n * @example\n * Hash, MAC, or derive key material with BLAKE3.\n * ```ts\n * import { blake3 } from '@noble/hashes/blake3.js';\n * import { utf8ToBytes } from '@noble/hashes/utils.js';\n * const data = new Uint8Array(32);\n * const hash = blake3(data);\n * const mac = blake3(data, { key: new Uint8Array(32) });\n * const kdf = blake3(data, { context: utf8ToBytes('application name') });\n * ```\n */\nexport const blake3: TRet<CHashXOF<_BLAKE3, Blake3Opts>> = /* @__PURE__ */ createHasher(\n (opts = {}) => new _BLAKE3(opts)\n);\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CANON-001: Canonical JSON encoder is hand-rolled, not JSON.stringify.\n// Status: decided (WI-002)\n// Rationale: JSON.stringify does not satisfy the canonicalization rules required for\n// stable content-addressing: (1) it does not sort object keys, (2) it emits numbers\n// in implementation-defined form that may include scientific notation or platform-\n// specific rounding, (3) it does not enforce no-trailing-zeros or signed-zero collapse.\n// A 90-line encoder written from first principles is the only way to guarantee\n// byte-identical output on every JS runtime.\n\nimport type { ContractSpec, NonFunctionalProperties } from \"./index.js\";\n\n// ---------------------------------------------------------------------------\n// QueryIntentCard — LLM-facing query surface (D2 ADR, DEC-V3-DISCOVERY-D2-001)\n// ---------------------------------------------------------------------------\n\n// @decision DEC-V3-IMPL-QUERY-001\n// title: Symmetric query-text derivation via canonicalizeQueryText\n// status: accepted\n// rationale: canonicalizeQueryText(card: QueryIntentCard): string projects the\n// query into a SpecYak-shaped canonical JSON text so that query and document\n// vectors occupy the same semantic space. Each provided dimension field maps\n// to the corresponding SpecYak optional field (behavior, guarantees, etc.).\n// Absent fields are omitted (same semantics as D1's absent-dimension zero-vector\n// rule). The function uses the same hand-rolled canonical encoder so key ordering\n// and number serialization are identical to storeBlock's embedding text.\n// Canonical site for the 1 - L²/4 formula re-stated in storage.ts (DEC-V3-IMPL-QUERY-007).\n// References: docs/archive/developer/adr/discovery-query-language.md §Q1, docs/archive/developer/adr/discovery-ranking.md §Q3.\n\n/**\n * A query parameter with optional name (at query time the LLM may not know\n * argument names; only the type is required). Used in QueryIntentCard.signature.\n */\nexport interface QueryTypeSignatureParam {\n /** Optional argument name. If absent, a positional sentinel is generated. */\n readonly name?: string | undefined;\n /** Required type string, e.g. \"number\", \"string[]\". */\n readonly type: string;\n}\n\n/**\n * The LLM-facing query surface for multi-dimensional vector search.\n *\n * Each field corresponds to one semantic dimension of a stored SpecYak.\n * Omitting a field skips that dimension at scoring time (D1 absent-dimension rule).\n *\n * @decision DEC-V3-DISCOVERY-D2-001 — Query schema. QueryIntentCard is intentionally\n * smaller than SpecYak: no id/hash/strictness/proof fields; all dimension fields\n * are optional; freeform description strings replace structured array types.\n * Reference: docs/archive/developer/adr/discovery-query-language.md §Q1.\n */\nexport interface QueryIntentCard {\n // Dimension fields — each is optional; omitting skips that dimension\n /** Behavior dimension: maps to SpecYak.behavior. */\n readonly behavior?: string | undefined;\n /** Guarantees dimension: freeform descriptions (no id required at query time). */\n readonly guarantees?: readonly string[] | undefined;\n /** Error-conditions dimension: freeform descriptions. */\n readonly errorConditions?: readonly string[] | undefined;\n /**\n * Non-functional dimension: any subset of NonFunctionalProperties.\n * purity and threadSafety are NOT required at query time (D2 §Q1 deviation).\n */\n readonly nonFunctional?: Partial<NonFunctionalProperties> | undefined;\n /** Property-tests dimension: freeform descriptions. */\n readonly propertyTests?: readonly string[] | undefined;\n /**\n * Structural-match dimension (not embedded; used by D3 Stage 2).\n * Maps to SpecYak inputs/outputs for structuralMatch().\n */\n readonly signature?:\n | {\n readonly inputs?: readonly QueryTypeSignatureParam[] | undefined;\n readonly outputs?: readonly QueryTypeSignatureParam[] | undefined;\n }\n | undefined;\n\n // Retrieval controls\n /**\n * Per-dimension relative weights for combinedScore computation (D3 §Q1).\n * Keys mirror SpecYak field names (no embedding_ prefix).\n * Omitted dimensions use default weight 1.0.\n */\n readonly weights?:\n | {\n readonly behavior?: number | undefined;\n readonly guarantees?: number | undefined;\n readonly errorConditions?: number | undefined;\n readonly nonFunctional?: number | undefined;\n readonly propertyTests?: number | undefined;\n }\n | undefined;\n /** Maximum number of candidates to return. Default: 10. */\n readonly topK?: number | undefined;\n /** Minimum combinedScore threshold; candidates below this are excluded. */\n readonly minScore?: number | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Internal JSON value type (closed over ContractSpec shape)\n// ---------------------------------------------------------------------------\n\ntype JsonPrimitive = string | number | boolean | null;\ntype JsonObject = { readonly [key: string]: JsonValue };\ntype JsonArray = readonly JsonValue[];\ntype JsonValue = JsonPrimitive | JsonObject | JsonArray;\n\n// ---------------------------------------------------------------------------\n// Encoder\n// ---------------------------------------------------------------------------\n\n/**\n * Encode a JSON value to its canonical string representation.\n *\n * Rules:\n * - Object keys sorted lexicographically (Unicode code-point order), depth-first.\n * - Arrays: element order preserved.\n * - Numbers: integer form when value is a finite integer, else fixed-form decimal\n * with no trailing zeros. NaN and Infinity throw — they are not valid contract\n * spec values and must not silently produce `null` (which JSON.stringify does).\n * - Strings: RFC 8259 JSON string encoding. Escape only the characters that MUST\n * be escaped per RFC (control chars U+0000–U+001F, U+0022 `\"`, U+005C `\\`).\n * No over-escaping of forward slashes or non-ASCII code points.\n * - Boolean / null: standard JSON literals.\n * - undefined: must not appear (optional fields absent from objects, not null).\n */\nfunction encodeValue(value: JsonValue): string {\n if (value === null) return \"null\";\n if (typeof value === \"boolean\") return value ? \"true\" : \"false\";\n if (typeof value === \"number\") return encodeNumber(value);\n if (typeof value === \"string\") return encodeString(value);\n if (Array.isArray(value)) {\n const arr = value as JsonArray;\n if (arr.length === 0) return \"[]\";\n return `[${arr.map(encodeValue).join(\",\")}]`;\n }\n // Object\n const obj = value as JsonObject;\n const keys = Object.keys(obj).sort();\n if (keys.length === 0) return \"{}\";\n const pairs = keys\n .filter((k) => (obj[k] as JsonValue | undefined) !== undefined)\n .map((k) => `${encodeString(k)}:${encodeValue(obj[k] as JsonValue)}`);\n if (pairs.length === 0) return \"{}\";\n return `{${pairs.join(\",\")}}`;\n}\n\n/**\n * Encode a number to its canonical JSON form.\n *\n * - NaN and ±Infinity are invalid and throw.\n * - Signed zero (-0) is collapsed to 0.\n * - Integer values use integer form (no decimal point).\n * - Non-integer values: JSON.stringify is canonical for the integer and\n * fixed-decimal range we currently encode. We throw on scientific notation\n * as a tripwire for future schema additions — if a ContractSpec field ever\n * holds a value in the magnitude range that triggers scientific notation\n * (e.g. 5e-7), this guard fires loudly rather than silently producing\n * non-deterministic output. Add a fixed-decimal renderer (e.g. Ryu) before\n * extending ContractSpec with values in such a magnitude range.\n */\nfunction encodeNumber(n: number): string {\n if (!Number.isFinite(n)) {\n throw new TypeError(\n `canonicalize: non-finite number ${String(n)} is not a valid ContractSpec value`,\n );\n }\n // Collapse -0 to 0\n if (Object.is(n, -0)) return \"0\";\n // Integer form: avoid decimal point for whole numbers. Use String() which is\n // canonical for integers within safe-integer range. For very large integers\n // (≥1e21) String() emits scientific notation — guard against that too.\n if (Number.isInteger(n)) {\n const s = String(n);\n if (s.includes(\"e\") || s.includes(\"E\")) {\n throw new TypeError(\n `Non-canonical numeric encoding for ${n}: produced scientific notation \"${s}\". Canonical encoding requires fixed-decimal form. Add a fixed-decimal renderer (e.g. Ryu) before extending ContractSpec with values in this magnitude range.`,\n );\n }\n return s;\n }\n // Non-integer: JSON.stringify produces the shortest round-trip decimal on all\n // V8/SpiderMonkey/JSC runtimes for ordinary finite non-integers. However, for\n // values with very small or very large magnitude it emits scientific notation\n // (e.g. 5e-7), which is not canonical. Throw immediately so the caller knows\n // a fixed-decimal renderer is required before adding such values to the schema.\n const s = JSON.stringify(n);\n if (s.includes(\"e\") || s.includes(\"E\")) {\n throw new TypeError(\n `Non-canonical numeric encoding for ${n}: produced scientific notation \"${s}\". Canonical encoding requires fixed-decimal form. Add a fixed-decimal renderer (e.g. Ryu) before extending ContractSpec with values in this magnitude range.`,\n );\n }\n return s;\n}\n\n/** RFC-8259 character escape table. */\nconst ESCAPE: Record<number, string> = {\n 0: \"\\\\u0000\",\n 1: \"\\\\u0001\",\n 2: \"\\\\u0002\",\n 3: \"\\\\u0003\",\n 4: \"\\\\u0004\",\n 5: \"\\\\u0005\",\n 6: \"\\\\u0006\",\n 7: \"\\\\u0007\",\n 8: \"\\\\b\",\n 9: \"\\\\t\",\n 10: \"\\\\n\",\n 11: \"\\\\u000b\",\n 12: \"\\\\f\",\n 13: \"\\\\r\",\n 14: \"\\\\u000e\",\n 15: \"\\\\u000f\",\n 16: \"\\\\u0010\",\n 17: \"\\\\u0011\",\n 18: \"\\\\u0012\",\n 19: \"\\\\u0013\",\n 20: \"\\\\u0014\",\n 21: \"\\\\u0015\",\n 22: \"\\\\u0016\",\n 23: \"\\\\u0017\",\n 24: \"\\\\u0018\",\n 25: \"\\\\u0019\",\n 26: \"\\\\u001a\",\n 27: \"\\\\u001b\",\n 28: \"\\\\u001c\",\n 29: \"\\\\u001d\",\n 30: \"\\\\u001e\",\n 31: \"\\\\u001f\",\n 34: '\\\\\"',\n 92: \"\\\\\\\\\",\n};\n\n/**\n * Encode a string to its canonical JSON form.\n * Escapes only what RFC 8259 requires; no over-escaping.\n */\nfunction encodeString(s: string): string {\n let out = '\"';\n for (let i = 0; i < s.length; i++) {\n const cp = s.charCodeAt(i);\n const esc = ESCAPE[cp];\n if (esc !== undefined) {\n out += esc;\n } else {\n out += s[i];\n }\n }\n return `${out}\"`;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\nconst TEXT_ENCODER = new TextEncoder();\n\n/**\n * Produce the canonical UTF-8 byte encoding of a ContractSpec.\n *\n * The canonical form deterministically encodes the spec such that two specs with\n * identical content produce the same byte sequence on every JS runtime, on every\n * platform, across time. This byte sequence is what is hashed to derive the\n * ContractId.\n *\n * Invariants:\n * - Object keys sorted lexicographically at every depth.\n * - Array element order preserved.\n * - NaN / Infinity → throws TypeError.\n * - undefined (absent optional fields) → omitted, not encoded as null.\n * - Signed zero → encoded as 0.\n */\nexport function canonicalize(spec: ContractSpec): Uint8Array {\n const text = canonicalizeText(spec);\n return TEXT_ENCODER.encode(text);\n}\n\n/**\n * Convenience: return the canonical form as a UTF-8 string rather than bytes.\n * Useful for debugging and for feeding into embedding models.\n */\nexport function canonicalizeText(spec: ContractSpec): string {\n return encodeValue(spec as unknown as JsonValue);\n}\n\n// ---------------------------------------------------------------------------\n// canonicalizeQueryText — D2/D3 query-text derivation (DEC-V3-IMPL-QUERY-001)\n// ---------------------------------------------------------------------------\n\n/**\n * Project a QueryIntentCard into a SpecYak-shaped canonical text for embedding.\n *\n * The produced text uses the same canonical JSON encoder as canonicalizeText(),\n * so the query and document vectors are in the same semantic space\n * (DEC-V3-IMPL-QUERY-001 — symmetric query-text derivation).\n *\n * Projection rules (per D2 ADR §Q1 and D3 ADR §Q3):\n * - `behavior` → \"behavior\" key (string, direct)\n * - `guarantees` → \"guarantees\" key: array of {id:\"q<n>\", description: text}\n * - `errorConditions` → \"errorConditions\" key: array of {description: text}\n * - `nonFunctional` → \"nonFunctional\" key: Partial<NonFunctionalProperties> as-is\n * - `propertyTests` → \"propertyTests\" key: array of {id:\"p<n>\", description: text}\n * - `signature.inputs` → \"inputs\" key: array of {name: name|\"arg<n>\", type}\n * - `signature.outputs` → \"outputs\" key: array of {name: name|\"out<n>\", type}\n *\n * Absent fields are omitted from the projection (D1 absent-dimension rule):\n * a query that includes only `behavior` produces only a \"behavior\" key — the\n * embedding model sees only the behavior text, not noise from absent dimensions.\n *\n * The function is pure and deterministic: same card → same string, byte-for-byte.\n *\n * @param card - The LLM-provided QueryIntentCard.\n * @returns A canonical JSON string suitable for embedding via EmbeddingProvider.embed().\n */\nexport function canonicalizeQueryText(card: QueryIntentCard): string {\n // Build a plain object with only the keys that are present in the card.\n // The encodeValue function will sort keys lexicographically, matching the\n // storage embedding's key order (same encoder, same rules).\n const projection: Record<string, JsonValue> = {};\n\n // behavior dimension — direct string\n if (card.behavior !== undefined && card.behavior !== \"\") {\n projection.behavior = card.behavior;\n }\n\n // errorConditions dimension — array of {description}\n if (card.errorConditions !== undefined && card.errorConditions.length > 0) {\n projection.errorConditions = card.errorConditions.map((desc) => ({\n description: desc,\n }));\n }\n\n // guarantees dimension — array of {description, id: \"q<n>\"}\n if (card.guarantees !== undefined && card.guarantees.length > 0) {\n projection.guarantees = card.guarantees.map((desc, i) => ({\n description: desc,\n id: `q${i}`,\n }));\n }\n\n // signature.inputs dimension — array of {name, type}\n if (card.signature?.inputs !== undefined && card.signature.inputs.length > 0) {\n projection.inputs = card.signature.inputs.map((p, i) => ({\n name: p.name ?? `arg${i}`,\n type: p.type,\n }));\n }\n\n // nonFunctional dimension — Partial<NonFunctionalProperties> as-is\n // Only include if at least one key is present (avoid empty-object noise).\n if (card.nonFunctional !== undefined) {\n const nf = card.nonFunctional;\n const nfEntry: Record<string, JsonValue> = {};\n if (nf.purity !== undefined) nfEntry.purity = nf.purity;\n if (nf.space !== undefined) nfEntry.space = nf.space;\n if (nf.threadSafety !== undefined) nfEntry.threadSafety = nf.threadSafety;\n if (nf.time !== undefined) nfEntry.time = nf.time;\n if (Object.keys(nfEntry).length > 0) {\n projection.nonFunctional = nfEntry;\n }\n }\n\n // signature.outputs dimension — array of {name, type}\n if (card.signature?.outputs !== undefined && card.signature.outputs.length > 0) {\n projection.outputs = card.signature.outputs.map((p, i) => ({\n name: p.name ?? `out${i}`,\n type: p.type,\n }));\n }\n\n // propertyTests dimension — array of {description, id: \"p<n>\"}\n if (card.propertyTests !== undefined && card.propertyTests.length > 0) {\n projection.propertyTests = card.propertyTests.map((desc, i) => ({\n description: desc,\n id: `p${i}`,\n }));\n }\n\n return encodeValue(projection);\n}\n\n// ---------------------------------------------------------------------------\n// Test-only exports\n// ---------------------------------------------------------------------------\n\n/**\n * Internal encoder surfaces exposed only for unit tests.\n * Do NOT import this namespace from production code.\n */\nexport const __testing__ = {\n encodeNumber,\n} as const;\n","// SPDX-License-Identifier: MIT\n// @decision DEC-HASH-WI002: Content-address uses BLAKE3 via @noble/hashes.\n// Status: decided (WI-002)\n// Rationale: BLAKE3 provides cryptographic collision resistance needed for a\n// content-addressed registry. @noble/hashes is MIT-licensed, dependency-free\n// (no native bindings), audited, and runs on any JS runtime. FNV-1a (the v0\n// facade) had no collision resistance guarantees; BLAKE3-256 gives 128-bit\n// preimage resistance which is more than sufficient for a registry of any\n// realistic size.\n\nimport { blake3 } from \"@noble/hashes/blake3.js\";\nimport { canonicalize } from \"./canonicalize.js\";\nimport type { ContractId, ContractSpec } from \"./index.js\";\n\n// ---------------------------------------------------------------------------\n// ContractId derivation\n// ---------------------------------------------------------------------------\n\n/**\n * Derive a ContractId from the canonical bytes of a ContractSpec.\n *\n * The id is a 64-character lowercase hex string encoding 32 bytes of BLAKE3\n * output (BLAKE3-256). The hex string is branded as ContractId.\n *\n * Use this overload when you have already called `canonicalize()` and want to\n * avoid re-canonicalizing. This is the primitive that `contractId()` is built on.\n */\nexport function contractIdFromBytes(canonical: Uint8Array): ContractId {\n const digest = blake3(canonical);\n return bytesToHex(digest) as ContractId;\n}\n\n/**\n * Derive a stable ContractId from a ContractSpec.\n *\n * Equivalent to `contractIdFromBytes(canonicalize(spec))`. Call sites are\n * unchanged whether they canonicalize first or not.\n */\nexport function contractId(spec: ContractSpec): ContractId {\n return contractIdFromBytes(canonicalize(spec));\n}\n\n/**\n * Return true if `s` is a validly-formed ContractId: 64 lowercase hex characters.\n * Does not verify that the id was ever registered; only validates the format.\n */\nexport function isValidContractId(s: string): s is ContractId {\n return /^[0-9a-f]{64}$/.test(s);\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Convert a Uint8Array to a lowercase hex string. */\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n","// SPDX-License-Identifier: MIT\n//\n// @decision DEC-WI463-GRANULARITY-001\n// title: Granularity dial — interface and plumbing (WI-GRANULARITY-DIAL, #463)\n// status: accepted\n// rationale:\n// The emit pipeline needs a configurable dial between tight-scoped atoms\n// (high hit rate, large registry, more decomposition passes) and broadly-\n// applicable atoms (lower atomization cost, faster emission, lower hit rate).\n// This module owns the type, the valid range, the default, and the parse\n// helper. Per-level semantics (what Granularity=1 vs Granularity=5 actually\n// changes in the pipeline) are TBD from B9 (#446) and B4 (#188) sweep data;\n// this plumbing layer is deliberately decoupled from that calibration so the\n// interface can ship before the data lands.\n// Three implementation decisions (sub-items):\n// (a) Integer 1–5 chosen over a labeled enum: labeled enums require every\n// consumer to import enum members; integer literals let callers write `3`\n// or `{ granularity: 3 }` without additional imports. The union type\n// provides structural safety.\n// (b) DEFAULT_GRANULARITY = 3 (mid-range) pending benchmark data. Will be\n// updated via a @decision amendment once B9/B4 sweep identifies the\n// cost×hit-rate×attack-surface sweet spot.\n// (c) parseGranularity is a pure, total function returning null on invalid\n// input rather than throwing, so CLI argument parsers can compose it\n// without try/catch.\n\n// ---------------------------------------------------------------------------\n// Type\n// ---------------------------------------------------------------------------\n\n/**\n * Integer dial controlling atom specificity on the emit hot path.\n *\n * 1 = tightest scoping — atoms are highly specific to their declared shape;\n * maximum decomposition; one block per micro-behaviour.\n * 3 = mid-range default — current baseline behaviour (pending benchmark data).\n * 5 = loosest scoping — atoms are broadly applicable; minimal decomposition.\n *\n * Per-level semantics (decomposition stopping rule, embedding match-threshold,\n * atom-size bucket) are calibrated from B9 (#446) and B4 (#188) sweep data.\n * See @decision DEC-WI463-GRANULARITY-001 for the full rationale.\n */\nexport type Granularity = 1 | 2 | 3 | 4 | 5;\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Lowest valid granularity level. */\nexport const MIN_GRANULARITY = 1 as const;\n\n/** Highest valid granularity level. */\nexport const MAX_GRANULARITY = 5 as const;\n\n/**\n * Default granularity level.\n *\n * @decision DEC-WI463-GRANULARITY-001(b): pinned at 3 (mid-range) pending\n * B9 (#446) and B4 (#188) sweep data. Amend this decision when the\n * (cost × hit-rate × attack-surface) sweet spot is determined.\n */\nexport const DEFAULT_GRANULARITY: Granularity = 3;\n\n// ---------------------------------------------------------------------------\n// Parse helper\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a raw string argument into a Granularity level.\n *\n * Returns the parsed Granularity (1–5) if the input is a valid integer in\n * range; returns null for any invalid input (non-integer, out-of-range, empty,\n * or non-numeric string). Never throws.\n *\n * @example\n * parseGranularity(\"3\") // → 3\n * parseGranularity(\"0\") // → null\n * parseGranularity(\"6\") // → null\n * parseGranularity(\"abc\") // → null\n */\nexport function parseGranularity(raw: string): Granularity | null {\n if (raw === \"\") return null;\n const n = Number(raw);\n if (!Number.isInteger(n) || n < MIN_GRANULARITY || n > MAX_GRANULARITY) return null;\n return n as Granularity;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-EMBED-010: Local embeddings via @xenova/transformers behind a\n// provider interface. Status: decided (MASTER_PLAN.md DEC-EMBED-010)\n// Rationale: Local-first matches v0's no-network stance. The provider interface\n// allows hosted providers to swap in later without changing call sites.\n// Model: Xenova/bge-small-en-v1.5 (384 dimensions, ~25MB quantized, MIT license).\n// (Default swapped from all-MiniLM-L6-v2 per DEC-EMBED-MODEL-DEFAULT-002, #326.)\n// Lazy singleton load: the model is not loaded at import time; it loads on the\n// first embed() call and is reused for all subsequent calls.\n\nimport { blake3 } from \"@noble/hashes/blake3.js\";\nimport { canonicalizeText } from \"./canonicalize.js\";\nimport type { ContractSpec } from \"./index.js\";\n\n// ---------------------------------------------------------------------------\n// Provider interface\n// ---------------------------------------------------------------------------\n\n/**\n * A provider that converts a text string into a fixed-dimension embedding vector.\n *\n * Implementations must be deterministic: identical inputs must produce\n * byte-identical Float32Array outputs (modulo platform floating-point, but\n * transformers.js with the same ONNX model and same backend is deterministic).\n */\nexport interface EmbeddingProvider {\n /** Dimensionality of vectors produced by this provider. */\n readonly dimension: number;\n /** Stable identifier for the model/configuration, e.g. \"Xenova/all-MiniLM-L6-v2\". */\n readonly modelId: string;\n /**\n * Embed a text string and return a Float32Array of length `dimension`.\n * Must be deterministic: same input → same output, byte-for-byte.\n */\n embed(text: string): Promise<Float32Array>;\n}\n\n// ---------------------------------------------------------------------------\n// Local provider (transformers.js)\n// ---------------------------------------------------------------------------\n\n// @decision DEC-EMBED-MODEL-SELECTION-001\n// @title DISCOVERY_EMBED_MODEL env-var for D5 embedding-model experiment (#326)\n// @status accepted\n// @rationale WI-V3-DISCOVERY-D5-EMBED-MODEL-EXPERIMENT requires running the full-corpus\n// harness against alternative embedding models without source edits. DISCOVERY_EMBED_MODEL\n// allows runtime model selection. The knob is intentionally experiment-scoped (not a\n// production config surface) — production always uses the committed LOCAL_MODEL_ID default.\n// If the experiment surfaces a better model, LOCAL_MODEL_ID and LOCAL_DIMENSION are updated\n// and DISCOVERY_EMBED_MODEL is no longer needed for that path.\n// DEC-CI-OFFLINE-001 preserved: all listed models are @xenova/transformers–compatible.\n// DEC-EMBED-010 preserved: model must be MIT or Apache 2.0 licensed.\n\n/**\n * Default production model: bge-small-en-v1.5 (384 dims, MIT, ~25MB quantized).\n *\n * @decision DEC-EMBED-MODEL-DEFAULT-002 — bge-small-en-v1.5 as production default\n * @status accepted (operator 2026-05-11)\n * @rationale WI-V3-DISCOVERY-D5-EMBED-MODEL-EXPERIMENT (#326) ran the full-corpus\n * harness against 3 alternative 384-dim models. Final numbers vs current default\n * (all-MiniLM-L6-v2 post-#322):\n *\n * Model M2 M3 M4 Strong-band N (M5 calibration)\n * --------------------- ----- --- ---- -----------\n * Xenova/all-MiniLM-L6-v2 62.5% 92.5% 0.742 (current, below M2 target)\n * Xenova/bge-small-en-v1.5 70.0% 100% 0.823 36/50 ← winner\n * Xenova/e5-small-v2 52.5% 87.5% 0.653 N/A (mis-calibrated)\n * Xenova/all-MiniLM-L12-v2 72.5% 97.5% 0.824 0/50 (score collapse)\n *\n * bge-small wins despite slightly lower M2 than L12 because:\n * - M3=100% across all 5 categories (every right atom in top-10, every time)\n * - Confidence distribution is operator-meaningful: 36/50 strong, 14/50 confident,\n * 0 weak/poor → the D2 auto-accept gate (combinedScore > 0.85 + gap > 0.15) fires\n * on most queries\n * - L12 by contrast puts 48/50 entries in the weak band — auto-accept never fires\n * and downstream consumers always see \"ambiguous, choose\"\n *\n * This swap closes the DEC-V3-INITIATIVE-002 measurement-first gate at the M2=70%\n * target. D1 multi-vector remains paused (and falsified): bge-small's best category\n * is multi-aspect at M2=87.5%, not its worst.\n */\nconst LOCAL_MODEL_ID = \"Xenova/bge-small-en-v1.5\";\n/** Output dimension for the default production model. */\nconst LOCAL_DIMENSION = 384;\n\n/**\n * Known offline-capable, MIT/Apache-2.0 licensed models and their output dimensions.\n * Used for default dimension lookup when `createLocalEmbeddingProvider` is called with\n * a model ID but no explicit dimension. Adding a model here is the canonical gate:\n * license check + offline verification must pass first.\n */\nexport const LOCAL_KNOWN_MODELS: ReadonlyMap<string, number> = new Map([\n [\"Xenova/bge-small-en-v1.5\", 384], // CURRENT DEFAULT (per DEC-EMBED-MODEL-DEFAULT-002); MIT; ~25MB; retrieval-tuned\n [\"Xenova/all-MiniLM-L6-v2\", 384], // prior default; MIT; ~25MB; below M2=70% target post-#322\n [\"Xenova/all-MiniLM-L12-v2\", 384], // 12-layer same-family; Apache 2.0; ~34MB; M2=72.5% but mis-calibrated (#326 reject)\n [\"Xenova/paraphrase-MiniLM-L6-v2\", 384], // paraphrase-tuned; Apache 2.0; ~25MB; not benchmarked\n [\"Xenova/e5-small-v2\", 384], // E5 retrieval model; MIT; ~25MB; M2=52.5% (#326 reject)\n [\"Xenova/all-mpnet-base-v2\", 768], // larger model; Apache 2.0; ~86MB; requires FLOAT[768] schema (deferred)\n]);\n\n// @decision DEC-EMBED-SINGLETON-CLOSURE-001: Pipeline singleton via closure, not module-level let.\n// Status: decided (WI-V2-02)\n// Rationale: A module-level `let pipelinePromise` violates the no-mutable-globals strict-subset\n// rule, which forbids top-level `let`/`var` declarations. Moving the mutable cell into a closure\n// (via an IIFE-style factory that returns a getter function) satisfies the rule: the `let` lives\n// inside a function scope, not at module scope. Runtime behaviour is byte-identical — the\n// pipeline is still loaded lazily on first embed() call and cached for all subsequent calls.\n\n// @decision DEC-EMBED-CUSTOM-MODEL-001: Per-model pipeline factory via makePipelineLoader.\n// Status: decided (WI-V3-DISCOVERY-D5-EMBED-MODEL-EXPERIMENT, issue #326)\n// Rationale: createLocalEmbeddingProvider now accepts an optional modelId to support embedding\n// model swaps for benchmarking (DISCOVERY_EMBED_MODEL env var). Non-default model IDs get\n// per-instance pipeline closures via makePipelineLoader; the default model still uses the\n// module-level singleton to preserve DEC-EMBED-SINGLETON-CLOSURE-001 semantics for production.\n\n/**\n * Create a lazy pipeline loader closure for the given model.\n *\n * @decision DEC-EMBED-LAZY-001: Dynamic import for lazy pipeline init.\n * Status: decided (WI-002)\n * Rationale: Static import triggers ONNX runtime at module load; dynamic defers to first use.\n */\nfunction makePipelineLoader(modelId: string): () => Promise<unknown> {\n let pipelinePromise: Promise<unknown> | null = null;\n return (): Promise<unknown> => {\n if (pipelinePromise === null) {\n pipelinePromise = import(\"@xenova/transformers\").then((mod) =>\n mod.pipeline(\"feature-extraction\", modelId),\n );\n }\n return pipelinePromise;\n };\n}\n\n/** Module-level singleton pipeline for the default model (DEC-EMBED-SINGLETON-CLOSURE-001). */\nconst getPipeline: () => Promise<unknown> = makePipelineLoader(LOCAL_MODEL_ID);\n\n/**\n * The output shape from transformers.js feature-extraction pipeline.\n * transformers.js returns a Tensor-like object; we access .data for the flat\n * Float32Array.\n */\ninterface TransformerOutput {\n data: Float32Array;\n}\n\n/**\n * Create a local EmbeddingProvider backed by transformers.js.\n *\n * The model is loaded lazily on first embed() call. Subsequent calls reuse the\n * same in-memory pipeline. Two calls with the same input text will return\n * byte-equal Float32Arrays.\n *\n * Default: Xenova/all-MiniLM-L6-v2, 384 dimensions, MIT license.\n *\n * Custom model support (DEC-EMBED-CUSTOM-MODEL-001): pass a different modelId\n * and optionally the corresponding dimension to benchmark alternative models.\n * If `dimension` is omitted, it is looked up from LOCAL_KNOWN_MODELS.\n * The schema is currently fixed at FLOAT[384]; 768-dim models require a schema\n * migration before they can be benchmarked against the bootstrap registry.\n *\n * Model selection when `modelId` is omitted (in priority order):\n * 1. `DISCOVERY_EMBED_MODEL` env var (experiment use)\n * 2. LOCAL_MODEL_ID default (`Xenova/all-MiniLM-L6-v2`)\n */\nexport function createLocalEmbeddingProvider(\n modelId: string = (typeof process !== \"undefined\"\n ? (process.env.DISCOVERY_EMBED_MODEL ?? LOCAL_MODEL_ID)\n : LOCAL_MODEL_ID),\n dimension: number = LOCAL_KNOWN_MODELS.get(\n typeof process !== \"undefined\"\n ? (process.env.DISCOVERY_EMBED_MODEL ?? LOCAL_MODEL_ID)\n : LOCAL_MODEL_ID,\n ) ?? LOCAL_DIMENSION,\n): EmbeddingProvider {\n // Default model reuses the module-level singleton (DEC-EMBED-SINGLETON-CLOSURE-001).\n // Custom models get a fresh per-instance closure so they don't share pipeline state.\n const getLoader: () => Promise<unknown> =\n modelId === LOCAL_MODEL_ID ? getPipeline : makePipelineLoader(modelId);\n\n return {\n dimension,\n modelId,\n\n async embed(text: string): Promise<Float32Array> {\n const pipe = getLoader() as Promise<\n (\n text: string,\n options: { pooling: string; normalize: boolean },\n ) => Promise<TransformerOutput>\n >;\n const extractor = await pipe;\n const output = await extractor(text, {\n pooling: \"mean\",\n normalize: true,\n });\n return new Float32Array(output.data);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Offline provider (deterministic, network-free)\n// ---------------------------------------------------------------------------\n\n/** Model identifier used by the offline deterministic provider. */\nconst OFFLINE_MODEL_ID = \"yakcc/offline-blake3-stub\";\n/** Dimension matches the local provider so the registry schema (DEC-EMBED-010) is unchanged. */\nconst OFFLINE_DIMENSION = 384;\n/** 32 bytes per BLAKE3 output × 12 chained hashes = 384 dims. */\nconst OFFLINE_HASH_BLOCKS = 12;\n\n// @decision DEC-EMBED-OFFLINE-PROVIDER-001\n// @title Offline embedding provider for bootstrap and other no-network paths\n// @status accepted\n// @rationale `yakcc bootstrap` (DEC-V2-BOOT-NO-AI-CORPUS-001) is contractually\n// network-free, but the registry's storeBlock pipeline always generates an\n// embedding before insert. The local transformers.js provider downloads the\n// tokenizer from HuggingFace on first call, breaking offline-by-design paths.\n// This deterministic BLAKE3-derived provider satisfies the EmbeddingProvider\n// contract with zero network I/O. Vectors are L2-normalized so cosine\n// distance still behaves; semantic quality is intentionally absent — these\n// embeddings exist only because the schema requires a vector column, not\n// because bootstrap performs intent search. Same input → same vector,\n// byte-for-byte (BLAKE3 is deterministic).\n\n/**\n * Create an offline EmbeddingProvider that produces deterministic vectors\n * via BLAKE3 hashing. No network I/O, no model downloads.\n *\n * Use this provider for bootstrap, CI, sandboxed test runs, and any other\n * code path that must remain network-free. The vectors are not semantically\n * meaningful (similar texts do not produce nearby vectors); they exist only\n * to satisfy the registry's vector-column schema. For semantic search, use\n * `createLocalEmbeddingProvider()` instead.\n *\n * Determinism: identical text inputs produce byte-identical Float32Array\n * outputs across machines, runs, and platforms.\n */\nexport function createOfflineEmbeddingProvider(): EmbeddingProvider {\n return {\n dimension: OFFLINE_DIMENSION,\n modelId: OFFLINE_MODEL_ID,\n\n async embed(text: string): Promise<Float32Array> {\n const encoder = new TextEncoder();\n const vec = new Float32Array(OFFLINE_DIMENSION);\n for (let block = 0; block < OFFLINE_HASH_BLOCKS; block++) {\n const hash = blake3(encoder.encode(`${text}|${block}`));\n for (let i = 0; i < 32; i++) {\n const byte = hash[i] ?? 0;\n vec[block * 32 + i] = (byte - 128) / 128;\n }\n }\n let norm = 0;\n for (let i = 0; i < OFFLINE_DIMENSION; i++) {\n const v = vec[i] ?? 0;\n norm += v * v;\n }\n norm = Math.sqrt(norm);\n if (norm > 0) {\n for (let i = 0; i < OFFLINE_DIMENSION; i++) {\n const v = vec[i] ?? 0;\n vec[i] = v / norm;\n }\n }\n return vec;\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Module-level default provider singleton\n// ---------------------------------------------------------------------------\n\n// @decision DEC-EMBED-SINGLETON-CLOSURE-002: Default-provider singleton via closure, not module-level let.\n// Status: decided (WI-V2-02)\n// Rationale: Same motivation as DEC-EMBED-SINGLETON-CLOSURE-001 — module-level `let` violates\n// no-mutable-globals. The mutable cell is moved into a closure so the `let` lives inside a\n// function scope. Runtime behaviour is byte-identical: the provider is created once on the first\n// call to generateEmbedding() that omits an explicit provider, and reused for all subsequent calls.\nconst getDefaultProvider: () => EmbeddingProvider = (() => {\n let defaultProvider: EmbeddingProvider | null = null;\n return (): EmbeddingProvider => {\n if (defaultProvider === null) {\n defaultProvider = createLocalEmbeddingProvider();\n }\n return defaultProvider;\n };\n})();\n\n// ---------------------------------------------------------------------------\n// Convenience export\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a vector embedding for a ContractSpec.\n *\n * The embedding is produced by converting the spec to its canonical text form\n * (key-sorted JSON) and then embedding that text via the given provider.\n * If no provider is supplied, the module-level local provider is used.\n *\n * The result is a Float32Array of length `provider.dimension`.\n * The embedding is deterministic: the same spec and the same provider always\n * return the same vector.\n */\nexport async function generateEmbedding(\n spec: ContractSpec,\n provider?: EmbeddingProvider,\n): Promise<Float32Array> {\n const p = provider ?? getDefaultProvider();\n const text = canonicalizeText(spec);\n return p.embed(text);\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-TRIPLET-IDENTITY-020: SpecHash is the existing ContractId derivation\n// applied to a canonicalized SpecYak. The spec hash is retained as the index used\n// by selectBlocks(specHash) → BlockMerkleRoot[], not the block's identity. The\n// canonicalization rule is unchanged from v0: BLAKE3-256 over canonicalize(spec).\n// Status: decided (MASTER_PLAN.md, VERIFICATION.md DEC-VERIFY-002)\n// Rationale: Continuous with v0's ContractId derivation so the migration path can\n// re-index the seed corpus without recomputing SpecHash from scratch. Two specs with\n// identical canonical representations share a SpecHash (and are therefore the same\n// contract), even if their corresponding block MerkleRoots differ (different impls).\n\n// @decision DEC-TRIPLET-L0-ONLY-019: The optional level-dependent fields (theory,\n// bounds, totality_witness, proof_kind, constant_time) are accepted by the schema\n// without enforcement at L0. The L1/L2/L3 validators that would enforce these fields\n// are deferred per the scope decision.\n// Status: decided (MASTER_PLAN.md)\n\n// ---------------------------------------------------------------------------\n// Branded types\n// ---------------------------------------------------------------------------\n\n/**\n * A stable content-address identifying a SpecYak by the hash of its canonical\n * form. Continuous with v0's ContractId: SpecHash = BLAKE3(canonicalize(spec.yak)).\n *\n * Two SpecYak values with identical canonical representations share a SpecHash.\n * The SpecHash is the index used by the registry's block selector:\n * selectBlocks(specHash) → BlockMerkleRoot[]\n *\n * Format: 64 lowercase hex characters (BLAKE3-256 output).\n */\nexport type SpecHash = string & { readonly __brand: \"SpecHash\" };\n\n// ---------------------------------------------------------------------------\n// SpecYak — the v1 contract specification format\n// ---------------------------------------------------------------------------\n\n/**\n * The v1 contract specification. Stored as JSON alongside impl.ts and proof/\n * in the block triplet. JSON-shaped for LLM-friendly authoring and mechanical\n * extraction into solver theories.\n *\n * Required fields per VERIFICATION.md §\"spec.yak\":\n * name, inputs, outputs, preconditions, postconditions, invariants, effects, level\n *\n * Optional, level-dependent fields:\n * theory (required at L2), bounds, totality_witness (required at L1+ when needed),\n * proof_kind (required at L3), constant_time\n *\n * SpecYak is structurally a superset of the v0 ContractSpec so existing seed specs\n * lift naturally. The v0 ContractSpec fields (behavior, guarantees, errorConditions,\n * nonFunctional, propertyTests) are preserved as optional fields here to allow a\n * clean structural lift; WI-T05 populates them during seed migration.\n */\nexport interface SpecYak {\n // -------------------------------------------------------------------------\n // Required fields (VERIFICATION.md §\"spec.yak\" required fields)\n // -------------------------------------------------------------------------\n\n /** Human-readable identifier. Informational; identity is derived from the hash. */\n readonly name: string;\n\n /**\n * Typed input schema in the strict-TS-subset type language.\n * At L0 this is free-form string-typed; the encoder lifts it at L2+.\n */\n readonly inputs: readonly SpecYakParameter[];\n\n /** Typed output schema. */\n readonly outputs: readonly SpecYakParameter[];\n\n /**\n * Assertions on inputs that the implementation may assume. Pure blocks with\n * no preconditions declare an empty array.\n */\n readonly preconditions: readonly string[];\n\n /**\n * Assertions on outputs the implementation must guarantee. The spec's\n * postconditions are lifted by the L2 encoder into the SMT refinement query.\n */\n readonly postconditions: readonly string[];\n\n /**\n * Properties preserved across the operation. Stateful contracts use this for\n * invariants; pure-by-default blocks with no state declare an empty array.\n */\n readonly invariants: readonly string[];\n\n /**\n * Declared object-capability requirements. Pure blocks declare an empty array.\n * Effectful blocks list each attenuated capability (e.g. \"WriteOnly:/tmp/x\").\n */\n readonly effects: readonly string[];\n\n /**\n * Declared verification level. The registry enforces the claim at registration.\n * L0 is the v0 floor (strict-TS + fast-check property tests).\n */\n readonly level: \"L0\" | \"L1\" | \"L2\" | \"L3\";\n\n // -------------------------------------------------------------------------\n // Optional, level-dependent fields (accepted without enforcement at L0)\n // -------------------------------------------------------------------------\n\n /**\n * Required at L2. Array of declared SMT theory tags (e.g. [\"bv64\", \"arrays\"]).\n * The encoder uses this to choose its lifting strategy; an undeclared theory\n * at L2 is a registration error.\n */\n readonly theory?: readonly string[] | undefined;\n\n /**\n * Explicit fuzz/BMC budgets. Visible contract metadata so consumers know\n * exactly what bound was used when a block falls back to bounded checking.\n * E.g. { bmc_depth: 16, fuzz_samples: 100000, solver_budget_ms: 5000 }\n */\n readonly bounds?: Record<string, number> | undefined;\n\n /**\n * Required at L1+ when the totality checker cannot conclude purely syntactically.\n * Either { structural_on: \"<argument-name>\" } or { fuel: \"<argument-name>\", max: <N> }.\n */\n readonly totality_witness?: Record<string, string | number> | undefined;\n\n /**\n * Required at L3. Identifies which checker artifact in proof/ is the canonical\n * refinement proof.\n */\n readonly proof_kind?: string | undefined;\n\n /**\n * Non-functional contract property for cryptographically-sensitive blocks.\n * Separate from verification level because constant-time is a side-channel\n * property, not a behavioral one (VERIFICATION.md DEC-VERIFY-004).\n */\n readonly constant_time?: boolean | undefined;\n\n // -------------------------------------------------------------------------\n // v0 structural lift fields (optional; populated by WI-T05 seed migration)\n // -------------------------------------------------------------------------\n\n /** Natural-language behavioral description (v0 ContractSpec.behavior). */\n readonly behavior?: string | undefined;\n\n /** Declared behavioral guarantees (v0 ContractSpec.guarantees). */\n readonly guarantees?:\n | ReadonlyArray<{ readonly id: string; readonly description: string }>\n | undefined;\n\n /** Declared error conditions (v0 ContractSpec.errorConditions). */\n readonly errorConditions?:\n | ReadonlyArray<{\n readonly description: string;\n readonly errorType?: string | undefined;\n }>\n | undefined;\n\n /** Non-functional properties (v0 ContractSpec.nonFunctional). */\n readonly nonFunctional?:\n | {\n readonly time?: string | undefined;\n readonly space?: string | undefined;\n readonly purity: string;\n readonly threadSafety: string;\n }\n | undefined;\n\n /** Property test cases (v0 ContractSpec.propertyTests). */\n readonly propertyTests?:\n | ReadonlyArray<{\n readonly id: string;\n readonly description: string;\n readonly arbitraries?: readonly string[] | undefined;\n }>\n | undefined;\n}\n\n/** A named, typed parameter in a SpecYak input or output schema. */\nexport interface SpecYakParameter {\n readonly name: string;\n readonly type: string;\n readonly description?: string | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Required field names (used by the validator to produce typed errors)\n// ---------------------------------------------------------------------------\n\nconst REQUIRED_FIELDS = [\n \"name\",\n \"inputs\",\n \"outputs\",\n \"preconditions\",\n \"postconditions\",\n \"invariants\",\n \"effects\",\n \"level\",\n] as const;\n\ntype RequiredField = (typeof REQUIRED_FIELDS)[number];\n\nconst VALID_LEVELS = new Set<string>([\"L0\", \"L1\", \"L2\", \"L3\"]);\n\n// ---------------------------------------------------------------------------\n// Validator\n// ---------------------------------------------------------------------------\n\n/**\n * Validate and narrow an unknown value to SpecYak.\n *\n * Throws a TypeError naming the first missing or invalid required field.\n * Optional fields are accepted as-is (no deep validation at L0).\n *\n * Forbidden shortcuts enforced here:\n * - No parallel \"validateContractSpecV0\" entry point ships (this is the sole\n * entry point for spec validation per Evaluation Contract forbidden shortcuts).\n */\nexport function validateSpecYak(value: unknown): SpecYak {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new TypeError(\"validateSpecYak: expected a non-null object\");\n }\n\n const obj = value as Record<string, unknown>;\n\n // Check all required fields are present and have valid types.\n for (const field of REQUIRED_FIELDS) {\n if (!(field in obj) || obj[field] === undefined) {\n throw new TypeError(`validateSpecYak: missing required field \"${field as RequiredField}\"`);\n }\n }\n\n // Validate level is a known value.\n const level = obj.level;\n if (typeof level !== \"string\" || !VALID_LEVELS.has(level)) {\n throw new TypeError(\n `validateSpecYak: invalid \"level\" value ${JSON.stringify(level)}; expected one of \"L0\", \"L1\", \"L2\", \"L3\"`,\n );\n }\n\n // Validate array fields are actually arrays.\n for (const arrayField of [\n \"inputs\",\n \"outputs\",\n \"preconditions\",\n \"postconditions\",\n \"invariants\",\n \"effects\",\n ] as const) {\n if (!Array.isArray(obj[arrayField])) {\n throw new TypeError(`validateSpecYak: field \"${arrayField}\" must be an array`);\n }\n }\n\n // Validate name is a non-empty string.\n if (typeof obj.name !== \"string\" || (obj.name as string).length === 0) {\n throw new TypeError(`validateSpecYak: field \"name\" must be a non-empty string`);\n }\n\n // The value is structurally valid. Return as SpecYak.\n return obj as unknown as SpecYak;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-TRIPLET-L0-ONLY-019: The proof/manifest.json schema accepts\n// smt_cert / lean_proof / coq_proof artifact kinds at the type level, but the\n// L0 manifest validator (validateProofManifestL0) rejects any artifact kind\n// other than \"property_tests\". L1/L2/L3 validators are deferred.\n// Status: decided (MASTER_PLAN.md)\n// Rationale: v0.6 ships L0 only; the schema must be expressive for future levels\n// but the L0 enforcement point is explicit and named so future levels add a new\n// validator rather than loosening this one.\n\n// ---------------------------------------------------------------------------\n// ProofManifest types\n// ---------------------------------------------------------------------------\n\n/**\n * The artifact kinds the proof/manifest.json schema recognizes.\n *\n * - property_tests: a fast-check property-test corpus (required at L0).\n * - smt_cert: an SMT-solver certificate (valid at L2+; schema-valid at L0 but\n * rejected by validateProofManifestL0).\n * - lean_proof: a Lean proof artifact (valid at L3+; schema-valid at L0 but\n * rejected by validateProofManifestL0).\n * - coq_proof: a Coq proof artifact (valid at L3+; schema-valid at L0 but\n * rejected by validateProofManifestL0).\n * - fuzz_bounds_witness: a bounded fuzzing result artifact (valid at L2+;\n * schema-valid at L0 but rejected by validateProofManifestL0).\n */\nexport type ArtifactKind =\n | \"property_tests\"\n | \"smt_cert\"\n | \"lean_proof\"\n | \"coq_proof\"\n | \"fuzz_bounds_witness\";\n\n/**\n * One artifact entry in the proof/manifest.json.\n *\n * The path is relative to the proof/ directory. Each artifact kind has a\n * registered checker in the registry's verifier set. The manifest is part of\n * the BlockMerkleRoot, so attestations cover not just the proof bytes but\n * the declaration of what kind of proof it is.\n */\nexport interface ProofArtifact {\n /** The kind of artifact and which checker to invoke. */\n readonly kind: ArtifactKind;\n /** Path to the artifact file, relative to the proof/ directory. */\n readonly path: string;\n /**\n * For smt_cert artifacts: the SMT theory tags used in this certificate.\n * Optional for other kinds.\n */\n readonly theory?: readonly string[] | undefined;\n /**\n * For lean_proof / coq_proof artifacts: the checker version used.\n * E.g. \"lean4@4.7.0\". Optional for other kinds.\n */\n readonly checker?: string | undefined;\n}\n\n/**\n * The proof/manifest.json schema.\n *\n * The manifest declares which verification artifacts are present in proof/\n * and which checker each invokes. The order of artifacts in this array is\n * the stable order used when computing proof_root in blockMerkleRoot():\n * proof_root = BLAKE3(canonicalize(manifest.json) || concat(BLAKE3(artifact_bytes_in_manifest_order)))\n *\n * The manifest itself is included in the Merkle derivation so that changing\n * the manifest (e.g. adding an artifact declaration) changes the block identity,\n * even if no artifact bytes changed.\n */\nexport interface ProofManifest {\n /**\n * Ordered list of verification artifacts in this block's proof/ directory.\n * The order determines the artifact byte concatenation order in proof_root.\n */\n readonly artifacts: readonly ProofArtifact[];\n}\n\n// ---------------------------------------------------------------------------\n// Validators\n// ---------------------------------------------------------------------------\n\n/**\n * Validate and narrow an unknown value to ProofManifest for L0 blocks.\n *\n * L0 validation rules:\n * 1. The value must be a non-null object with an \"artifacts\" array.\n * 2. The \"artifacts\" array must contain exactly one entry with kind \"property_tests\".\n * 3. All other artifact kinds (smt_cert, lean_proof, coq_proof, fuzz_bounds_witness)\n * are schema-valid at the type level but rejected by this L0-specific validator.\n * L1+/L2+/L3+ validators are deferred (DEC-TRIPLET-L0-ONLY-019).\n * 4. Each artifact entry must have a non-empty \"path\" string.\n *\n * Throws a TypeError with a descriptive message on invalid input.\n */\nexport function validateProofManifestL0(value: unknown): ProofManifest {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new TypeError(\"validateProofManifestL0: expected a non-null object\");\n }\n\n const obj = value as Record<string, unknown>;\n\n if (!(\"artifacts\" in obj) || obj.artifacts === undefined) {\n throw new TypeError('validateProofManifestL0: missing required field \"artifacts\"');\n }\n\n if (!Array.isArray(obj.artifacts)) {\n throw new TypeError('validateProofManifestL0: field \"artifacts\" must be an array');\n }\n\n const artifacts = obj.artifacts as unknown[];\n\n if (artifacts.length === 0) {\n throw new TypeError(\n \"validateProofManifestL0: L0 manifest must contain at least one artifact; \" +\n 'expected exactly one \"property_tests\" entry',\n );\n }\n\n // Validate each artifact entry and enforce L0 artifact-kind constraint.\n let propertyTestsCount = 0;\n for (let i = 0; i < artifacts.length; i++) {\n const entry = artifacts[i];\n if (entry === null || typeof entry !== \"object\" || Array.isArray(entry)) {\n throw new TypeError(`validateProofManifestL0: artifacts[${i}] must be a non-null object`);\n }\n\n const artifact = entry as Record<string, unknown>;\n\n if (!(\"kind\" in artifact) || artifact.kind === undefined) {\n throw new TypeError(`validateProofManifestL0: artifacts[${i}] missing required field \"kind\"`);\n }\n\n if (!(\"path\" in artifact) || artifact.path === undefined) {\n throw new TypeError(`validateProofManifestL0: artifacts[${i}] missing required field \"path\"`);\n }\n\n if (typeof artifact.path !== \"string\" || artifact.path.length === 0) {\n throw new TypeError(\n `validateProofManifestL0: artifacts[${i}].path must be a non-empty string`,\n );\n }\n\n const kind = artifact.kind;\n\n // L0 enforcement: only property_tests is allowed.\n if (kind !== \"property_tests\") {\n throw new TypeError(\n `validateProofManifestL0: artifacts[${i}].kind is \"${String(kind)}\" — only \"property_tests\" is allowed at L0. smt_cert, lean_proof, coq_proof, and fuzz_bounds_witness are schema-valid types but require L2+/L3+ validators (DEC-TRIPLET-L0-ONLY-019).`,\n );\n }\n\n propertyTestsCount++;\n }\n\n // L0 requires exactly one property_tests artifact.\n if (propertyTestsCount !== 1) {\n throw new TypeError(\n `validateProofManifestL0: L0 manifest must declare exactly one \"property_tests\" artifact; found ${propertyTestsCount}`,\n );\n }\n\n return obj as unknown as ProofManifest;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-TRIPLET-IDENTITY-020: BlockMerkleRoot encoding (L0).\n// Status: decided (MASTER_PLAN.md, VERIFICATION.md DEC-VERIFY-002)\n// Rationale: Block identity migrates from ContractId = BLAKE3(canonicalize(spec))\n// to BlockMerkleRoot = BLAKE3(spec_hash || impl_hash || proof_root). The L0-specific\n// encoding decisions below are bounded to L0; L1+ may adopt richer encodings.\n//\n// Concrete L0 encoding:\n// spec_hash = BLAKE3(canonicalize(spec.yak))\n// — same canonicalization rule as v0 contractId(ContractSpec).\n// — SpecHash values are continuous with v0 ContractId values when\n// applied to a spec that omits the v1-only required fields.\n// impl_hash = BLAKE3(impl.ts file bytes, as UTF-8)\n// — no ts-morph normalization at L0 (deferred to L1+ where the\n// totality pass normalizes the AST anyway; picking file bytes now\n// and AST bytes later is consistent with the strict partial-order\n// refinement in VERIFICATION.md §\"What each level guarantees\").\n// proof_root = BLAKE3(canonicalize(manifest.json) || concat(BLAKE3(artifact_bytes)\n// in the order the manifest declares))\n// — manifest is canonicalized (same rule as spec.yak) so that\n// manifest field-order differences are not identity-significant.\n// — artifact bytes are hashed independently and concatenated so that\n// each artifact contributes to the root independently of the others.\n// block_merkle_root = BLAKE3(spec_hash || impl_hash || proof_root)\n// — spec_hash, impl_hash, proof_root are the 32-byte raw digests\n// (not hex strings), concatenated in that order, then hashed once.\n//\n// Superseding this encoding requires a new DEC-ID entry in MASTER_PLAN.md per\n// DEC-IDENTITY-005. Implementations that change the encoding without a new DEC\n// entry violate the single-source-of-truth invariant.\n\nimport { blake3 } from \"@noble/hashes/blake3.js\";\nimport { canonicalize } from \"./canonicalize.js\";\nimport type { ContractSpec } from \"./index.js\";\nimport type { ProofManifest } from \"./proof-manifest.js\";\nimport type { SpecHash, SpecYak } from \"./spec-yak.js\";\n\n// ---------------------------------------------------------------------------\n// Branded types\n// ---------------------------------------------------------------------------\n\n/**\n * The content-address of a Block triplet (spec.yak, impl.ts, proof/).\n * A 64-character lowercase hex string encoding BLAKE3-256 output (32 bytes).\n *\n * Distinct from SpecHash: two blocks with the same SpecHash are alternative\n * implementations of the same contract; their BlockMerkleRoots differ.\n *\n * References between blocks (a composition pointing at a sub-block) carry\n * BlockMerkleRoot, not SpecHash (VERIFICATION.md DEC-VERIFY-002).\n */\nexport type BlockMerkleRoot = string & { readonly __brand: \"BlockMerkleRoot\" };\n\n// ---------------------------------------------------------------------------\n// BlockTriplet — the input shape for blockMerkleRoot()\n// ---------------------------------------------------------------------------\n\n/**\n * @decision DEC-V2-FOREIGN-BLOCK-SCHEMA-001 (sub-A: kind discriminator on Triplet)\n * Status: decided (PLAN_WI_V2_04.md §2.1, L1 contracts layer)\n * @rationale Foreign atoms are opaque leaves keyed by (pkg, export, dtsHash) per\n * DEC-IDENTITY-005. The `kind` discriminator participates in BlockMerkleRoot\n * so foreign and local atoms with otherwise-identical fields produce\n * different roots. Approach (b) was chosen: discriminated union on the\n * triplet type rather than a separate table, satisfying Sacred Practice #12\n * (one canonical type for \"block by merkle root\"). The `kind` field defaults\n * to `'local'` so existing callsites compile without change (L1-I2).\n * @scope L1 only — schema migration lands in L2.\n */\n\n/**\n * Fields shared or specific to a local (yakcc-shaved) block triplet.\n *\n * A LocalTriplet is the original \"local impl\" form: spec.yak + impl.ts + proof/.\n * The `kind: 'local'` discriminator defaults to `'local'` so existing callsites\n * that omit `kind` continue to compile without modification (L1-I2).\n */\nexport interface LocalTriplet {\n /** Discriminator. Defaults to `'local'` for backwards compat (L1-I2). */\n readonly kind?: \"local\";\n /** The parsed spec.yak content. */\n readonly spec: SpecYak;\n /** The impl.ts source text as UTF-8. At L0: raw file bytes, no normalization. */\n readonly implSource: string;\n /** The parsed proof/manifest.json content. */\n readonly manifest: ProofManifest;\n /**\n * Bytes for each artifact declared in manifest.artifacts, keyed by the\n * artifact's path field. All declared paths must be present.\n */\n readonly artifacts: Map<string, Uint8Array>;\n}\n\n/**\n * Fields for a foreign (npm-package or Node built-in) block triplet.\n *\n * Foreign blocks are opaque leaves. Their identity is keyed exclusively on\n * (kind, pkg, export, dtsHash?) per DEC-IDENTITY-005 and DEC-V2-FOREIGN-BLOCK-SCHEMA-001.\n * The `implSource` / `spec` / `manifest` / `artifacts` fields of a LocalTriplet\n * are intentionally absent — foreign blocks carry no shaved implementation.\n */\nexport interface ForeignTripletFields {\n /** Discriminator — must be `'foreign'` for this variant. */\n readonly kind: \"foreign\";\n /** The npm package name or Node built-in specifier, e.g. `\"node:fs\"`, `\"ts-morph\"`. */\n readonly pkg: string;\n /** The exported symbol name consumed at the use site, e.g. `\"readFileSync\"`. */\n readonly export: string;\n /**\n * Optional BLAKE3 hash of the declaration text from the package's `.d.ts` file.\n * When present, two foreign blocks with identical (pkg, export) but different\n * `.d.ts` shapes receive different BlockMerkleRoots (type drift is identity-significant).\n * When absent, identity is keyed on (pkg, export) only.\n */\n readonly dtsHash?: string;\n}\n\n/**\n * The data the blockMerkleRoot() function needs to derive a block's identity.\n *\n * `BlockTriplet` is a discriminated union of LocalTriplet | ForeignTripletFields.\n * The `kind` field is the discriminator:\n * - `kind: 'local'` (or absent) → local yakcc-shaved block\n * - `kind: 'foreign'` → foreign npm/Node block (opaque leaf)\n *\n * T01 is pure-function only; reading files from disk is T02's job. Callers\n * materialize the artifact bytes from whatever source they prefer (filesystem,\n * in-memory fixture, database blob) and pass the results here.\n *\n * Design note: Map<string, Uint8Array> is the simplest shape that does not\n * lock T02..T06 into a specific I/O strategy. T02 will populate this from\n * filesystem reads; T03 will populate it from database blobs; tests populate\n * it from inline literals. The function itself has no I/O dependencies.\n */\nexport type BlockTriplet = LocalTriplet | ForeignTripletFields;\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst TEXT_ENCODER = new TextEncoder();\n\n/** Concatenate multiple Uint8Arrays into one. */\nfunction concatBytes(...arrays: Uint8Array[]): Uint8Array {\n const totalLen = arrays.reduce((n, a) => n + a.length, 0);\n const out = new Uint8Array(totalLen);\n let offset = 0;\n for (const a of arrays) {\n out.set(a, offset);\n offset += a.length;\n }\n return out;\n}\n\n/** Convert a Uint8Array to a lowercase hex string. */\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n// ---------------------------------------------------------------------------\n// SpecHash derivation (re-exported for the continuity test)\n// ---------------------------------------------------------------------------\n\n/**\n * Derive a SpecHash from a SpecYak.\n *\n * SpecHash = BLAKE3(canonicalize(spec.yak))\n *\n * This is continuous with v0's contractId(ContractSpec) derivation: when a\n * SpecYak is projected to the ContractSpec shape (dropping v1-only fields),\n * specHash(spec) === contractId(projection). This continuity is what lets\n * T03's registry migration re-index without recomputing SpecHash from scratch.\n *\n * The canonicalize() function accepts ContractSpec; SpecYak is structurally\n * compatible (a strict superset), so the cast is safe.\n */\nexport function specHash(spec: SpecYak): SpecHash {\n const bytes = canonicalize(spec as unknown as ContractSpec);\n const digest = blake3(bytes);\n return bytesToHex(digest) as SpecHash;\n}\n\n// ---------------------------------------------------------------------------\n// blockMerkleRoot — the primary identity derivation\n// ---------------------------------------------------------------------------\n\n/**\n * Derive the BlockMerkleRoot for a block triplet.\n *\n * Dispatches on `triplet.kind`:\n *\n * **Local (kind: 'local' or omitted) — L0 encoding (DEC-TRIPLET-IDENTITY-020):**\n *\n * spec_hash = BLAKE3(canonicalize(spec.yak)) [32 raw bytes]\n * impl_hash = BLAKE3(UTF-8 bytes of implSource) [32 raw bytes]\n * proof_root = BLAKE3( [32 raw bytes]\n * canonicalize(manifest.json)\n * || BLAKE3(artifact[0].bytes)\n * || BLAKE3(artifact[1].bytes)\n * || ... [in manifest declaration order]\n * )\n * block_merkle_root = BLAKE3(spec_hash || impl_hash || proof_root)\n *\n * Throws if any artifact declared in manifest.artifacts is missing from the\n * artifacts Map.\n *\n * **Foreign (kind: 'foreign') — package-keyed identity (DEC-V2-FOREIGN-BLOCK-SCHEMA-001):**\n *\n * foreign_identity_bytes = canonicalize({ kind, pkg, export, dtsHash? })\n * block_merkle_root = BLAKE3(foreign_identity_bytes)\n *\n * The hash inputs are (kind, pkg, export, dtsHash?) only — NOT the impl source.\n * This is the \"package-keyed identity\" property: two foreign references to the same\n * (pkg, export, dtsHash?) always produce the same BlockMerkleRoot regardless of the\n * source file that references them. The `kind` discriminator participates in the hash\n * so a foreign triplet with otherwise-identical fields to a local triplet produces a\n * different root.\n */\nexport function blockMerkleRoot(triplet: BlockTriplet): BlockMerkleRoot {\n if (triplet.kind === \"foreign\") {\n return blockMerkleRootForeign(triplet);\n }\n return blockMerkleRootLocal(triplet);\n}\n\n/**\n * Derive BlockMerkleRoot for a local (yakcc-shaved) triplet.\n * See blockMerkleRoot() for full encoding spec.\n */\nfunction blockMerkleRootLocal(triplet: LocalTriplet): BlockMerkleRoot {\n // spec_hash: BLAKE3(canonicalize(spec.yak))\n const specBytes = canonicalize(triplet.spec as unknown as ContractSpec);\n const specHashBytes = blake3(specBytes); // 32 raw bytes\n\n // impl_hash: BLAKE3(UTF-8 bytes of impl.ts)\n const implBytes = TEXT_ENCODER.encode(triplet.implSource);\n const implHashBytes = blake3(implBytes); // 32 raw bytes\n\n // proof_root: BLAKE3(canonicalize(manifest.json) || concat(BLAKE3(artifact_i)))\n // Step 1: canonicalize the manifest (treats it as a generic JSON object).\n // The ProofManifest is structurally compatible with the JsonValue shape the\n // canonicalizer operates over; cast through unknown is safe here.\n const manifestBytes = canonicalize(triplet.manifest as unknown as ContractSpec);\n\n // Step 2: hash each artifact in manifest declaration order, then concatenate.\n const artifactHashParts: Uint8Array[] = [];\n for (const artifact of triplet.manifest.artifacts) {\n const artifactBytes = triplet.artifacts.get(artifact.path);\n if (artifactBytes === undefined) {\n throw new Error(\n `blockMerkleRoot: artifact \"${artifact.path}\" declared in manifest but not found in artifacts Map`,\n );\n }\n artifactHashParts.push(blake3(artifactBytes)); // each is 32 bytes\n }\n\n // Step 3: BLAKE3(manifest_canonical_bytes || artifact_hash_0 || artifact_hash_1 || ...)\n const proofInput = concatBytes(manifestBytes, ...artifactHashParts);\n const proofRootBytes = blake3(proofInput); // 32 raw bytes\n\n // block_merkle_root: BLAKE3(spec_hash || impl_hash || proof_root)\n const rootInput = concatBytes(specHashBytes, implHashBytes, proofRootBytes);\n const rootBytes = blake3(rootInput);\n\n return bytesToHex(rootBytes) as BlockMerkleRoot;\n}\n\n/**\n * Derive BlockMerkleRoot for a foreign (opaque leaf) triplet.\n *\n * Identity is keyed on (kind, pkg, export, dtsHash?) only — per DEC-IDENTITY-005\n * and DEC-V2-FOREIGN-BLOCK-SCHEMA-001. The canonical JSON is sorted by key,\n * so the discriminator `kind` always precedes `pkg` alphabetically, ensuring\n * the discriminator participates in the hash input.\n *\n * The `dtsHash` field is omitted from the canonical form when undefined so\n * that absent and absent produce the same root (omitted ≠ null per DEC-CANON-001).\n */\nfunction blockMerkleRootForeign(triplet: ForeignTripletFields): BlockMerkleRoot {\n // Build a plain object with only the identity-significant fields.\n // dtsHash is omitted when undefined so canonicalize() skips it (undefined → omitted).\n const identityObj: Record<string, string> = {\n kind: triplet.kind,\n pkg: triplet.pkg,\n // eslint-disable-next-line @typescript-eslint/naming-convention\n export: triplet.export,\n ...(triplet.dtsHash !== undefined ? { dtsHash: triplet.dtsHash } : {}),\n };\n\n // canonicalize() sorts keys lexicographically; for this object the order is:\n // dtsHash (when present), export, kind, pkg — deterministic across runtimes.\n const identityBytes = canonicalize(identityObj as unknown as ContractSpec);\n const rootBytes = blake3(identityBytes);\n return bytesToHex(rootBytes) as BlockMerkleRoot;\n}\n\n// ---------------------------------------------------------------------------\n// Type guards\n// ---------------------------------------------------------------------------\n\n/**\n * Narrow a BlockTriplet to LocalTriplet.\n *\n * Returns true when `kind` is `'local'` or absent (backwards-compat default).\n * The absent case covers all existing callsites that were created before the\n * `kind` discriminator was introduced (L1-I2).\n */\nexport function isLocalTriplet(t: BlockTriplet): t is LocalTriplet {\n return t.kind !== \"foreign\";\n}\n\n/**\n * Narrow a BlockTriplet to ForeignTripletFields.\n *\n * Returns true only when `kind === 'foreign'`.\n */\nexport function isForeignTriplet(t: BlockTriplet): t is ForeignTripletFields {\n return t.kind === \"foreign\";\n}\n","// SPDX-License-Identifier: MIT\n/**\n * @decision DEC-AST-CANON-001: Canonical AST hashing uses ts-morph + canonical print + local-rename.\n * Status: proposed (MASTER_PLAN.md edits are out of scope for WI-012-01)\n * Rationale: ts-morph wraps the TypeScript compiler API and provides a stable, high-level\n * AST walker with a built-in printer that handles comment stripping and whitespace normalization\n * via `Node.print({ removeComments: true })`. This avoids hand-rolling a TypeScript printer\n * and stays version-stable as long as ts-morph's output contract holds. The alternative —\n * the raw TypeScript compiler API — was rejected because it is already used by ts-morph\n * internally, and exposing it directly would create a parallel authority for AST traversal.\n * Local-only identifiers are renamed to positional placeholders (__v0, __v1, ...) in\n * declaration order so that semantically equivalent functions with different local variable\n * names hash identically, while exported names and public API surface remain verbatim.\n */\n\nimport { blake3 } from \"@noble/hashes/blake3.js\";\nimport { Project, ScriptKind, SyntaxKind, ts } from \"ts-morph\";\nimport type { Node, SourceFile } from \"ts-morph\";\n\n// ---------------------------------------------------------------------------\n// Branded type\n// ---------------------------------------------------------------------------\n\n/**\n * A 64-character lowercase hex BLAKE3-256 digest of the canonical AST form of\n * a TypeScript source fragment. Two source fragments are structurally equivalent\n * (modulo comments, whitespace, and local variable renaming) iff their\n * CanonicalAstHash values are equal.\n */\nexport type CanonicalAstHash = string & { readonly __brand: \"CanonicalAstHash\" };\n\n// ---------------------------------------------------------------------------\n// Error type\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when `canonicalAstHash` cannot produce a canonical hash because the\n * source is syntactically invalid or the requested range spans multiple AST nodes.\n */\nexport class CanonicalAstParseError extends Error {\n constructor(\n message: string,\n public readonly diagnostics: readonly string[],\n ) {\n super(message);\n this.name = \"CanonicalAstParseError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nconst TEXT_ENCODER = new TextEncoder();\n\n/** Convert a Uint8Array to a lowercase hex string. */\nfunction bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * Find the smallest single Node in `file` whose [getStart(), getEnd()]\n * covers the entirety of [range.start, range.end).\n *\n * Uses a depth-first walk: if a node's span covers the range, try its children;\n * the deepest single-node cover is the result. If no single node covers the\n * range (i.e. the range straddles a sibling boundary), throw.\n */\nfunction findEnclosingNode(\n file: SourceFile,\n range: { readonly start: number; readonly end: number },\n): Node {\n // SyntaxList is an internal grouping node that cannot be printed directly\n // (ts-morph's printer throws \"Unhandled SyntaxKind: SyntaxList\"). Skip it\n // when it covers the range — use its parent instead.\n const UNPRINTABLE_KINDS = new Set([SyntaxKind.SyntaxList]);\n\n function walk(node: Node): Node | undefined {\n const nodeStart = node.getStart();\n const nodeEnd = node.getEnd();\n if (nodeStart > range.start || nodeEnd < range.end) {\n return undefined; // node does not cover the range\n }\n // This node covers the range. Try children to find a tighter cover.\n for (const child of node.getChildren()) {\n const result = walk(child);\n if (result !== undefined) return result;\n }\n // Skip unprintable internal nodes — they cannot be used as a hash root.\n if (UNPRINTABLE_KINDS.has(node.getKind())) {\n return undefined;\n }\n return node; // this is the tightest printable covering node\n }\n\n const result = walk(file);\n\n if (result === undefined) {\n throw new CanonicalAstParseError(\"range-spans-multiple-nodes\", []);\n }\n\n // If the tightest covering node is the SourceFile itself, but the range does\n // not cover the full source (i.e. [range.start, range.end) is a strict\n // sub-range of [file.getStart(), file.getEnd())), then no single meaningful\n // sub-node covers the requested range — the range straddles multiple siblings.\n // Throw to prevent the caller from silently getting a hash of the whole file\n // when they asked for a sub-range.\n if (\n result.getKind() === SyntaxKind.SourceFile &&\n (range.start > file.getStart() || range.end < file.getEnd())\n ) {\n throw new CanonicalAstParseError(\"range-spans-multiple-nodes\", []);\n }\n\n return result;\n}\n\n/**\n * Check whether an AST node carries an `export` modifier using the raw\n * TypeScript compiler API (always available via ts-morph's re-exported `ts`).\n *\n * `ts.canHaveModifiers` guards the call so we don't ask for modifiers on node\n * kinds that TypeScript disallows (e.g. token nodes, literals).\n */\nfunction hasExportModifier(node: Node): boolean {\n const compilerNode = node.compilerNode;\n if (!ts.canHaveModifiers(compilerNode)) return false;\n const mods = ts.getModifiers(compilerNode);\n if (mods === undefined) return false;\n return mods.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);\n}\n\n/**\n * Determine whether a binding declared at `declarationNode` is \"local-only\"\n * within `root`, meaning:\n * - It is declared inside the root sub-tree.\n * - It is NOT exported from the root sub-tree (no `export` modifier on its\n * declaration or its containing statement).\n * - It is NOT a parameter of an exported function/method.\n * - It is NOT a property name on an object literal or JSX attribute.\n * - It is NOT a type parameter name.\n * - The declaration is a variable/binding element or parameter of a\n * non-exported function.\n *\n * Conservative: when in doubt, return false (keep original name).\n */\nfunction isLocalBinding(declarationNode: Node, root: Node): boolean {\n // Must be within the root sub-tree\n const declStart = declarationNode.getStart();\n const declEnd = declarationNode.getEnd();\n const rootStart = root.getStart();\n const rootEnd = root.getEnd();\n if (declStart < rootStart || declEnd > rootEnd) return false;\n\n const kind = declarationNode.getKind();\n\n // Only rename bindings from variable declarations and parameters of non-exported functions.\n if (\n kind !== SyntaxKind.VariableDeclaration &&\n kind !== SyntaxKind.Parameter &&\n kind !== SyntaxKind.BindingElement\n ) {\n return false;\n }\n\n // For parameters: only rename if the containing function is NOT exported.\n if (kind === SyntaxKind.Parameter) {\n let ancestor: Node | undefined = declarationNode.getParent();\n while (ancestor !== undefined) {\n const ancestorKind = ancestor.getKind();\n if (\n ancestorKind === SyntaxKind.FunctionDeclaration ||\n ancestorKind === SyntaxKind.FunctionExpression ||\n ancestorKind === SyntaxKind.ArrowFunction ||\n ancestorKind === SyntaxKind.MethodDeclaration\n ) {\n // Check for export modifier on the function/method itself\n if (hasExportModifier(ancestor)) return false;\n // Also check the parent statement for export (e.g. `export function f`)\n const parentOfFn = ancestor.getParent();\n if (parentOfFn !== undefined && hasExportModifier(parentOfFn)) return false;\n break;\n }\n ancestor = ancestor.getParent();\n }\n }\n\n // For variable declarations: check the containing VariableStatement for export.\n if (kind === SyntaxKind.VariableDeclaration || kind === SyntaxKind.BindingElement) {\n let ancestor: Node | undefined = declarationNode.getParent();\n while (ancestor !== undefined) {\n const ancestorKind = ancestor.getKind();\n if (ancestorKind === SyntaxKind.VariableStatement) {\n if (hasExportModifier(ancestor)) return false;\n break;\n }\n // Stop at function/arrow/class boundaries — the declaration is local to that scope\n if (\n ancestorKind === SyntaxKind.FunctionDeclaration ||\n ancestorKind === SyntaxKind.FunctionExpression ||\n ancestorKind === SyntaxKind.ArrowFunction ||\n ancestorKind === SyntaxKind.MethodDeclaration ||\n ancestorKind === SyntaxKind.ClassDeclaration ||\n ancestorKind === SyntaxKind.SourceFile\n ) {\n break;\n }\n ancestor = ancestor.getParent();\n }\n }\n\n return true;\n}\n\n/**\n * Collect a mapping from original local binding name → placeholder name\n * (__v0, __v1, ...) in declaration order within `root`.\n *\n * Walks the AST looking for identifier nodes that are binding names\n * (declarations), not references. Each unique name that qualifies as local\n * gets a sequentially assigned placeholder.\n */\nfunction collectLocalRenames(root: Node): Map<string, string> {\n const renames = new Map<string, string>();\n let counter = 0;\n\n function walk(node: Node): void {\n const kind = node.getKind();\n\n if (\n kind === SyntaxKind.VariableDeclaration ||\n kind === SyntaxKind.Parameter ||\n kind === SyntaxKind.BindingElement\n ) {\n // The name child of a VariableDeclaration / Parameter is the binding identifier\n const nameNode = (node as { getNameNode?: () => Node | undefined }).getNameNode?.();\n if (nameNode !== undefined && nameNode.getKind() === SyntaxKind.Identifier) {\n const name = nameNode.getText();\n if (!renames.has(name) && isLocalBinding(node, root)) {\n renames.set(name, `__v${counter++}`);\n }\n }\n }\n\n for (const child of node.getChildren()) {\n walk(child);\n }\n }\n\n walk(root);\n return renames;\n}\n\n/**\n * Emit canonical text for `root` by:\n * 1. Printing the node with comment removal via ts-morph's built-in printer.\n * 2. Applying local-rename substitutions via whole-word regex replacement.\n *\n * The ts-morph printer handles whitespace normalization and comment stripping;\n * we only post-process for local renames.\n */\nfunction emitCanonical(root: Node, renames: Map<string, string>): string {\n // Use ts-morph's built-in printer with removeComments flag.\n // For a SourceFile, print() returns the full source text without comments.\n // For a sub-node, print() returns just that node's text.\n let text = root.print({ removeComments: true });\n\n // Apply local renames: whole-word replacement to avoid replacing substrings.\n // Process in a stable order (insertion order of the Map is declaration order).\n for (const [original, placeholder] of renames) {\n // Escape special regex characters in the original name\n const escaped = original.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const re = new RegExp(`\\\\b${escaped}\\\\b`, \"g\");\n text = text.replace(re, placeholder);\n }\n\n return text;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Compute a canonical AST hash for a TypeScript source fragment.\n *\n * The hash is deterministic: two source strings that are structurally equivalent\n * modulo comments, whitespace normalization, and local identifier renaming will\n * produce the same hash.\n *\n * @param source - The TypeScript source text to hash.\n * @param sourceRange - Optional byte range `[start, end)` within `source`.\n * When provided, `canonicalAstHash` walks the AST to find the smallest single\n * Node covering the range and hashes only that node. If the range straddles\n * multiple top-level nodes, `CanonicalAstParseError` is thrown.\n *\n * @throws {CanonicalAstParseError} If the source contains TypeScript errors, or\n * if `sourceRange` spans multiple AST nodes, or if `sourceRange` is outside\n * the source bounds.\n */\nexport function canonicalAstHash(\n source: string,\n sourceRange?: { readonly start: number; readonly end: number },\n): CanonicalAstHash {\n // Validate range bounds eagerly before parsing.\n if (sourceRange !== undefined) {\n if (\n sourceRange.start < 0 ||\n sourceRange.end > source.length ||\n sourceRange.start > sourceRange.end\n ) {\n throw new CanonicalAstParseError(\"range-out-of-bounds\", []);\n }\n }\n\n // Phase 1: Parse the source in-memory with ts-morph.\n const project = new Project({\n useInMemoryFileSystem: true,\n compilerOptions: {\n allowJs: false,\n noEmit: true,\n skipLibCheck: true,\n },\n });\n\n const file = project.createSourceFile(\"anonymous.ts\", source, {\n scriptKind: ScriptKind.TS,\n });\n\n // Collect only SYNTAX diagnostics (TypeScript error codes 1000–1999).\n //\n // We intentionally exclude semantic errors such as \"Cannot find module X\"\n // (TS2307) and \"Cannot find name Y\" (TS2304). Those arise from the isolated\n // in-memory parse context that has no access to node_modules; they do NOT\n // indicate that the source text is structurally malformed. Callers hash\n // real-world TypeScript files that freely import from external packages.\n //\n // Syntax errors (1xxx) are the only class that can make the AST too broken\n // to print deterministically; they are the correct gate here.\n const syntaxDiagnostics = file\n .getPreEmitDiagnostics()\n .filter(\n (d) =>\n d.getCategory() === ts.DiagnosticCategory.Error &&\n d.getCode() >= 1000 &&\n d.getCode() < 2000,\n );\n\n if (syntaxDiagnostics.length > 0) {\n const messages = syntaxDiagnostics.map((d) => {\n const msg = d.getMessageText();\n return typeof msg === \"string\" ? msg : msg.getMessageText();\n });\n throw new CanonicalAstParseError(\n `TypeScript syntax error(s) in source: ${messages[0] ?? \"unknown error\"}`,\n messages,\n );\n }\n\n // Phase 2: Determine root node.\n const root: Node = sourceRange !== undefined ? findEnclosingNode(file, sourceRange) : file;\n\n // Phase 3: Collect local renames within the root.\n const renames = collectLocalRenames(root);\n\n // Phase 4: Emit canonical text.\n const canonical = emitCanonical(root, renames);\n\n // Phase 5: Hash via BLAKE3.\n const digest = blake3(TEXT_ENCODER.encode(canonical));\n return bytesToHex(digest) as CanonicalAstHash;\n}\n","// SPDX-License-Identifier: MIT\n/**\n * Shared TypeScript source-extraction primitives used by both:\n * - packages/shave/src/intent/static-extract.ts (atomize/storeBlock path)\n * - packages/contracts/src/query-from-source.ts (query enrichment path)\n *\n * @decision DEC-EMBED-QUERY-ENRICH-HELPER-001\n * @title Shared extraction primitive ensures byte-identical behavior/signature\n * derivation on both atomize-side and query-side (OD-2 Option A).\n * @status accepted\n * @rationale\n * The embedding asymmetry (#444 / #502 / #523) was caused partly by\n * query-side callers deriving QueryIntentCard fields independently from how\n * the atomize path (storeBlock) derives IntentCard fields from source. By\n * factoring the extraction code into this shared module, any future\n * improvement to extraction (e.g. a new @throws parser, rest-param handling)\n * is automatically applied to BOTH paths, making extraction-asymmetry drift\n * structurally harder to reintroduce.\n *\n * Design decisions inherited from static-extract.ts:\n * - DEC-INTENT-STATIC-003: source-text only via getTypeNode()?.getText()\n * (no type-checker, no lib loading, ~5ms vs ~200ms).\n * - DEC-INTENT-STATIC-002: JSDoc tag vocabulary (Eiffel/JML lineage).\n * - DEC-INTENT-STATIC-BEHAVIOR-COLLAPSE-001: whitespace collapse in\n * buildSignatureString prevents newlines in behavior field.\n * - DEC-INTENT-STATIC-001: primary-declaration preference chain\n * (re-implemented in source-pick.ts in this package).\n *\n * Dependencies: ts-morph only (already a contracts peer dependency).\n * No I/O. Deterministic. Pure.\n */\n\nimport { Node, type SourceFile } from \"ts-morph\";\n\n// ---------------------------------------------------------------------------\n// Exported primitive types\n// ---------------------------------------------------------------------------\n\n/**\n * A single extracted parameter: name + source-text type annotation.\n * Compatible with IntentParam.name/typeHint (shave) and\n * QueryTypeSignatureParam.name/type (contracts), but uses neutral field names\n * to avoid coupling to either package's schema.\n */\nexport interface ExtractedParam {\n /** Parameter name (or destructure pattern text, or \"...rest\"). */\n readonly name: string;\n /**\n * Source-text type annotation, e.g. \"number\", \"readonly string[]\", \"T\".\n * \"unknown\" when no annotation is present.\n * (DEC-INTENT-STATIC-003)\n */\n readonly typeAnnotation: string;\n}\n\n/**\n * Extracted signature info from a function-like declaration node.\n */\nexport interface ExtractedSignature {\n readonly params: readonly ExtractedParam[];\n readonly returnTypeAnnotation: string;\n /**\n * Deterministic signature string used as the behavior fallback when no\n * JSDoc summary is present.\n * e.g. \"function add(a, b) -> number\"\n * Undefined when the node has no function-like shape.\n */\n readonly signatureString: string | undefined;\n}\n\n/**\n * Structured JSDoc fields extracted from a declaration node.\n */\nexport interface ExtractedJsDoc {\n /** First sentence of the JSDoc description, or undefined if absent. */\n readonly summary: string | undefined;\n /** Map from param name to description text. */\n readonly params: ReadonlyMap<string, string>;\n /** @returns / @return description text, or undefined. */\n readonly returns: string | undefined;\n /** @requires precondition texts (in order). */\n readonly preconditions: readonly string[];\n /** @ensures postcondition texts (in order). */\n readonly postconditions: readonly string[];\n /** @throws / @throw / @exception description texts (with \"throws: \" prefix). */\n readonly throwDescriptions: readonly string[];\n /** @remarks / @example / @note annotation texts. */\n readonly notes: readonly string[];\n}\n\n// ---------------------------------------------------------------------------\n// Public API — signature extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the parameter list and return-type annotation from the primary\n * declaration node of a source file.\n *\n * @param node - The declaration node (may be FunctionDeclaration,\n * VariableStatement, ArrowFunction, or MethodDeclaration).\n * @returns ExtractedSignature (empty params / \"unknown\" / undefined when\n * the node has no function-like shape).\n */\nexport function extractSignatureFromNode(node: Node): ExtractedSignature {\n // Resolve the actual function/arrow node from VariableStatement wrapper.\n const funcNode = resolveFunctionNode(node);\n\n if (funcNode === undefined) {\n if (Node.isMethodDeclaration(node)) {\n return extractSignatureFromMethod(node);\n }\n return { params: [], returnTypeAnnotation: \"unknown\", signatureString: undefined };\n }\n\n const params = extractParams(funcNode);\n const returnTypeAnnotation = extractReturnType(funcNode);\n const signatureString = buildSignatureString(node, params, returnTypeAnnotation);\n\n return { params, returnTypeAnnotation, signatureString };\n}\n\n// ---------------------------------------------------------------------------\n// Public API — JSDoc extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Extract structured JSDoc fields from a declaration node.\n *\n * Critical: On `const f = () => ...`, JSDoc attaches to the VariableStatement,\n * NOT the inner ArrowFunction. Always pass the VariableStatement here —\n * see DEC-INTENT-STATIC-001.\n *\n * Tag vocabulary (DEC-INTENT-STATIC-002):\n * @param, @returns/@return, @requires, @ensures,\n * @throws/@throw/@exception, @remarks, @example, @note.\n *\n * @param node - The declaration node to extract JSDoc from.\n * @returns ExtractedJsDoc (all arrays empty, fields undefined, when no JSDoc).\n */\nexport function extractJsDoc(node: Node): ExtractedJsDoc {\n const result: {\n summary: string | undefined;\n params: Map<string, string>;\n returns: string | undefined;\n preconditions: string[];\n postconditions: string[];\n throwDescriptions: string[];\n notes: string[];\n } = {\n summary: undefined,\n params: new Map(),\n returns: undefined,\n preconditions: [],\n postconditions: [],\n throwDescriptions: [],\n notes: [],\n };\n\n if (\n !(\"getJsDocs\" in node && typeof (node as { getJsDocs(): unknown }).getJsDocs === \"function\")\n ) {\n return result;\n }\n\n const jsDocs = (node as { getJsDocs(): unknown[] }).getJsDocs();\n\n for (const docRaw of jsDocs) {\n const doc = docRaw as Node;\n if (!Node.isJSDoc(doc)) continue;\n\n // Summary: first sentence of the description.\n const descNode = doc.getDescription();\n if (\n result.summary === undefined &&\n typeof descNode === \"string\" &&\n descNode.trim().length > 0\n ) {\n result.summary = extractFirstSentence(descNode);\n }\n\n // Tags\n for (const tag of doc.getTags()) {\n const tagName = tag.getTagName();\n const tagText = getTagBody(tag);\n\n switch (tagName) {\n case \"param\": {\n const { paramName, body } = parseParamTag(tag);\n if (paramName !== \"\" && !result.params.has(paramName)) {\n result.params.set(paramName, body);\n }\n break;\n }\n\n case \"returns\":\n case \"return\":\n if (result.returns === undefined && tagText.length > 0) {\n result.returns = collapseWhitespace(tagText);\n }\n break;\n\n case \"requires\":\n if (tagText.length > 0) {\n result.preconditions.push(collapseWhitespace(tagText));\n }\n break;\n\n case \"ensures\":\n if (tagText.length > 0) {\n result.postconditions.push(collapseWhitespace(tagText));\n }\n break;\n\n case \"throws\":\n case \"throw\":\n case \"exception\":\n if (tagText.length > 0) {\n result.throwDescriptions.push(collapseWhitespace(tagText));\n }\n break;\n\n case \"remarks\":\n if (tagText.length > 0) {\n result.notes.push(`remarks: ${collapseWhitespace(tagText)}`);\n }\n break;\n\n case \"example\":\n if (tagText.length > 0) {\n result.notes.push(`example: ${collapseWhitespace(tagText)}`);\n }\n break;\n\n case \"note\":\n if (tagText.length > 0) {\n result.notes.push(collapseWhitespace(tagText));\n }\n break;\n\n default:\n // Unknown tag — skip.\n break;\n }\n }\n }\n\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Public API — named-function picker (for entryFunction option)\n// ---------------------------------------------------------------------------\n\n/**\n * Find an exported declaration node by name in a source file.\n *\n * Used by queryIntentCardFromSource when the caller specifies `entryFunction`.\n * Returns the VariableStatement for arrow-const declarations (JSDoc gotcha —\n * see DEC-INTENT-STATIC-001) or the FunctionDeclaration itself.\n *\n * @param sourceFile - Parsed ts-morph SourceFile.\n * @param name - Name of the exported function/const to find.\n * @returns The matching Node or undefined if not found.\n */\nexport function findExportedDeclarationByName(\n sourceFile: SourceFile,\n name: string,\n): Node | undefined {\n for (const stmt of sourceFile.getStatements()) {\n if (Node.isFunctionDeclaration(stmt)) {\n if (stmt.getName() === name) return stmt;\n }\n if (Node.isVariableStatement(stmt)) {\n const decls = stmt.getDeclarationList().getDeclarations();\n if (decls[0]?.getName() === name) return stmt;\n }\n }\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers — signature extraction\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve a VariableStatement to its arrow/function initializer, or return\n * the node as-is if it's already a function/arrow node.\n */\nfunction resolveFunctionNode(node: Node): Node | undefined {\n if (\n Node.isFunctionDeclaration(node) ||\n Node.isFunctionExpression(node) ||\n Node.isArrowFunction(node)\n ) {\n return node;\n }\n\n if (Node.isVariableStatement(node)) {\n const decls = node.getDeclarationList().getDeclarations();\n if (decls.length === 0) return undefined;\n const init = decls[0]?.getInitializer();\n if (init !== undefined && (Node.isArrowFunction(init) || Node.isFunctionExpression(init))) {\n return init;\n }\n }\n\n return undefined;\n}\n\n/** Extract params from a function/arrow node. */\nfunction extractParams(node: Node): ExtractedParam[] {\n if (\n !Node.isFunctionDeclaration(node) &&\n !Node.isFunctionExpression(node) &&\n !Node.isArrowFunction(node)\n ) {\n return [];\n }\n\n const params = (node as { getParameters(): unknown[] }).getParameters();\n return (params as Node[]).map((p) => ({\n name: getParamName(p),\n // DEC-INTENT-STATIC-003: source-text only.\n typeAnnotation: getParamTypeAnnotation(p),\n }));\n}\n\n/** Extract parameter name, handling destructured and rest patterns. */\nfunction getParamName(param: Node): string {\n if (\n \"getNameNode\" in param &&\n typeof (param as { getNameNode(): unknown }).getNameNode === \"function\"\n ) {\n const nameNode = (param as { getNameNode(): Node }).getNameNode();\n if (Node.isObjectBindingPattern(nameNode) || Node.isArrayBindingPattern(nameNode)) {\n return nameNode.getText();\n }\n const isRest =\n \"getDotDotDotToken\" in param &&\n typeof (param as { getDotDotDotToken(): unknown }).getDotDotDotToken === \"function\" &&\n (param as { getDotDotDotToken(): unknown }).getDotDotDotToken() !== undefined;\n const baseName = nameNode.getText();\n return isRest ? `...${baseName}` : baseName;\n }\n return param.getText();\n}\n\n/** Extract the source-text type annotation from a parameter node. */\nfunction getParamTypeAnnotation(param: Node): string {\n if (\n \"getTypeNode\" in param &&\n typeof (param as { getTypeNode(): unknown }).getTypeNode === \"function\"\n ) {\n const typeNode = (param as { getTypeNode(): Node | undefined }).getTypeNode();\n return typeNode?.getText() ?? \"unknown\";\n }\n return \"unknown\";\n}\n\n/** Extract return-type annotation from a function-like node. */\nfunction extractReturnType(node: Node): string {\n if (\n \"getReturnTypeNode\" in node &&\n typeof (node as { getReturnTypeNode(): unknown }).getReturnTypeNode === \"function\"\n ) {\n const rtNode = (node as { getReturnTypeNode(): Node | undefined }).getReturnTypeNode();\n if (rtNode !== undefined) {\n const text = rtNode.getText();\n if (text === \"void\" || text === \"never\") return \"void\";\n return text;\n }\n }\n return \"unknown\";\n}\n\n/** Extract signature info from a MethodDeclaration. */\nfunction extractSignatureFromMethod(method: Node): ExtractedSignature {\n const params: ExtractedParam[] = [];\n if (\n \"getParameters\" in method &&\n typeof (method as { getParameters(): unknown[] }).getParameters === \"function\"\n ) {\n for (const p of (method as { getParameters(): Node[] }).getParameters()) {\n params.push({\n name: getParamName(p),\n typeAnnotation: getParamTypeAnnotation(p),\n });\n }\n }\n const returnTypeAnnotation = extractReturnType(method);\n const methodName =\n \"getName\" in method && typeof (method as { getName(): unknown }).getName === \"function\"\n ? ((method as { getName(): string | undefined }).getName() ?? \"method\")\n : \"method\";\n const paramNames = params.map((p) => p.name).join(\", \");\n const signatureString = `method ${methodName}(${paramNames}) -> ${returnTypeAnnotation}`;\n return { params, returnTypeAnnotation, signatureString };\n}\n\n/**\n * Build the deterministic signature string used as the `behavior` fallback.\n *\n * Format: \"<async? > <kind> <name>(<paramNames>) -> <returnType>\"\n * e.g. \"function add(a, b) -> number\"\n * \"async arrow const fetch(url) -> Promise<Response>\"\n *\n * DEC-INTENT-STATIC-BEHAVIOR-COLLAPSE-001: output is passed through\n * collapseWhitespace() so multi-line return-type annotations never produce\n * newlines in the behavior field.\n */\nfunction buildSignatureString(\n node: Node,\n params: readonly ExtractedParam[],\n returnTypeAnnotation: string,\n): string {\n const paramNames = params.map((p) => p.name).join(\", \");\n let raw: string;\n\n if (Node.isFunctionDeclaration(node)) {\n const isAsync = node.isAsync();\n const name = node.getName() ?? \"anonymous\";\n const asyncPfx = isAsync ? \"async \" : \"\";\n raw = `${asyncPfx}function ${name}(${paramNames}) -> ${returnTypeAnnotation}`;\n } else if (Node.isVariableStatement(node)) {\n const decls = node.getDeclarationList().getDeclarations();\n const firstDecl = decls[0];\n const varName = firstDecl?.getName() ?? \"anonymous\";\n const init = firstDecl?.getInitializer();\n const isAsync =\n init !== undefined &&\n \"isAsync\" in init &&\n typeof (init as { isAsync(): boolean }).isAsync === \"function\" &&\n (init as { isAsync(): boolean }).isAsync();\n const asyncPfx = isAsync ? \"async \" : \"\";\n const kindStr =\n init !== undefined && Node.isArrowFunction(init) ? \"arrow const\" : \"function const\";\n raw = `${asyncPfx}${kindStr} ${varName}(${paramNames}) -> ${returnTypeAnnotation}`;\n } else if (Node.isFunctionExpression(node)) {\n const isAsync = node.isAsync();\n const name = node.getName() ?? \"anonymous\";\n const asyncPfx = isAsync ? \"async \" : \"\";\n raw = `${asyncPfx}function ${name}(${paramNames}) -> ${returnTypeAnnotation}`;\n } else if (Node.isArrowFunction(node)) {\n const isAsync = node.isAsync();\n const asyncPfx = isAsync ? \"async \" : \"\";\n raw = `${asyncPfx}arrow(${paramNames}) -> ${returnTypeAnnotation}`;\n } else {\n raw = `anonymous(${paramNames}) -> ${returnTypeAnnotation}`;\n }\n\n // DEC-INTENT-STATIC-BEHAVIOR-COLLAPSE-001: collapse whitespace so that\n // multi-line return-type annotations never introduce newlines.\n return collapseWhitespace(raw);\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers — JSDoc extraction\n// ---------------------------------------------------------------------------\n\nfunction parseParamTag(tag: Node): { paramName: string; body: string } {\n if (\n \"getNameNode\" in tag &&\n typeof (tag as { getNameNode(): unknown }).getNameNode === \"function\"\n ) {\n const nameNode = (tag as { getNameNode(): Node | undefined }).getNameNode();\n if (nameNode !== undefined) {\n const paramName = nameNode.getText().replace(/^\\.\\.\\./, \"\");\n const comment = getTagBody(tag);\n const stripped = comment.replace(new RegExp(`^${escapeRegex(paramName)}\\\\s*`), \"\");\n return { paramName, body: collapseWhitespace(stripped) };\n }\n }\n\n const raw = getTagBody(tag);\n const match = /^(\\S+)\\s*(.*)$/s.exec(raw);\n if (match !== null) {\n const paramName = (match[1] ?? \"\").replace(/^\\.\\.\\./, \"\");\n return { paramName, body: collapseWhitespace(match[2] ?? \"\") };\n }\n return { paramName: \"\", body: \"\" };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers — text utilities\n// ---------------------------------------------------------------------------\n\n/** Collapse whitespace (newlines, tabs, multiple spaces) to single spaces and trim. */\nexport function collapseWhitespace(text: string): string {\n return text.replace(/\\s+/g, \" \").trim();\n}\n\n/**\n * Extract the first sentence from a JSDoc description.\n *\n * \"First sentence\" is the text up to the first `.`, `!`, or `?` followed by\n * whitespace (or end of string), collapsed and truncated to 197 chars + \"...\".\n */\nexport function extractFirstSentence(description: string): string {\n const collapsed = collapseWhitespace(description);\n const match = /^(.*?[.!?])(?:\\s|$)/s.exec(collapsed);\n const sentence = match !== null ? (match[1] ?? collapsed) : collapsed;\n if (sentence.length <= 200) return sentence;\n return `${sentence.slice(0, 197)}...`;\n}\n\n/** Get the body text of a JSDoc tag (everything after the tag name). */\nfunction getTagBody(tag: Node): string {\n if (\"getComment\" in tag && typeof (tag as { getComment(): unknown }).getComment === \"function\") {\n const comment = (tag as { getComment(): string | undefined }).getComment();\n return typeof comment === \"string\" ? comment.trim() : \"\";\n }\n const text = tag.getText();\n const m = /^@\\w+\\s*(.*)$/s.exec(text);\n return m !== null ? (m[1] ?? \"\").trim() : \"\";\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n","// SPDX-License-Identifier: MIT\n/**\n * Primary-declaration picker for TypeScript source files.\n *\n * Factored from packages/shave/src/intent/static-pick.ts as part of OD-2\n * Option A (DEC-EMBED-QUERY-ENRICH-HELPER-001): both the atomize path\n * (@yakcc/shave/intent/static-extract) and the query-enrichment path\n * (@yakcc/contracts/query-from-source) use this shared picker so the\n * \"which function is primary?\" logic is identical on both sides.\n *\n * @decision DEC-INTENT-STATIC-001\n * @title Primary-declaration picker: deterministic preference chain for static extraction\n * @status accepted\n * @rationale\n * Static intent extraction needs a single \"primary\" declaration to extract a\n * function signature and JSDoc from. The preference chain below is ordered by\n * strength of intent signal:\n *\n * 1. export default <function|arrow> — strongest signal: the author explicitly\n * named this the module's primary export.\n * 2. First exported FunctionDeclaration / VariableStatement-with-arrow — next\n * best: exported by name, likely the public API.\n * 3. First non-exported FunctionDeclaration — common pattern in utility modules.\n * 4. First non-exported VariableStatement with ArrowFunction/FunctionExpression —\n * covers `const f = () => ...` patterns.\n * 5. First ClassDeclaration's first method — for class-based modules.\n * 6. undefined — bare expression / pure statement block; caller handles this\n * as the \"no-declaration\" fallback case.\n *\n * Critical JSDoc gotcha: on `const f = () => ...`, JSDoc attaches to the\n * VariableStatement, NOT the inner ArrowFunction. The picker returns the\n * VariableStatement so the caller can call getJsDocs() on it directly.\n * Returning the ArrowFunction would miss all JSDoc on arrow-const declarations.\n */\n\nimport {\n type ClassDeclaration,\n type FunctionDeclaration,\n Node,\n type SourceFile,\n SyntaxKind,\n type VariableStatement,\n} from \"ts-morph\";\n\n/**\n * The primary declaration node (or undefined for bare expression blocks).\n *\n * Returns one of:\n * - FunctionDeclaration\n * - VariableStatement (for `const f = () => ...` and `export default const`)\n * - MethodDeclaration (first method of first ClassDeclaration)\n * - undefined\n *\n * NOTE: For arrow-const declarations, this returns the VariableStatement so\n * the caller can call Node.getJsDocs() on the statement (JSDoc attaches to the\n * VariableStatement, not the inner ArrowFunction).\n */\nexport type PrimaryDeclaration = Node | undefined;\n\n/**\n * Select the primary declaration from a parsed source file.\n *\n * Implements the deterministic preference chain described in DEC-INTENT-STATIC-001.\n *\n * @param sourceFile - ts-morph SourceFile to inspect.\n * @returns The primary Node or undefined if no declaration found.\n */\nexport function pickPrimaryDeclaration(sourceFile: SourceFile): PrimaryDeclaration {\n // ------------------------------------------------------------------\n // Priority 1: export default function / export default arrow-const\n // ------------------------------------------------------------------\n const defaultExport = sourceFile.getDefaultExportSymbol();\n if (defaultExport !== undefined) {\n const decls = defaultExport.getDeclarations();\n for (const decl of decls) {\n if (Node.isFunctionDeclaration(decl) || Node.isFunctionExpression(decl)) {\n return decl;\n }\n if (Node.isArrowFunction(decl)) {\n const parent = decl.getParent();\n const vs = getEnclosingVariableStatement(parent ?? decl);\n return vs ?? decl;\n }\n // export default expression (ExportAssignment) — skip to lower priorities\n }\n }\n\n const statements = sourceFile.getStatements();\n\n // ------------------------------------------------------------------\n // Priority 2: First exported FunctionDeclaration or exported\n // VariableStatement whose initializer is arrow/function expr\n // ------------------------------------------------------------------\n for (const stmt of statements) {\n if (Node.isFunctionDeclaration(stmt) && isExported(stmt)) {\n return stmt;\n }\n if (Node.isVariableStatement(stmt) && isExported(stmt)) {\n const initializer = getFirstInitializer(stmt);\n if (\n initializer !== undefined &&\n (Node.isArrowFunction(initializer) || Node.isFunctionExpression(initializer))\n ) {\n return stmt;\n }\n }\n }\n\n // ------------------------------------------------------------------\n // Priority 3: First non-exported FunctionDeclaration\n // ------------------------------------------------------------------\n for (const stmt of statements) {\n if (Node.isFunctionDeclaration(stmt)) {\n return stmt;\n }\n }\n\n // ------------------------------------------------------------------\n // Priority 4: First non-exported VariableStatement with arrow/function\n // ------------------------------------------------------------------\n for (const stmt of statements) {\n if (Node.isVariableStatement(stmt)) {\n const initializer = getFirstInitializer(stmt);\n if (\n initializer !== undefined &&\n (Node.isArrowFunction(initializer) || Node.isFunctionExpression(initializer))\n ) {\n return stmt;\n }\n }\n }\n\n // ------------------------------------------------------------------\n // Priority 5: First method of first ClassDeclaration\n // ------------------------------------------------------------------\n for (const stmt of statements) {\n if (Node.isClassDeclaration(stmt)) {\n const method = getFirstMethod(stmt);\n if (method !== undefined) return method;\n }\n }\n\n // ------------------------------------------------------------------\n // Priority 6: None\n // ------------------------------------------------------------------\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isExported(node: FunctionDeclaration | VariableStatement): boolean {\n return node.hasModifier(SyntaxKind.ExportKeyword);\n}\n\nfunction getFirstInitializer(stmt: VariableStatement): Node | undefined {\n const declarations = stmt.getDeclarationList().getDeclarations();\n if (declarations.length === 0) return undefined;\n return declarations[0]?.getInitializer();\n}\n\nfunction getEnclosingVariableStatement(node: Node): VariableStatement | undefined {\n let current: Node | undefined = node;\n while (current !== undefined && !Node.isSourceFile(current)) {\n if (Node.isVariableStatement(current)) return current;\n current = current.getParent();\n }\n return undefined;\n}\n\nfunction getFirstMethod(cls: ClassDeclaration): Node | undefined {\n for (const member of cls.getMembers()) {\n if (Node.isMethodDeclaration(member)) return member;\n }\n return undefined;\n}\n","// SPDX-License-Identifier: MIT\n/**\n * queryIntentCardFromSource — derive a QueryIntentCard from TypeScript source.\n *\n * @decision DEC-EMBED-QUERY-ENRICH-HELPER-001\n * @title Discovery queries enrich to match stored ContractSpec via a shared helper.\n * @status accepted\n * @rationale\n * The field-coverage asymmetry between store-side (full ContractSpec canonical JSON)\n * and query-side (historically behavior-only) is resolved here: given TypeScript\n * source, this helper derives the same structural fields the atomize/storeBlock path\n * produces, so cosine ranking operates on comparable vectors.\n *\n * Uses the shared source-extraction primitives from ./source-extract.ts (OD-2\n * Option A). Both this helper and @yakcc/shave's static-extract.ts ultimately\n * call the same extraction functions, making extraction-asymmetry structurally\n * harder to reintroduce (R1 mitigation from the plan risk register).\n *\n * Pure, deterministic, no I/O. Throws TypeError on parse failure or when the\n * requested entryFunction is not found. Absent-dimension rule (D1): fields that\n * cannot be derived are omitted from the returned QueryIntentCard.\n *\n * Cross-references:\n * - DEC-V3-DISCOVERY-D2-001: QueryIntentCard schema (unchanged by this helper)\n * - DEC-V3-IMPL-QUERY-001: canonicalizeQueryText projection rules (unchanged)\n * - plans/wi-fix-523-query-enrich-helper.md §3.2 (output mapping table)\n */\n\nimport { Node, Project, type SourceFile } from \"ts-morph\";\nimport type { QueryIntentCard, QueryTypeSignatureParam } from \"./canonicalize.js\";\nimport {\n extractJsDoc,\n extractSignatureFromNode,\n findExportedDeclarationByName,\n} from \"./source-extract.js\";\nimport { pickPrimaryDeclaration } from \"./source-pick.js\";\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Options for queryIntentCardFromSource.\n */\nexport interface QueryIntentCardFromSourceOptions {\n /**\n * Name of the exported function to derive from.\n * When absent, the first exported function in the source is used\n * (per the same preference chain as @yakcc/shave's static-extract:\n * export default > first exported fn/arrow > first non-exported fn >\n * first non-exported arrow-const > first class method).\n *\n * Throws TypeError if the named function is not found in the source.\n */\n readonly entryFunction?: string | undefined;\n}\n\n/**\n * Derive a QueryIntentCard from TypeScript source code.\n *\n * Pure: no I/O, no network, no file system access. Deterministic.\n * Throws TypeError on parse failure or when entryFunction is specified\n * but not found.\n *\n * Field derivation (D1 absent-dimension rule — omit what cannot be derived):\n * behavior — JSDoc summary → signature string → fragment fallback\n * signature.inputs — TS parameter types (name optional, type required)\n * signature.outputs — TS return type\n * errorConditions — JSDoc @throws / @throw / @exception tags\n * guarantees — omitted (no static derivation path in P0)\n * nonFunctional — omitted (no static derivation path in P0)\n * propertyTests — omitted (callers don't have these from source alone)\n *\n * The extraction uses the SAME primitives as @yakcc/shave's atomize path\n * (DEC-EMBED-QUERY-ENRICH-HELPER-001 OD-2 Option A): given identical source,\n * the behavior/signature fields here are byte-identical to what specFromIntent()\n * would store.\n *\n * @param source - Raw TypeScript/JavaScript source text.\n * @param options - Optional: entryFunction name to target.\n * @returns A QueryIntentCard with all derivable fields populated.\n * @throws TypeError if the source fails to parse.\n * @throws TypeError if entryFunction is specified and not found in source.\n * @throws TypeError if source has no function declarations and entryFunction is absent.\n */\nexport function queryIntentCardFromSource(\n source: string,\n options?: QueryIntentCardFromSourceOptions,\n): QueryIntentCard {\n // Parse in-memory with ts-morph.\n // DEC-INTENT-STATIC-003: no type-checker, no lib loading (~5ms vs ~200ms).\n const project = new Project({\n useInMemoryFileSystem: true,\n compilerOptions: {\n strict: false,\n allowJs: true,\n noLib: true,\n },\n });\n\n let sourceFile: SourceFile;\n try {\n sourceFile = project.createSourceFile(\"__query_from_source__.ts\", source);\n } catch (err) {\n throw new TypeError(\n `queryIntentCardFromSource: failed to parse source: ${String(err)}`,\n );\n }\n\n // Select the target declaration node.\n let declarationNode: import(\"ts-morph\").Node | undefined;\n\n if (options?.entryFunction !== undefined) {\n // Explicit entryFunction: look it up by name.\n declarationNode = findExportedDeclarationByName(sourceFile, options.entryFunction);\n if (declarationNode === undefined) {\n throw new TypeError(\n `queryIntentCardFromSource: entryFunction \"${options.entryFunction}\" not found in source. ` +\n `Available declarations: ${listDeclarationNames(sourceFile).join(\", \") || \"(none)\"}`,\n );\n }\n } else {\n // No entryFunction: use the primary-declaration preference chain.\n declarationNode = pickPrimaryDeclaration(sourceFile);\n if (declarationNode === undefined) {\n throw new TypeError(\n \"queryIntentCardFromSource: source contains no function declarations. \" +\n \"Pass TypeScript source with at least one exported or top-level function.\",\n );\n }\n }\n\n // Extract signature and JSDoc from the selected node.\n const sig = extractSignatureFromNode(declarationNode);\n const jsdoc = extractJsDoc(declarationNode);\n\n // Behavior field: JSDoc summary → signature string → fragment fallback.\n const behavior: string =\n jsdoc.summary ??\n sig.signatureString ??\n buildFragmentFallback(sourceFile.getStatements().length, source.length);\n\n // Build the QueryIntentCard, omitting absent dimensions (D1 rule).\n // behavior dimension — always present (has at least a fragment fallback).\n const card: Record<string, unknown> = { behavior };\n\n // signature dimension — only when there are params or a non-unknown return type.\n const inputs: QueryTypeSignatureParam[] = sig.params.map((p) => {\n const entry: { name?: string; type: string } = { type: p.typeAnnotation };\n if (p.name !== \"\") {\n entry.name = p.name;\n }\n return entry;\n });\n const outputs: QueryTypeSignatureParam[] =\n sig.returnTypeAnnotation !== \"unknown\"\n ? [{ name: \"result\", type: sig.returnTypeAnnotation }]\n : [];\n\n if (inputs.length > 0 || outputs.length > 0) {\n const signatureDim: {\n inputs?: readonly QueryTypeSignatureParam[];\n outputs?: readonly QueryTypeSignatureParam[];\n } = {};\n if (inputs.length > 0) signatureDim.inputs = inputs;\n if (outputs.length > 0) signatureDim.outputs = outputs;\n card.signature = signatureDim;\n }\n\n // errorConditions dimension — from @throws / @throw / @exception tags (D1: omit if none).\n if (jsdoc.throwDescriptions.length > 0) {\n card.errorConditions = jsdoc.throwDescriptions;\n }\n\n // guarantees, nonFunctional, propertyTests — omitted in P0 (no static derivation path).\n\n return card as unknown as QueryIntentCard;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction buildFragmentFallback(stmtCount: number, byteCount: number): string {\n return `source fragment (${stmtCount} statements, ${byteCount} bytes)`;\n}\n\n/**\n * Collect all declaration names from a source file for error messages.\n */\nfunction listDeclarationNames(sourceFile: SourceFile): string[] {\n const names: string[] = [];\n for (const stmt of sourceFile.getStatements()) {\n if (Node.isFunctionDeclaration(stmt)) {\n const name = stmt.getName();\n if (name !== undefined) names.push(name);\n }\n if (Node.isVariableStatement(stmt)) {\n for (const decl of stmt.getDeclarationList().getDeclarations()) {\n names.push(decl.getName());\n }\n }\n }\n return [...new Set(names)];\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-IDENTITY-005: Contract identity is the hash of the canonicalized\n// contract spec; verification evidence is separate, mutable metadata.\n// Status: decided (MASTER_PLAN.md)\n// Rationale: Identity must be immutable so references are stable; trust evidence\n// must be mutable so monotonic improvement is possible. Conflating them breaks both.\n\n// @decision DEC-NO-OWNERSHIP-011: No author identity, no signatures, no reserved\n// columns for either in any type or schema in this package.\n// Status: decided (MASTER_PLAN.md)\n// Rationale: Cornerstone. The registry is a public-domain commons.\n\n// ---------------------------------------------------------------------------\n// Branded primitive types\n// ---------------------------------------------------------------------------\n\n/**\n * A stable content-address identifying a ContractSpec by the hash of its\n * canonical form. Two specs with identical canonical representations share an id.\n *\n * The brand prevents accidental substitution of bare strings for contract ids.\n * Derived via BLAKE3-256 over the canonicalized UTF-8 bytes of the ContractSpec.\n * Format: 64 lowercase hex characters (32 bytes).\n */\nexport type ContractId = string & { readonly __brand: \"ContractId\" };\n\n// ---------------------------------------------------------------------------\n// Non-functional property types\n// ---------------------------------------------------------------------------\n\n/** Purity classification for a basic block. */\nexport type Purity =\n | \"pure\" // referentially transparent; no side effects\n | \"io\" // performs I/O but is otherwise deterministic\n | \"stateful\" // reads or writes mutable state\n | \"nondeterministic\"; // result may differ across calls with identical inputs\n\n/** Thread-safety classification for a basic block. */\nexport type ThreadSafety =\n | \"safe\" // safe to call concurrently without external synchronization\n | \"unsafe\" // requires external synchronization\n | \"sequential\"; // must be called on a single dedicated thread\n\n/**\n * Non-functional properties of a basic block. All fields are optional to allow\n * partial declarations; absence means \"unspecified,\" not \"unconstrained.\"\n */\nexport interface NonFunctionalProperties {\n /** Big-O time complexity as a free-form string, e.g. \"O(n)\". */\n readonly time?: string | undefined;\n /** Big-O space complexity as a free-form string, e.g. \"O(1)\". */\n readonly space?: string | undefined;\n /** Purity classification. */\n readonly purity: Purity;\n /** Thread-safety classification. */\n readonly threadSafety: ThreadSafety;\n}\n\n// ---------------------------------------------------------------------------\n// Contract spec sub-types\n// ---------------------------------------------------------------------------\n\n/**\n * A named, typed parameter or return value in a contract.\n * Types are expressed as free-form TypeScript type strings in v0; WI-004\n * introduces a structured AST representation when the IR validator lands.\n */\nexport interface TypeSignature {\n readonly name: string;\n readonly type: string;\n /** Optional description of this parameter's role. */\n readonly description?: string | undefined;\n}\n\n/** A declared behavioral guarantee for a basic block. */\nexport interface BehavioralGuarantee {\n /** Short identifier for the guarantee, e.g. \"idempotent\". */\n readonly id: string;\n /** Human-readable description of what is guaranteed. */\n readonly description: string;\n}\n\n/** A declared error condition — when and what the block throws or rejects with. */\nexport interface ErrorCondition {\n /** Human-readable description of the error condition. */\n readonly description: string;\n /** The error type thrown or rejected, as a TS type string, e.g. \"SyntaxError\". */\n readonly errorType?: string | undefined;\n}\n\n/**\n * A property test case that exercises the block's contract.\n * Each test case is expressed as a fast-check property description; WI-006\n * provides the runner.\n */\nexport interface PropertyTestCase {\n /** Short identifier for this test case, e.g. \"round-trips through parse\". */\n readonly id: string;\n /** Human-readable description of what property is tested. */\n readonly description: string;\n /** fast-check arbitrary expressions used to generate inputs, as strings. */\n readonly arbitraries?: readonly string[] | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// ContractSpec — the core declaration\n// ---------------------------------------------------------------------------\n\n/**\n * The complete behavioral specification of a basic block.\n *\n * A ContractSpec is the unit of identity in Yakcc: the hash of its canonical\n * form is the ContractId. Two specs that are semantically equivalent but\n * canonicalize differently will receive distinct ids — canonicalization is\n * the equivalence relation, not semantic reasoning.\n *\n * All fields that affect identity are `readonly`. Verification evidence that\n * may evolve over time lives on `VerificationEvidence`, not here.\n */\nexport interface ContractSpec {\n /** Input parameters. */\n readonly inputs: readonly TypeSignature[];\n /** Output parameters. */\n readonly outputs: readonly TypeSignature[];\n /** Natural-language behavioral description. This is searchable and embedded. */\n readonly behavior: string;\n /** Declared behavioral guarantees. */\n readonly guarantees: readonly BehavioralGuarantee[];\n /** Declared error conditions. */\n readonly errorConditions: readonly ErrorCondition[];\n /** Non-functional properties. */\n readonly nonFunctional: NonFunctionalProperties;\n /** Property test cases that any conforming implementation must pass. */\n readonly propertyTests: readonly PropertyTestCase[];\n}\n\n// ---------------------------------------------------------------------------\n// Verification evidence — mutable metadata attached to an immutable id\n// ---------------------------------------------------------------------------\n\n/**\n * Mutable metadata attached to a ContractId describing what verification has\n * been performed against implementations that claim to satisfy this contract.\n *\n * Evidence grows monotonically — entries are appended, never removed. The\n * schema intentionally carries no author identity or signature fields.\n * DEC-NO-OWNERSHIP-011.\n */\nexport interface VerificationEvidence {\n /** History of test runs against implementations registered under this contract. */\n readonly testHistory: readonly TestHistoryEntry[];\n}\n\n/** One recorded test run. */\nexport interface TestHistoryEntry {\n /** ISO-8601 timestamp of the test run. */\n readonly runAt: string;\n /** Whether all property tests passed. */\n readonly passed: boolean;\n /** Number of property test cases exercised. */\n readonly caseCount: number;\n}\n\n// ---------------------------------------------------------------------------\n// Contract — id + spec + evidence\n// ---------------------------------------------------------------------------\n\n/**\n * A complete contract record: a stable id, the spec it was derived from, and\n * accumulated verification evidence.\n */\nexport interface Contract {\n readonly id: ContractId;\n readonly spec: ContractSpec;\n readonly evidence: VerificationEvidence;\n}\n\n// ---------------------------------------------------------------------------\n// Proposal result — discriminated union\n// ---------------------------------------------------------------------------\n\n/** The proposal was accepted as a new contract. */\nexport interface ProposalAccepted {\n readonly status: \"accepted\";\n readonly id: ContractId;\n}\n\n/**\n * The proposal matched an existing contract in the registry.\n * The caller should use the existing id rather than registering a new one.\n */\nexport interface ProposalMatched {\n readonly status: \"matched\";\n readonly id: ContractId;\n /** Similarity score in [0, 1] where 1.0 is an exact canonical match. */\n readonly score: number;\n}\n\n/** Discriminated union of outcomes from submitting a contract proposal. */\nexport type ProposalResult = ProposalAccepted | ProposalMatched;\n\n// ---------------------------------------------------------------------------\n// Proposal submission (facade — WI-003 connects this to the live registry)\n// ---------------------------------------------------------------------------\n\nexport { contractId } from \"./contract-id.js\";\n\n/**\n * Submit a ContractSpec as a proposal.\n *\n * Returns a ProposalResult indicating whether the spec matched an existing\n * contract in the registry or was accepted as new. v0 always returns \"accepted\"\n * using the derived ContractId; WI-003 connects this to the live registry for\n * real match detection.\n */\nexport async function proposeContract(spec: ContractSpec): Promise<ProposalResult> {\n const { contractId } = await import(\"./contract-id.js\");\n return {\n status: \"accepted\",\n id: contractId(spec),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Re-exports from sub-modules\n// ---------------------------------------------------------------------------\n\nexport {\n type Granularity,\n DEFAULT_GRANULARITY,\n MIN_GRANULARITY,\n MAX_GRANULARITY,\n parseGranularity,\n} from \"./granularity.js\";\nexport {\n canonicalize,\n canonicalizeText,\n canonicalizeQueryText,\n type QueryIntentCard,\n type QueryTypeSignatureParam,\n} from \"./canonicalize.js\";\nexport {\n contractIdFromBytes,\n isValidContractId,\n} from \"./contract-id.js\";\nexport {\n type EmbeddingProvider,\n LOCAL_KNOWN_MODELS,\n createLocalEmbeddingProvider,\n createOfflineEmbeddingProvider,\n generateEmbedding,\n} from \"./embeddings.js\";\nexport { type SpecHash, type SpecYak, type SpecYakParameter, validateSpecYak } from \"./spec-yak.js\";\nexport {\n type ProofArtifact,\n type ProofManifest,\n type ArtifactKind,\n validateProofManifestL0,\n} from \"./proof-manifest.js\";\nexport {\n type BlockTriplet,\n type LocalTriplet,\n type ForeignTripletFields,\n type BlockMerkleRoot,\n blockMerkleRoot,\n specHash,\n isLocalTriplet,\n isForeignTriplet,\n} from \"./merkle.js\";\nexport {\n canonicalAstHash,\n CanonicalAstParseError,\n type CanonicalAstHash,\n} from \"./canonical-ast.js\";\nexport {\n queryIntentCardFromSource,\n type QueryIntentCardFromSourceOptions,\n} from \"./query-from-source.js\";\n// Sub-path extraction primitives — re-exported from the barrel so that\n// workspace consumers that alias @yakcc/contracts → src/index.ts (vitest\n// workspace-source mode) can import these via \"@yakcc/contracts\" directly\n// without needing additional sub-path aliases in their vitest configs.\nexport {\n extractJsDoc,\n extractSignatureFromNode,\n findExportedDeclarationByName,\n type ExtractedJsDoc,\n type ExtractedParam,\n type ExtractedSignature,\n} from \"./source-extract.js\";\nexport { pickPrimaryDeclaration, type PrimaryDeclaration } from \"./source-pick.js\";\n","// SPDX-License-Identifier: MIT\n// SPDX-License-Identifier: MIT\n// @decision DEC-STORAGE-009: SQLite + sqlite-vec for registry storage.\n// Status: decided (MASTER_PLAN.md DEC-STORAGE-009)\n// Rationale: Single-file local store; vector index (vec0 virtual table) in the\n// same DB; embeds cleanly into a CLI. Federation in v1 layers on top, not under.\n\n// @decision DEC-NO-OWNERSHIP-011: No author, author_email, signature, or any\n// ownership-shaped column in any table. This is a hard project invariant.\n// Status: decided (MASTER_PLAN.md DEC-NO-OWNERSHIP-011)\n\n// @decision DEC-SCHEMA-VEC0-001: Use sqlite-vec vec0 virtual table for the\n// vector index. Status: decided (WI-003)\n// Rationale: vec0 supports arbitrary-length FLOAT vectors and KNN queries\n// via \"WHERE embedding MATCH ? AND k = N ORDER BY distance\". It co-locates the\n// vector index with the relational tables in one file, which is the v0 goal.\n\n// @decision DEC-SCHEMA-MIGRATION-002: Migration 1→2 is a clean re-create.\n// Status: decided (WI-T03, MASTER_PLAN.md DEC-TRIPLET-IDENTITY-020)\n// Rationale: The seed corpus has not been published externally, so re-deriving\n// BlockMerkleRoot is acceptable. The v0 `contracts` and `implementations` tables\n// are dropped entirely; no transition view or dual-table coexistence is allowed\n// (Sacred Practice #12, Evaluation Contract forbidden shortcuts).\n\n/**\n * @decision DEC-V2-FOREIGN-BLOCK-SCHEMA-001 (sub-A: column-on-blocks form)\n * @status decided (WI-V2-04 L2)\n * @rationale Foreign blocks live as discriminated rows on the existing blocks\n * table per Sacred Practice #12 (single source of truth) — NO\n * parallel foreign_blocks table (I-X6). Foreign-specific fields are\n * nullable columns; the kind discriminator + storage-layer guard\n * enforce the invariant: kind='foreign' => foreign_pkg IS NOT NULL\n * AND foreign_export IS NOT NULL (L2-I3). The DEFAULT 'local' covers\n * all pre-v6 rows without a backfill UPDATE (forbidden shortcut).\n * The block_foreign_refs table tracks per-block foreign-dependency\n * trees for the provenance manifest (L2 schema primitive; population\n * deferred to L4 CLI policy layer). SCHEMA_VERSION bumped 5 → 6.\n * @scope WI-V2-04 L2; foreign-row insertion paths land in L4 (CLI policy).\n */\n\n/**\n * @decision DEC-V2-REGISTRY-SCHEMA-BUMP-001\n * @title SCHEMA_VERSION 6 → 7 for source-file provenance and workspace-plumbing table\n * @status decided (WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P1)\n * @rationale P1 adds three nullable provenance columns to the blocks table\n * (source_pkg, source_file, source_offset) and a new workspace_plumbing table.\n * Migration is single-phase pure DDL (no business-logic backfill): new columns\n * default to NULL for all pre-v7 rows; canonical-corpus provenance is populated\n * by the bootstrap re-run, not an in-migration UPDATE statement.\n * `openRegistry()` runs the migration on schema-version-bump (existing pattern).\n */\n\n/**\n * Current schema version. Increment by 1 whenever a migration is added.\n * The `schema_version` table stores the applied version; `applyMigrations`\n * no-ops when `currentVersion >= SCHEMA_VERSION`.\n *\n * L2-I2 invariant: this constant must equal the highest MIGRATION_N_DDL number\n * (currently 10 after the v9 → v10 migration adding source_file_state table;\n * DEC-V2-REGISTRY-SCHEMA-BUMP-V10-001 / issue #363).\n *\n * @decision DEC-V2-REGISTRY-SCHEMA-BUMP-V9-001\n * @title SCHEMA_VERSION 8 → 9; single-phase additive migration adding block_occurrences table\n * @status decided (WI-V2-STORAGE-IDEMPOTENT-RECOMPILE #355)\n * @rationale Pure DDL addition; no backfill required (new table starts empty;\n * bootstrap re-run populates block_occurrences for each file processed).\n * Atom content (blocks table) stays monotonic with INSERT OR IGNORE.\n * Atom occurrences (block_occurrences) are refreshed per-file on every\n * bootstrap pass via atomic delete-then-insert (DEC-V2-OCCURRENCE-DELETE-INSERT-001).\n *\n * @decision DEC-V2-REGISTRY-SCHEMA-BUMP-V10-001\n * @title SCHEMA_VERSION 9 → 10; single-phase additive migration adding source_file_state table\n * @status decided (issue #363 / wi-363-shave-cache)\n * @rationale Pure DDL addition; no backfill required (new table starts empty;\n * rows accrete on each cache-miss shave write during bootstrap). The table\n * stores per-file BLAKE3-256 content hashes enabling the bootstrap fast path\n * to skip re-shaving unchanged files (DEC-V2-SHAVE-CACHE-STORAGE-001).\n * Two-phase migration is over-engineered: starting empty, no partial-state risk.\n */\nexport const SCHEMA_VERSION = 10;\n\n// ---------------------------------------------------------------------------\n// Migration 0 → 1: initial schema (v0)\n// ---------------------------------------------------------------------------\n\n/**\n * SQL statements that constitute migration 1 (the initial v0 schema).\n * Applied on fresh DBs to bring them from version 0 to version 1.\n * On a DB that was already at v1, the `IF NOT EXISTS` guards are no-ops;\n * migration 2 then drops the v0 tables and replaces them.\n *\n * No ownership-shaped columns anywhere — DEC-NO-OWNERSHIP-011.\n */\nconst MIGRATION_1: readonly string[] = [\n // Version tracking table\n `CREATE TABLE IF NOT EXISTS schema_version (\n version INTEGER NOT NULL\n )`,\n\n \"INSERT OR IGNORE INTO schema_version(version) VALUES (0)\",\n\n // ---------------------------------------------------------------------------\n // contracts — content-addressed contract storage (v0; dropped in migration 2)\n // ---------------------------------------------------------------------------\n `CREATE TABLE IF NOT EXISTS contracts (\n id TEXT PRIMARY KEY,\n canonical_bytes BLOB NOT NULL,\n spec_json TEXT NOT NULL,\n created_at INTEGER NOT NULL\n )`,\n\n // ---------------------------------------------------------------------------\n // implementations — basic blocks linked to a contract (v0; dropped in migration 2)\n // ---------------------------------------------------------------------------\n `CREATE TABLE IF NOT EXISTS implementations (\n id TEXT PRIMARY KEY,\n contract_id TEXT NOT NULL REFERENCES contracts(id),\n source TEXT NOT NULL,\n created_at INTEGER NOT NULL\n )`,\n\n `CREATE INDEX IF NOT EXISTS idx_implementations_contract_id\n ON implementations(contract_id)`,\n\n // ---------------------------------------------------------------------------\n // contract_embeddings — sqlite-vec virtual table (vec0) keyed on contract_id\n // Dropped and re-created in migration 2 keyed on spec_hash.\n // ---------------------------------------------------------------------------\n `CREATE VIRTUAL TABLE IF NOT EXISTS contract_embeddings USING vec0(\n contract_id TEXT PRIMARY KEY,\n embedding FLOAT[384]\n )`,\n\n // ---------------------------------------------------------------------------\n // test_history — verification evidence per contract (v0 shape; updated in migration 2)\n // ---------------------------------------------------------------------------\n `CREATE TABLE IF NOT EXISTS test_history (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n contract_id TEXT NOT NULL REFERENCES contracts(id),\n suite_id TEXT NOT NULL,\n passed INTEGER NOT NULL CHECK(passed IN (0, 1)),\n at INTEGER NOT NULL\n )`,\n\n `CREATE INDEX IF NOT EXISTS idx_test_history_contract_id\n ON test_history(contract_id)`,\n\n // ---------------------------------------------------------------------------\n // runtime_exposure — provenance for runtime/production exposure (v0 shape)\n // ---------------------------------------------------------------------------\n `CREATE TABLE IF NOT EXISTS runtime_exposure (\n contract_id TEXT PRIMARY KEY REFERENCES contracts(id),\n requests_seen INTEGER NOT NULL DEFAULT 0,\n last_seen INTEGER\n )`,\n\n // ---------------------------------------------------------------------------\n // strictness_edges — declared partial ordering (v0 shape; updated in migration 2)\n // ---------------------------------------------------------------------------\n `CREATE TABLE IF NOT EXISTS strictness_edges (\n stricter_id TEXT NOT NULL REFERENCES contracts(id),\n looser_id TEXT NOT NULL REFERENCES contracts(id),\n created_at INTEGER NOT NULL,\n PRIMARY KEY (stricter_id, looser_id)\n )`,\n\n `CREATE INDEX IF NOT EXISTS idx_strictness_edges_looser_id\n ON strictness_edges(looser_id)`,\n];\n\n// ---------------------------------------------------------------------------\n// Migration 1 → 2: replace (contracts, implementations) with blocks\n// ---------------------------------------------------------------------------\n\n/**\n * SQL statements for migration 2.\n *\n * This is a clean re-create (DEC-SCHEMA-MIGRATION-002 / Sacred Practice #12):\n * - Drop the v0 `contracts`, `implementations`, and the v0-shaped tables that\n * referenced them (`test_history`, `runtime_exposure`, `strictness_edges`).\n * - Drop and re-create `contract_embeddings` keyed on `spec_hash` instead of\n * `contract_id` (two blocks sharing a spec share an embedding).\n * - Create the new `blocks` table keyed by `block_merkle_root`.\n * - Re-create the ancillary tables referencing `block_merkle_root`.\n *\n * Foreign-key enforcement is assumed to be enabled before this migration runs\n * (storage.ts enables it via `PRAGMA foreign_keys = ON`). HOWEVER: we must\n * disable FKs temporarily to drop tables in the correct order, because\n * `strictness_edges`, `test_history`, and `runtime_exposure` reference\n * `contracts` which we want to drop. We use `PRAGMA foreign_keys = OFF` and\n * restore it after so that the drop sequence is order-independent.\n *\n * Execution order:\n * 1. Disable FKs (for drop safety)\n * 2. Drop dependent v0 tables: strictness_edges, test_history, runtime_exposure\n * 3. Drop v0 embeddings virtual table\n * 4. Drop v0 implementations table + index\n * 5. Drop v0 contracts table\n * 6. Create new blocks table + spec_hash index\n * 7. Re-create contract_embeddings keyed on spec_hash\n * 8. Re-create test_history, runtime_exposure, strictness_edges on block_merkle_root\n * 9. Re-enable FKs\n */\nconst MIGRATION_2: readonly string[] = [\n // Step 1: disable FK enforcement for the drop sequence.\n \"PRAGMA foreign_keys = OFF\",\n\n // Step 2: drop ancillary v0 tables that reference contracts.\n \"DROP TABLE IF EXISTS strictness_edges\",\n \"DROP INDEX IF EXISTS idx_strictness_edges_looser_id\",\n \"DROP TABLE IF EXISTS runtime_exposure\",\n \"DROP TABLE IF EXISTS test_history\",\n \"DROP INDEX IF EXISTS idx_test_history_contract_id\",\n\n // Step 3: drop v0 embeddings virtual table.\n \"DROP TABLE IF EXISTS contract_embeddings\",\n\n // Step 4: drop v0 implementations table.\n \"DROP INDEX IF EXISTS idx_implementations_contract_id\",\n \"DROP TABLE IF EXISTS implementations\",\n\n // Step 5: drop v0 contracts table.\n \"DROP TABLE IF EXISTS contracts\",\n\n // Step 6: create new blocks table.\n // block_merkle_root: BLAKE3(spec_hash || impl_hash || proof_root) — hex, 64 chars.\n // spec_hash: BLAKE3(canonicalize(spec.yak)) — hex, 64 chars, not unique.\n // spec_canonical_bytes: stored to avoid re-canonicalization on read.\n // impl_source: impl.ts text.\n // proof_manifest_json: manifest.json serialized.\n // level: L0/L1/L2/L3 per the block's declared verification level.\n // created_at: Unix epoch milliseconds.\n // No ownership-shaped columns — DEC-NO-OWNERSHIP-011.\n `CREATE TABLE IF NOT EXISTS blocks (\n block_merkle_root TEXT PRIMARY KEY,\n spec_hash TEXT NOT NULL,\n spec_canonical_bytes BLOB NOT NULL,\n impl_source TEXT NOT NULL,\n proof_manifest_json TEXT NOT NULL,\n level TEXT NOT NULL CHECK(level IN ('L0','L1','L2','L3')),\n created_at INTEGER NOT NULL\n )`,\n\n \"CREATE INDEX IF NOT EXISTS idx_blocks_spec_hash ON blocks(spec_hash)\",\n\n // Step 7: re-create contract_embeddings keyed on spec_hash.\n // Two blocks with the same spec_hash share a spec, so they share an embedding.\n // Embedding dimensionality 384 matches Xenova/all-MiniLM-L6-v2 (DEC-EMBED-010).\n `CREATE VIRTUAL TABLE IF NOT EXISTS contract_embeddings USING vec0(\n spec_hash TEXT PRIMARY KEY,\n embedding FLOAT[384]\n )`,\n\n // Step 8: re-create ancillary tables referencing block_merkle_root.\n\n // test_history — verification evidence per block.\n // block_merkle_root: FK → blocks.block_merkle_root.\n `CREATE TABLE IF NOT EXISTS test_history (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n block_merkle_root TEXT NOT NULL REFERENCES blocks(block_merkle_root),\n suite_id TEXT NOT NULL,\n passed INTEGER NOT NULL CHECK(passed IN (0, 1)),\n at INTEGER NOT NULL\n )`,\n\n `CREATE INDEX IF NOT EXISTS idx_test_history_block_merkle_root\n ON test_history(block_merkle_root)`,\n\n // runtime_exposure — production usage counts per block.\n // block_merkle_root: FK → blocks.block_merkle_root.\n `CREATE TABLE IF NOT EXISTS runtime_exposure (\n block_merkle_root TEXT PRIMARY KEY REFERENCES blocks(block_merkle_root),\n requests_seen INTEGER NOT NULL DEFAULT 0,\n last_seen INTEGER\n )`,\n\n // strictness_edges — declared partial ordering between block specs.\n // stricter/looser reference block_merkle_root values; the edge means\n // the block at stricter_root satisfies a strictly stronger spec than the\n // block at looser_root.\n `CREATE TABLE IF NOT EXISTS strictness_edges (\n stricter_root TEXT NOT NULL REFERENCES blocks(block_merkle_root),\n looser_root TEXT NOT NULL REFERENCES blocks(block_merkle_root),\n created_at INTEGER NOT NULL,\n PRIMARY KEY (stricter_root, looser_root)\n )`,\n\n `CREATE INDEX IF NOT EXISTS idx_strictness_edges_looser_root\n ON strictness_edges(looser_root)`,\n\n // Step 9: re-enable FK enforcement.\n \"PRAGMA foreign_keys = ON\",\n];\n\n// ---------------------------------------------------------------------------\n// Migration 2 → 3: add canonical_ast_hash column + index\n// ---------------------------------------------------------------------------\n\n// @decision DEC-REGISTRY-AST-HASH-002: Add canonical_ast_hash to blocks table.\n// Status: decided (WI-012-02)\n// Rationale: The canonical AST hash allows deduplication and cross-spec reuse\n// detection: two impls that are semantically equivalent under AST\n// canonicalization share this hash even if their source text differs. Stored\n// as a TEXT column (64 hex chars) with a non-unique index to support\n// findByCanonicalAstHash lookups. Default '' on ADD COLUMN is required by\n// SQLite (not null columns added via ALTER TABLE must have a default); the\n// backfill walk then fills in the real values before bumping the version.\n// The migration is idempotent: re-running on a v3 DB is a no-op.\n\n/**\n * SQL statements for migration 3.\n *\n * Adds `canonical_ast_hash TEXT NOT NULL DEFAULT ''` to the `blocks` table\n * and creates a non-unique index on it.\n *\n * SQLite requires a default value for NOT NULL columns added via ALTER TABLE.\n * The empty string default is a sentinel; the backfill in `applyMigrations`\n * immediately walks every existing row and updates it with the real hash\n * before bumping schema_version to 3.\n *\n * On a fresh DB (no existing rows) the backfill walk is a no-op.\n */\nconst MIGRATION_3_DDL: readonly string[] = [\n `ALTER TABLE blocks ADD COLUMN canonical_ast_hash TEXT NOT NULL DEFAULT ''`,\n \"CREATE INDEX IF NOT EXISTS idx_blocks_canonical_ast_hash ON blocks(canonical_ast_hash)\",\n];\n\n// ---------------------------------------------------------------------------\n// Migration 3 → 4: add parent_block_root column + index\n// ---------------------------------------------------------------------------\n\n// @decision DEC-REGISTRY-PARENT-BLOCK-004: Add parent_block_root to blocks table.\n// Status: decided (WI-014-04)\n// Rationale: Provenance manifest must surface parent-block lineage for atoms shaved\n// from a recursion tree (v0.7 acceptance item (e)). The column is NULL for root\n// blocks (hand-authored seeds, shave's top-level proposals) and non-NULL for\n// atoms that were shaved from a parent block. Population (passing parent-block\n// hashes through shave persistence) is a follow-up; for now the column always\n// stores NULL. Indexed for O(log n) lineage walks.\n// The migration is idempotent: a try/catch on the duplicate-column error handles\n// partial-migration recovery (crash between ADD COLUMN and the version bump).\n\n/**\n * SQL statements for migration 4.\n *\n * Adds `parent_block_root TEXT NULL` to the `blocks` table and creates a\n * non-unique index on it to support lineage walks.\n *\n * NULL means \"this block is the root of its recursion tree\" (e.g. hand-authored\n * seed blocks, or shave's top-level proposal). A non-NULL value is the\n * BlockMerkleRoot of the recursion-tree parent from which this atom was shaved.\n *\n * Unlike migration 3 (which requires a backfill via a business-logic function),\n * migration 4 is purely DDL — the column defaults to NULL for all existing rows,\n * which is the correct sentinel value. No backfill is needed, so `applyMigrations`\n * bumps `schema_version` to 4 directly (no two-phase split like migration 3).\n */\nconst MIGRATION_4_DDL: readonly string[] = [\n // parent_block_root: BlockMerkleRoot of the recursion-tree parent for shaved atoms.\n // NULL means \"this is the root of its recursion tree\" (e.g. hand-authored seed\n // blocks, or shave's top-level proposal). Indexed for quick lineage walks.\n \"ALTER TABLE blocks ADD COLUMN parent_block_root TEXT NULL\",\n \"CREATE INDEX IF NOT EXISTS idx_blocks_parent_block_root ON blocks(parent_block_root)\",\n];\n\n// ---------------------------------------------------------------------------\n// Migration 4 → 5: add block_artifacts table (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002)\n// ---------------------------------------------------------------------------\n\n// @decision DEC-V1-FEDERATION-WIRE-ARTIFACTS-002: Add block_artifacts table.\n// Status: decided (MASTER_PLAN.md WI-022)\n// Rationale: BlockTripletRow.artifacts closes the gap between the contracts\n// blockMerkleRoot() formula (which has folded artifact bytes since v0.6) and\n// the storage/shave persist path (which computed the formula with bytes but\n// then dropped them). The table is keyed by (block_merkle_root, path) with a\n// composite PRIMARY KEY, matching the manifest's declared path set.\n// declaration_index preserves manifest order for deterministic Map hydration.\n//\n// Migration note: pre-WI-022 rows had their merkle root computed against\n// whatever artifact bytes the persister had at write time. Back-deriving bytes\n// for those rows risks producing a different Map and invalidating the stored\n// root. The migration therefore backfills zero rows per pre-existing block\n// (empty Map at hydrate time). Pre-WI-022 rows are NOT federation-eligible\n// by construction (wire integrity gate will reject them); they are NOT\n// corrupted in the local registry.\n//\n// Idempotency note: CREATE TABLE IF NOT EXISTS is used, so the migration is\n// naturally idempotent — no try/catch needed for the CREATE TABLE statement\n// itself. The version bump to 5 is the only state change that needs recovery\n// semantics (a crash between the CREATE TABLE and the version bump leaves\n// table present at version=4; re-entry runs CREATE TABLE IF NOT EXISTS as a\n// no-op and bumps to 5 normally).\n\n/**\n * SQL statements for migration 5.\n *\n * Creates the `block_artifacts` table that stores one row per artifact\n * entry of a block's proof manifest. Each row holds:\n * - block_merkle_root: FK → blocks.block_merkle_root\n * - path: the manifest-declared artifact path (e.g. \"property_tests.ts\")\n * - bytes: the raw artifact bytes (BLOB)\n * - declaration_index: position in manifest.artifacts array (0-based),\n * used to reconstruct the Map in declaration order on hydration.\n *\n * Composite PRIMARY KEY (block_merkle_root, path) enforces uniqueness per\n * block+path combination and provides the idempotency guarantee for re-stores.\n *\n * No ownership-shaped columns — DEC-NO-OWNERSHIP-011.\n */\nconst MIGRATION_5_DDL: readonly string[] = [\n // block_artifacts: one row per artifact entry per block.\n // block_merkle_root: FK → blocks(block_merkle_root).\n // path: manifest-declared artifact path (e.g. \"property_tests.ts\").\n // bytes: raw artifact bytes, BLOB.\n // declaration_index: manifest.artifacts array position (0-based).\n // Composite PK enforces uniqueness; declaration_index enables ordered hydration.\n // No ownership columns — DEC-NO-OWNERSHIP-011.\n `CREATE TABLE IF NOT EXISTS block_artifacts (\n block_merkle_root TEXT NOT NULL REFERENCES blocks(block_merkle_root),\n path TEXT NOT NULL,\n bytes BLOB NOT NULL,\n declaration_index INTEGER NOT NULL,\n PRIMARY KEY (block_merkle_root, path)\n )`,\n // Non-unique index on block_merkle_root for efficient artifact hydration\n // (ORDER BY declaration_index) when loading all artifacts for a given block.\n \"CREATE INDEX IF NOT EXISTS idx_block_artifacts_block_merkle_root ON block_artifacts(block_merkle_root)\",\n];\n\n// ---------------------------------------------------------------------------\n// Migration 5 → 6: add kind discriminator + foreign columns + block_foreign_refs\n// (DEC-V2-FOREIGN-BLOCK-SCHEMA-001 sub-A / WI-V2-04 L2)\n// ---------------------------------------------------------------------------\n\n// @decision DEC-V2-FOREIGN-BLOCK-SCHEMA-001 (sub-A: column-on-blocks form)\n// Status: decided (WI-V2-04 L2)\n// Rationale: See top-of-file annotation. Foreign blocks are discriminated rows\n// on the existing blocks table. The four ADD COLUMN statements below are\n// idempotent via try/catch (duplicate-column-name swallowed, as in migration 3/4).\n// block_foreign_refs is a new table — CREATE TABLE IF NOT EXISTS is naturally\n// idempotent. All FK references use blocks.block_merkle_root (L2-I1: no other\n// package issues ALTER TABLE against blocks; this is the single authority).\n\n/**\n * SQL statements for migration 6.\n *\n * 1. `kind TEXT NOT NULL DEFAULT 'local'` — discriminator column on blocks.\n * DEFAULT 'local' ensures every pre-v6 row is correctly labelled without a\n * backfill UPDATE. kind='foreign' requires foreign_pkg/foreign_export IS NOT\n * NULL (enforced at the storage-layer guard in storage.ts, not in SQL, because\n * SQLite CHECK with cross-column NOT NULL requires a constraint that is awkward\n * to add after the fact; the application-level guard is the authority per L2-I3).\n *\n * 2. `foreign_pkg TEXT` — nullable; npm package or Node built-in specifier.\n * Non-NULL only when kind='foreign'.\n *\n * 3. `foreign_export TEXT` — nullable; exported symbol name at the use site.\n * Non-NULL only when kind='foreign'.\n *\n * 4. `foreign_dts_hash TEXT` — nullable; optional BLAKE3 of the .d.ts declaration\n * text. Absent for blocks that did not snapshot their d.ts at storage time.\n *\n * 5. `block_foreign_refs` table — tracks the foreign-dependency tree of each\n * non-foreign block. One row per foreign atom referenced at declaration_index N\n * in a parent block's impl/spec. Keyed by (parent_block_root, declaration_index).\n * FKs reference blocks.block_merkle_root so orphaned refs are rejected.\n *\n * Idempotency:\n * - The four ADD COLUMN statements use try/catch (not IF NOT EXISTS — SQLite\n * lacks that for ALTER TABLE). Matches migration 3/4 pattern.\n * - CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS are naturally idempotent.\n * - A crash between any statement and the version bump (in applyMigrations) is safe:\n * re-entry absorbs duplicate-column errors, re-runs the CREATE TABLE/INDEX as\n * no-ops, and bumps the version.\n *\n * No ownership-shaped columns — DEC-NO-OWNERSHIP-011.\n */\nconst MIGRATION_6_DDL: readonly string[] = [\n // Column 1: kind discriminator — 'local' (default) or 'foreign'.\n \"ALTER TABLE blocks ADD COLUMN kind TEXT NOT NULL DEFAULT 'local'\",\n\n // Column 2: npm package name / Node built-in specifier (foreign blocks only).\n \"ALTER TABLE blocks ADD COLUMN foreign_pkg TEXT\",\n\n // Column 3: exported symbol name at the use site (foreign blocks only).\n \"ALTER TABLE blocks ADD COLUMN foreign_export TEXT\",\n\n // Column 4: optional BLAKE3 of the .d.ts declaration text (foreign blocks only).\n \"ALTER TABLE blocks ADD COLUMN foreign_dts_hash TEXT\",\n\n // Table 5: per-block foreign-dependency manifest.\n // parent_block_root: FK → blocks.block_merkle_root (the non-foreign owner block).\n // foreign_block_root: FK → blocks.block_merkle_root (the foreign atom it references).\n // declaration_index: 0-based position of this foreign ref in the block's impl/spec.\n // Composite PK (parent_block_root, declaration_index) enforces at-most-one foreign\n // ref per position per block and provides the uniqueness contract for idempotent inserts.\n // No ownership columns — DEC-NO-OWNERSHIP-011.\n `CREATE TABLE IF NOT EXISTS block_foreign_refs (\n parent_block_root TEXT NOT NULL REFERENCES blocks(block_merkle_root),\n foreign_block_root TEXT NOT NULL REFERENCES blocks(block_merkle_root),\n declaration_index INTEGER NOT NULL,\n PRIMARY KEY (parent_block_root, declaration_index)\n )`,\n\n // Non-unique index on parent_block_root for getForeignRefs(merkleRoot) lookups\n // (ORDER BY declaration_index for deterministic hydration order).\n \"CREATE INDEX IF NOT EXISTS idx_block_foreign_refs_parent ON block_foreign_refs(parent_block_root)\",\n];\n\n// ---------------------------------------------------------------------------\n// Migration 6 → 7: add source-file provenance columns + workspace_plumbing table\n// (DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001 / DEC-V2-WORKSPACE-PLUMBING-AUTHORITY-001 /\n// DEC-V2-REGISTRY-SCHEMA-BUMP-001 / WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P1)\n// ---------------------------------------------------------------------------\n\n/**\n * @decision DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001\n * @title Three nullable provenance columns on blocks — source_pkg, source_file, source_offset\n * @status decided (WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P1)\n * @rationale Provenance is 1:1 with the FIRST observed atom occurrence. Nullable columns on\n * the keyed row (not a side table) are consistent with the foreign-block pattern\n * (DEC-V2-FOREIGN-BLOCK-SCHEMA-001 sub-A). First-observed-wins via the existing\n * INSERT OR IGNORE path in storeBlock — no UPDATE on conflict (forbidden shortcut per\n * evaluation contract). NULL is correct for foreign atoms (kind='foreign') and for\n * seed blocks, which have no workspace source. source_pkg is the workspace package dir\n * (e.g. 'packages/cli'). source_file is the workspace-relative path of the originating\n * .ts file. source_offset is the byte offset of the atom's implSource within source_file.\n * These fields are NOT folded into blockMerkleRoot — provenance is metadata only.\n *\n * @decision DEC-V2-WORKSPACE-PLUMBING-AUTHORITY-001\n * @title Registry owns workspace_plumbing storage (option a)\n * @status decided (WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P1)\n * @rationale The registry is already the canonical authority for everything reproducible\n * from `yakcc bootstrap` (Sacred Practice #12). A workspace_plumbing table makes the\n * full workspace shape recoverable from the registry alone, preserving the\n * \"compile-self is content-addressed\" property. P1 CREATES the table empty;\n * P2 populates and consumes it. Options (b) copy-from-canonical and (c)\n * commit-fixtures were rejected as parallel-authority violations.\n */\nconst MIGRATION_7_DDL: readonly string[] = [\n // Column 1: source package directory (e.g. 'packages/cli'). NULL for foreign atoms\n // and seed blocks. Non-NULL only for local blocks shaved during bootstrap.\n \"ALTER TABLE blocks ADD COLUMN source_pkg TEXT\",\n\n // Column 2: workspace-relative path of the originating .ts file\n // (e.g. 'packages/cli/src/commands/compile.ts'). NULL for foreign atoms and seed blocks.\n \"ALTER TABLE blocks ADD COLUMN source_file TEXT\",\n\n // Column 3: byte offset of the atom's implSource within source_file.\n // NULL when unknown. Used to order multiple atoms from the same file when\n // reconstructing source. NOT folded into blockMerkleRoot.\n \"ALTER TABLE blocks ADD COLUMN source_offset INTEGER\",\n\n // Table: workspace_plumbing — records files needed to make a compiled output bootable.\n // workspace_path: workspace-relative path, PRIMARY KEY (e.g. 'package.json').\n // content_bytes: raw file bytes, BLOB.\n // content_hash: BLAKE3(content_bytes), hex — indexed for dedup/integrity checks.\n // created_at: Unix epoch milliseconds of insertion.\n //\n // P1 CREATES the table empty. P2 populates it during bootstrap and consumes it\n // in compile-self to materialise the workspace shape in the output directory.\n // No ownership columns — DEC-NO-OWNERSHIP-011.\n `CREATE TABLE IF NOT EXISTS workspace_plumbing (\n workspace_path TEXT PRIMARY KEY,\n content_bytes BLOB NOT NULL,\n content_hash TEXT NOT NULL,\n created_at INTEGER NOT NULL DEFAULT 0\n )`,\n\n // Non-unique index on content_hash for deduplication checks.\n \"CREATE INDEX IF NOT EXISTS idx_workspace_plumbing_hash ON workspace_plumbing(content_hash)\",\n];\n\n// ---------------------------------------------------------------------------\n// Migration 7 → 8: add source_file_glue table\n// (DEC-V2-GLUE-CAPTURE-AUTHORITY-001 / DEC-V2-REGISTRY-SCHEMA-BUMP-V8-001 /\n// WI-V2-WORKSPACE-PLUMBING-GLUE-CAPTURE #333)\n// ---------------------------------------------------------------------------\n\n/**\n * @decision DEC-V2-GLUE-CAPTURE-AUTHORITY-001\n * @title Per-file glue lives in a new source_file_glue table (option b); single\n * concatenated blob per (source_pkg, source_file); boundaries derived from\n * blocks.source_offset at consume time\n * @status decided (WI-V2-WORKSPACE-PLUMBING-GLUE-CAPTURE #333)\n * @rationale\n * Glue is the byte content of every source file that is NOT covered by any atom's\n * implSource. It is stored as a single concatenated blob (not per-region rows)\n * because the atom offsets are already authoritative in blocks.source_offset —\n * storing region boundaries again would create a dual-authority violation\n * (Sacred Practice #12). The consumer (compile-self) derives glue region boundaries\n * from blocks.source_offset at reconstruction time.\n *\n * Options rejected:\n * (a) Extend workspace_plumbing — would create dual-authority for source .ts files\n * (both plumbing whole-file row and atom rows). Sacred Practice #12 violation.\n * (c) Promote glue to atoms — glue has no contract (no spec.yak); cannot be\n * content-addressed in the BlockMerkleRoot formula. DEC-V2-GLUE-AWARE-SHAVE-001\n * explicitly keeps glue project-local.\n *\n * No ownership columns — DEC-NO-OWNERSHIP-011.\n * No merkle-root column — glue is workspace-reconstruction metadata, not a\n * registry citizen. content_hash is for dedup/integrity only (not a merkle root).\n *\n * @decision DEC-V2-REGISTRY-SCHEMA-BUMP-V8-001\n * @title SCHEMA_VERSION 7 → 8; single-phase additive migration matching the\n * MIGRATION_7 pattern (CREATE TABLE IF NOT EXISTS + CREATE INDEX IF NOT EXISTS)\n * @status decided (WI-V2-WORKSPACE-PLUMBING-GLUE-CAPTURE #333)\n * @rationale Pure DDL addition; no backfill required (new table starts empty;\n * bootstrap re-run populates it). Same idempotent pattern as every prior migration.\n * Existing tables and columns are unchanged. Schema bumped from 7 to 8.\n */\nconst MIGRATION_8_DDL: readonly string[] = [\n // source_file_glue: one row per source file, capturing all non-atom byte regions\n // as a single concatenated blob.\n //\n // source_pkg: workspace package directory (e.g. 'packages/cli'). Part of PK.\n // source_file: workspace-relative path (e.g. 'packages/cli/src/commands/foo.ts'). Part of PK.\n // content_hash: BLAKE3-256 hex of content_blob — for integrity checks and dedup.\n // content_blob: concatenated bytes of all non-atom regions in source order.\n // Byte-spans (glue regions) are: [0, A1.start) ++ [A1.end, A2.start) ++ ...\n // ++ [An.end, fileLen). The consumer reconstructs the original file by\n // interleaving this blob with atom implSources using blocks.source_offset.\n // created_at: Unix epoch milliseconds of first insertion.\n //\n // PRIMARY KEY (source_pkg, source_file): one row per source file.\n // INSERT OR REPLACE semantics for idempotent re-bootstrap.\n // No ownership columns — DEC-NO-OWNERSHIP-011.\n // No merkle-root column — DEC-V2-GLUE-AWARE-SHAVE-001 glue-stays-local invariant.\n `CREATE TABLE IF NOT EXISTS source_file_glue (\n source_pkg TEXT NOT NULL,\n source_file TEXT NOT NULL,\n content_hash TEXT NOT NULL,\n content_blob BLOB NOT NULL,\n created_at INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (source_pkg, source_file)\n )`,\n\n // Non-unique index on content_hash for deduplication checks and integrity scans.\n \"CREATE INDEX IF NOT EXISTS idx_source_file_glue_hash ON source_file_glue(content_hash)\",\n];\n\n// ---------------------------------------------------------------------------\n// Migration 8 → 9: add block_occurrences table + indexes\n// (DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001 / DEC-V2-BLOCK-OCCURRENCES-SCHEMA-001 /\n// DEC-V2-REGISTRY-SCHEMA-BUMP-V9-001 / WI-V2-STORAGE-IDEMPOTENT-RECOMPILE #355)\n// ---------------------------------------------------------------------------\n\n/**\n * @decision DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001\n * @title Option (b): block_occurrences table replaces blocks.source_* per-occurrence semantics\n * @status decided (WI-V2-STORAGE-IDEMPOTENT-RECOMPILE #355)\n * @rationale\n * DEC-STORAGE-IDEMPOTENT-001 made storeBlock() use INSERT OR IGNORE keyed on\n * block_merkle_root. After P1 added source_pkg/source_file/source_offset columns to\n * the same row, first-observed-wins became load-bearing for location as well as\n * content. That is the bug: block_merkle_root (content-identity) is correct as\n * INSERT OR IGNORE; source_offset (per-occurrence position) is NOT correct as\n * first-observed-wins when source files are edited and atoms shift.\n *\n * Atom content (blocks) and atom occurrence (block_occurrences) are now two\n * independently authoritative tables. The blocks.source_* columns become a\n * write-once shim driven from the first occurrence observed; block_occurrences\n * is the authoritative read source for per-file ranges.\n *\n * Supersedes DEC-STORAGE-IDEMPOTENT-001 for occurrence semantics only.\n * Content-identity semantics (INSERT OR IGNORE on blocks keyed by block_merkle_root)\n * are unchanged.\n *\n * @decision DEC-V2-BLOCK-OCCURRENCES-SCHEMA-001\n * @title block_occurrences table shape: PK (source_pkg, source_file, source_offset),\n * FK on blocks(block_merkle_root), length materialized for reconstruction\n * @status decided (WI-V2-STORAGE-IDEMPOTENT-RECOMPILE #355)\n * @rationale\n * PK (source_pkg, source_file, source_offset) makes each occurrence uniquely\n * identifiable and supports the per-file delete-then-insert refresh pattern.\n * FK on block_merkle_root enforces that an occurrence cannot reference a block\n * that has not yet been stored (bootstrap order: storeBlock first, then\n * replaceSourceFileOccurrences).\n * length is materialized (not derived) so reconstruction does not need a join\n * against blocks to compute range extents.\n * Two indexes cover the two dominant read patterns:\n * idx_block_occurrences_root — by block (cross-file sharing introspection)\n * idx_block_occurrences_file — by file (reconstruction / glue computation)\n */\nconst MIGRATION_9_DDL: readonly string[] = [\n // block_occurrences: one row per occurrence of a block atom in a source file.\n //\n // source_pkg: Workspace package directory (e.g. 'packages/cli').\n // source_file: Workspace-relative path of the source file. Part of PK.\n // source_offset: Byte offset of the atom's implSource within source_file.\n // Together with source_pkg and source_file, forms the PK.\n // length: Byte length of the atom's implSource at write time.\n // Materialized so reconstruction does not need a join.\n // block_merkle_root: FK → blocks(block_merkle_root). The atom's content address.\n // Must be stored first (storeBlock before storeBlockOccurrence).\n //\n // PRIMARY KEY (source_pkg, source_file, source_offset): one row per atom position.\n // FOREIGN KEY (block_merkle_root) enforces referential integrity.\n // No ownership columns — DEC-NO-OWNERSHIP-011.\n `CREATE TABLE IF NOT EXISTS block_occurrences (\n source_pkg TEXT NOT NULL,\n source_file TEXT NOT NULL,\n source_offset INTEGER NOT NULL,\n length INTEGER NOT NULL,\n block_merkle_root TEXT NOT NULL,\n PRIMARY KEY (source_pkg, source_file, source_offset),\n FOREIGN KEY (block_merkle_root) REFERENCES blocks(block_merkle_root)\n )`,\n\n // Index for cross-file sharing introspection: given a block_merkle_root,\n // find all files that contain it.\n \"CREATE INDEX IF NOT EXISTS idx_block_occurrences_root ON block_occurrences(block_merkle_root)\",\n\n // Index for reconstruction / glue computation: given (source_pkg, source_file),\n // find all occurrences sorted by offset for interleaving.\n \"CREATE INDEX IF NOT EXISTS idx_block_occurrences_file ON block_occurrences(source_pkg, source_file)\",\n];\n\n// ---------------------------------------------------------------------------\n// Migration 9 → 10: add source_file_state table + index\n// (DEC-V2-SHAVE-CACHE-STORAGE-001 / DEC-V2-REGISTRY-SCHEMA-BUMP-V10-001 /\n// issue #363 / wi-363-shave-cache)\n// ---------------------------------------------------------------------------\n\n/**\n * @decision DEC-V2-SHAVE-CACHE-STORAGE-001\n * @title New source_file_state table (Option B) — NOT a new column on source_file_glue\n * @status decided (issue #363 / wi-363-shave-cache)\n * @rationale source_file_glue.content_hash already stores BLAKE3 of the glue blob,\n * a different domain from per-source-file content. Single-authority-per-fact\n * (Sacred Practice #12) requires these two domains to live in separate tables.\n * Reusing the column creates a dual-meaning cell: glue-blob hash for glue rows,\n * source-file-content hash for a cache hit — both keyed identically, different\n * semantics. The extra CREATE TABLE is cheap compared to the correctness risk.\n * Option A (ALTER TABLE source_file_glue ADD COLUMN) was rejected because:\n * (a) files with zero atoms have a glue row but would need the cache column\n * for different reasons, blurring the row invariant,\n * (b) the dual-meaning cell violates Sacred Practice #12.\n *\n * @decision DEC-V2-REGISTRY-SCHEMA-BUMP-V10-001\n * @title SCHEMA_VERSION 9 → 10; single-phase additive migration adding source_file_state table\n * @status decided (issue #363 / wi-363-shave-cache)\n * @rationale Pure DDL addition; no backfill required (table starts empty; rows\n * accrete on each cache-miss bootstrap pass). Same idempotent pattern as\n * migrations 8 and 9. No partial-state risk because there are no rows to migrate.\n */\nconst MIGRATION_10_DDL: readonly string[] = [\n // source_file_state: one row per source file, storing the BLAKE3-256 hex content\n // hash of the file's UTF-8 bytes at the time of the most recent successful shave.\n //\n // source_pkg: Workspace package directory (e.g. 'packages/cli'). Part of PK.\n // source_file: Workspace-relative path (e.g. 'packages/cli/src/commands/foo.ts').\n // Part of PK. Must be workspace-relative, not absolute.\n // content_hash: BLAKE3-256 hex of the source file's UTF-8 bytes at shave time.\n // Compared on next bootstrap: match = cache hit, skip shave.\n // NOT the same as source_file_glue.content_hash (which is\n // BLAKE3 of the glue blob — a different domain entirely).\n // last_shave_time: Unix epoch milliseconds of the last successful shave write.\n // Informational only; not used in cache-key comparison.\n //\n // PRIMARY KEY (source_pkg, source_file): one row per source file.\n // INSERT OR REPLACE semantics for idempotent re-bootstrap (storeSourceFileContentHash).\n // No ownership columns — DEC-NO-OWNERSHIP-011.\n `CREATE TABLE IF NOT EXISTS source_file_state (\n source_pkg TEXT NOT NULL,\n source_file TEXT NOT NULL,\n content_hash TEXT NOT NULL,\n last_shave_time INTEGER NOT NULL,\n PRIMARY KEY (source_pkg, source_file)\n )`,\n\n // Non-unique index on content_hash for future dedup/scan use (e.g. finding all\n // files that share the same content hash, or bulk invalidation by hash prefix).\n \"CREATE INDEX IF NOT EXISTS idx_source_file_state_hash ON source_file_state(content_hash)\",\n];\n\n// ---------------------------------------------------------------------------\n// Migration driver\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal interface for what applyMigrations needs from the DB.\n * Avoids importing better-sqlite3 types in this module; the storage layer\n * passes a concrete Database instance.\n */\nexport interface MigrationsDb {\n exec(sql: string): void;\n prepare(sql: string): {\n // better-sqlite3 Statement.get() always takes binding parameters\n // (zero or more), so the signature must accept rest args to remain\n // compatible with the concrete Statement<P, R> types the library returns.\n get(...params: unknown[]): unknown;\n run(...args: unknown[]): unknown;\n };\n}\n\n/**\n * Apply all pending schema migrations up to `SCHEMA_VERSION`.\n *\n * Idempotent: calling this function on a DB that is already at `SCHEMA_VERSION`\n * is a no-op. Each migration is guarded by checking `currentVersion` before\n * applying, so partial re-runs from a crash are safe.\n *\n * Migration sequence:\n * 0 → 1: initial v0 schema (contracts, implementations, v0 ancillaries).\n * 1 → 2: clean re-create as blocks table (DEC-SCHEMA-MIGRATION-002).\n * 2 → 3: add canonical_ast_hash column + non-unique index (DEC-REGISTRY-AST-HASH-002).\n * 3 → 4: add parent_block_root column + non-unique index (DEC-REGISTRY-PARENT-BLOCK-004).\n * 4 → 5: add block_artifacts table + index (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002).\n * 5 → 6: add kind/foreign_* columns + block_foreign_refs table (DEC-V2-FOREIGN-BLOCK-SCHEMA-001).\n * 6 → 7: add source_pkg/source_file/source_offset columns + workspace_plumbing table\n * (DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001 / DEC-V2-WORKSPACE-PLUMBING-AUTHORITY-001).\n * 7 → 8: add source_file_glue table + hash index\n * (DEC-V2-GLUE-CAPTURE-AUTHORITY-001 / DEC-V2-REGISTRY-SCHEMA-BUMP-V8-001).\n * 8 → 9: add block_occurrences table + indexes for per-occurrence offset tracking\n * (DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001 / DEC-V2-BLOCK-OCCURRENCES-SCHEMA-001 /\n * DEC-V2-REGISTRY-SCHEMA-BUMP-V9-001).\n * 9 → 10: add source_file_state table + content_hash index for per-file content-hash caching\n * (DEC-V2-SHAVE-CACHE-STORAGE-001 / DEC-V2-REGISTRY-SCHEMA-BUMP-V10-001 / issue #363).\n *\n * TWO-PHASE INVARIANT FOR MIGRATION 2 → 3:\n * `applyMigrations` (this function, in schema.ts) owns the DDL phase only:\n * it adds the `canonical_ast_hash` column with default '' and creates the\n * index. It does NOT bump `schema_version` to 3.\n *\n * `openRegistry` in storage.ts owns the backfill + version-bump phase: after\n * calling `applyMigrations`, it walks every row with `canonical_ast_hash = ''`\n * and fills in the real hash via `canonicalAstHash(impl_source)` from\n * `@yakcc/contracts`, then bumps `schema_version` to 3.\n *\n * This split exists because schema.ts is pure DDL — it has no dependency on\n * `@yakcc/contracts` and must remain free of business logic. Callers that\n * invoke `applyMigrations` directly (without going through `openRegistry`)\n * will therefore leave `schema_version` at 2, because the version bump\n * requires `canonicalAstHash` which schema.ts does not import.\n *\n * The try/catch on the duplicate-column error makes DDL re-entry safe\n * regardless of caller path: if a crash occurs between the ADD COLUMN and the\n * caller's version bump, the next open will see version=2 but the column\n * already present. The catch absorbs that case and the backfill + bump\n * complete normally.\n *\n * Future migrations should follow the same two-phase pattern (DDL here,\n * backfill + bump in storage.ts) OR import their backfill helpers into\n * schema.ts to keep the invariant single-phase, whichever is appropriate for\n * the migration's dependency footprint.\n *\n * Idempotency design:\n * Bootstrap step creates schema_version with no-op semantics if absent.\n * Version is read once after bootstrap. Migrations are only applied when\n * currentVersion < their target version. On a fully-migrated DB this means\n * neither MIGRATION_1 nor MIGRATION_2 is re-run, so there is no risk of\n * re-executing DDL that references tables already dropped by a later migration.\n *\n * @param db - An open SQLite database with the sqlite-vec extension loaded.\n */\nexport function applyMigrations(db: MigrationsDb): void {\n // Bootstrap: ensure schema_version table and initial row exist.\n // These two statements are safe to run on any DB state and are the only\n // unconditional DDL. We do NOT run the rest of MIGRATION_1 here.\n db.exec(\"CREATE TABLE IF NOT EXISTS schema_version (version INTEGER NOT NULL)\");\n db.exec(\"INSERT OR IGNORE INTO schema_version(version) VALUES (0)\");\n\n // Read the current version. If the DB is fresh, this returns 0.\n const row = db.prepare(\"SELECT version FROM schema_version LIMIT 1\").get() as\n | { version: number }\n | undefined;\n const currentVersion = row?.version ?? 0;\n\n // Migration 0 → 1: install the v0 schema (contracts, implementations, v0 ancillaries).\n // Only applied on a genuinely fresh DB (version 0).\n if (currentVersion < 1) {\n for (const sql of MIGRATION_1) {\n db.exec(sql);\n }\n db.prepare(\"UPDATE schema_version SET version = ?\").run(1);\n }\n\n // Migration 1 → 2: drop v0 tables, create blocks table and new ancillaries.\n if (currentVersion < 2) {\n for (const sql of MIGRATION_2) {\n db.exec(sql);\n }\n db.prepare(\"UPDATE schema_version SET version = ?\").run(2);\n }\n\n // Migration 2 → 3: add canonical_ast_hash column + index (DEC-REGISTRY-AST-HASH-002).\n // The backfill of existing rows is performed by the caller (storage.ts openRegistry)\n // because it requires the canonicalAstHash function from @yakcc/contracts, which\n // this schema module does not import (schema.ts is pure DDL — no business logic).\n //\n // Idempotency note: SQLite has no ADD COLUMN IF NOT EXISTS. If a crash occurs\n // between the ADD COLUMN and the caller's schema_version bump (which happens\n // after the backfill), re-entry would see version=2 but the column already\n // present. We catch the \"duplicate column name\" error specifically to handle\n // this partial-migration recovery path. Any other DDL error is re-thrown.\n // MIGRATION_3_DDL[1] (CREATE INDEX IF NOT EXISTS) is already idempotent and\n // runs unconditionally within the if block.\n if (currentVersion < 3) {\n // Wrap the ALTER TABLE in a try/catch: ADD COLUMN is not idempotent in\n // SQLite, but a crash between this DDL and the version bump leaves us with\n // the column present at version=2. On re-entry we must not throw.\n try {\n db.exec(MIGRATION_3_DDL[0] as string); // ALTER TABLE ... ADD COLUMN canonical_ast_hash\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (!/duplicate column name: canonical_ast_hash/i.test(msg)) {\n throw err;\n }\n // Column already exists (partial migration recovery) — continue normally.\n }\n // CREATE INDEX IF NOT EXISTS is already idempotent; always safe to re-run.\n db.exec(MIGRATION_3_DDL[1] as string);\n // NOTE: schema_version is bumped to 3 by the caller AFTER it performs the\n // backfill, not here. This lets openRegistry detect a partial migration\n // (version still 2, column now added) and complete the backfill safely.\n // On a fresh DB (version 0 → 3 path), currentVersion will be 0 here after\n // migrations 1 and 2 ran above, so we run the DDL but the caller still does\n // the backfill (which is a no-op on an empty table) and bumps to 3.\n }\n // Migration 3 → 4: add parent_block_root column + index (DEC-REGISTRY-PARENT-BLOCK-004).\n // Unlike migration 3, no backfill is needed: NULL is the correct default for all\n // existing rows (every pre-existing block is the root of its own recursion tree).\n // applyMigrations therefore bumps schema_version to 4 directly.\n //\n // Idempotency note: SQLite has no ADD COLUMN IF NOT EXISTS. If a crash occurs\n // between the ADD COLUMN and the version bump, re-entry sees version=3 but the\n // column already present. We catch the \"duplicate column name\" error specifically\n // to handle this partial-migration recovery path. Any other DDL error is re-thrown.\n // MIGRATION_4_DDL[1] (CREATE INDEX IF NOT EXISTS) is already idempotent.\n if (currentVersion < 4) {\n try {\n db.exec(MIGRATION_4_DDL[0] as string); // ALTER TABLE ... ADD COLUMN parent_block_root\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (!/duplicate column name: parent_block_root/i.test(msg)) {\n throw err;\n }\n // Column already exists (partial migration recovery) — continue normally.\n }\n // CREATE INDEX IF NOT EXISTS is already idempotent; always safe to re-run.\n db.exec(MIGRATION_4_DDL[1] as string);\n // Bump version: no backfill required (NULL is the correct default).\n db.prepare(\"UPDATE schema_version SET version = ?\").run(4);\n }\n\n // Migration 4 → 5: add block_artifacts table + index (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002).\n // CREATE TABLE IF NOT EXISTS is naturally idempotent — no try/catch needed for the DDL.\n // A crash between CREATE TABLE and the version bump leaves the table present at version=4;\n // re-entry runs CREATE TABLE IF NOT EXISTS as a no-op and bumps to 5 normally.\n // CREATE INDEX IF NOT EXISTS is always idempotent.\n //\n // No backfill: pre-WI-022 blocks get zero rows in block_artifacts (empty Map at hydrate\n // time). Back-deriving artifact bytes would invalidate pre-existing blockMerkleRoot values\n // (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002 migration note — forbidden shortcut).\n if (currentVersion < 5) {\n db.exec(MIGRATION_5_DDL[0] as string); // CREATE TABLE IF NOT EXISTS block_artifacts\n db.exec(MIGRATION_5_DDL[1] as string); // CREATE INDEX IF NOT EXISTS idx_block_artifacts_*\n db.prepare(\"UPDATE schema_version SET version = ?\").run(5);\n }\n // Migration 5 → 6: add foreign-block columns + block_foreign_refs table\n // (DEC-V2-FOREIGN-BLOCK-SCHEMA-001 sub-A / WI-V2-04 L2).\n //\n // Column additions use ADD COLUMN — idempotency handled by try/catch on\n // \"duplicate column name\" errors, matching the MIGRATION_3/MIGRATION_4 pattern.\n // CREATE TABLE IF NOT EXISTS and CREATE INDEX IF NOT EXISTS are naturally\n // idempotent; no try/catch needed for those statements.\n //\n // No backfill: DEFAULT 'local' covers all pre-v6 rows for `kind` (correct\n // sentinel — every existing row is a local block). foreign_pkg, foreign_export,\n // foreign_dts_hash are all NULL for pre-v6 rows (also correct — they have no\n // foreign identity fields). No UPDATE SET is performed (forbidden shortcut per\n // I-X6 / Evaluation Contract).\n if (currentVersion < 6) {\n for (const sql of MIGRATION_6_DDL) {\n try {\n db.exec(sql);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Absorb duplicate-column-name errors (partial migration recovery).\n // All other errors are re-thrown.\n if (!/duplicate column name:/i.test(msg)) {\n throw err;\n }\n // Column already exists — continue normally.\n }\n }\n db.prepare(\"UPDATE schema_version SET version = ?\").run(6);\n }\n\n // Migration 6 → 7: add source-file provenance columns + workspace_plumbing table\n // (DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001 / DEC-V2-WORKSPACE-PLUMBING-AUTHORITY-001 /\n // DEC-V2-REGISTRY-SCHEMA-BUMP-001 / WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P1).\n //\n // Three ADD COLUMN statements for source_pkg, source_file, source_offset — all nullable,\n // so no backfill is required. Provenance for existing rows is populated by re-running\n // `yakcc bootstrap`, not by an in-migration UPDATE (forbidden shortcut #4 in evaluation\n // contract). NULL is the correct sentinel for pre-v7 rows that predate provenance tracking.\n //\n // workspace_plumbing table: CREATE TABLE IF NOT EXISTS is naturally idempotent.\n // P1 creates the table empty; P2 populates it during bootstrap.\n //\n // Idempotency: the three ADD COLUMN statements use try/catch to absorb\n // \"duplicate column name\" errors — matching the MIGRATION_3/4/6 pattern.\n // CREATE TABLE IF NOT EXISTS and CREATE INDEX IF NOT EXISTS are naturally idempotent.\n if (currentVersion < 7) {\n for (const sql of MIGRATION_7_DDL) {\n try {\n db.exec(sql);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n // Absorb duplicate-column-name errors (partial migration recovery for\n // ADD COLUMN statements). CREATE TABLE IF NOT EXISTS / CREATE INDEX IF NOT EXISTS\n // never throw on re-entry, so those statements do not reach this branch.\n // All other errors are re-thrown.\n if (!/duplicate column name:/i.test(msg)) {\n throw err;\n }\n // Column already exists (partial migration recovery) — continue normally.\n }\n }\n db.prepare(\"UPDATE schema_version SET version = ?\").run(7);\n }\n\n // Migration 7 → 8: add source_file_glue table + index\n // (DEC-V2-GLUE-CAPTURE-AUTHORITY-001 / DEC-V2-REGISTRY-SCHEMA-BUMP-V8-001 /\n // WI-V2-WORKSPACE-PLUMBING-GLUE-CAPTURE #333).\n //\n // Pure DDL — CREATE TABLE IF NOT EXISTS and CREATE INDEX IF NOT EXISTS are both\n // naturally idempotent. No ADD COLUMN statements, no try/catch needed.\n // No backfill: the new table starts empty; `yakcc bootstrap` populates it.\n // A crash between the first DDL statement and the version bump leaves the table\n // present at version=7; re-entry runs CREATE TABLE IF NOT EXISTS as a no-op\n // and bumps to 8 normally.\n if (currentVersion < 8) {\n for (const sql of MIGRATION_8_DDL) {\n db.exec(sql);\n }\n db.prepare(\"UPDATE schema_version SET version = ?\").run(8);\n }\n\n // Migration 8 → 9: add block_occurrences table + indexes\n // (DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001 / DEC-V2-BLOCK-OCCURRENCES-SCHEMA-001 /\n // DEC-V2-REGISTRY-SCHEMA-BUMP-V9-001 / WI-V2-STORAGE-IDEMPOTENT-RECOMPILE #355).\n //\n // Pure DDL — CREATE TABLE IF NOT EXISTS and CREATE INDEX IF NOT EXISTS are both\n // naturally idempotent. No ADD COLUMN statements, no try/catch needed.\n // No backfill: block_occurrences starts empty; the next `yakcc bootstrap` run\n // calls replaceSourceFileOccurrences() for each file and populates it from scratch.\n // A crash between the first DDL statement and the version bump leaves the table\n // present at version=8; re-entry runs CREATE TABLE IF NOT EXISTS as a no-op\n // and bumps to 9 normally.\n if (currentVersion < 9) {\n for (const sql of MIGRATION_9_DDL) {\n db.exec(sql);\n }\n db.prepare(\"UPDATE schema_version SET version = ?\").run(9);\n }\n\n // Migration 9 → 10: add source_file_state table + content_hash index\n // (DEC-V2-SHAVE-CACHE-STORAGE-001 / DEC-V2-REGISTRY-SCHEMA-BUMP-V10-001 / issue #363).\n //\n // Pure DDL — CREATE TABLE IF NOT EXISTS and CREATE INDEX IF NOT EXISTS are both\n // naturally idempotent. No ADD COLUMN statements, no try/catch needed.\n // No backfill: source_file_state starts empty; rows accrete on each cache-miss\n // bootstrap pass via storeSourceFileContentHash() after a successful shave.\n // A crash between the first DDL statement and the version bump leaves the table\n // present at version=9; re-entry runs CREATE TABLE IF NOT EXISTS as a no-op\n // and bumps to 10 normally.\n if (currentVersion < 10) {\n for (const sql of MIGRATION_10_DDL) {\n db.exec(sql);\n }\n db.prepare(\"UPDATE schema_version SET version = ?\").run(10);\n }\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-SEARCH-STRUCTURAL-001: Structural matching is a pure function\n// with no DB access. Status: decided (WI-003)\n// Rationale: Keeps the matching logic independently testable and reusable.\n// The storage layer calls structuralMatch per candidate and filters on the\n// result. This separation means the structural logic can evolve (v0.5+) without\n// touching the persistence layer.\n\n// @decision DEC-SEARCH-V0-PRAGMATIC-001: v0 structural matching uses deep-equal\n// on TypeSignature arrays and subset-check on errorConditions. Status: decided (WI-003)\n// Rationale: Full type-system subtyping is undecidable in general; v0 takes the\n// pragmatic path of exact structural equality for types plus a subset-check for\n// error conditions. This is sufficient for the seed corpus and flags divergences\n// at selection time. Refinements land in v0.5+.\n\n// @decision DEC-SEARCH-SPECYAK-MIGRATE-001: structuralMatch now operates on\n// SpecYak instead of ContractSpec. The internal logic (deep-equal on type\n// signatures, subset-check on errors) is unchanged. SpecYak's nonFunctional,\n// errorConditions, inputs, and outputs fields are optional (for v0 lift\n// compatibility), so absent fields on caller or candidate are treated as\n// \"no constraint\" and match anything.\n// Status: decided (WI-T03)\n\nimport type { SpecYak } from \"@yakcc/contracts\";\n\n// ---------------------------------------------------------------------------\n// Public result type\n// ---------------------------------------------------------------------------\n\n/**\n * The result of a structural match check between a caller's spec and a\n * candidate spec from the registry.\n *\n * - `{ matches: true }` — the candidate satisfies the caller's requirements.\n * - `{ matches: false, reasons: string[] }` — the candidate does not satisfy\n * the caller's requirements; `reasons` names each divergence.\n */\nexport type MatchResult =\n | { readonly matches: true }\n | { readonly matches: false; readonly reasons: readonly string[] };\n\n// ---------------------------------------------------------------------------\n// Public function\n// ---------------------------------------------------------------------------\n\n/**\n * Evaluate whether `candidate` could satisfy the caller's `spec`.\n *\n * v0 matching rules (each failure appends a string to `reasons`):\n *\n * 1. Input signatures must match exactly in count, name, and type (when both\n * specs declare inputs; absent inputs fields are treated as \"no constraint\").\n * 2. Output signatures must match exactly in count, name, and type.\n * 3. The candidate's declared error conditions must be a subset of the\n * conditions the caller's spec declares — extra undeclared errors on the\n * candidate mean the caller might not handle them. When caller has no\n * errorConditions field, it is treated as an empty array (no tolerance).\n * 4. Non-functional properties that the caller specifies must be at least as\n * strong on the candidate (same purity, same thread-safety, compatible\n * time/space complexity). When nonFunctional is absent, no NF check is done.\n *\n * Monotonicity invariant (tested in search.test.ts): relaxing a caller's\n * requirement (removing a constraint from `spec`) must not turn a\n * `matches: true` into `matches: false` for the same candidate.\n *\n * @param spec - The caller's required spec.\n * @param candidate - The registry candidate being evaluated.\n */\nexport function structuralMatch(spec: SpecYak, candidate: SpecYak): MatchResult {\n const reasons: string[] = [];\n\n // -----------------------------------------------------------------------\n // 1. Input signatures — exact match (count, name, type)\n // -----------------------------------------------------------------------\n const callerInputs = spec.inputs;\n const candidateInputs = candidate.inputs;\n if (callerInputs.length !== candidateInputs.length) {\n reasons.push(\n `input count mismatch: caller needs ${callerInputs.length}, candidate has ${candidateInputs.length}`,\n );\n } else {\n for (let i = 0; i < callerInputs.length; i++) {\n const want = callerInputs[i];\n const got = candidateInputs[i];\n if (want === undefined || got === undefined) continue;\n if (want.name !== got.name) {\n reasons.push(\n `input[${i}] name mismatch: caller needs \"${want.name}\", candidate has \"${got.name}\"`,\n );\n }\n if (want.type !== got.type) {\n reasons.push(\n `input[${i}] type mismatch: caller needs \"${want.type}\", candidate has \"${got.type}\"`,\n );\n }\n }\n }\n\n // -----------------------------------------------------------------------\n // 2. Output signatures — exact match (count, name, type)\n // -----------------------------------------------------------------------\n const callerOutputs = spec.outputs;\n const candidateOutputs = candidate.outputs;\n if (callerOutputs.length !== candidateOutputs.length) {\n reasons.push(\n `output count mismatch: caller needs ${callerOutputs.length}, candidate has ${candidateOutputs.length}`,\n );\n } else {\n for (let i = 0; i < callerOutputs.length; i++) {\n const want = callerOutputs[i];\n const got = candidateOutputs[i];\n if (want === undefined || got === undefined) continue;\n if (want.name !== got.name) {\n reasons.push(\n `output[${i}] name mismatch: caller needs \"${want.name}\", candidate has \"${got.name}\"`,\n );\n }\n if (want.type !== got.type) {\n reasons.push(\n `output[${i}] type mismatch: caller needs \"${want.type}\", candidate has \"${got.type}\"`,\n );\n }\n }\n }\n\n // -----------------------------------------------------------------------\n // 3. Error conditions — candidate's errors ⊆ caller's tolerated errors\n //\n // If the caller's spec declares no error conditions (or omits the field),\n // it means \"I don't tolerate any errors.\" If the candidate has error\n // conditions the caller hasn't declared, those are unexpected errors the\n // caller won't handle.\n //\n // Matching is by errorType string when present, otherwise by description.\n // -----------------------------------------------------------------------\n const callerErrors = spec.errorConditions ?? [];\n const candidateErrors = candidate.errorConditions ?? [];\n for (const candidateErr of candidateErrors) {\n const key = candidateErr.errorType ?? candidateErr.description;\n const callerTolerates = callerErrors.some((e) => (e.errorType ?? e.description) === key);\n if (!callerTolerates) {\n reasons.push(`candidate declares error condition not tolerated by caller: \"${key}\"`);\n }\n }\n\n // -----------------------------------------------------------------------\n // 4. Non-functional properties — candidate must be at least as strong\n // Only checked when both caller and candidate declare nonFunctional.\n // If either omits the field, the check is skipped (no constraint).\n // -----------------------------------------------------------------------\n const callerNF = spec.nonFunctional;\n const candidateNF = candidate.nonFunctional;\n\n if (callerNF !== undefined && candidateNF !== undefined) {\n // Purity ordering: pure > io > stateful > nondeterministic.\n const PURITY_RANK: Record<string, number> = {\n pure: 3,\n io: 2,\n stateful: 1,\n nondeterministic: 0,\n };\n const callerPurityRank = PURITY_RANK[callerNF.purity] ?? 0;\n const candidatePurityRank = PURITY_RANK[candidateNF.purity] ?? 0;\n if (candidatePurityRank < callerPurityRank) {\n reasons.push(\n `purity mismatch: caller requires \"${callerNF.purity}\", candidate offers \"${candidateNF.purity}\"`,\n );\n }\n\n // Thread-safety ordering: safe > sequential > unsafe.\n const THREAD_RANK: Record<string, number> = {\n safe: 2,\n sequential: 1,\n unsafe: 0,\n };\n const callerThreadRank = THREAD_RANK[callerNF.threadSafety] ?? 0;\n const candidateThreadRank = THREAD_RANK[candidateNF.threadSafety] ?? 0;\n if (candidateThreadRank < callerThreadRank) {\n reasons.push(\n `threadSafety mismatch: caller requires \"${callerNF.threadSafety}\", candidate offers \"${candidateNF.threadSafety}\"`,\n );\n }\n\n // Time complexity: if caller specifies, candidate must match (exact in v0).\n if (\n callerNF.time !== undefined &&\n candidateNF.time !== undefined &&\n callerNF.time !== candidateNF.time\n ) {\n reasons.push(\n `time complexity mismatch: caller requires \"${callerNF.time}\", candidate offers \"${candidateNF.time}\"`,\n );\n }\n\n // Space complexity: same.\n if (\n callerNF.space !== undefined &&\n candidateNF.space !== undefined &&\n callerNF.space !== candidateNF.space\n ) {\n reasons.push(\n `space complexity mismatch: caller requires \"${callerNF.space}\", candidate offers \"${candidateNF.space}\"`,\n );\n }\n }\n\n if (reasons.length === 0) {\n return { matches: true };\n }\n return { matches: false, reasons };\n}\n","// SPDX-License-Identifier: MIT\n//\n// @decision DEC-EMBED-MODEL-MIGRATION-001\n// title: Public rebuildRegistry surface lifted from test-local reembedRegistry helper\n// status: accepted (issue #338, WI-EMBED-MODEL-MIGRATION-PATH)\n// rationale: The private reembedRegistry helper at discovery-eval-full-corpus.test.ts:375\n// (DEC-V3-DISCOVERY-EVAL-FIX-001 H4) re-embeds all stored blocks using a new provider.\n// This WI lifts that logic to a public surface so the CLI `yakcc registry rebuild` command\n// and the programmatic autoRebuild path can invoke it.\n//\n// Design choices:\n// - Accepts (registry, embeddings?) — same signature as the test-local helper.\n// embeddings is optional; when absent, the rebuild re-embeds using the provider\n// that was passed to openRegistry() (the registry's own provider).\n// - Uses storeBlock() as the sole mutation path — DELETE+INSERT on contract_embeddings is\n// the existing idempotent update mechanism; no schema migration needed for same-dim swap.\n// - onProgress callback enables CLI progress reporting without coupling to console.log.\n// - Idempotent: calling twice produces identical embeddings (deterministic provider).\n//\n// This is single-dimension-to-single-dimension only. The bge-small-en-v1.5 swap is\n// 384→384 (same schema-FLOAT[384] column); no DDL changes required.\n// (DEC-V3-DISCOVERY-D5-EMBED-MODEL-EXPERIMENT-001 constraint preserved.)\n//\n// DEC-EMBED-010 is PRESERVED: rebuildRegistry creates a new embedding index consistent\n// with the current provider. It does NOT bypass the cross-provider rejection gate —\n// callers that open the registry after rebuild will find a consistent provider+vector state.\n//\n// The test-local helper at discovery-eval-full-corpus.test.ts:375 duplicates this logic.\n// Per the scope authority note: that test-local helper SHOULD be replaced by an import\n// from this module to avoid drift; if scope pressure prevents replacement, the duplication\n// is documented explicitly here. Future Implementers: prefer importing from rebuild.ts.\n\nimport type { EmbeddingProvider } from \"@yakcc/contracts\";\nimport type { BlockMerkleRoot, Registry } from \"./index.js\";\n\n/**\n * Options for rebuildRegistry().\n */\nexport interface RebuildRegistryOptions {\n /**\n * Optional progress callback. Called once per re-embedded block with the\n * count of blocks processed so far and the total count.\n *\n * Example: `(done, total) => console.log(`rebuilding ${done}/${total}...`)`\n */\n onProgress?: ((done: number, total: number) => void) | undefined;\n}\n\n/**\n * Result of a rebuildRegistry() call.\n */\nexport interface RebuildResult {\n /** Number of blocks that were re-embedded. */\n readonly reembedded: number;\n /** Model ID of the embedding provider used for the rebuild. */\n readonly modelId: string;\n}\n\n/**\n * Re-embed all stored blocks in a registry using the given (or registry-default) embedding provider.\n *\n * This is the canonical migration path after an embedding model swap\n * (e.g. MiniLM-L6-v2 → bge-small-en-v1.5, DEC-EMBED-MODEL-DEFAULT-002, PR #336).\n * It iterates every stored block via `enumerateSpecs → selectBlocks → getBlock`\n * and re-stores each block via `storeBlock()`, which triggers `DELETE+INSERT` on\n * `contract_embeddings` — replacing the stale embedding vector with a fresh one\n * from the current provider.\n *\n * Idempotent: calling twice on the same registry with the same provider produces\n * identical embeddings (each call replaces the same vector with the same new value).\n *\n * Safe for same-dimension swaps (384→384): no DDL changes are needed because\n * the `contract_embeddings` schema is FLOAT[384] — both MiniLM-L6-v2 and\n * bge-small-en-v1.5 use 384 dimensions. Cross-dimension migration (384→768) is\n * out of scope for this WI (DEC-V3-DISCOVERY-D5-EMBED-MODEL-EXPERIMENT-001).\n *\n * NOT a bypass of DEC-EMBED-010: after a successful rebuild, the registry's\n * stored vectors are consistent with the provider's modelId, so the cross-provider\n * rejection gate will no longer fire for that provider.\n *\n * Lifted from the test-local `reembedRegistry` helper at\n * `packages/registry/src/discovery-eval-full-corpus.test.ts:375`\n * (DEC-V3-DISCOVERY-EVAL-FIX-001 H4). That helper should import from this module\n * to avoid drift; see the authority note in the @decision block above.\n *\n * @param registry - An already-opened Registry instance. The registry's own\n * embedding provider (the one passed to openRegistry) is used for re-embedding.\n * @param embeddings - The embedding provider whose modelId to record in the result.\n * Pass the same provider used when calling openRegistry(). Required for the result\n * modelId field; the actual embedding happens via storeBlock() on the registry.\n * @param options - Optional configuration including a progress callback.\n * @returns The number of blocks re-embedded and the provider model ID used.\n *\n * @example\n * // After a model swap: open registry with the new provider and rebuild.\n * const provider = createLocalEmbeddingProvider(); // bge-small-en-v1.5\n * const registry = await openRegistry(dbPath, { embeddings: provider });\n * const result = await rebuildRegistry(registry, provider, {\n * onProgress: (done, total) => console.log(`${done}/${total} blocks rebuilt`),\n * });\n * console.log(`Rebuilt ${result.reembedded} blocks with ${result.modelId}`);\n */\nexport async function rebuildRegistry(\n registry: Registry,\n embeddings: EmbeddingProvider,\n options?: RebuildRegistryOptions,\n): Promise<RebuildResult> {\n const specHashes = await registry.enumerateSpecs();\n\n // Pre-count total blocks for accurate progress reporting.\n let total = 0;\n const specBlocks: Array<{ roots: readonly BlockMerkleRoot[] }> = [];\n for (const sh of specHashes) {\n const roots = await registry.selectBlocks(sh);\n specBlocks.push({ roots });\n total += roots.length;\n }\n\n let reembedded = 0;\n for (const { roots } of specBlocks) {\n for (const root of roots) {\n const block = await registry.getBlock(root);\n if (block === null) continue;\n // storeBlock always runs DELETE+INSERT on contract_embeddings, even when\n // INSERT OR IGNORE skips the blocks row (block already present).\n // This replaces the stale embedding vector with a fresh one from the\n // current provider. Default validateOnStore:true recomputes BMR to verify\n // consistency — the same guard used by all write paths.\n await registry.storeBlock(block);\n reembedded++;\n options?.onProgress?.(reembedded, total);\n }\n }\n\n return {\n reembedded,\n modelId: embeddings.modelId,\n };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-STORAGE-LIBRARY-001: better-sqlite3 + sqlite-vec extension.\n// Status: decided (WI-003)\n// Rationale: better-sqlite3 is synchronous, has the best Node.js performance\n// profile of any SQLite binding, and is widely used. sqlite-vec provides the\n// vec0 virtual table that backs the vector index. Both are mature enough for\n// the v0 local-only requirement. The sync API is fine for v0 (no concurrent\n// writers; the registry is a CLI tool). Async wrappers are added at the\n// Promise boundary to match the Registry interface.\n\n// @decision DEC-STORAGE-FAIL-LOUD-001: No in-memory fallback on SQLite open\n// failure. Status: decided (WI-003)\n// Rationale: A silent fallback would mask DB errors and let callers believe\n// they have a real registry when they don't. Fail loudly with a descriptive\n// error so the operator knows immediately that the DB is unavailable.\n\n// @decision DEC-STORAGE-IDEMPOTENT-001: storeBlock() uses INSERT OR IGNORE for\n// the blocks table to ensure idempotency on re-store of the same\n// content-addressed block_merkle_root. The vector table uses DELETE+INSERT for\n// the same reason (vec0 does not support INSERT OR IGNORE / ON CONFLICT).\n// Status: decided (WI-T03, continuing DEC-STORAGE-IDEMPOTENT-001 from WI-003)\n// Rationale: Block identity is content-addressed; the same block_merkle_root\n// always means the same content. Idempotent store means callers never need to\n// check for existence before storing.\n\n// @decision DEC-SCHEMA-MIGRATION-002: WI-T03 clean re-create schema.\n// Status: decided (MASTER_PLAN.md WI-T03 Evaluation Contract)\n// Rationale: The v0 (contracts, implementations) two-table schema is replaced\n// with a single `blocks` table keyed by block_merkle_root with a spec_hash\n// index. No dual-table coexistence; no read-time fallback derivation of\n// block_merkle_root (the column must be stored). See schema.ts for DDL.\n\nimport { blake3 } from \"@noble/hashes/blake3.js\";\nimport {\n type BlockMerkleRoot,\n type CanonicalAstHash,\n type EmbeddingProvider,\n type ProofManifest,\n type QueryIntentCard,\n type SpecHash,\n type SpecYak,\n canonicalAstHash,\n canonicalize,\n canonicalizeQueryText,\n blockMerkleRoot as computeBlockMerkleRoot,\n generateEmbedding,\n} from \"@yakcc/contracts\";\nimport Database from \"better-sqlite3\";\nimport * as sqliteVec from \"sqlite-vec\";\nimport type {\n BlockTripletRow,\n BootstrapManifestEntry,\n CandidateMatch,\n CandidateNearMiss,\n FindCandidatesByQueryOptions,\n FindCandidatesByQueryResult,\n FindCandidatesOptions,\n ForeignRefRow,\n IntentQuery,\n PerDimensionScores,\n Provenance,\n QueryCandidate,\n Registry,\n SourceFileGlueEntry,\n WorkspacePlumbingEntry,\n} from \"./index.js\";\nimport { SCHEMA_VERSION, applyMigrations } from \"./schema.js\";\nimport { structuralMatch } from \"./search.js\";\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Serialize a Float32Array to a Buffer for sqlite-vec storage. */\nfunction serializeEmbedding(vec: Float32Array): Buffer {\n return Buffer.from(vec.buffer, vec.byteOffset, vec.byteLength);\n}\n\n// ---------------------------------------------------------------------------\n// Internal DB row shapes\n// ---------------------------------------------------------------------------\n\ninterface BlockRow {\n block_merkle_root: string;\n spec_hash: string;\n spec_canonical_bytes: Buffer;\n impl_source: string;\n proof_manifest_json: string;\n level: string;\n created_at: number;\n canonical_ast_hash: string;\n /** NULL for root blocks; non-NULL for atoms shaved from a parent block. */\n parent_block_root: string | null;\n /**\n * Discriminator column added in migration 6 (DEC-V2-FOREIGN-BLOCK-SCHEMA-001).\n * 'local' for all pre-v6 rows (DEFAULT covers them); 'foreign' for foreign atoms.\n */\n kind: string;\n /**\n * npm package name or Node built-in specifier. Non-NULL iff kind='foreign'.\n * NULL for local blocks and for all pre-v6 rows.\n */\n foreign_pkg: string | null;\n /**\n * Exported symbol name at the use site. Non-NULL iff kind='foreign'.\n * NULL for local blocks and for all pre-v6 rows.\n */\n foreign_export: string | null;\n /**\n * Optional BLAKE3 hash of the .d.ts declaration text. NULL when not snapshotted.\n * Only meaningful when kind='foreign'.\n */\n foreign_dts_hash: string | null;\n\n // ---------------------------------------------------------------------------\n // Migration-7 fields (DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001 / P1)\n // NULL for all pre-v7 rows (no backfill UPDATE — forbidden shortcut #4).\n // Provenance is populated by re-running `yakcc bootstrap`.\n // ---------------------------------------------------------------------------\n\n /**\n * Workspace package directory (e.g. 'packages/cli'). NULL for foreign atoms,\n * seed blocks, and all pre-v7 rows. First-observed-wins via INSERT OR IGNORE.\n */\n source_pkg: string | null;\n\n /**\n * Workspace-relative path of the originating .ts source file\n * (e.g. 'packages/cli/src/commands/compile.ts'). NULL for foreign atoms,\n * seed blocks, and all pre-v7 rows.\n */\n source_file: string | null;\n\n /**\n * Byte offset of the atom's implSource within source_file. NULL when unknown.\n * NOT folded into blockMerkleRoot — provenance is metadata only.\n */\n source_offset: number | null;\n}\n\ninterface TestHistoryRow {\n suite_id: string;\n passed: number;\n at: number;\n}\n\ninterface RuntimeExposureRow {\n requests_seen: number;\n last_seen: number | null;\n}\n\ninterface StrictnessEdgeRow {\n stricter_root: string;\n looser_root: string;\n}\n\n/** One row from the block_artifacts table (WI-022 / DEC-V1-FEDERATION-WIRE-ARTIFACTS-002). */\ninterface BlockArtifactRow {\n path: string;\n bytes: Buffer;\n declaration_index: number;\n}\n\n// ---------------------------------------------------------------------------\n// SQLite-backed Registry implementation (v0.6 triplet schema)\n// ---------------------------------------------------------------------------\n\nclass SqliteRegistry implements Registry {\n private readonly db: Database.Database;\n private readonly embeddings: EmbeddingProvider;\n private readonly autoRebuild: boolean;\n private closed = false;\n\n // ---------------------------------------------------------------------------\n // @decision DEC-V2-OCCURRENCE-WRITE-PERF-001\n // @title Hoist DELETE + INSERT prepared statements outside the per-file loop\n // @status decided (WI-V2-BOOTSTRAP-PERF-OCCURRENCES / issue #377)\n // @rationale\n // Prior implementation called this.db.prepare(...) on every invocation of\n // replaceSourceFileOccurrences (once per source file during bootstrap).\n // With ~145 files, that is 290 wasted prepare() calls (DELETE + INSERT each).\n // better-sqlite3 prepare() parses and compiles SQL on every call; reusing the\n // same Statement object across files saves that compilation overhead.\n // The fix: prepare once in the constructor, bind new parameters per call.\n // Per-file transaction boundary (DEC-V2-OCCURRENCE-DELETE-INSERT-001) is\n // preserved — only the prepare() location moves, not the transaction scope.\n // ---------------------------------------------------------------------------\n private readonly stmtDeleteOccurrences: Database.Statement<[string, string]>;\n private readonly stmtInsertOccurrence: Database.Statement<\n [string, string, number, number, string]\n >;\n\n constructor(db: Database.Database, embeddings: EmbeddingProvider, autoRebuild = false) {\n this.db = db;\n this.embeddings = embeddings;\n this.autoRebuild = autoRebuild;\n // Prepare the occurrence write-path statements once for the lifetime of this\n // registry instance. See @decision DEC-V2-OCCURRENCE-WRITE-PERF-001.\n this.stmtDeleteOccurrences = db.prepare<[string, string]>(\n \"DELETE FROM block_occurrences WHERE source_pkg = ? AND source_file = ?\",\n );\n this.stmtInsertOccurrence = db.prepare<[string, string, number, number, string]>(\n \"INSERT INTO block_occurrences(source_pkg, source_file, source_offset, length, block_merkle_root) VALUES (?, ?, ?, ?, ?)\",\n );\n }\n\n // -------------------------------------------------------------------------\n // storeBlock\n // -------------------------------------------------------------------------\n\n async storeBlock(row: BlockTripletRow, opts: { validateOnStore?: boolean } = {}): Promise<void> {\n this.assertOpen();\n\n const validateOnStore = opts.validateOnStore !== false; // default: true\n\n // -----------------------------------------------------------------------\n // Integrity check: recompute blockMerkleRoot from stored fields and compare\n // against row.blockMerkleRoot. Rejects rows whose stored root doesn't match\n // the canonical contracts formula (DEC-CONTRACTS-AUTHORITY-001).\n //\n // The check is default-on (validateOnStore: true). Migration-internal callers\n // that pre-date artifact threading pass validateOnStore: false to skip.\n //\n // @decision DEC-V1-FEDERATION-WIRE-ARTIFACTS-002: Registry-side integrity gate.\n // Status: decided (WI-022). Rationale: closes the same loop the wire-side\n // gate will close in WI-020; ensures every persisted row's stored merkle root\n // matches its bytes — foundational for federation round-trip correctness.\n // -----------------------------------------------------------------------\n // -----------------------------------------------------------------------\n // L2-I3 invariant guard: kind='foreign' requires foreign_pkg and\n // foreign_export to be non-null. Enforced here at the single insert path\n // (DEC-V2-FOREIGN-BLOCK-SCHEMA-001 / WI-V2-04 L2-I3).\n // This is an application-level guard because SQLite's ADD COLUMN cannot\n // add cross-column CHECK constraints after the fact.\n // -----------------------------------------------------------------------\n if (row.kind === \"foreign\") {\n if (row.foreignPkg == null || row.foreignExport == null) {\n const err = new Error(\n \"storeBlock invariant violation (L2-I3): kind='foreign' requires foreignPkg and foreignExport to be non-null\",\n );\n (err as Error & { reason: string }).reason = \"foreign_invariant_failed\";\n throw err;\n }\n }\n\n if (validateOnStore) {\n let recomputed: string;\n if (row.kind === \"foreign\") {\n // Foreign blocks: identity is keyed on (kind, pkg, export, dtsHash?).\n // Pass the ForeignTripletFields shape directly to blockMerkleRoot().\n // L2-I3 guard above guarantees foreignPkg/foreignExport are non-null\n // for kind='foreign' before we reach this point.\n const pkg = row.foreignPkg ?? \"\";\n const foreignExport = row.foreignExport ?? \"\";\n recomputed = computeBlockMerkleRoot({\n kind: \"foreign\",\n pkg,\n export: foreignExport,\n ...(row.foreignDtsHash != null ? { dtsHash: row.foreignDtsHash } : {}),\n });\n } else {\n // Local blocks: identity is keyed on (spec, implSource, manifest, artifacts).\n const spec = JSON.parse(Buffer.from(row.specCanonicalBytes).toString(\"utf-8\")) as SpecYak;\n const manifest = JSON.parse(row.proofManifestJson) as ProofManifest;\n // row.artifacts is ReadonlyMap; blockMerkleRoot() accepts Map — cast is safe.\n const artifacts = row.artifacts as Map<string, Uint8Array>;\n recomputed = computeBlockMerkleRoot({\n spec: spec as unknown as SpecYak,\n implSource: row.implSource,\n manifest,\n artifacts,\n });\n }\n if (recomputed !== row.blockMerkleRoot) {\n const err = new Error(\n `storeBlock integrity check failed: stored blockMerkleRoot ${row.blockMerkleRoot} does not match recomputed value ${recomputed}`,\n );\n (err as Error & { reason: string }).reason = \"integrity_failed\";\n throw err;\n }\n }\n\n const now = row.createdAt > 0 ? row.createdAt : Date.now();\n\n // Parse the spec canonical bytes back to a SpecYak so we can generate an\n // embedding. The canonical bytes were produced by canonicalize(spec), so\n // we JSON-parse them (canonicalize produces UTF-8 JSON) to get the spec.\n // We need the spec text (its canonical JSON) for embedding generation.\n // The embedding provider accepts a text string derived from the spec.\n // We use the UTF-8 string of the canonical bytes as the embedding input,\n // consistent with the v0 approach (generateEmbedding expects a ContractSpec\n // but we pass the decoded canonical text as a surrogate spec string).\n const specText = Buffer.from(row.specCanonicalBytes).toString(\"utf-8\");\n // Parse the canonical JSON to get the spec object for the embedding provider.\n const specObj = JSON.parse(specText) as SpecYak;\n const embedding = await generateEmbedding(\n specObj as unknown as Parameters<typeof generateEmbedding>[0],\n this.embeddings,\n );\n\n // Trust the canonicalAstHash already computed by the caller (e.g. makeBlockRow,\n // the seed package, and all future write paths). Callers are responsible for\n // computing canonicalAstHash once at row-construction time; the storage layer\n // accepts the value as-given rather than re-parsing implSource through ts-morph\n // on every write. The migration backfill path (see migrateAddCanonicalAstHash)\n // is the only code path that must compute the hash here because pre-WI-012-02\n // rows arrive without the field populated.\n const implAstHash = row.canonicalAstHash;\n\n // @decision DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001\n // INSERT OR IGNORE is the load-bearing first-observed-wins mechanism.\n // A second storeBlock call for the same blockMerkleRoot with null provenance\n // does NOT overwrite the existing non-null provenance — the entire row is\n // ignored on conflict (UNIQUE constraint on block_merkle_root PRIMARY KEY).\n // This is correct: the registry is monotonic; provenance is set at first-write\n // and never changed. Callers that need to update provenance must not assume\n // a second storeBlock will succeed — it will silently no-op per this design.\n const insertBlock = this.db.prepare<\n [\n string,\n string,\n Buffer,\n string,\n string,\n string,\n number,\n string,\n string | null,\n string,\n string | null,\n string | null,\n string | null,\n string | null,\n string | null,\n number | null,\n ]\n >(\n \"INSERT OR IGNORE INTO blocks(block_merkle_root, spec_hash, spec_canonical_bytes, impl_source, proof_manifest_json, level, created_at, canonical_ast_hash, parent_block_root, kind, foreign_pkg, foreign_export, foreign_dts_hash, source_pkg, source_file, source_offset) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\",\n );\n\n // vec0 does not support INSERT OR IGNORE / ON CONFLICT, so use DELETE+INSERT\n // to make this idempotent (DEC-STORAGE-IDEMPOTENT-001).\n const deleteEmbedding = this.db.prepare<[string]>(\n \"DELETE FROM contract_embeddings WHERE spec_hash = ?\",\n );\n const insertEmbedding = this.db.prepare<[string, Buffer]>(\n \"INSERT INTO contract_embeddings(spec_hash, embedding) VALUES (?, ?)\",\n );\n\n // Artifact INSERT prepared statement. One row per Map entry.\n // INSERT OR IGNORE: idempotent on re-store of the same (block_merkle_root, path).\n // The composite PK prevents duplicate rows; a second store is a no-op.\n const insertArtifact = this.db.prepare<[string, string, Buffer, number]>(\n \"INSERT OR IGNORE INTO block_artifacts(block_merkle_root, path, bytes, declaration_index) VALUES (?, ?, ?, ?)\",\n );\n\n const embeddingBuf = serializeEmbedding(embedding);\n\n // Capture artifact entries in Map iteration order (= declaration order per\n // the BlockTriplet contract: keys match manifest.artifacts[*].path in order).\n const artifactEntries = [...row.artifacts.entries()];\n\n const txn = this.db.transaction(() => {\n insertBlock.run(\n row.blockMerkleRoot,\n row.specHash,\n Buffer.from(row.specCanonicalBytes),\n row.implSource,\n row.proofManifestJson,\n row.level,\n now,\n implAstHash,\n row.parentBlockRoot ?? null,\n // Migration-6 columns (DEC-V2-FOREIGN-BLOCK-SCHEMA-001 / L2-I3).\n // kind defaults to 'local' for rows that omit the field (backward compat).\n row.kind ?? \"local\",\n row.foreignPkg ?? null,\n row.foreignExport ?? null,\n row.foreignDtsHash ?? null,\n // Migration-7 columns (DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001 / P1).\n // Optional fields: callers that omit them (federation.ts, seed.ts,\n // assemble-candidate.ts) pass null — correct for non-bootstrap atoms.\n // Bootstrap walker sets real values for local atoms via ShaveOptions.\n // INSERT OR IGNORE means first-observed-wins: if the row already exists\n // with non-null provenance, this second store is a silent no-op.\n row.sourcePkg ?? null,\n row.sourceFile ?? null,\n row.sourceOffset ?? null,\n );\n // Only write the embedding if the spec_hash doesn't already have one.\n // Check by attempting DELETE (no-op if absent) then INSERT.\n // This is safe for idempotent re-stores of the same spec_hash because\n // the embedding is deterministic for the same spec.\n deleteEmbedding.run(row.specHash);\n insertEmbedding.run(row.specHash, embeddingBuf);\n // Persist artifact bytes in declaration order (Map iteration order).\n // INSERT OR IGNORE ensures idempotency on re-store: the composite PK\n // (block_merkle_root, path) rejects duplicates without error.\n for (let i = 0; i < artifactEntries.length; i++) {\n const entry = artifactEntries[i];\n if (entry === undefined) continue;\n const [artifactPath, artifactBytes] = entry;\n insertArtifact.run(row.blockMerkleRoot, artifactPath, Buffer.from(artifactBytes), i);\n }\n });\n\n txn();\n }\n\n // -------------------------------------------------------------------------\n // selectBlocks — return all BlockMerkleRoots for a given spec hash\n // -------------------------------------------------------------------------\n\n async selectBlocks(specHash: SpecHash): Promise<BlockMerkleRoot[]> {\n this.assertOpen();\n\n // Load all blocks for this spec_hash.\n const blockRows = this.db\n .prepare<[string], BlockRow>(\n \"SELECT * FROM blocks WHERE spec_hash = ? ORDER BY created_at ASC\",\n )\n .all(specHash);\n\n if (blockRows.length === 0) return [];\n\n // Load strictness edges for these blocks (by block_merkle_root).\n const roots = blockRows.map((r) => r.block_merkle_root);\n const placeholders = roots.map(() => \"?\").join(\", \");\n const edgeRows = this.db\n .prepare<string[], StrictnessEdgeRow>(\n `SELECT stricter_root, looser_root FROM strictness_edges WHERE stricter_root IN (${placeholders}) OR looser_root IN (${placeholders})`,\n )\n .all(...roots, ...roots);\n\n // Load passing test counts for each block.\n const passingRunsMap = new Map<string, number>();\n for (const root of roots) {\n const row = this.db\n .prepare<[string], { passing: number }>(\n \"SELECT COUNT(*) AS passing FROM test_history WHERE block_merkle_root = ? AND passed = 1\",\n )\n .get(root);\n passingRunsMap.set(root, row?.passing ?? 0);\n }\n\n // Build edge set restricted to roots in the candidate set.\n const rootSet = new Set(roots);\n type EdgeKey = string;\n const edgeSet = new Set<EdgeKey>();\n for (const e of edgeRows) {\n if (\n rootSet.has(e.stricter_root) &&\n rootSet.has(e.looser_root) &&\n e.stricter_root !== e.looser_root\n ) {\n edgeSet.add(`${e.stricter_root}|${e.looser_root}`);\n }\n }\n\n // Sort by: (1) maximally-strict first, (2) non-functional quality of the\n // spec (parsed from canonical bytes), (3) passing test runs, (4) lex root.\n // For simplicity at v0.6 (all blocks for the same spec_hash share the same\n // spec, so NF tiebreak is the same for all siblings), we sort by:\n // - strictness partial order (stricter first)\n // - more passing test runs\n // - lexicographically smaller block_merkle_root\n const sorted = [...blockRows].sort((a, b) => {\n // Strictness: if a is declared stricter than b, a comes first.\n const aStricterThanB = isStricterThan(\n a.block_merkle_root,\n b.block_merkle_root,\n edgeSet,\n roots,\n );\n const bStricterThanA = isStricterThan(\n b.block_merkle_root,\n a.block_merkle_root,\n edgeSet,\n roots,\n );\n if (aStricterThanB && !bStricterThanA) return -1;\n if (bStricterThanA && !aStricterThanB) return 1;\n\n // Passing test runs — more is better.\n const passingA = passingRunsMap.get(a.block_merkle_root) ?? 0;\n const passingB = passingRunsMap.get(b.block_merkle_root) ?? 0;\n if (passingA !== passingB) return passingB - passingA;\n\n // Lexicographic tiebreak — smaller root first.\n return a.block_merkle_root < b.block_merkle_root ? -1 : 1;\n });\n\n return sorted.map((r) => r.block_merkle_root as BlockMerkleRoot);\n }\n\n // -------------------------------------------------------------------------\n // getBlock — retrieve a full block row by BlockMerkleRoot\n // -------------------------------------------------------------------------\n\n async getBlock(merkleRoot: BlockMerkleRoot): Promise<BlockTripletRow | null> {\n this.assertOpen();\n\n const row = this.db\n .prepare<[string], BlockRow>(\"SELECT * FROM blocks WHERE block_merkle_root = ?\")\n .get(merkleRoot);\n\n if (row === undefined) return null;\n\n // Eagerly hydrate artifact bytes in declaration order.\n // Ordered by declaration_index to reconstruct the Map in manifest order.\n // Pre-WI-022 blocks that have no block_artifacts rows return an empty Map\n // (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002 migration note).\n const artifactRows = this.db\n .prepare<[string], BlockArtifactRow>(\n \"SELECT path, bytes, declaration_index FROM block_artifacts WHERE block_merkle_root = ? ORDER BY declaration_index ASC\",\n )\n .all(merkleRoot);\n\n return hydrateBlock(row, artifactRows);\n }\n\n // -------------------------------------------------------------------------\n // getForeignRefs — return block_foreign_refs rows for a parent block\n // (DEC-V2-FOREIGN-BLOCK-SCHEMA-001 / WI-V2-04 L2)\n // -------------------------------------------------------------------------\n\n async getForeignRefs(merkleRoot: BlockMerkleRoot): Promise<readonly ForeignRefRow[]> {\n this.assertOpen();\n\n const rows = this.db\n .prepare<\n [string],\n { parent_block_root: string; foreign_block_root: string; declaration_index: number }\n >(\n \"SELECT parent_block_root, foreign_block_root, declaration_index FROM block_foreign_refs WHERE parent_block_root = ? ORDER BY declaration_index ASC\",\n )\n .all(merkleRoot);\n\n return rows.map((r) => ({\n parentBlockRoot: r.parent_block_root as BlockMerkleRoot,\n foreignBlockRoot: r.foreign_block_root as BlockMerkleRoot,\n declarationIndex: r.declaration_index,\n }));\n }\n\n // -------------------------------------------------------------------------\n // findByCanonicalAstHash — look up blocks by impl canonical AST hash\n // -------------------------------------------------------------------------\n\n async findByCanonicalAstHash(hash: CanonicalAstHash): Promise<readonly BlockMerkleRoot[]> {\n this.assertOpen();\n\n const rows = this.db\n .prepare<[string], { block_merkle_root: string }>(\n \"SELECT block_merkle_root FROM blocks WHERE canonical_ast_hash = ? ORDER BY created_at ASC, block_merkle_root ASC\",\n )\n .all(hash);\n\n return rows.map((r) => r.block_merkle_root as BlockMerkleRoot);\n }\n\n // -------------------------------------------------------------------------\n // getProvenance — test history + runtime exposure for a block\n // -------------------------------------------------------------------------\n\n async getProvenance(merkleRoot: BlockMerkleRoot): Promise<Provenance> {\n this.assertOpen();\n\n const testRows = this.db\n .prepare<[string], TestHistoryRow>(\n \"SELECT suite_id, passed, at FROM test_history WHERE block_merkle_root = ? ORDER BY at ASC\",\n )\n .all(merkleRoot);\n\n const exposureRow = this.db\n .prepare<[string], RuntimeExposureRow>(\n \"SELECT requests_seen, last_seen FROM runtime_exposure WHERE block_merkle_root = ?\",\n )\n .get(merkleRoot);\n\n const testHistory = testRows.map((r) => ({\n runAt: new Date(r.at).toISOString(),\n passed: r.passed === 1,\n caseCount: 0, // caseCount not persisted in v0.6 schema\n }));\n\n const runtimeExposure =\n exposureRow !== undefined && exposureRow.requests_seen > 0\n ? [\n {\n observedAt: new Date(exposureRow.last_seen ?? Date.now()).toISOString(),\n assembledInto: merkleRoot, // placeholder; real assembledInto tracked by compile\n },\n ]\n : [];\n\n return { testHistory, runtimeExposure };\n }\n\n // -------------------------------------------------------------------------\n // findCandidatesByIntent — vector KNN search + optional structural rerank\n // -------------------------------------------------------------------------\n\n // @decision DEC-VECTOR-RETRIEVAL-002\n // title: Query-text derivation for findCandidatesByIntent\n // status: accepted (see also index.ts DEC-VECTOR-RETRIEVAL-002 annotation)\n // rationale: behavior + \"\\n\" + \"name: typeHint\" per input + per output gives\n // a text that mirrors the embedding input used at storeBlock time. The\n // spec's embedding is derived from its behavior+parameter text; the query\n // uses the same template so query and document vectors are comparable.\n\n async findCandidatesByIntent(\n card: IntentQuery,\n options: FindCandidatesOptions = {},\n ): Promise<readonly CandidateMatch[]> {\n this.assertOpen();\n\n const k = options.k ?? 10;\n\n // Derive query text from the card (DEC-VECTOR-RETRIEVAL-002).\n // behavior + each input as \"name: typeHint\" + each output as \"name: typeHint\"\n const parts: string[] = [card.behavior];\n for (const p of card.inputs) {\n parts.push(`${p.name}: ${p.typeHint}`);\n }\n for (const p of card.outputs) {\n parts.push(`${p.name}: ${p.typeHint}`);\n }\n const queryText = parts.join(\"\\n\");\n\n // Generate embedding for the query text by calling the provider directly.\n // We bypass generateEmbedding() (which calls canonicalizeText on a ContractSpec)\n // and call embed() directly with our derived query text. This is intentional:\n // the query text is already in the same format as the text embedded at write time\n // (storeBlock calls generateEmbedding on the spec, which canonicalizes its JSON;\n // we match that space by embedding the same behavior+params text directly).\n const queryEmbedding = await this.embeddings.embed(queryText);\n\n const queryBuf = serializeEmbedding(queryEmbedding);\n\n // KNN query against contract_embeddings (vec0 virtual table).\n // Returns rows ordered by ascending distance (closest first).\n // The vec0 KNN syntax: WHERE embedding MATCH ? AND k = N ORDER BY distance\n interface EmbeddingRow {\n spec_hash: string;\n distance: number;\n }\n\n let embeddingRows: EmbeddingRow[];\n try {\n embeddingRows = this.db\n .prepare<[Buffer, number], EmbeddingRow>(\n \"SELECT spec_hash, distance FROM contract_embeddings WHERE embedding MATCH ? AND k = ? ORDER BY distance\",\n )\n .all(queryBuf, k);\n } catch {\n // If the embeddings table is empty, vec0 may throw. Return [] gracefully.\n return [];\n }\n\n if (embeddingRows.length === 0) return [];\n\n // For each spec_hash, find the best block (first from selectBlocks which uses\n // the strictness-aware sort). We hydrate one block per spec_hash hit.\n const results: CandidateMatch[] = [];\n\n for (const eRow of embeddingRows) {\n const specHash = eRow.spec_hash as SpecHash;\n // Get all blocks for this spec, ordered by strictness/quality.\n const roots = await this.selectBlocks(specHash);\n if (roots.length === 0) continue;\n\n // Take the best block (first in strictness order).\n const bestRoot = roots[0];\n if (bestRoot === undefined) continue;\n\n const block = await this.getBlock(bestRoot);\n if (block === null) continue;\n\n results.push({\n block,\n cosineDistance: eRow.distance,\n });\n }\n\n // Optional structural rerank (DEC-VECTOR-RETRIEVAL-003).\n if (options.rerank === \"structural\") {\n // Build a minimal SpecYak from the card for structural comparison.\n // structuralMatch requires SpecYak; we construct the minimum valid shape.\n const querySpec: SpecYak = {\n name: \"query\",\n inputs: card.inputs.map((p) => ({ name: p.name, type: p.typeHint })),\n outputs: card.outputs.map((p) => ({ name: p.name, type: p.typeHint })),\n preconditions: [],\n postconditions: [],\n invariants: [],\n effects: [],\n level: \"L0\",\n };\n\n // Annotate each result with a structural score, then sort by combined score.\n const annotated = results.map((m) => {\n const candidateSpec = JSON.parse(\n Buffer.from(m.block.specCanonicalBytes).toString(\"utf-8\"),\n ) as SpecYak;\n const matchResult = structuralMatch(querySpec, candidateSpec);\n const structuralScore = matchResult.matches ? 1.0 : 0.0;\n return { ...m, structuralScore };\n });\n\n // Sort by combined score descending: (1 - cosineDistance) + structuralScore.\n // cosineDistance is in [0, 2] on the unit sphere; (1 - d) maps to [-1, 1].\n // structuralScore is 0 or 1 at v0. Higher combined score = better match.\n annotated.sort((a, b) => {\n const sa = 1 - a.cosineDistance + a.structuralScore;\n const sb = 1 - b.cosineDistance + b.structuralScore;\n return sb - sa;\n });\n\n return annotated;\n }\n\n // Default: return in KNN distance order (already ascending from vec0 query).\n return results;\n }\n\n // -------------------------------------------------------------------------\n // findCandidatesByQuery — D3 5-stage multi-dimensional discovery pipeline\n // -------------------------------------------------------------------------\n\n // @decision DEC-V3-IMPL-QUERY-002\n // title: Cross-provider rejection at constructor layer\n // status: accepted\n // rationale: The embedding provider modelId is snapshotted at openRegistry()\n // time (this.embeddings.modelId). findCandidatesByQuery() checks the caller's\n // queryEmbeddings.modelId against the snapshot before any KNN call. Mismatch\n // throws a typed Error with reason='cross_provider_rejected' (D2 cross-provider\n // invariant). Silent fallback is explicitly forbidden (D2 §\"Cross-provider\n // rejection invariant\"). This check fires at the method boundary, not at\n // first-use, so mocks that throw if reached are the correct test sentinel.\n\n // @decision DEC-V3-IMPL-QUERY-003\n // title: Per-dimension weight collapse for v1 (key 'unified')\n // status: accepted\n // rationale: v3 uses the single contract_embeddings.embedding column (no\n // per-column KNN — migration 7 for 5-column schema is deferred). Per-dimension\n // weights in QueryIntentCard.weights are accepted by the API for forward-compat\n // but collapsed to a single 'unified' weight. combinedScore = 1 - L²/4\n // (DEC-V3-IMPL-QUERY-007 re-stated; canonical site: discovery-eval-helpers.ts).\n // perDimensionScores carries a single entry under the queried dimension key(s).\n\n // @decision DEC-V3-IMPL-QUERY-006\n // title: D3 5-stage pipeline + CandidateNearMiss\n // status: accepted\n // rationale: Pipeline stages per D3 §Q3:\n // Stage 1: KNN with K' = max(topK*5, 50) against contract_embeddings.\n // Stage 2: structuralMatch() gate on QueryIntentCard.signature.\n // Stage 3: Strictness gate on level, nonFunctional.purity, nonFunctional.threadSafety.\n // Stage 4: Reserved no-op (DEC-VERIFY-010 v3.1 trigger; MUST NOT implement logic).\n // Stage 5: combinedScore ranking (desc), ε=0.02 lex-BlockMerkleRoot tiebreaker\n // (smaller wins), minScore filter, truncate to topK.\n // When Stages 2–4 reduce to 0: near-miss envelope from Stage 1 K' set.\n // autoAccepted: top-1.combinedScore > 0.85 AND (top-1 - top-2).combinedScore > 0.15.\n\n /**\n * Query the registry for atom candidates matching a multi-dimensional intent card.\n *\n * TRUST DOMAIN: This API is correct only within a same-process trust domain.\n * Registry-file portability across machines with different default embedding\n * providers is a known-untreated boundary deferred to migration-7 /\n * WI-V3-DISCOVERY-IMPL-MIGRATION-VERIFY. The cross-provider rejection gate\n * (see below) catches provider mismatches within the same process, but it\n * cannot detect binary-identical model IDs from different machines.\n *\n * @see DEC-V3-IMPL-QUERY-002 for the cross-provider rejection design.\n */\n async findCandidatesByQuery(\n query: QueryIntentCard,\n options?: FindCandidatesByQueryOptions,\n ): Promise<FindCandidatesByQueryResult> {\n this.assertOpen();\n\n // @decision DEC-EMBED-MODEL-MIGRATION-001\n // title: Enriched cross-provider rejection error with remediation command\n // status: accepted (issue #338, WI-EMBED-MODEL-MIGRATION-PATH)\n // rationale: When the bge-small-en-v1.5 swap (DEC-EMBED-MODEL-DEFAULT-002, PR #336)\n // causes a model mismatch at query time, the user needs to know (a) what model is\n // stored, (b) what model is current, and (c) exactly what command to run to fix it.\n // This enriches DEC-V3-IMPL-QUERY-002 with the remediation path.\n // DEC-EMBED-010 is PRESERVED: we add a remediation path, not a bypass.\n\n // Cross-provider rejection (DEC-V3-IMPL-QUERY-002):\n // Verify model ID before any KNN call. Throw loud error on mismatch.\n // TRUST DOMAIN NOTE: This gate is correct only within a same-process trust domain.\n // Registry-file portability across machines with different default embedding\n // providers is a known-untreated boundary deferred to migration-7 /\n // WI-V3-DISCOVERY-IMPL-MIGRATION-VERIFY. See DEC-V3-IMPL-QUERY-002.\n const registryModelId = this.embeddings.modelId;\n const callerModelId = options?.queryEmbeddings?.modelId;\n if (callerModelId !== undefined && callerModelId !== registryModelId) {\n const registryPath = this.db.name;\n\n // @decision DEC-EMBED-MODEL-MIGRATION-001 (autoRebuild escape valve)\n // When autoRebuild is true (explicit opt-in), trigger a full re-embed of all\n // stored blocks using the current provider, then retry the query. This is\n // intended for CI / clean-rebuild workflows — NOT for interactive use.\n // DEC-EMBED-010 is PRESERVED: the rebuild restores consistency; it does not\n // bypass the check. Default (false) is always fail-loud.\n if (this.autoRebuild) {\n // Dynamic import to avoid circular module initialization:\n // storage.ts → rebuild.ts → index.ts → storage.ts.\n // ESM handles live bindings, but dynamic import defers resolution safely.\n const { rebuildRegistry } = await import(\"./rebuild.js\");\n await rebuildRegistry(this, this.embeddings);\n // After rebuild, the registry's embeddings are consistent with the current\n // provider. The cross-provider check will pass on the retry below because\n // all vectors now match this.embeddings.modelId.\n // Fall through to the rest of findCandidatesByQuery — do NOT recurse to\n // avoid infinite rebuild loops if something is wrong.\n } else {\n const err = new Error(\n `ERROR: registry at ${registryPath} was embedded with model \"${registryModelId}\", but the current query provider uses \"${callerModelId}\". Embeddings are not portable across models. To migrate: run \\`yakcc registry rebuild --path ${registryPath}\\` (or \\`yakcc bootstrap\\` to regenerate the bootstrap-managed registry).`,\n );\n (err as Error & { reason: string }).reason = \"cross_provider_rejected\";\n throw err;\n }\n }\n\n const topK = query.topK ?? 10;\n const kPrime = Math.max(topK * 5, 50); // D3 §Q3: K' = max(K×5, 50)\n\n // -----------------------------------------------------------------------\n // Stage 1 — Vector KNN retrieval with K' candidates\n // -----------------------------------------------------------------------\n\n // @decision DEC-VECTOR-RETRIEVAL-002\n // @title Stage 1 KNN uses canonicalizeQueryText for symmetric query-text derivation\n // @status accepted (supersedes DEC-V3-DISCOVERY-D3-FILTER-STRICTNESS-FIX-001 Stage-1 rationale)\n // @rationale\n // Round-trip requirement (issues #444, #502): a spec stored via storeBlock must appear\n // in the top-K results of findCandidatesByQuery called with the same behavior text.\n // storeBlock embeds canonicalizeText(spec) — full canonical SpecYak JSON. Stage 1 must\n // use the canonicalizeQueryText() path so query and document vectors are derived from\n // the same structural space (DEC-V3-IMPL-QUERY-001 — symmetric query-text derivation).\n //\n // Prior state (DEC-V3-DISCOVERY-D3-FILTER-STRICTNESS-FIX-001, commit dc0bed6):\n // Stage 1 used query.behavior ?? \"\" (plain string), matching findCandidatesByIntent's\n // text path. That change fixed M2=20%→62.5% on the full-corpus harness (issue #309)\n // but introduced a store/query asymmetry that breaks round-trip BMR-in-top-K for\n // freshly atomized atoms (B7 commit slice 3: 22/32 failures; v0-smoke Step 9 FAIL).\n //\n // The round-trip failure mode: the atomized spec's embedding is full canonical JSON;\n // the plain-string query vector lives in a different region of L2 space. Nearby seed\n // atoms (also full-JSON embedded) outrank the exact atom at K=5.\n //\n // canonicalizeQueryText(query) projects only the dimensions present in the card into\n // SpecYak-shaped canonical JSON (absent optional fields are omitted per D1 absent-\n // dimension rule). A behavior-only query produces {\"behavior\":\"...\"} — structurally\n // closer to the stored full JSON than the plain string, placing the query vector\n // in the same structural token-space as document vectors.\n //\n // The no-op rule (issue #319 §Correct semantics) is preserved: optional dimensions\n // absent from the query are omitted by canonicalizeQueryText, so they cannot bias\n // Stage 1 ranking. Only dimensions present in the query card shift the vector.\n const stage1QueryText = canonicalizeQueryText(query);\n const queryEmbedding = await this.embeddings.embed(stage1QueryText);\n const queryBuf = serializeEmbedding(queryEmbedding);\n\n interface EmbeddingRow {\n spec_hash: string;\n distance: number;\n }\n\n let stage1Rows: EmbeddingRow[];\n try {\n stage1Rows = this.db\n .prepare<[Buffer, number], EmbeddingRow>(\n \"SELECT spec_hash, distance FROM contract_embeddings WHERE embedding MATCH ? AND k = ? ORDER BY distance\",\n )\n .all(queryBuf, kPrime);\n } catch {\n // vec0 throws when table is empty\n return { candidates: [], nearMisses: [] };\n }\n\n if (stage1Rows.length === 0) {\n return { candidates: [], nearMisses: [] };\n }\n\n // Hydrate blocks for each Stage 1 candidate.\n // Use the same hydration logic as findCandidatesByIntent: best block per spec_hash.\n interface HydratedCandidate {\n block: BlockTripletRow;\n cosineDistance: number;\n specYak: SpecYak;\n }\n\n const stage1: HydratedCandidate[] = [];\n for (const eRow of stage1Rows) {\n const specHash = eRow.spec_hash as SpecHash;\n const roots = await this.selectBlocks(specHash);\n if (roots.length === 0) continue;\n const bestRoot = roots[0];\n if (bestRoot === undefined) continue;\n const block = await this.getBlock(bestRoot);\n if (block === null) continue;\n const specYak = JSON.parse(\n Buffer.from(block.specCanonicalBytes).toString(\"utf-8\"),\n ) as SpecYak;\n stage1.push({ block, cosineDistance: eRow.distance, specYak });\n }\n\n if (stage1.length === 0) {\n return { candidates: [], nearMisses: [] };\n }\n\n // -----------------------------------------------------------------------\n // Stage 2 — Structural filter (structuralMatch on QueryIntentCard.signature)\n // -----------------------------------------------------------------------\n // Build a minimal SpecYak query shape from the card's signature field.\n // If no signature is provided, this stage is a no-op (all pass).\n\n // Track which Stage 1 candidates failed Stage 2 (for near-miss envelope).\n const stage2Failed: Array<{ item: HydratedCandidate; reason: string }> = [];\n let stage2Passed: HydratedCandidate[];\n\n if (\n query.signature === undefined ||\n (query.signature.inputs === undefined && query.signature.outputs === undefined)\n ) {\n // No-op: all Stage 1 candidates pass through.\n stage2Passed = stage1;\n } else {\n const querySpec: SpecYak = {\n name: \"query\",\n inputs: (query.signature.inputs ?? []).map((p, i) => ({\n name: p.name ?? `arg${i}`,\n type: p.type,\n })),\n outputs: (query.signature.outputs ?? []).map((p, i) => ({\n name: p.name ?? `out${i}`,\n type: p.type,\n })),\n preconditions: [],\n postconditions: [],\n invariants: [],\n effects: [],\n level: \"L0\",\n };\n\n stage2Passed = [];\n for (const item of stage1) {\n const result = structuralMatch(querySpec, item.specYak);\n if (result.matches) {\n stage2Passed.push(item);\n } else {\n const reason = result.reasons.join(\"; \");\n stage2Failed.push({ item, reason });\n }\n }\n }\n\n // -----------------------------------------------------------------------\n // Stage 3 — Strictness filter (level, nonFunctional.purity, threadSafety)\n // -----------------------------------------------------------------------\n // Only applies when QueryIntentCard.nonFunctional is provided.\n // A candidate fails Stage 3 if:\n // - Its purity rank < query's purity rank\n // - Its threadSafety rank < query's threadSafety rank\n // (The same ordering as structuralMatch's NF check.)\n\n const stage3Failed: Array<{ item: HydratedCandidate; reason: string }> = [];\n let stage3Passed: HydratedCandidate[];\n\n const queryNF = query.nonFunctional;\n if (queryNF === undefined) {\n // No-op: all Stage 2 survivors pass through.\n stage3Passed = stage2Passed;\n } else {\n const PURITY_RANK: Record<string, number> = {\n pure: 3,\n io: 2,\n stateful: 1,\n nondeterministic: 0,\n };\n const THREAD_RANK: Record<string, number> = {\n safe: 2,\n sequential: 1,\n unsafe: 0,\n };\n\n stage3Passed = [];\n for (const item of stage2Passed) {\n const reasons: string[] = [];\n const candidateNF = item.specYak.nonFunctional;\n\n // Graceful-skip semantics per DEC-V3-DISCOVERY-D3-FILTER-STRICTNESS-FIX\n // (issue #314, follow-up to DEC-V3-INITIATIVE-002-DISPOSITION):\n // - If candidate has no nonFunctional declaration, the strictness\n // dimension is SKIPPED for this candidate (not a rejection).\n // - Rejection only applies when BOTH query and candidate declare the\n // field AND candidate is strictly weaker.\n // Rationale: corpus + registry have sparse nonFunctional coverage\n // (0/50 in stratified corpus; rare in source-shaved atoms). Treating\n // missing-on-candidate as rejection gave intent-mode A/B +42.5pts M2\n // / +20pts M3 / +0.364 M4 vs query-mode in #289/#309. Graceful-skip is\n // the correct semantics: a candidate that doesn't declare the field is\n // no worse than one that declared it and matched.\n if (candidateNF !== undefined) {\n if (queryNF.purity !== undefined && candidateNF.purity !== undefined) {\n const qRank = PURITY_RANK[queryNF.purity] ?? 0;\n const cRank = PURITY_RANK[candidateNF.purity] ?? 0;\n if (cRank < qRank) {\n reasons.push(`purity=${candidateNF.purity} but query requires ${queryNF.purity}`);\n }\n }\n if (queryNF.threadSafety !== undefined && candidateNF.threadSafety !== undefined) {\n const qRank = THREAD_RANK[queryNF.threadSafety] ?? 0;\n const cRank = THREAD_RANK[candidateNF.threadSafety] ?? 0;\n if (cRank < qRank) {\n reasons.push(\n `threadSafety=${candidateNF.threadSafety} but query requires ${queryNF.threadSafety}`,\n );\n }\n }\n }\n // candidateNF === undefined → graceful skip per\n // @decision DEC-V3-DISCOVERY-D3-FILTER-STRICTNESS-FIX-001 (#319/#314):\n // a candidate that doesn't declare nonFunctional is NOT asserting failure;\n // it simply doesn't assert anything about purity/threadSafety. Stage 3\n // treats this as no-op (pass through), not rejection. The legitimate\n // filter case (candidateNF declared AND strictly weaker) is in the\n // candidateNF !== undefined branch above and remains unchanged.\n\n if (reasons.length > 0) {\n stage3Failed.push({ item, reason: reasons.join(\"; \") });\n } else {\n stage3Passed.push(item);\n }\n }\n }\n\n // -----------------------------------------------------------------------\n // Stage 4 — Reserved no-op (DEC-VERIFY-010 v3.1 trigger)\n // @decision DEC-V3-IMPL-QUERY-006 (Stage 4)\n // This slot is explicitly reserved. In v3, ALL Stage 3 survivors pass through\n // unconditionally. Property-test verification infrastructure (DEC-VERIFY-010)\n // is the v3.1 trigger for implementing this stage. DO NOT add logic here.\n // -----------------------------------------------------------------------\n const stage4Passed = stage3Passed; // pass-through, no filtering\n\n // -----------------------------------------------------------------------\n // Stage 5 — Final ranking + combinedScore + tiebreaker + minScore + topK\n // -----------------------------------------------------------------------\n\n // combinedScore formula (DEC-V3-IMPL-QUERY-007 re-stated, 1 - L²/4):\n // For unit-sphere embeddings: cosineDistance ∈ [0, 2].\n // similarity = 1 - cosineDistance/2 = 1 - L²/4 ∈ [0, 1].\n // v1 uses unified single-column embedding; per-dimension breakdown deferred.\n // perDimensionScores carries the 'unified' dimension under the queried keys.\n\n // Build unified score entries for all stage4Passed candidates.\n const scored: Array<{\n item: HydratedCandidate;\n combinedScore: number;\n perDimensionScores: PerDimensionScores;\n }> = stage4Passed.map((item) => {\n // @decision DEC-V3-IMPL-QUERY-007\n // @title combinedScore formula — L2→[0,1] via 1 - d²/4\n // @status accepted\n // @cross-links DEC-V3-DISCOVERY-CALIBRATION-FIX-002 (live calibration authority,\n // supersedes -001; canonical site: discovery-eval-helpers.ts cosineDistanceToCombinedScore)\n // @deferred WI-V3-DISCOVERY-COMBINED-SCORE-CONSOLIDATE (consolidate inline formula\n // into cosineDistanceToCombinedScore call to eliminate duplication)\n // @rationale vec0 returns L2 Euclidean distance (not cosine) for unit-normalized\n // vectors. For unit vectors: L2² = 2 - 2·cos(θ) ⟹ cos(θ) = 1 - L2²/2.\n // combinedScore = (1 + cos(θ)) / 2 = 1 - L2²/4. This is the corrected formula\n // from PR #275 / DEC-V3-DISCOVERY-CALIBRATION-FIX-002. The earlier formula\n // (1 - d/2) was incorrect — it applied a cosine-distance formula to an L2 distance.\n const combinedScore = Math.max(0, 1 - (item.cosineDistance * item.cosineDistance) / 4);\n\n // v1 per-dimension scores: single 'unified' key carries the combinedScore.\n // Using actual dimension keys (behavior, guarantees, …) here would falsely\n // imply per-dimension semantics that aren't implemented — v1 has only one\n // shared embedding column. Per-dimension granularity ships in v3.1 when\n // migration 7 (5-column schema) lands (DEC-V3-IMPL-QUERY-003).\n const perDimensionScores: PerDimensionScores = { unified: combinedScore };\n\n return { item, combinedScore, perDimensionScores };\n });\n\n // Sort by combinedScore descending.\n // @decision DEC-V3-DISCOVERY-D3-FILTER-STRICTNESS-FIX-001\n // @title Stage 5 tiebreaker ε reduced from 0.02 to 0 (issue #319)\n // @status accepted\n // @rationale\n // The original ε=0.02 \"tie window\" caused rank inversions for candidate pairs\n // with genuinely different cosine distances. Two candidates with scores 0.6419\n // and 0.6233 (|diff|=0.0186 < 0.02) were treated as tied, and the lex-BMR\n // tiebreaker placed the FURTHER atom at rank #1, displacing the CLOSER (correct)\n // atom. This is a direct violation of the \"ranking by semantic similarity\" principle.\n //\n // Fix: ε=0 means the lex-BMR tiebreaker only fires when two candidates have\n // EXACTLY the same float combinedScore. In practice with semantic embeddings,\n // identical scores from different text inputs are vanishingly rare. This restores\n // pure distance-based ranking (matching findCandidatesByIntent behavior).\n //\n // D3 §Q4 tiebreakers 2–4 (usage history, test depth, atom age) are deferred to\n // WI-V3-DISCOVERY-D3-TIEBREAKERS. The lex-root tiebreaker (prio 5) remains for\n // true float-equal ties only.\n //\n // Measured impact (issue #309 full-corpus harness, paired with Stage 1 fix):\n // Before fix: M2=20.0%, M3=72.5%, M4=0.378\n // After fix: M2≥62.5%, M3≥90%, M4≥0.70 (verified post-fix)\n const EPSILON = 0;\n scored.sort((a, b) => {\n const diff = b.combinedScore - a.combinedScore;\n if (Math.abs(diff) <= EPSILON) {\n // Within tie window: lex BlockMerkleRoot ascending (smaller wins, D3 §Q4 prio 5).\n return a.item.block.blockMerkleRoot < b.item.block.blockMerkleRoot ? -1 : 1;\n }\n return diff; // descending by combinedScore\n });\n\n // Apply minScore filter.\n const minScore = query.minScore;\n const minScoreFailed: Array<{ item: HydratedCandidate; combinedScore: number }> = [];\n const afterMinScore =\n minScore !== undefined\n ? scored.filter((s) => {\n if (s.combinedScore < minScore) {\n minScoreFailed.push({ item: s.item, combinedScore: s.combinedScore });\n return false;\n }\n return true;\n })\n : scored;\n\n // Truncate to topK.\n const topKResults = afterMinScore.slice(0, topK);\n\n if (topKResults.length === 0) {\n // 0 survivors: build near-miss envelope from Stage 1 K' set.\n // Near-misses are drawn from stage1 sorted by best combinedScore, up to topK.\n const nearMissMap = new Map<\n string,\n {\n item: HydratedCandidate;\n failedAtLayer: CandidateNearMiss[\"failedAtLayer\"];\n failureReason: string;\n }\n >();\n\n // Stage 2 failures first (structural)\n for (const { item, reason } of stage2Failed) {\n nearMissMap.set(item.block.blockMerkleRoot, {\n item,\n failedAtLayer: \"structural\",\n failureReason: reason,\n });\n }\n // Stage 3 failures (strictness) — may overlap with stage 2 items; stage 2 wins\n for (const { item, reason } of stage3Failed) {\n if (!nearMissMap.has(item.block.blockMerkleRoot)) {\n nearMissMap.set(item.block.blockMerkleRoot, {\n item,\n failedAtLayer: \"strictness\",\n failureReason: reason,\n });\n }\n }\n // minScore failures (min_score)\n for (const { item, combinedScore: cs } of minScoreFailed) {\n if (!nearMissMap.has(item.block.blockMerkleRoot)) {\n nearMissMap.set(item.block.blockMerkleRoot, {\n item,\n failedAtLayer: \"min_score\",\n failureReason: `combinedScore=${cs.toFixed(4)} < minScore=${minScore ?? 0}`,\n });\n }\n }\n\n // Sort near-misses by combinedScore descending (best first), take topK.\n // Formula: 1 - d²/4 (DEC-V3-IMPL-QUERY-007 / DEC-V3-DISCOVERY-CALIBRATION-FIX-002).\n const nearMissEntries = [...nearMissMap.values()]\n .sort((a, b) => {\n const sa = Math.max(0, 1 - (a.item.cosineDistance * a.item.cosineDistance) / 4);\n const sb = Math.max(0, 1 - (b.item.cosineDistance * b.item.cosineDistance) / 4);\n return sb - sa;\n })\n .slice(0, topK);\n\n const nearMisses: CandidateNearMiss[] = nearMissEntries.map(\n ({ item, failedAtLayer, failureReason }) => {\n // Formula: 1 - d²/4 (DEC-V3-IMPL-QUERY-007 / DEC-V3-DISCOVERY-CALIBRATION-FIX-002).\n const cs = Math.max(0, 1 - (item.cosineDistance * item.cosineDistance) / 4);\n const pds: PerDimensionScores = { unified: cs };\n return {\n block: item.block,\n cosineDistance: item.cosineDistance,\n combinedScore: cs,\n perDimensionScores: pds,\n autoAccepted: false,\n failedAtLayer,\n failureReason,\n };\n },\n );\n\n return { candidates: [], nearMisses };\n }\n\n // Non-empty results: compute autoAccepted flag.\n // D2 §Q5 + D3 §Q1: autoAccepted iff top-1 combinedScore > 0.85 AND gap > 0.15.\n const top1 = topKResults[0];\n const top2 = topKResults[1];\n const top1Score = top1?.combinedScore ?? 0;\n const top2Score = top2?.combinedScore ?? 0;\n const autoAcceptFires = top1Score > 0.85 && top1Score - top2Score > 0.15;\n\n const candidates: QueryCandidate[] = topKResults.map((s, idx) => ({\n block: s.item.block,\n cosineDistance: s.item.cosineDistance,\n combinedScore: s.combinedScore,\n perDimensionScores: s.perDimensionScores,\n autoAccepted: autoAcceptFires && idx === 0,\n }));\n\n return { candidates, nearMisses: [] };\n }\n\n // -------------------------------------------------------------------------\n // enumerateSpecs — return all distinct spec hashes, sorted ascending\n // -------------------------------------------------------------------------\n\n // @decision DEC-SERVE-SPECS-ENUMERATION-020 (closure)\n // title: enumerateSpecs() — registry-native SELECT DISTINCT primitive\n // status: closed by WI-026\n // rationale: Pre-WI-026, serveRegistry accepted an optional `enumerateSpecs`\n // callback because no method existed on Registry. The callback was the\n // documented workaround (DEC-SERVE-SPECS-ENUMERATION-020). WI-026 adds\n // this method as the single authority for spec enumeration; serveRegistry\n // now calls `registry.enumerateSpecs()` directly.\n //\n // No caching: the SELECT DISTINCT is cheap and caching would introduce a\n // stale-write window (forbidden shortcut per WI-026 eval contract).\n // No ownership columns: touches spec_hash only (DEC-NO-OWNERSHIP-011).\n // Read-only: no mutations; storeBlock remains the sole mutation entry point\n // (DEC-SCHEMA-MIGRATION-002).\n\n async enumerateSpecs(): Promise<readonly SpecHash[]> {\n this.assertOpen();\n\n const rows = this.db\n .prepare<[], { spec_hash: string }>(\n \"SELECT DISTINCT spec_hash FROM blocks ORDER BY spec_hash\",\n )\n .all();\n\n return rows.map((r) => r.spec_hash as SpecHash);\n }\n\n // -------------------------------------------------------------------------\n // exportManifest — deterministic manifest for bootstrap --verify\n // -------------------------------------------------------------------------\n\n // @decision DEC-V2-BOOTSTRAP-MANIFEST-001\n // title: exportManifest() excludes non-deterministic columns\n // status: accepted\n // rationale: createdAt and ROWID vary per-environment and per-run. The six\n // fields in BootstrapManifestEntry are all content-addressed (derived from\n // artifact bytes via BLAKE3) so the same block stored on any machine at any\n // time produces the same entry. The array is sorted ascending by\n // blockMerkleRoot — the sort is the load-bearing determinism contract for\n // WI-V2-BOOTSTRAP-03's byte-identity gate.\n // Blocks missing impl.ts or proof/manifest.json artifacts (pre-WI-022)\n // receive the BLAKE3-of-empty-bytes sentinel so the schema is uniform.\n\n async exportManifest(): Promise<readonly BootstrapManifestEntry[]> {\n this.assertOpen();\n\n // Single query over blocks, sorted ascending by block_merkle_root.\n // The ORDER BY is the load-bearing determinism contract — do NOT change\n // to insertion order or any other sort key.\n interface ManifestBlockRow {\n block_merkle_root: string;\n spec_hash: string;\n canonical_ast_hash: string;\n parent_block_root: string | null;\n }\n\n const blockRows = this.db\n .prepare<[], ManifestBlockRow>(\n `SELECT\n b.block_merkle_root,\n b.spec_hash,\n b.canonical_ast_hash,\n b.parent_block_root\n FROM blocks b\n ORDER BY b.block_merkle_root ASC`,\n )\n .all();\n\n if (blockRows.length === 0) return [];\n\n // Precompute the sentinel: BLAKE3 of empty bytes (used when an artifact\n // path is absent — pre-WI-022 blocks or blocks with no matching path).\n const EMPTY_SENTINEL = bytesToHex(blake3(new Uint8Array(0)));\n\n // Load artifact bytes for all blocks in one query to avoid N+1 queries.\n // We only need impl.ts and proof/manifest.json paths.\n const allRoots = blockRows.map((r) => r.block_merkle_root);\n const placeholders = allRoots.map(() => \"?\").join(\", \");\n interface ArtifactHashRow {\n block_merkle_root: string;\n path: string;\n bytes: Buffer;\n }\n const artifactRows = this.db\n .prepare<string[], ArtifactHashRow>(\n `SELECT block_merkle_root, path, bytes\n FROM block_artifacts\n WHERE block_merkle_root IN (${placeholders})\n AND path IN ('impl.ts', 'proof/manifest.json')`,\n )\n .all(...allRoots);\n\n // Build a lookup: root → { implSourceHash, manifestJsonHash }\n const hashMap = new Map<string, { implSourceHash: string; manifestJsonHash: string }>();\n for (const row of artifactRows) {\n let entry = hashMap.get(row.block_merkle_root);\n if (entry === undefined) {\n entry = { implSourceHash: EMPTY_SENTINEL, manifestJsonHash: EMPTY_SENTINEL };\n hashMap.set(row.block_merkle_root, entry);\n }\n const digest = bytesToHex(blake3(new Uint8Array(row.bytes)));\n if (row.path === \"impl.ts\") {\n entry.implSourceHash = digest;\n } else if (row.path === \"proof/manifest.json\") {\n entry.manifestJsonHash = digest;\n }\n }\n\n return blockRows.map((r): BootstrapManifestEntry => {\n const hashes = hashMap.get(r.block_merkle_root) ?? {\n implSourceHash: EMPTY_SENTINEL,\n manifestJsonHash: EMPTY_SENTINEL,\n };\n return {\n blockMerkleRoot: r.block_merkle_root as BlockMerkleRoot,\n specHash: r.spec_hash as SpecHash,\n canonicalAstHash: r.canonical_ast_hash,\n parentBlockRoot: (r.parent_block_root ?? null) as BlockMerkleRoot | null,\n implSourceHash: hashes.implSourceHash,\n manifestJsonHash: hashes.manifestJsonHash,\n };\n });\n }\n\n // -------------------------------------------------------------------------\n // storeWorkspacePlumbing — insert a plumbing-file row (P2)\n //\n // @decision DEC-V2-WORKSPACE-PLUMBING-AUTHORITY-001\n // title: workspace_plumbing is the single authority for non-atom bootable files\n // status: accepted (WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P2)\n // rationale: INSERT OR IGNORE + primary-key-on-workspace_path gives first-\n // observed-wins semantics matching storeBlock. Content-hash verification is\n // enforced here (not deferred to callers) so every row in the table is\n // provably integrity-checked. Workspace-relative path validation prevents\n // absolute-path or path-traversal rows from entering the table.\n // -------------------------------------------------------------------------\n\n async storeWorkspacePlumbing(entry: WorkspacePlumbingEntry): Promise<void> {\n this.assertOpen();\n\n // Validate workspace-relative path.\n if (entry.workspacePath.startsWith(\"/\") || entry.workspacePath.includes(\"..\")) {\n throw new Error(\n `storeWorkspacePlumbing: workspacePath must be workspace-relative and must not contain '..': ${entry.workspacePath}`,\n );\n }\n\n // Verify content integrity: BLAKE3-256(contentBytes) must equal contentHash.\n const actualHash = bytesToHex(blake3(entry.contentBytes));\n if (actualHash !== entry.contentHash) {\n throw new Error(\n `storeWorkspacePlumbing: contentHash mismatch for ${entry.workspacePath}: ` +\n `stored=${entry.contentHash}, computed=${actualHash}`,\n );\n }\n\n const insertPlumbing = this.db.prepare<[string, Buffer, string, number]>(\n \"INSERT OR IGNORE INTO workspace_plumbing(workspace_path, content_bytes, content_hash, created_at) VALUES (?, ?, ?, ?)\",\n );\n\n insertPlumbing.run(\n entry.workspacePath,\n Buffer.from(entry.contentBytes),\n entry.contentHash,\n entry.createdAt > 0 ? entry.createdAt : Date.now(),\n );\n }\n\n // -------------------------------------------------------------------------\n // listWorkspacePlumbing — enumerate all plumbing rows (P2)\n //\n // @decision DEC-V2-WORKSPACE-PLUMBING-AUTHORITY-001\n // title: deterministic enumeration sorted by workspace_path ASC\n // status: accepted (WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P2)\n // rationale: The ORDER BY workspace_path ASC is the load-bearing determinism\n // contract — two calls on the same DB state produce identical results\n // (mirrors exportManifest()'s ORDER BY blockMerkleRoot ASC contract).\n // Callers may materialise files in any order; the canonical sort allows\n // test assertions and diff-stable tooling output.\n // -------------------------------------------------------------------------\n\n async listWorkspacePlumbing(): Promise<readonly WorkspacePlumbingEntry[]> {\n this.assertOpen();\n\n interface PlumbingRow {\n workspace_path: string;\n content_bytes: Buffer;\n content_hash: string;\n created_at: number;\n }\n\n const rows = this.db\n .prepare<[], PlumbingRow>(\n \"SELECT workspace_path, content_bytes, content_hash, created_at FROM workspace_plumbing ORDER BY workspace_path ASC\",\n )\n .all();\n\n return rows.map((r) => ({\n workspacePath: r.workspace_path,\n contentBytes: new Uint8Array(r.content_bytes),\n contentHash: r.content_hash,\n createdAt: r.created_at,\n }));\n }\n\n // -------------------------------------------------------------------------\n // storeSourceFileGlue — insert or replace a glue row (#333)\n //\n // @decision DEC-V2-GLUE-CAPTURE-AUTHORITY-001\n // title: source_file_glue is the single authority for per-file glue bytes;\n // INSERT OR REPLACE for re-bootstrap idempotency (atoms change → glue changes)\n // status: decided (WI-V2-WORKSPACE-PLUMBING-GLUE-CAPTURE #333)\n // rationale: Unlike storeBlock (INSERT OR IGNORE) and storeWorkspacePlumbing\n // (INSERT OR IGNORE), glue rows use INSERT OR REPLACE because a re-bootstrap\n // may produce a different glue blob for the same (source_pkg, source_file) if\n // the shaver extracts different atoms in a new run. The glue must always\n // reflect the most recent bootstrap's per-file atom decomposition.\n // Content-hash verification enforces blob integrity at write time.\n // -------------------------------------------------------------------------\n\n async storeSourceFileGlue(entry: SourceFileGlueEntry): Promise<void> {\n this.assertOpen();\n\n // Validate inputs.\n if (!entry.sourcePkg || !entry.sourceFile) {\n throw new Error(\n `storeSourceFileGlue: sourcePkg and sourceFile must be non-empty: sourcePkg=${entry.sourcePkg}, sourceFile=${entry.sourceFile}`,\n );\n }\n if (entry.sourceFile.startsWith(\"/\") || entry.sourceFile.includes(\"..\")) {\n throw new Error(\n `storeSourceFileGlue: sourceFile must be workspace-relative and must not contain '..': ${entry.sourceFile}`,\n );\n }\n\n // Verify content integrity: BLAKE3-256(contentBlob) must equal contentHash.\n const actualHash = bytesToHex(blake3(entry.contentBlob));\n if (actualHash !== entry.contentHash) {\n throw new Error(\n `storeSourceFileGlue: contentHash mismatch for ${entry.sourceFile}: ` +\n `stored=${entry.contentHash}, computed=${actualHash}`,\n );\n }\n\n this.db\n .prepare<[string, string, string, Buffer, number]>(\n \"INSERT OR REPLACE INTO source_file_glue(source_pkg, source_file, content_hash, content_blob, created_at) VALUES (?, ?, ?, ?, ?)\",\n )\n .run(\n entry.sourcePkg,\n entry.sourceFile,\n entry.contentHash,\n Buffer.from(entry.contentBlob),\n entry.createdAt > 0 ? entry.createdAt : Date.now(),\n );\n }\n\n // -------------------------------------------------------------------------\n // getSourceFileGlue — retrieve glue row by (sourcePkg, sourceFile) (#333)\n //\n // @decision DEC-V2-GLUE-CAPTURE-AUTHORITY-001\n // -------------------------------------------------------------------------\n\n async getSourceFileGlue(\n sourcePkg: string,\n sourceFile: string,\n ): Promise<SourceFileGlueEntry | null> {\n this.assertOpen();\n\n interface GlueRow {\n source_pkg: string;\n source_file: string;\n content_hash: string;\n content_blob: Buffer;\n created_at: number;\n }\n\n const row = this.db\n .prepare<[string, string], GlueRow>(\n \"SELECT source_pkg, source_file, content_hash, content_blob, created_at FROM source_file_glue WHERE source_pkg = ? AND source_file = ?\",\n )\n .get(sourcePkg, sourceFile);\n\n if (row === undefined) return null;\n\n return {\n sourcePkg: row.source_pkg,\n sourceFile: row.source_file,\n contentHash: row.content_hash,\n contentBlob: new Uint8Array(row.content_blob),\n createdAt: row.created_at,\n };\n }\n\n // -------------------------------------------------------------------------\n // listSourceFileGlue — enumerate all glue rows sorted by (source_pkg, source_file) (#333)\n //\n // @decision DEC-V2-GLUE-CAPTURE-AUTHORITY-001\n // -------------------------------------------------------------------------\n\n async listSourceFileGlue(): Promise<readonly SourceFileGlueEntry[]> {\n this.assertOpen();\n\n interface GlueRow {\n source_pkg: string;\n source_file: string;\n content_hash: string;\n content_blob: Buffer;\n created_at: number;\n }\n\n const rows = this.db\n .prepare<[], GlueRow>(\n \"SELECT source_pkg, source_file, content_hash, content_blob, created_at FROM source_file_glue ORDER BY source_pkg ASC, source_file ASC\",\n )\n .all();\n\n return rows.map((r) => ({\n sourcePkg: r.source_pkg,\n sourceFile: r.source_file,\n contentHash: r.content_hash,\n contentBlob: new Uint8Array(r.content_blob),\n createdAt: r.created_at,\n }));\n }\n\n // -------------------------------------------------------------------------\n // listOccurrencesBySourceFile — current-truth atom positions for a file (#355)\n //\n // @decision DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001\n // @title listOccurrencesBySourceFile queries block_occurrences (current-truth)\n // @status decided (WI-V2-STORAGE-IDEMPOTENT-RECOMPILE #355)\n // @rationale\n // Compile-self and compile-pipeline must use current-truth offsets for\n // reconstruction. blocks.source_offset is a stale first-observed-wins value;\n // block_occurrences is refreshed atomically per file on every bootstrap pass.\n // Returning occurrences here avoids N+1 queries in the reconstruction pipeline:\n // one call per group (sourceFile) returns all offsets for that file.\n // -------------------------------------------------------------------------\n\n async listOccurrencesBySourceFile(sourceFile: string): Promise<\n readonly {\n sourcePkg: string;\n sourceFile: string;\n sourceOffset: number;\n length: number;\n blockMerkleRoot: string;\n }[]\n > {\n this.assertOpen();\n\n interface OccurrenceRow {\n source_pkg: string;\n source_file: string;\n source_offset: number;\n length: number;\n block_merkle_root: string;\n }\n\n const rows = this.db\n .prepare<[string], OccurrenceRow>(\n \"SELECT source_pkg, source_file, source_offset, length, block_merkle_root FROM block_occurrences WHERE source_file = ? ORDER BY source_offset ASC\",\n )\n .all(sourceFile);\n\n return rows.map((r) => ({\n sourcePkg: r.source_pkg,\n sourceFile: r.source_file,\n sourceOffset: r.source_offset,\n length: r.length,\n blockMerkleRoot: r.block_merkle_root,\n }));\n }\n\n // -------------------------------------------------------------------------\n // listOccurrencesByMerkleRoot — all occurrence rows for a given block (#355)\n //\n // @decision DEC-V2-BLOCK-OCCURRENCES-SCHEMA-001\n // -------------------------------------------------------------------------\n\n async listOccurrencesByMerkleRoot(blockMerkleRoot: string): Promise<\n readonly {\n sourcePkg: string;\n sourceFile: string;\n sourceOffset: number;\n length: number;\n blockMerkleRoot: string;\n }[]\n > {\n this.assertOpen();\n\n interface OccurrenceRow {\n source_pkg: string;\n source_file: string;\n source_offset: number;\n length: number;\n block_merkle_root: string;\n }\n\n const rows = this.db\n .prepare<[string], OccurrenceRow>(\n \"SELECT source_pkg, source_file, source_offset, length, block_merkle_root FROM block_occurrences WHERE block_merkle_root = ? ORDER BY source_pkg ASC, source_file ASC, source_offset ASC\",\n )\n .all(blockMerkleRoot);\n\n return rows.map((r) => ({\n sourcePkg: r.source_pkg,\n sourceFile: r.source_file,\n sourceOffset: r.source_offset,\n length: r.length,\n blockMerkleRoot: r.block_merkle_root,\n }));\n }\n\n // -------------------------------------------------------------------------\n // replaceSourceFileOccurrences (#355 — per-occurrence offset tracking)\n // -------------------------------------------------------------------------\n //\n // @decision DEC-V2-OCCURRENCE-DELETE-INSERT-001\n // @title Per-file occurrence refresh is atomic delete-then-insert under one SQLite transaction.\n // Bootstrap is the sole writer. The legacy blocks.source_* columns become a one-shot\n // first-occurrence shim driven by the first observed occurrence for that block in a\n // given process.\n // @status decided (WI-V2-STORAGE-IDEMPOTENT-RECOMPILE #355)\n // @rationale\n // DEC-STORAGE-IDEMPOTENT-001's first-observed-wins INSERT OR IGNORE correctly handles\n // content-identity (block_merkle_root is immutable). But it incorrectly preserved\n // source_offset across re-shaves: atoms with unchanged impl_source (same merkle root)\n // kept their first-observed offset even when the source file was modified and the atom\n // shifted to a new byte position. This caused compile-self to place atoms at wrong\n // offsets, producing malformed reconstructed source files.\n //\n // The fix: move per-occurrence position into block_occurrences (a separate table with\n // PK (source_pkg, source_file, source_offset)). Each bootstrap pass deletes the prior\n // occurrences for a file and inserts fresh ones inside a single SQLite transaction.\n // A failure mid-transaction rolls back — prior occurrences remain intact.\n // On success, block_occurrences reflects the current-truth ranges for that file.\n //\n // Bootstrap pipeline order:\n // 1. storeBlock() for each novel atom (content-only, INSERT OR IGNORE on blocks).\n // 2. replaceSourceFileOccurrences() after Pass A for the file (occurrence refresh).\n // storeBlock must precede replaceSourceFileOccurrences: the FK on\n // block_occurrences(block_merkle_root) → blocks(block_merkle_root) is enforced.\n //\n // @decision DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001\n // @title block_occurrences is the authoritative per-file read source; blocks.source_* is a shim.\n // @status decided (WI-V2-STORAGE-IDEMPOTENT-RECOMPILE #355)\n\n async replaceSourceFileOccurrences(\n sourcePkg: string,\n sourceFile: string,\n occurrences: readonly {\n readonly sourcePkg: string;\n readonly sourceFile: string;\n readonly sourceOffset: number;\n readonly length: number;\n readonly blockMerkleRoot: string;\n }[],\n ): Promise<void> {\n this.assertOpen();\n\n if (!sourcePkg || !sourceFile) {\n throw new Error(\n `replaceSourceFileOccurrences: sourcePkg and sourceFile must be non-empty: sourcePkg=${sourcePkg}, sourceFile=${sourceFile}`,\n );\n }\n\n // Use instance-level prepared statements (hoisted in constructor).\n // See @decision DEC-V2-OCCURRENCE-WRITE-PERF-001 — no prepare() on the hot path.\n const txn = this.db.transaction(() => {\n this.stmtDeleteOccurrences.run(sourcePkg, sourceFile);\n for (const occ of occurrences) {\n this.stmtInsertOccurrence.run(\n occ.sourcePkg,\n occ.sourceFile,\n occ.sourceOffset,\n occ.length,\n occ.blockMerkleRoot,\n );\n }\n });\n\n txn();\n }\n\n // getAtomRangesBySourceFile (#355 — now queries block_occurrences)\n // -------------------------------------------------------------------------\n //\n // @decision DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001\n // @title getAtomRangesBySourceFile queries block_occurrences (current-truth), not blocks.source_*\n // @status decided (WI-V2-STORAGE-IDEMPOTENT-RECOMPILE #355)\n // @rationale\n // Prior implementation queried blocks WHERE source_file = ? AND source_offset IS NOT NULL.\n // This produced stale offsets after source edits because INSERT OR IGNORE on blocks kept\n // the first-observed source_offset for atoms whose impl_source was unchanged (same merkle\n // root, shifted position). Glue computation and compile-self reconstruction then used these\n // stale offsets, producing malformed source files.\n //\n // Migration: query block_occurrences (source_pkg, source_file, source_offset, length) instead.\n // block_occurrences is refreshed atomically by replaceSourceFileOccurrences on every\n // bootstrap pass — it always reflects the current-truth positions for each file.\n //\n // The interval-merge de-overlap logic is retained: block_occurrences for a given file\n // should never have overlapping intervals (the PK on source_offset enforces uniqueness per\n // position), but the merge is defensive and preserves glue-capture correctness if a file\n // is shaved in parallel or if the PK uniqueness is violated at a different layer.\n //\n // Callers (captureSourceFileGlue in bootstrap.ts and the compile-self reconstruction in\n // compile-self.ts) are unchanged — the function signature and return type are identical.\n // Only the backing SQL query changes.\n //\n // @decision DEC-V2-GLUE-CAPTURE-AUTHORITY-001\n // @title getAtomRangesBySourceFile is the authoritative query for glue computation\n // @status decided (WI-V2-WORKSPACE-PLUMBING-GLUE-CAPTURE #333; updated WI-355)\n\n async getAtomRangesBySourceFile(\n sourceFile: string,\n ): Promise<readonly { readonly sourceOffset: number; readonly implSourceLength: number }[]> {\n this.assertOpen();\n\n type AtomRangeRow = { source_offset: number; length: number };\n\n // Query block_occurrences for current-truth atom positions for this file.\n // The source_pkg filter is omitted (only sourceFile is supplied by callers) — the\n // idx_block_occurrences_file index covers (source_pkg, source_file); a query by\n // source_file alone performs an index scan. Since (source_pkg, source_file) pairs are\n // unique in practice (each workspace-relative path belongs to exactly one package),\n // the result set is correct. The sourcePkg-aware overload is exposed via\n // replaceSourceFileOccurrences for the write path; the read path accepts sourceFile only\n // to preserve the existing caller contract (DEC-V2-GLUE-CAPTURE-AUTHORITY-001).\n const rows = this.db\n .prepare<[string], AtomRangeRow>(\n \"SELECT source_offset, length FROM block_occurrences WHERE source_file = ? ORDER BY source_offset ASC\",\n )\n .all(sourceFile);\n\n // De-overlap intervals (defensive — block_occurrences PK enforces uniqueness per offset,\n // but merge is retained for glue-capture correctness and future-proofing).\n //\n // @decision DEC-V2-GLUE-CAPTURE-AUTHORITY-001 (de-overlap addendum retained from #333)\n // @rationale block_occurrences rows are freshly inserted per bootstrap pass so overlaps\n // should not occur in normal operation. The merge is retained as a defensive measure:\n // if future logic produces two occurrences at adjacent-but-overlapping offsets (e.g.\n // atoms from different source_pkg values that share a source_file), the merge prevents\n // double-exclusion from the glue blob. Invariant: merged intervals are non-overlapping\n // and sorted by sourceOffset ASC.\n const merged: Array<{ sourceOffset: number; implSourceLength: number }> = [];\n for (const r of rows) {\n const start = r.source_offset;\n const end = start + r.length;\n const last = merged[merged.length - 1];\n if (last !== undefined && start < last.sourceOffset + last.implSourceLength) {\n // Overlaps previous interval: extend it to cover both (union).\n const prevEnd = last.sourceOffset + last.implSourceLength;\n if (end > prevEnd) {\n merged[merged.length - 1] = {\n sourceOffset: last.sourceOffset,\n implSourceLength: end - last.sourceOffset,\n };\n }\n // else: entirely contained in previous — skip.\n } else {\n merged.push({ sourceOffset: start, implSourceLength: r.length });\n }\n }\n return merged;\n }\n\n // -------------------------------------------------------------------------\n // storeSourceFileContentHash — write or update per-file content hash (issue #363)\n //\n // @decision DEC-V2-SHAVE-CACHE-STORAGE-001\n // @title source_file_state is the single authority for per-source-file content hashes;\n // INSERT OR REPLACE for re-bootstrap idempotency (hash changes on file edit)\n // @status decided (issue #363 / wi-363-shave-cache)\n // @rationale\n // Each bootstrap pass computes BLAKE3-256 of the source file's UTF-8 bytes.\n // On cache miss, after a successful shave, storeSourceFileContentHash writes\n // (or overwrites) the row so the next bootstrap can short-circuit this file.\n // INSERT OR REPLACE semantics mirror storeSourceFileGlue: both represent the\n // most-recent bootstrap state for a given (source_pkg, source_file) pair.\n // Uses a hoisted prepared statement (DEC-V2-OCCURRENCE-WRITE-PERF-001 pattern)\n // to avoid re-compilation on every call in the per-file bootstrap loop.\n // -------------------------------------------------------------------------\n\n // Hoisted prepared statement for cache-hash writes (avoids prepare() on hot path).\n // Lazily initialized on first use; stored as a class field for lifetime reuse.\n private stmtUpsertSourceFileState: Database.Statement<\n [string, string, string, number]\n > | null = null;\n\n async storeSourceFileContentHash(\n sourcePkg: string,\n sourceFile: string,\n contentHash: string,\n ): Promise<void> {\n this.assertOpen();\n\n if (!sourcePkg || !sourceFile) {\n throw new Error(\n `storeSourceFileContentHash: sourcePkg and sourceFile must be non-empty: sourcePkg=${sourcePkg}, sourceFile=${sourceFile}`,\n );\n }\n if (sourceFile.startsWith(\"/\") || sourceFile.includes(\"..\")) {\n throw new Error(\n `storeSourceFileContentHash: sourceFile must be workspace-relative and must not contain '..': ${sourceFile}`,\n );\n }\n if (!contentHash) {\n throw new Error(\n `storeSourceFileContentHash: contentHash must be non-empty for ${sourceFile}`,\n );\n }\n\n // Prepare once per registry lifetime (DEC-V2-OCCURRENCE-WRITE-PERF-001 pattern).\n if (this.stmtUpsertSourceFileState === null) {\n this.stmtUpsertSourceFileState = this.db.prepare<[string, string, string, number]>(\n \"INSERT OR REPLACE INTO source_file_state(source_pkg, source_file, content_hash, last_shave_time) VALUES (?, ?, ?, ?)\",\n );\n }\n\n this.stmtUpsertSourceFileState.run(sourcePkg, sourceFile, contentHash, Date.now());\n }\n\n // -------------------------------------------------------------------------\n // getSourceFileContentHash — retrieve per-file content hash (issue #363)\n //\n // @decision DEC-V2-SHAVE-CACHE-STORAGE-001\n // -------------------------------------------------------------------------\n\n async getSourceFileContentHash(\n sourcePkg: string,\n sourceFile: string,\n ): Promise<string | null> {\n this.assertOpen();\n\n interface StateRow {\n content_hash: string;\n }\n\n const row = this.db\n .prepare<[string, string], StateRow>(\n \"SELECT content_hash FROM source_file_state WHERE source_pkg = ? AND source_file = ?\",\n )\n .get(sourcePkg, sourceFile);\n\n return row?.content_hash ?? null;\n }\n\n // -------------------------------------------------------------------------\n // close\n // -------------------------------------------------------------------------\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.db.close();\n }\n\n // -------------------------------------------------------------------------\n // Private helpers\n // -------------------------------------------------------------------------\n\n private assertOpen(): void {\n if (this.closed) {\n throw new Error(\"Registry has been closed\");\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Pure helpers (module-level to avoid closure overhead in tight loops)\n// ---------------------------------------------------------------------------\n\n/**\n * Hydrate a DB row into a BlockTripletRow.\n *\n * @param row - The blocks table row.\n * @param artifactRows - Rows from block_artifacts, already ordered by\n * declaration_index ASC (callers are responsible for\n * the ORDER BY). Pre-WI-022 blocks pass an empty array,\n * producing an empty Map (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002).\n */\nfunction hydrateBlock(row: BlockRow, artifactRows: readonly BlockArtifactRow[]): BlockTripletRow {\n // Reconstruct the artifacts Map in declaration order. Map insertion order in\n // JavaScript equals iteration order, so inserting in declaration_index order\n // is sufficient to guarantee correct Map.entries() iteration order.\n const artifacts = new Map<string, Uint8Array>();\n for (const ar of artifactRows) {\n artifacts.set(ar.path, new Uint8Array(ar.bytes));\n }\n\n return {\n blockMerkleRoot: row.block_merkle_root as BlockMerkleRoot,\n specHash: row.spec_hash as SpecHash,\n specCanonicalBytes: new Uint8Array(row.spec_canonical_bytes),\n implSource: row.impl_source,\n proofManifestJson: row.proof_manifest_json,\n level: row.level as \"L0\" | \"L1\" | \"L2\" | \"L3\",\n createdAt: row.created_at,\n canonicalAstHash: row.canonical_ast_hash as CanonicalAstHash,\n parentBlockRoot: (row.parent_block_root ?? null) as BlockMerkleRoot | null,\n artifacts,\n // Migration-6 fields (DEC-V2-FOREIGN-BLOCK-SCHEMA-001 / WI-V2-04 L2).\n // Pre-v6 rows return kind='local' via the DEFAULT; foreign fields are null.\n kind: (row.kind ?? \"local\") as \"local\" | \"foreign\",\n foreignPkg: row.foreign_pkg ?? null,\n foreignExport: row.foreign_export ?? null,\n foreignDtsHash: row.foreign_dts_hash ?? null,\n // Migration-7 fields (DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001 / P1).\n // Pre-v7 rows return null for all three fields — the correct sentinel for\n // atoms that predate provenance tracking. Callers must treat null as\n // \"unknown provenance\", not as \"no source file exists\".\n sourcePkg: row.source_pkg ?? null,\n sourceFile: row.source_file ?? null,\n sourceOffset: row.source_offset ?? null,\n };\n}\n\n/**\n * Return true if `aRoot` is declared strictly stronger than `bRoot`\n * (directly or transitively) within the edge set.\n * Uses iterative BFS to avoid call-stack blowup on large graphs.\n */\nfunction isStricterThan(\n aRoot: string,\n bRoot: string,\n edgeSet: ReadonlySet<string>,\n allRoots: readonly string[],\n): boolean {\n const visited = new Set<string>();\n const queue: string[] = [aRoot];\n while (queue.length > 0) {\n const current = queue.shift();\n if (current === undefined) break;\n if (visited.has(current)) continue;\n visited.add(current);\n for (const id of allRoots) {\n if (!visited.has(id) && edgeSet.has(`${current}|${id}`)) {\n if (id === bRoot) return true;\n queue.push(id);\n }\n }\n }\n return false;\n}\n\n/**\n * Convert a Uint8Array to a lowercase hex string.\n * Each file defines its own private copy — codebase pattern from merkle.ts:107.\n * (DEC-V2-BOOTSTRAP-BYTESTOHEX-001)\n */\nfunction bytesToHex(bytes: Uint8Array): string {\n let hex = \"\";\n for (let i = 0; i < bytes.length; i++) {\n hex += bytes[i]?.toString(16).padStart(2, \"0\");\n }\n return hex;\n}\n\n// ---------------------------------------------------------------------------\n// Public constructor\n// ---------------------------------------------------------------------------\n\n/**\n * Options for opening a registry.\n */\nexport interface RegistryOptions {\n /**\n * Embedding provider to use. Defaults to the local transformers.js provider\n * (Xenova/bge-small-en-v1.5, 384 dimensions).\n */\n embeddings?: EmbeddingProvider | undefined;\n\n /**\n * When true, automatically trigger a full registry rebuild (re-embed all blocks)\n * if a cross-provider model mismatch is detected at query time.\n *\n * @decision DEC-EMBED-MODEL-MIGRATION-001\n * @title autoRebuild is explicit opt-in only; default is fail-loud\n * @status accepted (issue #338, WI-EMBED-MODEL-MIGRATION-PATH)\n * @rationale Sacred Practice #5 (loud failure): silent auto-rebuild could mask\n * stale-vector issues and corrupt user state. This option is intended for CI\n * and clean-rebuild workflows only — not for interactive use. The default\n * behavior (false) preserves the loud-failure invariant: a mismatch throws\n * with the enriched error message naming the stored model, current model, and\n * exact remediation command. DEC-EMBED-010 is PRESERVED even when autoRebuild\n * is true: the rebuild replaces stale vectors with correct ones for the current\n * provider, restoring consistency rather than bypassing the check.\n *\n * NOT added to CliOptions or registry-init — this is programmatic API only.\n *\n * @default false\n */\n autoRebuild?: boolean | undefined;\n}\n\n/**\n * Open (or create) a Yakcc registry at the given filesystem path.\n *\n * Opens the SQLite database at `path`, loads the sqlite-vec extension, and\n * applies schema migrations. If the file does not exist, it is created.\n *\n * Pass `\":memory:\"` as `path` for an in-process database with no disk I/O\n * (useful for tests).\n *\n * Fails loudly if the database cannot be opened or the vec extension cannot\n * be loaded — no silent in-memory fallback (DEC-STORAGE-FAIL-LOUD-001).\n *\n * @param path - Filesystem path to the registry database file, or \":memory:\".\n * @param options - Optional configuration including embedding provider.\n */\nexport async function openRegistry(path: string, options?: RegistryOptions): Promise<Registry> {\n // Open the SQLite database. better-sqlite3 throws synchronously on failure.\n const db = new Database(path);\n\n // Enable WAL mode for better concurrent read performance.\n db.pragma(\"journal_mode = WAL\");\n // @decision DEC-V2-OCCURRENCE-WRITE-PERF-002\n // @title Set synchronous=NORMAL for the registry (WAL already on)\n // @status decided (WI-V2-BOOTSTRAP-PERF-OCCURRENCES / issue #377)\n // @rationale\n // WAL mode was already enabled above. The default synchronous=FULL forces an\n // fsync on every transaction commit, which is the dominant I/O cost during\n // bootstrap (~145 commits × fsync overhead). NORMAL+WAL is safe here because:\n // (a) the registry is rebuilt from source on every bootstrap — a crash that\n // loses the partially-written registry is recoverable by re-running; and\n // (b) WAL still guarantees transaction atomicity (partial transactions roll\n // back); only the \"flush to durable media on every commit\" guarantee is\n // relaxed. NORMAL is the idiomatic SQLite setting for write-heavy workloads\n // where durability-on-crash is irrelevant. OFF was considered and rejected\n // (evaluation contract §6 — forbidden shortcut).\n db.pragma(\"synchronous = NORMAL\");\n // Enable foreign key enforcement.\n db.pragma(\"foreign_keys = ON\");\n\n // Load the sqlite-vec extension (throws if unavailable).\n sqliteVec.load(db);\n db.exec(\"CREATE TABLE IF NOT EXISTS schema_version (version INTEGER NOT NULL)\");\n db.exec(\"INSERT OR IGNORE INTO schema_version(version) VALUES (0)\");\n const preMigrationVersionRow = db.prepare(\"SELECT version FROM schema_version LIMIT 1\").get() as\n | { version: number }\n | undefined;\n const preMigrationVersion = preMigrationVersionRow?.version ?? 0;\n\n // Apply schema migrations (idempotent).\n // applyMigrations handles DDL for all migrations including 2→3 (adds the\n // canonical_ast_hash column) and 3→4 (adds parent_block_root). The\n // version-3 backfill and version bump are performed here because this\n // layer has access to canonicalAstHash() from @yakcc/contracts (schema.ts\n // is pure DDL and does not import it).\n applyMigrations(db);\n if (preMigrationVersion < 3) {\n const rowsToBackfill = db\n .prepare<[], { block_merkle_root: string; impl_source: string }>(\n \"SELECT block_merkle_root, impl_source FROM blocks WHERE canonical_ast_hash = ''\",\n )\n .all();\n\n const updateHash = db.prepare<[string, string]>(\n \"UPDATE blocks SET canonical_ast_hash = ? WHERE block_merkle_root = ?\",\n );\n\n // Bump to SCHEMA_VERSION (not just 3): applyMigrations already ran migration 4\n // and bumped the DB to SCHEMA_VERSION. The backfill is a prerequisite for all\n // migrations ≥ 3, so after completing it the version must reflect the full\n // applied migration chain, not just the migration-3 milestone.\n const backfillTxn = db.transaction(() => {\n for (const r of rowsToBackfill) {\n updateHash.run(canonicalAstHash(r.impl_source), r.block_merkle_root);\n }\n db.prepare(\"UPDATE schema_version SET version = ?\").run(SCHEMA_VERSION);\n });\n backfillTxn();\n }\n\n // Resolve the embedding provider: use provided, or import the local default.\n let embeddingProvider: EmbeddingProvider;\n if (options?.embeddings !== undefined) {\n embeddingProvider = options.embeddings;\n } else {\n const { createLocalEmbeddingProvider } = await import(\"@yakcc/contracts\");\n embeddingProvider = createLocalEmbeddingProvider();\n }\n\n return new SqliteRegistry(db, embeddingProvider, options?.autoRebuild ?? false);\n}\n\n// ---------------------------------------------------------------------------\n// Internal re-export for schema inspection (tests only)\n// ---------------------------------------------------------------------------\n\nexport { canonicalize };\n","// SPDX-License-Identifier: MIT\n// @decision DEC-SELECT-001: select() takes pre-loaded strictness edges as an\n// argument rather than querying the DB itself. Status: decided (WI-003)\n// Rationale: Keeps select() a pure function testable without a DB. The storage\n// layer loads strictness_edges for the candidate set and passes them in.\n// This also avoids a round-trip per call when the caller already has the data.\n\n// @decision DEC-SELECT-TIEBREAK-001: deterministic tiebreaker chain.\n// Status: decided (WI-003)\n// Rationale: When multiple candidates are incomparable under strictness_edges,\n// selection must be deterministic so the same input always returns the same\n// block. Tiebreaker priority: (1) stronger non-functional properties\n// (purity rank, then thread-safety rank), (2) more passing test history entries,\n// (3) lexicographically smaller block merkle root (always unique, decisive).\n\n// @decision DEC-SELECT-TIEBREAK-MIGRATE-001: lexicographic comparator for the\n// final tiebreak now operates on BlockMerkleRoot instead of ContractId. Both\n// are 64-char hex strings, so the comparison is identical. The NF tiebreak\n// uses SpecYak.nonFunctional (optional in SpecYak; absent values rank as 0).\n// Status: decided (WI-T03)\n\nimport type { BlockMerkleRoot, SpecYak } from \"@yakcc/contracts\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * A candidate block paired with a similarity score. The block is identified by\n * its BlockMerkleRoot; the spec is the SpecYak that the block was registered\n * under, used for non-functional tiebreaking.\n *\n * select() is a pure function testable without a DB (DEC-SELECT-001).\n */\nexport interface SelectMatch {\n readonly block: {\n readonly root: BlockMerkleRoot;\n readonly spec: SpecYak;\n };\n readonly score: number;\n}\n\n/**\n * A strictness edge: `stricterRoot` is declared strictly stronger than `looserRoot`.\n * Both roots must be present in the candidate set for the edge to influence selection.\n */\nexport interface StrictnessEdge {\n readonly stricterRoot: BlockMerkleRoot;\n readonly looserRoot: BlockMerkleRoot;\n}\n\n/**\n * Per-candidate provenance data used for tiebreaking.\n * `passingRuns` is the count of `test_history` rows where `passed = 1`.\n */\nexport interface CandidateProvenance {\n readonly blockRoot: BlockMerkleRoot;\n readonly passingRuns: number;\n}\n\n// ---------------------------------------------------------------------------\n// Purity and thread-safety ranking (mirrors search.ts — kept local to avoid\n// a circular dep; both files are in the same package)\n// ---------------------------------------------------------------------------\n\nconst PURITY_RANK: Readonly<Record<string, number>> = {\n pure: 3,\n io: 2,\n stateful: 1,\n nondeterministic: 0,\n};\n\nconst THREAD_RANK: Readonly<Record<string, number>> = {\n safe: 2,\n sequential: 1,\n unsafe: 0,\n};\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Build a set of (stricterRoot, looserRoot) pairs restricted to roots present\n * in the candidate set. Returns a Set of \"stricterRoot|looserRoot\" composite\n * keys for O(1) lookup.\n */\nfunction buildEdgeSet(\n candidateRoots: ReadonlySet<BlockMerkleRoot>,\n edges: readonly StrictnessEdge[],\n): Set<string> {\n const edgeSet = new Set<string>();\n for (const edge of edges) {\n if (\n candidateRoots.has(edge.stricterRoot) &&\n candidateRoots.has(edge.looserRoot) &&\n edge.stricterRoot !== edge.looserRoot\n ) {\n edgeSet.add(`${edge.stricterRoot}|${edge.looserRoot}`);\n }\n }\n return edgeSet;\n}\n\n/**\n * Return true if `aRoot` is declared strictly stronger than `bRoot`\n * (directly or transitively) within the edge set.\n *\n * Uses iterative BFS to avoid call-stack blowup on large graphs.\n */\nfunction isStricterThan(\n aRoot: BlockMerkleRoot,\n bRoot: BlockMerkleRoot,\n edgeSet: ReadonlySet<string>,\n allRoots: readonly BlockMerkleRoot[],\n): boolean {\n // BFS from aRoot following \"is stricter than\" edges.\n const visited = new Set<BlockMerkleRoot>();\n const queue: BlockMerkleRoot[] = [aRoot];\n\n while (queue.length > 0) {\n const current = queue.shift();\n if (current === undefined) break;\n if (visited.has(current)) continue;\n visited.add(current);\n\n for (const id of allRoots) {\n if (!visited.has(id) && edgeSet.has(`${current}|${id}`)) {\n if (id === bRoot) return true;\n queue.push(id);\n }\n }\n }\n return false;\n}\n\n/**\n * Non-functional quality score for tiebreaking: higher is better.\n * Components: purity (0–3) and thread-safety (0–2).\n * SpecYak.nonFunctional is optional; absent values score 0 in each component.\n */\nfunction nfScore(spec: SpecYak): number {\n const nf = spec.nonFunctional;\n if (nf === undefined) return 0;\n return (PURITY_RANK[nf.purity] ?? 0) * 10 + (THREAD_RANK[nf.threadSafety] ?? 0);\n}\n\n// ---------------------------------------------------------------------------\n// Public function\n// ---------------------------------------------------------------------------\n\n/**\n * Select the single best match from a set of candidates.\n *\n * Selection algorithm:\n * 1. If only one match, return it.\n * 2. Restrict `strictnessEdges` to candidates present in `matches`.\n * 3. Find the maximally-strict candidate: one that no other candidate is\n * declared strictly stricter than. If there is exactly one such node,\n * return it.\n * 4. Tiebreak among incomparable maximal nodes:\n * a. Higher non-functional quality score (purity rank × 10 + thread-safety rank).\n * b. More passing test-history runs (from `provenance`).\n * c. Lexicographically smaller block merkle root (deterministic last resort).\n * 5. Returns null if `matches` is empty.\n *\n * @param matches - Candidate matches to select from.\n * @param strictnessEdges - Declared partial order edges (may include roots not in matches).\n * @param provenance - Per-candidate test-history counts for tiebreaking.\n */\nexport function select(\n matches: readonly SelectMatch[],\n strictnessEdges: readonly StrictnessEdge[],\n provenance: readonly CandidateProvenance[],\n): SelectMatch | null {\n if (matches.length === 0) return null;\n if (matches.length === 1) return matches[0] ?? null;\n\n const allRoots = matches.map((m) => m.block.root);\n const candidateRootSet = new Set<BlockMerkleRoot>(allRoots);\n const edgeSet = buildEdgeSet(candidateRootSet, strictnessEdges);\n\n // Build a provenance lookup for tiebreaking.\n const provenanceMap = new Map<BlockMerkleRoot, number>();\n for (const p of provenance) {\n if (candidateRootSet.has(p.blockRoot)) {\n provenanceMap.set(p.blockRoot, p.passingRuns);\n }\n }\n\n // Find maximally-strict candidates: those where no other candidate is\n // declared stricter than them.\n const maximal = matches.filter((m) => {\n const root = m.block.root;\n // Is any other candidate declared stricter than this one?\n return !allRoots.some(\n (otherRoot) => otherRoot !== root && isStricterThan(otherRoot, root, edgeSet, allRoots),\n );\n });\n\n // Should always have at least one maximal element.\n const pool = maximal.length > 0 ? maximal : matches;\n\n if (pool.length === 1) return pool[0] ?? null;\n\n // Tiebreak deterministically.\n const sorted = [...pool].sort((a, b) => {\n // a. Non-functional quality score — higher wins.\n const nfA = nfScore(a.block.spec);\n const nfB = nfScore(b.block.spec);\n if (nfA !== nfB) return nfB - nfA; // descending\n\n // b. Passing test history runs — more wins.\n const passingA = provenanceMap.get(a.block.root) ?? 0;\n const passingB = provenanceMap.get(b.block.root) ?? 0;\n if (passingA !== passingB) return passingB - passingA; // descending\n\n // c. Lexicographically smaller block merkle root — a < b means a wins.\n return a.block.root < b.block.root ? -1 : 1;\n });\n\n return sorted[0] ?? null;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-STORAGE-009: SQLite + sqlite-vec for registry storage.\n// Status: decided (MASTER_PLAN.md DEC-STORAGE-009); implemented by WI-003.\n// Rationale: Single-file local store; vector index in the same DB; embeds\n// cleanly into a CLI. Federation in v1 layers on top, not under.\n\n// @decision DEC-NO-OWNERSHIP-011: No author identity, no signatures, no\n// reserved columns for either in any type exported from this package.\n// Status: decided (MASTER_PLAN.md DEC-NO-OWNERSHIP-011)\n\n// @decision DEC-V1-FEDERATION-WIRE-ARTIFACTS-002: BlockTripletRow.artifacts\n// threads artifact bytes from buildTriplet through registry storage and onto\n// the federation wire. The field is required (NOT optional) — a missing-field\n// default would silently treat \"no artifacts\" the same as \"never threaded\n// artifacts through\" (Sacred Practice #5). Keyed by manifest-declared path;\n// no ownership columns (DEC-NO-OWNERSHIP-011). Pre-WI-022 rows backfill with\n// empty Map; their pre-existing blockMerkleRoot values are not recomputed.\n// Status: decided (MASTER_PLAN.md WI-022)\n\n// @decision DEC-SCHEMA-MIGRATION-002: WI-T03 replaces the v0 (contracts,\n// implementations) two-table schema with a single `blocks` table keyed by\n// BlockMerkleRoot with a spec_hash index. The public API surface changes:\n// - store(contract, impl) → storeBlock(row: BlockTripletRow)\n// - selectImplementation(contractId) → selectBlocks(specHash) + getBlock(merkleRoot)\n// No dual-table coexistence; no fallback path (Sacred Practice #12).\n// Status: decided (MASTER_PLAN.md WI-T03 Evaluation Contract)\n\n// Internal import: used by BlockTripletRow, Registry, and related interface\n// definitions throughout this file.\nimport type { BlockMerkleRoot, CanonicalAstHash, SpecHash } from \"@yakcc/contracts\";\n\n// Re-export core content-address types so consumers can import from a single\n// package. BlockMerkleRoot, CanonicalAstHash, and SpecHash appear in the public\n// surface of BlockTripletRow, Registry, and related interfaces — callers need\n// these types to construct and type-check registry calls without a direct\n// @yakcc/contracts dependency. (export type re-export is separate from the\n// import type above: the import brings names into scope locally; the export\n// makes them available to consumers of this module.)\nexport type { BlockMerkleRoot, CanonicalAstHash, SpecHash } from \"@yakcc/contracts\";\n\n// Re-export query surface types from @yakcc/contracts so consumers can import\n// from a single package (DEC-V3-IMPL-QUERY-005 coexistence contract).\nexport type { QueryIntentCard, QueryTypeSignatureParam } from \"@yakcc/contracts\";\nexport { canonicalizeQueryText } from \"@yakcc/contracts\";\n\n// ---------------------------------------------------------------------------\n// Registry value types (v0.6 triplet schema)\n// ---------------------------------------------------------------------------\n\n/**\n * A complete block triplet row as stored in (and retrieved from) the `blocks`\n * table. This is the canonical in-memory representation of a registered block.\n *\n * No ownership-shaped columns — DEC-NO-OWNERSHIP-011.\n */\nexport interface BlockTripletRow {\n /** BLAKE3(spec_hash || impl_hash || proof_root) — the block's content address. */\n readonly blockMerkleRoot: BlockMerkleRoot;\n /**\n * BLAKE3(canonicalize(spec.yak)) — the spec's content address.\n * Two blocks that satisfy the same contract share a spec_hash.\n */\n readonly specHash: SpecHash;\n /**\n * The canonicalized spec bytes. Stored to avoid re-canonicalization on read\n * and to verify integrity of specHash.\n */\n readonly specCanonicalBytes: Uint8Array;\n /** The impl.ts source text as UTF-8. */\n readonly implSource: string;\n /** The proof/manifest.json content, serialized as JSON text. */\n readonly proofManifestJson: string;\n /** The declared verification level: L0 | L1 | L2 | L3. */\n readonly level: \"L0\" | \"L1\" | \"L2\" | \"L3\";\n /** Unix epoch milliseconds of insertion. */\n readonly createdAt: number;\n /**\n * BLAKE3 hash of the canonical AST of impl.ts.\n * Two impls that are semantically identical under AST canonicalization share\n * this hash even if their source text differs. Used for deduplication and\n * cross-spec reuse detection. Populated by `canonicalAstHash(implSource)`.\n */\n readonly canonicalAstHash: CanonicalAstHash;\n /**\n * BlockMerkleRoot of the recursion-tree parent from which this block was shaved.\n * NULL (or omitted) means this block is the root of its recursion tree — e.g.\n * a hand-authored seed block or shave's top-level proposal.\n * Non-null values record lineage for atoms produced during a shave recursion.\n * Population of this field is deferred to shave persistence (WI-014-04 follow-up);\n * callers should default to null when constructing rows today.\n */\n readonly parentBlockRoot?: BlockMerkleRoot | null;\n /**\n * Artifact bytes keyed by manifest-declared path (same paths as\n * `manifest.artifacts[*].path`). This Map is the single source of truth for\n * the artifact bytes that fed into `blockMerkleRoot()` — it MUST be the exact\n * Map passed to `blockMerkleRoot({..., artifacts})` at row-construction time.\n *\n * Required (NOT optional). Callers that have no artifact bytes supply\n * `new Map()`. A missing field is a compile-time error — Sacred Practice #5.\n *\n * Pre-WI-022 rows that were persisted before this field existed hydrate with\n * `new Map()` (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002 migration note).\n *\n * No ownership-shaped keys or values — DEC-NO-OWNERSHIP-011.\n */\n readonly artifacts: ReadonlyMap<string, Uint8Array>;\n\n // ---------------------------------------------------------------------------\n // Migration-6 fields (DEC-V2-FOREIGN-BLOCK-SCHEMA-001 / WI-V2-04 L2)\n // ---------------------------------------------------------------------------\n\n /**\n * Block kind discriminator. 'local' for yakcc-shaved blocks (the default for\n * all pre-v6 rows); 'foreign' for foreign npm/Node opaque leaf blocks.\n *\n * Optional for backwards compatibility: existing callsites that omit `kind`\n * are treated as 'local' by the storage layer (L2-I3 invariant guard).\n * Hydrated rows always carry an explicit value ('local' or 'foreign').\n */\n readonly kind?: \"local\" | \"foreign\";\n\n /**\n * npm package name or Node built-in specifier (e.g. `\"node:fs\"`, `\"ts-morph\"`).\n * Non-null only when kind='foreign'. Null / absent for local blocks.\n * L2-I3: if kind='foreign', foreignPkg MUST be non-null (enforced at storeBlock).\n */\n readonly foreignPkg?: string | null;\n\n /**\n * Exported symbol name consumed at the use site (e.g. `\"readFileSync\"`).\n * Non-null only when kind='foreign'. Null / absent for local blocks.\n * L2-I3: if kind='foreign', foreignExport MUST be non-null (enforced at storeBlock).\n */\n readonly foreignExport?: string | null;\n\n /**\n * Optional BLAKE3 hash of the .d.ts declaration text at storage time.\n * Present when the foreign block was snapshotted with type-drift identity.\n * Null / absent when not snapshotted or for local blocks.\n */\n readonly foreignDtsHash?: string | null;\n\n // ---------------------------------------------------------------------------\n // Migration-7 fields (DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001 / P1)\n // ---------------------------------------------------------------------------\n\n /**\n * Workspace package directory from which this atom was shaved\n * (e.g. `\"packages/cli\"`). Non-null only for local blocks produced by\n * `yakcc bootstrap`. Null / absent for foreign atoms, seed blocks, and\n * compose-time atoms (any caller that does not supply source context).\n *\n * Optional (not required) so all pre-v7 callers — federation.ts, seed.ts,\n * assemble-candidate.ts — compile without changes (T11 invariant).\n * First-observed-wins via INSERT OR IGNORE in storeBlock; a second store with\n * null does not overwrite an existing non-null value.\n *\n * @decision DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001\n */\n readonly sourcePkg?: string | null;\n\n /**\n * Workspace-relative path of the originating .ts source file\n * (e.g. `\"packages/cli/src/commands/compile.ts\"`). Non-null only for local\n * blocks produced by `yakcc bootstrap`. Null / absent otherwise.\n *\n * sourcePkg is always a prefix of sourceFile when both are non-null.\n *\n * @decision DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001\n */\n readonly sourceFile?: string | null;\n\n /**\n * Byte offset of the atom's `implSource` within `sourceFile`. Used to sort\n * multiple atoms from the same file when reconstructing source order during\n * compile-self (P2). Null when unknown (all pre-v7 rows, foreign atoms,\n * seed blocks).\n *\n * NOT folded into blockMerkleRoot — provenance is metadata only.\n *\n * @decision DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001\n */\n readonly sourceOffset?: number | null;\n}\n\n/**\n * A contract paired with a similarity score indicating how well it matches\n * a caller's proposal. Scores are in [0, 1] where 1.0 is an exact canonical\n * match. Scores are only meaningful relative to each other within a single\n * search response — do not use cosine distance as a correctness criterion\n * (DEC-EMBED-010, DESIGN.md \"The embedding is just an index\").\n *\n * Retained for the search() path which returns ContractSpec-level matches\n * against the spec_hash index.\n */\nexport interface Match {\n /** The spec hash identifying the matched contract. */\n readonly specHash: SpecHash;\n /** Similarity score in [0, 1]. Higher is closer. */\n readonly score: number;\n}\n\n/**\n * A candidate returned by vector search: a Match plus the BlockMerkleRoots\n * of all blocks satisfying the matched spec.\n */\nexport interface Candidate {\n readonly match: Match;\n /** All block merkle roots satisfying the matched spec. */\n readonly blockMerkleRoots: readonly BlockMerkleRoot[];\n}\n\n/**\n * Provenance metadata for a block identified by BlockMerkleRoot.\n *\n * This record intentionally carries no author identity or signature fields\n * (DEC-NO-OWNERSHIP-011). Trust mechanisms, if they arrive, attach to block\n * merkle roots in a sidecar layer — they do not pre-bake columns here.\n */\nexport interface Provenance {\n /** Recorded test runs against this block. */\n readonly testHistory: readonly ProvenanceTestEntry[];\n /**\n * Recorded production exposures: how many times this block has been\n * invoked in real program assemblies.\n */\n readonly runtimeExposure: readonly RuntimeExposureEntry[];\n}\n\n/** One recorded test run in a provenance record. */\nexport interface ProvenanceTestEntry {\n readonly runAt: string;\n readonly passed: boolean;\n readonly caseCount: number;\n}\n\n/** One recorded production-exposure event. */\nexport interface RuntimeExposureEntry {\n readonly observedAt: string;\n /** The BlockMerkleRoot of the top-level program this block was assembled into. */\n readonly assembledInto: BlockMerkleRoot;\n}\n\n// ---------------------------------------------------------------------------\n// WI-V2-BOOTSTRAP-01: BootstrapManifestEntry — export-manifest primitive\n// ---------------------------------------------------------------------------\n\n/**\n * One entry per stored block, suitable for committed-artifact comparison.\n * Excludes timestamps and other non-deterministic columns (createdAt, ROWID).\n *\n * Used by `yakcc bootstrap --verify` (WI-V2-BOOTSTRAP-03) for byte-identity\n * gating: the caller serialises a `readonly BootstrapManifestEntry[]` (sorted\n * ascending by blockMerkleRoot) to JSON, commits the result as\n * `bootstrap/expected-roots.json`, and re-derives it on every CI run.\n *\n * @decision DEC-V2-BOOTSTRAP-MANIFEST-001\n * @title BootstrapManifestEntry excludes non-deterministic columns\n * @status accepted\n * @rationale createdAt and ROWID vary per-environment and per-run. Including\n * them would make the committed artifact non-reproducible. The six fields\n * below are all content-addressed (derived from artifact bytes via BLAKE3) —\n * the same block stored on any machine at any time produces the same entry.\n * This is the load-bearing determinism contract for the bootstrap demo.\n */\nexport interface BootstrapManifestEntry {\n /** Content address of the block triplet (BLAKE3 of spec||impl||proof). */\n readonly blockMerkleRoot: BlockMerkleRoot;\n /** Content address of the spec.yak (BLAKE3 of canonicalized spec bytes). */\n readonly specHash: SpecHash;\n /** String form of the canonical AST hash of impl.ts. */\n readonly canonicalAstHash: string;\n /** BlockMerkleRoot of the recursion-tree parent, or null for root blocks. */\n readonly parentBlockRoot: BlockMerkleRoot | null;\n /**\n * Hex BLAKE3-256 of the impl.ts artifact bytes (the raw bytes stored in\n * block_artifacts WHERE path = 'impl.ts'). Sentinel (BLAKE3 of empty\n * string) when the artifact is absent — see exportManifest() fallback note.\n */\n readonly implSourceHash: string;\n /**\n * Hex BLAKE3-256 of the proof/manifest.json artifact bytes (stored in\n * block_artifacts WHERE path = 'proof/manifest.json'). Sentinel when absent.\n */\n readonly manifestJsonHash: string;\n}\n\n// ---------------------------------------------------------------------------\n// P2: WorkspacePlumbingEntry — one row from workspace_plumbing\n// (DEC-V2-WORKSPACE-PLUMBING-AUTHORITY-001 / WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P2)\n// ---------------------------------------------------------------------------\n\n/**\n * One row from the `workspace_plumbing` table.\n *\n * Captures non-atom workspace files (package.json, tsconfig*.json,\n * pnpm-workspace.yaml, etc.) needed to make a recompiled workspace\n * pnpm-installable and buildable. Populated by `yakcc bootstrap`.\n *\n * @decision DEC-V2-WORKSPACE-PLUMBING-AUTHORITY-001\n * @title workspace_plumbing is the single authority for non-atom bootable files\n * @status accepted (WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P2)\n * @rationale Compile-self must reconstruct a pnpm workspace from the registry\n * alone. Reading plumbing files from the live filesystem at compile-self time\n * would be a parallel authority (Sacred Practice #12). The workspace_plumbing\n * table is populated during bootstrap and read at compile-self time; no\n * filesystem fallback is permitted at compile-self time.\n */\nexport interface WorkspacePlumbingEntry {\n /**\n * Workspace-relative path of the captured file\n * (e.g. 'package.json', 'packages/cli/package.json').\n * Must not start with '/' or contain '..'.\n */\n readonly workspacePath: string;\n /** Raw file bytes, byte-faithful (no normalization). */\n readonly contentBytes: Uint8Array;\n /**\n * BLAKE3-256 hex digest of contentBytes.\n * Verified by storeWorkspacePlumbing at write time.\n */\n readonly contentHash: string;\n /** Unix epoch milliseconds of first insertion. */\n readonly createdAt: number;\n}\n\n// ---------------------------------------------------------------------------\n// WI-V2-WORKSPACE-PLUMBING-GLUE-CAPTURE: SourceFileGlueEntry — one row from source_file_glue\n// (DEC-V2-GLUE-CAPTURE-AUTHORITY-001 / WI-V2-WORKSPACE-PLUMBING-GLUE-CAPTURE #333)\n// ---------------------------------------------------------------------------\n\n/**\n * One row from the `source_file_glue` table.\n *\n * Captures all non-atom byte regions of a single source file as a single\n * concatenated blob. The blob is the byte content of the file minus the byte\n * ranges of each atom's implSource (in source order), concatenated in the order\n * they appear in the source file:\n *\n * glue_blob = F[0..A1.start) ++ F[A1.end..A2.start) ++ ... ++ F[An.end..fileLen)\n *\n * The consumer (compile-self) reconstructs the original file by interleaving\n * atom implSources with slices of this blob, using blocks.source_offset as the\n * single authority for atom byte positions.\n *\n * @decision DEC-V2-GLUE-CAPTURE-AUTHORITY-001\n * @title Per-file glue lives in source_file_glue; single concatenated blob per\n * (source_pkg, source_file); boundaries derived from blocks.source_offset at\n * consume time — no dual-authority with blocks.source_offset column.\n * @status decided (WI-V2-WORKSPACE-PLUMBING-GLUE-CAPTURE #333)\n *\n * No ownership-shaped fields — DEC-NO-OWNERSHIP-011.\n * No merkle-root field — glue is workspace-reconstruction metadata, not a\n * registry citizen. contentHash is BLAKE3-256 for integrity/dedup only.\n */\nexport interface SourceFileGlueEntry {\n /**\n * Workspace package directory (e.g. 'packages/cli').\n * Must match the sourcePkg on the corresponding blocks rows.\n */\n readonly sourcePkg: string;\n /**\n * Workspace-relative path of the source file (e.g. 'packages/cli/src/commands/foo.ts').\n * Must match the sourceFile on the corresponding blocks rows.\n */\n readonly sourceFile: string;\n /**\n * BLAKE3-256 hex digest of contentBlob.\n * Verified at write time; callers must supply a matching hash.\n */\n readonly contentHash: string;\n /**\n * Concatenated bytes of all non-atom regions in source order.\n * Length: fileLength - sum(atom.implSource.length for atoms in this file).\n */\n readonly contentBlob: Uint8Array;\n /** Unix epoch milliseconds of first insertion. */\n readonly createdAt: number;\n}\n\n// ---------------------------------------------------------------------------\n// WI-V2-04 L2: ForeignRefRow — one entry from block_foreign_refs\n// (DEC-V2-FOREIGN-BLOCK-SCHEMA-001 / WI-V2-04 L2)\n// ---------------------------------------------------------------------------\n\n/**\n * One row from the `block_foreign_refs` table.\n *\n * Tracks the foreign-dependency tree of a non-foreign block: each row records\n * one foreign atom referenced at a specific declaration_index in the parent\n * block's impl/spec. Returned in declaration_index ASC order by getForeignRefs().\n *\n * No ownership-shaped fields — DEC-NO-OWNERSHIP-011.\n */\nexport interface ForeignRefRow {\n /** The non-foreign block that depends on the foreign atom. */\n readonly parentBlockRoot: BlockMerkleRoot;\n /** The foreign block (kind='foreign') referenced at declarationIndex. */\n readonly foreignBlockRoot: BlockMerkleRoot;\n /** 0-based position of this foreign reference in the parent block's impl/spec. */\n readonly declarationIndex: number;\n}\n\n// ---------------------------------------------------------------------------\n// #355: BlockOccurrenceEntry — one row from block_occurrences\n// (DEC-V2-BLOCK-OCCURRENCES-SCHEMA-001 / WI-V2-STORAGE-IDEMPOTENT-RECOMPILE)\n// ---------------------------------------------------------------------------\n\n/**\n * One row from the `block_occurrences` table.\n *\n * Records a single occurrence of a block atom at a specific position in a\n * source file. Per-occurrence state is authoritative here; the legacy\n * `blocks.source_offset` column is a write-once shim for the first observed\n * occurrence and must not be used as an authoritative offset after re-bootstrap.\n *\n * @decision DEC-V2-BLOCK-OCCURRENCES-SCHEMA-001\n * @decision DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001\n *\n * No ownership-shaped fields — DEC-NO-OWNERSHIP-011.\n */\nexport interface BlockOccurrenceEntry {\n /**\n * Workspace package directory (e.g. 'packages/cli').\n * Part of the composite PK in block_occurrences.\n */\n readonly sourcePkg: string;\n /**\n * Workspace-relative path of the source file\n * (e.g. 'packages/cli/src/commands/bootstrap.ts').\n * Part of the composite PK.\n */\n readonly sourceFile: string;\n /**\n * JS string character offset of the atom's implSource within sourceFile.\n * This is the current-truth offset from the most recent bootstrap pass —\n * NOT the stale first-observed-wins value in blocks.source_offset.\n */\n readonly sourceOffset: number;\n /**\n * JS string character count of the atom's implSource.\n * Materialized so reconstruction does not need a join against blocks.\n */\n readonly length: number;\n /** Content address of the atom block. FK → blocks(block_merkle_root). */\n readonly blockMerkleRoot: string;\n}\n\n// ---------------------------------------------------------------------------\n// WI-025: Intent query shape + vector-search types (findCandidatesByIntent)\n// ---------------------------------------------------------------------------\n\n// @decision DEC-VECTOR-RETRIEVAL-004\n// title: IntentQuery is a local structural type, not an import from @yakcc/shave\n// status: accepted\n// rationale: @yakcc/shave depends on @yakcc/registry, so importing IntentCard\n// from @yakcc/shave into @yakcc/registry would create a circular dependency.\n// IntentQuery is a structural subset of IntentCard (same field names and types\n// for the fields used by findCandidatesByIntent). Any IntentCard value is\n// assignable to IntentQuery without casting. The query method only needs\n// behavior, inputs[].{name,typeHint}, and outputs[].{name,typeHint} — the\n// remaining IntentCard fields (modelVersion, promptVersion, etc.) are\n// irrelevant to the KNN query. TypeScript's structural typing ensures\n// IntentCard values pass to findCandidatesByIntent without explicit conversion.\n\n/** A named typed parameter used in an intent query (structural subset of IntentCard). */\nexport interface IntentQueryParam {\n readonly name: string;\n readonly typeHint: string;\n}\n\n/**\n * A minimal intent query shape for findCandidatesByIntent().\n *\n * Structurally compatible with @yakcc/shave's IntentCard — any IntentCard\n * is assignable here. @yakcc/registry intentionally does not import IntentCard\n * to avoid a circular dependency (DEC-VECTOR-RETRIEVAL-004).\n */\nexport interface IntentQuery {\n readonly behavior: string;\n readonly inputs: readonly IntentQueryParam[];\n readonly outputs: readonly IntentQueryParam[];\n}\n\n// ---------------------------------------------------------------------------\n// WI-025: Vector-search result types\n// ---------------------------------------------------------------------------\n\n// @decision DEC-VECTOR-RETRIEVAL-002\n// title: Query-text derivation rule for findCandidatesByIntent\n// status: accepted\n// rationale: The query text is constructed by joining the card's behavior string\n// with each input's \"name: typeHint\" and each output's \"name: typeHint\",\n// separated by newlines. This produces a text that captures the full functional\n// signature and behavioral intent in a format the embedding model understands.\n// The join mirrors what is embedded at write time (storeBlock generates embeddings\n// from the spec's behavior+parameter text), so query and document vectors are in\n// the same semantic space. Empty inputs/outputs are handled gracefully (the\n// behavior string alone is a valid query).\n\n/**\n * A candidate block returned by findCandidatesByIntent().\n *\n * cosineDistance is the raw KNN distance from sqlite-vec (lower = more similar).\n * structuralScore is present only when rerank: \"structural\" was requested;\n * it is the structural match score used in the combined ranking formula.\n *\n * Do not use cosineDistance as a correctness criterion — it is a retrieval\n * index, not a behavioral proof (DEC-EMBED-010, DESIGN.md cornerstone #4).\n */\nexport interface CandidateMatch {\n /** The full block triplet row. */\n readonly block: BlockTripletRow;\n /**\n * Cosine distance from the query embedding to this block's spec embedding.\n * Lower values indicate greater semantic similarity.\n * Results are ordered ascending in cosineDistance by default.\n */\n readonly cosineDistance: number;\n /**\n * Combined structural match score. Present only when rerank: \"structural\"\n * was requested. Derived from structuralMatch(querySpec, candidateSpec).\n * 1.0 = exact structural match; 0.0 = no structural match.\n *\n * @decision DEC-VECTOR-RETRIEVAL-003\n * @title Structural rerank scoring formula\n * @status accepted\n * @rationale The combined ranking score is (1 - cosineDistance) + structuralScore,\n * sorted descending. This additive formula gives equal weight to cosine similarity\n * and structural match quality. structuralScore from structuralMatch is 0 or 1 at\n * v0 (binary: matches or not). A multiplicative formula was rejected because it\n * would zero-out structurally-unmatched results, collapsing them all to the same\n * rank and making cosine order meaningless among mismatches.\n */\n readonly structuralScore?: number | undefined;\n}\n\n/**\n * Options for findCandidatesByIntent().\n */\nexport interface FindCandidatesOptions {\n /**\n * Maximum number of candidates to retrieve from the KNN index.\n * Defaults to 10.\n */\n readonly k?: number | undefined;\n /**\n * Reranking strategy. Defaults to \"none\" (cosine distance order only).\n * - \"none\": return KNN results ordered by ascending cosineDistance.\n * - \"structural\": reorder by combined (1 - cosineDistance) + structuralScore descending.\n */\n readonly rerank?: \"structural\" | \"none\" | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// WI-V3-DISCOVERY-IMPL-QUERY: Multi-dimensional query types (D2+D3 ADRs)\n// ---------------------------------------------------------------------------\n\n// @decision DEC-V3-IMPL-QUERY-004\n// title: QueryCandidate shape + autoAccepted flag\n// status: accepted\n// rationale: D2 §Q3 established Candidate shape; D3 §Q1 pinned autoAccepted:boolean.\n// Renamed QueryCandidate (not Candidate) to avoid shadowing the existing Candidate\n// type (for search() path) already exported from this module. The autoAccepted flag\n// is computed at query time (never inherited from storage) per D3 §Q1.\n// perDimensionScores uses null (not undefined) for zero-vector dimensions per D1.\n\n/**\n * Per-dimension cosine similarity scores for a QueryCandidate.\n *\n * In v1 (single unified embedding column), `unified` carries the sole\n * combinedScore. The per-dimension keys (behavior, guarantees, …) are reserved\n * for v3.1 when migration 7 (5-column schema) ships. Callers must key off\n * `unified` for v1; per-dimension keys will be undefined.\n *\n * A dimension is absent (undefined) when the query did not include that dimension,\n * OR when the stored atom has a zero vector for that dimension (D1 absent-dimension\n * fallback). null means the dimension was queried but the stored atom has no signal.\n *\n * This lets callers distinguish \"not queried\" from \"queried but no signal\".\n */\nexport interface PerDimensionScores {\n /** v1 unified score — present in all v1 QueryCandidate results (DEC-V3-IMPL-QUERY-003). */\n readonly unified?: number | null | undefined;\n /** Reserved for v3.1 per-dimension schema (migration 7 / D1). */\n readonly behavior?: number | null | undefined;\n readonly guarantees?: number | null | undefined;\n readonly errorConditions?: number | null | undefined;\n readonly nonFunctional?: number | null | undefined;\n readonly propertyTests?: number | null | undefined;\n}\n\n/**\n * A candidate returned by findCandidatesByQuery().\n *\n * Extends CandidateMatch with multi-dimensional scores (D2 §Q3 + D3 §Q1).\n *\n * @decision DEC-V3-IMPL-QUERY-004 (see above)\n */\nexport interface QueryCandidate extends CandidateMatch {\n /** Per-dimension cosine similarity scores in [0, 1]. */\n readonly perDimensionScores: PerDimensionScores;\n /**\n * Weighted sum of per-dimension scores, normalized over surviving dimensions.\n * Always in [0, 1] (D3 §Q1 renormalized formula).\n * Formula: Σ(weights[d] * similarity[d]) / Σ(weights[d]) for d in surviving_dims\n * where similarity[d] = 1 - L²/4 (re-stated: 1 - cosineDistance[d]/2).\n *\n * @decision DEC-V3-IMPL-QUERY-007 — re-stated 1 - L²/4 formula inline.\n * Canonical site: discovery-eval-helpers.ts cosineDistanceToCombinedScore.\n */\n readonly combinedScore: number;\n /**\n * True when auto-accept rule fires: combinedScore > 0.85 AND gap-to-top-2 > 0.15\n * (D2 §Q5 + D3 §Q1). Always false for CandidateNearMiss entries.\n */\n readonly autoAccepted: boolean;\n}\n\n/**\n * A near-miss candidate that failed at a pipeline filter stage.\n *\n * Returned in FindCandidatesByQueryResult.nearMisses when 0 regular candidates\n * survive all pipeline stages (D3 §Q6).\n *\n * @decision DEC-V3-IMPL-QUERY-006\n * @title D3 5-stage pipeline + CandidateNearMiss envelope\n * @status accepted\n * @rationale D3 §Q6 requires near-miss surfacing when the pipeline returns 0\n * candidates. Near-misses are drawn from Stage 1 K' candidates, annotated\n * with failedAtLayer and failureReason. autoAccepted is always false (D3 §Q1).\n * The v3 pipeline: Stage1=KNN, Stage2=structural, Stage3=strictness,\n * Stage4=reserved(no-op), Stage5=ranking+tiebreak. Stage 4 is a pass-through\n * with @decision annotation per DEC-VERIFY-010 trigger (v3.1 boundary).\n */\nexport interface CandidateNearMiss extends QueryCandidate {\n /** Always false — near-misses are never auto-accepted. */\n readonly autoAccepted: false;\n /**\n * Pipeline layer at which this candidate was rejected:\n * - 'structural' — failed Stage 2 (structuralMatch returned false)\n * - 'strictness' — failed Stage 3 (level/purity/threadSafety mismatch)\n * - 'property_test' — reserved Stage 4 (v3.1; unused in v3)\n * - 'min_score' — survived filter stages but combinedScore < minScore\n */\n readonly failedAtLayer: \"structural\" | \"strictness\" | \"property_test\" | \"min_score\";\n /** One-line human-readable rejection reason. */\n readonly failureReason: string;\n}\n\n/**\n * Options for findCandidatesByQuery().\n *\n * @decision DEC-V3-IMPL-QUERY-003\n * @title Per-dimension weight collapse for v1 (key 'unified')\n * @status accepted\n * @rationale v1 of findCandidatesByQuery uses a single unified embedding column\n * (the existing contract_embeddings.embedding) rather than the 5 per-dimension\n * columns planned by D1 (those require schema migration 7, deferred).\n * Per-dimension weights in QueryIntentCard.weights are accepted by the API\n * surface for forward-compatibility but collapsed to a single weight key\n * ('unified') internally. The combinedScore is computed from the single\n * cosineDistance as: 1 - L²/4. All perDimensionScores map to the unified\n * score; per-dimension granularity ships in v3.1 once migration 7 lands.\n */\nexport interface FindCandidatesByQueryOptions {\n /**\n * Embedding provider model ID used to write the query-time vectors.\n * Must match the registry's stored model ID; otherwise the query is rejected\n * with reason='cross_provider_rejected' (D2 cross-provider invariant).\n * Defaults to the registry's own embedding provider model ID.\n */\n readonly queryEmbeddings?: { readonly modelId: string } | undefined;\n}\n\n/**\n * Result envelope for findCandidatesByQuery().\n *\n * Distinguishes matched candidates from near-misses (D3 §Q6).\n * The two lists are always separate; matched candidates are never mixed\n * with near-misses (D3 §Q6 \"result envelope distinction\").\n */\nexport interface FindCandidatesByQueryResult {\n /** Matched candidates that survived all pipeline stages, ranked by combinedScore. */\n readonly candidates: readonly QueryCandidate[];\n /**\n * Near-miss candidates when candidates is empty (0 survivors).\n * Empty array when candidates is non-empty.\n */\n readonly nearMisses: readonly CandidateNearMiss[];\n}\n\n// ---------------------------------------------------------------------------\n// Registry interface (v0.6 triplet schema)\n// ---------------------------------------------------------------------------\n\n/**\n * The primary interface for all registry operations.\n *\n * The registry is monotonic: `storeBlock` is the only mutation; there is no\n * `delete` or `update`. Entries improve monotonically as stricter blocks are\n * added alongside originals.\n *\n * Selection operates at two levels:\n * - `selectBlocks(specHash)` returns all blocks satisfying a spec.\n * - `getBlock(merkleRoot)` retrieves a specific block by content address.\n */\nexport interface Registry {\n /**\n * Store a block triplet in the registry.\n *\n * Idempotent: storing the same block (same blockMerkleRoot) twice is a no-op.\n * The spec embedding (keyed on spec_hash) is written once per unique spec —\n * subsequent blocks with the same spec_hash share the embedding row.\n *\n * Throws if `row.blockMerkleRoot` does not match the computed\n * `blockMerkleRoot(triplet)` — callers must pre-compute and supply the root.\n * (The column must be stored, not re-derived at read time — Evaluation\n * Contract forbidden shortcuts.)\n */\n storeBlock(row: BlockTripletRow): Promise<void>;\n\n /**\n * Return all BlockMerkleRoots satisfying the given spec hash, ordered by\n * selection criteria (strictness partial order → non-functional quality →\n * passing test history → lexicographic merkle root).\n *\n * Returns an empty array when no blocks are registered for that spec_hash.\n */\n selectBlocks(specHash: SpecHash): Promise<BlockMerkleRoot[]>;\n\n /**\n * Retrieve the full block triplet row for a given BlockMerkleRoot.\n *\n * Returns null when no block with the given merkle root is registered.\n */\n getBlock(merkleRoot: BlockMerkleRoot): Promise<BlockTripletRow | null>;\n\n /**\n * Return all BlockMerkleRoots whose impl source has the given canonical AST\n * hash, ordered by insertion order then lexicographic merkle root\n * (`ORDER BY created_at ASC, block_merkle_root ASC`).\n *\n * This is a structural-equivalence query, not a candidate-ranking query.\n * It returns every block that was compiled from a semantically identical\n * impl, regardless of which spec it satisfies. Because canonical-ast-hash\n * lookup crosses spec boundaries, the strictness partial order and\n * test-history ranking used by `selectBlocks` do not apply here — those\n * criteria are only meaningful within a single spec. Results are sorted by\n * insertion order then merkle root for stable, deterministic iteration;\n * callers seeking a ranked candidate for a specific spec should use\n * `selectBlocks` instead.\n *\n * Returns an empty array when no blocks match.\n */\n findByCanonicalAstHash(hash: CanonicalAstHash): Promise<readonly BlockMerkleRoot[]>;\n\n /**\n * Retrieve provenance metadata for a block by its BlockMerkleRoot.\n *\n * Returns a Provenance record with empty arrays if no evidence has been\n * recorded yet — absence of evidence is not evidence of absence.\n */\n getProvenance(merkleRoot: BlockMerkleRoot): Promise<Provenance>;\n\n // @decision DEC-V3-IMPL-QUERY-005\n // title: findCandidatesByQuery coexistence with findCandidatesByIntent\n // status: accepted\n // rationale: D2 ADR §Q3 mandates that findCandidatesByIntent remains on the\n // Registry interface unchanged. The new findCandidatesByQuery is added\n // alongside it (not as a replacement). A QueryIntentCard with only behavior\n // set is semantically equivalent to findCandidatesByIntent for a behavior-only\n // query. The implementation WI owns the runtime decision (deprecate/alias/wrap);\n // D2 constrains: no breaking changes without a documented migration path.\n // findCandidatesByIntent callers are NOT broken.\n /**\n * Find candidates using a multi-dimensional QueryIntentCard (D2/D3 ADRs).\n *\n * Runs the D3 5-stage pipeline:\n * Stage 1 — KNN retrieval (K' = max(topK*5, 50))\n * Stage 2 — Structural filter (structuralMatch on signature)\n * Stage 3 — Strictness filter (level/purity/threadSafety)\n * Stage 4 — Reserved no-op (DEC-VERIFY-010 trigger; v3.1 boundary)\n * Stage 5 — Ranking (combinedScore desc, ε=0.02 tiebreaker, minScore, topK)\n *\n * When 0 candidates survive Stages 2–4, returns near-misses from Stage 1\n * candidates annotated with failedAtLayer/failureReason (D3 §Q6).\n *\n * Cross-provider rejection: throws Error with reason='cross_provider_rejected'\n * when options.queryEmbeddings.modelId !== registry's embedding model ID\n * (D2 cross-provider invariant, checked before any KNN call).\n *\n * @param query - The LLM-provided QueryIntentCard.\n * @param options - Optional: queryEmbeddings model ID for cross-provider check.\n */\n findCandidatesByQuery(\n query: import(\"@yakcc/contracts\").QueryIntentCard,\n options?: FindCandidatesByQueryOptions,\n ): Promise<FindCandidatesByQueryResult>;\n\n // @decision DEC-VECTOR-RETRIEVAL-001\n // title: Public vector-search surface on the Registry interface\n // status: accepted\n // rationale: WI-025 adds findCandidatesByIntent() as the first semantic\n // (embedding-based) retrieval method. Prior retrieval was structural-only\n // (selectBlocks by specHash). This new path derives a query text from an\n // IntentCard, runs a sqlite-vec KNN query against contract_embeddings, and\n // optionally reranks by combined cosine+structural score. The method lives\n // on the Registry interface (not a standalone function) so it shares the\n // same DB connection, embedding provider, and lifecycle as the rest of the\n // registry. WI-026 (Claude Code hook interception) is the primary consumer.\n /**\n * Find candidate blocks semantically close to an intent card.\n *\n * Derives query text from the card (behavior + \"name: typeHint\" for each\n * input and output), generates an embedding, and runs a KNN query against\n * the contract_embeddings vec0 table. Optionally reranks by combined\n * cosine + structural score.\n *\n * Returns an empty array when the registry has no blocks.\n * Results are ordered by ascending cosineDistance by default.\n * When rerank: \"structural\" is requested, results are reordered by\n * (1 - cosineDistance) + structuralScore descending.\n *\n * @param intentCard - The caller's intent card (e.g. from staticExtract).\n * @param options - Optional: k (default 10), rerank (\"none\" | \"structural\").\n */\n findCandidatesByIntent(\n intentCard: IntentQuery,\n options?: FindCandidatesOptions,\n ): Promise<readonly CandidateMatch[]>;\n\n /**\n * Return all distinct spec hashes present in the registry, sorted ascending\n * by spec_hash value.\n *\n * This is the server-side primitive for GET /v1/specs in the federation\n * serve path (FEDERATION_PROTOCOL.md §3, DEC-TRANSPORT-LIST-METHODS-020).\n *\n * Implemented as a single `SELECT DISTINCT spec_hash FROM blocks ORDER BY\n * spec_hash` query — O(n blocks) read, no mutations.\n *\n * Returns an empty array for an empty registry.\n *\n * @decision DEC-SERVE-SPECS-ENUMERATION-020 (closure note):\n * WI-026 adds this method to replace the optional `enumerateSpecs` callback\n * that serveRegistry previously accepted in ServeOptions. The callback was a\n * workaround because Registry had no enumerate-distinct-specs primitive.\n * Post-WI-026, serveRegistry consumes `registry.enumerateSpecs()` directly\n * and the ServeOptions.enumerateSpecs field is removed.\n *\n * No ownership-shaped fields — DEC-NO-OWNERSHIP-011. The query touches\n * `spec_hash` only; no JOIN against any owner-shaped column.\n */\n enumerateSpecs(): Promise<readonly SpecHash[]>;\n\n /**\n * Export a deterministic manifest of every stored block, sorted ascending by\n * `blockMerkleRoot` string value. The sort order is the load-bearing\n * determinism contract: two calls on the same DB state — on any machine at\n * any time — must produce byte-identical JSON when the result is serialized.\n *\n * Excludes `createdAt` and ROWID — both are non-deterministic across\n * environments and irrelevant to content identity (DEC-V2-BOOTSTRAP-MANIFEST-001).\n *\n * `implSourceHash` is BLAKE3-256(hex) of the bytes stored in `block_artifacts`\n * at path `impl.ts`. `manifestJsonHash` is BLAKE3-256(hex) of the bytes stored\n * at path `proof/manifest.json`. When either artifact path is absent (pre-WI-022\n * blocks, or blocks with no matching path), the sentinel value\n * (BLAKE3 of empty Uint8Array, hex-encoded) is used so the schema is uniform.\n *\n * Primary consumer: `yakcc bootstrap --verify` (WI-V2-BOOTSTRAP-03), which\n * re-derives this manifest and compares it byte-for-byte against the committed\n * `bootstrap/expected-roots.json` artifact.\n *\n * Returns an empty array for an empty registry.\n */\n exportManifest(): Promise<readonly BootstrapManifestEntry[]>;\n\n /**\n * Return all foreign-dependency refs for a given parent block, ordered by\n * `declaration_index ASC`. Returns an empty array when the block has no\n * recorded foreign refs (e.g. all pre-v6 local blocks).\n *\n * This is the L2 schema primitive for the provenance manifest's foreign-dep\n * tree. Population of rows (inserting block_foreign_refs during shave) is\n * deferred to L4 (CLI policy layer); this reader exists so the schema is\n * exercised and proven correct in L2 tests before the writer lands.\n *\n * @decision DEC-V2-FOREIGN-BLOCK-SCHEMA-001 (sub-A) — getForeignRefs is the\n * single reader authority for block_foreign_refs. No other path reads this\n * table directly; callers go through this method.\n *\n * @param merkleRoot - The parent block's BlockMerkleRoot.\n */\n getForeignRefs(merkleRoot: BlockMerkleRoot): Promise<readonly ForeignRefRow[]>;\n\n // ---------------------------------------------------------------------------\n // P2: workspace_plumbing accessors\n // (DEC-V2-WORKSPACE-PLUMBING-AUTHORITY-001 / WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P2)\n // ---------------------------------------------------------------------------\n\n /**\n * Insert a plumbing-file row into `workspace_plumbing`.\n *\n * Idempotent via PRIMARY KEY (workspace_path) + INSERT OR IGNORE.\n * First-observed-wins matches the storeBlock pattern — a second call with\n * the same workspacePath is a silent no-op.\n *\n * Throws if:\n * - `entry.contentHash` does not equal BLAKE3-256(contentBytes) — content\n * integrity must be verified by the caller before insertion.\n * - `entry.workspacePath` is an absolute path (starts with '/') or\n * contains '..' — workspace-relative paths only.\n *\n * @decision DEC-V2-WORKSPACE-PLUMBING-AUTHORITY-001\n */\n storeWorkspacePlumbing(entry: WorkspacePlumbingEntry): Promise<void>;\n\n /**\n * Enumerate every plumbing-file row, sorted ascending by workspace_path.\n *\n * Deterministic — two calls on the same DB state produce identical results\n * (same load-bearing invariant as exportManifest()). Returns an empty array\n * for an empty workspace_plumbing table.\n *\n * @decision DEC-V2-WORKSPACE-PLUMBING-AUTHORITY-001\n */\n listWorkspacePlumbing(): Promise<readonly WorkspacePlumbingEntry[]>;\n\n // ---------------------------------------------------------------------------\n // #333: source_file_glue accessors\n // (DEC-V2-GLUE-CAPTURE-AUTHORITY-001 / WI-V2-WORKSPACE-PLUMBING-GLUE-CAPTURE)\n // ---------------------------------------------------------------------------\n\n /**\n * Insert or replace a glue row for `(sourcePkg, sourceFile)`.\n *\n * Idempotent via PRIMARY KEY (source_pkg, source_file) + INSERT OR REPLACE.\n * Re-bootstrap overwrites a prior glue row with refreshed content — this is\n * intentional (unlike storeBlock which is INSERT OR IGNORE). Glue is derived\n * at bootstrap time from the live shave output; a re-run may produce a\n * different glue blob if atoms change. INSERT OR REPLACE ensures the registry\n * reflects the most recent bootstrap run.\n *\n * Throws if:\n * - `entry.contentHash` does not equal BLAKE3-256(contentBlob)\n * - `entry.sourcePkg` or `entry.sourceFile` is empty\n * - `entry.sourceFile` is an absolute path or contains '..'\n *\n * @decision DEC-V2-GLUE-CAPTURE-AUTHORITY-001\n */\n storeSourceFileGlue(entry: SourceFileGlueEntry): Promise<void>;\n\n /**\n * Retrieve the glue row for `(sourcePkg, sourceFile)`.\n *\n * Returns null when no glue row exists for the given key — the consumer\n * must treat null as \"glue not captured\" and emit a `null-glue` gap row\n * rather than assuming the file has no glue.\n *\n * @decision DEC-V2-GLUE-CAPTURE-AUTHORITY-001\n */\n getSourceFileGlue(sourcePkg: string, sourceFile: string): Promise<SourceFileGlueEntry | null>;\n\n /**\n * Enumerate every glue row, sorted ascending by (source_pkg, source_file).\n *\n * Deterministic — two calls on the same DB state produce identical results.\n * Returns an empty array when the source_file_glue table is empty.\n *\n * @decision DEC-V2-GLUE-CAPTURE-AUTHORITY-001\n */\n listSourceFileGlue(): Promise<readonly SourceFileGlueEntry[]>;\n\n /**\n * Return the source-offset and implSource length for every block stored with\n * the given `sourceFile`, sorted ascending by `sourceOffset`.\n *\n * As of schema v9 (#355), this queries `block_occurrences` (current-truth\n * per-file positions) instead of `blocks.source_offset` (stale first-observed\n * position). The function signature and return type are unchanged — callers\n * in `captureSourceFileGlue` and `compile-self.ts` are transparent to this\n * migration.\n *\n * @decision DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001\n * @decision DEC-V2-GLUE-CAPTURE-AUTHORITY-001\n */\n getAtomRangesBySourceFile(\n sourceFile: string,\n ): Promise<readonly { readonly sourceOffset: number; readonly implSourceLength: number }[]>;\n\n /**\n * Return all occurrence rows for `sourceFile` sorted ascending by\n * `sourceOffset`. Queries `block_occurrences` (current-truth), not\n * `blocks.source_offset` (stale first-observed-wins value).\n *\n * Returns an empty array when no occurrences are stored for that file —\n * i.e., the file has not been processed by a v9+ bootstrap pass yet.\n *\n * Primary consumer: compile-self and compile-pipeline reconstruction\n * pipelines, which need current-truth offsets to place atoms correctly in\n * the reconstructed source file.\n *\n * @decision DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001\n * @decision DEC-V2-BLOCK-OCCURRENCES-SCHEMA-001\n */\n listOccurrencesBySourceFile(sourceFile: string): Promise<readonly BlockOccurrenceEntry[]>;\n\n /**\n * Return all occurrence rows for the given `blockMerkleRoot`, sorted\n * ascending by `(source_pkg, source_file, source_offset)`.\n *\n * Returns an empty array when no occurrences are stored for that block.\n *\n * Primary consumer: diagnostic tooling and the planner's stale-offset\n * introspection tests.\n *\n * @decision DEC-V2-BLOCK-OCCURRENCES-SCHEMA-001\n */\n listOccurrencesByMerkleRoot(blockMerkleRoot: string): Promise<readonly BlockOccurrenceEntry[]>;\n\n /**\n * Atomically replace all occurrence rows for `(sourcePkg, sourceFile)` in\n * the `block_occurrences` table.\n *\n * Runs inside a single SQLite transaction:\n * BEGIN;\n * DELETE FROM block_occurrences WHERE source_pkg=? AND source_file=?;\n * for each occurrence: INSERT INTO block_occurrences(...);\n * COMMIT;\n * On failure: ROLLBACK keeps the previous occurrences intact.\n *\n * Bootstrap pipeline contract:\n * 1. Call `storeBlock()` for every novel atom first (FK enforced).\n * 2. Call `replaceSourceFileOccurrences()` once per file after Pass A.\n *\n * Passing an empty `occurrences` array deletes all prior rows for the file\n * without inserting new ones (valid for files that yielded zero atoms).\n *\n * Throws if `sourcePkg` or `sourceFile` is empty.\n *\n * @decision DEC-V2-OCCURRENCE-DELETE-INSERT-001\n * @decision DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001\n */\n replaceSourceFileOccurrences(\n sourcePkg: string,\n sourceFile: string,\n occurrences: readonly {\n readonly sourcePkg: string;\n readonly sourceFile: string;\n readonly sourceOffset: number;\n readonly length: number;\n readonly blockMerkleRoot: string;\n }[],\n ): Promise<void>;\n\n // ---------------------------------------------------------------------------\n // #363: source_file_state accessors — per-file content-hash cache\n // (DEC-V2-SHAVE-CACHE-STORAGE-001 / DEC-V2-REGISTRY-SCHEMA-BUMP-V10-001)\n // ---------------------------------------------------------------------------\n\n /**\n * Write (or overwrite) the BLAKE3-256 hex content hash for a source file in\n * the `source_file_state` table (schema v10, issue #363).\n *\n * Called by `yakcc bootstrap` after a successful cache-miss shave, so the\n * next bootstrap run can short-circuit re-shaving this file if its bytes are\n * unchanged.\n *\n * Uses INSERT OR REPLACE semantics: a second call for the same\n * `(sourcePkg, sourceFile)` overwrites the stored hash and updates\n * `last_shave_time`. This is intentional — the most-recent shave wins.\n *\n * Throws if:\n * - `sourcePkg` or `sourceFile` is empty.\n * - `sourceFile` is an absolute path (starts with '/') or contains '..'.\n * - `contentHash` is empty.\n *\n * @decision DEC-V2-SHAVE-CACHE-STORAGE-001\n */\n storeSourceFileContentHash(\n sourcePkg: string,\n sourceFile: string,\n contentHash: string,\n ): Promise<void>;\n\n /**\n * Retrieve the stored BLAKE3-256 hex content hash for a source file from the\n * `source_file_state` table (schema v10, issue #363).\n *\n * Returns `null` when no row exists for `(sourcePkg, sourceFile)` — meaning\n * the file has never been successfully shaved with cache support, so the next\n * bootstrap pass must perform a full shave (cache miss).\n *\n * Returns the stored hex string when a row exists — the bootstrap caller\n * compares it against the current file's BLAKE3 hash to decide cache hit vs miss.\n *\n * @decision DEC-V2-SHAVE-CACHE-STORAGE-001\n */\n getSourceFileContentHash(sourcePkg: string, sourceFile: string): Promise<string | null>;\n\n /** Release all resources held by this registry instance. */\n close(): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// Public API re-exports\n// ---------------------------------------------------------------------------\n\nexport { openRegistry, type RegistryOptions } from \"./storage.js\";\nexport { structuralMatch, type MatchResult } from \"./search.js\";\nexport {\n select,\n type SelectMatch,\n type StrictnessEdge,\n type CandidateProvenance,\n} from \"./select.js\";\nexport { applyMigrations, SCHEMA_VERSION, type MigrationsDb } from \"./schema.js\";\n\n// @decision DEC-EMBED-MODEL-MIGRATION-001\n// title: rebuildRegistry public surface exported from @yakcc/registry\n// status: accepted (issue #338, WI-EMBED-MODEL-MIGRATION-PATH)\n// rationale: Lifted from the private test helper at discovery-eval-full-corpus.test.ts:375\n// (DEC-V3-DISCOVERY-EVAL-FIX-001 H4). CLI consumers (registry-rebuild.ts) and\n// programmatic callers (openRegistry autoRebuild path) import from this public surface.\n// Exported alongside RegistryOptions so the type-export discipline established by\n// openRegistry is preserved: callers import everything from @yakcc/registry.\nexport {\n rebuildRegistry,\n type RebuildRegistryOptions,\n type RebuildResult,\n} from \"./rebuild.js\";\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CONTINUOUS-SHAVE-022: Constants governing the intent extraction\n// pipeline: model identifier, prompt version, and schema version. Changing any\n// of these values forces a cache miss for all existing entries, which is the\n// correct behavior when the extraction contract changes.\n// Status: decided (MASTER_PLAN.md DEC-CONTINUOUS-SHAVE-022)\n// Rationale: Centralizing these values in a single module prevents drift between\n// the extractor, cache key derivation, and validator. All three must agree on\n// the active schema version.\n\n/**\n * Default Anthropic model used for intent extraction.\n * Override via ExtractIntentContext.model or ShaveOptions.model.\n */\nexport const DEFAULT_MODEL = \"claude-haiku-4-5-20251001\";\n\n/**\n * Monotonic version tag for the system prompt template.\n * Bump this whenever the prompt text changes to invalidate cached results\n * that were produced by the old prompt.\n */\nexport const INTENT_PROMPT_VERSION = \"1\";\n\n/**\n * Schema version discriminant for the IntentCard shape.\n * Must match the `schemaVersion` field written into every IntentCard.\n * Bump together with the IntentCard type definition in intent/types.ts.\n */\nexport const INTENT_SCHEMA_VERSION = 1 as const;\n\n/**\n * @decision DEC-INTENT-STATIC-CACHE-001\n * @title Static extractor model/prompt version tags for cache-key isolation\n * @status accepted\n * @rationale\n * The existing keyFromIntentInputs() BLAKE3 derivation (cache/key.ts) mixes\n * modelTag and promptVersion into the composite cache key. By assigning\n * \"static-ts@1\" and \"static-jsdoc@1\" — values that no Anthropic model\n * identifier can equal — static and LLM cards land in permanently disjoint\n * cache namespaces with no additional registry or discriminant field.\n *\n * Versioning rule:\n * - Bump STATIC_MODEL_TAG (@1->@2) when AST-extraction logic changes output\n * shape for the same source (picker rules, type-hint format). This\n * invalidates only the static cache namespace; LLM entries are untouched.\n * - Bump STATIC_PROMPT_VERSION (@1->@2) when JSDoc parsing changes (new tag,\n * prefix string, body normalization). Same isolation guarantee applies.\n * Both bumps are backward-compatible from the LLM path's perspective.\n */\nexport const STATIC_MODEL_TAG = \"static-ts@1\";\n\n/**\n * Monotonic version tag for the static JSDoc-parsing logic.\n * See DEC-INTENT-STATIC-CACHE-001 for versioning rules.\n */\nexport const STATIC_PROMPT_VERSION = \"static-jsdoc@1\";\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CONTINUOUS-SHAVE-022: AnthropicLikeClient is a minimal interface\n// extracted from the SDK's messages.create shape. This indirection allows tests\n// to inject a synchronous mock without importing the SDK, and allows the lazy\n// import inside createDefaultAnthropicClient to remain the only SDK entry point.\n// Status: decided (MASTER_PLAN.md DEC-CONTINUOUS-SHAVE-022)\n// Rationale: Lazily importing the SDK keeps `import @yakcc/shave` lightweight\n// for callers that never need live extraction (offline/cached workflows). The\n// interface boundary makes this testable without network access.\n\n/**\n * A single text content block from the Anthropic messages API.\n * Only the text variant is expected from extraction responses.\n */\nexport interface AnthropicTextBlock {\n readonly type: \"text\";\n readonly text: string;\n}\n\n/**\n * The subset of an Anthropic message response used by extractIntent.\n */\nexport interface AnthropicMessageResponse {\n readonly content: ReadonlyArray<AnthropicTextBlock | { readonly type: string }>;\n}\n\n/**\n * Parameters accepted by the messages.create call used for intent extraction.\n */\nexport interface AnthropicCreateParams {\n readonly model: string;\n readonly system: string;\n readonly messages: ReadonlyArray<{\n readonly role: \"user\" | \"assistant\";\n readonly content: string;\n }>;\n readonly max_tokens: number;\n}\n\n/**\n * Minimal client interface that extractIntent depends on.\n *\n * The real Anthropic SDK satisfies this shape. Tests may inject any object\n * implementing this interface to avoid network calls.\n */\nexport interface AnthropicLikeClient {\n create(params: AnthropicCreateParams): Promise<AnthropicMessageResponse>;\n}\n\n/**\n * Construct the default AnthropicLikeClient using the official SDK.\n *\n * The SDK is imported lazily inside this function body so that simply\n * importing @yakcc/shave does not pull the SDK into the caller's bundle.\n * This function should only be called when a live API call is required.\n *\n * @param apiKey - The Anthropic API key (ANTHROPIC_API_KEY env var value).\n */\nexport async function createDefaultAnthropicClient(apiKey: string): Promise<AnthropicLikeClient> {\n // Lazy import: the SDK is not required unless live extraction is requested.\n const { default: Anthropic } = await import(\"@anthropic-ai/sdk\");\n const client = new Anthropic({ apiKey });\n return {\n create(params: AnthropicCreateParams): Promise<AnthropicMessageResponse> {\n return client.messages.create({\n model: params.model,\n system: params.system,\n messages: params.messages.map((m) => ({ role: m.role, content: m.content })),\n max_tokens: params.max_tokens,\n }) as Promise<AnthropicMessageResponse>;\n },\n };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CONTINUOUS-SHAVE-022: The system prompt is a versioned constant,\n// not a template function, because the only dynamic input is the unit source\n// (sent as the user message). Keeping the prompt static lets us version it with\n// INTENT_PROMPT_VERSION and invalidate cache entries whenever it changes.\n// Status: decided (MASTER_PLAN.md DEC-CONTINUOUS-SHAVE-022)\n// Rationale: A static prompt is easier to diff, review, and version-control\n// than a template. Dynamic fields (source text) travel via the user turn.\n\n/**\n * System prompt sent to the Anthropic model for intent extraction.\n *\n * The model is instructed to analyze the provided source code and return a\n * single JSON object between <json>...</json> fences. The schema matches the\n * IntentCard shape (minus the envelope fields filled by extractIntent itself:\n * modelVersion, promptVersion, sourceHash, extractedAt).\n *\n * IMPORTANT: The prompt text is part of the cache key contract via\n * INTENT_PROMPT_VERSION. Changing this string requires bumping that constant.\n */\nexport const SYSTEM_PROMPT = `You are a code-intent extractor. Your job is to analyze a TypeScript or JavaScript code unit and produce a structured behavioral description.\n\nReturn ONLY a single JSON object between <json> and </json> fences. No prose outside the fences. No markdown code blocks. No explanation.\n\nThe JSON object must have exactly these fields (no others):\n\n{\n \"schemaVersion\": 1,\n \"behavior\": \"<one-line summary, ≤200 characters, no newlines>\",\n \"inputs\": [\n { \"name\": \"<param name>\", \"typeHint\": \"<TypeScript type or 'unknown'>\", \"description\": \"<what this input represents>\" }\n ],\n \"outputs\": [\n { \"name\": \"<output name or 'return'>\", \"typeHint\": \"<TypeScript type or 'unknown'>\", \"description\": \"<what this output represents>\" }\n ],\n \"preconditions\": [\"<condition that must hold before the function is called>\"],\n \"postconditions\": [\"<condition guaranteed to hold after successful return>\"],\n \"notes\": [\"<any notable implementation detail, edge case, or constraint>\"]\n}\n\nRules:\n- \"behavior\" must be a single line, at most 200 characters, describing what the code does.\n- Arrays may be empty ([]) if there are no inputs/outputs/preconditions/postconditions/notes.\n- Each item in \"inputs\" and \"outputs\" must have exactly: \"name\", \"typeHint\", \"description\" — no other keys.\n- Each item in \"preconditions\", \"postconditions\", \"notes\" must be a plain string.\n- Do not include \"modelVersion\", \"promptVersion\", \"sourceHash\", or \"extractedAt\" — those are added by the system.\n- \"schemaVersion\" must be the number 1.\n\nExample output:\n<json>\n{\n \"schemaVersion\": 1,\n \"behavior\": \"Parses a comma-separated string of integers and returns them as a sorted array.\",\n \"inputs\": [\n { \"name\": \"raw\", \"typeHint\": \"string\", \"description\": \"Comma-separated integer tokens\" }\n ],\n \"outputs\": [\n { \"name\": \"return\", \"typeHint\": \"number[]\", \"description\": \"Sorted array of parsed integers\" }\n ],\n \"preconditions\": [\"raw contains only decimal integer tokens separated by commas\"],\n \"postconditions\": [\"result is sorted in ascending order\", \"result.length equals token count\"],\n \"notes\": [\"Throws on empty string or non-integer tokens\"]\n}\n</json>\n\nNow analyze the code unit provided by the user and return the JSON object.`;\n","// SPDX-License-Identifier: MIT\n// @decision DEC-NO-OWNERSHIP-011: No author, signer, owner, email, account,\n// username, organization, session, or any person-shaped identifier anywhere\n// in these types. The wire shape is a direct projection of BlockTripletRow,\n// which itself carries no ownership fields by schema design.\n// Status: decided (MASTER_PLAN.md DEC-NO-OWNERSHIP-011)\n// Rationale: Cornerstone. The registry is a public-domain commons; federation\n// mirrors content-addressed blocks, not identities.\n\n// @decision DEC-V1-WAVE-1-SCOPE-001: F1 read-only mirror only.\n// No F2 publishing, no auth, no signed manifests in v1 wave-1.\n// Status: decided (MASTER_PLAN.md DEC-V1-WAVE-1-SCOPE-001)\n\n// @decision DEC-TRIPLET-L0-ONLY-019: v0.6 ships L0 only. The wire transport\n// carries the `level` field but the receiver rejects any level other than \"L0\".\n// Status: decided (MASTER_PLAN.md DEC-TRIPLET-L0-ONLY-019)\n\nimport type { BlockMerkleRoot, SpecHash } from \"@yakcc/contracts\";\n\n// ---------------------------------------------------------------------------\n// Peer identity\n// ---------------------------------------------------------------------------\n\n/**\n * An opaque mirror-URL token identifying a remote peer.\n * v1 has no peer keypair — a peer is identified solely by its mirror URL.\n * Per FEDERATION_PROTOCOL.md §2 and DEC-V1-FEDERATION-PROTOCOL-001.\n */\nexport type RemotePeer = string;\n\n// ---------------------------------------------------------------------------\n// Wire shape returned by GET /v1/manifest\n// ---------------------------------------------------------------------------\n\n/**\n * The manifest root returned by a remote peer's GET /v1/manifest endpoint.\n * Entry point for mirror-client compatibility negotiation.\n *\n * FEDERATION_PROTOCOL.md §3 \"GET /v1/manifest\".\n * No ownership fields — DEC-NO-OWNERSHIP-011.\n */\nexport interface RemoteManifest {\n /** Always \"v1\" in v1 wave-1. */\n readonly protocolVersion: string;\n /**\n * Registry schema version. Must match the local SCHEMA_VERSION\n * (imported from @yakcc/registry). Mismatch → VersionMismatchError.\n */\n readonly schemaVersion: number;\n /** Total blocks served by this peer. */\n readonly blockCount: number;\n /**\n * BLAKE3-256(sorted_concat(every_BlockMerkleRoot_served)) as hex.\n * Advisory short-circuit: unchanged digest means no new blocks.\n */\n readonly rootsDigest: string;\n /** Always \"blake3-256\" in v1. */\n readonly rootsDigestAlgorithm: string;\n /** ISO-8601 timestamp of when this manifest was generated. */\n readonly servedAt: string;\n}\n\n// ---------------------------------------------------------------------------\n// Wire shape returned by GET /v1/blocks\n// ---------------------------------------------------------------------------\n\n/**\n * A page of the remote peer's block catalog.\n * FEDERATION_PROTOCOL.md §3 \"GET /v1/blocks\".\n */\nexport interface CatalogPage {\n /** Block merkle roots in lexicographic sorted order. */\n readonly blocks: readonly BlockMerkleRoot[];\n /**\n * Cursor for the next page. Pass as `after=` on the next request.\n * null when the catalog is exhausted.\n */\n readonly nextCursor: BlockMerkleRoot | null;\n}\n\n// ---------------------------------------------------------------------------\n// Mirror operation report\n// ---------------------------------------------------------------------------\n\n/**\n * Reason a block was rejected during a mirror operation.\n * Each reason maps to a distinct failure mode in FEDERATION_PROTOCOL.md §10.\n */\nexport type MirrorRejectionReason =\n | \"integrity_failed\" // blockMerkleRoot or specHash recomputation mismatch\n | \"version_mismatch\" // level or schemaVersion incompatibility\n | \"manifest_invalid\" // proofManifestJson failed validateProofManifestL0\n | \"level_unsupported\" // level !== \"L0\" (v1 wave-1 only accepts L0)\n | \"transport_error\"; // network / HTTP error during fetch\n\n/**\n * A single block rejection entry in a MirrorReport.\n */\nexport interface MirrorRejection {\n readonly merkleRoot: BlockMerkleRoot;\n readonly reason: MirrorRejectionReason;\n}\n\n/**\n * Summary of a completed mirror operation.\n * Returned by mirrorRegistry() on both success and partial failure.\n *\n * FEDERATION_PROTOCOL.md §5 \"Types\".\n * No ownership fields — DEC-NO-OWNERSHIP-011.\n *\n * @decision DEC-MIRROR-REPORT-020: MirrorReport shape for Slice D v2.\n * Status: decided (WI-020 Dispatch D)\n * Rationale: The v2 mirror walks by spec→block hierarchy (listSpecs / listBlocks)\n * rather than a flat catalog page walk, so the report shape tracks specsWalked,\n * blocksConsidered, blocksInserted, and blocksSkipped to reflect idempotency.\n * Failures carry per-failure ISO-8601 timestamps for observability.\n */\nexport interface MirrorReport {\n /** The remote serve URL that was mirrored from. */\n readonly serveUrl: string;\n /** Schema version reported by the remote peer. */\n readonly schemaVersion: number;\n /** ISO-8601 timestamp when the mirror operation began. */\n readonly startedAt: string;\n /** ISO-8601 timestamp when the mirror operation completed. */\n readonly finishedAt: string;\n /** Number of distinct spec hashes walked on the remote. */\n readonly specsWalked: number;\n /** Total number of blocks examined across all specs. */\n readonly blocksConsidered: number;\n /** Number of blocks successfully fetched and inserted into the local registry. */\n readonly blocksInserted: number;\n /** Number of blocks already present in the local registry (idempotency skips). */\n readonly blocksSkipped: number;\n /**\n * Blocks that failed integrity checks or could not be fetched.\n * Mirror failures are loud, partial, and recoverable — FEDERATION_PROTOCOL.md §10.\n * Failures include per-failure ISO-8601 timestamps for observability.\n */\n readonly failures: ReadonlyArray<{\n readonly specHash: string;\n /** null when the block root could not be decoded from the wire. */\n readonly blockMerkleRoot: string | null;\n readonly reason: string;\n /** ISO-8601 timestamp of when this failure was recorded. */\n readonly at: string;\n }>;\n}\n\n// ---------------------------------------------------------------------------\n// Transport interface\n// ---------------------------------------------------------------------------\n\n/**\n * The byte-fetch abstraction between the mirror logic and the network.\n *\n * v1 wave-1 ships one concrete implementation: createHttpTransport().\n * The interface is abstract enough to slot in libp2p/IPFS transports in v2\n * without rewriting the merge logic — FEDERATION_PROTOCOL.md §3 \"Why HTTP+JSON\".\n *\n * DEC-V1-FEDERATION-PROTOCOL-001: Transport choice is HTTP+JSON in v1;\n * the interface is the seam for future non-HTTP transports.\n *\n * @decision DEC-TRANSPORT-SCHEMA-VERSION-020: getSchemaVersion() is the first call\n * mirrorRegistry makes before inserting anything. If the remote schema version\n * exceeds the local SCHEMA_VERSION, mirrorRegistry throws SchemaVersionMismatchError\n * before touching the local registry. This is a hard abort, not a per-block failure.\n * Status: decided (WI-020 Dispatch D)\n *\n * @decision DEC-TRANSPORT-LIST-METHODS-020: listSpecs/listBlocks replace the flat\n * catalog-page walk used in the v1 mirror. The spec-then-blocks hierarchy maps more\n * cleanly onto the registry's own selectBlocks(specHash) authority and enables\n * per-spec idempotency checks. listBlocks is semantically identical to fetchSpec\n * but its name communicates intent at the call site inside mirrorRegistry.\n * Status: decided (WI-020 Dispatch D)\n */\nexport interface Transport {\n /**\n * Fetch the manifest from a remote peer.\n * Maps to GET /v1/manifest.\n */\n fetchManifest(remote: RemotePeer): Promise<RemoteManifest>;\n\n /**\n * Fetch one page of the remote peer's block catalog.\n * Maps to GET /v1/blocks?limit=<limit>&after=<after>.\n * after=null fetches the first page.\n */\n fetchCatalogPage(\n remote: RemotePeer,\n after: BlockMerkleRoot | null,\n limit: number,\n ): Promise<CatalogPage>;\n\n /**\n * Fetch a single block triplet by its merkle root.\n * Maps to GET /v1/block/<root>.\n * Returns the raw WireBlockTriplet (deserialization/integrity-check\n * is the caller's responsibility via deserializeWireBlockTriplet).\n */\n fetchBlock(remote: RemotePeer, root: BlockMerkleRoot): Promise<WireBlockTriplet>;\n\n /**\n * Fetch all block merkle roots the remote serves for a given spec hash.\n * Maps to GET /v1/spec/<specHash>.\n * Returns [] when the remote returns 404 (no blocks for that spec).\n */\n fetchSpec(remote: RemotePeer, specHash: SpecHash): Promise<readonly BlockMerkleRoot[]>;\n\n /**\n * Fetch the registry schema version from the remote peer.\n * Maps to GET /schema-version (via schemaVersionUrl() builder in transport.ts).\n *\n * mirrorRegistry calls this first, before inserting anything. A schema version\n * greater than the local SCHEMA_VERSION causes a SchemaVersionMismatchError abort.\n *\n * DEC-TRANSPORT-SCHEMA-VERSION-020.\n */\n getSchemaVersion(remote: RemotePeer): Promise<{ readonly schemaVersion: number }>;\n\n /**\n * List all distinct spec hashes served by the remote peer.\n * Maps to GET /v1/specs.\n * Returns an empty array when the remote serves no specs.\n *\n * DEC-TRANSPORT-LIST-METHODS-020.\n */\n listSpecs(remote: RemotePeer): Promise<readonly SpecHash[]>;\n\n /**\n * List all block merkle roots the remote serves for a given spec hash.\n * Maps to GET /v1/spec/<specHash> (same endpoint as fetchSpec).\n * Returns [] when the remote has no blocks for that spec.\n *\n * DEC-TRANSPORT-LIST-METHODS-020.\n */\n listBlocks(remote: RemotePeer, specHash: SpecHash): Promise<readonly BlockMerkleRoot[]>;\n}\n\n// ---------------------------------------------------------------------------\n// Wire block triplet (JSON projection of BlockTripletRow with base64 bytes)\n// ---------------------------------------------------------------------------\n\n/**\n * The v1 wire shape for a block triplet.\n * A direct JSON projection of BlockTripletRow with binary fields base64-encoded.\n * FEDERATION_PROTOCOL.md §4.\n *\n * @decision DEC-V1-FEDERATION-WIRE-ARTIFACTS-002: artifactBytes is REQUIRED.\n * The wire integrity check calls @yakcc/contracts blockMerkleRoot() directly,\n * which folds artifact bytes into the proof root. The receiver decodes\n * artifactBytes, reconstructs Map<string, Uint8Array> in manifest declaration\n * order, and passes it to blockMerkleRoot(). Any wire that omits artifactBytes\n * will produce a different hash than the persisted BlockMerkleRoot and fail\n * the integrity gate.\n * Status: decided (MASTER_PLAN.md DEC-V1-FEDERATION-WIRE-ARTIFACTS-002)\n *\n * No ownership fields — DEC-NO-OWNERSHIP-011. The wire shape is derived from\n * BlockTripletRow which itself has no ownership columns by schema design.\n * Keys in artifactBytes are manifest-declared paths; they carry no author data.\n */\nexport interface WireBlockTriplet {\n readonly blockMerkleRoot: string; // hex(BlockMerkleRoot)\n readonly specHash: string; // hex(SpecHash)\n readonly specCanonicalBytes: string; // base64(Uint8Array)\n readonly implSource: string; // UTF-8 source text\n readonly proofManifestJson: string; // JSON text (already a string in the row)\n /**\n * Artifact bytes in manifest declaration order.\n * Keys are the artifact paths declared in proofManifestJson.artifacts[*].path.\n * Values are base64-encoded artifact bytes (one entry per declared artifact).\n *\n * Required for integrity: the receiver must reconstruct artifacts as\n * Map<string, Uint8Array> and pass to blockMerkleRoot() to verify the root.\n * DEC-V1-FEDERATION-WIRE-ARTIFACTS-002.\n */\n readonly artifactBytes: Record<string, string>; // { [path]: base64(bytes) }\n readonly level: \"L0\" | \"L1\" | \"L2\" | \"L3\";\n readonly createdAt: number; // epoch ms (peer-local; informational)\n readonly canonicalAstHash: string; // hex(CanonicalAstHash)\n readonly parentBlockRoot: string | null; // hex(BlockMerkleRoot) | null\n}\n\n// ---------------------------------------------------------------------------\n// Error classes\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when a received WireBlockTriplet fails an integrity check.\n * Covers: blockMerkleRoot recomputation mismatch, specHash mismatch,\n * level=L1/L2/L3 rejection, proofManifestJson validation failure.\n *\n * FEDERATION_PROTOCOL.md §8 \"Within the same BlockMerkleRoot\".\n */\nexport class IntegrityError extends Error {\n readonly reason: MirrorRejectionReason;\n\n constructor(opts: { reason: MirrorRejectionReason; message?: string }) {\n super(opts.message ?? `Integrity check failed: ${opts.reason}`);\n this.name = \"IntegrityError\";\n this.reason = opts.reason;\n }\n}\n\n/**\n * Thrown when the remote peer's protocolVersion or schemaVersion is\n * incompatible with the local registry's expected versions.\n *\n * FEDERATION_PROTOCOL.md §3 \"GET /v1/manifest\" — schemaVersion mismatch is\n * a hard error; the client refuses to mirror.\n */\nexport class VersionMismatchError extends Error {\n constructor(message?: string) {\n super(message ?? \"Protocol or schema version mismatch with remote peer\");\n this.name = \"VersionMismatchError\";\n }\n}\n\n/**\n * Thrown when the remote peer's schemaVersion is greater than the local\n * registry's SCHEMA_VERSION. mirrorRegistry aborts before inserting anything.\n *\n * Distinct from VersionMismatchError to allow callers to differentiate a\n * schema-forward incompatibility (remote is newer) from a protocol mismatch.\n *\n * DEC-TRANSPORT-SCHEMA-VERSION-020, WI-020 Dispatch D.\n */\nexport class SchemaVersionMismatchError extends Error {\n /** The schema version reported by the remote peer. */\n readonly remoteSchemaVersion: number;\n /** The local SCHEMA_VERSION that the client supports. */\n readonly localSchemaVersion: number;\n\n constructor(opts: { remoteSchemaVersion: number; localSchemaVersion: number; message?: string }) {\n super(\n opts.message ??\n `Remote schema version ${opts.remoteSchemaVersion} exceeds local SCHEMA_VERSION ${opts.localSchemaVersion}; mirror aborted`,\n );\n this.name = \"SchemaVersionMismatchError\";\n this.remoteSchemaVersion = opts.remoteSchemaVersion;\n this.localSchemaVersion = opts.localSchemaVersion;\n }\n}\n\n/**\n * Thrown when an HTTP (or other transport) operation fails.\n * Carries the wire error code from the §3 error envelope when available.\n *\n * FEDERATION_PROTOCOL.md §3 \"Errors\".\n */\nexport class TransportError extends Error {\n /** Wire error code from { \"error\": \"<code>\", \"message\": \"...\" } envelope, or \"internal_error\". */\n readonly code: string;\n\n constructor(opts: { code: string; message?: string }) {\n super(opts.message ?? `Transport error: ${opts.code}`);\n this.name = \"TransportError\";\n this.code = opts.code;\n }\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-WIRE-FORMAT-020: Wire serialization/deserialization for BlockTripletRow.\n// Status: decided (WI-020 Dispatch B, FEDERATION_PROTOCOL.md §4)\n// Title: Wire format — serialize/deserialize + integrity gate\n// Rationale:\n// The v1 wire shape is a direct JSON projection of BlockTripletRow with binary\n// fields base64-encoded (FEDERATION_PROTOCOL.md §4). Deserialization performs ALL\n// integrity checks before returning — no \"best-effort\" deserialization.\n// Integrity checks:\n// 1. Structural shape validation\n// 2. base64 decode of specCanonicalBytes → Uint8Array\n// 3. base64 decode of each artifactBytes entry → Map<string, Uint8Array>\n// 4. level === 'L0' enforced (DEC-TRIPLET-L0-ONLY-019)\n// 5. validateProofManifestL0(JSON.parse(proofManifestJson)) (DEC-TRIPLET-L0-ONLY-019)\n// 6. artifactBytes key set matches manifest artifact paths (no extras, no missing)\n// 7. Recompute specHash(specCanonicalBytes) → compare (FEDERATION_PROTOCOL.md §4)\n// 8. Recompute blockMerkleRoot({spec, implSource, manifest, artifacts}) via\n// @yakcc/contracts → compare (FEDERATION_PROTOCOL.md §4)\n// This is the v2 fix: artifact bytes fold into the proof root.\n//\n// @decision DEC-V1-FEDERATION-WIRE-ARTIFACTS-002: artifactBytes is REQUIRED on the wire.\n// The integrity check calls @yakcc/contracts blockMerkleRoot() directly — NO local\n// merkle helper inside @yakcc/federation. The receiver decodes artifactBytes,\n// reconstructs Map<string, Uint8Array> in manifest declaration order, and passes it\n// to blockMerkleRoot(). Any wire that omits artifactBytes will produce a different\n// hash than the persisted BlockMerkleRoot and fail the integrity gate.\n// Status: decided (MASTER_PLAN.md DEC-V1-FEDERATION-WIRE-ARTIFACTS-002)\n//\n// @decision DEC-NO-OWNERSHIP-011: No ownership fields on the wire.\n// Status: decided (MASTER_PLAN.md DEC-NO-OWNERSHIP-011)\n// Rationale: The wire shape is derived from BlockTripletRow which has no ownership\n// columns by schema design. Test enumerates wire keys and asserts disjoint from\n// ownership field set.\n//\n// @decision DEC-TRIPLET-L0-ONLY-019: level === 'L0' enforced at deserialization.\n// Status: decided (MASTER_PLAN.md DEC-TRIPLET-L0-ONLY-019)\n// Rationale: v1 wave-1 ships L0 only. Any L1/L2/L3 block is rejected with\n// IntegrityError({ reason: 'level_unsupported' }).\n//\n// @decision DEC-CONTRACTS-AUTHORITY-001: @yakcc/contracts is the single authority for\n// the block identity formula. All callers — registry, federation wire, tests — must\n// call blockMerkleRoot() from @yakcc/contracts rather than re-implementing the formula.\n// Status: decided (MASTER_PLAN.md DEC-CONTRACTS-AUTHORITY-001)\n\nimport { blockMerkleRoot, specHash, validateProofManifestL0 } from \"@yakcc/contracts\";\nimport type {\n BlockMerkleRoot,\n CanonicalAstHash,\n ProofManifest,\n SpecHash,\n SpecYak,\n} from \"@yakcc/contracts\";\nimport type { BlockTripletRow } from \"@yakcc/registry\";\nimport { IntegrityError } from \"./types.js\";\nimport type { WireBlockTriplet } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Serialization\n// ---------------------------------------------------------------------------\n\n/**\n * Serialize a BlockTripletRow to the v1 wire shape (WireBlockTriplet).\n *\n * Pure projection:\n * - base64-encodes specCanonicalBytes\n * - base64-encodes each entry in the artifacts Map → artifactBytes Record\n * (keys are manifest-declared paths; values are base64(bytes))\n * - all other fields pass through verbatim\n * - parentBlockRoot null/undefined → null\n *\n * Does NOT recompute or validate blockMerkleRoot — the row is taken as trusted\n * local state. Integrity verification happens at deserialization (the receiver's\n * responsibility per FEDERATION_PROTOCOL.md §4).\n *\n * Artifact key order: follows Map iteration order, which matches the order\n * entries were inserted into the Map. Callers building rows from\n * blockMerkleRoot() ensure Map keys match manifest declaration order.\n *\n * No ownership fields in the output — DEC-NO-OWNERSHIP-011.\n */\nexport function serializeWireBlockTriplet(row: BlockTripletRow): WireBlockTriplet {\n // Encode each artifact: Map<string, Uint8Array> → Record<string, string (base64)>\n const artifactBytes: Record<string, string> = {};\n for (const [path, bytes] of row.artifacts) {\n artifactBytes[path] = Buffer.from(bytes).toString(\"base64\");\n }\n\n return {\n blockMerkleRoot: row.blockMerkleRoot,\n specHash: row.specHash,\n specCanonicalBytes: Buffer.from(row.specCanonicalBytes).toString(\"base64\"),\n implSource: row.implSource,\n proofManifestJson: row.proofManifestJson,\n artifactBytes,\n level: row.level,\n createdAt: row.createdAt,\n canonicalAstHash: row.canonicalAstHash,\n parentBlockRoot: row.parentBlockRoot ?? null,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Deserialization + integrity gate\n// ---------------------------------------------------------------------------\n\n/**\n * Deserialize and integrity-check a WireBlockTriplet, returning a BlockTripletRow.\n *\n * ALL integrity checks run before returning. A partial-but-corrupt triplet is\n * never returned; callers either get a fully-validated row or an IntegrityError.\n *\n * Checks performed (in order):\n * 1. Structural shape validation — rejects non-objects, missing or wrong-typed fields.\n * artifactBytes must be a non-null plain object (Record<string, string>).\n * 2. base64 decode of specCanonicalBytes → Uint8Array.\n * 3. base64 decode of each artifactBytes value → Map<string, Uint8Array>.\n * 4. level === 'L0' — else IntegrityError({ reason: 'level_unsupported' })\n * per DEC-TRIPLET-L0-ONLY-019.\n * 5. validateProofManifestL0(JSON.parse(proofManifestJson)) — else\n * IntegrityError({ reason: 'manifest_invalid' }).\n * 6. artifactBytes key set == manifest.artifacts[*].path set (no extras, no missing)\n * — else IntegrityError({ reason: 'manifest_invalid' }) (artifact_key_mismatch sub-reason).\n * 7. Parse specCanonicalBytes as JSON → SpecYak, call specHash(parsedSpec) via\n * @yakcc/contracts and compare to wire.specHash\n * — else IntegrityError({ reason: 'integrity_failed' }).\n * 8. Recompute blockMerkleRoot({spec, implSource, manifest, artifacts}) via @yakcc/contracts\n * with the reconstructed artifacts Map (in manifest declaration order) and compare to\n * wire.blockMerkleRoot — else IntegrityError({ reason: 'integrity_failed' }).\n * DEC-V1-FEDERATION-WIRE-ARTIFACTS-002, DEC-CONTRACTS-AUTHORITY-001.\n *\n * Reconstructed artifacts Map: keys inserted in manifest declaration order (the order\n * that blockMerkleRoot() iterates to build proof_root).\n *\n * The returned BlockTripletRow uses Date.now() as createdAt only if the wire's\n * createdAt is <= 0 (the sentinel value). Otherwise the wire's createdAt is\n * preserved for round-trip fidelity.\n *\n * @throws IntegrityError for any integrity or validation failure.\n * @throws TypeError for structural shape violations.\n */\nexport function deserializeWireBlockTriplet(value: unknown): BlockTripletRow {\n // ---------------------------------------------------------------------------\n // Step 1: Structural shape validation\n // ---------------------------------------------------------------------------\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new TypeError(\"deserializeWireBlockTriplet: expected a non-null JSON object\");\n }\n\n const w = value as Record<string, unknown>;\n\n // Required non-empty string fields\n const stringFields = [\n \"blockMerkleRoot\",\n \"specHash\",\n \"specCanonicalBytes\",\n \"implSource\",\n \"proofManifestJson\",\n \"level\",\n \"canonicalAstHash\",\n ] as const;\n\n for (const field of stringFields) {\n if (typeof w[field] !== \"string\" || (w[field] as string).length === 0) {\n throw new TypeError(\n `deserializeWireBlockTriplet: field \"${field}\" must be a non-empty string; got ${typeof w[field]}`,\n );\n }\n }\n\n // createdAt: number\n if (typeof w.createdAt !== \"number\") {\n throw new TypeError(\n `deserializeWireBlockTriplet: field \"createdAt\" must be a number; got ${typeof w.createdAt}`,\n );\n }\n\n // parentBlockRoot: string | null (null is allowed; undefined is not)\n if (w.parentBlockRoot !== null && typeof w.parentBlockRoot !== \"string\") {\n throw new TypeError(\n `deserializeWireBlockTriplet: field \"parentBlockRoot\" must be a string or null; got ${typeof w.parentBlockRoot}`,\n );\n }\n\n // artifactBytes: non-null plain object (not array)\n if (\n w.artifactBytes === null ||\n w.artifactBytes === undefined ||\n typeof w.artifactBytes !== \"object\" ||\n Array.isArray(w.artifactBytes)\n ) {\n throw new TypeError(\n `deserializeWireBlockTriplet: field \"artifactBytes\" must be a non-null plain object; got ${w.artifactBytes === null ? \"null\" : typeof w.artifactBytes}`,\n );\n }\n\n const wireBlockMerkleRoot = w.blockMerkleRoot as string;\n const wireSpecHash = w.specHash as string;\n const wireSpecCanonicalBytesB64 = w.specCanonicalBytes as string;\n const wireImplSource = w.implSource as string;\n const wireProofManifestJson = w.proofManifestJson as string;\n const wireLevel = w.level as string;\n const wireCreatedAt = w.createdAt as number;\n const wireCanonicalAstHash = w.canonicalAstHash as string;\n const wireParentBlockRoot = w.parentBlockRoot as string | null;\n const wireArtifactBytes = w.artifactBytes as Record<string, unknown>;\n\n // Validate level is a known value before the L0 check (better error for unknown vs. unsupported).\n const KNOWN_LEVELS = new Set([\"L0\", \"L1\", \"L2\", \"L3\"]);\n if (!KNOWN_LEVELS.has(wireLevel)) {\n throw new IntegrityError({\n reason: \"level_unsupported\",\n message: `deserializeWireBlockTriplet: unknown level \"${wireLevel}\"; expected one of \"L0\",\"L1\",\"L2\",\"L3\"`,\n });\n }\n\n // Validate each artifactBytes value is a non-empty string (valid base64)\n for (const [path, b64val] of Object.entries(wireArtifactBytes)) {\n if (typeof b64val !== \"string\") {\n throw new TypeError(\n `deserializeWireBlockTriplet: artifactBytes[\"${path}\"] must be a base64 string; got ${typeof b64val}`,\n );\n }\n }\n\n // ---------------------------------------------------------------------------\n // Step 2: base64 decode of specCanonicalBytes\n // ---------------------------------------------------------------------------\n let specCanonicalBytes: Uint8Array;\n try {\n const buf = Buffer.from(wireSpecCanonicalBytesB64, \"base64\");\n if (buf.length === 0) {\n throw new RangeError(\"decoded to zero bytes\");\n }\n specCanonicalBytes = new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);\n } catch (err) {\n throw new TypeError(\n `deserializeWireBlockTriplet: specCanonicalBytes is not valid base64: ${String(err)}`,\n );\n }\n\n // ---------------------------------------------------------------------------\n // Step 3: base64 decode of each artifactBytes value\n // ---------------------------------------------------------------------------\n // Decoded into a staging Map keyed by path — order of manifest declaration\n // is not yet enforced here; that comes in step 6 after manifest parsing.\n const artifactBytesDecoded = new Map<string, Uint8Array>();\n for (const [path, b64val] of Object.entries(wireArtifactBytes)) {\n try {\n const buf = Buffer.from(b64val as string, \"base64\");\n artifactBytesDecoded.set(path, new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));\n } catch (err) {\n throw new TypeError(\n `deserializeWireBlockTriplet: artifactBytes[\"${path}\"] is not valid base64: ${String(err)}`,\n );\n }\n }\n\n // ---------------------------------------------------------------------------\n // Step 4: level === 'L0' check (DEC-TRIPLET-L0-ONLY-019)\n // ---------------------------------------------------------------------------\n if (wireLevel !== \"L0\") {\n throw new IntegrityError({\n reason: \"level_unsupported\",\n message: `deserializeWireBlockTriplet: level \"${wireLevel}\" is not supported in v1 wave-1 (only \"L0\" is accepted, per DEC-TRIPLET-L0-ONLY-019)`,\n });\n }\n\n // ---------------------------------------------------------------------------\n // Step 5: validateProofManifestL0\n // ---------------------------------------------------------------------------\n let proofManifest: ProofManifest;\n try {\n const parsed = JSON.parse(wireProofManifestJson) as unknown;\n proofManifest = validateProofManifestL0(parsed);\n } catch (err) {\n // Re-throw IntegrityError as-is (from validateProofManifestL0 wrapped below),\n // but JSON.parse SyntaxError → IntegrityError(manifest_invalid).\n if (err instanceof SyntaxError) {\n throw new IntegrityError({\n reason: \"manifest_invalid\",\n message: `deserializeWireBlockTriplet: proofManifestJson is not valid JSON: ${String(err)}`,\n });\n }\n throw new IntegrityError({\n reason: \"manifest_invalid\",\n message: `deserializeWireBlockTriplet: proofManifestJson failed L0 validation: ${String(err)}`,\n });\n }\n\n // ---------------------------------------------------------------------------\n // Step 6: artifactBytes key set == manifest artifact paths\n // ---------------------------------------------------------------------------\n // The manifest declares the artifact paths in declaration order. The wire's\n // artifactBytes must contain exactly those keys — no extras, no missing.\n const manifestPaths = proofManifest.artifacts.map((a) => a.path);\n const wireArtifactPaths = new Set(Object.keys(wireArtifactBytes));\n const manifestPathSet = new Set(manifestPaths);\n\n const missingFromWire = manifestPaths.filter((p) => !wireArtifactPaths.has(p));\n const extraInWire = [...wireArtifactPaths].filter((p) => !manifestPathSet.has(p));\n\n if (missingFromWire.length > 0 || extraInWire.length > 0) {\n throw new IntegrityError({\n reason: \"manifest_invalid\",\n message: `deserializeWireBlockTriplet: artifactBytes key mismatch — missing: [${missingFromWire.join(\", \")}], extra: [${extraInWire.join(\", \")}]`,\n });\n }\n\n // Reconstruct artifacts Map in manifest declaration order (required by blockMerkleRoot()).\n // The key set equality check above guarantees every manifestPaths entry is present in\n // artifactBytesDecoded; the fallback to new Uint8Array(0) is unreachable dead code that\n // satisfies the type checker without a forbidden non-null assertion.\n const artifacts = new Map<string, Uint8Array>();\n for (const path of manifestPaths) {\n artifacts.set(path, artifactBytesDecoded.get(path) ?? new Uint8Array(0));\n }\n\n // ---------------------------------------------------------------------------\n // Steps 7 + 8: Parse specCanonicalBytes once, then recompute both specHash\n // and blockMerkleRoot via @yakcc/contracts and compare.\n //\n // DEC-CONTRACTS-AUTHORITY-001: @yakcc/contracts is the single authority for\n // the block identity formula. NO direct blake3 calls in wire.ts.\n //\n // DEC-V1-FEDERATION-WIRE-ARTIFACTS-002: blockMerkleRoot() is called with the\n // reconstructed artifacts Map so artifact bytes fold into proof_root. Any\n // single-byte mutation in an artifact will produce a different root.\n //\n // The `spec` parameter expected by specHash() and blockMerkleRoot() is SpecYak.\n // We have pre-canonicalized bytes on the wire, so we parse them as JSON to\n // recover the SpecYak object. blockMerkleRoot() internally calls canonicalize(spec)\n // again — the round-trip invariant guarantees the result matches the original\n // bytes when they were produced by canonicalize(spec.yak).\n // ---------------------------------------------------------------------------\n let parsedSpec: SpecYak;\n let computedBlockMerkleRoot: BlockMerkleRoot;\n try {\n const specJsonText = new TextDecoder().decode(specCanonicalBytes);\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n parsedSpec = JSON.parse(specJsonText) as unknown as SpecYak;\n } catch (err) {\n throw new IntegrityError({\n reason: \"integrity_failed\",\n message: `deserializeWireBlockTriplet: specCanonicalBytes is not valid JSON: ${String(err)}`,\n });\n }\n\n // Step 7: recompute specHash via @yakcc/contracts and compare.\n const computedSpecHash = specHash(parsedSpec);\n if (computedSpecHash !== wireSpecHash) {\n throw new IntegrityError({\n reason: \"integrity_failed\",\n message: `deserializeWireBlockTriplet: specHash mismatch — wire has \"${wireSpecHash}\", computed \"${computedSpecHash}\"`,\n });\n }\n\n // Step 8: recompute blockMerkleRoot via @yakcc/contracts and compare.\n try {\n computedBlockMerkleRoot = blockMerkleRoot({\n spec: parsedSpec,\n implSource: wireImplSource,\n manifest: proofManifest,\n artifacts,\n });\n } catch (err) {\n // blockMerkleRoot() throws if an artifact declared in the manifest is\n // missing from the artifacts Map — should not happen after step 6, but\n // treated as integrity_failed defensively.\n if (err instanceof IntegrityError) {\n throw err;\n }\n throw new IntegrityError({\n reason: \"integrity_failed\",\n message: `deserializeWireBlockTriplet: blockMerkleRoot recomputation failed: ${String(err)}`,\n });\n }\n\n if (computedBlockMerkleRoot !== wireBlockMerkleRoot) {\n throw new IntegrityError({\n reason: \"integrity_failed\",\n message: `deserializeWireBlockTriplet: blockMerkleRoot mismatch — wire has \"${wireBlockMerkleRoot}\", computed \"${computedBlockMerkleRoot}\"`,\n });\n }\n\n // ---------------------------------------------------------------------------\n // All checks passed — return the validated BlockTripletRow\n // ---------------------------------------------------------------------------\n return {\n blockMerkleRoot: wireBlockMerkleRoot as BlockMerkleRoot,\n specHash: wireSpecHash as SpecHash,\n specCanonicalBytes,\n implSource: wireImplSource,\n proofManifestJson: wireProofManifestJson,\n level: wireLevel as \"L0\",\n createdAt: wireCreatedAt > 0 ? wireCreatedAt : Date.now(),\n canonicalAstHash: wireCanonicalAstHash as CanonicalAstHash,\n parentBlockRoot: wireParentBlockRoot as BlockMerkleRoot | null,\n artifacts,\n };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-HTTP-TRANSPORT-URL-020: Pure URL builders for federation endpoints.\n// Status: decided (WI-020 Dispatch C, FEDERATION_PROTOCOL.md §3)\n// Title: Transport URL builders — no fetch dependency, pure string construction\n// Rationale:\n// Isolating URL construction into pure functions makes them independently testable\n// and keeps http-transport.ts focused on network I/O. The builders accept a\n// RemotePeer (opaque mirror URL string) and produce the canonical endpoint URLs\n// defined in FEDERATION_PROTOCOL.md §3. Trailing slashes on the peer URL are\n// handled by normalizing once at construction time.\n//\n// @decision DEC-V1-FEDERATION-PROTOCOL-001: HTTP+JSON over HTTPS; peers identified by mirror URL.\n// Status: decided (MASTER_PLAN.md DEC-V1-FEDERATION-PROTOCOL-001)\n//\n// @decision DEC-NO-OWNERSHIP-011: No ownership fields on the wire.\n// Status: decided (MASTER_PLAN.md DEC-NO-OWNERSHIP-011)\n// Rationale: URL builders carry only content-addressed identifiers (BlockMerkleRoot,\n// SpecHash) and pagination parameters. No person-shaped identifiers appear anywhere.\n\nimport type { BlockMerkleRoot, SpecHash } from \"@yakcc/contracts\";\nimport type { RemotePeer } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize a peer URL by stripping any trailing slash.\n * All endpoint paths start with \"/v1/...\" so the join is always unambiguous.\n */\nfunction normalizeBase(remote: RemotePeer): string {\n return remote.endsWith(\"/\") ? remote.slice(0, -1) : remote;\n}\n\n// ---------------------------------------------------------------------------\n// URL builders\n// ---------------------------------------------------------------------------\n\n/**\n * Build the URL for GET /v1/manifest.\n *\n * Entry point for mirror-client compatibility negotiation.\n * FEDERATION_PROTOCOL.md §3 \"GET /v1/manifest\".\n */\nexport function manifestUrl(remote: RemotePeer): string {\n return `${normalizeBase(remote)}/v1/manifest`;\n}\n\n/**\n * Build the URL for GET /v1/blocks with pagination query params.\n *\n * @param remote - The remote peer mirror URL.\n * @param limit - Maximum number of roots per page.\n * @param after - Cursor from the previous page's nextCursor, or null for the first page.\n *\n * FEDERATION_PROTOCOL.md §3 \"GET /v1/blocks\".\n */\nexport function blocksUrl(\n remote: RemotePeer,\n limit: number,\n after: BlockMerkleRoot | null,\n): string {\n const base = `${normalizeBase(remote)}/v1/blocks?limit=${encodeURIComponent(limit)}`;\n if (after !== null) {\n return `${base}&after=${encodeURIComponent(after)}`;\n }\n return base;\n}\n\n/**\n * Build the URL for GET /v1/block/<root>.\n *\n * Returns the full triplet row keyed by BlockMerkleRoot.\n * FEDERATION_PROTOCOL.md §3 \"GET /v1/block/<merkleRoot>\".\n */\nexport function blockUrl(remote: RemotePeer, root: BlockMerkleRoot): string {\n return `${normalizeBase(remote)}/v1/block/${encodeURIComponent(root)}`;\n}\n\n/**\n * Build the URL for GET /v1/spec/<specHash>.\n *\n * Returns the list of BlockMerkleRoots the peer serves for a given SpecHash.\n * FEDERATION_PROTOCOL.md §3 \"GET /v1/spec/<specHash>\".\n */\nexport function specUrl(remote: RemotePeer, specHash: SpecHash): string {\n return `${normalizeBase(remote)}/v1/spec/${encodeURIComponent(specHash)}`;\n}\n\n/**\n * Build the URL for GET /schema-version.\n *\n * Returns `{ schemaVersion: number }` from the remote peer.\n * mirrorRegistry calls this first, before inserting anything, to abort early\n * if the remote schema version is incompatible (DEC-TRANSPORT-SCHEMA-VERSION-020).\n *\n * Note: No /v1/ prefix — this is a top-level negotiation endpoint, independent\n * of the v1 protocol version. It must be reachable even if the protocol version\n * is unknown or mismatched.\n */\nexport function schemaVersionUrl(remote: RemotePeer): string {\n return `${normalizeBase(remote)}/schema-version`;\n}\n\n/**\n * Build the URL for GET /v1/specs.\n *\n * Returns the list of all SpecHashes served by the remote peer.\n * DEC-TRANSPORT-LIST-METHODS-020.\n */\nexport function specsUrl(remote: RemotePeer): string {\n return `${normalizeBase(remote)}/v1/specs`;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-HTTP-TRANSPORT-020: HTTP+JSON transport implementation for federation.\n// Status: decided (WI-020 Dispatch C, FEDERATION_PROTOCOL.md §3)\n// Title: createHttpTransport — Node 22 global fetch, injected for tests\n// Rationale:\n// v1 uses Node 22's built-in global fetch (no undici/axios/node-fetch dep).\n// The fetch implementation is injectable via opts.fetch so test suites can\n// provide stub fetch functions without real network I/O.\n//\n// Error handling per FEDERATION_PROTOCOL.md §3 \"Errors\":\n// - Non-2xx with { \"error\": \"<code>\", \"message\": \"...\" } body\n// → TransportError({ code: error, message })\n// - Non-2xx without a parseable error envelope\n// → TransportError({ code: 'internal_error' })\n// - 404 on fetchSpec → returns [] (normal: peer has no blocks for that spec)\n//\n// @decision DEC-V1-FEDERATION-PROTOCOL-001: Transport is HTTP+JSON. The Transport\n// interface (types.ts) is the seam for future libp2p/IPFS transports. createHttpTransport\n// is the only concrete implementation shipped in v1 wave-1.\n// Status: decided (MASTER_PLAN.md DEC-V1-FEDERATION-PROTOCOL-001)\n//\n// @decision DEC-NO-OWNERSHIP-011: No ownership fields anywhere.\n// Status: decided (MASTER_PLAN.md DEC-NO-OWNERSHIP-011)\n// Rationale: No person-shaped identifier appears in request construction, response\n// parsing, or error reporting. The wire shapes consumed here are defined in types.ts\n// and carry no ownership fields by schema design.\n\nimport type { BlockMerkleRoot, SpecHash } from \"@yakcc/contracts\";\nimport {\n blockUrl,\n blocksUrl,\n manifestUrl,\n schemaVersionUrl,\n specUrl,\n specsUrl,\n} from \"./transport.js\";\nimport { TransportError } from \"./types.js\";\nimport type {\n CatalogPage,\n RemoteManifest,\n RemotePeer,\n Transport,\n WireBlockTriplet,\n} from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Error envelope parsing\n// ---------------------------------------------------------------------------\n\n/**\n * The JSON error envelope defined in FEDERATION_PROTOCOL.md §3 \"Errors\".\n * Non-2xx responses from a compliant peer include this shape.\n */\ninterface WireErrorEnvelope {\n error: string;\n message?: string;\n}\n\n/**\n * Attempt to parse a WireErrorEnvelope from an unknown value.\n * Returns the envelope if it has a string `error` field; null otherwise.\n */\nfunction parseErrorEnvelope(value: unknown): WireErrorEnvelope | null {\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n const v = value as Record<string, unknown>;\n if (typeof v.error === \"string\" && v.error.length > 0) {\n const envelope: WireErrorEnvelope = { error: v.error };\n if (typeof v.message === \"string\") {\n envelope.message = v.message;\n }\n return envelope;\n }\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Response helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read a fetch Response and return the parsed JSON body.\n * Throws TransportError for any non-2xx status.\n *\n * Per FEDERATION_PROTOCOL.md §3:\n * - Non-2xx with valid { error, message } envelope → TransportError with wire code.\n * - Non-2xx without parseable envelope → TransportError({ code: 'internal_error' }).\n */\nasync function readJsonResponse(response: Response): Promise<unknown> {\n if (response.ok) {\n return response.json() as Promise<unknown>;\n }\n\n // Non-2xx — attempt to parse error envelope.\n let body: unknown;\n try {\n body = await (response.json() as Promise<unknown>);\n } catch {\n // Body is not JSON — protocol violation; treat as internal_error.\n throw new TransportError({\n code: \"internal_error\",\n message: `HTTP ${response.status} with non-JSON body from ${response.url}`,\n });\n }\n\n const envelope = parseErrorEnvelope(body);\n if (envelope !== null) {\n throw new TransportError({\n code: envelope.error,\n message: envelope.message ?? `HTTP ${response.status}: ${envelope.error}`,\n });\n }\n\n // Non-2xx with JSON body that is not a valid error envelope — internal_error.\n throw new TransportError({\n code: \"internal_error\",\n message: `HTTP ${response.status} with unexpected body shape from ${response.url}`,\n });\n}\n\n// ---------------------------------------------------------------------------\n// createHttpTransport\n// ---------------------------------------------------------------------------\n\n/**\n * Options for createHttpTransport.\n *\n * The `fetch` option is the escape hatch for tests: inject a stub fetch function\n * to exercise all transport logic without real network I/O. Production callers\n * omit it and use the Node 22 global fetch.\n *\n * Per DEC-HTTP-TRANSPORT-020: no undici/axios/node-fetch. Node 22 global fetch only.\n */\nexport interface HttpTransportOptions {\n /** Override the global fetch (for tests). Defaults to the Node 22 global fetch. */\n fetch?: typeof fetch;\n}\n\n/**\n * Create the default HTTP+JSON transport.\n *\n * This is the v1 concrete implementation of the Transport interface. Its role\n * is to turn URL + fetch response into the typed domain objects defined in\n * types.ts. Integrity checking of WireBlockTriplet is NOT performed here —\n * that responsibility belongs to the caller (pullBlock / mirrorRegistry) via\n * deserializeWireBlockTriplet, per FEDERATION_PROTOCOL.md §4.\n *\n * @param opts - Optional overrides (primarily `fetch` for test injection).\n * @returns A Transport implementation backed by HTTP+JSON.\n *\n * FEDERATION_PROTOCOL.md §3, §5.\n */\nexport function createHttpTransport(opts?: HttpTransportOptions): Transport {\n // Capture the fetch implementation once at construction time. This ensures a\n // stable reference even if the caller modifies `opts` after construction.\n const _fetch: typeof fetch = opts?.fetch ?? globalThis.fetch;\n\n return {\n // -----------------------------------------------------------------------\n // fetchManifest — GET /v1/manifest\n // -----------------------------------------------------------------------\n\n async fetchManifest(remote: RemotePeer): Promise<RemoteManifest> {\n const url = manifestUrl(remote);\n const response = await _fetch(url);\n const body = await readJsonResponse(response);\n // Return the body as-is; structural validation happens at the caller\n // (mirrorRegistry checks protocolVersion/schemaVersion).\n return body as RemoteManifest;\n },\n\n // -----------------------------------------------------------------------\n // fetchCatalogPage — GET /v1/blocks?limit=<limit>&after=<after>\n // -----------------------------------------------------------------------\n\n async fetchCatalogPage(\n remote: RemotePeer,\n after: BlockMerkleRoot | null,\n limit: number,\n ): Promise<CatalogPage> {\n const url = blocksUrl(remote, limit, after);\n const response = await _fetch(url);\n const body = await readJsonResponse(response);\n return body as CatalogPage;\n },\n\n // -----------------------------------------------------------------------\n // fetchBlock — GET /v1/block/<root>\n // -----------------------------------------------------------------------\n\n async fetchBlock(remote: RemotePeer, root: BlockMerkleRoot): Promise<WireBlockTriplet> {\n const url = blockUrl(remote, root);\n const response = await _fetch(url);\n const body = await readJsonResponse(response);\n return body as WireBlockTriplet;\n },\n\n // -----------------------------------------------------------------------\n // fetchSpec — GET /v1/spec/<specHash>\n // -----------------------------------------------------------------------\n\n async fetchSpec(remote: RemotePeer, specHash: SpecHash): Promise<readonly BlockMerkleRoot[]> {\n const url = specUrl(remote, specHash);\n const response = await _fetch(url);\n\n // 404 is normal: the peer has no blocks for this spec (FEDERATION_PROTOCOL.md §3).\n if (response.status === 404) {\n return [];\n }\n\n const body = await readJsonResponse(response);\n // Wire shape: { specHash: string; blockMerkleRoots: string[] }\n const envelope = body as { specHash: string; blockMerkleRoots: BlockMerkleRoot[] };\n return envelope.blockMerkleRoots;\n },\n\n // -----------------------------------------------------------------------\n // getSchemaVersion — GET /schema-version\n // DEC-TRANSPORT-SCHEMA-VERSION-020: first call mirrorRegistry makes before\n // inserting anything. Aborts on mismatch via SchemaVersionMismatchError.\n // -----------------------------------------------------------------------\n\n async getSchemaVersion(remote: RemotePeer): Promise<{ readonly schemaVersion: number }> {\n const url = schemaVersionUrl(remote);\n const response = await _fetch(url);\n const body = await readJsonResponse(response);\n return body as { readonly schemaVersion: number };\n },\n\n // -----------------------------------------------------------------------\n // listSpecs — GET /v1/specs\n // DEC-TRANSPORT-LIST-METHODS-020: lists all spec hashes served by the peer.\n // -----------------------------------------------------------------------\n\n async listSpecs(remote: RemotePeer): Promise<readonly SpecHash[]> {\n const url = specsUrl(remote);\n const response = await _fetch(url);\n const body = await readJsonResponse(response);\n // Wire shape: { specHashes: string[] }\n const envelope = body as { specHashes: SpecHash[] };\n return envelope.specHashes;\n },\n\n // -----------------------------------------------------------------------\n // listBlocks — GET /v1/spec/<specHash>\n // DEC-TRANSPORT-LIST-METHODS-020: same endpoint as fetchSpec; named\n // separately to communicate intent at the mirrorRegistry call site.\n // -----------------------------------------------------------------------\n\n async listBlocks(remote: RemotePeer, specHash: SpecHash): Promise<readonly BlockMerkleRoot[]> {\n const url = specUrl(remote, specHash);\n const response = await _fetch(url);\n\n // 404 is normal: the peer has no blocks for this spec.\n if (response.status === 404) {\n return [];\n }\n\n const body = await readJsonResponse(response);\n const envelope = body as { specHash: string; blockMerkleRoots: BlockMerkleRoot[] };\n return envelope.blockMerkleRoots;\n },\n };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-PULL-020: pullBlock and pullSpec primitives for federation F1 mirror.\n// Status: decided (WI-020 Dispatch C, MASTER_PLAN.md)\n// Title: pull.ts — transport-agnostic block/spec fetch with mandatory integrity gate\n// Rationale:\n// pullBlock is the primary consumer-facing entry point for fetching a single block.\n// It MUST route every fetched WireBlockTriplet through deserializeWireBlockTriplet,\n// which performs the full integrity gate (shape validation, L0-only, specHash\n// recomputation, blockMerkleRoot recomputation via @yakcc/contracts with the\n// reconstructed artifacts Map). Callers never receive an unverified row. This is\n// an authority invariant per DEC-V1-FEDERATION-WIRE-ARTIFACTS-002: the artifact\n// bytes fold into the proof root, so the integrity gate also covers artifact tampering.\n//\n// pullSpec wraps transport.fetchSpec and translates the TransportError(code='not_found')\n// sentinel into an empty array per FEDERATION_PROTOCOL.md §3. Other TransportErrors\n// propagate unchanged so callers can distinguish network failures from\n// \"no blocks for this spec\".\n//\n// Default transport: createHttpTransport() is lazily imported from ./http-transport.js\n// only when opts.transport is not supplied. This avoids initialising the HTTP transport\n// module (which captures globalThis.fetch at construction time) on every module load\n// in test environments.\n//\n// @decision DEC-V1-FEDERATION-WIRE-ARTIFACTS-002: artifactBytes is REQUIRED on the wire.\n// The integrity gate in deserializeWireBlockTriplet calls @yakcc/contracts blockMerkleRoot()\n// directly with the reconstructed artifacts Map. Any single-byte mutation in an artifact\n// causes a root mismatch and throws IntegrityError. pullBlock routes through this gate\n// unconditionally.\n// Status: decided (MASTER_PLAN.md DEC-V1-FEDERATION-WIRE-ARTIFACTS-002)\n//\n// @decision DEC-NO-OWNERSHIP-011: No ownership fields.\n// Status: decided (MASTER_PLAN.md DEC-NO-OWNERSHIP-011)\n//\n// @decision DEC-V1-WAVE-1-SCOPE-001: F1 read-only mirror only.\n// Status: decided (MASTER_PLAN.md DEC-V1-WAVE-1-SCOPE-001)\n\nimport type { BlockMerkleRoot, SpecHash } from \"@yakcc/contracts\";\nimport type { BlockTripletRow } from \"@yakcc/registry\";\nimport { TransportError } from \"./types.js\";\nimport type { RemotePeer, Transport } from \"./types.js\";\nimport { deserializeWireBlockTriplet } from \"./wire.js\";\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * Options shared by pullBlock and pullSpec.\n *\n * Providing opts.transport bypasses the lazy-loaded default HTTP transport.\n * Use this in tests to inject a stub Transport without touching the network.\n *\n * Per DEC-PULL-020: the default transport is createHttpTransport() resolved\n * lazily to prevent module-load-time fetch initialisation in test envs.\n */\nexport interface PullOptions {\n /** Transport implementation. Defaults to createHttpTransport(). */\n transport?: Transport;\n}\n\n// ---------------------------------------------------------------------------\n// Lazy default transport\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the transport from opts or fall back to the lazily-imported default.\n *\n * The dynamic import of ./http-transport.js is intentional: it defers the\n * capture of globalThis.fetch to the first actual pull call, not module load.\n * This is important in test environments where global fetch may not be set up\n * at import time.\n */\nasync function resolveTransport(opts?: PullOptions): Promise<Transport> {\n if (opts?.transport !== undefined) {\n return opts.transport;\n }\n // Dynamic import so the HTTP transport module (and its globalThis.fetch\n // capture) is only loaded when actually needed.\n const { createHttpTransport } = await import(\"./http-transport.js\");\n return createHttpTransport();\n}\n\n// ---------------------------------------------------------------------------\n// pullBlock\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch and verify a single block triplet from a remote peer.\n *\n * Production sequence:\n * 1. Resolve transport (injected or default HTTP).\n * 2. Call transport.fetchBlock(remote, root) → WireBlockTriplet.\n * 3. Call deserializeWireBlockTriplet(wire) — performs ALL integrity checks,\n * including artifact bytes reconstruction and blockMerkleRoot recomputation\n * via @yakcc/contracts with the full artifacts Map.\n * 4. Return the fully-validated BlockTripletRow (with artifacts Map populated).\n *\n * Authority invariant (DEC-PULL-020, DEC-V1-FEDERATION-WIRE-ARTIFACTS-002):\n * every WireBlockTriplet received from the network MUST pass through\n * deserializeWireBlockTriplet before being returned. There is no shortcut path\n * that trusts the wire value without verification. Artifact tampering is caught\n * because blockMerkleRoot() folds artifact bytes into the proof root.\n *\n * @param remote - The mirror URL of the remote peer.\n * @param root - The BlockMerkleRoot to fetch.\n * @param opts - Optional: inject a Transport for test isolation.\n * @returns A fully integrity-checked BlockTripletRow with artifacts Map populated.\n * @throws IntegrityError if any integrity check fails (including artifact tampering).\n * @throws TransportError if the transport layer fails.\n */\nexport async function pullBlock(\n remote: RemotePeer,\n root: BlockMerkleRoot,\n opts?: PullOptions,\n): Promise<BlockTripletRow> {\n const transport = await resolveTransport(opts);\n const wire = await transport.fetchBlock(remote, root);\n // Authority invariant: deserializeWireBlockTriplet is the integrity gate.\n // This call is mandatory. No inline trust-and-return shortcut.\n // The returned row has artifacts Map populated (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002).\n return deserializeWireBlockTriplet(wire);\n}\n\n// ---------------------------------------------------------------------------\n// pullSpec\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch all BlockMerkleRoots a remote peer serves for a given SpecHash.\n *\n * Per FEDERATION_PROTOCOL.md §3: a 404 from the remote is a normal response\n * meaning the peer has no blocks for this spec. This translates to `[]`.\n * All other TransportErrors propagate unchanged.\n *\n * @param remote - The mirror URL of the remote peer.\n * @param specHash - The SpecHash to look up.\n * @param opts - Optional: inject a Transport for test isolation.\n * @returns An array of BlockMerkleRoots (empty if the remote has none for this spec).\n * @throws TransportError for any transport failure other than not_found.\n */\nexport async function pullSpec(\n remote: RemotePeer,\n specHash: SpecHash,\n opts?: PullOptions,\n): Promise<readonly BlockMerkleRoot[]> {\n const transport = await resolveTransport(opts);\n try {\n return await transport.fetchSpec(remote, specHash);\n } catch (err) {\n // TransportError with code='not_found' → normal 404 → return empty array.\n // Per FEDERATION_PROTOCOL.md §3 and the dispatch contract §\"What to build\".\n if (err instanceof TransportError && err.code === \"not_found\") {\n return [];\n }\n // All other errors (network failure, server error, etc.) propagate.\n throw err;\n }\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-MIRROR-REPORT-020: mirrorRegistry orchestrates the Slice D mirror walk.\n// Status: decided (WI-020 Dispatch D, MASTER_PLAN.md)\n// Title: mirror.ts — spec-hierarchy mirror with idempotency and partial-failure resilience\n// Rationale:\n// The v2 mirror walks by spec→block hierarchy (listSpecs → listBlocks) rather than\n// a flat catalog page walk. This maps cleanly onto the registry's selectBlocks(specHash)\n// authority and enables per-spec idempotency checks via registry.getBlock(merkleRoot).\n//\n// Schema-version gate: getSchemaVersion() is called FIRST, before any insert. If the\n// remote schema version exceeds the local SCHEMA_VERSION, mirrorRegistry throws\n// SchemaVersionMismatchError immediately — no rows are touched (DEC-TRANSPORT-SCHEMA-VERSION-020).\n//\n// Integrity gate: every block goes through pullBlock → deserializeWireBlockTriplet.\n// No inline merkle helper; @yakcc/contracts is the single authority for block identity\n// (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002, DEC-CONTRACTS-AUTHORITY-001).\n//\n// Partial failure resilience: a failure on any single block is captured in\n// MirrorReport.failures and the walk continues. The operation is never aborted\n// mid-run except for the schema-version hard abort (FEDERATION_PROTOCOL.md §10).\n//\n// @decision DEC-V1-FEDERATION-WIRE-ARTIFACTS-002: NO new merkle helper inside federation.\n// Status: decided (MASTER_PLAN.md)\n// Rationale: Integrity is handled entirely by pullBlock → deserializeWireBlockTriplet →\n// @yakcc/contracts.blockMerkleRoot. No direct @noble/hashes import; no blockMerkleRootFromRow.\n//\n// @decision DEC-NO-OWNERSHIP-011: No ownership fields anywhere.\n// Status: decided (MASTER_PLAN.md DEC-NO-OWNERSHIP-011)\n//\n// @decision DEC-V1-WAVE-1-SCOPE-001: F1 read-only mirror only.\n// Status: decided (MASTER_PLAN.md DEC-V1-WAVE-1-SCOPE-001)\n\nimport type { SpecHash } from \"@yakcc/contracts\";\nimport type { Registry } from \"@yakcc/registry\";\nimport { SCHEMA_VERSION } from \"@yakcc/registry\";\nimport { pullBlock } from \"./pull.js\";\nimport type { MirrorReport, RemotePeer, Transport } from \"./types.js\";\nimport { SchemaVersionMismatchError } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * Options for mirrorRegistry.\n */\nexport interface MirrorOptions {\n /**\n * Optional clock override for deterministic tests.\n * Default: () => new Date()\n *\n * Injecting this makes timestamp assertions in tests deterministic without\n * mocking global Date.\n */\n readonly clock?: () => Date;\n}\n\n// ---------------------------------------------------------------------------\n// mirrorRegistry\n// ---------------------------------------------------------------------------\n\n/**\n * Mirror all blocks from a remote peer's registry into the local registry.\n *\n * Production sequence (DEC-MIRROR-REPORT-020):\n * 1. Capture startedAt = clock().toISOString().\n * 2. getSchemaVersion(serveUrl): if remote > local SCHEMA_VERSION → throw\n * SchemaVersionMismatchError IMMEDIATELY. No rows touched.\n * 3. listSpecs(serveUrl): for each spec hash,\n * listBlocks(serveUrl, specHash): for each block merkle root,\n * - increment blocksConsidered.\n * - idempotency check: if registry.getBlock(root) returns non-null → skip, increment blocksSkipped.\n * - pullBlock(serveUrl, root, { transport }): integrity gate via deserializeWireBlockTriplet.\n * - registry.storeBlock(row): persist.\n * - increment blocksInserted.\n * - on any error: push failure entry, continue.\n * - increment specsWalked after each spec (success or partial failure).\n * 4. Capture finishedAt = clock().toISOString().\n * 5. Return MirrorReport.\n *\n * Authority invariants:\n * - Integrity: pullBlock → deserializeWireBlockTriplet → @yakcc/contracts blockMerkleRoot().\n * No new merkle helper in this file (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002).\n * - No ownership fields (DEC-NO-OWNERSHIP-011).\n * - No F2 publishing (DEC-V1-WAVE-1-SCOPE-001).\n *\n * @param serveUrl - The remote peer's mirror URL (RemotePeer).\n * @param registry - The local registry to mirror blocks into.\n * @param transport - The Transport implementation for all remote calls.\n * @param options - Optional: clock override for deterministic timestamps in tests.\n * @returns A MirrorReport describing the completed mirror operation.\n * @throws SchemaVersionMismatchError if the remote schema version exceeds SCHEMA_VERSION.\n */\nexport async function mirrorRegistry(\n serveUrl: RemotePeer,\n registry: Registry,\n transport: Transport,\n options?: MirrorOptions,\n): Promise<MirrorReport> {\n const clock = options?.clock ?? (() => new Date());\n\n // Step 1: capture start time.\n const startedAt = clock().toISOString();\n\n // Step 2: schema-version gate — MUST run before any insert.\n // Per DEC-TRANSPORT-SCHEMA-VERSION-020: if remote schemaVersion > local SCHEMA_VERSION,\n // throw immediately. No rows are written before this check.\n const { schemaVersion: remoteSchemaVersion } = await transport.getSchemaVersion(serveUrl);\n if (remoteSchemaVersion > SCHEMA_VERSION) {\n throw new SchemaVersionMismatchError({\n remoteSchemaVersion,\n localSchemaVersion: SCHEMA_VERSION,\n });\n }\n\n // Step 3: walk specs → blocks.\n let specsWalked = 0;\n let blocksConsidered = 0;\n let blocksInserted = 0;\n let blocksSkipped = 0;\n const failures: Array<{\n readonly specHash: string;\n readonly blockMerkleRoot: string | null;\n readonly reason: string;\n readonly at: string;\n }> = [];\n\n const specHashes = await transport.listSpecs(serveUrl);\n\n for (const specHash of specHashes as SpecHash[]) {\n const blockRoots = await transport.listBlocks(serveUrl, specHash);\n\n for (const blockRoot of blockRoots) {\n blocksConsidered++;\n\n try {\n // Idempotency check: if the row already exists, skip it.\n // registry.getBlock() returns null when the block is not present.\n const existing = await registry.getBlock(blockRoot);\n if (existing !== null) {\n blocksSkipped++;\n continue;\n }\n\n // Pull and integrity-check via pullBlock → deserializeWireBlockTriplet.\n // This is the authority path — no inline merkle computation here.\n // DEC-V1-FEDERATION-WIRE-ARTIFACTS-002, DEC-CONTRACTS-AUTHORITY-001.\n const row = await pullBlock(serveUrl, blockRoot, { transport });\n\n // Insert into the local registry.\n await registry.storeBlock(row);\n blocksInserted++;\n } catch (err: unknown) {\n // Per FEDERATION_PROTOCOL.md §10: individual block failures are loud,\n // partial, and recoverable. Capture and continue — do not abort the walk.\n const reason =\n err instanceof Error ? err.message : typeof err === \"string\" ? err : \"unknown error\";\n\n failures.push({\n specHash,\n blockMerkleRoot: blockRoot,\n reason,\n at: clock().toISOString(),\n });\n }\n }\n\n // specsWalked increments after each spec finishes (success or partial failure).\n specsWalked++;\n }\n\n // Step 4: capture finish time.\n const finishedAt = clock().toISOString();\n\n // Step 5: return the report.\n return {\n serveUrl,\n schemaVersion: remoteSchemaVersion,\n startedAt,\n finishedAt,\n specsWalked,\n blocksConsidered,\n blocksInserted,\n blocksSkipped,\n failures,\n };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-SERVE-E-020: serveRegistry — read-only HTTP mirror server (Slice E).\n// Status: decided (WI-020 Dispatch E, FEDERATION_PROTOCOL.md §3)\n// Title: serve.ts — F1 read-only registry HTTP server (four Slice-E endpoints)\n// Rationale:\n// serveRegistry exposes a local Registry over HTTP following FEDERATION_PROTOCOL.md §3.\n// It is strictly GET-only (all non-GET methods → 405). The four Slice-E endpoints are:\n// GET /schema-version → { schemaVersion: number }\n// GET /v1/specs → { specHashes: SpecHash[] }\n// GET /v1/spec/<specHash> → { specHash, blockMerkleRoots: BlockMerkleRoot[] } | 404\n// GET /v1/block/<root> → WireBlockTriplet via serializeWireBlockTriplet | 404\n// /v1/manifest and /v1/blocks are intentionally deferred to Slice F (not in scope here).\n// No mutation endpoint exists — not even behind a flag (DEC-V1-WAVE-1-SCOPE-001).\n// port: 0 binds an OS-assigned port (useful for tests). close() shuts down cleanly.\n//\n// @decision DEC-SERVE-SPECS-ENUMERATION-020: enumerateSpecs via Registry interface (WI-026 closure).\n// Status: superseded/closed (WI-026)\n// Title: Spec enumeration via Registry.enumerateSpecs()\n// Rationale:\n// WI-026 added Registry.enumerateSpecs() as a first-class method on the Registry interface.\n// The former optional callback (ServeOptions.enumerateSpecs) was a workaround because the\n// Registry interface had no enumerate-distinct-specs primitive. Post-WI-026, serveRegistry\n// calls registry.enumerateSpecs() directly. The callback field is removed from ServeOptions.\n// No dual-authority: the old callback path is gone (Sacred Practice #12).\n//\n// @decision DEC-NO-OWNERSHIP-011: No ownership fields anywhere.\n// Status: decided (MASTER_PLAN.md DEC-NO-OWNERSHIP-011)\n//\n// @decision DEC-V1-WAVE-1-SCOPE-001: F1 read-only mirror only in v1 wave-1.\n// Status: decided (MASTER_PLAN.md DEC-V1-WAVE-1-SCOPE-001)\n\nimport * as http from \"node:http\";\nimport type { IncomingMessage, Server, ServerResponse } from \"node:http\";\nimport type { BlockMerkleRoot, SpecHash } from \"@yakcc/contracts\";\nimport type { Registry } from \"@yakcc/registry\";\nimport { SCHEMA_VERSION } from \"@yakcc/registry\";\nimport { serializeWireBlockTriplet } from \"./wire.js\";\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * Options for serveRegistry.\n */\nexport interface ServeOptions {\n /**\n * TCP port to listen on. Default 0 (OS-assigned).\n * Pass 0 to let the OS pick an available port; use ServeHandle.url to find it.\n */\n readonly port?: number;\n /**\n * Bind host. Default \"127.0.0.1\".\n * Pass \"0.0.0.0\" to accept connections on all interfaces.\n */\n readonly host?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Response helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Send a JSON response with the given status code and Content-Type.\n * All federation endpoints respond with application/json per FEDERATION_PROTOCOL.md §3.\n */\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n const payload = JSON.stringify(body);\n res.writeHead(status, {\n \"Content-Type\": \"application/json\",\n \"Content-Length\": Buffer.byteLength(payload, \"utf-8\"),\n });\n res.end(payload);\n}\n\n/**\n * Send a protocol §3 error envelope: { \"error\": \"<code>\" }.\n *\n * @param res - The outgoing HTTP response.\n * @param status - HTTP status code (404, 405, etc.).\n * @param code - Wire error code string.\n */\nfunction sendError(res: ServerResponse, status: number, code: string): void {\n sendJson(res, status, { error: code });\n}\n\n// ---------------------------------------------------------------------------\n// Route handlers\n// ---------------------------------------------------------------------------\n\n/**\n * GET /schema-version\n *\n * Returns { schemaVersion: SCHEMA_VERSION }.\n * Per DEC-TRANSPORT-SCHEMA-VERSION-020: mirrorRegistry calls this first to abort\n * if the remote schema version exceeds the local one.\n */\nfunction handleSchemaVersion(_req: IncomingMessage, res: ServerResponse): void {\n sendJson(res, 200, { schemaVersion: SCHEMA_VERSION });\n}\n\n/**\n * GET /v1/specs\n *\n * Returns { specHashes: SpecHash[] } — all distinct spec hashes served.\n * Calls registry.enumerateSpecs() directly (DEC-SERVE-SPECS-ENUMERATION-020, WI-026 closure).\n *\n * DEC-TRANSPORT-LIST-METHODS-020 (see types.ts): listSpecs maps to this endpoint.\n */\nasync function handleListSpecs(\n _req: IncomingMessage,\n res: ServerResponse,\n local: Registry,\n): Promise<void> {\n const specHashes = await local.enumerateSpecs();\n sendJson(res, 200, { specHashes });\n}\n\n/**\n * GET /v1/spec/<specHash>\n *\n * Returns { specHash, blockMerkleRoots: BlockMerkleRoot[] } for the given specHash,\n * or 404 with { error: \"spec_not_found\" } if no blocks exist for that spec.\n *\n * Per http-transport.ts: the receiver parses `envelope.blockMerkleRoots`.\n */\nasync function handleGetSpec(\n _req: IncomingMessage,\n res: ServerResponse,\n local: Registry,\n specHashParam: string,\n): Promise<void> {\n const roots = await local.selectBlocks(specHashParam as SpecHash);\n if (roots.length === 0) {\n sendError(res, 404, \"spec_not_found\");\n return;\n }\n sendJson(res, 200, {\n specHash: specHashParam,\n blockMerkleRoots: roots,\n });\n}\n\n/**\n * GET /v1/block/<merkleRoot>\n *\n * Returns the WireBlockTriplet for the given merkle root (via serializeWireBlockTriplet),\n * or 404 with { error: \"block_not_found\" } if the block is not in the registry.\n *\n * Per DEC-V1-FEDERATION-WIRE-ARTIFACTS-002: serializeWireBlockTriplet encodes\n * all artifact bytes into the wire shape. The receiver validates via\n * deserializeWireBlockTriplet before trusting the row.\n */\nasync function handleGetBlock(\n _req: IncomingMessage,\n res: ServerResponse,\n local: Registry,\n merkleRootParam: string,\n): Promise<void> {\n const row = await local.getBlock(merkleRootParam as BlockMerkleRoot);\n if (row === null) {\n sendError(res, 404, \"block_not_found\");\n return;\n }\n sendJson(res, 200, serializeWireBlockTriplet(row));\n}\n\n// ---------------------------------------------------------------------------\n// Request dispatcher\n// ---------------------------------------------------------------------------\n\n/**\n * Parse the incoming request and route it to the appropriate handler.\n *\n * All non-GET methods return 405 with the §3 error envelope.\n * Unknown GET paths return 404 with { error: \"not_found\" }.\n */\nasync function dispatch(req: IncomingMessage, res: ServerResponse, local: Registry): Promise<void> {\n // All non-GET methods return 405 (DEC-V1-WAVE-1-SCOPE-001: read-only).\n if (req.method !== \"GET\") {\n res.setHeader(\"Allow\", \"GET\");\n sendError(res, 405, \"method_not_allowed\");\n return;\n }\n\n // Parse path — strip query string.\n const rawPath = (req.url ?? \"/\").split(\"?\")[0] ?? \"/\";\n\n // Route: GET /schema-version\n if (rawPath === \"/schema-version\") {\n handleSchemaVersion(req, res);\n return;\n }\n\n // Route: GET /v1/specs\n if (rawPath === \"/v1/specs\") {\n await handleListSpecs(req, res, local);\n return;\n }\n\n // Route: GET /v1/spec/<specHash>\n const specMatch = /^\\/v1\\/spec\\/([^/]+)$/.exec(rawPath);\n if (specMatch !== null) {\n const specHashParam = specMatch[1];\n if (specHashParam !== undefined && specHashParam.length > 0) {\n await handleGetSpec(req, res, local, specHashParam);\n return;\n }\n }\n\n // Route: GET /v1/block/<merkleRoot>\n const blockMatch = /^\\/v1\\/block\\/([^/]+)$/.exec(rawPath);\n if (blockMatch !== null) {\n const merkleRootParam = blockMatch[1];\n if (merkleRootParam !== undefined && merkleRootParam.length > 0) {\n await handleGetBlock(req, res, local, merkleRootParam);\n return;\n }\n }\n\n // Unknown path → 404 not_found.\n sendError(res, 404, \"not_found\");\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Result of serveRegistry. Carries the resolved URL and a clean-shutdown function.\n */\nexport interface ServeHandle {\n /** The underlying node:http Server instance. */\n readonly server: Server;\n /**\n * The URL the server is listening on, e.g. \"http://127.0.0.1:54321\".\n * When opts.port was 0, this reflects the OS-assigned port.\n */\n readonly url: string;\n /** Shut down the HTTP server gracefully. Resolves once the server is fully closed. */\n readonly close: () => Promise<void>;\n}\n\n/**\n * Start a read-only HTTP server exposing `registry` as a federation peer.\n *\n * Implements the Slice-E GET endpoints of FEDERATION_PROTOCOL.md §3:\n * GET /schema-version → { schemaVersion: number }\n * GET /v1/specs → { specHashes: SpecHash[] }\n * GET /v1/spec/<specHash> → { specHash, blockMerkleRoots } | 404\n * GET /v1/block/<merkleRoot> → WireBlockTriplet | 404\n *\n * /v1/manifest and /v1/blocks are intentionally deferred to Slice F.\n *\n * All non-GET methods return 405 { error: \"method_not_allowed\" }.\n * Unknown GET paths return 404 { error: \"not_found\" }.\n *\n * When opts.port === 0 (the default), the OS picks an available port and the\n * resolved URL is returned in ServeHandle.url.\n *\n * Per DEC-V1-WAVE-1-SCOPE-001: READ-ONLY. No mutation endpoint.\n * Per DEC-SERVE-SPECS-ENUMERATION-020 (WI-026 closure): GET /v1/specs calls\n * registry.enumerateSpecs() directly — no callback needed in options.\n *\n * @param registry - The local Registry to serve blocks from.\n * @param options - Bind address and port.\n * @returns A ServeHandle with the resolved URL and a close() function.\n */\nexport async function serveRegistry(\n registry: Registry,\n options?: ServeOptions,\n): Promise<ServeHandle> {\n const port = options?.port ?? 0;\n const host = options?.host ?? \"127.0.0.1\";\n\n const server = http.createServer((req, res) => {\n dispatch(req, res, registry).catch((err: unknown) => {\n // Last-resort error handler: respond 500 and log, so the server doesn't crash\n // on an unhandled async rejection from a request handler.\n if (!res.headersSent) {\n sendError(res, 500, \"internal_error\");\n } else {\n res.end();\n }\n console.error(\"[serveRegistry] Unhandled request error:\", err);\n });\n });\n\n // Wait for the server to bind and resolve the actual port.\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, host, () => {\n server.removeListener(\"error\", reject);\n resolve();\n });\n });\n\n const address = server.address();\n if (address === null || typeof address === \"string\") {\n server.close();\n throw new Error(\"serveRegistry: unexpected server address type\");\n }\n\n const resolvedUrl = `http://${host}:${address.port}`;\n\n let closed = false;\n\n return {\n server,\n url: resolvedUrl,\n close(): Promise<void> {\n // Idempotent: a second call to close() is a no-op.\n if (closed) return Promise.resolve();\n closed = true;\n return new Promise<void>((resolve, reject) => {\n server.close((err) => {\n if (err !== undefined) {\n // \"Server is not running\" — already stopped — treat as success.\n if ((err as NodeJS.ErrnoException).code === \"ERR_SERVER_NOT_RUNNING\") {\n resolve();\n } else {\n reject(err);\n }\n } else {\n resolve();\n }\n });\n });\n },\n };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-V1-FEDERATION-PROTOCOL-001: HTTP+JSON transport, content-addressed\n// block identity (BlockMerkleRoot + SpecHash), nominal peer trust (mirror URL only),\n// pull-only read-only sync direction.\n// Status: decided (MASTER_PLAN.md DEC-V1-FEDERATION-PROTOCOL-001)\n// Contract document: FEDERATION_PROTOCOL.md\n// Rationale: Minimal new infrastructure; maps cleanly onto content-addressed URLs;\n// Transport interface seam allows future libp2p/IPFS without rewriting merge logic.\n//\n// @decision DEC-V1-FEDERATION-WIRE-ARTIFACTS-002: WireBlockTriplet.artifactBytes\n// is required (Record<string, string>, base64-encoded). The wire integrity check\n// must call @yakcc/contracts blockMerkleRoot() directly — NO parallel merkle helper\n// inside @yakcc/federation. artifactBytes carries the bytes the contracts formula\n// folds into the proof root; without them the recomputed root diverges from the\n// persisted BlockMerkleRoot.\n// Status: decided (MASTER_PLAN.md DEC-V1-FEDERATION-WIRE-ARTIFACTS-002)\n//\n// Barrel export strategy: Slice 0 exports types only. Runtime function exports\n// (serializeWireBlockTriplet, deserializeWireBlockTriplet, pullBlock, pullSpec,\n// mirrorRegistry, createHttpTransport, serveRegistry) are added in subsequent\n// slices A–F as their owning modules land. This avoids typecheck failures from\n// forwarding to modules that don't yet exist.\n\n// ---------------------------------------------------------------------------\n// Types (Slice 0 — complete public type surface)\n// ---------------------------------------------------------------------------\n\nexport type {\n RemotePeer,\n RemoteManifest,\n CatalogPage,\n MirrorRejectionReason,\n MirrorRejection,\n MirrorReport,\n Transport,\n WireBlockTriplet,\n} from \"./types.js\";\n\nexport {\n IntegrityError,\n VersionMismatchError,\n SchemaVersionMismatchError,\n TransportError,\n} from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Wire serialization (Slice A)\n// ---------------------------------------------------------------------------\n\nexport { serializeWireBlockTriplet, deserializeWireBlockTriplet } from \"./wire.js\";\n\n// ---------------------------------------------------------------------------\n// HTTP transport (Slice B)\n// ---------------------------------------------------------------------------\n\nexport { createHttpTransport } from \"./http-transport.js\";\nexport type { HttpTransportOptions } from \"./http-transport.js\";\n\n// ---------------------------------------------------------------------------\n// Pull primitives (Slice C)\n// ---------------------------------------------------------------------------\n\nexport { pullBlock, pullSpec } from \"./pull.js\";\nexport type { PullOptions } from \"./pull.js\";\n\n// ---------------------------------------------------------------------------\n// Mirror (Slice D)\n// ---------------------------------------------------------------------------\n\nexport { mirrorRegistry } from \"./mirror.js\";\nexport type { MirrorOptions } from \"./mirror.js\";\n\n// ---------------------------------------------------------------------------\n// Serve (Slice E)\n// ---------------------------------------------------------------------------\n\nexport { serveRegistry } from \"./serve.js\";\nexport type { ServeHandle, ServeOptions } from \"./serve.js\";\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CLI-FEDERATION-001: federation command — thin CLI wrapper around\n// @yakcc/federation public surface (mirrorRegistry, serveRegistry, pullBlock,\n// createHttpTransport) and @yakcc/registry (openRegistry).\n// Status: implemented (WI-020 Slice G)\n// Rationale: Follows the established (argv, logger) → Promise<number> contract from\n// seed.ts/shave.ts. Transport injection seam (opts.transport) enables unit tests\n// without network I/O. Serve path accepts noBlock option so tests can obtain the\n// ServeHandle without blocking on SIGINT. Output is JSON (mirror report) or\n// concise summary lines (pull) to keep the CLI output useful without dumping base64.\n//\n// @decision DEC-CLI-FEDERATION-IMPORTS-001: federation.ts imports ONLY from\n// @yakcc/federation and @yakcc/registry. No direct @yakcc/contracts, @yakcc/shave,\n// @yakcc/compile, or any other workspace package is imported here.\n// Status: decided (WI-020 Slice G)\n// Rationale: The federation surface is the single authority for federation logic;\n// the CLI is a thin dispatch layer that should not bypass the public API.\n//\n// @decision DEC-NO-OWNERSHIP-011: No ownership fields anywhere.\n// Status: decided (MASTER_PLAN.md DEC-NO-OWNERSHIP-011)\n//\n// @decision DEC-V1-WAVE-1-SCOPE-001: F1 read-only mirror only. No push/auth.\n// Status: decided (MASTER_PLAN.md DEC-V1-WAVE-1-SCOPE-001)\n\nimport { parseArgs } from \"node:util\";\nimport type { BlockMerkleRoot } from \"@yakcc/contracts\";\nimport {\n SchemaVersionMismatchError,\n createHttpTransport,\n mirrorRegistry,\n pullBlock,\n serveRegistry,\n} from \"@yakcc/federation\";\nimport type { ServeHandle, Transport } from \"@yakcc/federation\";\nimport type { Registry, RegistryOptions } from \"@yakcc/registry\";\nimport { openRegistry } from \"@yakcc/registry\";\nimport type { Logger } from \"../index.js\";\n\n// ---------------------------------------------------------------------------\n// Internal seam options (for test injection — not exposed in CLI args)\n// ---------------------------------------------------------------------------\n\n/**\n * Internal options for runFederation.\n * Callers may inject these in tests to bypass network I/O and SIGINT waiting.\n */\nexport interface FederationOptions {\n /**\n * Transport to use for mirror/pull operations.\n * Default: createHttpTransport() (real HTTP).\n * Tests inject a stub Transport to avoid network I/O.\n */\n transport?: Transport;\n /**\n * When true, the serve subcommand starts the server and returns the\n * ServeHandle immediately without registering SIGINT/SIGTERM handlers or\n * blocking indefinitely.\n * Default: false (production — blocks until signal).\n */\n noBlock?: boolean;\n /**\n * Embedding provider forwarded to openRegistry.\n * Default: createLocalEmbeddingProvider() (network-dependent).\n * Tests inject createOfflineEmbeddingProvider() to avoid HuggingFace I/O.\n */\n embeddings?: RegistryOptions[\"embeddings\"];\n}\n\n// ---------------------------------------------------------------------------\n// Usage text\n// ---------------------------------------------------------------------------\n\n/** Print federation subcommand usage to logger.log. */\nexport function printFederationUsage(logger: Logger): void {\n logger.log(\n [\n \"Usage: yakcc federation <subcommand> [options]\",\n \"\",\n \"Subcommands:\",\n \" serve --registry <db-path> [--port <n>] [--host <h>]\",\n \" Start a read-only HTTP registry server\",\n \" mirror --remote <url> --registry <db-path>\",\n \" Mirror all blocks from a remote registry peer\",\n \" pull --remote <url> --root <merkleRoot> --registry <db-path>\",\n \" Pull a single block triplet from a remote peer\",\n ].join(\"\\n\"),\n );\n}\n\n// ---------------------------------------------------------------------------\n// Subcommand: serve\n// ---------------------------------------------------------------------------\n\n/**\n * Run `yakcc federation serve`.\n *\n * In production (opts.noBlock === false, the default), binds the port and then\n * waits on SIGINT/SIGTERM, calling handle.close() and exiting 0 on receipt.\n *\n * In test mode (opts.noBlock === true), binds and returns immediately with exit\n * code 0. The caller obtains the ServeHandle via the returned value of\n * runFederationServe() directly (not via process signals).\n *\n * Returns the ServeHandle so tests can interact with the running server.\n *\n * @param argv - Args after \"serve\" has been consumed.\n * @param logger - Output sink.\n * @param opts - Internal options (noBlock for test isolation).\n * @returns { code: number; handle: ServeHandle | null }\n */\nexport async function runFederationServe(\n argv: readonly string[],\n logger: Logger,\n opts?: FederationOptions,\n): Promise<{ code: number; handle: ServeHandle | null }> {\n const parsed = (() => {\n try {\n return parseArgs({\n args: [...argv],\n allowPositionals: false,\n options: {\n registry: { type: \"string\" },\n port: { type: \"string\" },\n host: { type: \"string\" },\n },\n });\n } catch (err) {\n logger.error(`error: ${(err as Error).message}`);\n return null;\n }\n })();\n if (parsed === null) return { code: 1, handle: null };\n\n const registryPath = parsed.values.registry;\n if (registryPath === undefined || registryPath === \"\") {\n logger.error(\"error: --registry <db-path> is required for 'federation serve'\");\n return { code: 1, handle: null };\n }\n\n const portArg = parsed.values.port;\n const port = portArg !== undefined ? Number.parseInt(portArg, 10) : 0;\n if (portArg !== undefined && (Number.isNaN(port) || port < 0 || port > 65535)) {\n logger.error(`error: invalid --port value: ${portArg}`);\n return { code: 1, handle: null };\n }\n\n const host = parsed.values.host ?? \"127.0.0.1\";\n\n let registry: Registry;\n try {\n registry = await openRegistry(registryPath, { embeddings: opts?.embeddings });\n } catch (err) {\n logger.error(`error: failed to open registry at ${registryPath}: ${String(err)}`);\n return { code: 1, handle: null };\n }\n\n let handle: ServeHandle;\n try {\n handle = await serveRegistry(registry, { port, host });\n } catch (err) {\n logger.error(`error: failed to start server: ${String(err)}`);\n await registry.close();\n return { code: 1, handle: null };\n }\n\n logger.log(`federation serve: listening at ${handle.url}`);\n\n if (opts?.noBlock === true) {\n // Test path: return immediately with the handle so the caller can interact\n // with the server and call handle.close() themselves.\n return { code: 0, handle };\n }\n\n // Production path: wait on SIGINT/SIGTERM.\n await new Promise<void>((resolve) => {\n const shutdown = (): void => {\n handle\n .close()\n .then(() => {\n registry.close().finally(resolve);\n })\n .catch(resolve);\n };\n process.once(\"SIGINT\", shutdown);\n process.once(\"SIGTERM\", shutdown);\n });\n\n return { code: 0, handle };\n}\n\n// ---------------------------------------------------------------------------\n// Subcommand: mirror\n// ---------------------------------------------------------------------------\n\n/**\n * Run `yakcc federation mirror`.\n * Mirrors all blocks from the remote peer into the local registry.\n * Prints the MirrorReport as JSON (two-space indent) to logger.log.\n * Exits 0 on success (even if failures[] is non-empty — those are recoverable).\n * Exits 1 only on a thrown error (e.g. SchemaVersionMismatchError).\n *\n * @param argv - Args after \"mirror\" has been consumed.\n * @param logger - Output sink.\n * @param opts - Internal options (transport for test injection).\n * @returns Process exit code.\n */\nasync function runFederationMirror(\n argv: readonly string[],\n logger: Logger,\n opts?: FederationOptions,\n): Promise<number> {\n const parsed = (() => {\n try {\n return parseArgs({\n args: [...argv],\n allowPositionals: false,\n options: {\n remote: { type: \"string\" },\n registry: { type: \"string\" },\n },\n });\n } catch (err) {\n logger.error(`error: ${(err as Error).message}`);\n return null;\n }\n })();\n if (parsed === null) return 1;\n\n const remote = parsed.values.remote;\n if (remote === undefined || remote === \"\") {\n logger.error(\"error: --remote <url> is required for 'federation mirror'\");\n return 1;\n }\n\n const registryPath = parsed.values.registry;\n if (registryPath === undefined || registryPath === \"\") {\n logger.error(\"error: --registry <db-path> is required for 'federation mirror'\");\n return 1;\n }\n\n let registry: Registry;\n try {\n registry = await openRegistry(registryPath, { embeddings: opts?.embeddings });\n } catch (err) {\n logger.error(`error: failed to open registry at ${registryPath}: ${String(err)}`);\n return 1;\n }\n\n // Resolve transport: injected (tests) or real HTTP (production).\n const transport = opts?.transport ?? createHttpTransport();\n\n try {\n const report = await mirrorRegistry(remote, registry, transport);\n logger.log(JSON.stringify(report, null, 2));\n return 0;\n } catch (err) {\n if (err instanceof SchemaVersionMismatchError) {\n logger.error(`error: schema version mismatch: ${err.message}`);\n } else {\n logger.error(`error: mirror failed: ${String(err)}`);\n }\n return 1;\n } finally {\n await registry.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Subcommand: pull\n// ---------------------------------------------------------------------------\n\n/**\n * Run `yakcc federation pull`.\n * Pulls a single block from the remote peer, integrity-checks it, and prints\n * a concise summary (blockMerkleRoot + specHash) to logger.log.\n *\n * When --registry is supplied, persists the pulled row to the named registry\n * idempotently (DEC-STORAGE-IDEMPOTENT-001). The persist side-effect relies\n * entirely on storeBlock's existing idempotency — the CLI does NOT pre-check\n * whether the row is already present (Sacred Practice #12, DEC-SCHEMA-MIGRATION-002).\n *\n * When --registry is omitted, the pull is a read-only diagnostic verb (no\n * registry is opened; the existing diagnostic-line surface is preserved).\n *\n * Failure-mode separation (three distinct error messages per eval contract):\n * - Registry open failure → \"error: failed to open registry at <path>: ...\"\n * - Pull/transport failure → \"error: pull failed: ...\"\n * - Persist failure → \"error: failed to persist block to registry: ...\"\n *\n * @decision DEC-CLI-PULL-PERSIST-001: persist-on-pull wiring (WI-030)\n * Status: implemented (WI-030)\n * Rationale: CLI is the only layer that gains persistence; pullBlock stays pure\n * (DEC-V1-FEDERATION-PROTOCOL-001). Open before pull (fail-fast on bad path),\n * close in finally (resource-cleanup invariant). Row is passed to storeBlock\n * byte-identical (DEC-TRIPLET-IDENTITY-020, DEC-NO-OWNERSHIP-011). Idempotency\n * is provided by DEC-STORAGE-IDEMPOTENT-001 — no pre-check at CLI layer.\n *\n * @param argv - Args after \"pull\" has been consumed.\n * @param logger - Output sink.\n * @param opts - Internal options (transport for test injection).\n * @returns Process exit code.\n */\nasync function runFederationPull(\n argv: readonly string[],\n logger: Logger,\n opts?: FederationOptions,\n): Promise<number> {\n const parsed = (() => {\n try {\n return parseArgs({\n args: [...argv],\n allowPositionals: false,\n options: {\n remote: { type: \"string\" },\n root: { type: \"string\" },\n registry: { type: \"string\" },\n },\n });\n } catch (err) {\n logger.error(`error: ${(err as Error).message}`);\n return null;\n }\n })();\n if (parsed === null) return 1;\n\n const remote = parsed.values.remote;\n if (remote === undefined || remote === \"\") {\n logger.error(\"error: --remote <url> is required for 'federation pull'\");\n return 1;\n }\n\n const root = parsed.values.root;\n if (root === undefined || root === \"\") {\n logger.error(\"error: --root <merkleRoot> is required for 'federation pull'\");\n return 1;\n }\n\n // --registry: empty string treated as missing, matching serve/mirror pattern\n // (lines 127-130 and 228-231). When absent, pull is a read-only diagnostic verb.\n const registryPath = parsed.values.registry;\n const hasRegistry = registryPath !== undefined && registryPath !== \"\";\n if (registryPath !== undefined && registryPath === \"\") {\n logger.error(\"error: --registry <db-path> must not be an empty string for 'federation pull'\");\n return 1;\n }\n\n // Open registry BEFORE pull: fail-fast on bad path (DEC-CLI-PULL-PERSIST-001).\n // Only opened when --registry is present.\n let registry: Registry | null = null;\n if (hasRegistry) {\n try {\n registry = await openRegistry(registryPath as string, { embeddings: opts?.embeddings });\n } catch (err) {\n logger.error(`error: failed to open registry at ${registryPath as string}: ${String(err)}`);\n return 1;\n }\n }\n\n // Resolve transport: injected (tests) or real HTTP (production).\n const transport = opts?.transport ?? createHttpTransport();\n\n try {\n const row = await pullBlock(remote as BlockMerkleRoot, root as BlockMerkleRoot, { transport });\n\n // Diagnostic lines — preserved byte-identical from pre-WI-030 path.\n logger.log(\"pulled block:\");\n logger.log(` blockMerkleRoot: ${row.blockMerkleRoot}`);\n logger.log(` specHash: ${row.specHash}`);\n\n // Persist when --registry was supplied.\n // storeBlock is idempotent (DEC-STORAGE-IDEMPOTENT-001) — no pre-check here.\n // Row is passed through unchanged (DEC-TRIPLET-IDENTITY-020, DEC-NO-OWNERSHIP-011).\n if (registry !== null) {\n try {\n await registry.storeBlock(row);\n } catch (err) {\n logger.error(`error: failed to persist block to registry: ${String(err)}`);\n return 1;\n }\n // Optional appended line (DEC-CLI-PULL-PERSIST-001 implementation choice).\n logger.log(` persisted: ${registryPath as string}`);\n }\n\n return 0;\n } catch (err) {\n logger.error(`error: pull failed: ${String(err)}`);\n return 1;\n } finally {\n // Resource-cleanup invariant: close registry handle on all paths.\n if (registry !== null) {\n await registry.close();\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public dispatch entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Run `yakcc federation <subcommand> [args]`.\n *\n * Dispatches to serve/mirror/pull based on args[0].\n * Unknown subcommand → print usage, return 1.\n *\n * opts is an internal test-injection seam — production callers (index.ts) pass\n * no opts and get the real HTTP transport + blocking SIGINT behaviour.\n *\n * @param args - Remaining args after \"federation\" has been consumed (args[0] = subcommand).\n * @param logger - Output sink; defaults to CONSOLE_LOGGER via the caller in index.ts.\n * @param opts - Internal options for test injection (transport, noBlock).\n * @returns Promise<number> — process exit code.\n */\nexport async function runFederation(\n args: string[],\n logger: Logger,\n opts?: FederationOptions,\n): Promise<number> {\n const [sub, ...rest] = args;\n\n switch (sub) {\n case \"serve\": {\n const result = await runFederationServe(rest, logger, opts);\n return result.code;\n }\n\n case \"mirror\": {\n return runFederationMirror(rest, logger, opts);\n }\n\n case \"pull\": {\n return runFederationPull(rest, logger, opts);\n }\n\n default: {\n printFederationUsage(logger);\n return 1;\n }\n }\n}\n","#!/usr/bin/env node\n// SPDX-License-Identifier: MIT\n// Entry point for the `yakcc` CLI binary.\n// Delegates to runCli and exits with the returned code.\n// WI-005 wires the real command handlers.\nimport { realpathSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { runCli } from \"./index.js\";\nimport { patchSqliteDatabase } from \"./pkg-native-compat.js\";\n\n// Apply the pkg snapshot .so extraction patch before any registry command can\n// call db.loadExtension(). better-sqlite3 is a CJS module; we use createRequire\n// to access it so that patchSqliteDatabase receives the real Database constructor.\n// This is a no-op when running from source (insideSnapshot returns false).\n{\n const _require = createRequire(import.meta.url);\n try {\n // better-sqlite3 is a CJS module; require() returns the Database\n // constructor directly (not wrapped in { default: ... }).\n const Database = _require(\"better-sqlite3\") as {\n prototype: { loadExtension?: (...a: unknown[]) => unknown };\n };\n patchSqliteDatabase(Database);\n } catch (err) {\n // better-sqlite3 not available at patch time; skip silently.\n // The patch is best-effort: if it can't load here, the native\n // addon may not be in the snapshot either, and the real error\n // will surface when the command runs.\n // Set YAKCC_DEBUG=1 to surface this warning for troubleshooting.\n if (process.env.YAKCC_DEBUG && err instanceof Error) {\n console.warn(`[yakcc] pkg-native-compat patch skipped: ${err.message}`);\n }\n }\n}\n\n/**\n * @decision DEC-CLI-BIN-MAIN-MODULE-001\n * @title Cross-platform main-module guard using fileURLToPath\n * @status superseded\n * @rationale Superseded by DEC-CLI-BIN-MAIN-MODULE-GUARD-WINDOWS-001. The original\n * fileURLToPath(import.meta.url) === process.argv[1] guard was a correct fix for\n * #274 but does not cover symlinks, Windows case-insensitive drive letters (C:\\ vs c:\\),\n * or 8.3 short-name forms (PROGRA~1 vs Program Files). See new decision below.\n */\n\n/**\n * @decision DEC-CLI-BIN-MAIN-MODULE-GUARD-WINDOWS-001\n * @title Defense-in-depth main-module guard: realpathSync normalizes path before comparison\n * @status accepted\n * @rationale WI-ALPHA-WINDOWS-BIN-JS (#385): the original fileURLToPath guard (DEC-CLI-BIN-MAIN-MODULE-001)\n * is functionally correct for the typical `node bin.js` invocation but leaves open three\n * Windows edge cases: (1) symlink-to-bin.js where process.argv[1] is the symlink path\n * and import.meta.url is the real path; (2) case-insensitive drive letter normalization\n * (C:\\foo vs c:\\foo); (3) 8.3 short-name forms (PROGRA~1 vs Program Files).\n * realpathSync resolves all three: it canonicalizes symlinks, normalizes case on\n * case-insensitive filesystems, and resolves 8.3 short names. fileURLToPath handles\n * the file:///C:/... drive-letter URL form on Windows. The byte-string comparison\n * after both transforms is platform-agnostic.\n * FORBIDDEN: NO try-catch around the comparison itself — that re-introduces the\n * silent-no-op failure mode that #274 was filed to defeat. Only the ENOENT case\n * (process.argv[1] path does not exist, e.g. node -e \"...\") falls back to URL comparison.\n * PRESERVES: WI-361 patchSqliteDatabase invocation above; this guard is a separate concern.\n */\n// Guard dispatch on direct execution so this module can be imported without\n// side effects. realpathSync(fileURLToPath(...)) normalizes case + symlinks +\n// 8.3 short-names on Windows; fileURLToPath handles the file:/// drive-letter form.\n// Both sides resolve to OS-native canonical paths before the byte comparison.\nfunction isMainModule(): boolean {\n try {\n const thisFile = realpathSync(fileURLToPath(import.meta.url));\n const entryFile = process.argv[1] ? realpathSync(process.argv[1]) : \"\";\n return thisFile === entryFile;\n } catch (err) {\n // realpathSync throws ENOENT when the path does not exist (e.g. node -e \"...\").\n // Fall back to URL string comparison ONLY for the not-found case.\n // Do NOT catch other errors (e.g. EACCES, EPERM) — propagate them.\n if (err instanceof Error && (err as NodeJS.ErrnoException).code === \"ENOENT\") {\n return import.meta.url === pathToFileURL(process.argv[1] ?? \"\").href;\n }\n throw err;\n }\n}\n\nif (isMainModule()) {\n runCli(process.argv.slice(2)).then((code) => process.exit(code));\n}\n","// SPDX-License-Identifier: MIT\n// bootstrap.ts — `yakcc bootstrap` command.\n//\n// Walks the yakcc source tree (packages/*/src and examples/*/src), shaves\n// each .ts file through the offline static-intent pipeline, and dumps the\n// deterministic registry manifest via Registry.exportManifest() (WI-V2-BOOTSTRAP-01).\n//\n// Outputs:\n// bootstrap/expected-roots.json - sorted deterministic manifest\n// bootstrap/report.json - per-file shave outcomes\n// bootstrap/yakcc.registry.sqlite - SQLite registry (gitignored)\n//\n// @decision DEC-V2-BOOT-CLI-001\n// @title yakcc bootstrap is a CLI orchestrator over per-file shave\n// @status accepted\n// @rationale The bootstrap verb is intentionally a thin orchestrator — it walks\n// files, calls shave() per-file, and dumps the registry manifest. All pipeline\n// logic stays in @yakcc/shave. This matches the (argv, logger) => Promise<number>\n// contract shared by all yakcc commands.\n//\n// @decision DEC-V2-BOOT-FILE-ORDER-001\n// @title Lexicographic file ordering is the canonical iteration order\n// @status accepted\n// @rationale Glob results are not order-stable across OSes (e.g. node:fs readdir\n// on Windows vs Linux). Sorting lexicographically before iteration ensures the\n// per-file processing order is identical across platforms, which preserves\n// deterministic registry insert order. The merkle root is order-independent\n// (content-addressed), but the report JSON and log output need stable order\n// for diff-friendliness.\n//\n// @decision DEC-V2-BOOT-NO-AI-CORPUS-001\n// @title Force-disable AI-derived corpus extraction in bootstrap mode\n// @status accepted\n// @rationale The corpus extractor has a source-C path that hits an AI cache.\n// Cache cold/warm transitions would make the bootstrap output non-deterministic\n// across runs (the load-bearing content-address invariant). Bootstrap mode forces\n// offline:true + intentStrategy:\"static\" so the extracted corpus comes only from\n// upstream tests + documented usage.\n// Status: implemented (WI-V2-BOOTSTRAP-02)\n//\n// @decision DEC-BOOTSTRAP-CORPUS-OPT-001\n// @title Inline offline/static-intent flags are sufficient; disableSourceC option not needed\n// @status accepted\n// @rationale A TODO anticipated adding ShaveOptions.corpusOptions.disableSourceC as a\n// more explicit mechanism to disable AI-derived corpus extraction. Option B (keep inline\n// flags, drop the TODO) was chosen: offline:true + intentStrategy:\"static\" already\n// provide the required determinism guarantee. The anticipated disableSourceC option adds\n// no safety beyond what the inline flags deliver, and introducing it would require\n// ShaveOptions API changes and consumer updates for zero practical benefit. The inline\n// mechanism is the permanent solution.\n//\n// @decision DEC-BOOTSTRAP-MANIFEST-ACCUMULATE-001\n// @title bootstrap/expected-roots.json is a monotonic accumulator — never shrinks\n// @status accepted\n// @rationale PR #280 deleted the in-house TS→WASM lowerer (~25,880 LoC). Atoms\n// from that source existed in the manifest on feature branches that never merged\n// to main. Re-running bootstrap against the current codebase would silently drop\n// them, violating the registry's monotonic invariant: atoms are never deleted.\n// Solution: `yakcc bootstrap` is now additive — prior entries absent from the\n// current shave are RETAINED; new entries are ADDED; the result is always the\n// superset sorted by blockMerkleRoot ASC. CI is the sole writer. Implementers\n// MUST NOT run `yakcc bootstrap` manually to update the manifest; CI handles it.\n// Three sub-decisions:\n// (a) expected-roots.json is a monotonic superset, never shrinks.\n// (b) --verify checks current_shave ⊆ committed_manifest (not byte-equality).\n// Archived atoms (in committed but not in current shave) are EXPECTED and\n// are NOT a failure. New atoms NOT in committed ARE a failure (named in output).\n// (c) CI is the sole writer of manifest updates going forward.\n\nimport { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative, resolve } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { contractIdFromBytes } from \"@yakcc/contracts\";\nimport type {\n BootstrapManifestEntry,\n Registry,\n RegistryOptions,\n SourceFileGlueEntry,\n} from \"@yakcc/registry\";\nimport { openRegistry } from \"@yakcc/registry\";\nimport { shave as shaveImpl } from \"@yakcc/shave\";\nimport type { Logger } from \"../index.js\";\nimport { PLUMBING_INCLUDE_GLOBS, plumbingPathAllowed } from \"./plumbing-globs.js\";\n\n// ---------------------------------------------------------------------------\n// Expected-failures schema\n//\n// @decision DEC-V2-BOOT-EXPECTED-FAILURES-001\n// @title expected-failures.json documents intentional fixture failure cases\n// @status accepted\n// @rationale Some fixture files are intentionally designed to fail shave (e.g.\n// a deliberately malformed fixture exercising DidNotReachAtomError or similar).\n// These must not contribute to the bootstrap failure count.\n// The expected-failures.json file (bootstrap/expected-failures.json) documents\n// each such case with path + errorClass + rationale. Both path and errorClass\n// must match for the reclassification to apply (path alone is insufficient —\n// a file that fails for a different reason is a real failure). If an entry\n// is never triggered, a WARNING is emitted: that either means the file was\n// renamed/deleted or the underlying issue was fixed, both of which warrant\n// removing the entry. Untriggered entries do NOT fail the bootstrap.\n// Note: LicenseRefusedError cases were the canonical example prior to\n// DEC-LICENSE-GATE-REMOVE-001 (WI-682, 2026-05-17); the mechanism is\n// general-purpose for any intentional-failure case.\n// ---------------------------------------------------------------------------\n\n/** One entry in expected-failures.json. */\ninterface ExpectedFailureEntry {\n readonly path: string; // repo-relative, matches outcomes[].path\n readonly errorClass: string; // constructor name, e.g. \"DidNotReachAtomError\"\n readonly rationale: string; // human-readable explanation\n}\n\n/** Top-level shape of bootstrap/expected-failures.json (schemaVersion: 1). */\ninterface ExpectedFailuresFile {\n readonly schemaVersion: 1;\n readonly entries: readonly ExpectedFailureEntry[];\n}\n\n/**\n * Load and parse expected-failures.json from the given path.\n * Returns an empty entry list if the file does not exist.\n * Throws if the file exists but is malformed (fast-fail: bad config is worse\n * than a missed exemption).\n */\nfunction loadExpectedFailures(filePath: string): readonly ExpectedFailureEntry[] {\n if (!existsSync(filePath)) return [];\n const raw = readFileSync(filePath, \"utf-8\");\n const parsed = JSON.parse(raw) as ExpectedFailuresFile;\n if (parsed.schemaVersion !== 1) {\n throw new Error(\n `expected-failures.json: unsupported schemaVersion ${String(parsed.schemaVersion)} (expected 1)`,\n );\n }\n if (!Array.isArray(parsed.entries)) {\n throw new Error(\"expected-failures.json: 'entries' must be an array\");\n }\n return parsed.entries;\n}\n\n// ---------------------------------------------------------------------------\n// Argument parsing\n// ---------------------------------------------------------------------------\n\nconst BOOTSTRAP_PARSE_OPTIONS = {\n registry: { type: \"string\" as const },\n report: { type: \"string\" as const },\n manifest: { type: \"string\" as const },\n \"expected-failures\": { type: \"string\" as const },\n verify: { type: \"boolean\" as const, default: false },\n help: { type: \"boolean\" as const, short: \"h\", default: false },\n} as const;\n\nconst DEFAULT_REGISTRY_PATH = join(\"bootstrap\", \"yakcc.registry.sqlite\");\nconst DEFAULT_MANIFEST_PATH = join(\"bootstrap\", \"expected-roots.json\");\nconst DEFAULT_REPORT_PATH = join(\"bootstrap\", \"report.json\");\nconst DEFAULT_EXPECTED_FAILURES_PATH = join(\"bootstrap\", \"expected-failures.json\");\n\n// Module-level TextEncoder: reused across all per-file hash computations in\n// the bootstrap loop (avoids constructing a new instance per file).\n// contractIdFromBytes accepts Uint8Array; this encoder converts UTF-8 strings.\n// @decision DEC-V2-SHAVE-CACHE-STORAGE-001\nconst TEXT_ENCODER = new TextEncoder();\n\n// ---------------------------------------------------------------------------\n// File-walking helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively collect all .ts files under a directory.\n * Does not follow symlinks.\n */\nfunction walkTs(dir: string, results: string[]): void {\n if (!existsSync(dir)) return;\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n walkTs(fullPath, results);\n } else if (entry.isFile() && entry.name.endsWith(\".ts\")) {\n results.push(fullPath);\n }\n }\n}\n\n/**\n * Determine if a file path should be excluded from the bootstrap walk.\n *\n * Filter rules (match WI-037 SPDX sweep logic):\n * - Skip *.test.ts\n * - Skip *.d.ts\n * - Skip vitest.config.ts\n * - Skip anything under __tests__/, __fixtures__/, __snapshots__/, node_modules/, dist/\n */\nfunction shouldSkip(absPath: string): boolean {\n const basename = absPath.split(/[\\\\/]/).pop() ?? \"\";\n\n // Skip by filename\n if (basename.endsWith(\".test.ts\")) return true;\n if (basename.endsWith(\".d.ts\")) return true;\n if (basename === \"vitest.config.ts\") return true;\n // *.props.ts are hand-authored property-test corpus files (WI-V2-07-L8).\n // They are consumed as corpus by the shave pipeline when processing the\n // sibling source file; they must not be shaved themselves.\n if (basename.endsWith(\".props.ts\")) return true;\n\n // Skip by directory segment — normalize to forward slashes for cross-platform matching\n const normalized = absPath.replace(/\\\\/g, \"/\");\n if (normalized.includes(\"/__tests__/\")) return true;\n if (normalized.includes(\"/__fixtures__/\")) return true;\n if (normalized.includes(\"/__snapshots__/\")) return true;\n if (normalized.includes(\"/node_modules/\")) return true;\n if (normalized.includes(\"/dist/\")) return true;\n\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Repo-root resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Search upward from startDir for a directory containing:\n * - pnpm-workspace.yaml (primary indicator for a monorepo root), OR\n * - package.json with \"name\": \"yakcc\" (fallback for single-package repos)\n *\n * Falls back to startDir if nothing is found at or above.\n */\nfunction findRepoRoot(startDir: string): string {\n let dir = startDir;\n for (let i = 0; i < 20; i++) {\n if (existsSync(join(dir, \"pnpm-workspace.yaml\"))) return dir;\n try {\n const pkgJson = readFileSync(join(dir, \"package.json\"), \"utf-8\");\n const parsed = JSON.parse(pkgJson) as { name?: string };\n if (parsed.name === \"yakcc\") return dir;\n } catch {\n // package.json missing or unparseable — continue upward\n }\n const parent = dirname(dir);\n if (parent === dir) break; // filesystem root\n dir = parent;\n }\n return startDir;\n}\n\n// ---------------------------------------------------------------------------\n// Bootstrap-mode embedding provider — deterministic zeros, no network access\n//\n// @decision DEC-V2-BOOTSTRAP-EMBEDDING-001\n// @title Bootstrap uses a zero-vector EmbeddingProvider to avoid network deps\n// @status accepted\n// @rationale exportManifest() does not read the embeddings table — the manifest\n// only contains content-addressed fields (blockMerkleRoot, specHash, etc).\n// Using the real local embedding provider (Xenova/all-MiniLM-L6-v2) would\n// require a huggingface.co download that is unavailable in sandboxed CI and\n// offline environments, and would add non-determinism risk if the model\n// version ever changes. A deterministic zero vector is correct for bootstrap:\n// it satisfies the registry's embedding column constraint, cannot affect the\n// content-address invariant, and makes bootstrap fully reproducible everywhere.\n// ---------------------------------------------------------------------------\n\nconst BOOTSTRAP_EMBEDDING_OPTS: Pick<RegistryOptions, \"embeddings\"> = {\n embeddings: {\n dimension: 384,\n modelId: \"bootstrap/null-zero\",\n embed: async (_text: string): Promise<Float32Array> => new Float32Array(384),\n },\n};\n\n// ---------------------------------------------------------------------------\n// Per-file outcome types\n// ---------------------------------------------------------------------------\n\ninterface FileOutcomeSuccess {\n readonly path: string;\n readonly outcome: \"success\";\n readonly atomCount: number;\n readonly intentCardCount: number;\n}\n\ninterface FileOutcomeFailure {\n readonly path: string;\n readonly outcome: \"failure\";\n readonly errorClass: string;\n readonly errorMessage: string;\n}\n\n/**\n * A failure that was reclassified as an expected-failure because it matches an\n * entry in expected-failures.json (path + errorClass both match).\n * These are surfaced in the summary but do NOT count toward the failure total\n * and do NOT cause a non-zero exit code.\n */\ninterface FileOutcomeExpectedFailure {\n readonly path: string;\n readonly outcome: \"expected-failure\";\n readonly errorClass: string;\n readonly errorMessage: string;\n /** The rationale string from the matching expected-failures.json entry. */\n readonly rationale: string;\n}\n\n/**\n * A file that was skipped because its content hash matched the stored value in\n * source_file_state — the file's bytes are identical to the last successful shave.\n *\n * @decision DEC-V2-SHAVE-CACHE-STORAGE-001 — source_file_state is the cache authority.\n * @decision DEC-V2-SHAVE-CACHE-VERIFY-FLAG-001 — cache hits only occur when !--verify.\n */\ninterface FileOutcomeCacheHit {\n readonly path: string;\n readonly outcome: \"cache-hit\";\n /** Number of atom occurrences already in block_occurrences for this file. */\n readonly atomCount: number;\n}\n\ntype FileOutcome =\n | FileOutcomeSuccess\n | FileOutcomeFailure\n | FileOutcomeExpectedFailure\n | FileOutcomeCacheHit;\n\n// ---------------------------------------------------------------------------\n// VerifyDiff — structured diff result for --verify mode\n// ---------------------------------------------------------------------------\n\ninterface VerifyDiff {\n readonly addedRoots: ReadonlyArray<{ merkleRoot: string; sourcePath: string | null }>;\n readonly removedRoots: ReadonlyArray<{ merkleRoot: string }>;\n}\n\n// ---------------------------------------------------------------------------\n// mergeManifestEntries() — additive merge of prior + shaved entries\n//\n// @decision DEC-BOOTSTRAP-MANIFEST-ACCUMULATE-001 (see file header)\n// @title Additive manifest merge — union keyed on blockMerkleRoot, sorted ASC\n// @status accepted\n// @rationale Prior entries absent from the current shave are retained (archived\n// atoms from deleted branches/PRs must not be lost). New entries are added.\n// The superset is sorted by blockMerkleRoot ASC for diff-stability.\n// ---------------------------------------------------------------------------\n\n/**\n * Merge prior manifest entries with fresh shave entries into a monotonic superset.\n *\n * - Entries from `prior` absent in `shaved` are RETAINED (archived atoms).\n * - Entries from `shaved` absent in `prior` are ADDED.\n * - Duplicate roots (same blockMerkleRoot) → prior entry wins (stable identity).\n * - Result is sorted by blockMerkleRoot ASC.\n *\n * @param prior - Entries already committed to the manifest (may be empty).\n * @param shaved - Entries produced by the current shave run (may be empty).\n * @returns The merged superset sorted by blockMerkleRoot ASC.\n */\nexport function mergeManifestEntries(\n prior: ReadonlyArray<BootstrapManifestEntry>,\n shaved: ReadonlyArray<BootstrapManifestEntry>,\n): Array<BootstrapManifestEntry> {\n // Build a map keyed by blockMerkleRoot. Prior wins on collision (stable identity).\n const merged = new Map<string, BootstrapManifestEntry>();\n for (const entry of prior) {\n merged.set(entry.blockMerkleRoot, entry);\n }\n for (const entry of shaved) {\n if (!merged.has(entry.blockMerkleRoot)) {\n merged.set(entry.blockMerkleRoot, entry);\n }\n }\n // Sort by blockMerkleRoot ASC for deterministic, diff-stable output.\n return [...merged.values()].sort((a, b) => a.blockMerkleRoot.localeCompare(b.blockMerkleRoot));\n}\n\n// ---------------------------------------------------------------------------\n// collectSourceFiles() — shared file-walk used by both modes\n// ---------------------------------------------------------------------------\n\nfunction collectSourceFiles(repoRoot: string): string[] {\n const rawFiles: string[] = [];\n for (const topDir of [\"packages\", \"examples\"]) {\n const topAbs = join(repoRoot, topDir);\n if (!existsSync(topAbs)) continue;\n\n const pkgDirs = readdirSync(topAbs, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => join(topAbs, e.name, \"src\"));\n\n for (const srcDir of pkgDirs) {\n walkTs(srcDir, rawFiles);\n }\n }\n return rawFiles.filter((f) => !shouldSkip(f)).sort();\n}\n\n// ---------------------------------------------------------------------------\n// runVerify() — --verify mode implementation\n//\n// @decision DEC-V2-BOOTSTRAP-VERIFY-001\n// @title verify mode uses :memory: registry and superset gate\n// @status accepted (amended by DEC-BOOTSTRAP-MANIFEST-ACCUMULATE-001)\n// @rationale Original: byte-identity gate. Amended: superset gate.\n// The committed manifest is a monotonic accumulator (DEC-BOOTSTRAP-MANIFEST-ACCUMULATE-001).\n// Archived atoms (in committed, not in current shave) are EXPECTED — they came\n// from branches/PRs that were deleted after their atoms were recorded. They must\n// NOT cause a verify failure. Only atoms in the current shave that are ABSENT\n// from the committed manifest are a failure (unrecorded new atoms).\n//\n// Semantics:\n// PASS — current_shave ⊆ committed_manifest\n// PASS — committed_manifest has strictly MORE entries (archived atoms OK)\n// FAIL — current_shave has atom(s) NOT in committed_manifest\n// → error message names each missing root\n// ---------------------------------------------------------------------------\n\nasync function runVerify(\n committedManifestPath: string,\n repoRoot: string,\n logger: Logger,\n): Promise<number> {\n // Read committed manifest.\n if (!existsSync(committedManifestPath)) {\n logger.error(\n `error: committed manifest not found at ${committedManifestPath}. Run 'yakcc bootstrap' first to generate it.`,\n );\n return 1;\n }\n const committedText = readFileSync(committedManifestPath, \"utf-8\");\n\n // Collect source files (same walk as normal mode).\n const sourceFiles = collectSourceFiles(repoRoot);\n if (sourceFiles.length === 0) {\n logger.error(\n `error: no source files found under ${repoRoot}. Expected packages/*/src/**/*.ts or examples/*/src/**/*.ts.`,\n );\n return 1;\n }\n\n // Open a fresh :memory: registry with zero-embedding provider (DEC-V2-BOOTSTRAP-EMBEDDING-001).\n let registry: Registry;\n try {\n registry = await openRegistry(\":memory:\", BOOTSTRAP_EMBEDDING_OPTS);\n } catch (err) {\n logger.error(`error: failed to open in-memory registry: ${(err as Error).message}`);\n return 1;\n }\n\n const shaveRegistry = {\n selectBlocks: (specHash: Parameters<typeof registry.selectBlocks>[0]) =>\n registry.selectBlocks(specHash),\n getBlock: async (merkleRoot: Parameters<typeof registry.getBlock>[0]) => {\n const row = await registry.getBlock(merkleRoot);\n return row ?? undefined;\n },\n findByCanonicalAstHash: registry.findByCanonicalAstHash?.bind(registry),\n storeBlock: registry.storeBlock?.bind(registry),\n };\n\n // Shave all files, tracking which source path produced each merkle root.\n const rootToSource = new Map<string, string>();\n\n for (const absPath of sourceFiles) {\n const relPath = relative(repoRoot, absPath);\n try {\n const result = await shaveImpl(absPath, shaveRegistry, {\n offline: true,\n intentStrategy: \"static\",\n });\n for (const atom of result.atoms) {\n if (atom.merkleRoot !== undefined) {\n rootToSource.set(atom.merkleRoot, relPath);\n }\n }\n } catch {\n // Shave errors mean that source file produced no atoms. Do not abort.\n }\n }\n\n // Export the deterministic manifest from :memory: registry.\n let freshManifest: readonly BootstrapManifestEntry[];\n try {\n freshManifest = await registry.exportManifest();\n } catch (err) {\n logger.error(`error: failed to export fresh manifest: ${(err as Error).message}`);\n await registry.close();\n return 1;\n }\n await registry.close();\n\n // Parse the committed manifest.\n const committedManifest = JSON.parse(committedText) as BootstrapManifestEntry[];\n const committedRoots = new Set(committedManifest.map((e) => e.blockMerkleRoot));\n const freshRoots = new Set(freshManifest.map((e) => e.blockMerkleRoot));\n\n // Superset gate (DEC-BOOTSTRAP-MANIFEST-ACCUMULATE-001 part (b)):\n // PASS — every fresh root is already in committed (current_shave ⊆ committed)\n // FAIL — any fresh root is NOT in committed (unrecorded new atoms)\n //\n // Archived atoms (in committed, not in fresh) are expected and not reported.\n const unrecordedRoots: Array<{ merkleRoot: string; sourcePath: string | null }> = [...freshRoots]\n .filter((r) => !committedRoots.has(r))\n .map((r) => ({ merkleRoot: r, sourcePath: rootToSource.get(r) ?? null }));\n\n if (unrecordedRoots.length === 0) {\n // All current atoms are in the committed manifest — PASS.\n const archivedCount = [...committedRoots].filter((r) => !freshRoots.has(r)).length;\n if (archivedCount > 0) {\n logger.log(\n `bootstrap --verify: OK (${freshManifest.length} shaved ⊆ ${committedManifest.length} committed; ${archivedCount} archived atoms retained)`,\n );\n } else {\n logger.log(`bootstrap --verify: OK (${committedManifest.length} entries)`);\n }\n return 0;\n }\n\n // FAIL — current shave produced atoms not recorded in the committed manifest.\n // Name every missing root explicitly (Sacred Practice #5: loud failure).\n logger.error(\"bootstrap --verify: FAILED\");\n logger.error(` committed: ${committedManifestPath} (${committedManifest.length} entries)`);\n logger.error(` shaved: ${freshManifest.length} entries`);\n logger.error(\n `\\nUnrecorded atoms (${unrecordedRoots.length} — in current shave, NOT in committed manifest):`,\n );\n logger.error(\" Fix: run 'yakcc bootstrap' to record these atoms, then commit the manifest.\");\n\n // Group by source path for readability.\n const bySource = new Map<string, string[]>();\n for (const { merkleRoot, sourcePath } of unrecordedRoots) {\n const key = sourcePath ?? \"(unknown source)\";\n const list = bySource.get(key) ?? [];\n list.push(merkleRoot);\n bySource.set(key, list);\n }\n for (const [sourcePath, roots] of bySource) {\n logger.error(` ${sourcePath}:`);\n for (const root of roots) {\n logger.error(` + ${root}`);\n }\n }\n\n return 1;\n}\n\n// ---------------------------------------------------------------------------\n// captureWorkspacePlumbing — bootstrap plumbing capture pass (P2)\n//\n// @decision DEC-V2-WORKSPACE-PLUMBING-CAPTURE-001\n// @title Bootstrap captures plumbing files via a single named glob set\n// @status decided (WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P2)\n// @rationale The glob constant lives in plumbing-globs.ts (single authority).\n// This function performs the matching and calls registry.storeWorkspacePlumbing\n// for each matching file. Errors are reported loudly — never silently dropped\n// (Sacred Practice #5). Idempotent: re-running bootstrap on an existing registry\n// is a no-op for rows already present (INSERT OR IGNORE semantics).\n// ---------------------------------------------------------------------------\n\n/**\n * Expand a workspace-plumbing glob pattern to matching file paths.\n *\n * Uses simple manual expansion: splits the glob on '/' and matches\n * directory segments that contain '*' (single-segment wildcard only —\n * sufficient for the PLUMBING_INCLUDE_GLOBS patterns).\n *\n * Returns workspace-relative forward-slash paths.\n */\nfunction expandPlumbingGlob(pattern: string, repoRoot: string): string[] {\n const segments = pattern.split(\"/\");\n const results: string[] = [];\n\n function walk(segIdx: number, currentDir: string, currentRel: string): void {\n if (segIdx === segments.length) {\n // Base case: check the file exists.\n if (existsSync(currentDir) && statSync(currentDir).isFile()) {\n results.push(currentRel);\n }\n return;\n }\n\n const seg = segments[segIdx];\n if (seg === undefined) return;\n\n if (seg.includes(\"*\")) {\n // Wildcard segment: enumerate the current directory.\n if (!existsSync(currentDir)) return;\n let entries: import(\"node:fs\").Dirent[];\n try {\n // @decision DEC-V2-PLUMBING-WALK-DETERMINISM-001\n // @title expandPlumbingGlob sorts readdir results before walking\n // @status accepted (WI-FIX-494-TWOPASS-NONDETERM)\n // @rationale Aligns with the \"sort before iterate\" convention at\n // seeds/src/seed.ts:75 and bootstrap.ts:387. Eliminates latent\n // platform-readdir-order risk even though registry SELECTs are\n // ORDER BY workspace_path ASC and currently make order non-load-bearing.\n entries = (\n readdirSync(currentDir, { withFileTypes: true }) as import(\"node:fs\").Dirent[]\n ).sort((a, b) => a.name.localeCompare(b.name));\n } catch {\n return;\n }\n // Build a simple regex from the glob segment (* = any non-slash chars).\n const regexSrc = `^${seg.replace(/\\./g, \"\\\\.\").replace(/\\*/g, \"[^/]*\")}$`;\n const re = new RegExp(regexSrc);\n for (const entry of entries) {\n if (re.test(entry.name)) {\n const childAbs = join(currentDir, entry.name);\n const childRel = currentRel ? `${currentRel}/${entry.name}` : entry.name;\n walk(segIdx + 1, childAbs, childRel);\n }\n }\n } else {\n // Literal segment.\n const childAbs = join(currentDir, seg);\n const childRel = currentRel ? `${currentRel}/${seg}` : seg;\n walk(segIdx + 1, childAbs, childRel);\n }\n }\n\n walk(0, repoRoot, \"\");\n return results;\n}\n\n/**\n * Capture workspace plumbing files into the registry.\n *\n * Uses PLUMBING_INCLUDE_GLOBS from plumbing-globs.ts as the single authority\n * for which files constitute \"workspace plumbing\". Each matched file is\n * content-hashed (BLAKE3-256) and stored via registry.storeWorkspacePlumbing.\n *\n * Idempotent: INSERT OR IGNORE semantics in the registry mean re-running\n * bootstrap against an existing registry is a no-op for already-captured rows.\n *\n * @decision DEC-V2-WORKSPACE-PLUMBING-CAPTURE-001\n *\n * @param registry - Open registry instance (storeWorkspacePlumbing will be called).\n * @param repoRoot - Absolute path to the workspace root.\n * @param logger - Output sink.\n * @returns Number of plumbing files captured (new rows inserted, not no-ops).\n */\nasync function captureWorkspacePlumbing(\n registry: Registry,\n repoRoot: string,\n logger: Logger,\n): Promise<number> {\n // Expand all inclusion globs to concrete file paths.\n const seen = new Set<string>(); // deduplicate across globs\n const candidates: string[] = [];\n for (const glob of PLUMBING_INCLUDE_GLOBS) {\n const expanded = expandPlumbingGlob(glob, repoRoot);\n for (const relPath of expanded) {\n if (!seen.has(relPath)) {\n seen.add(relPath);\n candidates.push(relPath);\n }\n }\n }\n\n // Filter by exclusion rules (single authority in plumbing-globs.ts).\n const toCapture = candidates.filter((p) => plumbingPathAllowed(p));\n\n let capturedCount = 0;\n for (const relPath of toCapture) {\n const absPath = join(repoRoot, relPath);\n let bytes: Buffer;\n try {\n bytes = readFileSync(absPath);\n } catch (err) {\n // File exists (was expanded) but cannot be read — loud failure.\n logger.error(\n `warning: bootstrap plumbing: cannot read ${relPath}: ${(err as Error).message}`,\n );\n continue;\n }\n\n const contentBytes = new Uint8Array(bytes);\n // contractIdFromBytes(bytes) = BLAKE3-256(bytes) → hex, same hash used by\n // storage.ts to verify contentHash. No direct @noble/hashes dep in @yakcc/cli\n // (sacred-practice #12: single dependency path via @yakcc/contracts).\n const contentHash: string = contractIdFromBytes(contentBytes);\n\n try {\n await registry.storeWorkspacePlumbing({\n workspacePath: relPath,\n contentBytes,\n contentHash,\n createdAt: Date.now(),\n });\n capturedCount++;\n } catch (err) {\n // storeWorkspacePlumbing only throws on integrity failure — loud failure.\n logger.error(\n `error: bootstrap plumbing: failed to store ${relPath}: ${(err as Error).message}`,\n );\n }\n }\n\n logger.log(\n `bootstrap: workspace plumbing captured — ${toCapture.length} candidates, ${capturedCount} stored`,\n );\n return capturedCount;\n}\n\n// ---------------------------------------------------------------------------\n// captureSourceFileGlue — per-file glue capture pass (#333)\n//\n// @decision DEC-V2-GLUE-CAPTURE-AUTHORITY-001\n// @title Per-file glue stored as single concatenated blob; boundaries derived\n// from DB-resident atoms for this sourceFile (not live shave stubs)\n// @status decided (WI-V2-WORKSPACE-PLUMBING-GLUE-CAPTURE #333)\n// @rationale\n// Glue = all non-atom byte regions of the source file as seen by the\n// reconstruction algorithm. The reconstruction algorithm queries atoms by\n// (source_pkg, source_file) from the registry. Therefore the glue blob must\n// be the complement of those DB atoms — NOT the complement of all live shave\n// atoms.\n//\n// Root cause of the original bug: live shave stubs include PointerEntry atoms\n// (already in DB from another file's first-observed-wins INSERT OR IGNORE).\n// PointerEntry stubs have merkleRoot=undefined — the slicer never propagates\n// the existing DB merkleRoot to ShavedAtomStub for pointer entries. There is\n// also no way to distinguish a PointerEntry that belongs to THIS file vs one\n// that belongs to ANOTHER file using only the stub (both have merkleRoot=undefined).\n//\n// Attempting to filter stubs by merkleRoot!==undefined (novel atoms only) also\n// fails: when re-bootstrapping a file whose atoms are already in the DB, ALL\n// atoms are PointerEntries, so storedAtoms = [] and the entire file is treated\n// as glue (storing the full 14k-char source as a blob).\n//\n// FIX: query registry.getAtomRangesBySourceFile(sourceFile) AFTER Pass A\n// completes (so all novel atoms for this file are in the DB). This returns\n// exactly the ranges that reconstruction will use. Glue = complement of those\n// ranges. INSERT OR IGNORE on storeBlock means each atom retains the sourceFile\n// from the first bootstrap run that stored it, which is consistent with what\n// getAtomRangesBySourceFile returns for this file.\n//\n// The file is read as UTF-8 text (matching shave/src/index.ts which reads\n// with readFile(path, \"utf-8\")). Atom ranges are JS string character offsets\n// (not byte offsets). The glue blob is encoded as UTF-8 bytes for storage.\n// ---------------------------------------------------------------------------\n\n/**\n * Compute the concatenated glue blob for a single source file.\n *\n * Glue = regions of the file NOT covered by any atom range, concatenated in\n * source order:\n * glue_blob = F[0..A1.start) ++ F[A1.end..A2.start) ++ ... ++ F[An.end..fileLen)\n *\n * Input ranges must be pre-sorted ascending by start (caller's responsibility).\n * Overlapping/adjacent ranges are merged before computing gaps.\n *\n * Returns null when there are no glue regions (all bytes are covered by atoms,\n * or the source string is empty). A null result means the caller should skip\n * the storeSourceFileGlue call for this file.\n *\n * @param source - Source file content as a JS string (read with utf-8 encoding).\n * @param atomRanges - Sorted atom intervals: [{start, end}], where end = start + implSourceLength.\n * These must be the DB-resident ranges for this sourceFile\n * (from getAtomRangesBySourceFile), NOT live shave stubs.\n * @returns UTF-8-encoded glue blob, or null if there are no glue bytes.\n */\nexport function computeGlueBlob(\n source: string,\n atomRanges: readonly { readonly start: number; readonly end: number }[],\n): Uint8Array | null {\n const encoder = new TextEncoder();\n\n if (atomRanges.length === 0) {\n // No atoms stored for this file — entire file is glue.\n const blob = encoder.encode(source);\n return blob.byteLength > 0 ? blob : null;\n }\n\n // Merge overlapping or adjacent intervals to avoid double-counting.\n // Input is pre-sorted by start (getAtomRangesBySourceFile ORDER BY source_offset ASC).\n const merged: Array<{ start: number; end: number }> = [];\n for (const range of atomRanges) {\n const last = merged[merged.length - 1];\n if (last !== undefined && range.start <= last.end) {\n // Extend the last interval if this one reaches further.\n if (range.end > last.end) last.end = range.end;\n } else {\n merged.push({ start: range.start, end: range.end });\n }\n }\n\n // Build glue spans: gaps before, between, and after atom intervals.\n const glueParts: string[] = [];\n\n let cursor = 0;\n for (const { start, end } of merged) {\n if (cursor < start) {\n glueParts.push(source.slice(cursor, start));\n }\n cursor = end;\n }\n // Trailing span: [lastAtom.end .. fileLen)\n if (cursor < source.length) {\n glueParts.push(source.slice(cursor));\n }\n\n if (glueParts.length === 0) return null;\n\n const glueText = glueParts.join(\"\");\n if (glueText.length === 0) return null;\n\n return encoder.encode(glueText);\n}\n\n/**\n * Capture source-file glue (non-atom regions) for a single source file.\n *\n * Reads the file, computes the glue blob from DB-resident atom ranges for this\n * sourceFile, and stores it via registry.storeSourceFileGlue. No-ops when the\n * file has no glue bytes (all content is covered by atoms for this file).\n *\n * Must be called AFTER Pass A (atom persistence) so getAtomRangesBySourceFile\n * reflects all atoms stored with sourceFile = this file.\n *\n * @decision DEC-V2-GLUE-CAPTURE-AUTHORITY-001\n *\n * @param registry - Open registry instance.\n * @param absPath - Absolute path to the source file (for fs.readFileSync).\n * @param sourcePkg - Workspace package dir (e.g. 'packages/cli').\n * @param sourceFile - Workspace-relative path (e.g. 'packages/cli/src/commands/foo.ts').\n * @param logger - Output sink.\n * @returns true if a glue row was stored, false if skipped (no glue bytes).\n */\nasync function captureSourceFileGlue(\n registry: Registry,\n absPath: string,\n sourcePkg: string,\n sourceFile: string,\n logger: Logger,\n): Promise<boolean> {\n // Read the source file content (UTF-8 string, matching shave pipeline).\n let source: string;\n try {\n source = readFileSync(absPath, \"utf-8\");\n } catch (err) {\n logger.error(\n `warning: bootstrap glue: cannot read ${sourceFile} for glue capture: ${(err as Error).message}`,\n );\n return false;\n }\n\n // Query DB for atom ranges stored with this sourceFile.\n // This is the authoritative source — it matches what reconstruction will use.\n let atomRanges: readonly { readonly sourceOffset: number; readonly implSourceLength: number }[];\n try {\n atomRanges = await registry.getAtomRangesBySourceFile(sourceFile);\n } catch (err) {\n logger.error(\n `warning: bootstrap glue: cannot query atom ranges for ${sourceFile}: ${(err as Error).message}`,\n );\n return false;\n }\n\n // Convert to {start, end} format for computeGlueBlob.\n // Already sorted ascending by sourceOffset (getAtomRangesBySourceFile ORDER BY source_offset ASC).\n const ranges = atomRanges.map((r) => ({\n start: r.sourceOffset,\n end: r.sourceOffset + r.implSourceLength,\n }));\n\n const glueBlob = computeGlueBlob(source, ranges);\n if (glueBlob === null) {\n // No glue bytes — all content is atoms. Skip silently.\n return false;\n }\n\n const contentHash: string = contractIdFromBytes(glueBlob);\n\n const entry: SourceFileGlueEntry = {\n sourcePkg,\n sourceFile,\n contentHash,\n contentBlob: glueBlob,\n createdAt: Date.now(),\n };\n\n try {\n await registry.storeSourceFileGlue(entry);\n return true;\n } catch (err) {\n logger.error(\n `error: bootstrap glue: failed to store glue for ${sourceFile}: ${(err as Error).message}`,\n );\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// bootstrap() — public command handler\n// ---------------------------------------------------------------------------\n\n/**\n * Handler for `yakcc bootstrap [--registry <p>] [--manifest <p>] [--report <p>]`.\n *\n * Walks source files in the repo, shaves each through the offline\n * static-intent pipeline, and writes:\n * - A per-file outcome report (--report)\n * - The deterministic registry manifest (--manifest)\n *\n * Exits 0 only if all files shave successfully. Any failure → exit 1.\n *\n * @param argv - Subcommand args after \"bootstrap\" has been consumed.\n * @param logger - Output sink; defaults to CONSOLE_LOGGER via the caller.\n * @returns Promise<number> — 0 on success, 1 on error.\n */\nexport async function bootstrap(argv: ReadonlyArray<string>, logger: Logger): Promise<number> {\n // Parse arguments.\n const parsed = (() => {\n try {\n return parseArgs({\n args: [...argv],\n allowPositionals: false,\n options: BOOTSTRAP_PARSE_OPTIONS,\n });\n } catch (err) {\n logger.error(`error: ${(err as Error).message}`);\n return null;\n }\n })();\n if (parsed === null) return 1;\n\n if (parsed.values.help) {\n logger.log(\n [\n \"Usage: yakcc bootstrap [--registry <path>] [--manifest <path>] [--report <path>] [--verify]\",\n \"\",\n \" Walk packages/*/src/**/*.ts and examples/*/src/**/*.ts, shave each file\",\n \" offline, and additively merge results into the committed manifest.\",\n \"\",\n \" The manifest (bootstrap/expected-roots.json) is a monotonic accumulator:\",\n \" atoms from prior runs are RETAINED even if deleted from source.\",\n \" CI is the sole writer — do not run 'yakcc bootstrap' manually.\",\n \"\",\n \" --registry <path> Registry SQLite path (default: bootstrap/yakcc.registry.sqlite)\",\n \" --manifest <path> Manifest path: read+write in normal mode, committed reference in --verify\",\n \" (default: bootstrap/expected-roots.json)\",\n \" --report <path> Per-file outcome report (default: bootstrap/report.json)\",\n \" --expected-failures <path> Expected-failures exemption list (default: bootstrap/expected-failures.json)\",\n \" Entries with matching path+errorClass are reclassified as expected-failures\",\n \" and do NOT count toward the failure total.\",\n \" --verify Shave into :memory: registry and check current_shave ⊆ committed manifest.\",\n \" PASS if all shaved atoms are in committed (archived atoms OK).\",\n \" FAIL if any shaved atom is absent from committed (names missing roots).\",\n \" -h, --help Print this help and exit\",\n ].join(\"\\n\"),\n );\n return 0;\n }\n\n const manifestPath = resolve(parsed.values.manifest ?? DEFAULT_MANIFEST_PATH);\n\n // Resolve the repo root.\n const repoRoot = findRepoRoot(process.cwd());\n\n // --verify mode: delegate to runVerify() which uses :memory: and byte-compares.\n if (parsed.values.verify) {\n return runVerify(manifestPath, repoRoot, logger);\n }\n\n const registryPath = resolve(parsed.values.registry ?? DEFAULT_REGISTRY_PATH);\n const reportPath = resolve(parsed.values.report ?? DEFAULT_REPORT_PATH);\n\n // Load expected-failures exemption list (DEC-V2-BOOT-EXPECTED-FAILURES-001).\n // Resolve relative to repoRoot so the default path works regardless of cwd.\n const expectedFailuresPath = resolve(\n repoRoot,\n parsed.values[\"expected-failures\"] ?? DEFAULT_EXPECTED_FAILURES_PATH,\n );\n let expectedFailures: readonly ExpectedFailureEntry[];\n try {\n expectedFailures = loadExpectedFailures(expectedFailuresPath);\n } catch (err) {\n logger.error(\n `error: failed to load expected-failures file at ${expectedFailuresPath}: ${(err as Error).message}`,\n );\n return 1;\n }\n\n // Build a lookup set keyed by \"path\\0errorClass\" for O(1) matching.\n const expectedFailureKeys = new Map<string, ExpectedFailureEntry>();\n for (const entry of expectedFailures) {\n expectedFailureKeys.set(`${entry.path}\\0${entry.errorClass}`, entry);\n }\n\n // Collect source files (lexicographic, DEC-V2-BOOT-FILE-ORDER-001).\n const sourceFiles = collectSourceFiles(repoRoot);\n\n if (sourceFiles.length === 0) {\n logger.error(\n `error: no source files found under ${repoRoot}. Expected packages/*/src/**/*.ts or examples/*/src/**/*.ts.`,\n );\n return 1;\n }\n\n // Ensure output parent directories exist.\n for (const outputPath of [registryPath, manifestPath, reportPath]) {\n mkdirSync(dirname(outputPath), { recursive: true });\n }\n\n // Open registry with zero-embedding provider (DEC-V2-BOOTSTRAP-EMBEDDING-001).\n let registry: Registry;\n try {\n registry = await openRegistry(registryPath, BOOTSTRAP_EMBEDDING_OPTS);\n } catch (err) {\n logger.error(`error: failed to open registry at ${registryPath}: ${(err as Error).message}`);\n return 1;\n }\n\n // Adapt Registry → ShaveRegistryView (same pattern as shave.ts lines ~79-87).\n //\n // @decision DEC-V2-OCCURRENCE-DELETE-INSERT-001\n // storeBlock is wrapped (not bound) so the bootstrap can intercept each novel\n // atom's (blockMerkleRoot, sourceOffset, length) for block_occurrences storage.\n // The wrapper captures occurrences into a per-file mutable array that is drained\n // after shave() returns and before captureSourceFileGlue() runs.\n //\n // Novel atoms are captured here. PointerEntry atoms (already in the DB) are\n // resolved after shave by reading stub.merkleRoot directly — shave() now\n // propagates PointerEntry.merkleRoot to ShavedAtomStub (DEC-SHAVE-POINTER-MERKLROOT-PROPAGATION-001).\n // This ensures block_occurrences reflects the current-truth offsets for ALL atoms\n // in the file, regardless of whether they are novel or pre-existing in the DB.\n //\n // @decision DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001\n\n // Per-file occurrence accumulator. Populated by the storeBlock wrapper below\n // and by the PointerEntry resolution pass after shave. Drained per file.\n type OccurrenceRecord = {\n sourcePkg: string;\n sourceFile: string;\n sourceOffset: number;\n length: number;\n blockMerkleRoot: string;\n };\n let perFileOccurrences: OccurrenceRecord[] = [];\n\n const shaveRegistry = {\n selectBlocks: (specHash: Parameters<typeof registry.selectBlocks>[0]) =>\n registry.selectBlocks(specHash),\n getBlock: async (merkleRoot: Parameters<typeof registry.getBlock>[0]) => {\n const row = await registry.getBlock(merkleRoot);\n return row ?? undefined;\n },\n findByCanonicalAstHash: registry.findByCanonicalAstHash?.bind(registry),\n storeBlock: async (row: Parameters<typeof registry.storeBlock>[0]): Promise<void> => {\n // Forward the store to the real registry first.\n await registry.storeBlock(row);\n // Capture the occurrence if sourceContext provenance is present.\n // sourceOffset is set per-atom inside shave() from entry.sourceRange.start.\n // Both sourceOffset and length are JS string character counts (not byte counts) —\n // the glue/reconstruct algorithms use source.slice() which is character-based.\n if (row.sourceFile != null && row.sourcePkg != null && row.sourceOffset != null) {\n perFileOccurrences.push({\n sourcePkg: row.sourcePkg,\n sourceFile: row.sourceFile,\n sourceOffset: row.sourceOffset,\n length: row.implSource.length, // character count, not byte count\n blockMerkleRoot: row.blockMerkleRoot,\n });\n }\n },\n };\n\n // Process each file.\n const rawOutcomes: FileOutcome[] = [];\n\n for (const absPath of sourceFiles) {\n const relPath = relative(repoRoot, absPath);\n try {\n // Compute source provenance context for registry storage.\n // sourcePkg: the package directory (e.g. 'packages/cli'), derived from the\n // workspace-relative path by taking the first two segments (topDir/pkgName).\n // sourceFile: the full workspace-relative path (e.g. 'packages/cli/src/commands/foo.ts').\n // sourceOffset: computed per-atom from entry.sourceRange.start in shave() internals.\n // @decision DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001\n const relSegments = relPath.replace(/\\\\/g, \"/\").split(\"/\");\n // relPath format: \"packages/<pkg>/src/...\" or \"examples/<pkg>/src/...\"\n // sourcePkg is the first two segments (e.g. \"packages/cli\").\n const sourcePkg =\n relSegments.length >= 2 ? `${relSegments[0]}/${relSegments[1]}` : (relSegments[0] ?? \"\");\n\n // Reset per-file occurrence accumulator before shave.\n perFileOccurrences = [];\n\n // Force offline: true to disable AI-corpus extraction (DEC-V2-BOOT-NO-AI-CORPUS-001).\n // @decision DEC-BOOTSTRAP-CORPUS-OPT-001 — inline flags are the permanent solution.\n const sourceFileNorm = relPath.replace(/\\\\/g, \"/\");\n // Read source text for exact-match comparison in the pointer stub pass below.\n // Canonical-AST matching can unify atoms from different files whose textual\n // representations differ (e.g. variable names). When recording pointer\n // occurrences, we skip atoms whose stored impl_source doesn't match the\n // actual source text at that position — those spans are better captured as\n // glue so that reconstruction emits the correct verbatim text.\n // DEC-V2-POINTER-OCCURRENCE-LENGTH-001\n const sourceText = readFileSync(absPath, \"utf-8\");\n\n // ---------------------------------------------------------------------------\n // Cache-check (issue #363 / DEC-V2-SHAVE-CACHE-STORAGE-001)\n //\n // Compute BLAKE3-256 of the source file's UTF-8 bytes. The hash is computed\n // AFTER readFileSync above — both the cache check and the subsequent shave\n // operate on the same in-memory string, so there is no TOCTOU race.\n //\n // fileContentHash is hoisted outside the if-block so it is available after\n // a successful shave to write back to source_file_state (cache miss write).\n //\n // @decision DEC-V2-SHAVE-CACHE-VERIFY-FLAG-001\n // --verify bypasses the cache entirely; the guard is here so that future\n // refactors cannot accidentally cache under --verify. Today --verify uses\n // a :memory: registry and never reaches this path; the guard is forward-safety.\n //\n // @decision DEC-V2-SHAVE-CACHE-STORAGE-001\n // Cache key = BLAKE3-256 hex of UTF-8 bytes. NOT mtime/size/ctime.\n // The single call site contract: exactly one getSourceFileContentHash() call\n // per source file per bootstrap run (evaluation contract RP-3 / forbidden shortcut).\n // ---------------------------------------------------------------------------\n const fileContentHash = contractIdFromBytes(TEXT_ENCODER.encode(sourceText));\n if (!parsed.values.verify) {\n const storedHash = await registry.getSourceFileContentHash(sourcePkg, sourceFileNorm);\n\n if (storedHash === fileContentHash) {\n // Cache hit: byte-identical to last shave — skip full shave.\n // Atoms are already in blocks + block_occurrences from the prior run.\n const occurrences = await registry.listOccurrencesBySourceFile(sourceFileNorm);\n rawOutcomes.push({\n path: relPath,\n outcome: \"cache-hit\",\n atomCount: occurrences.length,\n });\n continue;\n }\n // Cache miss: fall through to full shave below.\n // After a successful shave, storeSourceFileContentHash is called to record\n // the current hash so the next run can cache-hit this file.\n }\n\n const result = await shaveImpl(absPath, shaveRegistry, {\n offline: true,\n intentStrategy: \"static\",\n sourceContext: {\n sourcePkg,\n sourceFile: sourceFileNorm,\n // sourceOffset is null at the ShaveOptions level — per-atom offsets are\n // derived from entry.sourceRange.start inside shave() and forwarded into\n // PersistOptions.sourceContext.sourceOffset per novel-glue entry.\n sourceOffset: null,\n },\n });\n\n // Pass A (occurrence resolution): after shave, resolve PointerEntry atom\n // occurrences that were not captured by the storeBlock interceptor.\n //\n // @decision DEC-V2-OCCURRENCE-DELETE-INSERT-001\n // Novel atoms are already in perFileOccurrences (captured by storeBlock wrapper above).\n // PointerEntry atoms (result.atoms entries with stub.merkleRoot !== undefined, set\n // by shave/src/index.ts DEC-SHAVE-POINTER-MERKLROOT-PROPAGATION-001) were matched\n // from the DB and NOT passed through storeBlock — their new sourceOffset is\n // known (stub.sourceRange.start) and their blockMerkleRoot is now directly\n // available via stub.merkleRoot.\n //\n // Previous approach: re-derive blockMerkleRoot via canonicalAstHash(source, range)\n // → findByCanonicalAstHash(). This failed for type-only declarations and export\n // statements because canonicalAstHash throws CanonicalAstParseError when the\n // source range spans multiple AST nodes (e.g. `export type { A, B, C }`). Files\n // like universalize/types.ts (100% type-only) had 0/N occurrences stored.\n //\n // Current approach: read stub.merkleRoot directly. shave() now propagates\n // entry.merkleRoot from PointerEntry to ShavedAtomStub, so no re-derivation\n // is needed. The block's implSource.length is still fetched from the registry\n // to record the correct occurrence length (character count).\n //\n // Deduplication: storeBlock (novel atoms) and this pointer pass must not\n // both record an occurrence for the same source offset. Build a set of\n // already-captured offsets from perFileOccurrences before iterating stubs.\n // A stub whose offset is already captured is a novel atom — skip it here.\n const capturedOffsets = new Set(perFileOccurrences.map((o) => o.sourceOffset));\n const pointerStubs = result.atoms.filter(\n (stub) => stub.merkleRoot !== undefined && !capturedOffsets.has(stub.sourceRange.start),\n );\n for (const stub of pointerStubs) {\n const merkleRoot = stub.merkleRoot;\n // merkleRoot is guaranteed non-undefined by the filter above, but the\n // TypeScript type is BlockMerkleRoot|undefined, so we guard explicitly.\n if (merkleRoot === undefined) continue;\n // @decision DEC-V2-POINTER-OCCURRENCE-LENGTH-001\n // @title Pointer stub occurrence: skip when impl_source text doesn't match actual source\n // @status active\n // @rationale\n // Canonical-AST matching can unify atoms from different files whose textual\n // representation differs (e.g. `const encoder = new TextEncoder()` in one file\n // vs `const TEXT_ENCODER = new TextEncoder()` in another). These atoms share\n // the same blockMerkleRoot because they normalize to the same canonical AST.\n //\n // Problem: if we record an occurrence for the `TEXT_ENCODER` span using\n // the shared block (impl_source = \"const encoder = ...\"), reconstruction\n // will emit \"const encoder = ...\" instead of \"const TEXT_ENCODER = ...\",\n // breaking byte-identity. Worse, the glue computation excludes the atom span,\n // so the original \"TEXT_ENCODER\" text is lost from both the atom block AND\n // the glue blob — reconstruction cannot recover it.\n //\n // Fix: compare the actual source text at the stub's span with the block's\n // impl_source. When they differ, skip the occurrence record. The span then\n // falls entirely into glue (computeGlueBlob includes it), and reconstruction\n // emits the correct verbatim text from the glue blob.\n //\n // When they match (most pointer atoms), record the occurrence using the\n // actual span length (stub.sourceRange.end - stub.sourceRange.start) so the\n // glue cursor advances by the correct character count.\n {\n const block = await registry.getBlock(merkleRoot);\n if (block === null) {\n logger.error(\n `warning: bootstrap occurrence: block not found for pointer stub in ${sourceFileNorm} range=[${stub.sourceRange.start},${stub.sourceRange.end}] merkleRoot=${merkleRoot} — occurrence skipped`,\n );\n continue;\n }\n const actualSpan = sourceText.slice(stub.sourceRange.start, stub.sourceRange.end);\n if (actualSpan !== block.implSource) {\n // Text mismatch: the canonical-AST match unified two structurally equivalent\n // but textually different atoms. Skip the occurrence; the span falls into glue\n // and the verbatim original text is preserved for reconstruction.\n continue;\n }\n perFileOccurrences.push({\n sourcePkg,\n sourceFile: sourceFileNorm,\n sourceOffset: stub.sourceRange.start,\n // Actual character span in THIS file.\n length: stub.sourceRange.end - stub.sourceRange.start,\n blockMerkleRoot: merkleRoot,\n });\n }\n }\n\n // Deduplicate perFileOccurrences by sourceOffset before storing.\n //\n // The slicer can produce multiple stubs at the same sourceRange.start when\n // overlapping slice plan entries share a start position (e.g. nested blocks or\n // adjacent atoms with identical starts). Inserting two rows at the same\n // (source_pkg, source_file, source_offset) violates the block_occurrences PK.\n // Strategy: keep the LAST entry for each offset (stub order from result.atoms\n // is stable, so \"last wins\" is deterministic). Using a Map preserves insertion\n // order and overwrites earlier duplicates.\n {\n const deduped = new Map<number, (typeof perFileOccurrences)[number]>();\n for (const occ of perFileOccurrences) {\n deduped.set(occ.sourceOffset, occ);\n }\n perFileOccurrences = [...deduped.values()];\n }\n\n // Pass A (occurrence store): atomically replace per-file occurrences in block_occurrences.\n //\n // @decision DEC-V2-OCCURRENCE-DELETE-INSERT-001\n // @decision DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001\n // All atom occurrences for this file (both novel and pointer-resolved) are now\n // in perFileOccurrences. replaceSourceFileOccurrences atomically deletes the\n // prior set and inserts the fresh set inside one SQLite transaction.\n // On failure: prior occurrences remain intact (transaction rollback).\n try {\n await registry.replaceSourceFileOccurrences(sourcePkg, sourceFileNorm, perFileOccurrences);\n } catch (err) {\n logger.error(\n `error: bootstrap occurrence: replaceSourceFileOccurrences failed for ${sourceFileNorm}: ${(err as Error).message}`,\n );\n // Non-fatal: log and continue. getAtomRangesBySourceFile will return an\n // empty set for this file (no occurrences stored), and glue capture will\n // store the full file as glue — reconstruction will be degraded but safe.\n }\n\n // Pass B: capture per-file glue (non-atom regions) into source_file_glue.\n // Queries DB for atoms stored with sourceFile = sourceFileNorm via\n // getAtomRangesBySourceFile — which now reads from block_occurrences\n // (DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001 / DEC-V2-GLUE-CAPTURE-AUTHORITY-001).\n // Must run after Pass A so novel atoms and occurrences are committed first.\n // Errors are logged but do not fail the file outcome.\n await captureSourceFileGlue(registry, absPath, sourcePkg, sourceFileNorm, logger);\n\n // Pass C: cache-miss write — record the file's content hash in source_file_state\n // so the next bootstrap run can cache-hit this file if its bytes are unchanged.\n //\n // Only written on the on-disk (non-verify) path. --verify uses :memory: and\n // the guard below is the explicit forward-safety check.\n //\n // Non-fatal: a write failure means the cache row is missing and the next run\n // will re-shave this file (correct, just slightly slower). F1 failure mode per plan.\n //\n // @decision DEC-V2-SHAVE-CACHE-STORAGE-001\n // @decision DEC-V2-SHAVE-CACHE-VERIFY-FLAG-001\n if (!parsed.values.verify) {\n try {\n await registry.storeSourceFileContentHash(sourcePkg, sourceFileNorm, fileContentHash);\n } catch (cacheWriteErr) {\n logger.error(\n `warning: failed to write source_file_state for ${sourceFileNorm} (next run will re-shave): ${(cacheWriteErr as Error).message}`,\n );\n }\n }\n\n rawOutcomes.push({\n path: relPath,\n outcome: \"success\",\n atomCount: result.atoms.length,\n intentCardCount: result.intentCards.length,\n });\n } catch (err) {\n const e = err as Error;\n rawOutcomes.push({\n path: relPath,\n outcome: \"failure\",\n errorClass: e.constructor.name,\n errorMessage: e.message,\n });\n }\n }\n\n // Capture workspace plumbing files into the registry (P2).\n // Runs after the shave loop so all atoms are already persisted.\n // Ordering is not required by the schema FK, but mirrors logical dependency.\n //\n // @decision DEC-V2-WORKSPACE-PLUMBING-CAPTURE-001\n try {\n await captureWorkspacePlumbing(registry, repoRoot, logger);\n } catch (err) {\n logger.error(`error: workspace plumbing capture failed: ${(err as Error).message}`);\n await registry.close();\n return 1;\n }\n\n // Reclassify failures that match an expected-failures entry (DEC-V2-BOOT-EXPECTED-FAILURES-001).\n // Track which expected-failure keys were actually triggered so we can warn about untriggered ones.\n const triggeredKeys = new Set<string>();\n const outcomes: FileOutcome[] = rawOutcomes.map((o) => {\n if (o.outcome !== \"failure\") return o;\n const key = `${o.path}\\0${o.errorClass}`;\n const entry = expectedFailureKeys.get(key);\n if (entry === undefined) return o;\n triggeredKeys.add(key);\n return {\n path: o.path,\n outcome: \"expected-failure\" as const,\n errorClass: o.errorClass,\n errorMessage: o.errorMessage,\n rationale: entry.rationale,\n };\n });\n\n // Warn about untriggered expected-failure entries (they may have been fixed or renamed).\n for (const [key, entry] of expectedFailureKeys) {\n if (!triggeredKeys.has(key)) {\n logger.log(\n `warning: expected-failure entry was not triggered — either the file was fixed, renamed, or deleted. Remove or update this entry in expected-failures.json: ${entry.path} (${entry.errorClass})`,\n );\n }\n }\n\n // Export the deterministic manifest from the :memory: shave run.\n let shavedManifest: readonly BootstrapManifestEntry[];\n try {\n shavedManifest = await registry.exportManifest();\n } catch (err) {\n logger.error(`error: failed to export manifest: ${(err as Error).message}`);\n await registry.close();\n return 1;\n }\n\n // Close registry — done with the shave-run DB.\n await registry.close();\n\n // --- Additive merge (DEC-BOOTSTRAP-MANIFEST-ACCUMULATE-001 part (a)) ---\n // Load prior entries from the committed manifest (if it exists) and merge\n // with the shaved entries. Prior entries absent from this shave are RETAINED.\n let priorEntries: ReadonlyArray<BootstrapManifestEntry> = [];\n if (existsSync(manifestPath)) {\n try {\n const priorText = readFileSync(manifestPath, \"utf-8\");\n priorEntries = JSON.parse(priorText) as Array<BootstrapManifestEntry>;\n } catch (err) {\n logger.error(\n `error: failed to read prior manifest at ${manifestPath}: ${(err as Error).message}`,\n );\n return 1;\n }\n }\n\n const shavedEntries = shavedManifest as unknown as Array<BootstrapManifestEntry>;\n const mergedManifest = mergeManifestEntries(priorEntries, shavedEntries);\n\n const priorCount = priorEntries.length;\n const shavedCount = shavedEntries.length;\n const addedCount = mergedManifest.length - priorCount;\n const totalCount = mergedManifest.length;\n\n // Write outputs.\n writeFileSync(manifestPath, `${JSON.stringify(mergedManifest, null, 2)}\\n`, \"utf-8\");\n writeFileSync(reportPath, `${JSON.stringify(outcomes, null, 2)}\\n`, \"utf-8\");\n\n // Summarise.\n const successCount = outcomes.filter((o) => o.outcome === \"success\").length;\n const cacheHitCount = outcomes.filter((o) => o.outcome === \"cache-hit\").length;\n const expectedFailureCount = outcomes.filter((o) => o.outcome === \"expected-failure\").length;\n const failureCount = outcomes.filter((o) => o.outcome === \"failure\").length;\n\n logger.log(\"Bootstrap complete:\");\n logger.log(` files processed: ${outcomes.length}`);\n logger.log(` cache hits: ${cacheHitCount}`);\n logger.log(` shaved: ${successCount}`);\n if (expectedFailureCount > 0) {\n logger.log(` expected-failures: ${expectedFailureCount} (license-refused, documented)`);\n }\n logger.log(` failed: ${failureCount}`);\n // Additive summary line (DEC-BOOTSTRAP-MANIFEST-ACCUMULATE-001).\n logger.log(\n `bootstrap: prior=${priorCount}, shaved=${shavedCount}, added=${addedCount}, total=${totalCount}`,\n );\n // Cache summary line (issue #363 / DEC-V2-SHAVE-CACHE-STORAGE-001).\n logger.log(`bootstrap: cache_hits=${cacheHitCount}, shaved=${successCount}`);\n logger.log(` manifest: ${manifestPath} (${totalCount} entries)`);\n logger.log(` report: ${reportPath}`);\n\n if (failureCount > 0) {\n logger.error(`error: ${failureCount} file(s) failed to shave:`);\n for (const o of outcomes) {\n if (o.outcome === \"failure\") {\n logger.error(` ${o.path}: ${o.errorClass} — ${o.errorMessage}`);\n }\n }\n return 1;\n }\n\n return 0;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CONTINUOUS-SHAVE-022: The shave package exposes three public\n// entry points — shave() for one-shot file processing, universalize() for\n// continuous single-block processing, and IntentExtractionHook for hookable\n// pipeline extensions. All three operate over the same registry view interface\n// so callers can substitute any registry implementation without coupling to\n// the SQLite storage layer.\n// Status: decided (MASTER_PLAN.md DEC-CONTINUOUS-SHAVE-022)\n// Rationale: Separating the registry view from the full Registry interface\n// keeps shave testable with lightweight noop stubs and decoupled from storage.\n\nimport type { BlockMerkleRoot, CanonicalAstHash, SpecHash } from \"@yakcc/contracts\";\nimport type { BlockTripletRow } from \"@yakcc/registry\";\nimport type { IntentCard } from \"./intent/types.js\";\n// DEC-LICENSE-GATE-REMOVE-001: LicenseDetection import removed (WI-682, 2026-05-17).\nimport type { SlicePlanEntry } from \"./universalize/types.js\";\n\n// ---------------------------------------------------------------------------\n// Foreign policy\n// ---------------------------------------------------------------------------\n\n/**\n * How the shave pipeline handles foreign-block dependencies encountered during\n * slicing. Passed as ShaveOptions.foreignPolicy.\n *\n * 'allow' — foreign deps are silently accepted; nothing extra is emitted.\n * 'reject' — the shave pipeline throws/fails when a foreign dep is found.\n * 'tag' — foreign deps are accepted but tagged in the slice plan output\n * so callers and CLI output can surface them.\n */\nexport type ForeignPolicy = \"allow\" | \"reject\" | \"tag\";\n\n/**\n * @decision DEC-V2-FOREIGN-BLOCK-SCHEMA-001 (sub-C: default policy)\n * title: FOREIGN_POLICY_DEFAULT = 'tag'\n * status: closed (WI-V2-04 L4)\n * rationale: 'tag' is the visible-by-default option. Code-is-Truth means foreign\n * deps should appear in summary output unless explicitly opted out. 'allow' would\n * silently accept foreign deps; 'reject' would block legitimate workflows. 'tag'\n * surfaces foreign deps without blocking, making the default safe and informative.\n * This constant is the single source of truth for the default (I-X3 invariant):\n * all other references (CLI --foreign-policy, ShaveOptions) import this constant.\n * @scope WI-V2-04 L4\n */\nexport const FOREIGN_POLICY_DEFAULT: ForeignPolicy = \"tag\";\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration options passed to shave() and universalize().\n * All fields are optional; defaults are documented on each field.\n */\nexport interface ShaveOptions {\n /**\n * Directory for the file-system intent-extraction cache.\n * Defaults to os.tmpdir()/yakcc-shave-cache when not specified.\n * WI-010-02 fills the cache implementation.\n */\n readonly cacheDir?: string | undefined;\n /**\n * The Anthropic model identifier to use for intent extraction.\n * Defaults to \"claude-3-5-haiku-20241022\".\n * WI-010-02 connects this to the Anthropic SDK.\n */\n readonly model?: string | undefined;\n /**\n * When true, skip live extraction and rely entirely on cache hits.\n * Useful for CI environments without API access.\n */\n readonly offline?: boolean | undefined;\n /**\n * Intent extraction strategy. (WI-022)\n *\n * @decision DEC-INTENT-STRATEGY-001\n * - \"static\" (default): TypeScript Compiler API + JSDoc parser. No network,\n * no API key required. Offline-safe. Produces deterministic IntentCards.\n * - \"llm\": Anthropic API. Requires ANTHROPIC_API_KEY or a ctx.client.\n * Subject to offline checks. Produces AI-written documentation fields.\n *\n * When omitted, defaults to \"static\".\n */\n readonly intentStrategy?: \"static\" | \"llm\" | undefined;\n /**\n * Tuning options forwarded to decompose() for the AST recursion.\n * maxDepth (default 8) and maxControlFlowBoundaries (default 1) control\n * when the recursion stops and what counts as atomic. WI-012-06.\n */\n readonly recursionOptions?:\n | {\n readonly maxDepth?: number;\n readonly maxControlFlowBoundaries?: number;\n }\n | undefined;\n /**\n * Controls how the pipeline reacts when a foreign-block dependency is\n * encountered during slicing.\n *\n * 'allow' — foreign deps are silently accepted.\n * 'reject' — the pipeline throws when a foreign dep is found.\n * 'tag' — foreign deps are accepted but tagged in slice plan output.\n *\n * Defaults to FOREIGN_POLICY_DEFAULT ('tag').\n * Authority invariant I-X3: all references to the default value import\n * FOREIGN_POLICY_DEFAULT from this module; no inline 'tag' literals elsewhere.\n */\n readonly foreignPolicy?: ForeignPolicy | undefined;\n\n /**\n * Source-file provenance context for registry storage.\n *\n * When provided, the shave pipeline forwards these values into each persisted\n * BlockTripletRow so the registry can record which workspace file and byte\n * offset produced the atom. Callers that shave a specific source file should\n * populate this from the walker's knowledge of the file being processed.\n *\n * Absent for interactive shaves (yakcc shave CLI) and non-bootstrap runners —\n * those atoms are stored with null provenance, which is correct because they\n * are not part of the canonical bootstrap corpus.\n *\n * @decision DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001\n * @scope WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P1\n */\n readonly sourceContext?:\n | {\n /** Workspace package directory (e.g. 'packages/cli'). */\n readonly sourcePkg: string;\n /** Workspace-relative path of the source file (e.g. 'packages/cli/src/commands/foo.ts'). */\n readonly sourceFile: string;\n /** Byte offset of the atom within the source file. Null when not computable. */\n readonly sourceOffset: number | null;\n }\n | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Diagnostics\n// ---------------------------------------------------------------------------\n\n/**\n * Diagnostic information returned alongside every shave/universalize result.\n *\n * `stubbed` lists capabilities that are not yet implemented in this work item:\n * - \"decomposition\": atom decomposition is a stub (WI-010-01; removed WI-012-06)\n * - \"variance\": variance scoring is a stub (WI-011; pending WI-014)\n * - \"license-gate\": license gating is a stub (WI-013-01; removed WI-013-02)\n *\n * `cacheHits` and `cacheMisses` count intent-extraction cache events.\n * Both will be 0 in WI-010-01 stubs; WI-010-02 populates them.\n */\nexport interface ShaveDiagnostics {\n readonly stubbed: readonly (\"decomposition\" | \"variance\" | \"license-gate\")[];\n readonly cacheHits: number;\n readonly cacheMisses: number;\n}\n\n// ---------------------------------------------------------------------------\n// Shave result types\n// ---------------------------------------------------------------------------\n\n/**\n * A stub placeholder for a decomposed atom within a source file.\n *\n * WI-012 replaces this with a real atom backed by a content-addressed\n * block in the registry. In WI-010-01 the atoms array is always empty.\n *\n * WI-014-03: merkleRoot is populated when the atom was persisted to the\n * registry as a novel block triplet. Absent for pointer atoms (which\n * already have an existing registry entry) and for atoms that were not\n * persisted (no storeBlock on the registry view, or no intentCard on the\n * entry).\n */\nexport interface ShavedAtomStub {\n /** A generated placeholder identifier for this atom position. */\n readonly placeholderId: string;\n /** Byte range within the source file that this atom covers. */\n readonly sourceRange: { readonly start: number; readonly end: number };\n /**\n * The BlockMerkleRoot of the persisted block, if this atom was stored in\n * the registry during the shave() call. Undefined for pointer atoms and\n * for atoms skipped by the persistence path (no storeBlock support or no\n * intentCard).\n */\n readonly merkleRoot?: BlockMerkleRoot | undefined;\n}\n\n/**\n * The result of a shave() call on a single source file.\n *\n * In WI-010-01 all arrays are empty stubs. WI-010-02 populates intentCards\n * via the real extractIntent path; WI-012 populates atoms via the DFG slicer.\n */\nexport interface ShaveResult {\n /** Absolute path of the source file that was processed. */\n readonly sourcePath: string;\n /** Decomposed atom stubs — empty until WI-012. */\n readonly atoms: readonly ShavedAtomStub[];\n /** Extracted intent cards — empty until WI-010-02. */\n readonly intentCards: readonly IntentCard[];\n /** Diagnostic information about the processing run. */\n readonly diagnostics: ShaveDiagnostics;\n}\n\n// ---------------------------------------------------------------------------\n// Universalize result types\n// ---------------------------------------------------------------------------\n\n/**\n * One entry in the slice plan produced by universalize().\n *\n * WI-012-06 wires universalize() to the real DFG slicer. This is a type alias\n * for SlicePlanEntry (PointerEntry | NovelGlueEntry), keeping the public\n * surface aligned with the slicer's discriminated union while letting callers\n * import UniversalizeSlicePlanEntry from the top-level types path.\n *\n * @decision DEC-UNIVERSALIZE-WIRING-001\n * title: UniversalizeSlicePlanEntry aliased to SlicePlanEntry\n * status: decided\n * rationale: The WI-010-01 placeholder shape ({ placeholderId, sourceRange })\n * was superseded by the slicer's PointerEntry | NovelGlueEntry discriminated\n * union (WI-012-05). Re-exporting SlicePlanEntry as UniversalizeSlicePlanEntry\n * avoids two parallel types describing the same thing and keeps the public API\n * surface stable without requiring callers to import from the sub-module path.\n */\nexport type UniversalizeSlicePlanEntry = SlicePlanEntry;\n\n/**\n * The result of a universalize() call on a single candidate block.\n *\n * The intentCard is the primary output — it describes the behavioral intent\n * of the candidate. slicePlan is populated by WI-012-06 via the real DFG\n * slicer. matchedPrimitives carries the deduplicated (canonicalAstHash,\n * merkleRoot) pairs from the slicer. Variance-based matching (WI-011) is\n * still stubbed and listed in diagnostics.stubbed.\n */\nexport interface UniversalizeResult {\n /** The extracted intent card for this candidate. */\n readonly intentCard: IntentCard;\n /** DFG-based slice plan — live as of WI-012-06. */\n readonly slicePlan: readonly SlicePlanEntry[];\n /**\n * Registry primitives matched by canonical AST hash — populated by WI-012-06.\n * Each entry carries (canonicalAstHash, merkleRoot) from the slicer's\n * PointerEntry nodes. Variance-scored matches (specHash) are WI-011.\n */\n readonly matchedPrimitives: readonly {\n readonly canonicalAstHash: CanonicalAstHash;\n readonly merkleRoot: BlockMerkleRoot;\n }[];\n // DEC-LICENSE-GATE-REMOVE-001: licenseDetection field removed (WI-682, 2026-05-17).\n // License-of-origin gating is gone; the field is no longer populated or meaningful.\n readonly diagnostics: ShaveDiagnostics;\n}\n\n/**\n * Options for universalize().\n *\n * Extends ShaveOptions with an optional `persist` flag. When `persist: true`,\n * universalize() will call maybePersistNovelGlueAtom for each NovelGlueEntry in\n * the slice plan (postorder, lineage-threaded via parentBlockRoot) and surface\n * the resulting BlockMerkleRoot on each entry.\n *\n * `shave()` delegates to universalize({persist:true}) and passes its file path\n * via `sourceFilePath` so that the props-file corpus extractor can locate the\n * sibling `.props.ts` file (WI-423: DEC-V2-SHAVE-DELEGATES-UNIVERSALIZE-001).\n *\n * @decision DEC-UNIVERSALIZE-PERSIST-API-001\n * @title UniversalizeOptions extends ShaveOptions with optional persist flag\n * @status accepted (WI-373)\n * @rationale\n * Option A chosen over Option B (universalizeAndPersist()) and Option C\n * (caller-side wrapping). A new extending type keeps ShaveOptions clean while\n * signalling that persist is only meaningful for universalize(). The flag is\n * optional and defaults to false so all existing callers are unaffected.\n * If persist:true is requested but the registry has no storeBlock, a loud\n * PersistRequestedButNotSupportedError is thrown (Sacred Practice #5).\n * WI-423 (DEC-V2-SHAVE-DELEGATES-UNIVERSALIZE-001) completes the consolidation:\n * shave() now delegates its persist loop to universalize({persist:true}) and\n * passes sourceFilePath so the props-file corpus path remains functional.\n * Flag as P1 follow-up: atomize.ts in @yakcc/hooks-base also runs a parallel\n * buildBlockRow+storeBlock loop; once universalize({persist:true}) lands, that\n * caller should consolidate onto this path too (see WI-373 plan §7).\n */\nexport interface UniversalizeOptions extends ShaveOptions {\n /**\n * When true, universalize() will persist each NovelGlueEntry atom to the\n * registry (using maybePersistNovelGlueAtom) in postorder DFS order with\n * parentBlockRoot lineage threading, and surface the resulting BlockMerkleRoot\n * on each NovelGlueEntry.\n *\n * Defaults to false (undefined). When false or omitted, no persistence occurs\n * and NovelGlueEntry.merkleRoot is always undefined — preserving today's\n * default behavior bit-for-bit.\n *\n * Throws PersistRequestedButNotSupportedError if registry.storeBlock is absent.\n */\n readonly persist?: boolean | undefined;\n\n /**\n * Absolute path to the source file being processed.\n *\n * When provided alongside `persist: true`, this path is forwarded to\n * maybePersistNovelGlueAtom as `sourceFilePath`, enabling the props-file\n * corpus extractor to locate the sibling `<stem>.props.ts` file.\n *\n * Set by shave() from its own `sourcePath` parameter (WI-423). Interactive\n * universalize() callers (e.g. assembleCandidate) leave this undefined, which\n * disables the props-file source for those atoms — correct per\n * REQ-NOGO-006: atoms persist with null provenance.\n *\n * @decision DEC-V2-SHAVE-DELEGATES-UNIVERSALIZE-001\n * @scope WI-423\n */\n readonly sourceFilePath?: string | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Candidate block\n// ---------------------------------------------------------------------------\n\n/**\n * A candidate block submitted for universalization.\n *\n * `source` is the raw source text. `hint` carries optional metadata that\n * hooks or callers may supply to improve extraction quality — it is never\n * required for correctness.\n */\nexport interface CandidateBlock {\n readonly source: string;\n readonly hint?:\n | {\n readonly name?: string | undefined;\n readonly origin?: \"user\" | \"ai-hook\" | \"compile-resolver\" | undefined;\n }\n | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Registry view — narrow read-only interface for shave operations\n// ---------------------------------------------------------------------------\n\n/**\n * The minimal read interface that shave() and universalize() require from the\n * registry. This is intentionally narrower than @yakcc/registry's full Registry\n * interface to keep the shave package decoupled from the SQLite storage layer.\n *\n * findByCanonicalAstHash is optional: shave uses it for structural deduplication\n * when available but degrades gracefully to spec-hash-only lookup otherwise.\n *\n * storeBlock is optional (WI-014-03): when present, shave() persists NovelGlueEntry\n * atoms that carry an intentCard. Callers that pass a full Registry automatically\n * satisfy this interface. Callers with a read-only stub leave it undefined and\n * persistence is silently skipped (graceful degradation, matching the\n * findByCanonicalAstHash? pattern).\n */\nexport interface ShaveRegistryView {\n selectBlocks(specHash: SpecHash): Promise<readonly BlockMerkleRoot[]>;\n getBlock(merkleRoot: BlockMerkleRoot): Promise<BlockTripletRow | undefined>;\n findByCanonicalAstHash?(canonicalAstHash: string): Promise<readonly BlockMerkleRoot[]>;\n /**\n * Optional: store a block triplet row. When present, shave() calls this for\n * each novel atom it persists. When absent, persistence is silently skipped.\n */\n storeBlock?(row: BlockTripletRow): Promise<void>;\n}\n\n// ---------------------------------------------------------------------------\n// Intent extraction hook interface\n// ---------------------------------------------------------------------------\n\n/**\n * A hookable extension point for the intent-extraction pipeline.\n *\n * Hooks intercept CandidateBlock instances before the default Anthropic\n * extractor runs. A hook may return a fully-populated UniversalizeResult\n * (short-circuiting the default extractor) or may mutate the candidate and\n * pass it on.\n *\n * DEC-CONTINUOUS-SHAVE-022: hooks participate in the continuous universalizer\n * loop so that third-party packages can inject specialized extractors (e.g.\n * for specific DSLs or test frameworks) without forking the shave package.\n */\nexport interface IntentExtractionHook {\n /** Unique identifier for this hook, e.g. \"yakcc.shave.default\". */\n readonly id: string;\n intercept(\n candidate: CandidateBlock,\n registry: ShaveRegistryView,\n options?: ShaveOptions,\n ): Promise<UniversalizeResult>;\n}\n","/**\n * Utilities for hex, bytes, CSPRNG.\n * @module\n */\n/*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) */\n\n// We use WebCrypto aka globalThis.crypto, which exists in browsers and node.js 16+.\n// node.js versions earlier than v19 don't declare it in global scope.\n// For node.js, package.json#exports field mapping rewrites import\n// from `crypto` to `cryptoNode`, which imports native module.\n// Makes the utils un-importable in browsers without a bundler.\n// Once node.js 18 is deprecated (2025-04-30), we can just drop the import.\nimport { crypto } from '@noble/hashes/crypto';\n\n/** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */\nexport function isBytes(a: unknown): a is Uint8Array {\n return a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');\n}\n\n/** Asserts something is positive integer. */\nexport function anumber(n: number): void {\n if (!Number.isSafeInteger(n) || n < 0) throw new Error('positive integer expected, got ' + n);\n}\n\n/** Asserts something is Uint8Array. */\nexport function abytes(b: Uint8Array | undefined, ...lengths: number[]): void {\n if (!isBytes(b)) throw new Error('Uint8Array expected');\n if (lengths.length > 0 && !lengths.includes(b.length))\n throw new Error('Uint8Array expected of length ' + lengths + ', got length=' + b.length);\n}\n\n/** Asserts something is hash */\nexport function ahash(h: IHash): void {\n if (typeof h !== 'function' || typeof h.create !== 'function')\n throw new Error('Hash should be wrapped by utils.createHasher');\n anumber(h.outputLen);\n anumber(h.blockLen);\n}\n\n/** Asserts a hash instance has not been destroyed / finished */\nexport function aexists(instance: any, checkFinished = true): void {\n if (instance.destroyed) throw new Error('Hash instance has been destroyed');\n if (checkFinished && instance.finished) throw new Error('Hash#digest() has already been called');\n}\n\n/** Asserts output is properly-sized byte array */\nexport function aoutput(out: any, instance: any): void {\n abytes(out);\n const min = instance.outputLen;\n if (out.length < min) {\n throw new Error('digestInto() expects output buffer of length at least ' + min);\n }\n}\n\n/** Generic type encompassing 8/16/32-byte arrays - but not 64-byte. */\n// prettier-ignore\nexport type TypedArray = Int8Array | Uint8ClampedArray | Uint8Array |\n Uint16Array | Int16Array | Uint32Array | Int32Array;\n\n/** Cast u8 / u16 / u32 to u8. */\nexport function u8(arr: TypedArray): Uint8Array {\n return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/** Cast u8 / u16 / u32 to u32. */\nexport function u32(arr: TypedArray): Uint32Array {\n return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));\n}\n\n/** Zeroize a byte array. Warning: JS provides no guarantees. */\nexport function clean(...arrays: TypedArray[]): void {\n for (let i = 0; i < arrays.length; i++) {\n arrays[i].fill(0);\n }\n}\n\n/** Create DataView of an array for easy byte-level manipulation. */\nexport function createView(arr: TypedArray): DataView {\n return new DataView(arr.buffer, arr.byteOffset, arr.byteLength);\n}\n\n/** The rotate right (circular right shift) operation for uint32 */\nexport function rotr(word: number, shift: number): number {\n return (word << (32 - shift)) | (word >>> shift);\n}\n\n/** The rotate left (circular left shift) operation for uint32 */\nexport function rotl(word: number, shift: number): number {\n return (word << shift) | ((word >>> (32 - shift)) >>> 0);\n}\n\n/** Is current platform little-endian? Most are. Big-Endian platform: IBM */\nexport const isLE: boolean = /* @__PURE__ */ (() =>\n new Uint8Array(new Uint32Array([0x11223344]).buffer)[0] === 0x44)();\n\n/** The byte swap operation for uint32 */\nexport function byteSwap(word: number): number {\n return (\n ((word << 24) & 0xff000000) |\n ((word << 8) & 0xff0000) |\n ((word >>> 8) & 0xff00) |\n ((word >>> 24) & 0xff)\n );\n}\n/** Conditionally byte swap if on a big-endian platform */\nexport const swap8IfBE: (n: number) => number = isLE\n ? (n: number) => n\n : (n: number) => byteSwap(n);\n\n/** @deprecated */\nexport const byteSwapIfBE: typeof swap8IfBE = swap8IfBE;\n/** In place byte swap for Uint32Array */\nexport function byteSwap32(arr: Uint32Array): Uint32Array {\n for (let i = 0; i < arr.length; i++) {\n arr[i] = byteSwap(arr[i]);\n }\n return arr;\n}\n\nexport const swap32IfBE: (u: Uint32Array) => Uint32Array = isLE\n ? (u: Uint32Array) => u\n : byteSwap32;\n\n// Built-in hex conversion https://caniuse.com/mdn-javascript_builtins_uint8array_fromhex\nconst hasHexBuiltin: boolean = /* @__PURE__ */ (() =>\n // @ts-ignore\n typeof Uint8Array.from([]).toHex === 'function' && typeof Uint8Array.fromHex === 'function')();\n\n// Array where index 0xf0 (240) is mapped to string 'f0'\nconst hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) =>\n i.toString(16).padStart(2, '0')\n);\n\n/**\n * Convert byte array to hex string. Uses built-in function, when available.\n * @example bytesToHex(Uint8Array.from([0xca, 0xfe, 0x01, 0x23])) // 'cafe0123'\n */\nexport function bytesToHex(bytes: Uint8Array): string {\n abytes(bytes);\n // @ts-ignore\n if (hasHexBuiltin) return bytes.toHex();\n // pre-caching improves the speed 6x\n let hex = '';\n for (let i = 0; i < bytes.length; i++) {\n hex += hexes[bytes[i]];\n }\n return hex;\n}\n\n// We use optimized technique to convert hex string to byte array\nconst asciis = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 } as const;\nfunction asciiToBase16(ch: number): number | undefined {\n if (ch >= asciis._0 && ch <= asciis._9) return ch - asciis._0; // '2' => 50-48\n if (ch >= asciis.A && ch <= asciis.F) return ch - (asciis.A - 10); // 'B' => 66-(65-10)\n if (ch >= asciis.a && ch <= asciis.f) return ch - (asciis.a - 10); // 'b' => 98-(97-10)\n return;\n}\n\n/**\n * Convert hex string to byte array. Uses built-in function, when available.\n * @example hexToBytes('cafe0123') // Uint8Array.from([0xca, 0xfe, 0x01, 0x23])\n */\nexport function hexToBytes(hex: string): Uint8Array {\n if (typeof hex !== 'string') throw new Error('hex string expected, got ' + typeof hex);\n // @ts-ignore\n if (hasHexBuiltin) return Uint8Array.fromHex(hex);\n const hl = hex.length;\n const al = hl / 2;\n if (hl % 2) throw new Error('hex string expected, got unpadded hex of length ' + hl);\n const array = new Uint8Array(al);\n for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {\n const n1 = asciiToBase16(hex.charCodeAt(hi));\n const n2 = asciiToBase16(hex.charCodeAt(hi + 1));\n if (n1 === undefined || n2 === undefined) {\n const char = hex[hi] + hex[hi + 1];\n throw new Error('hex string expected, got non-hex character \"' + char + '\" at index ' + hi);\n }\n array[ai] = n1 * 16 + n2; // multiply first octet, e.g. 'a3' => 10*16+3 => 160 + 3 => 163\n }\n return array;\n}\n\n/**\n * There is no setImmediate in browser and setTimeout is slow.\n * Call of async fn will return Promise, which will be fullfiled only on\n * next scheduler queue processing step and this is exactly what we need.\n */\nexport const nextTick = async (): Promise<void> => {};\n\n/** Returns control to thread each 'tick' ms to avoid blocking. */\nexport async function asyncLoop(\n iters: number,\n tick: number,\n cb: (i: number) => void\n): Promise<void> {\n let ts = Date.now();\n for (let i = 0; i < iters; i++) {\n cb(i);\n // Date.now() is not monotonic, so in case if clock goes backwards we return return control too\n const diff = Date.now() - ts;\n if (diff >= 0 && diff < tick) continue;\n await nextTick();\n ts += diff;\n }\n}\n\n// Global symbols, but ts doesn't see them: https://github.com/microsoft/TypeScript/issues/31535\ndeclare const TextEncoder: any;\ndeclare const TextDecoder: any;\n\n/**\n * Converts string to bytes using UTF8 encoding.\n * @example utf8ToBytes('abc') // Uint8Array.from([97, 98, 99])\n */\nexport function utf8ToBytes(str: string): Uint8Array {\n if (typeof str !== 'string') throw new Error('string expected');\n return new Uint8Array(new TextEncoder().encode(str)); // https://bugzil.la/1681809\n}\n\n/**\n * Converts bytes to string using UTF8 encoding.\n * @example bytesToUtf8(Uint8Array.from([97, 98, 99])) // 'abc'\n */\nexport function bytesToUtf8(bytes: Uint8Array): string {\n return new TextDecoder().decode(bytes);\n}\n\n/** Accepted input of hash functions. Strings are converted to byte arrays. */\nexport type Input = string | Uint8Array;\n/**\n * Normalizes (non-hex) string or Uint8Array to Uint8Array.\n * Warning: when Uint8Array is passed, it would NOT get copied.\n * Keep in mind for future mutable operations.\n */\nexport function toBytes(data: Input): Uint8Array {\n if (typeof data === 'string') data = utf8ToBytes(data);\n abytes(data);\n return data;\n}\n\n/** KDFs can accept string or Uint8Array for user convenience. */\nexport type KDFInput = string | Uint8Array;\n/**\n * Helper for KDFs: consumes uint8array or string.\n * When string is passed, does utf8 decoding, using TextDecoder.\n */\nexport function kdfInputToBytes(data: KDFInput): Uint8Array {\n if (typeof data === 'string') data = utf8ToBytes(data);\n abytes(data);\n return data;\n}\n\n/** Copies several Uint8Arrays into one. */\nexport function concatBytes(...arrays: Uint8Array[]): Uint8Array {\n let sum = 0;\n for (let i = 0; i < arrays.length; i++) {\n const a = arrays[i];\n abytes(a);\n sum += a.length;\n }\n const res = new Uint8Array(sum);\n for (let i = 0, pad = 0; i < arrays.length; i++) {\n const a = arrays[i];\n res.set(a, pad);\n pad += a.length;\n }\n return res;\n}\n\ntype EmptyObj = {};\nexport function checkOpts<T1 extends EmptyObj, T2 extends EmptyObj>(\n defaults: T1,\n opts?: T2\n): T1 & T2 {\n if (opts !== undefined && {}.toString.call(opts) !== '[object Object]')\n throw new Error('options should be object or undefined');\n const merged = Object.assign(defaults, opts);\n return merged as T1 & T2;\n}\n\n/** Hash interface. */\nexport type IHash = {\n (data: Uint8Array): Uint8Array;\n blockLen: number;\n outputLen: number;\n create: any;\n};\n\n/** For runtime check if class implements interface */\nexport abstract class Hash<T extends Hash<T>> {\n abstract blockLen: number; // Bytes per block\n abstract outputLen: number; // Bytes in output\n abstract update(buf: Input): this;\n // Writes digest into buf\n abstract digestInto(buf: Uint8Array): void;\n abstract digest(): Uint8Array;\n /**\n * Resets internal state. Makes Hash instance unusable.\n * Reset is impossible for keyed hashes if key is consumed into state. If digest is not consumed\n * by user, they will need to manually call `destroy()` when zeroing is necessary.\n */\n abstract destroy(): void;\n /**\n * Clones hash instance. Unsafe: doesn't check whether `to` is valid. Can be used as `clone()`\n * when no options are passed.\n * Reasons to use `_cloneInto` instead of clone: 1) performance 2) reuse instance => all internal\n * buffers are overwritten => causes buffer overwrite which is used for digest in some cases.\n * There are no guarantees for clean-up because it's impossible in JS.\n */\n abstract _cloneInto(to?: T): T;\n // Safe version that clones internal state\n abstract clone(): T;\n}\n\n/**\n * XOF: streaming API to read digest in chunks.\n * Same as 'squeeze' in keccak/k12 and 'seek' in blake3, but more generic name.\n * When hash used in XOF mode it is up to user to call '.destroy' afterwards, since we cannot\n * destroy state, next call can require more bytes.\n */\nexport type HashXOF<T extends Hash<T>> = Hash<T> & {\n xof(bytes: number): Uint8Array; // Read 'bytes' bytes from digest stream\n xofInto(buf: Uint8Array): Uint8Array; // read buf.length bytes from digest stream into buf\n};\n\n/** Hash function */\nexport type CHash = ReturnType<typeof createHasher>;\n/** Hash function with output */\nexport type CHashO = ReturnType<typeof createOptHasher>;\n/** XOF with output */\nexport type CHashXO = ReturnType<typeof createXOFer>;\n\n/** Wraps hash function, creating an interface on top of it */\nexport function createHasher<T extends Hash<T>>(\n hashCons: () => Hash<T>\n): {\n (msg: Input): Uint8Array;\n outputLen: number;\n blockLen: number;\n create(): Hash<T>;\n} {\n const hashC = (msg: Input): Uint8Array => hashCons().update(toBytes(msg)).digest();\n const tmp = hashCons();\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = () => hashCons();\n return hashC;\n}\n\nexport function createOptHasher<H extends Hash<H>, T extends Object>(\n hashCons: (opts?: T) => Hash<H>\n): {\n (msg: Input, opts?: T): Uint8Array;\n outputLen: number;\n blockLen: number;\n create(opts?: T): Hash<H>;\n} {\n const hashC = (msg: Input, opts?: T): Uint8Array => hashCons(opts).update(toBytes(msg)).digest();\n const tmp = hashCons({} as T);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = (opts?: T) => hashCons(opts);\n return hashC;\n}\n\nexport function createXOFer<H extends HashXOF<H>, T extends Object>(\n hashCons: (opts?: T) => HashXOF<H>\n): {\n (msg: Input, opts?: T): Uint8Array;\n outputLen: number;\n blockLen: number;\n create(opts?: T): HashXOF<H>;\n} {\n const hashC = (msg: Input, opts?: T): Uint8Array => hashCons(opts).update(toBytes(msg)).digest();\n const tmp = hashCons({} as T);\n hashC.outputLen = tmp.outputLen;\n hashC.blockLen = tmp.blockLen;\n hashC.create = (opts?: T) => hashCons(opts);\n return hashC;\n}\nexport const wrapConstructor: typeof createHasher = createHasher;\nexport const wrapConstructorWithOpts: typeof createOptHasher = createOptHasher;\nexport const wrapXOFConstructorWithOpts: typeof createXOFer = createXOFer;\n\n/** Cryptographically secure PRNG. Uses internal OS-level `crypto.getRandomValues`. */\nexport function randomBytes(bytesLength = 32): Uint8Array {\n if (crypto && typeof crypto.getRandomValues === 'function') {\n return crypto.getRandomValues(new Uint8Array(bytesLength));\n }\n // Legacy Node.js compatibility\n if (crypto && typeof crypto.randomBytes === 'function') {\n return Uint8Array.from(crypto.randomBytes(bytesLength));\n }\n throw new Error('crypto.getRandomValues must be defined');\n}\n","/**\n * Internal Merkle-Damgard hash utils.\n * @module\n */\nimport { type Input, Hash, abytes, aexists, aoutput, clean, createView, toBytes } from './utils.ts';\n\n/** Polyfill for Safari 14. https://caniuse.com/mdn-javascript_builtins_dataview_setbiguint64 */\nexport function setBigUint64(\n view: DataView,\n byteOffset: number,\n value: bigint,\n isLE: boolean\n): void {\n if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE);\n const _32n = BigInt(32);\n const _u32_max = BigInt(0xffffffff);\n const wh = Number((value >> _32n) & _u32_max);\n const wl = Number(value & _u32_max);\n const h = isLE ? 4 : 0;\n const l = isLE ? 0 : 4;\n view.setUint32(byteOffset + h, wh, isLE);\n view.setUint32(byteOffset + l, wl, isLE);\n}\n\n/** Choice: a ? b : c */\nexport function Chi(a: number, b: number, c: number): number {\n return (a & b) ^ (~a & c);\n}\n\n/** Majority function, true if any two inputs is true. */\nexport function Maj(a: number, b: number, c: number): number {\n return (a & b) ^ (a & c) ^ (b & c);\n}\n\n/**\n * Merkle-Damgard hash construction base class.\n * Could be used to create MD5, RIPEMD, SHA1, SHA2.\n */\nexport abstract class HashMD<T extends HashMD<T>> extends Hash<T> {\n protected abstract process(buf: DataView, offset: number): void;\n protected abstract get(): number[];\n protected abstract set(...args: number[]): void;\n abstract destroy(): void;\n protected abstract roundClean(): void;\n\n readonly blockLen: number;\n readonly outputLen: number;\n readonly padOffset: number;\n readonly isLE: boolean;\n\n // For partial updates less than block size\n protected buffer: Uint8Array;\n protected view: DataView;\n protected finished = false;\n protected length = 0;\n protected pos = 0;\n protected destroyed = false;\n\n constructor(blockLen: number, outputLen: number, padOffset: number, isLE: boolean) {\n super();\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.padOffset = padOffset;\n this.isLE = isLE;\n this.buffer = new Uint8Array(blockLen);\n this.view = createView(this.buffer);\n }\n update(data: Input): this {\n aexists(this);\n data = toBytes(data);\n abytes(data);\n const { view, buffer, blockLen } = this;\n const len = data.length;\n for (let pos = 0; pos < len; ) {\n const take = Math.min(blockLen - this.pos, len - pos);\n // Fast path: we have at least one block in input, cast it to view and process\n if (take === blockLen) {\n const dataView = createView(data);\n for (; blockLen <= len - pos; pos += blockLen) this.process(dataView, pos);\n continue;\n }\n buffer.set(data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n pos += take;\n if (this.pos === blockLen) {\n this.process(view, 0);\n this.pos = 0;\n }\n }\n this.length += data.length;\n this.roundClean();\n return this;\n }\n digestInto(out: Uint8Array): void {\n aexists(this);\n aoutput(out, this);\n this.finished = true;\n // Padding\n // We can avoid allocation of buffer for padding completely if it\n // was previously not allocated here. But it won't change performance.\n const { buffer, view, blockLen, isLE } = this;\n let { pos } = this;\n // append the bit '1' to the message\n buffer[pos++] = 0b10000000;\n clean(this.buffer.subarray(pos));\n // we have less than padOffset left in buffer, so we cannot put length in\n // current block, need process it and pad again\n if (this.padOffset > blockLen - pos) {\n this.process(view, 0);\n pos = 0;\n }\n // Pad until full block byte with zeros\n for (let i = pos; i < blockLen; i++) buffer[i] = 0;\n // Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that\n // You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen.\n // So we just write lowest 64 bits of that value.\n setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE);\n this.process(view, 0);\n const oview = createView(out);\n const len = this.outputLen;\n // NOTE: we do division by 4 later, which should be fused in single op with modulo by JIT\n if (len % 4) throw new Error('_sha2: outputLen should be aligned to 32bit');\n const outLen = len / 4;\n const state = this.get();\n if (outLen > state.length) throw new Error('_sha2: outputLen bigger than state');\n for (let i = 0; i < outLen; i++) oview.setUint32(4 * i, state[i], isLE);\n }\n digest(): Uint8Array {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res;\n }\n _cloneInto(to?: T): T {\n to ||= new (this.constructor as any)() as T;\n to.set(...this.get());\n const { blockLen, buffer, length, finished, destroyed, pos } = this;\n to.destroyed = destroyed;\n to.finished = finished;\n to.length = length;\n to.pos = pos;\n if (length % blockLen) to.buffer.set(buffer);\n return to;\n }\n clone(): T {\n return this._cloneInto();\n }\n}\n\n/**\n * Initial SHA-2 state: fractional parts of square roots of first 16 primes 2..53.\n * Check out `test/misc/sha2-gen-iv.js` for recomputation guide.\n */\n\n/** Initial SHA256 state. Bits 0..32 of frac part of sqrt of primes 2..19 */\nexport const SHA256_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Initial SHA224 state. Bits 32..64 of frac part of sqrt of primes 23..53 */\nexport const SHA224_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4,\n]);\n\n/** Initial SHA384 state. Bits 0..64 of frac part of sqrt of primes 23..53 */\nexport const SHA384_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0xcbbb9d5d, 0xc1059ed8, 0x629a292a, 0x367cd507, 0x9159015a, 0x3070dd17, 0x152fecd8, 0xf70e5939,\n 0x67332667, 0xffc00b31, 0x8eb44a87, 0x68581511, 0xdb0c2e0d, 0x64f98fa7, 0x47b5481d, 0xbefa4fa4,\n]);\n\n/** Initial SHA512 state. Bits 0..64 of frac part of sqrt of primes 2..19 */\nexport const SHA512_IV: Uint32Array = /* @__PURE__ */ Uint32Array.from([\n 0x6a09e667, 0xf3bcc908, 0xbb67ae85, 0x84caa73b, 0x3c6ef372, 0xfe94f82b, 0xa54ff53a, 0x5f1d36f1,\n 0x510e527f, 0xade682d1, 0x9b05688c, 0x2b3e6c1f, 0x1f83d9ab, 0xfb41bd6b, 0x5be0cd19, 0x137e2179,\n]);\n","/**\n * Internal helpers for u64. BigUint64Array is too slow as per 2025, so we implement it using Uint32Array.\n * @todo re-check https://issues.chromium.org/issues/42212588\n * @module\n */\nconst U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);\nconst _32n = /* @__PURE__ */ BigInt(32);\n\nfunction fromBig(\n n: bigint,\n le = false\n): {\n h: number;\n l: number;\n} {\n if (le) return { h: Number(n & U32_MASK64), l: Number((n >> _32n) & U32_MASK64) };\n return { h: Number((n >> _32n) & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };\n}\n\nfunction split(lst: bigint[], le = false): Uint32Array[] {\n const len = lst.length;\n let Ah = new Uint32Array(len);\n let Al = new Uint32Array(len);\n for (let i = 0; i < len; i++) {\n const { h, l } = fromBig(lst[i], le);\n [Ah[i], Al[i]] = [h, l];\n }\n return [Ah, Al];\n}\n\nconst toBig = (h: number, l: number): bigint => (BigInt(h >>> 0) << _32n) | BigInt(l >>> 0);\n// for Shift in [0, 32)\nconst shrSH = (h: number, _l: number, s: number): number => h >>> s;\nconst shrSL = (h: number, l: number, s: number): number => (h << (32 - s)) | (l >>> s);\n// Right rotate for Shift in [1, 32)\nconst rotrSH = (h: number, l: number, s: number): number => (h >>> s) | (l << (32 - s));\nconst rotrSL = (h: number, l: number, s: number): number => (h << (32 - s)) | (l >>> s);\n// Right rotate for Shift in (32, 64), NOTE: 32 is special case.\nconst rotrBH = (h: number, l: number, s: number): number => (h << (64 - s)) | (l >>> (s - 32));\nconst rotrBL = (h: number, l: number, s: number): number => (h >>> (s - 32)) | (l << (64 - s));\n// Right rotate for shift===32 (just swaps l&h)\nconst rotr32H = (_h: number, l: number): number => l;\nconst rotr32L = (h: number, _l: number): number => h;\n// Left rotate for Shift in [1, 32)\nconst rotlSH = (h: number, l: number, s: number): number => (h << s) | (l >>> (32 - s));\nconst rotlSL = (h: number, l: number, s: number): number => (l << s) | (h >>> (32 - s));\n// Left rotate for Shift in (32, 64), NOTE: 32 is special case.\nconst rotlBH = (h: number, l: number, s: number): number => (l << (s - 32)) | (h >>> (64 - s));\nconst rotlBL = (h: number, l: number, s: number): number => (h << (s - 32)) | (l >>> (64 - s));\n\n// JS uses 32-bit signed integers for bitwise operations which means we cannot\n// simple take carry out of low bit sum by shift, we need to use division.\nfunction add(\n Ah: number,\n Al: number,\n Bh: number,\n Bl: number\n): {\n h: number;\n l: number;\n} {\n const l = (Al >>> 0) + (Bl >>> 0);\n return { h: (Ah + Bh + ((l / 2 ** 32) | 0)) | 0, l: l | 0 };\n}\n// Addition with more than 2 elements\nconst add3L = (Al: number, Bl: number, Cl: number): number => (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0);\nconst add3H = (low: number, Ah: number, Bh: number, Ch: number): number =>\n (Ah + Bh + Ch + ((low / 2 ** 32) | 0)) | 0;\nconst add4L = (Al: number, Bl: number, Cl: number, Dl: number): number =>\n (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0);\nconst add4H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number): number =>\n (Ah + Bh + Ch + Dh + ((low / 2 ** 32) | 0)) | 0;\nconst add5L = (Al: number, Bl: number, Cl: number, Dl: number, El: number): number =>\n (Al >>> 0) + (Bl >>> 0) + (Cl >>> 0) + (Dl >>> 0) + (El >>> 0);\nconst add5H = (low: number, Ah: number, Bh: number, Ch: number, Dh: number, Eh: number): number =>\n (Ah + Bh + Ch + Dh + Eh + ((low / 2 ** 32) | 0)) | 0;\n\n// prettier-ignore\nexport {\n add, add3H, add3L, add4H, add4L, add5H, add5L, fromBig, rotlBH, rotlBL, rotlSH, rotlSL, rotr32H, rotr32L, rotrBH, rotrBL, rotrSH, rotrSL, shrSH, shrSL, split, toBig\n};\n// prettier-ignore\nconst u64: { fromBig: typeof fromBig; split: typeof split; toBig: (h: number, l: number) => bigint; shrSH: (h: number, _l: number, s: number) => number; shrSL: (h: number, l: number, s: number) => number; rotrSH: (h: number, l: number, s: number) => number; rotrSL: (h: number, l: number, s: number) => number; rotrBH: (h: number, l: number, s: number) => number; rotrBL: (h: number, l: number, s: number) => number; rotr32H: (_h: number, l: number) => number; rotr32L: (h: number, _l: number) => number; rotlSH: (h: number, l: number, s: number) => number; rotlSL: (h: number, l: number, s: number) => number; rotlBH: (h: number, l: number, s: number) => number; rotlBL: (h: number, l: number, s: number) => number; add: typeof add; add3L: (Al: number, Bl: number, Cl: number) => number; add3H: (low: number, Ah: number, Bh: number, Ch: number) => number; add4L: (Al: number, Bl: number, Cl: number, Dl: number) => number; add4H: (low: number, Ah: number, Bh: number, Ch: number, Dh: number) => number; add5H: (low: number, Ah: number, Bh: number, Ch: number, Dh: number, Eh: number) => number; add5L: (Al: number, Bl: number, Cl: number, Dl: number, El: number) => number; } = {\n fromBig, split, toBig,\n shrSH, shrSL,\n rotrSH, rotrSL, rotrBH, rotrBL,\n rotr32H, rotr32L,\n rotlSH, rotlSL, rotlBH, rotlBL,\n add, add3L, add3H, add4L, add4H, add5H, add5L,\n};\nexport default u64;\n","/**\n * Internal helpers for blake hash.\n * @module\n */\nimport { rotr } from './utils.ts';\n\n/**\n * Internal blake variable.\n * For BLAKE2b, the two extra permutations for rounds 10 and 11 are SIGMA[10..11] = SIGMA[0..1].\n */\n// prettier-ignore\nexport const BSIGMA: Uint8Array = /* @__PURE__ */ Uint8Array.from([\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\n 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,\n 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,\n 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,\n 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,\n 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,\n 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11,\n 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10,\n 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5,\n 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0,\n 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,\n 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3,\n // Blake1, unused in others\n 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4,\n 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8,\n 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13,\n 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9,\n]);\n\n// prettier-ignore\nexport type Num4 = { a: number; b: number; c: number; d: number; };\n\n// Mixing function G splitted in two halfs\nexport function G1s(a: number, b: number, c: number, d: number, x: number): Num4 {\n a = (a + b + x) | 0;\n d = rotr(d ^ a, 16);\n c = (c + d) | 0;\n b = rotr(b ^ c, 12);\n return { a, b, c, d };\n}\n\nexport function G2s(a: number, b: number, c: number, d: number, x: number): Num4 {\n a = (a + b + x) | 0;\n d = rotr(d ^ a, 8);\n c = (c + d) | 0;\n b = rotr(b ^ c, 7);\n return { a, b, c, d };\n}\n","/**\n * blake2b (64-bit) & blake2s (8 to 32-bit) hash functions.\n * b could have been faster, but there is no fast u64 in js, so s is 1.5x faster.\n * @module\n */\nimport { BSIGMA, G1s, G2s } from './_blake.ts';\nimport { SHA256_IV } from './_md.ts';\nimport * as u64 from './_u64.ts';\n// prettier-ignore\nimport {\n abytes, aexists, anumber, aoutput,\n clean, createOptHasher, Hash, swap32IfBE, swap8IfBE, toBytes, u32,\n type CHashO, type Input\n} from './utils.ts';\n\n/** Blake hash options. dkLen is output length. key is used in MAC mode. salt is used in KDF mode. */\nexport type Blake2Opts = {\n dkLen?: number;\n key?: Input;\n salt?: Input;\n personalization?: Input;\n};\n\n// Same as SHA512_IV, but swapped endianness: LE instead of BE. iv[1] is iv[0], etc.\nconst B2B_IV = /* @__PURE__ */ Uint32Array.from([\n 0xf3bcc908, 0x6a09e667, 0x84caa73b, 0xbb67ae85, 0xfe94f82b, 0x3c6ef372, 0x5f1d36f1, 0xa54ff53a,\n 0xade682d1, 0x510e527f, 0x2b3e6c1f, 0x9b05688c, 0xfb41bd6b, 0x1f83d9ab, 0x137e2179, 0x5be0cd19,\n]);\n// Temporary buffer\nconst BBUF = /* @__PURE__ */ new Uint32Array(32);\n\n// Mixing function G splitted in two halfs\nfunction G1b(a: number, b: number, c: number, d: number, msg: Uint32Array, x: number) {\n // NOTE: V is LE here\n const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore\n let Al = BBUF[2 * a], Ah = BBUF[2 * a + 1]; // prettier-ignore\n let Bl = BBUF[2 * b], Bh = BBUF[2 * b + 1]; // prettier-ignore\n let Cl = BBUF[2 * c], Ch = BBUF[2 * c + 1]; // prettier-ignore\n let Dl = BBUF[2 * d], Dh = BBUF[2 * d + 1]; // prettier-ignore\n // v[a] = (v[a] + v[b] + x) | 0;\n let ll = u64.add3L(Al, Bl, Xl);\n Ah = u64.add3H(ll, Ah, Bh, Xh);\n Al = ll | 0;\n // v[d] = rotr(v[d] ^ v[a], 32)\n ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });\n ({ Dh, Dl } = { Dh: u64.rotr32H(Dh, Dl), Dl: u64.rotr32L(Dh, Dl) });\n // v[c] = (v[c] + v[d]) | 0;\n ({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));\n // v[b] = rotr(v[b] ^ v[c], 24)\n ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });\n ({ Bh, Bl } = { Bh: u64.rotrSH(Bh, Bl, 24), Bl: u64.rotrSL(Bh, Bl, 24) });\n (BBUF[2 * a] = Al), (BBUF[2 * a + 1] = Ah);\n (BBUF[2 * b] = Bl), (BBUF[2 * b + 1] = Bh);\n (BBUF[2 * c] = Cl), (BBUF[2 * c + 1] = Ch);\n (BBUF[2 * d] = Dl), (BBUF[2 * d + 1] = Dh);\n}\n\nfunction G2b(a: number, b: number, c: number, d: number, msg: Uint32Array, x: number) {\n // NOTE: V is LE here\n const Xl = msg[x], Xh = msg[x + 1]; // prettier-ignore\n let Al = BBUF[2 * a], Ah = BBUF[2 * a + 1]; // prettier-ignore\n let Bl = BBUF[2 * b], Bh = BBUF[2 * b + 1]; // prettier-ignore\n let Cl = BBUF[2 * c], Ch = BBUF[2 * c + 1]; // prettier-ignore\n let Dl = BBUF[2 * d], Dh = BBUF[2 * d + 1]; // prettier-ignore\n // v[a] = (v[a] + v[b] + x) | 0;\n let ll = u64.add3L(Al, Bl, Xl);\n Ah = u64.add3H(ll, Ah, Bh, Xh);\n Al = ll | 0;\n // v[d] = rotr(v[d] ^ v[a], 16)\n ({ Dh, Dl } = { Dh: Dh ^ Ah, Dl: Dl ^ Al });\n ({ Dh, Dl } = { Dh: u64.rotrSH(Dh, Dl, 16), Dl: u64.rotrSL(Dh, Dl, 16) });\n // v[c] = (v[c] + v[d]) | 0;\n ({ h: Ch, l: Cl } = u64.add(Ch, Cl, Dh, Dl));\n // v[b] = rotr(v[b] ^ v[c], 63)\n ({ Bh, Bl } = { Bh: Bh ^ Ch, Bl: Bl ^ Cl });\n ({ Bh, Bl } = { Bh: u64.rotrBH(Bh, Bl, 63), Bl: u64.rotrBL(Bh, Bl, 63) });\n (BBUF[2 * a] = Al), (BBUF[2 * a + 1] = Ah);\n (BBUF[2 * b] = Bl), (BBUF[2 * b + 1] = Bh);\n (BBUF[2 * c] = Cl), (BBUF[2 * c + 1] = Ch);\n (BBUF[2 * d] = Dl), (BBUF[2 * d + 1] = Dh);\n}\n\nfunction checkBlake2Opts(\n outputLen: number,\n opts: Blake2Opts | undefined = {},\n keyLen: number,\n saltLen: number,\n persLen: number\n) {\n anumber(keyLen);\n if (outputLen < 0 || outputLen > keyLen) throw new Error('outputLen bigger than keyLen');\n const { key, salt, personalization } = opts;\n if (key !== undefined && (key.length < 1 || key.length > keyLen))\n throw new Error('key length must be undefined or 1..' + keyLen);\n if (salt !== undefined && salt.length !== saltLen)\n throw new Error('salt must be undefined or ' + saltLen);\n if (personalization !== undefined && personalization.length !== persLen)\n throw new Error('personalization must be undefined or ' + persLen);\n}\n\n/** Class, from which others are subclassed. */\nexport abstract class BLAKE2<T extends BLAKE2<T>> extends Hash<T> {\n protected abstract compress(msg: Uint32Array, offset: number, isLast: boolean): void;\n protected abstract get(): number[];\n protected abstract set(...args: number[]): void;\n abstract destroy(): void;\n protected buffer: Uint8Array;\n protected buffer32: Uint32Array;\n protected finished = false;\n protected destroyed = false;\n protected length: number = 0;\n protected pos: number = 0;\n readonly blockLen: number;\n readonly outputLen: number;\n\n constructor(blockLen: number, outputLen: number) {\n super();\n anumber(blockLen);\n anumber(outputLen);\n this.blockLen = blockLen;\n this.outputLen = outputLen;\n this.buffer = new Uint8Array(blockLen);\n this.buffer32 = u32(this.buffer);\n }\n update(data: Input): this {\n aexists(this);\n data = toBytes(data);\n abytes(data);\n // Main difference with other hashes: there is flag for last block,\n // so we cannot process current block before we know that there\n // is the next one. This significantly complicates logic and reduces ability\n // to do zero-copy processing\n const { blockLen, buffer, buffer32 } = this;\n const len = data.length;\n const offset = data.byteOffset;\n const buf = data.buffer;\n for (let pos = 0; pos < len; ) {\n // If buffer is full and we still have input (don't process last block, same as blake2s)\n if (this.pos === blockLen) {\n swap32IfBE(buffer32);\n this.compress(buffer32, 0, false);\n swap32IfBE(buffer32);\n this.pos = 0;\n }\n const take = Math.min(blockLen - this.pos, len - pos);\n const dataOffset = offset + pos;\n // full block && aligned to 4 bytes && not last in input\n if (take === blockLen && !(dataOffset % 4) && pos + take < len) {\n const data32 = new Uint32Array(buf, dataOffset, Math.floor((len - pos) / 4));\n swap32IfBE(data32);\n for (let pos32 = 0; pos + blockLen < len; pos32 += buffer32.length, pos += blockLen) {\n this.length += blockLen;\n this.compress(data32, pos32, false);\n }\n swap32IfBE(data32);\n continue;\n }\n buffer.set(data.subarray(pos, pos + take), this.pos);\n this.pos += take;\n this.length += take;\n pos += take;\n }\n return this;\n }\n digestInto(out: Uint8Array): void {\n aexists(this);\n aoutput(out, this);\n const { pos, buffer32 } = this;\n this.finished = true;\n // Padding\n clean(this.buffer.subarray(pos));\n swap32IfBE(buffer32);\n this.compress(buffer32, 0, true);\n swap32IfBE(buffer32);\n const out32 = u32(out);\n this.get().forEach((v, i) => (out32[i] = swap8IfBE(v)));\n }\n digest(): Uint8Array {\n const { buffer, outputLen } = this;\n this.digestInto(buffer);\n const res = buffer.slice(0, outputLen);\n this.destroy();\n return res;\n }\n _cloneInto(to?: T): T {\n const { buffer, length, finished, destroyed, outputLen, pos } = this;\n to ||= new (this.constructor as any)({ dkLen: outputLen }) as T;\n to.set(...this.get());\n to.buffer.set(buffer);\n to.destroyed = destroyed;\n to.finished = finished;\n to.length = length;\n to.pos = pos;\n // @ts-ignore\n to.outputLen = outputLen;\n return to;\n }\n clone(): T {\n return this._cloneInto();\n }\n}\n\nexport class BLAKE2b extends BLAKE2<BLAKE2b> {\n // Same as SHA-512, but LE\n private v0l = B2B_IV[0] | 0;\n private v0h = B2B_IV[1] | 0;\n private v1l = B2B_IV[2] | 0;\n private v1h = B2B_IV[3] | 0;\n private v2l = B2B_IV[4] | 0;\n private v2h = B2B_IV[5] | 0;\n private v3l = B2B_IV[6] | 0;\n private v3h = B2B_IV[7] | 0;\n private v4l = B2B_IV[8] | 0;\n private v4h = B2B_IV[9] | 0;\n private v5l = B2B_IV[10] | 0;\n private v5h = B2B_IV[11] | 0;\n private v6l = B2B_IV[12] | 0;\n private v6h = B2B_IV[13] | 0;\n private v7l = B2B_IV[14] | 0;\n private v7h = B2B_IV[15] | 0;\n\n constructor(opts: Blake2Opts = {}) {\n const olen = opts.dkLen === undefined ? 64 : opts.dkLen;\n super(128, olen);\n checkBlake2Opts(olen, opts, 64, 16, 16);\n let { key, personalization, salt } = opts;\n let keyLength = 0;\n if (key !== undefined) {\n key = toBytes(key);\n keyLength = key.length;\n }\n this.v0l ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24);\n if (salt !== undefined) {\n salt = toBytes(salt);\n const slt = u32(salt);\n this.v4l ^= swap8IfBE(slt[0]);\n this.v4h ^= swap8IfBE(slt[1]);\n this.v5l ^= swap8IfBE(slt[2]);\n this.v5h ^= swap8IfBE(slt[3]);\n }\n if (personalization !== undefined) {\n personalization = toBytes(personalization);\n const pers = u32(personalization);\n this.v6l ^= swap8IfBE(pers[0]);\n this.v6h ^= swap8IfBE(pers[1]);\n this.v7l ^= swap8IfBE(pers[2]);\n this.v7h ^= swap8IfBE(pers[3]);\n }\n if (key !== undefined) {\n // Pad to blockLen and update\n const tmp = new Uint8Array(this.blockLen);\n tmp.set(key);\n this.update(tmp);\n }\n }\n // prettier-ignore\n protected get(): [\n number, number, number, number, number, number, number, number,\n number, number, number, number, number, number, number, number\n ] {\n let { v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h } = this;\n return [v0l, v0h, v1l, v1h, v2l, v2h, v3l, v3h, v4l, v4h, v5l, v5h, v6l, v6h, v7l, v7h];\n }\n // prettier-ignore\n protected set(\n v0l: number, v0h: number, v1l: number, v1h: number,\n v2l: number, v2h: number, v3l: number, v3h: number,\n v4l: number, v4h: number, v5l: number, v5h: number,\n v6l: number, v6h: number, v7l: number, v7h: number\n ): void {\n this.v0l = v0l | 0;\n this.v0h = v0h | 0;\n this.v1l = v1l | 0;\n this.v1h = v1h | 0;\n this.v2l = v2l | 0;\n this.v2h = v2h | 0;\n this.v3l = v3l | 0;\n this.v3h = v3h | 0;\n this.v4l = v4l | 0;\n this.v4h = v4h | 0;\n this.v5l = v5l | 0;\n this.v5h = v5h | 0;\n this.v6l = v6l | 0;\n this.v6h = v6h | 0;\n this.v7l = v7l | 0;\n this.v7h = v7h | 0;\n }\n protected compress(msg: Uint32Array, offset: number, isLast: boolean): void {\n this.get().forEach((v, i) => (BBUF[i] = v)); // First half from state.\n BBUF.set(B2B_IV, 16); // Second half from IV.\n let { h, l } = u64.fromBig(BigInt(this.length));\n BBUF[24] = B2B_IV[8] ^ l; // Low word of the offset.\n BBUF[25] = B2B_IV[9] ^ h; // High word.\n // Invert all bits for last block\n if (isLast) {\n BBUF[28] = ~BBUF[28];\n BBUF[29] = ~BBUF[29];\n }\n let j = 0;\n const s = BSIGMA;\n for (let i = 0; i < 12; i++) {\n G1b(0, 4, 8, 12, msg, offset + 2 * s[j++]);\n G2b(0, 4, 8, 12, msg, offset + 2 * s[j++]);\n G1b(1, 5, 9, 13, msg, offset + 2 * s[j++]);\n G2b(1, 5, 9, 13, msg, offset + 2 * s[j++]);\n G1b(2, 6, 10, 14, msg, offset + 2 * s[j++]);\n G2b(2, 6, 10, 14, msg, offset + 2 * s[j++]);\n G1b(3, 7, 11, 15, msg, offset + 2 * s[j++]);\n G2b(3, 7, 11, 15, msg, offset + 2 * s[j++]);\n\n G1b(0, 5, 10, 15, msg, offset + 2 * s[j++]);\n G2b(0, 5, 10, 15, msg, offset + 2 * s[j++]);\n G1b(1, 6, 11, 12, msg, offset + 2 * s[j++]);\n G2b(1, 6, 11, 12, msg, offset + 2 * s[j++]);\n G1b(2, 7, 8, 13, msg, offset + 2 * s[j++]);\n G2b(2, 7, 8, 13, msg, offset + 2 * s[j++]);\n G1b(3, 4, 9, 14, msg, offset + 2 * s[j++]);\n G2b(3, 4, 9, 14, msg, offset + 2 * s[j++]);\n }\n this.v0l ^= BBUF[0] ^ BBUF[16];\n this.v0h ^= BBUF[1] ^ BBUF[17];\n this.v1l ^= BBUF[2] ^ BBUF[18];\n this.v1h ^= BBUF[3] ^ BBUF[19];\n this.v2l ^= BBUF[4] ^ BBUF[20];\n this.v2h ^= BBUF[5] ^ BBUF[21];\n this.v3l ^= BBUF[6] ^ BBUF[22];\n this.v3h ^= BBUF[7] ^ BBUF[23];\n this.v4l ^= BBUF[8] ^ BBUF[24];\n this.v4h ^= BBUF[9] ^ BBUF[25];\n this.v5l ^= BBUF[10] ^ BBUF[26];\n this.v5h ^= BBUF[11] ^ BBUF[27];\n this.v6l ^= BBUF[12] ^ BBUF[28];\n this.v6h ^= BBUF[13] ^ BBUF[29];\n this.v7l ^= BBUF[14] ^ BBUF[30];\n this.v7h ^= BBUF[15] ^ BBUF[31];\n clean(BBUF);\n }\n destroy(): void {\n this.destroyed = true;\n clean(this.buffer32);\n this.set(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);\n }\n}\n\n/**\n * Blake2b hash function. 64-bit. 1.5x slower than blake2s in JS.\n * @param msg - message that would be hashed\n * @param opts - dkLen output length, key for MAC mode, salt, personalization\n */\nexport const blake2b: CHashO = /* @__PURE__ */ createOptHasher<BLAKE2b, Blake2Opts>(\n (opts) => new BLAKE2b(opts)\n);\n\n// =================\n// Blake2S\n// =================\n\n// prettier-ignore\nexport type Num16 = {\n v0: number; v1: number; v2: number; v3: number;\n v4: number; v5: number; v6: number; v7: number;\n v8: number; v9: number; v10: number; v11: number;\n v12: number; v13: number; v14: number; v15: number;\n};\n\n// prettier-ignore\nexport function compress(s: Uint8Array, offset: number, msg: Uint32Array, rounds: number,\n v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number,\n v8: number, v9: number, v10: number, v11: number, v12: number, v13: number, v14: number, v15: number,\n): Num16 {\n let j = 0;\n for (let i = 0; i < rounds; i++) {\n ({ a: v0, b: v4, c: v8, d: v12 } = G1s(v0, v4, v8, v12, msg[offset + s[j++]]));\n ({ a: v0, b: v4, c: v8, d: v12 } = G2s(v0, v4, v8, v12, msg[offset + s[j++]]));\n ({ a: v1, b: v5, c: v9, d: v13 } = G1s(v1, v5, v9, v13, msg[offset + s[j++]]));\n ({ a: v1, b: v5, c: v9, d: v13 } = G2s(v1, v5, v9, v13, msg[offset + s[j++]]));\n ({ a: v2, b: v6, c: v10, d: v14 } = G1s(v2, v6, v10, v14, msg[offset + s[j++]]));\n ({ a: v2, b: v6, c: v10, d: v14 } = G2s(v2, v6, v10, v14, msg[offset + s[j++]]));\n ({ a: v3, b: v7, c: v11, d: v15 } = G1s(v3, v7, v11, v15, msg[offset + s[j++]]));\n ({ a: v3, b: v7, c: v11, d: v15 } = G2s(v3, v7, v11, v15, msg[offset + s[j++]]));\n\n ({ a: v0, b: v5, c: v10, d: v15 } = G1s(v0, v5, v10, v15, msg[offset + s[j++]]));\n ({ a: v0, b: v5, c: v10, d: v15 } = G2s(v0, v5, v10, v15, msg[offset + s[j++]]));\n ({ a: v1, b: v6, c: v11, d: v12 } = G1s(v1, v6, v11, v12, msg[offset + s[j++]]));\n ({ a: v1, b: v6, c: v11, d: v12 } = G2s(v1, v6, v11, v12, msg[offset + s[j++]]));\n ({ a: v2, b: v7, c: v8, d: v13 } = G1s(v2, v7, v8, v13, msg[offset + s[j++]]));\n ({ a: v2, b: v7, c: v8, d: v13 } = G2s(v2, v7, v8, v13, msg[offset + s[j++]]));\n ({ a: v3, b: v4, c: v9, d: v14 } = G1s(v3, v4, v9, v14, msg[offset + s[j++]]));\n ({ a: v3, b: v4, c: v9, d: v14 } = G2s(v3, v4, v9, v14, msg[offset + s[j++]]));\n }\n return { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 };\n}\n\nconst B2S_IV = SHA256_IV;\nexport class BLAKE2s extends BLAKE2<BLAKE2s> {\n // Internal state, same as SHA-256\n private v0 = B2S_IV[0] | 0;\n private v1 = B2S_IV[1] | 0;\n private v2 = B2S_IV[2] | 0;\n private v3 = B2S_IV[3] | 0;\n private v4 = B2S_IV[4] | 0;\n private v5 = B2S_IV[5] | 0;\n private v6 = B2S_IV[6] | 0;\n private v7 = B2S_IV[7] | 0;\n\n constructor(opts: Blake2Opts = {}) {\n const olen = opts.dkLen === undefined ? 32 : opts.dkLen;\n super(64, olen);\n checkBlake2Opts(olen, opts, 32, 8, 8);\n let { key, personalization, salt } = opts;\n let keyLength = 0;\n if (key !== undefined) {\n key = toBytes(key);\n keyLength = key.length;\n }\n this.v0 ^= this.outputLen | (keyLength << 8) | (0x01 << 16) | (0x01 << 24);\n if (salt !== undefined) {\n salt = toBytes(salt);\n const slt = u32(salt as Uint8Array);\n this.v4 ^= swap8IfBE(slt[0]);\n this.v5 ^= swap8IfBE(slt[1]);\n }\n if (personalization !== undefined) {\n personalization = toBytes(personalization);\n const pers = u32(personalization as Uint8Array);\n this.v6 ^= swap8IfBE(pers[0]);\n this.v7 ^= swap8IfBE(pers[1]);\n }\n if (key !== undefined) {\n // Pad to blockLen and update\n abytes(key);\n const tmp = new Uint8Array(this.blockLen);\n tmp.set(key);\n this.update(tmp);\n }\n }\n protected get(): [number, number, number, number, number, number, number, number] {\n const { v0, v1, v2, v3, v4, v5, v6, v7 } = this;\n return [v0, v1, v2, v3, v4, v5, v6, v7];\n }\n // prettier-ignore\n protected set(\n v0: number, v1: number, v2: number, v3: number, v4: number, v5: number, v6: number, v7: number\n ): void {\n this.v0 = v0 | 0;\n this.v1 = v1 | 0;\n this.v2 = v2 | 0;\n this.v3 = v3 | 0;\n this.v4 = v4 | 0;\n this.v5 = v5 | 0;\n this.v6 = v6 | 0;\n this.v7 = v7 | 0;\n }\n protected compress(msg: Uint32Array, offset: number, isLast: boolean): void {\n const { h, l } = u64.fromBig(BigInt(this.length));\n // prettier-ignore\n const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =\n compress(\n BSIGMA, offset, msg, 10,\n this.v0, this.v1, this.v2, this.v3, this.v4, this.v5, this.v6, this.v7,\n B2S_IV[0], B2S_IV[1], B2S_IV[2], B2S_IV[3], l ^ B2S_IV[4], h ^ B2S_IV[5], isLast ? ~B2S_IV[6] : B2S_IV[6], B2S_IV[7]\n );\n this.v0 ^= v0 ^ v8;\n this.v1 ^= v1 ^ v9;\n this.v2 ^= v2 ^ v10;\n this.v3 ^= v3 ^ v11;\n this.v4 ^= v4 ^ v12;\n this.v5 ^= v5 ^ v13;\n this.v6 ^= v6 ^ v14;\n this.v7 ^= v7 ^ v15;\n }\n destroy(): void {\n this.destroyed = true;\n clean(this.buffer32);\n this.set(0, 0, 0, 0, 0, 0, 0, 0);\n }\n}\n\n/**\n * Blake2s hash function. Focuses on 8-bit to 32-bit platforms. 1.5x faster than blake2b in JS.\n * @param msg - message that would be hashed\n * @param opts - dkLen output length, key for MAC mode, salt, personalization\n */\nexport const blake2s: CHashO = /* @__PURE__ */ createOptHasher<BLAKE2s, Blake2Opts>(\n (opts) => new BLAKE2s(opts)\n);\n","/**\n * Blake3 fast hash is Blake2 with reduced security (round count). Can also be used as MAC & KDF.\n *\n * It is advertised as \"the fastest cryptographic hash\". However, it isn't true in JS.\n * Why is this so slow? While it should be 6x faster than blake2b, perf diff is only 20%:\n *\n * * There is only 30% reduction in number of rounds from blake2s\n * * Speed-up comes from tree structure, which is parallelized using SIMD & threading.\n * These features are not present in JS, so we only get overhead from trees.\n * * Parallelization only happens on 1024-byte chunks: there is no benefit for small inputs.\n * * It is still possible to make it faster using: a) loop unrolling b) web workers c) wasm\n * @module\n */\nimport { SHA256_IV } from './_md.ts';\nimport { fromBig } from './_u64.ts';\nimport { BLAKE2, compress } from './blake2.ts';\n// prettier-ignore\nimport {\n abytes, aexists, anumber, aoutput,\n clean, createXOFer, swap32IfBE, toBytes, u32, u8,\n type CHashXO, type HashXOF, type Input\n} from './utils.ts';\n\n// Flag bitset\nconst B3_Flags = {\n CHUNK_START: 0b1,\n CHUNK_END: 0b10,\n PARENT: 0b100,\n ROOT: 0b1000,\n KEYED_HASH: 0b10000,\n DERIVE_KEY_CONTEXT: 0b100000,\n DERIVE_KEY_MATERIAL: 0b1000000,\n} as const;\n\nconst B3_IV = SHA256_IV.slice();\n\nconst B3_SIGMA: Uint8Array = /* @__PURE__ */ (() => {\n const Id = Array.from({ length: 16 }, (_, i) => i);\n const permute = (arr: number[]) =>\n [2, 6, 3, 10, 7, 0, 4, 13, 1, 11, 12, 5, 9, 14, 15, 8].map((i) => arr[i]);\n const res: number[] = [];\n for (let i = 0, v = Id; i < 7; i++, v = permute(v)) res.push(...v);\n return Uint8Array.from(res);\n})();\n\n/**\n * Ensure to use EITHER `key` OR `context`, not both.\n *\n * * `key`: 32-byte MAC key.\n * * `context`: string for KDF. Should be hardcoded, globally unique, and application - specific.\n * A good default format for the context string is \"[application] [commit timestamp] [purpose]\".\n */\nexport type Blake3Opts = { dkLen?: number; key?: Input; context?: Input };\n\n/** Blake3 hash. Can be used as MAC and KDF. */\nexport class BLAKE3 extends BLAKE2<BLAKE3> implements HashXOF<BLAKE3> {\n private chunkPos = 0; // Position of current block in chunk\n private chunksDone = 0; // How many chunks we already have\n private flags = 0 | 0;\n private IV: Uint32Array;\n private state: Uint32Array;\n private stack: Uint32Array[] = [];\n // Output\n private posOut = 0;\n private bufferOut32 = new Uint32Array(16);\n private bufferOut: Uint8Array;\n private chunkOut = 0; // index of output chunk\n private enableXOF = true;\n\n constructor(opts: Blake3Opts = {}, flags = 0) {\n super(64, opts.dkLen === undefined ? 32 : opts.dkLen);\n const { key, context } = opts;\n const hasContext = context !== undefined;\n if (key !== undefined) {\n if (hasContext) throw new Error('Only \"key\" or \"context\" can be specified at same time');\n const k = toBytes(key).slice();\n abytes(k, 32);\n this.IV = u32(k);\n swap32IfBE(this.IV);\n this.flags = flags | B3_Flags.KEYED_HASH;\n } else if (hasContext) {\n const ctx = toBytes(context);\n const contextKey = new BLAKE3({ dkLen: 32 }, B3_Flags.DERIVE_KEY_CONTEXT)\n .update(ctx)\n .digest();\n this.IV = u32(contextKey);\n swap32IfBE(this.IV);\n this.flags = flags | B3_Flags.DERIVE_KEY_MATERIAL;\n } else {\n this.IV = B3_IV.slice();\n this.flags = flags;\n }\n this.state = this.IV.slice();\n this.bufferOut = u8(this.bufferOut32);\n }\n // Unused\n protected get(): [] {\n return [];\n }\n protected set(): void {}\n private b2Compress(counter: number, flags: number, buf: Uint32Array, bufPos: number = 0) {\n const { state: s, pos } = this;\n const { h, l } = fromBig(BigInt(counter), true);\n // prettier-ignore\n const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =\n compress(\n B3_SIGMA, bufPos, buf, 7,\n s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7],\n B3_IV[0], B3_IV[1], B3_IV[2], B3_IV[3], h, l, pos, flags\n );\n s[0] = v0 ^ v8;\n s[1] = v1 ^ v9;\n s[2] = v2 ^ v10;\n s[3] = v3 ^ v11;\n s[4] = v4 ^ v12;\n s[5] = v5 ^ v13;\n s[6] = v6 ^ v14;\n s[7] = v7 ^ v15;\n }\n protected compress(buf: Uint32Array, bufPos: number = 0, isLast: boolean = false): void {\n // Compress last block\n let flags = this.flags;\n if (!this.chunkPos) flags |= B3_Flags.CHUNK_START;\n if (this.chunkPos === 15 || isLast) flags |= B3_Flags.CHUNK_END;\n if (!isLast) this.pos = this.blockLen;\n this.b2Compress(this.chunksDone, flags, buf, bufPos);\n this.chunkPos += 1;\n // If current block is last in chunk (16 blocks), then compress chunks\n if (this.chunkPos === 16 || isLast) {\n let chunk = this.state;\n this.state = this.IV.slice();\n // If not the last one, compress only when there are trailing zeros in chunk counter\n // chunks used as binary tree where current stack is path. Zero means current leaf is finished and can be compressed.\n // 1 (001) - leaf not finished (just push current chunk to stack)\n // 2 (010) - leaf finished at depth=1 (merge with last elm on stack and push back)\n // 3 (011) - last leaf not finished\n // 4 (100) - leafs finished at depth=1 and depth=2\n for (let last, chunks = this.chunksDone + 1; isLast || !(chunks & 1); chunks >>= 1) {\n if (!(last = this.stack.pop())) break;\n this.buffer32.set(last, 0);\n this.buffer32.set(chunk, 8);\n this.pos = this.blockLen;\n this.b2Compress(0, this.flags | B3_Flags.PARENT, this.buffer32, 0);\n chunk = this.state;\n this.state = this.IV.slice();\n }\n this.chunksDone++;\n this.chunkPos = 0;\n this.stack.push(chunk);\n }\n this.pos = 0;\n }\n _cloneInto(to?: BLAKE3): BLAKE3 {\n to = super._cloneInto(to) as BLAKE3;\n const { IV, flags, state, chunkPos, posOut, chunkOut, stack, chunksDone } = this;\n to.state.set(state.slice());\n to.stack = stack.map((i) => Uint32Array.from(i));\n to.IV.set(IV);\n to.flags = flags;\n to.chunkPos = chunkPos;\n to.chunksDone = chunksDone;\n to.posOut = posOut;\n to.chunkOut = chunkOut;\n to.enableXOF = this.enableXOF;\n to.bufferOut32.set(this.bufferOut32);\n return to;\n }\n destroy(): void {\n this.destroyed = true;\n clean(this.state, this.buffer32, this.IV, this.bufferOut32);\n clean(...this.stack);\n }\n // Same as b2Compress, but doesn't modify state and returns 16 u32 array (instead of 8)\n private b2CompressOut() {\n const { state: s, pos, flags, buffer32, bufferOut32: out32 } = this;\n const { h, l } = fromBig(BigInt(this.chunkOut++));\n swap32IfBE(buffer32);\n // prettier-ignore\n const { v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 } =\n compress(\n B3_SIGMA, 0, buffer32, 7,\n s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7],\n B3_IV[0], B3_IV[1], B3_IV[2], B3_IV[3], l, h, pos, flags\n );\n out32[0] = v0 ^ v8;\n out32[1] = v1 ^ v9;\n out32[2] = v2 ^ v10;\n out32[3] = v3 ^ v11;\n out32[4] = v4 ^ v12;\n out32[5] = v5 ^ v13;\n out32[6] = v6 ^ v14;\n out32[7] = v7 ^ v15;\n out32[8] = s[0] ^ v8;\n out32[9] = s[1] ^ v9;\n out32[10] = s[2] ^ v10;\n out32[11] = s[3] ^ v11;\n out32[12] = s[4] ^ v12;\n out32[13] = s[5] ^ v13;\n out32[14] = s[6] ^ v14;\n out32[15] = s[7] ^ v15;\n swap32IfBE(buffer32);\n swap32IfBE(out32);\n this.posOut = 0;\n }\n protected finish(): void {\n if (this.finished) return;\n this.finished = true;\n // Padding\n clean(this.buffer.subarray(this.pos));\n // Process last chunk\n let flags = this.flags | B3_Flags.ROOT;\n if (this.stack.length) {\n flags |= B3_Flags.PARENT;\n swap32IfBE(this.buffer32);\n this.compress(this.buffer32, 0, true);\n swap32IfBE(this.buffer32);\n this.chunksDone = 0;\n this.pos = this.blockLen;\n } else {\n flags |= (!this.chunkPos ? B3_Flags.CHUNK_START : 0) | B3_Flags.CHUNK_END;\n }\n this.flags = flags;\n this.b2CompressOut();\n }\n private writeInto(out: Uint8Array) {\n aexists(this, false);\n abytes(out);\n this.finish();\n const { blockLen, bufferOut } = this;\n for (let pos = 0, len = out.length; pos < len; ) {\n if (this.posOut >= blockLen) this.b2CompressOut();\n const take = Math.min(blockLen - this.posOut, len - pos);\n out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);\n this.posOut += take;\n pos += take;\n }\n return out;\n }\n xofInto(out: Uint8Array): Uint8Array {\n if (!this.enableXOF) throw new Error('XOF is not possible after digest call');\n return this.writeInto(out);\n }\n xof(bytes: number): Uint8Array {\n anumber(bytes);\n return this.xofInto(new Uint8Array(bytes));\n }\n digestInto(out: Uint8Array): Uint8Array {\n aoutput(out, this);\n if (this.finished) throw new Error('digest() was already called');\n this.enableXOF = false;\n this.writeInto(out);\n this.destroy();\n return out;\n }\n digest(): Uint8Array {\n return this.digestInto(new Uint8Array(this.outputLen));\n }\n}\n\n/**\n * BLAKE3 hash function. Can be used as MAC and KDF.\n * @param msg - message that would be hashed\n * @param opts - `dkLen` for output length, `key` for MAC mode, `context` for KDF mode\n * @example\n * const data = new Uint8Array(32);\n * const hash = blake3(data);\n * const mac = blake3(data, { key: new Uint8Array(32) });\n * const kdf = blake3(data, { context: 'application name' });\n */\nexport const blake3: CHashXO = /* @__PURE__ */ createXOFer<BLAKE3, Blake3Opts>(\n (opts) => new BLAKE3(opts)\n);\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CONTINUOUS-SHAVE-022: The intent cache uses a two-level\n// directory structure (<cacheDir>/<key[0..2]>/<key>.json) to avoid filesystem\n// performance degradation with large numbers of files in a single directory.\n// Writes are atomic: content goes to a .tmp.<random> file then renamed into\n// place, so a concurrent reader never sees a partial write.\n// Status: decided (MASTER_PLAN.md DEC-CONTINUOUS-SHAVE-022)\n// Rationale: Atomic rename is the standard POSIX durability pattern. The\n// two-level sharding mirrors content-addressable stores like Git's object DB.\n\nimport { mkdir, readFile, unlink, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { IntentCard } from \"../intent/types.js\";\nimport { renameWithRetry } from \"./atomic-write.js\";\n\n/**\n * Compute the shard directory (first 3 hex chars of the key) and full file\n * path for a given cache key.\n */\nfunction cachePaths(cacheDir: string, cacheKey: string): { shardDir: string; filePath: string } {\n const shard = cacheKey.slice(0, 3);\n const shardDir = join(cacheDir, shard);\n const filePath = join(shardDir, `${cacheKey}.json`);\n return { shardDir, filePath };\n}\n\n/**\n * Read a cached intent card from disk.\n *\n * Returns the raw parsed JSON value (unvalidated) on a cache hit, or\n * `undefined` on a miss (ENOENT). If the file exists but cannot be parsed,\n * logs a warning, deletes the corrupt entry, and returns `undefined`.\n *\n * Validation against the IntentCard schema is the caller's responsibility\n * (extractIntent calls validateIntentCard after readIntent returns).\n *\n * @param cacheDir - Root cache directory (e.g. <projectRoot>/.yakcc/shave-cache/intent/).\n * @param cacheKey - 64-char hex key produced by keyFromIntentInputs().\n * @returns Parsed JSON value, or undefined on miss.\n */\nexport async function readIntent(cacheDir: string, cacheKey: string): Promise<unknown | undefined> {\n const { filePath } = cachePaths(cacheDir, cacheKey);\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf-8\");\n } catch (err) {\n if (isEnoent(err)) return undefined;\n // Stat or permission error — treat as miss; warn.\n console.warn(`[shave cache] Failed to read cache file ${filePath}:`, err);\n return undefined;\n }\n\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n console.warn(`[shave cache] Corrupt cache entry at ${filePath}; deleting.`);\n await unlink(filePath).catch(() => {\n // Best-effort delete; ignore failures.\n });\n return undefined;\n }\n}\n\n/**\n * Atomically write an IntentCard to the cache.\n *\n * Steps:\n * 1. Ensure the shard directory exists (mkdir -p).\n * 2. Write JSON to `<key>.json.tmp.<random>`.\n * 3. Rename the tmp file to `<key>.json`.\n * 4. On rename failure, attempt to unlink the tmp file and rethrow.\n *\n * Concurrent readers will see either the old file or the new file, never a\n * partial write.\n *\n * @param cacheDir - Root cache directory.\n * @param cacheKey - 64-char hex key.\n * @param value - Validated IntentCard to persist.\n */\nexport async function writeIntent(\n cacheDir: string,\n cacheKey: string,\n value: IntentCard,\n): Promise<void> {\n const { shardDir, filePath } = cachePaths(cacheDir, cacheKey);\n\n await mkdir(shardDir, { recursive: true });\n\n const tmpPath = `${filePath}.tmp.${Math.random().toString(36).slice(2)}`;\n const json = JSON.stringify(value, null, 2);\n\n await writeFile(tmpPath, json, \"utf-8\");\n\n try {\n await renameWithRetry(tmpPath, filePath);\n } catch (err) {\n // Rename failed — clean up the tmp file to avoid orphaned partials.\n await unlink(tmpPath).catch(() => {\n // Best-effort; ignore cleanup failures.\n });\n throw err;\n }\n}\n\n/** Type guard for ENOENT errors from Node.js fs operations. */\nfunction isEnoent(err: unknown): boolean {\n return (\n typeof err === \"object\" &&\n err !== null &&\n \"code\" in err &&\n (err as { code: unknown }).code === \"ENOENT\"\n );\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-SHAVE-CACHE-RENAME-RETRY-001: writeIntent's tmp→final rename\n// retries on EPERM/EBUSY (Windows transient lock) with bounded exponential\n// backoff (5 attempts: 10/20/40/80/160 ms; ~310 ms total worst-case budget).\n// Status: decided (plans/wi-525-cache-eperm.md §4.1)\n// Rationale: Windows MoveFileEx surfaces EPERM/EBUSY when the destination\n// path is briefly held by a concurrent writer's handle (sibling writeIntent\n// completing its own tmp→final rename). The lock window typically clears in\n// <100 ms; 5 attempts at ~310 ms total absorbs the 99th-percentile contention\n// window without masking genuine persistent failures. Non-retryable codes\n// (EISDIR, ENOENT, EACCES, ENOSPC, …) rethrow immediately on the first\n// attempt so existing error semantics are fully preserved. POSIX rename is\n// atomic over open handles, so this helper is a no-op overhead on Linux/macOS.\n\nimport { rename } from \"node:fs/promises\";\n\n/** Maximum number of rename attempts (1 initial + 4 retries). */\nconst MAX_ATTEMPTS = 5;\n\n/**\n * Exponential backoff delays in milliseconds between successive attempts.\n * Index 0 is the delay *after* the first failed attempt, index 3 after the\n * fourth. The fifth attempt (if it fails) exhausts the budget and rethrows\n * without sleeping.\n */\nconst BACKOFF_MS: readonly number[] = [10, 20, 40, 80, 160];\n\n/**\n * Error codes that indicate a transient Windows file-lock condition.\n * All other codes are treated as permanent and rethrown immediately.\n */\nconst RETRYABLE_CODES = new Set<string>([\"EPERM\", \"EBUSY\"]);\n\n/**\n * Atomically rename `src` to `dst`, retrying on transient Windows lock errors.\n *\n * On POSIX systems `fs.rename` is atomic with respect to open handles and\n * never returns EPERM/EBUSY from the lock-contention path, so this function\n * adds zero observable overhead on Linux/macOS.\n *\n * On Windows, concurrent `writeIntent` calls that race on the same `dst` path\n * may surface EPERM (ERROR_ACCESS_DENIED from MoveFileEx) or EBUSY\n * (ERROR_SHARING_VIOLATION) while a sibling handle holds the destination\n * briefly. This helper retries up to MAX_ATTEMPTS times with exponential\n * backoff, then rethrows the final error so the caller's cleanup logic fires\n * as before.\n *\n * Non-retryable codes (EISDIR, ENOENT, EACCES on the parent directory, ENOSPC,\n * etc.) are rethrown on the very first attempt — preserving all existing error\n * semantics observed by callers and tests.\n *\n * @param src - Absolute path to the source (tmp) file.\n * @param dst - Absolute path to the destination (final) file.\n */\nexport async function renameWithRetry(src: string, dst: string): Promise<void> {\n let lastErr: unknown;\n\n for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {\n try {\n await rename(src, dst);\n return; // success — done\n } catch (err) {\n lastErr = err;\n\n // Extract the error code; unknown shape → treat as non-retryable.\n const code =\n err !== null && typeof err === \"object\" && \"code\" in err\n ? (err as { code: unknown }).code\n : undefined;\n\n if (typeof code !== \"string\" || !RETRYABLE_CODES.has(code)) {\n // Non-retryable: rethrow the original error object immediately so the\n // caller sees the exact error (important for the EISDIR test in\n // cache.test.ts:190 which asserts rejects.toThrow() on the original).\n throw err;\n }\n\n // Retryable (EPERM or EBUSY): sleep before the next attempt, except\n // after the last attempt where we are about to rethrow anyway.\n if (attempt < MAX_ATTEMPTS - 1) {\n const delay = BACKOFF_MS[attempt] ?? BACKOFF_MS[BACKOFF_MS.length - 1] ?? 160;\n await new Promise<void>((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n // Exhausted all attempts; rethrow the last retryable error so the caller's\n // catch block (which unlinks the tmp file) fires normally.\n throw lastErr;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CONTINUOUS-SHAVE-022: Source normalization ensures that\n// superficial differences (CRLF vs LF, trailing whitespace) do not produce\n// distinct cache keys. The rule is intentionally minimal: only line-ending\n// normalization and leading/trailing whitespace trimming. More aggressive\n// normalization (e.g. removing comments) would alter the semantic content\n// and produce incorrect cache hits.\n// Status: decided (MASTER_PLAN.md DEC-CONTINUOUS-SHAVE-022)\n// Rationale: Minimal normalization maximizes cache hit rate for the most\n// common superficial differences (editor line-ending settings, trailing\n// newlines) while preserving all semantically meaningful content.\n\n/**\n * Normalize a source string for cache-key computation.\n *\n * Normalization is intentionally minimal:\n * 1. Replace all CRLF sequences with LF.\n * 2. Trim leading and trailing whitespace.\n *\n * This ensures that editor line-ending settings and trailing newlines do not\n * produce spurious cache misses. The normalized form is used only for hashing;\n * the original source text is sent to the model unchanged.\n *\n * @param s - Raw source string from the candidate block.\n * @returns Normalized string suitable for BLAKE3 hashing.\n */\nexport function normalizeSource(s: string): string {\n return s.replace(/\\r\\n/g, \"\\n\").trim();\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CONTINUOUS-SHAVE-022: Cache keys are BLAKE3 hashes of the\n// concatenated inputs, NUL-delimited to prevent collisions across field\n// boundaries. Two separate keys are produced: one for the source alone\n// (sourceHash, used inside the IntentCard), and one composite key for the\n// cache file name (incorporates model, prompt version, schema version).\n// Status: decided (MASTER_PLAN.md DEC-CONTINUOUS-SHAVE-022)\n// Rationale: Separating sourceHash from the composite cache key lets the\n// IntentCard record what source it was extracted from (sourceHash) while the\n// cache file name encodes the full extraction context that must match for a\n// cache hit to be valid.\n\nimport { blake3 } from \"@noble/hashes/blake3\";\nimport { bytesToHex } from \"@noble/hashes/utils\";\nimport { normalizeSource } from \"./normalize.js\";\n\nconst encoder = new TextEncoder();\n\n/**\n * Compute the BLAKE3-256 hex hash of the normalized source text.\n *\n * This value is stored in IntentCard.sourceHash and is the first input\n * to the composite cache key.\n *\n * @param unitSource - Raw source string from the candidate block.\n * @returns 64-character lowercase hex string.\n */\nexport function sourceHash(unitSource: string): string {\n const normalized = normalizeSource(unitSource);\n return bytesToHex(blake3(encoder.encode(normalized)));\n}\n\n/**\n * Parameters for the composite intent-extraction cache key.\n */\nexport interface IntentKeyInputs {\n readonly sourceHash: string;\n readonly modelTag: string;\n readonly promptVersion: string;\n readonly schemaVersion: number;\n}\n\n/**\n * Derive the file-system cache key from the full set of extraction inputs.\n *\n * The key is BLAKE3-256 of the NUL-delimited concatenation:\n * sourceHash \\x00 modelTag \\x00 promptVersion \\x00 schemaVersion\n *\n * NUL delimiters prevent collisions where one field's value is a prefix of\n * another (e.g. sourceHash=\"abc\\x00def\" with modelTag=\"\" vs\n * sourceHash=\"abc\" with modelTag=\"def\").\n *\n * @returns 64-character lowercase hex string used as the cache filename stem.\n */\nexport function keyFromIntentInputs(inputs: IntentKeyInputs): string {\n const { sourceHash: sh, modelTag, promptVersion, schemaVersion } = inputs;\n const raw = `${sh}\\x00${modelTag}\\x00${promptVersion}\\x00${schemaVersion}`;\n return bytesToHex(blake3(encoder.encode(raw)));\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CORPUS-002\n// title: AI-derived corpus uses the same file-cache.ts surface with corpus-v1 schemaVersion\n// status: decided (WI-016)\n// rationale:\n// DEC-SHAVE-003 mandates a single file-cache authority. The corpus AI path reuses\n// file-cache.ts (readIntent/writeIntent) and key.ts (keyFromIntentInputs/sourceHash)\n// with a distinct schemaVersion constant (\"corpus-v1\" numeric equivalent) so that\n// corpus and intent cache entries cannot collide. No parallel cache directory, no\n// alternative serialization format.\n//\n// DEC-SHAVE-002 offline discipline: the AI-derived path works without ANTHROPIC_API_KEY\n// when the cache is warm. The actual Anthropic SDK call is never made in unit tests.\n// Integration tests that exercise the live path are gated by the YAKCC_CORPUS_AI_TEST\n// env flag and default OFF in CI.\n//\n// Cache key domain separation: CORPUS_SCHEMA_VERSION = 2 (distinct from\n// INTENT_SCHEMA_VERSION = 1) ensures intent and corpus entries never collide even\n// if the same source text is processed by both pipelines.\n\nimport { blake3 } from \"@noble/hashes/blake3\";\nimport { bytesToHex } from \"@noble/hashes/utils\";\nimport { readIntent, writeIntent } from \"../cache/file-cache.js\";\nimport { sourceHash as computeSourceHash, keyFromIntentInputs } from \"../cache/key.js\";\nimport type { CorpusResult, IntentCardInput } from \"./types.js\";\n\nconst encoder = new TextEncoder();\n\n/**\n * Canonical artifact path for AI-derived property-test files.\n */\nconst AI_DERIVED_PATH = \"property-tests.fast-check.ts\";\n\n/**\n * Schema version discriminant for corpus cache entries.\n *\n * Must be different from INTENT_SCHEMA_VERSION (1) to prevent collisions.\n * Corpus entries and intent entries share the same cache directory layout\n * (file-cache.ts two-level sharding) but are distinguished by this version.\n *\n * @decision DEC-CORPUS-002: value = 2 (intent = 1, corpus = 2)\n */\nexport const CORPUS_SCHEMA_VERSION = 2 as const;\n\n/**\n * Model tag used for corpus AI extraction.\n * Defaults to the same DEFAULT_MODEL as intent extraction for consistency.\n */\nexport const CORPUS_DEFAULT_MODEL = \"claude-haiku-4-5-20251001\";\n\n/**\n * Prompt version for the corpus extraction prompt template.\n * Bump when the corpus generation prompt changes to invalidate stale cache entries.\n */\nexport const CORPUS_PROMPT_VERSION = \"corpus-1\";\n\n/**\n * Inputs for AI-derived corpus cache key derivation.\n *\n * These mirror IntentKeyInputs from cache/key.ts — the same keyFromIntentInputs()\n * function is reused with CORPUS_SCHEMA_VERSION so entries cannot collide.\n */\nexport interface CorpusKeySpec {\n readonly source: string;\n readonly cacheDir: string;\n readonly model?: string | undefined;\n readonly promptVersion?: string | undefined;\n}\n\n/**\n * Derive the cache key for a corpus entry.\n *\n * Uses the same BLAKE3-based derivation as intent extraction\n * (keyFromIntentInputs) but with CORPUS_SCHEMA_VERSION = 2.\n */\nexport function corpusCacheKey(spec: CorpusKeySpec): string {\n const model = spec.model ?? CORPUS_DEFAULT_MODEL;\n const pv = spec.promptVersion ?? CORPUS_PROMPT_VERSION;\n const sh = computeSourceHash(spec.source);\n return keyFromIntentInputs({\n sourceHash: sh,\n modelTag: model,\n promptVersion: pv,\n schemaVersion: CORPUS_SCHEMA_VERSION,\n });\n}\n\n/**\n * A cached corpus entry as stored in the file cache.\n *\n * The `content` field is the fast-check property-test file content (UTF-8 string).\n * The `schemaVersion` discriminant must match CORPUS_SCHEMA_VERSION for the entry\n * to be considered valid.\n */\nexport interface CachedCorpusEntry {\n readonly schemaVersion: typeof CORPUS_SCHEMA_VERSION;\n readonly content: string;\n readonly sourceHash: string;\n readonly generatedAt: string;\n}\n\n/**\n * Type guard for a valid CachedCorpusEntry.\n */\nfunction isCachedCorpusEntry(value: unknown): value is CachedCorpusEntry {\n if (value === null || typeof value !== \"object\") return false;\n const obj = value as Record<string, unknown>;\n return (\n obj.schemaVersion === CORPUS_SCHEMA_VERSION &&\n typeof obj.content === \"string\" &&\n obj.content.length > 0 &&\n typeof obj.sourceHash === \"string\" &&\n typeof obj.generatedAt === \"string\"\n );\n}\n\n/**\n * Read a cached corpus entry from disk.\n *\n * Returns the CachedCorpusEntry on a cache hit, or undefined on a miss or\n * invalid/stale entry (schemaVersion mismatch counts as a miss).\n */\nexport async function readCorpusCache(\n cacheDir: string,\n key: string,\n): Promise<CachedCorpusEntry | undefined> {\n // readIntent returns unknown | undefined; we validate shape ourselves.\n // This reuse is intentional: same file-cache.ts, same two-level sharding,\n // same atomic write protocol. The only difference is the schemaVersion.\n const raw = await (readIntent as (dir: string, key: string) => Promise<unknown>)(cacheDir, key);\n if (raw === undefined) return undefined;\n if (isCachedCorpusEntry(raw)) return raw;\n // Wrong schema version or corrupted entry — treat as miss.\n return undefined;\n}\n\n/**\n * Write a corpus entry to the file cache.\n *\n * Uses writeIntent() with a CachedCorpusEntry cast to IntentCard — the cache\n * is content-agnostic (stores JSON) so the type cast is safe at the storage level.\n * The schemaVersion discriminant prevents cross-domain reads.\n */\nexport async function writeCorpusCache(\n cacheDir: string,\n key: string,\n entry: CachedCorpusEntry,\n): Promise<void> {\n // writeIntent accepts IntentCard; we cast because the cache is JSON-agnostic.\n // The CORPUS_SCHEMA_VERSION discriminant ensures a corpus entry is never\n // mistakenly read by the intent extraction path (schemaVersion=1 check fails).\n await (writeIntent as (dir: string, key: string, value: unknown) => Promise<void>)(\n cacheDir,\n key,\n entry,\n );\n}\n\n/**\n * Attempt AI-derived corpus extraction from the file cache (offline path).\n *\n * Returns a CorpusResult with source=\"ai-derived\" on a cache hit, or undefined\n * on a miss. Does NOT call the Anthropic SDK — that path is handled by the\n * integration test helper (seedCorpusCache) and the live extraction path\n * (not wired in unit tests per DEC-SHAVE-002).\n *\n * @param intentCard - The extracted intent card for this atom.\n * @param source - The raw source text of the atom.\n * @param cacheDir - Root cache directory.\n * @param model - Optional model tag override (defaults to CORPUS_DEFAULT_MODEL).\n * @param promptVersion - Optional prompt version override (defaults to CORPUS_PROMPT_VERSION).\n * @returns CorpusResult on cache hit, undefined on miss.\n */\nexport async function extractFromAiDerivedCached(\n intentCard: IntentCardInput,\n source: string,\n cacheDir: string,\n model?: string,\n promptVersion?: string,\n): Promise<CorpusResult | undefined> {\n const key = corpusCacheKey({ source, cacheDir, model, promptVersion });\n const cached = await readCorpusCache(cacheDir, key);\n if (cached === undefined) return undefined;\n\n const bytes = encoder.encode(cached.content);\n const contentHash = bytesToHex(blake3(bytes));\n\n // Attach intentCard reference for provenance (unused in return value but\n // accessible via closure for future diagnostics).\n void intentCard;\n\n return {\n source: \"ai-derived\",\n bytes,\n path: AI_DERIVED_PATH,\n contentHash,\n };\n}\n\n/**\n * Write a fast-check corpus string into the AI-derived corpus cache.\n *\n * **Test-helper only.** Do NOT call from production code. Production corpus\n * entries are written by the live AI extraction path (not wired in unit tests).\n *\n * This function is the corpus analogue of seedIntentCache() from index.ts.\n * It uses the same key derivation as extractFromAiDerivedCached() so that\n * a seeded entry is found on the first cache lookup.\n *\n * @param spec - Source text and cache location; identifies the cache slot.\n * @param content - The fast-check file content to store.\n */\nexport async function seedCorpusCache(spec: CorpusKeySpec, content: string): Promise<void> {\n const key = corpusCacheKey(spec);\n const sh = computeSourceHash(spec.source);\n const entry: CachedCorpusEntry = {\n schemaVersion: CORPUS_SCHEMA_VERSION,\n content,\n sourceHash: sh,\n generatedAt: new Date().toISOString(),\n };\n await writeCorpusCache(spec.cacheDir, key, entry);\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-V2-07-PREFLIGHT-L8-001\n// title: props-file extraction uses name-based prop_<atom>_<property> mapping, whole-file corpus\n// status: accepted (WI-V2-07-PREFLIGHT L8)\n// rationale:\n// The props-file source is the highest-priority corpus extractor. When a sibling\n// *.props.ts file exists adjacent to the atom's source file, this extractor:\n// 1. Reads the props file.\n// 2. Infers the atom function name from the atom source text.\n// 3. Checks whether any prop_<atomName>_* export exists in the props file.\n// 4. If found, uses the ENTIRE props file content as the corpus bytes.\n//\n// Whole-file strategy (vs per-export extraction): the props file is authored\n// per-source-file, not per-atom. It typically contains cross-atom helper\n// arbitraries (e.g. contractSpecArb in merkle.props.ts) that are shared between\n// prop_* exports. Extracting only matching exports would silently drop these\n// dependencies, producing a broken corpus artifact. Using the whole file avoids\n// this and is correct since the file IS the property suite for all atoms in\n// the sibling source.\n//\n// DEC-V2-GLUE-AWARE-SHAVE-001: glue atoms (kind=\"glue\") have no intentCard and\n// are never passed to extractCorpus(); this extractor is never called for glue.\n\nimport { readFile } from \"node:fs/promises\";\nimport { blake3 } from \"@noble/hashes/blake3\";\nimport { bytesToHex } from \"@noble/hashes/utils\";\nimport type { CorpusResult, IntentCardInput } from \"./types.js\";\n\nconst encoder = new TextEncoder();\n\n/**\n * Canonical artifact path for props-file corpus results.\n * Parameterized by atom name so different atoms get distinct manifest paths.\n */\nfunction propsArtifactPath(atomName: string): string {\n return `${atomName}.props.ts`;\n}\n\n/**\n * Attempt to extract a property-test corpus from a sibling *.props.ts file.\n *\n * Reads propsFilePath and checks whether it contains any `prop_<atomName>_*`\n * export corresponding to the atom's function name (inferred from `source`).\n * If found, returns the entire file content as the corpus artifact.\n *\n * Returns undefined when:\n * - The file does not exist or cannot be read.\n * - No function name can be inferred from the atom source.\n * - No prop_<atomName>_* export is found in the props file.\n *\n * Callers should fall through to the next source in the priority chain when\n * this function returns undefined.\n *\n * @param propsFilePath - Absolute path to the sibling *.props.ts file.\n * @param _intentCard - Unused (kept for signature symmetry with other extractors).\n * @param source - Raw source text of the atom; used to infer the function name.\n * @returns A CorpusResult with source=\"props-file\", or undefined on no match.\n */\nexport async function extractFromPropsFile(\n propsFilePath: string,\n _intentCard: IntentCardInput,\n source: string,\n): Promise<CorpusResult | undefined> {\n let fileContent: string;\n try {\n fileContent = await readFile(propsFilePath, \"utf-8\");\n } catch {\n return undefined;\n }\n\n const atomName = inferFunctionName(source);\n if (atomName === undefined) {\n return undefined;\n }\n\n // Check whether any prop_<atomName>_* export exists (name-based mapping).\n // The regex uses a word boundary before `prop_` to avoid false matches on\n // names like `my_prop_foo_bar` (which would not be a valid prop export).\n // Escape regex meta chars in atomName before substitution. Fixes #165: identifiers\n // like `$` or `$0` contain regex metacharacters (notably `$` as end-of-input anchor)\n // that would silently break the match when interpolated unescaped.\n const escapedAtomName = atomName.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n if (!new RegExp(`(?:^|\\\\s|;)export\\\\s+const\\\\s+prop_${escapedAtomName}_`).test(fileContent)) {\n return undefined;\n }\n\n const bytes = encoder.encode(fileContent);\n const contentHash = bytesToHex(blake3(bytes));\n\n return {\n source: \"props-file\",\n bytes,\n path: propsArtifactPath(atomName),\n contentHash,\n };\n}\n\n/**\n * Infer the primary function name from a source string.\n * Mirrors the same helper used in upstream-test.ts and documented-usage.ts.\n */\nfunction inferFunctionName(source: string): string | undefined {\n const fnMatch = source.match(/(?:^|\\s)function\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);\n if (fnMatch?.[1]) return fnMatch[1];\n\n const constMatch = source.match(/(?:^|\\s)(?:const|let|var)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*=/);\n if (constMatch?.[1]) return constMatch[1];\n\n return undefined;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CORPUS-001 (see corpus/types.ts)\n// title: Documented-usage synthesis derives fast-check properties from JSDoc examples + type signature\n// status: decided (WI-016, revised WI-376)\n// rationale:\n// Source (b) in the priority chain. When IntentCard.preconditions and postconditions are\n// empty (upstream-test adaptation would produce only a behavior stub), documented-usage\n// synthesis extracts JSDoc @example blocks and the inferred type signature from the source\n// text to produce more concrete fast-check arbitraries.\n//\n// Priority logic: this extractor is always attempted after upstream-test. The main\n// extractCorpus() function uses the priority-ordered chain, so upstream-test is preferred.\n// In practice, documented-usage provides richer output than upstream-test when the source\n// has JSDoc @example annotations.\n//\n// No I/O or API calls. Deterministic given the same source + intentCard.\n//\n// @decision DEC-PROPTEST-DOCUMENTED-USAGE-001\n// title: Loud refusal over silent placeholder -- documented-usage corpus extractor\n// status: accepted (WI-376)\n// rationale:\n// The original extractor emitted `return true; // placeholder` in every generated\n// it() block -- a test that always passes regardless of input. This violates Sacred\n// Practice #5 (fail loudly, never silently): a `proof/manifest.json` entry that\n// records `property_tests` but whose assertions are trivially vacuous is a lie to\n// the operator.\n//\n// Decision: extract real assertions via Option A (deterministic @example parsing) when\n// possible. Option A recognises patterns of the form:\n// fn(arg) // => expected\n// fn(arg) // -> expected\n// and emits `expect(fn(arg)).toEqual(expected)` assertions. All other @example\n// forms (multi-line, NLP-required, prose-only) are unstructured -- the extractor\n// REFUSES to emit an it() block for them and logs the skip reason so the caller can\n// surface the gap.\n//\n// The type-signature-derived \"catch-all\" test (which ALWAYS produced a placeholder)\n// is removed entirely. It had no postcondition to assert substantively without\n// actual function invocation or NLP-derived synthesis.\n//\n// Consequence: `extractFromDocumentedUsage` now returns `CorpusResult | undefined`.\n// `undefined` means \"no real assertions could be derived -- do not record\n// `property_tests` in the proof manifest for this atom via this path.\"\n// Callers (index.ts) must fall through to the next source (ai-derived) or throw\n// when no source produces a non-placeholder corpus.\n//\n// This honors the L0 floor: only atoms with substantive property tests are recorded\n// as having `property_tests` in their proof manifest.\n\nimport { blake3 } from \"@noble/hashes/blake3\";\nimport { bytesToHex } from \"@noble/hashes/utils\";\nimport type { CorpusResult, IntentCardInput } from \"./types.js\";\n\nconst encoder = new TextEncoder();\n\n/**\n * Canonical artifact path for documented-usage-synthesized property-test files.\n */\nconst DOCUMENTED_USAGE_PATH = \"property-tests.fast-check.ts\";\n\n/**\n * A JSDoc @example block extracted from source text.\n */\ninterface JsDocExample {\n /** The raw example text (trimmed). */\n readonly text: string;\n /** Zero-based index of this example within the JSDoc comment. */\n readonly index: number;\n}\n\n/**\n * A parsed assertion derived from a JSDoc @example block.\n *\n * Only produced for deterministic Option-A patterns of the form:\n * fn(arg) // => expected\n * fn(arg) // -> expected\n */\ninterface ParsedAssertion {\n /** The full call expression as written in the @example, e.g. fn(\"1,2,3\"). */\n readonly callExpr: string;\n /** The expected value as written in the @example, e.g. [1, 2, 3]. */\n readonly expectedExpr: string;\n /** Original @example text, preserved for comments in generated output. */\n readonly exampleText: string;\n /** Zero-based index of the originating example. */\n readonly index: number;\n}\n\n/**\n * Reason an @example block could not be parsed into a real assertion.\n *\n * Surfaced in stderr logging so operators can see the gap without a hard failure.\n */\nexport interface ExampleSkipReason {\n /** Zero-based index of the @example block that was skipped. */\n readonly index: number;\n /** The raw @example text that was unstructured. */\n readonly text: string;\n /** Human-readable reason for the skip. */\n readonly reason: string;\n}\n\n/**\n * Internal build result: the generated file content plus skip reasons for each\n * @example block that could not be parsed into a real assertion.\n */\ninterface BuildResult {\n readonly content: string;\n readonly skipped: readonly ExampleSkipReason[];\n}\n\n/**\n * Extract @example blocks from a source text JSDoc comments.\n *\n * Scans all block comments and collects @example tagged sections.\n * Returns an empty array if no @example blocks are found.\n */\nfunction extractJsDocExamples(source: string): JsDocExample[] {\n const examples: JsDocExample[] = [];\n // Match JSDoc-style block comments -- use Array.from to avoid assignment-in-expression\n const blockMatches = Array.from(source.matchAll(/\\/\\*\\*([\\s\\S]*?)\\*\\//g));\n\n for (const match of blockMatches) {\n const commentBody = match[1] ?? \"\";\n // Find @example tags within the block\n const exampleMatches = Array.from(commentBody.matchAll(/@example\\s*([\\s\\S]*?)(?=@\\w|$)/g));\n for (const exMatch of exampleMatches) {\n const text = (exMatch[1] ?? \"\").trim();\n if (text.length > 0) {\n examples.push({ text, index: examples.length });\n }\n }\n }\n\n return examples;\n}\n\n/**\n * Attempt to parse a JSDoc @example text into a deterministic assertion.\n *\n * Recognises Option-A patterns only:\n * fn(arg) // => expected\n * fn(arg) // -> expected\n *\n * The call expression must be on a single line ending with a // comment\n * that uses => or -> as a returns-arrow. Multi-line examples, prose-only\n * examples, and examples that require NLP are rejected.\n *\n * @decision DEC-PROPTEST-DOCUMENTED-USAGE-001 -- only deterministic single-line\n * patterns are accepted; everything else is a loud refusal.\n */\nfunction tryParseExampleAssertion(\n example: JsDocExample,\n):\n | { kind: \"success\"; assertion: ParsedAssertion }\n | { kind: \"failure\"; skipReason: ExampleSkipReason } {\n // Normalise: strip JSDoc leading-asterisk decoration from lines like ` * fn(x) // => y`\n const stripped = example.text\n .split(\"\\n\")\n .map((l) => l.replace(/^\\s*\\*\\s?/, \"\").trim())\n .filter((l) => l.length > 0);\n\n // Reject multi-line examples -- they require NLP to interpret.\n if (stripped.length !== 1) {\n return {\n kind: \"failure\",\n skipReason: {\n index: example.index,\n text: example.text,\n reason: `multi-line @example (${stripped.length} lines) -- too unstructured to derive a deterministic assertion`,\n },\n };\n }\n\n const [line] = stripped;\n if (line === undefined) {\n return {\n kind: \"failure\",\n skipReason: {\n index: example.index,\n text: example.text,\n reason: \"internal parser invariant violated: expected one normalized @example line\",\n },\n };\n }\n\n // Match: <callExpr> // => <expectedExpr> OR <callExpr> // -> <expectedExpr>\n // The call expression must contain parentheses (it is a function call, not prose).\n const m = line.match(/^([^/]+\\([^)]*\\))\\s*\\/\\/\\s*[-=]>\\s*(.+)$/);\n if (!m) {\n return {\n kind: \"failure\",\n skipReason: {\n index: example.index,\n text: example.text,\n reason:\n \"@example does not match deterministic pattern fn(args) // => expected -- too unstructured to derive an assertion\",\n },\n };\n }\n\n const rawCallExpr = m[1];\n const rawExpectedExpr = m[2];\n if (rawCallExpr === undefined || rawExpectedExpr === undefined) {\n return {\n kind: \"failure\",\n skipReason: {\n index: example.index,\n text: example.text,\n reason:\n \"internal parser invariant violated: expected call and expected-expression captures\",\n },\n };\n }\n\n const callExpr = rawCallExpr.trim();\n const expectedExpr = rawExpectedExpr.trim();\n\n return {\n kind: \"success\",\n assertion: {\n callExpr,\n expectedExpr,\n exampleText: example.text,\n index: example.index,\n },\n };\n}\n\n/**\n * Infer a fast-check arbitrary expression from a TypeScript type hint string.\n *\n * This is a best-effort approximation for common primitive types.\n * Complex types fall back to fc.anything().\n */\nfunction typeHintToArbitrary(typeHint: string): string {\n const t = typeHint.trim().toLowerCase();\n if (t === \"string\") return \"fc.string()\";\n if (t === \"number\") return \"fc.float()\";\n if (t === \"integer\" || t === \"int\") return \"fc.integer()\";\n if (t === \"boolean\") return \"fc.boolean()\";\n if (t === \"bigint\") return \"fc.bigInt()\";\n if (t.endsWith(\"[]\") || t.startsWith(\"array<\")) return \"fc.array(fc.anything())\";\n if (t.startsWith(\"string[]\")) return \"fc.array(fc.string())\";\n if (t.startsWith(\"number[]\")) return \"fc.array(fc.float())\";\n return \"fc.anything()\";\n}\n\n/**\n * Synthesize a fast-check property-test file from JSDoc examples.\n *\n * This is corpus extraction source (b): documented-usage synthesis. It extracts\n * @example blocks from JSDoc comments in the source and attempts to parse each\n * block into a real assertion using Option-A deterministic parsing.\n *\n * @decision DEC-PROPTEST-DOCUMENTED-USAGE-001:\n * Returns undefined (loud refusal) when NO @example block yields a parseable\n * assertion. This prevents hollow placeholder tests from entering the proof manifest.\n * Only atoms with at least one real deterministic assertion are recorded as having\n * property_tests via this extraction path.\n *\n * Callers MUST treat undefined as a \"no real tests available\" signal and either\n * fall through to the next corpus source (ai-derived) or surface the gap loudly.\n *\n * @param intentCard - The extracted intent card for this atom.\n * @param source - The raw source text of the atom.\n * @returns A CorpusResult with source=\"documented-usage\", or undefined when\n * no real assertions could be derived (loud refusal per Sacred Practice #5).\n */\nexport function extractFromDocumentedUsage(\n intentCard: IntentCardInput,\n source: string,\n): CorpusResult | undefined {\n const result = buildDocumentedUsageContent(intentCard, source);\n if (result === undefined) {\n return undefined;\n }\n\n const { content, skipped } = result;\n // Log skipped examples to stderr so operators can see the gap without a hard failure.\n // This is the \"loud\" part of loud refusal: each skipped example is named + explained.\n for (const skip of skipped) {\n process.stderr.write(`[documented-usage] skipped @example ${skip.index + 1}: ${skip.reason}\\n`);\n }\n\n const bytes = encoder.encode(content);\n const contentHash = bytesToHex(blake3(bytes));\n\n return {\n source: \"documented-usage\",\n bytes,\n path: DOCUMENTED_USAGE_PATH,\n contentHash,\n };\n}\n\n/**\n * Build the fast-check property-test file content from JSDoc examples.\n *\n * Returns undefined when no parseable assertion could be derived from any @example\n * block (i.e., every example was skipped or there were no @example blocks at all).\n *\n * @decision DEC-PROPTEST-DOCUMENTED-USAGE-001 -- the type-signature-derived catch-all\n * test (former signatureTest) is intentionally REMOVED. It produced only a\n * return true placeholder and had no postcondition to assert substantively. Removing\n * it means this extractor now returns undefined when no parseable example exists,\n * letting the caller fall through to ai-derived synthesis instead of recording a hollow\n * test.\n */\nfunction buildDocumentedUsageContent(\n intentCard: IntentCardInput,\n source: string,\n): BuildResult | undefined {\n const fnName = inferFunctionName(source) ?? \"atom\";\n const safeDescribe = JSON.stringify(`${fnName} — documented usage properties`);\n\n const examples = extractJsDocExamples(source);\n\n if (examples.length === 0) {\n // No @example blocks -- nothing to derive from -- loud refusal.\n return undefined;\n }\n\n // Build input arbitraries from the type signature (used only for comment headers).\n const inputArbitraries = intentCard.inputs.map((inp) => ({\n name: inp.name,\n arbitrary: typeHintToArbitrary(inp.typeHint),\n typeHint: inp.typeHint,\n description: inp.description,\n }));\n\n const skipped: ExampleSkipReason[] = [];\n const assertionTests: string[] = [];\n\n for (const ex of examples) {\n const parseResult = tryParseExampleAssertion(ex);\n if (parseResult.kind === \"failure\") {\n skipped.push(parseResult.skipReason);\n continue;\n }\n\n const { assertion } = parseResult;\n const label = JSON.stringify(`example ${assertion.index + 1}: ${assertion.callExpr}`);\n const commentLines = assertion.exampleText\n .split(\"\\n\")\n .map((l) => ` * ${l}`)\n .join(\"\\n\");\n\n assertionTests.push(`\n /**\n * Derived from JSDoc @example:\n${commentLines}\n */\n it(${label}, () => {\n expect(${assertion.callExpr}).toEqual(${assertion.expectedExpr});\n });`);\n }\n\n // Loud refusal: if every example was unstructured, return undefined rather than\n // emitting a test file with no real assertions.\n if (assertionTests.length === 0) {\n return undefined;\n }\n\n const inputComments = inputArbitraries\n .map((a) => ` // ${a.name}: ${a.typeHint} — ${a.description} → ${a.arbitrary}`)\n .join(\"\\n\");\n\n const content = `// Auto-generated property-test corpus (source: documented-usage synthesis)\n// Derived from JSDoc @example blocks (Option-A deterministic parsing).\n// DO NOT EDIT -- regenerated by WI-016/WI-376 corpus extraction.\n// @decision DEC-PROPTEST-DOCUMENTED-USAGE-001: only @example blocks matching\n// fn(args) // => expected produce real assertions; others are refused (no it() emitted).\n\nimport { describe, expect, it } from \"vitest\";\n\n// Inferred input arbitraries (for context):\n${inputComments || \"// (no typed inputs found)\"}\n\ndescribe(${safeDescribe}, () => {${assertionTests.join(\"\")}\n});\n`;\n\n return { content, skipped };\n}\n\n/**\n * Attempt to infer the primary function name from a source string.\n *\n * Looks for the first function <name> or const <name> = declaration.\n * Returns undefined if no function name can be determined.\n */\nfunction inferFunctionName(source: string): string | undefined {\n const fnMatch = source.match(/(?:^|\\s)function\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);\n if (fnMatch?.[1]) return fnMatch[1];\n\n const constMatch = source.match(/(?:^|\\s)(?:const|let|var)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*=/);\n if (constMatch?.[1]) return constMatch[1];\n\n return undefined;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CORPUS-001 (see corpus/types.ts)\n// title: Upstream-test adaptation builds a fast-check file from IntentCard.propertyTests hints\n// status: decided (WI-016)\n// rationale:\n// The IntentCard's preconditions, postconditions, and notes fields carry behavioral\n// specification text that maps naturally to fast-check properties. When no external\n// upstream test fixture is available (which is the common case at L0), we adapt the\n// IntentCard's own property-test hints (behavior + preconditions + postconditions) into\n// deterministic fast-check property stubs. This is \"source (a)\" in the priority chain.\n//\n// The generated file is deterministic given the same IntentCard: it is a string\n// interpolation of the spec fields. No I/O, no API calls, no randomness.\n\nimport { blake3 } from \"@noble/hashes/blake3\";\nimport { bytesToHex } from \"@noble/hashes/utils\";\nimport type { CorpusResult, IntentCardInput } from \"./types.js\";\n\nconst encoder = new TextEncoder();\n\n/**\n * Canonical artifact path for upstream-test-adapted property-test files.\n */\nconst UPSTREAM_TEST_PATH = \"property-tests.fast-check.ts\";\n\n/**\n * Adapt the IntentCard's behavioral specification into a fast-check property-test file.\n *\n * This is corpus extraction source (a): upstream-test adaptation. It derives\n * deterministic property stubs from the IntentCard fields (behavior, inputs,\n * outputs, preconditions, postconditions, notes). No I/O or API calls are made.\n *\n * The generated file bundles all properties into a single `describe` block with\n * one `fc.property` per documented behavioral constraint. This satisfies the L0\n * manifest constraint of exactly one \"property_tests\" artifact per atom.\n *\n * @param intentCard - The extracted intent card for this atom.\n * @param source - The raw source text of the atom (used for function name inference).\n * @returns A CorpusResult with source=\"upstream-test\".\n */\nexport function extractFromUpstreamTest(intentCard: IntentCardInput, source: string): CorpusResult {\n const content = buildUpstreamTestContent(intentCard, source);\n const bytes = encoder.encode(content);\n const contentHash = bytesToHex(blake3(bytes));\n\n return {\n source: \"upstream-test\",\n bytes,\n path: UPSTREAM_TEST_PATH,\n contentHash,\n };\n}\n\n/**\n * Build the fast-check property-test file content from an IntentCard.\n *\n * The generated structure:\n * 1. Import block (fast-check, vitest)\n * 2. One describe block named after the atom\n * 3. One it() + fc.property() per precondition (input domain constraint)\n * 4. One it() + fc.property() per postcondition (output guarantee)\n * 5. One it() for the general behavior description\n *\n * All property bodies are stubs — the test structure is correct but the\n * implementation calls `fc.pre` / `expect` with TODO comments. This is\n * intentional: the corpus establishes the property-test shape without\n * hard-coding implementation-specific assertions that would break if the\n * atom's source changes.\n */\nfunction buildUpstreamTestContent(intentCard: IntentCardInput, source: string): string {\n const fnName = inferFunctionName(source) ?? \"atom\";\n const safeDescribe = JSON.stringify(`${fnName} — property tests`);\n\n const inputArbLines = intentCard.inputs.map(\n (inp) => ` // Input: ${inp.name}: ${inp.typeHint} — ${inp.description}`,\n );\n\n const preconditionTests = intentCard.preconditions.map((pre, i) => {\n const label = JSON.stringify(`precondition ${i + 1}: ${pre}`);\n return `\n it(${label}, () => {\n fc.assert(\n fc.property(fc.anything(), (_input) => {\n // TODO: Replace with typed arbitrary matching the input signature.\n // Precondition: ${pre}\n fc.pre(true); // placeholder — add real precondition guard here\n return true; // placeholder — add real assertion here\n }),\n { numRuns: 100 },\n );\n });`;\n });\n\n const postconditionTests = intentCard.postconditions.map((post, i) => {\n const label = JSON.stringify(`postcondition ${i + 1}: ${post}`);\n return `\n it(${label}, () => {\n fc.assert(\n fc.property(fc.anything(), (_input) => {\n // TODO: Replace with typed arbitrary matching the input signature.\n // Postcondition: ${post}\n return true; // placeholder — add real assertion here\n }),\n { numRuns: 100 },\n );\n });`;\n });\n\n const behaviorTest = `\n it(${JSON.stringify(`behavior: ${intentCard.behavior.slice(0, 80)}`)}, () => {\n fc.assert(\n fc.property(fc.anything(), (_input) => {\n // TODO: Replace with typed arbitrary and real implementation call.\n // Behavior: ${intentCard.behavior}\n return true; // placeholder\n }),\n { numRuns: 100 },\n );\n });`;\n\n // If no preconditions or postconditions, at least emit the behavior test.\n const allTests = [...preconditionTests, ...postconditionTests, behaviorTest];\n\n const inputComments =\n inputArbLines.length > 0 ? `\\n // Inputs:\\n${inputArbLines.join(\"\\n\")}\\n` : \"\";\n\n const outputComments =\n intentCard.outputs.length > 0\n ? ` // Outputs: ${intentCard.outputs.map((o) => `${o.name}: ${o.typeHint}`).join(\", \")}\\n`\n : \"\";\n\n return `// Auto-generated property-test corpus (source: upstream-test adaptation)\n// Generated from IntentCard behavioral specification.\n// DO NOT EDIT — regenerated by WI-016 corpus extraction.\n\nimport * as fc from \"fast-check\";\nimport { describe, it } from \"vitest\";\n${inputComments}${outputComments}\ndescribe(${safeDescribe}, () => {${allTests.join(\"\")}\n});\n`;\n}\n\n/**\n * Attempt to infer the primary function name from a source string.\n *\n * Looks for the first `function <name>` or `const <name> =` declaration.\n * Returns undefined if no function name can be determined.\n */\nfunction inferFunctionName(source: string): string | undefined {\n const fnMatch = source.match(/(?:^|\\s)function\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/);\n if (fnMatch?.[1]) return fnMatch[1];\n\n const constMatch = source.match(/(?:^|\\s)(?:const|let|var)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*=/);\n if (constMatch?.[1]) return constMatch[1];\n\n return undefined;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CORPUS-001 (see corpus/types.ts)\n// title: extractCorpus() implements a four-source priority chain for property-test corpus\n// status: decided (WI-016, extended WI-V2-07-L8, revised WI-376)\n// rationale:\n// Priority order: props-file (0) > upstream-test (a) > documented-usage (b) > ai-derived (c).\n// The highest-priority source that succeeds (produces non-empty bytes) wins;\n// lower-priority sources are not consulted. This ensures that the cheapest,\n// most deterministic source is always preferred.\n//\n// Source (0) props-file: hand-authored sibling *.props.ts file. Attempted only when\n// CorpusAtomSpec.propsFilePath is set. Returns undefined when no matching prop_<atom>_*\n// export is found, falling through to source (a).\n//\n// \"Succeeds\" means: the extractor returns a CorpusResult without throwing.\n// Source (a) is a pure function and always succeeds. Source (b) may return\n// undefined (loud refusal per DEC-PROPTEST-DOCUMENTED-USAGE-001) when no\n// parseable @example assertion can be derived; in that case it falls through to (c).\n// Source (c) returns undefined on a cache miss and is only attempted when cacheDir\n// is provided.\n//\n// DEC-SHAVE-002 offline discipline: sources (0), (a) and (b) work without API key.\n// Source (c) reads from cache only in unit tests; live AI calls are never made\n// in the test suite.\n//\n// The CorpusResult from whichever source wins is the single artifact referenced\n// by the ProofManifest. Multiple property checks are bundled into one file --\n// no multiple manifest entries.\n\nexport type {\n CorpusResult,\n CorpusSource,\n CorpusAtomSpec,\n CorpusExtractionOptions,\n} from \"./types.js\";\nexport {\n seedCorpusCache,\n CORPUS_SCHEMA_VERSION,\n CORPUS_DEFAULT_MODEL,\n CORPUS_PROMPT_VERSION,\n} from \"./ai-derived.js\";\n\nexport { extractFromPropsFile } from \"./props-file.js\";\n\nimport { extractFromAiDerivedCached } from \"./ai-derived.js\";\nimport { extractFromDocumentedUsage } from \"./documented-usage.js\";\nimport { extractFromPropsFile } from \"./props-file.js\";\nimport type { CorpusAtomSpec, CorpusExtractionOptions, CorpusResult } from \"./types.js\";\nimport { extractFromUpstreamTest } from \"./upstream-test.js\";\n\n/**\n * Extract a property-test corpus for an atom using a four-source priority chain.\n *\n * Priority order (highest to lowest):\n * (0) props-file — hand-authored sibling *.props.ts (highest priority; optional).\n * (a) upstream-test adaptation — deterministic, derived from IntentCard spec fields.\n * (b) documented-usage synthesis — deterministic, derived from JSDoc @example blocks.\n * Returns undefined (loud refusal) when no parseable assertion can be derived.\n * (c) ai-derived synthesis — cache-backed, requires cacheDir; offline-only in tests.\n *\n * Source (a) always produces a result. Source (b) may refuse (return undefined) when no\n * @example block in the source is parseable into a real assertion\n * (per DEC-PROPTEST-DOCUMENTED-USAGE-001). When (b) refuses, the chain falls through\n * to (c). This prevents hollow placeholder tests from entering the proof manifest.\n *\n * The returned CorpusResult bundles all property checks into a single fast-check file.\n * This satisfies the L0 manifest constraint of exactly one \"property_tests\" artifact\n * (validateProofManifestL0).\n *\n * @param atomSpec - Atom description: source text, IntentCard, optional cacheDir.\n * @param options - Optional source-enable flags. Default: all sources enabled.\n * @returns A CorpusResult from the highest-priority available source.\n * @throws Error if all enabled sources are disabled or all fail.\n */\nexport async function extractCorpus(\n atomSpec: CorpusAtomSpec,\n options?: CorpusExtractionOptions,\n): Promise<CorpusResult> {\n const enableProps = options?.enablePropsFile ?? true;\n const enableA = options?.enableUpstreamTest ?? true;\n const enableB = options?.enableDocumentedUsage ?? true;\n const enableC = options?.enableAiDerived ?? true;\n\n // Source (0): props-file -- hand-authored sibling *.props.ts corpus.\n // Highest priority. Only attempted when propsFilePath is provided.\n // Returns undefined when no matching prop_<atom>_* export is found,\n // in which case the chain falls through to source (a).\n if (enableProps && atomSpec.propsFilePath !== undefined) {\n const result = await extractFromPropsFile(\n atomSpec.propsFilePath,\n atomSpec.intentCard,\n atomSpec.source,\n );\n if (result !== undefined) {\n return result;\n }\n }\n\n // Source (a): upstream-test adaptation.\n // Always succeeds (pure, deterministic). Attempted first among generated sources.\n if (enableA) {\n const result = extractFromUpstreamTest(atomSpec.intentCard, atomSpec.source);\n return result;\n }\n\n // Source (b): documented-usage synthesis.\n // May return undefined (loud refusal) when no @example block is parseable into a\n // real assertion (DEC-PROPTEST-DOCUMENTED-USAGE-001). Fall through to (c) on refusal.\n if (enableB) {\n const result = extractFromDocumentedUsage(atomSpec.intentCard, atomSpec.source);\n if (result !== undefined) {\n return result;\n }\n }\n\n // Source (c): AI-derived synthesis.\n // Only attempted when cacheDir is provided. Returns undefined on cache miss.\n if (enableC && atomSpec.cacheDir !== undefined) {\n const result = await extractFromAiDerivedCached(\n atomSpec.intentCard,\n atomSpec.source,\n atomSpec.cacheDir,\n );\n if (result !== undefined) {\n return result;\n }\n }\n\n throw new Error(\n \"extractCorpus: all enabled sources failed or were disabled. \" +\n \"Ensure at least one of enableUpstreamTest, enableDocumentedUsage, or enableAiDerived is true \" +\n \"and that cacheDir is provided for the ai-derived source.\",\n );\n}\n\n/**\n * Extract corpus using the full priority chain including fallback from (a) to (b) to (c).\n *\n * This variant attempts all enabled sources in priority order and falls through to the\n * next source when a higher-priority source is explicitly disabled or unavailable.\n *\n * Source (b) may now also refuse (return undefined per DEC-PROPTEST-DOCUMENTED-USAGE-001)\n * when no @example block is parseable; in that case the cascade continues to (c).\n *\n * True cascade behaviour:\n * if (props-file enabled and matches) -> return props-file\n * else if (a enabled) -> return a\n * else if (b enabled and produces real assertions) -> return b\n * else if (c enabled and cache hit) -> return c\n * else throw\n *\n * @param atomSpec - Atom description: source text, IntentCard, optional cacheDir.\n * @param options - Optional source-enable flags. Default: all sources enabled.\n * @returns A CorpusResult from the highest-priority available source.\n */\nexport async function extractCorpusCascade(\n atomSpec: CorpusAtomSpec,\n options?: CorpusExtractionOptions,\n): Promise<CorpusResult> {\n const enableProps = options?.enablePropsFile ?? true;\n const enableA = options?.enableUpstreamTest ?? true;\n const enableB = options?.enableDocumentedUsage ?? true;\n const enableC = options?.enableAiDerived ?? true;\n\n // Source (0): props-file -- highest priority when propsFilePath is set.\n if (enableProps && atomSpec.propsFilePath !== undefined) {\n const result = await extractFromPropsFile(\n atomSpec.propsFilePath,\n atomSpec.intentCard,\n atomSpec.source,\n );\n if (result !== undefined) {\n return result;\n }\n }\n\n // Source (a): upstream-test adaptation (always succeeds when enabled).\n if (enableA) {\n return extractFromUpstreamTest(atomSpec.intentCard, atomSpec.source);\n }\n\n // Source (b): documented-usage synthesis.\n // May return undefined (loud refusal per DEC-PROPTEST-DOCUMENTED-USAGE-001).\n if (enableB) {\n const result = extractFromDocumentedUsage(atomSpec.intentCard, atomSpec.source);\n if (result !== undefined) {\n return result;\n }\n }\n\n // Source (c): AI-derived synthesis (cache-only in tests).\n if (enableC && atomSpec.cacheDir !== undefined) {\n const result = await extractFromAiDerivedCached(\n atomSpec.intentCard,\n atomSpec.source,\n atomSpec.cacheDir,\n );\n if (result !== undefined) {\n return result;\n }\n }\n\n throw new Error(\n \"extractCorpusCascade: all enabled sources failed or were disabled. \" +\n \"Ensure at least one source is enabled and available.\",\n );\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CONTINUOUS-SHAVE-022: Three named error classes cover the three\n// distinct failure modes of intent extraction. Named classes (rather than a\n// generic Error with a code field) let callers catch specific error types with\n// `instanceof` without importing an enum or string constant.\n// Status: decided (MASTER_PLAN.md DEC-CONTINUOUS-SHAVE-022)\n// Rationale: TypeScript `instanceof` narrowing works reliably for named Error\n// subclasses. A code-string approach requires callers to import and compare\n// string constants, which is error-prone.\n//\n// WI-013-02: LicenseRefusedError was the fourth error class; removed by\n// DEC-LICENSE-GATE-REMOVE-001 (WI-682, 2026-05-17). The license gate\n// and its associated error type are deleted entirely.\n\n/**\n * Thrown when a live extraction is attempted but ANTHROPIC_API_KEY is not set\n * and no `client` was provided in ExtractIntentContext.\n *\n * Resolution: set the ANTHROPIC_API_KEY environment variable, or pass a\n * pre-constructed client (including a mock) via ExtractIntentContext.client.\n */\nexport class AnthropicApiKeyMissingError extends Error {\n constructor() {\n super(\n \"ANTHROPIC_API_KEY is not set; cannot extract intent. \" +\n \"Set the env var or pass `client` in ExtractIntentContext.\",\n );\n this.name = \"AnthropicApiKeyMissingError\";\n }\n}\n\n/**\n * Thrown when offline mode is active and the cache does not contain an entry\n * for the requested cache key.\n *\n * Resolution: pre-populate the cache with `extractIntent` in online mode, or\n * disable offline mode to allow live API calls.\n */\nexport class OfflineCacheMissError extends Error {\n constructor(cacheKey: string) {\n super(`Offline mode: cache miss for key ${cacheKey}.`);\n this.name = \"OfflineCacheMissError\";\n }\n}\n\n/**\n * Thrown when an IntentCard value fails schema validation.\n *\n * This covers: malformed API responses (missing JSON fences, unparseable JSON,\n * wrong types), unknown top-level fields, and invalid field values (e.g.\n * behavior > 200 chars, sourceHash not 64 hex chars).\n *\n * The `detail` parameter names the specific offending field or condition.\n */\nexport class IntentCardSchemaError extends Error {\n constructor(detail: string) {\n super(`IntentCard schema violation: ${detail}`);\n this.name = \"IntentCardSchemaError\";\n }\n}\n\n/**\n * Thrown by universalize() when `options.persist === true` is requested but\n * the registry view does not implement `storeBlock`.\n *\n * This is a loud-fail per Sacred Practice #5: callers that request persistence\n * must supply a registry that supports it. Silent no-op (the graceful-degradation\n * path used when persist is NOT requested) is intentionally not available here —\n * if the caller says \"persist\", the absence of storeBlock is a programmer error,\n * not a configuration choice.\n *\n * Resolution: pass a full Registry (which implements storeBlock) or remove the\n * persist: true option from the universalize() call.\n *\n * @decision DEC-UNIVERSALIZE-PERSIST-ERR-001 (WI-373)\n * @see UniversalizeOptions.persist\n */\nexport class PersistRequestedButNotSupportedError extends Error {\n constructor() {\n super(\n \"universalize: persist:true was requested but the registry view does not implement storeBlock. \" +\n \"Pass a full Registry that supports storeBlock, or remove persist:true from the options.\",\n );\n this.name = \"PersistRequestedButNotSupportedError\";\n }\n}\n\n/**\n * Thrown by shave() when foreignPolicy === 'reject' and the slice plan\n * contains one or more ForeignLeafEntry records.\n *\n * @decision DEC-V2-FOREIGN-BLOCK-SCHEMA-001 (sub-L5: policy gate)\n * title: ForeignPolicyRejectError — structured reject-policy failure\n * status: decided (WI-V2-04 L5)\n * rationale:\n * Named error class (rather than generic Error) lets the CLI catch it with\n * `instanceof` and format the error message without string-parsing. The\n * `foreignRefs` array carries every offending (pkg, export) pair in\n * source-declaration order so the CLI can emit all offenders in one message.\n *\n * L5-I3: the structured error must contain pkg+export of each foreign ref.\n * The message format is: \"foreign-policy reject: <pkg>#<export>[, ...]\"\n * so the CLI stderr line contains both the package name and the export name.\n *\n * Not thrown for 'allow' or 'tag' — only for 'reject'.\n */\nexport class ForeignPolicyRejectError extends Error {\n /** All foreign (pkg, export) pairs from the slice plan, in source order. */\n readonly foreignRefs: readonly { readonly pkg: string; readonly export: string }[];\n\n constructor(foreignRefs: readonly { readonly pkg: string; readonly export: string }[]) {\n const refList = foreignRefs.map((r) => `${r.pkg}#${r.export}`).join(\", \");\n super(`foreign-policy reject: ${refList}`);\n this.name = \"ForeignPolicyRejectError\";\n this.foreignRefs = foreignRefs;\n }\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CONTINUOUS-SHAVE-022: validateIntentCard is a loud, exact\n// validator — it rejects unknown top-level fields and sub-object fields rather\n// than silently ignoring them. This protects against schema drift: if the model\n// returns an extra field (e.g. from a future prompt change), the validator\n// surfaces the mismatch immediately rather than silently persisting an invalid\n// entry to cache.\n// Status: decided (MASTER_PLAN.md DEC-CONTINUOUS-SHAVE-022)\n// Rationale: Silent acceptance of unknown fields is how schema drift goes\n// undetected for months. Loud rejection keeps the IntentCard contract\n// enforceable and cache entries trustworthy.\n\nimport { IntentCardSchemaError } from \"../errors.js\";\nimport type { IntentCard, IntentParam } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Allowed field sets — single source of truth for the schema shape.\n// Any field not in these sets causes immediate rejection.\n// ---------------------------------------------------------------------------\n\nconst INTENT_CARD_ALLOWED_KEYS = new Set<string>([\n \"schemaVersion\",\n \"behavior\",\n \"inputs\",\n \"outputs\",\n \"preconditions\",\n \"postconditions\",\n \"notes\",\n \"modelVersion\",\n \"promptVersion\",\n \"sourceHash\",\n \"extractedAt\",\n]);\n\nconst INTENT_PARAM_ALLOWED_KEYS = new Set<string>([\"name\", \"typeHint\", \"description\"]);\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\nfunction fail(detail: string): never {\n throw new IntentCardSchemaError(detail);\n}\n\nfunction requireString(value: unknown, fieldPath: string): string {\n if (typeof value !== \"string\") {\n fail(`field \"${fieldPath}\" must be a string, got ${typeof value}`);\n }\n return value;\n}\n\nfunction requireArray(value: unknown, fieldPath: string): unknown[] {\n if (!Array.isArray(value)) {\n fail(`field \"${fieldPath}\" must be an array, got ${typeof value}`);\n }\n return value as unknown[];\n}\n\nfunction requireStringArray(value: unknown, fieldPath: string): string[] {\n const arr = requireArray(value, fieldPath);\n for (let i = 0; i < arr.length; i++) {\n if (typeof arr[i] !== \"string\") {\n fail(`field \"${fieldPath}[${i}]\" must be a string, got ${typeof arr[i]}`);\n }\n }\n return arr as string[];\n}\n\nfunction validateIntentParam(value: unknown, fieldPath: string): IntentParam {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n fail(`field \"${fieldPath}\" must be a plain object`);\n }\n const obj = value as Record<string, unknown>;\n\n // Reject unknown keys\n for (const key of Object.keys(obj)) {\n if (!INTENT_PARAM_ALLOWED_KEYS.has(key)) {\n fail(`field \"${fieldPath}\" has unknown key \"${key}\"`);\n }\n }\n\n const name = requireString(obj.name, `${fieldPath}.name`);\n const typeHint = requireString(obj.typeHint, `${fieldPath}.typeHint`);\n const description = requireString(obj.description, `${fieldPath}.description`);\n\n return { name, typeHint, description };\n}\n\nfunction validateIntentParamArray(value: unknown, fieldPath: string): IntentParam[] {\n const arr = requireArray(value, fieldPath);\n return arr.map((item, i) => validateIntentParam(item, `${fieldPath}[${i}]`));\n}\n\n// ---------------------------------------------------------------------------\n// Public validator\n// ---------------------------------------------------------------------------\n\n/**\n * Validate an unknown value against the IntentCard schema.\n *\n * Throws IntentCardSchemaError with a specific field-level message on any\n * violation, including:\n * - Not a plain object\n * - Missing required fields\n * - Wrong types (string where array expected, etc.)\n * - schemaVersion !== 1\n * - behavior empty, contains newline, or > 200 chars\n * - sourceHash not exactly 64 lowercase hex characters\n * - Unknown top-level fields\n * - inputs/outputs items missing required keys or having extra keys\n *\n * Returns the value typed as IntentCard on success. Does not clone or\n * transform the input — it is returned as-is after validation.\n *\n * @param value - The raw value to validate (typically parsed JSON).\n * @returns The validated value typed as IntentCard.\n */\nexport function validateIntentCard(value: unknown): IntentCard {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n fail(\"value must be a plain object\");\n }\n\n const obj = value as Record<string, unknown>;\n\n // Reject unknown top-level keys.\n for (const key of Object.keys(obj)) {\n if (!INTENT_CARD_ALLOWED_KEYS.has(key)) {\n fail(`unknown top-level field \"${key}\"`);\n }\n }\n\n // schemaVersion\n if (obj.schemaVersion !== 1) {\n fail(`field \"schemaVersion\" must be 1, got ${JSON.stringify(obj.schemaVersion)}`);\n }\n\n // behavior — non-empty, no newlines, ≤ 200 chars\n const behavior = requireString(obj.behavior, \"behavior\");\n if (behavior.length === 0) {\n fail('field \"behavior\" must not be empty');\n }\n if (/[\\n\\r]/.test(behavior)) {\n fail('field \"behavior\" must not contain newline characters');\n }\n if (behavior.length > 200) {\n fail(`field \"behavior\" must be ≤200 characters, got ${behavior.length}`);\n }\n\n // inputs / outputs — arrays of IntentParam\n const inputs = validateIntentParamArray(obj.inputs, \"inputs\");\n const outputs = validateIntentParamArray(obj.outputs, \"outputs\");\n\n // preconditions / postconditions / notes — arrays of strings\n const preconditions = requireStringArray(obj.preconditions, \"preconditions\");\n const postconditions = requireStringArray(obj.postconditions, \"postconditions\");\n const notes = requireStringArray(obj.notes, \"notes\");\n\n // modelVersion\n const modelVersion = requireString(obj.modelVersion, \"modelVersion\");\n\n // promptVersion\n const promptVersion = requireString(obj.promptVersion, \"promptVersion\");\n\n // sourceHash — exactly 64 lowercase hex characters\n const sourceHash = requireString(obj.sourceHash, \"sourceHash\");\n if (!/^[0-9a-f]{64}$/.test(sourceHash)) {\n fail(\n `field \"sourceHash\" must be 64 lowercase hex characters, got \"${sourceHash.slice(0, 16)}${sourceHash.length > 16 ? \"…\" : \"\"}\" (length ${sourceHash.length})`,\n );\n }\n\n // extractedAt — must be a non-empty string (ISO-8601 format not strictly\n // enforced here, but presence is required)\n const extractedAt = requireString(obj.extractedAt, \"extractedAt\");\n if (extractedAt.length === 0) {\n fail('field \"extractedAt\" must not be empty');\n }\n\n return {\n schemaVersion: 1,\n behavior,\n inputs,\n outputs,\n preconditions,\n postconditions,\n notes,\n modelVersion,\n promptVersion,\n sourceHash,\n extractedAt,\n };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CONTINUOUS-SHAVE-022: Public API entry point for @yakcc/shave.\n// Three exported entry points: shave() (one-shot file), universalize()\n// (single-block continuous), createIntentExtractionHook() (hookable pipeline).\n// extractIntent is intentionally NOT exported — it is an internal detail.\n// WI-010-03: universalize is now wired to the live extractIntent path.\n// The sentinel IntentCard from WI-010-01 is removed.\n// WI-018: seedIntentCache() is a public test-helper that writes an IntentCard\n// into the file-system cache via the same key-derivation path as extractIntent.\n// WI-016: extractCorpus() and seedCorpusCache() are public exports. extractCorpus()\n// is the primary API for property-test corpus extraction. seedCorpusCache() is a\n// test-helper for seeding the AI-derived corpus cache in offline tests.\n// Status: decided (MASTER_PLAN.md DEC-CONTINUOUS-SHAVE-022)\n// Rationale: Keeping extractIntent internal ensures callers depend only on\n// the stable public surface; the extraction implementation can evolve freely.\n\n// ---------------------------------------------------------------------------\n// Re-exports — public type surface\n// ---------------------------------------------------------------------------\n\nexport type {\n CandidateBlock,\n ForeignPolicy,\n IntentExtractionHook,\n ShaveDiagnostics,\n ShaveOptions,\n ShaveRegistryView,\n ShaveResult,\n ShavedAtomStub,\n UniversalizeOptions,\n UniversalizeResult,\n UniversalizeSlicePlanEntry,\n} from \"./types.js\";\n\nexport { FOREIGN_POLICY_DEFAULT } from \"./types.js\";\n\nexport type { IntentCard, IntentParam } from \"./intent/types.js\";\n\n// ---------------------------------------------------------------------------\n// Re-exports — WI-016 public corpus surface\n// ---------------------------------------------------------------------------\n\n/**\n * @decision DEC-CORPUS-001 (see corpus/types.ts and corpus/index.ts)\n * title: extractCorpus and seedCorpusCache are public exports on the main entry point\n * status: decided (WI-016)\n * rationale:\n * extractCorpus() is the primary API for property-test corpus extraction. It\n * implements a three-source priority chain (upstream-test > documented-usage >\n * ai-derived) and returns a CorpusResult suitable for buildTriplet(). Placing it\n * on the main entry keeps the public contract stable while letting the corpus\n * implementation evolve internally.\n *\n * seedCorpusCache() is the corpus analogue of seedIntentCache(): a test-helper\n * that pre-populates the AI-derived corpus cache for offline tests. It MUST use\n * the same BLAKE3-based key derivation as the AI-derived extractor so that seeded\n * entries are found on the first cache lookup.\n *\n * DEC-SHAVE-002 offline discipline: loadling @yakcc/shave and calling extractCorpus()\n * MUST work without ANTHROPIC_API_KEY. Sources (a) and (b) are pure; source (c)\n * reads from cache only.\n */\n\nexport { extractCorpus } from \"./corpus/index.js\";\nexport type {\n CorpusResult,\n CorpusSource,\n CorpusAtomSpec,\n CorpusExtractionOptions,\n} from \"./corpus/index.js\";\nexport {\n seedCorpusCache,\n CORPUS_SCHEMA_VERSION,\n CORPUS_DEFAULT_MODEL,\n CORPUS_PROMPT_VERSION,\n} from \"./corpus/index.js\";\n\n// ---------------------------------------------------------------------------\n// Re-exports — WI-018 public test-helper surface\n// ---------------------------------------------------------------------------\n\n/**\n * @decision DEC-SHAVE-SEED-001\n * title: seedIntentCache is a public test-helper export on the main entry point\n * status: decided (WI-018)\n * rationale:\n * External consumers (e.g. @yakcc/compile tests) need to pre-populate the\n * intent-extraction cache for offline testing without calling the Anthropic\n * API. Before WI-018, tests reached into @yakcc/shave/dist/cache/file-cache.js\n * directly, which is an unstable internal path not in the package exports map.\n *\n * Design constraints (from DEC-SHAVE-003, Sacred Practice #12):\n * - seedIntentCache MUST use the same BLAKE3-based key derivation as\n * extractIntent (sourceHash → keyFromIntentInputs). It MUST NOT compute the\n * key itself or accept a pre-computed key.\n * - seedIntentCache MUST delegate to writeIntent for the actual cache write.\n * No parallel cache-write logic, no separate cache directory, no alternative\n * serialization format.\n * - DEC-SHAVE-002 offline discipline: loading @yakcc/shave and calling\n * seedIntentCache MUST work in the unit-test runner without an\n * ANTHROPIC_API_KEY environment variable. This function is offline-only —\n * it performs no LLM call and MUST NOT trigger the SDK import path.\n *\n * Placement: on the main entry (not a sub-path like @yakcc/shave/test-helpers)\n * so there is one public contract for external callers. The function is\n * clearly named and typed as a test helper; production code should never\n * call it because it writes to the cache from an externally-supplied card\n * rather than from a live extraction.\n */\n\n/**\n * Inputs that identify a cache slot for intent extraction.\n *\n * These mirror the ExtractIntentContext fields that feed into key derivation\n * (sourceHash → keyFromIntentInputs). Callers supply `source` (the raw source\n * text) and `cacheDir`; `model` and `promptVersion` default to the package\n * constants so the seeded key matches what extractIntent would produce under\n * the same defaults.\n *\n * WI-022: `strategy` controls which tag pair is used as the default for\n * `model`/`promptVersion`. Explicit `model`/`promptVersion` overrides still\n * win. Backward-compatible: tests not passing `strategy` get LLM-tag defaults,\n * preserving WI-018's three re-enabled `assemble-candidate` tests verbatim.\n */\nexport interface SeedIntentSpec {\n /** The raw source text whose BLAKE3 hash is the first key component. */\n readonly source: string;\n /** Root cache directory — must match the cacheDir used in the test's ShaveOptions. */\n readonly cacheDir: string;\n /** Anthropic model tag. Defaults based on `strategy` when omitted. */\n readonly model?: string | undefined;\n /** Prompt version tag. Defaults based on `strategy` when omitted. */\n readonly promptVersion?: string | undefined;\n /**\n * Strategy that controls the default tag pair when model/promptVersion are\n * omitted. (WI-022, DEC-INTENT-STRATEGY-001)\n *\n * - undefined / \"llm\" (default for backward-compat): defaults to DEFAULT_MODEL\n * and INTENT_PROMPT_VERSION. WI-018's assemble-candidate tests seed LLM-mode\n * cards and do not pass strategy; they continue working unchanged.\n * - \"static\": defaults to STATIC_MODEL_TAG and STATIC_PROMPT_VERSION, producing\n * a key that matches what extractIntent(..., { strategy: \"static\" }) would use.\n */\n readonly strategy?: \"static\" | \"llm\" | undefined;\n}\n\n/**\n * Write an IntentCard into the file-system intent cache under the key that\n * extractIntent() would produce for the same source+model+promptVersion inputs.\n *\n * **Test-helper only.** Do NOT call from production code. Production intent\n * cards are written exclusively by extractIntent() after a live API round-trip;\n * bypassing that path in production would produce unvalidated cache entries.\n *\n * Key derivation (DEC-SHAVE-003):\n * cacheKey = BLAKE3(sourceHash || \\x00 || modelTag || \\x00 || promptVersion || \\x00 || schemaVersion)\n * where sourceHash = BLAKE3(normalize(spec.source)).\n * This is identical to the key extractIntent() would derive for the same inputs.\n *\n * @param spec - Source text and cache location; identifies the cache slot.\n * @param card - The IntentCard to write. Must already be validated by the caller\n * (validateIntentCard is not called here — this is a raw write).\n */\nexport async function seedIntentCache(spec: SeedIntentSpec, card: IntentCard): Promise<void> {\n // @decision DEC-SHAVE-SEED-001 (see module comment above)\n // Delegate to the same key-derivation functions and writeIntent that\n // extractIntent() uses. No logic is duplicated here.\n //\n // WI-022: strategy-aware defaults. When strategy === \"static\", use the static\n // tag pair so the seeded key matches what extractIntent({ strategy: \"static\" })\n // would derive. Default (undefined/\"llm\") uses LLM tags for backward-compat.\n const isStatic = spec.strategy === \"static\";\n const modelTag = spec.model ?? (isStatic ? STATIC_MODEL_TAG : DEFAULT_MODEL);\n const pv = spec.promptVersion ?? (isStatic ? STATIC_PROMPT_VERSION : INTENT_PROMPT_VERSION);\n\n const srcHash = _sourceHash(spec.source);\n const cacheKey = _keyFromIntentInputs({\n sourceHash: srcHash,\n modelTag,\n promptVersion: pv,\n schemaVersion: INTENT_SCHEMA_VERSION,\n });\n\n await _writeIntent(spec.cacheDir, cacheKey, card);\n}\n\n// ---------------------------------------------------------------------------\n// Re-exports — WI-010-02 public surface\n// ---------------------------------------------------------------------------\n\n// Validator — callers that receive an IntentCard from an external source can\n// call this to verify it conforms to the current schema before use.\nexport { validateIntentCard } from \"./intent/validate-intent-card.js\";\n\n// Error classes — exported as named classes so callers can use instanceof.\n// DEC-LICENSE-GATE-REMOVE-001: LicenseRefusedError removed (WI-682, 2026-05-17).\nexport {\n AnthropicApiKeyMissingError,\n ForeignPolicyRejectError,\n IntentCardSchemaError,\n OfflineCacheMissError,\n PersistRequestedButNotSupportedError,\n} from \"./errors.js\";\n\n// Version constants — exported so callers can introspect the cache keying\n// policy and detect when their cached results were produced by a different\n// model or prompt version.\nexport {\n DEFAULT_MODEL,\n INTENT_PROMPT_VERSION,\n INTENT_SCHEMA_VERSION,\n STATIC_MODEL_TAG,\n STATIC_PROMPT_VERSION,\n} from \"./intent/constants.js\";\n\n// extractIntent is NOT exported — it remains an internal implementation detail.\n\n// ---------------------------------------------------------------------------\n// Re-exports — WI-024 public cache-helper surface\n// ---------------------------------------------------------------------------\n\n/**\n * @decision DEC-PUBLIC-CACHE-CONSTS-001\n * @title sourceHash is exported on the main entry point (WI-024)\n * @status accepted\n * @rationale\n * assemble-candidate.test.ts (in @yakcc/compile) needs to compute the\n * source hash that seedIntentCache() uses internally so it can populate\n * IntentCard.sourceHash with the exact value that extractIntent() would\n * produce. Before WI-024 the test reached into the package via a\n * cross-package relative import (../../../packages/shave/src/cache/key.js),\n * which caused tsc to emit stray .d.ts/.js/.map artifacts into\n * packages/shave/src/ and fail with TS6059/TS6307 when building\n * @yakcc/compile.\n *\n * Additive change — no breaking changes to existing callers. DEFAULT_MODEL\n * and INTENT_PROMPT_VERSION were already public (WI-018); this adds sourceHash\n * alongside them so all the imports in assemble-candidate.test.ts resolve\n * through the stable @yakcc/shave workspace alias.\n *\n * sourceHash is a pure function (BLAKE3 of normalized source). Exporting it\n * does not expose internal mutable state or implementation details that must\n * remain private; the function signature and semantics are already documented\n * in cache/key.ts.\n */\nexport { sourceHash } from \"./cache/key.js\";\n\n// ---------------------------------------------------------------------------\n// Re-exports — WI-012-03 atom-test public surface\n// ---------------------------------------------------------------------------\n\n// isAtom predicate — the gate for the WI-012 universalizer recursion.\nexport { isAtom } from \"./universalize/atom-test.js\";\nexport type { AtomTestOptions, AtomTestResult, AtomTestReason } from \"./universalize/types.js\";\n\n// ---------------------------------------------------------------------------\n// Re-exports — WI-012-04 decomposition recursion public surface\n// ---------------------------------------------------------------------------\n\nexport {\n decompose,\n DidNotReachAtomError,\n RecursionDepthExceededError,\n} from \"./universalize/recursion.js\";\nexport type {\n RecursionNode,\n AtomLeaf,\n BranchNode,\n RecursionTree,\n RecursionOptions,\n} from \"./universalize/types.js\";\n\n// ---------------------------------------------------------------------------\n// Re-exports — WI-012-05 DFG slicer public surface\n// ---------------------------------------------------------------------------\n\nexport { slice } from \"./universalize/slicer.js\";\nexport type {\n SlicePlan,\n SlicePlanEntry,\n PointerEntry,\n NovelGlueEntry,\n GlueLeafEntry,\n} from \"./universalize/types.js\";\n\n// ---------------------------------------------------------------------------\n// WI-V2-04 L5: foreign-policy gate public surface\n// ---------------------------------------------------------------------------\n\n/**\n * A single foreign dependency reference surfaced by the shave() policy gate.\n *\n * @decision DEC-V2-FOREIGN-BLOCK-SCHEMA-001 (sub-L5: ForeignRef)\n * title: ForeignRef — public (pkg, export) token for policy-gate output\n * status: decided (WI-V2-04 L5)\n * rationale:\n * shave() needs to surface foreign-dep information to callers (CLI, tests)\n * without modifying ShaveResult (types.ts is L4-owned and frozen for L5).\n * ForeignRef is a minimal structural type: the pkg specifier and the export\n * name as emitted by classifyForeign(). For namespace imports, export is \"*\".\n * The CLI renders these as \"pkg#export\" tokens in the summary line.\n */\nexport interface ForeignRef {\n /** Module specifier as written in the source, e.g. 'node:fs', 'ts-morph'. */\n readonly pkg: string;\n /** Imported binding name, e.g. 'readFileSync', 'Project', '*', 'default'. */\n readonly export: string;\n}\n\n/**\n * Extends ShaveResult with an optional foreignDeps field populated by the\n * policy gate when foreignPolicy === 'tag'. The field is absent for 'allow'\n * (silently accepted) and never reached for 'reject' (which throws).\n *\n * Using a structural extension rather than modifying ShaveResult (types.ts)\n * preserves L4 type stability while allowing L5 to surface foreign-dep info\n * to callers without an opaque side channel.\n *\n * @decision DEC-V2-FOREIGN-BLOCK-SCHEMA-001 (sub-L5: ShaveResultWithForeign)\n * title: ShaveResultWithForeign extends ShaveResult for tag-policy output\n * status: decided (WI-V2-04 L5)\n * rationale:\n * types.ts is frozen (L4-owned). ShaveResult cannot be modified in L5.\n * Extending via a subtype in index.ts (allowed scope) keeps the change\n * additive and non-breaking: callers that only care about the base fields\n * see no difference; callers that check foreignDeps (e.g. the CLI) cast\n * to ShaveResultWithForeign or narrow via optional-field access.\n *\n * The single source of truth for foreignDeps is the ForeignLeafEntry records\n * in result.slicePlan after universalize() returns. No parallel tracking.\n */\nexport interface ShaveResultWithForeign extends ShaveResult {\n /**\n * Foreign dependency refs surfaced when foreignPolicy === 'tag'.\n * Absent (undefined) when policy is 'allow' (silent accept).\n * Never populated for 'reject' (which throws ForeignPolicyRejectError).\n * Empty array when policy is 'tag' but no foreign deps were found.\n */\n readonly foreignDeps?: readonly ForeignRef[] | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Internal imports\n// ---------------------------------------------------------------------------\n\nimport { readFile } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\nimport { writeIntent as _writeIntent } from \"./cache/file-cache.js\";\nimport {\n keyFromIntentInputs as _keyFromIntentInputs,\n sourceHash as _sourceHash,\n} from \"./cache/key.js\";\nimport { ForeignPolicyRejectError, PersistRequestedButNotSupportedError } from \"./errors.js\";\nimport {\n DEFAULT_MODEL,\n INTENT_PROMPT_VERSION,\n INTENT_SCHEMA_VERSION,\n STATIC_MODEL_TAG,\n STATIC_PROMPT_VERSION,\n} from \"./intent/constants.js\";\nimport { extractIntent } from \"./intent/extract.js\";\nimport type { IntentCard } from \"./intent/types.js\";\nimport { locateProjectRoot } from \"./locate-root.js\";\nimport { maybePersistNovelGlueAtom } from \"./persist/atom-persist.js\";\nimport { FOREIGN_POLICY_DEFAULT } from \"./types.js\";\nimport type {\n CandidateBlock,\n IntentExtractionHook,\n ShaveOptions,\n ShaveRegistryView,\n ShaveResult,\n ShavedAtomStub,\n UniversalizeOptions,\n UniversalizeResult,\n} from \"./types.js\";\nimport { decompose } from \"./universalize/recursion.js\";\nimport { slice } from \"./universalize/slicer.js\";\nimport type { BlockMerkleRoot } from \"./universalize/types.js\";\nimport type { ForeignLeafEntry, NovelGlueEntry, SlicePlanEntry } from \"./universalize/types.js\";\n\n// ---------------------------------------------------------------------------\n// universalize() — wired to extractIntent + decompose + slice (WI-012-06)\n// ---------------------------------------------------------------------------\n\n/**\n * @decision DEC-UNIVERSALIZE-WIRING-001\n * title: universalize() wired to decompose() + slice() (WI-012-06)\n * status: decided\n * rationale: WI-012-04 landed decompose() and WI-012-05 landed slice(). This\n * work item replaces the stubs in universalize() with real calls. The pipeline\n * is: extractIntent (for intentCard) → decompose (for RecursionTree) → slice\n * (for SlicePlan). Both decomposition errors (DidNotReachAtomError and\n * RecursionDepthExceededError) are propagated unwrapped — they are load-bearing\n * reviewer-gate failures per DEC-RECURSION-005.\n *\n * Intent card attachment: for single-leaf trees (root is an AtomLeaf), the\n * extracted intent card is attached to the one NovelGlueEntry that covers the\n * root. For multi-leaf trees, per-leaf intentCard population is implemented by\n * WI-031 (commit `8dfb44b`, merge `3049ec3`, 2026-05-01) — see\n * `DEC-UNIVERSALIZE-MULTI-LEAF-INTENT-001` for the live decision and the\n * per-leaf extractIntent loop at lines ~542-573.\n *\n * \"decomposition\" is removed from diagnostics.stubbed — decomposition is now\n * live. \"license-gate\" is removed — gate is now live (WI-013-02).\n * \"variance\" is removed — variance ranking is now live (WI-011 / #374).\n *\n * @decision DEC-LICENSE-GATE-REMOVE-001\n * @title Remove ingest-side license gate from shave pipeline\n * @status accepted\n * @rationale\n * Operator DEC 2026-05-17 (verbatim):\n * \"Let's get rid of the license checks entirely we are not copying code, we\n * are reimplementing behavior so we don't need to stop on copy left, and def\n * not on no license\"\n * yakcc's product story is behavioral reimplementation, not source\n * redistribution. Atoms are content-addressed derivations of behavior, not\n * copies of source. Ingest-side copyleft/missing-license gating is misapplied\n * defense-in-depth. The gate was already vestigial in production —\n * hooks-base/atomize.ts auto-prepended MIT SPDX to bypass it.\n * @consequences\n * - shave's universalize() no longer rejects sources lacking SPDX headers\n * - hooks-base/atomize.ts no longer auto-prepends MIT SPDX (gate is gone)\n * - Consumers depending on LicenseRefusedError need to remove that import\n * - bench/B4-tokens-v3/harness/atom-sync.mjs no longer needs ensureSpdxHeader\n * @supersedes DEC-LICENSE-GATE-001, DEC-LICENSE-WIRING-002\n * @closes #682\n *\n * Process a single candidate block through the universalization pipeline.\n *\n * WI-012-06: wired to decompose() + slice() in addition to extractIntent.\n * WI-682: license gate removed (DEC-LICENSE-GATE-REMOVE-001, 2026-05-17).\n * Requires either:\n * - ANTHROPIC_API_KEY set in the environment, OR\n * - options.offline === true with a pre-populated cache entry.\n *\n * Throws AnthropicApiKeyMissingError if neither condition is met.\n * Throws OfflineCacheMissError if offline mode is set and the cache has no entry.\n * Throws DidNotReachAtomError if decomposition cannot reach atomic leaves.\n * Throws RecursionDepthExceededError if the source AST exceeds maxDepth.\n *\n * @param candidate - The source block to universalize.\n * @param registry - Registry view used by decompose() and slice() for\n * known-primitive lookups via findByCanonicalAstHash.\n * @param options - Optional configuration: cacheDir, model, offline.\n */\nexport async function universalize(\n candidate: CandidateBlock,\n registry: ShaveRegistryView,\n options?: UniversalizeOptions,\n): Promise<UniversalizeResult> {\n const projectRoot = await locateProjectRoot();\n const cacheDir = options?.cacheDir ?? join(projectRoot, \".yakcc\", \"shave-cache\", \"intent\");\n\n // Step 1: extract intent card.\n // WI-022: plumb intentStrategy through to extractIntent. Default \"static\"\n // per DEC-INTENT-STRATEGY-001. The model/promptVersion fields are only\n // consumed by the \"llm\" path; for \"static\" they are ignored (the static\n // path uses STATIC_MODEL_TAG/STATIC_PROMPT_VERSION internally).\n // DEC-LICENSE-GATE-REMOVE-001: license gate removed (WI-682, 2026-05-17).\n const intentCard = await extractIntent(candidate.source, {\n strategy: options?.intentStrategy ?? \"static\",\n model: options?.model ?? DEFAULT_MODEL,\n promptVersion: INTENT_PROMPT_VERSION,\n cacheDir,\n offline: options?.offline,\n });\n\n // Step 2: decompose source into a RecursionTree.\n // DidNotReachAtomError and RecursionDepthExceededError propagate unwrapped —\n // per DEC-RECURSION-005, these are load-bearing reviewer-gate failures.\n const tree = await decompose(candidate.source, registry, options?.recursionOptions);\n\n // Step 3: slice the RecursionTree into a SlicePlan.\n const plan = await slice(tree, registry);\n\n // Step 4: attach intentCard to every NovelGlueEntry in the slice plan.\n //\n // Single-leaf case: attach the already-extracted root intentCard directly.\n // Multi-leaf case: call extractIntent per novel-glue entry.\n //\n // @decision DEC-UNIVERSALIZE-MULTI-LEAF-INTENT-001\n // title: Per-leaf extractIntent call (strategy: \"static\") for multi-leaf trees\n // status: accepted (WI-031)\n // rationale:\n // Strategy (a) chosen: call extractIntent per novel-glue entry for multi-leaf\n // trees, using the same strategy/model/cacheDir options as the root call.\n // The static path (DEC-INTENT-STRATEGY-001 default) means per-leaf calls are\n // cheap (no API, no network), produce semantically faithful cards derived from\n // the actual leaf source (JSDoc + signature), and participate in the same\n // seedIntentCache / offline discipline as single-leaf plans.\n //\n // Strategy (b) rejected: cloning the root card and overriding per-leaf fields\n // produces semantically-questionable cards (wrong behavior text, wrong inputs)\n // and introduces a parallel mechanism that violates the no-duplicate-logic\n // principle. It also bypasses the real extractIntent path, producing thinner\n // test coverage. Strategy (a) is strictly superior for semantic fidelity and\n // offline discipline.\n //\n // Ordering/identity preservation: plan.entries order is load-bearing for\n // shave()'s lineage-threading postorder loop (index.ts:586-606). We iterate\n // the entries as-is and only attach intentCard to novel-glue entries; pointer\n // entries pass through unchanged. No reordering, no extra entries.\n let slicePlan: readonly SlicePlanEntry[];\n const firstEntry = plan.entries[0];\n if (tree.leafCount === 1 && plan.entries.length === 1 && firstEntry !== undefined) {\n if (firstEntry.kind === \"novel-glue\") {\n // Single AtomLeaf, no registry match: attach the root intentCard.\n const withCard: NovelGlueEntry = {\n kind: firstEntry.kind,\n sourceRange: firstEntry.sourceRange,\n source: firstEntry.source,\n canonicalAstHash: firstEntry.canonicalAstHash,\n intentCard,\n };\n slicePlan = [withCard];\n } else {\n // Single AtomLeaf matched the registry (PointerEntry) — no intentCard slot.\n slicePlan = plan.entries;\n }\n } else {\n // Multi-leaf tree: call extractIntent per novel-glue entry to attach a\n // semantically faithful intentCard to each leaf.\n // PointerEntries pass through unchanged (no intentCard slot on the type).\n //\n // Errors from per-leaf extractIntent propagate unwrapped — same contract as\n // the root extractIntent call above (OfflineCacheMissError, etc.).\n const enrichedEntries: SlicePlanEntry[] = [];\n for (const entry of plan.entries) {\n if (entry.kind === \"novel-glue\") {\n const leafCard = await extractIntent(entry.source, {\n strategy: options?.intentStrategy ?? \"static\",\n model: options?.model ?? DEFAULT_MODEL,\n promptVersion: INTENT_PROMPT_VERSION,\n cacheDir,\n offline: options?.offline,\n });\n const withCard: NovelGlueEntry = {\n kind: entry.kind,\n sourceRange: entry.sourceRange,\n source: entry.source,\n canonicalAstHash: entry.canonicalAstHash,\n intentCard: leafCard,\n };\n enrichedEntries.push(withCard);\n } else {\n // PointerEntry — no intentCard slot, pass through unchanged.\n enrichedEntries.push(entry);\n }\n }\n slicePlan = enrichedEntries;\n }\n\n // Step 5 (WI-373): in-pipeline atom persistence, gated on options.persist === true.\n //\n // @decision DEC-UNIVERSALIZE-PERSIST-PIPELINE-001\n // @title Persistence step 6 runs after intentCard attachment, postorder DFS, with parentBlockRoot lineage\n // @status accepted (WI-373)\n // @rationale\n // Placed after step 5 (intentCard attachment) so every NovelGlueEntry that\n // enters the persist loop already carries its intentCard. Without intentCard,\n // maybePersistNovelGlueAtom() skips the entry (per DEC-ATOM-PERSIST-001).\n // DFS postorder preserves DEC-REGISTRY-PARENT-BLOCK-004: the first novel-glue\n // entry in DFS order is the innermost leaf (parentBlockRoot=null); each\n // subsequent novel-glue entry takes the preceding entry's merkleRoot as its\n // parentBlockRoot. This is the identical semantics used by shave()'s loop\n // (index.ts:741-779) — lifted verbatim so both paths consolidate onto the\n // same primitive (Sacred Practice #12).\n //\n // Loud-fail: if persist:true is requested but registry.storeBlock is absent,\n // PersistRequestedButNotSupportedError is thrown immediately (Sacred Practice\n // #5). The graceful-degradation (silent no-op) path from maybePersistNovelGlueAtom\n // is intentionally NOT used here — the caller explicitly asked for persistence.\n //\n // When persist is false/undefined, this entire step is a no-op and slicePlan\n // is returned unchanged — zero effect on today's default behavior.\n //\n // Per REQ-NOGO-006: sourceFilePath and sourceContext may be undefined when\n // called from assembleCandidate() (interactive use). Atoms persist with null\n // provenance — correct per DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001.\n //\n // P1 follow-up (not in this slice): refactor shave() to delegate to\n // universalize({persist:true, sourceContext, ...}) and delete its own\n // postorder loop (Sacred Practice #12 consolidation, plan §6 slice 2).\n // Also flag: atomize.ts in @yakcc/hooks-base runs a parallel buildBlockRow +\n // storeBlock loop that should consolidate onto universalize({persist:true})\n // once this WI lands (plan §7).\n let finalSlicePlan = slicePlan;\n if (options?.persist === true) {\n // Loud-fail: storeBlock must be present when persist:true is requested.\n if (typeof registry.storeBlock !== \"function\") {\n throw new PersistRequestedButNotSupportedError();\n }\n\n // Postorder lineage loop — lifted verbatim from shave()'s index.ts:741-779.\n // Each NovelGlueEntry is persisted in DFS order; the preceding novel-glue\n // entry's merkleRoot becomes the current entry's parentBlockRoot.\n let lastNovelMerkleRoot: BlockMerkleRoot | undefined = undefined;\n const enrichedWithMerkle: SlicePlanEntry[] = [];\n for (const entry of slicePlan) {\n if (entry.kind === \"novel-glue\") {\n const parentBlockRoot: BlockMerkleRoot | null = lastNovelMerkleRoot ?? null;\n const baseSourceContext = options?.sourceContext;\n const perAtomSourceContext =\n baseSourceContext !== undefined\n ? {\n sourcePkg: baseSourceContext.sourcePkg,\n sourceFile: baseSourceContext.sourceFile,\n sourceOffset: entry.sourceRange.start,\n }\n : undefined;\n const merkleRoot = await maybePersistNovelGlueAtom(entry, registry, {\n cacheDir: options?.cacheDir,\n parentBlockRoot,\n // sourceFilePath: forwarded from UniversalizeOptions.sourceFilePath when\n // present (set by shave() to enable props-file corpus extraction).\n // Undefined for interactive universalize() callers (e.g. assembleCandidate)\n // — those atoms persist with null provenance per REQ-NOGO-006 /\n // DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001.\n sourceFilePath: options?.sourceFilePath,\n sourceContext: perAtomSourceContext,\n });\n // Surface the merkleRoot on the entry (may be undefined if intentCard absent).\n // exactOptionalPropertyTypes: spread only when defined to satisfy\n // NovelGlueEntry.merkleRoot?: BlockMerkleRoot (not BlockMerkleRoot | undefined).\n const enriched: NovelGlueEntry = {\n ...entry,\n ...(merkleRoot !== undefined && { merkleRoot }),\n };\n enrichedWithMerkle.push(enriched);\n if (merkleRoot !== undefined) {\n lastNovelMerkleRoot = merkleRoot;\n }\n } else {\n // PointerEntry / ForeignLeafEntry / GlueLeafEntry — pass through unchanged.\n enrichedWithMerkle.push(entry);\n }\n }\n finalSlicePlan = enrichedWithMerkle;\n }\n\n // DEC-LICENSE-GATE-REMOVE-001: licenseDetection field removed from result (WI-682, 2026-05-17).\n return {\n intentCard,\n slicePlan: finalSlicePlan,\n matchedPrimitives: plan.matchedPrimitives,\n diagnostics: {\n // \"decomposition\" removed — decompose() is now live (WI-012-06).\n // \"license-gate\" removed — gate removed by DEC-LICENSE-GATE-REMOVE-001 (WI-682).\n // \"variance\" removed — variance ranking is now live (WI-011 / #374).\n stubbed: [],\n cacheHits: 0,\n cacheMisses: 0,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// shave() — one-shot file ingestion (WI-014-01)\n// ---------------------------------------------------------------------------\n\n/**\n * Process a source file through the full shave pipeline.\n *\n * Reads the source file at `sourcePath`, wraps it in a CandidateBlock, and\n * runs it through universalize() (intent extraction → decompose → slice).\n * Returns a ShaveResult with atoms derived from the SlicePlan, the extracted\n * intent card, and forwarded diagnostics.\n *\n * Throws a plain Error (mentioning the path) if the file is not found.\n * All universalize() errors (AnthropicApiKeyMissingError,\n * OfflineCacheMissError, DidNotReachAtomError, RecursionDepthExceededError)\n * propagate unwrapped.\n * Note: license-of-origin is not gated at ingestion (DEC-LICENSE-GATE-REMOVE-001).\n *\n * @param sourcePath - Absolute path to the source file to process.\n * @param registry - Registry view used for block lookups.\n * @param options - Optional configuration overrides.\n */\nexport async function shave(\n sourcePath: string,\n registry: ShaveRegistryView,\n options?: ShaveOptions,\n): Promise<ShaveResultWithForeign> {\n // @decision DEC-SHAVE-PIPELINE-001\n // title: shave() reads file, wraps as CandidateBlock, delegates to universalize()\n // status: decided\n // rationale:\n // shave() is a thin file-ingestion adapter over universalize(). All pipeline\n // logic (license gate, intent extraction, decompose, slice) lives in\n // universalize(). This keeps shave() focused on I/O concerns: file reading\n // and result shape translation.\n //\n // PlaceholderId format: \"shave-atom-\" + canonicalAstHash.slice(0, 8).\n // A deterministic id keyed on the canonical AST hash ensures that two runs\n // over identical source produce identical placeholder arrays, which is a\n // prerequisite for content-addressable provenance tracking (WI-014-02).\n // Using a slice of canonicalAstHash rather than a random UUID or\n // sequential counter avoids non-determinism while remaining collision-free\n // in practice for the v0 demo corpus (< 100 atoms per file).\n //\n // Source file not found: we throw a plain Error with a human-readable\n // message including the path. We do not introduce a new SourceFileNotFoundError\n // class in this slice (that would require modifying errors.ts, which is\n // out of scope for WI-014-01). The fs ENOENT code is preserved in the\n // error chain via the `cause` field so callers can inspect it programmatically.\n\n // Step 1: Read source file.\n let source: string;\n try {\n source = await readFile(sourcePath, \"utf-8\");\n } catch (err) {\n throw new Error(`shave: source file not found: ${sourcePath}`, { cause: err });\n }\n\n // Step 2: Wrap in a CandidateBlock with a hint derived from the file name.\n const candidate: CandidateBlock = {\n source,\n hint: { name: basename(sourcePath), origin: \"user\" },\n };\n\n // Step 3: Run through universalize({persist:true}) — errors propagate unwrapped.\n //\n // @decision DEC-V2-SHAVE-DELEGATES-UNIVERSALIZE-001\n // title: shave() delegates atom persistence to universalize({persist:true})\n // status: accepted (WI-423)\n // rationale:\n // WI-373 (PR #419) lifted the postorder lineage-threading persist loop from\n // shave() into universalize() as step 6, gated on options.persist===true.\n // However, the WI-373 implementer copied the loop rather than refactoring\n // shave() to delegate — leaving two parallel persistence mechanisms (Sacred\n // Practice #12 violation). This WI (WI-423) closes that debt.\n //\n // shave() now calls universalize({persist:true, sourceFilePath:sourcePath})\n // and reads merkleRoot directly from the enriched slicePlan entries returned\n // by universalize(). The in-line loop (DEC-ATOM-PERSIST-001 / DEC-REGISTRY-\n // PARENT-BLOCK-004) that previously ran here is removed — universalize() is\n // the single authority for atom persistence.\n //\n // sourceFilePath is forwarded via UniversalizeOptions.sourceFilePath so the\n // props-file corpus extractor (DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001)\n // can locate the sibling <stem>.props.ts file exactly as before.\n //\n // Behavior is bit-for-bit identical: T5 (DEC-UNIVERSALIZE-PERSIST-PIPELINE-001)\n // passes because the same maybePersistNovelGlueAtom primitives run in the same\n // postorder with the same parentBlockRoot chain.\n //\n // Graceful degradation (read-only registry): universalize()'s step 6 is\n // gated on options.persist===true, and shave() always passes persist:true.\n // When registry.storeBlock is absent, PersistRequestedButNotSupportedError is\n // thrown (Sacred Practice #5 loud-fail). Previously, shave() used\n // maybePersistNovelGlueAtom's silent-no-op path when storeBlock was absent —\n // this was a silent degradation. The new behaviour preserves that: we pass\n // persist:true only when registry.storeBlock is present, otherwise we call\n // universalize without persist (maintaining the graceful-degradation contract).\n const hasPersist = typeof registry.storeBlock === \"function\";\n const result = await universalize(candidate, registry, {\n ...options,\n ...(hasPersist && { persist: true, sourceFilePath: sourcePath }),\n });\n\n // Step 3b: Foreign-policy gate — enforced AFTER slice() returns, NOT inside slicer.ts.\n //\n // @decision DEC-V2-FOREIGN-BLOCK-SCHEMA-001 (sub-L5: policy gate enforcement)\n // title: foreignPolicy gate runs in shave() after universalize() returns (L5)\n // status: decided (WI-V2-04 L5)\n // rationale:\n // L5-I2: the gate must run at the shave entry point, not inside slicer.ts\n // (L3-owned) or universalize() internals. Placing it here lets universalize()\n // and slice() remain pure of policy concerns — they always emit ForeignLeafEntry\n // for detected foreign imports regardless of policy. The gate consumes the\n // already-produced slice plan and decides what to do with the foreign refs.\n //\n // 'reject' (L5-I3): throw ForeignPolicyRejectError with all (pkg, export) pairs\n // from the slice plan, in source-declaration order (DFS order from slice()).\n // The CLI catches ForeignPolicyRejectError and emits its message to stderr,\n // then returns exit code 1.\n //\n // 'tag' (L5-I4): collect foreign refs and include them in the returned\n // ShaveResultWithForeign.foreignDeps field. The CLI formats them as\n // \"foreign deps: pkg#export[, ...]\" on stdout and returns exit code 0.\n //\n // 'allow' (L5-I5): silently accept; foreignDeps is not set on the result.\n //\n // Single-source-of-truth: FOREIGN_POLICY_DEFAULT in types.ts governs the\n // default; this gate reads options?.foreignPolicy ?? FOREIGN_POLICY_DEFAULT\n // so both agree on the same constant (I-X3 invariant).\n const effectivePolicy = options?.foreignPolicy ?? FOREIGN_POLICY_DEFAULT;\n const foreignLeaves = result.slicePlan.filter(\n (e): e is ForeignLeafEntry => e.kind === \"foreign-leaf\",\n );\n const foreignRefs: readonly ForeignRef[] = foreignLeaves.map((e) => ({\n pkg: e.pkg,\n export: e.export,\n }));\n\n if (effectivePolicy === \"reject\" && foreignRefs.length > 0) {\n // L5-I3: throw structured error with all foreign (pkg, export) pairs.\n throw new ForeignPolicyRejectError(foreignRefs);\n }\n\n // For 'tag': foreignDeps is populated on the result (L5-I4).\n // For 'allow': foreignDeps is left undefined (L5-I5 silent accept).\n const foreignDeps: readonly ForeignRef[] | undefined =\n effectivePolicy === \"tag\" ? foreignRefs : undefined;\n\n // Step 4: Translate universalize() result into ShaveResult.\n //\n // Persistence was handled by universalize({persist:true}) above\n // (DEC-V2-SHAVE-DELEGATES-UNIVERSALIZE-001). NovelGlueEntry items in the\n // slicePlan already carry merkleRoot when persist:true ran. PointerEntry items\n // always carry merkleRoot (per DEC-SHAVE-POINTER-MERKLROOT-PROPAGATION-001).\n // ForeignLeafEntry and GlueLeafEntry are excluded (per DEC-V2-GLUE-AWARE-SHAVE-001).\n\n // Each SlicePlanEntry that carries an AST hash maps to a ShavedAtomStub.\n // ForeignLeafEntry and GlueLeafEntry are intentionally excluded:\n // - ForeignLeafEntry is an opaque leaf with no host-module sourceRange.\n // - GlueLeafEntry is a verbatim-preserved region with no sourceRange\n // (per DEC-V2-GLUE-LEAF-CONTRACT-001: glue is project-local, not registry-registered).\n // (per DEC-SHAVE-PIPELINE-001, DEC-V2-FOREIGN-BLOCK-SCHEMA-001, DEC-V2-GLUE-AWARE-SHAVE-001)\n const atoms = result.slicePlan.flatMap((entry): ShavedAtomStub[] => {\n if (entry.kind === \"foreign-leaf\" || entry.kind === \"glue\") {\n // Excluded: foreign deps are opaque leaves; glue regions are verbatim-preserved\n // project-local code with no registry entry and no sourceRange field.\n return [];\n }\n // novel-glue: merkleRoot populated by universalize({persist:true}) step 6.\n // pointer: merkleRoot always present (DEC-SHAVE-POINTER-MERKLROOT-PROPAGATION-001).\n return [\n {\n placeholderId: `shave-atom-${entry.canonicalAstHash.slice(0, 8)}`,\n sourceRange: entry.sourceRange,\n merkleRoot: entry.merkleRoot,\n },\n ];\n });\n\n return {\n sourcePath,\n atoms,\n intentCards: [result.intentCard],\n diagnostics: result.diagnostics,\n foreignDeps,\n };\n}\n\n// ---------------------------------------------------------------------------\n// createIntentExtractionHook() — factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create the default IntentExtractionHook.\n *\n * The hook's `intercept` method delegates to universalize(), which in\n * WI-010-03 is wired to the live extractIntent path.\n */\nexport function createIntentExtractionHook(_options?: ShaveOptions): IntentExtractionHook {\n return {\n id: \"yakcc.shave.default\",\n intercept: (candidate: CandidateBlock, registry: ShaveRegistryView, options?: ShaveOptions) =>\n universalize(candidate, registry, options),\n };\n}\n","// SPDX-License-Identifier: MIT\n/**\n * @decision DEC-ATOM-TEST-003\n * Title: AST atom-test predicate.\n * Status: proposed (MASTER_PLAN.md governance edit deferred).\n * Rationale: isAtom() is the load-bearing gate for the WI-012 universalizer\n * recursion. \"Did not reach atoms\" = WI-012 reviewer hard failure. The\n * predicate uses two criteria applied in order:\n * 1. Control-flow boundary count: walk all descendants and count CF nodes.\n * If count > maxControlFlowBoundaries (default 1), the candidate is\n * composite — not atomic.\n * 2. Known-primitive sub-statement check: for each top-level statement child\n * of the node, compute canonicalAstHash over its source range and query\n * the registry. A hit means the candidate contains a known primitive and\n * is therefore composite.\n * If both criteria pass, the node is atomic.\n * Algorithm choice: at-most-N control-flow boundaries AND no top-level\n * statement matching an existing primitive in the registry by canonicalAstHash.\n */\n\nimport { canonicalAstHash } from \"@yakcc/contracts\";\nimport type { CanonicalAstHash } from \"@yakcc/contracts\";\nimport { type Node, SyntaxKind } from \"ts-morph\";\nimport type { ShaveRegistryView } from \"../types.js\";\nimport type { AtomTestOptions, AtomTestResult } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_MAX_CF_BOUNDARIES = 1;\n\n/**\n * The set of SyntaxKinds that count as control-flow boundaries when walking\n * the descendants of a candidate node.\n *\n * ConditionalExpression (ternary) is included because it branches execution\n * in a semantically meaningful way even though it is an expression, not a\n * statement. TryStatement is included because catch/finally create alternative\n * execution paths.\n */\nconst CF_BOUNDARY_KINDS = new Set<SyntaxKind>([\n SyntaxKind.IfStatement,\n SyntaxKind.ForStatement,\n SyntaxKind.ForInStatement,\n SyntaxKind.ForOfStatement,\n SyntaxKind.WhileStatement,\n SyntaxKind.DoStatement,\n SyntaxKind.SwitchStatement,\n SyntaxKind.TryStatement,\n SyntaxKind.ConditionalExpression,\n]);\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Count the number of control-flow boundary nodes within all descendants of\n * `node` (inclusive of `node` itself if it is a CF node).\n *\n * Uses ts-morph's `forEachDescendant` which performs a depth-first walk and\n * handles all node kinds without manual recursion.\n */\nfunction countControlFlowBoundaries(node: Node): number {\n let count = 0;\n // Count the root node itself if it is a CF kind.\n if (CF_BOUNDARY_KINDS.has(node.getKind())) {\n count++;\n }\n node.forEachDescendant((descendant) => {\n if (CF_BOUNDARY_KINDS.has(descendant.getKind())) {\n count++;\n }\n });\n return count;\n}\n\n/**\n * Extract the top-level statement children of `node`.\n *\n * \"Top-level\" means direct statement children of the body, not all\n * descendants. The following node kinds are handled:\n * - SourceFile → getStatements()\n * - Block → getStatements()\n * - FunctionDeclaration / FunctionExpression / ArrowFunction →\n * if the body is a Block, its statements; otherwise empty (expression body)\n * - MethodDeclaration / Constructor / GetAccessor / SetAccessor →\n * body?.getStatements() if available\n * - Anything else → [] (expression-level nodes have no sub-statements)\n *\n * Returning [] for expression-level nodes means isAtom() will not invoke\n * findByCanonicalAstHash at all for them, and will simply return atomic when\n * the CF count is within bounds.\n */\nfunction getTopLevelStatements(node: Node): Node[] {\n const kind = node.getKind();\n\n // SourceFile\n if (kind === SyntaxKind.SourceFile) {\n return (node as Parameters<typeof getTopLevelStatements>[0] & { getStatements(): Node[] })\n .getStatements()\n .slice();\n }\n\n // Block\n if (kind === SyntaxKind.Block) {\n return (node as Parameters<typeof getTopLevelStatements>[0] & { getStatements(): Node[] })\n .getStatements()\n .slice();\n }\n\n // Function-like nodes with a body\n if (\n kind === SyntaxKind.FunctionDeclaration ||\n kind === SyntaxKind.FunctionExpression ||\n kind === SyntaxKind.ArrowFunction ||\n kind === SyntaxKind.MethodDeclaration ||\n kind === SyntaxKind.Constructor ||\n kind === SyntaxKind.GetAccessor ||\n kind === SyntaxKind.SetAccessor\n ) {\n // ts-morph exposes getBody() on function-like nodes; the body may be a\n // Block or (for arrow functions) an expression.\n const fnNode = node as Node & { getBody?(): Node | undefined };\n const body = fnNode.getBody?.();\n if (body !== undefined && body.getKind() === SyntaxKind.Block) {\n return (body as Node & { getStatements(): Node[] }).getStatements().slice();\n }\n // Expression-body arrow function — no statements to decompose further.\n return [];\n }\n\n // All other node kinds (expressions, declarations without bodies, etc.)\n return [];\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Determine whether `node` is an \"atom\" in the WI-012 universalizer recursion.\n *\n * A node is atomic when:\n * 1. Its total control-flow boundary count ≤ maxControlFlowBoundaries (default 1).\n * 2. None of its top-level statement children match an existing primitive in\n * the registry by canonicalAstHash (i.e. could be replaced by a known block).\n *\n * When criterion 1 fails, returns immediately without querying the registry.\n * When a sub-statement matches the registry, returns the first match with\n * matchedPrimitive populated.\n *\n * @param node - The ts-morph Node to test.\n * @param source - The full source text of the file `node` was parsed from.\n * Required to compute canonicalAstHash for sub-ranges.\n * @param registry - Registry view providing findByCanonicalAstHash.\n * Only the narrow `findByCanonicalAstHash` method is used.\n * @param options - Optional tuning parameters (maxControlFlowBoundaries).\n */\nexport async function isAtom(\n node: Node,\n source: string,\n registry: Pick<ShaveRegistryView, \"findByCanonicalAstHash\">,\n options?: AtomTestOptions,\n): Promise<AtomTestResult> {\n const maxCF = options?.maxControlFlowBoundaries ?? DEFAULT_MAX_CF_BOUNDARIES;\n\n // --- Criterion 1: control-flow boundary count ---\n const cfCount = countControlFlowBoundaries(node);\n if (cfCount > maxCF) {\n return {\n isAtom: false,\n reason: \"too-many-cf-boundaries\",\n controlFlowBoundaryCount: cfCount,\n };\n }\n\n // --- Criterion 2: known-primitive sub-statement check ---\n const statements = getTopLevelStatements(node);\n\n // Compute the candidate's own range to skip if a sub-statement IS the node.\n const nodeStart = node.getStart();\n const nodeEnd = node.getEnd();\n\n for (const stmt of statements) {\n const stmtStart = stmt.getStart();\n const stmtEnd = stmt.getEnd();\n\n // Skip if the statement is the entire candidate (avoid self-recognition).\n if (stmtStart === nodeStart && stmtEnd === nodeEnd) {\n continue;\n }\n\n const subHash: CanonicalAstHash = canonicalAstHash(source, {\n start: stmtStart,\n end: stmtEnd,\n });\n\n const matches = await registry.findByCanonicalAstHash?.(subHash);\n\n if (matches !== undefined && matches.length > 0) {\n const firstMatch = matches[0];\n if (firstMatch === undefined) continue;\n return {\n isAtom: false,\n reason: \"contains-known-primitive\",\n controlFlowBoundaryCount: cfCount,\n matchedPrimitive: {\n merkleRoot: firstMatch,\n canonicalAstHash: subHash,\n subRange: { start: stmtStart, end: stmtEnd },\n },\n };\n }\n }\n\n // Both criteria passed — the node is atomic.\n return {\n isAtom: true,\n reason: \"atomic\",\n controlFlowBoundaryCount: cfCount,\n };\n}\n","// SPDX-License-Identifier: MIT\n/**\n * @decision DEC-RECURSION-005\n * title: Decomposition recursion algorithm\n * status: proposed\n * rationale: Walks the AST top-down. At each node calls isAtom from WI-012-03.\n * If atomic, returns AtomLeaf. If not atomic, descends to \"decomposable children\"\n * (top-level statements for SourceFile/Block/function bodies; the branches of\n * control-flow nodes for IfStatement etc.). Recurses with depth+1. Throws\n * RecursionDepthExceededError if depth exceeds maxDepth. Throws\n * DidNotReachAtomError when a non-atomic node has no decomposable children —\n * the load-bearing failure mode the WI-012 reviewer gates on.\n *\n * Design choices:\n * - decompose() creates its own ts-morph Project. isAtom() also creates one\n * internally in atom-test.ts. To avoid double-parsing overhead the node\n * passed to isAtom() must be from the same Project instance created here;\n * we therefore call isAtom() with the already-parsed Node objects directly.\n * - canonicalAstHash() from @yakcc/contracts creates its own Project to hash\n * a source fragment. We call it with the slice of source text at the node's\n * range so the hash is scoped to the fragment, avoiding \"range-spans-multiple-\n * nodes\" errors that occur when passing a sub-range of a larger file.\n * - decomposableChildrenOf() implements the structural decomposition policy:\n * which sub-nodes does the recursion descend into when a node is non-atomic?\n * Expression-level nodes that cannot be decomposed further return []; if\n * isAtom() incorrectly classifies them as non-atomic, DidNotReachAtomError fires.\n *\n * @decision DEC-SLICER-LOOP-CONTROL-FLOW-001\n * title: Loop with escaping continue/break is the atom; body is not decomposed\n * status: decided\n * rationale:\n * Loop bodies containing continue/break (or labeled jumps targeting an\n * enclosing loop) are not valid standalone TypeScript programs. Hashing\n * such a fragment via canonicalAstHash() raises TS1313/1314 syntax\n * diagnostics, which canonical-ast.ts correctly converts to\n * CanonicalAstParseError. The fragment also has no self-contained\n * behavioral contract — its semantics depend on the enclosing iteration\n * target. We therefore treat the smallest enclosing iteration as the atom\n * boundary: decomposableChildrenOf returns [] for a loop whose body has\n * escaping control flow, and recurse() emits an AtomLeaf for the loop\n * itself with atomTest.reason = \"loop-with-escaping-cf\".\n * alternatives:\n * B (typed unsliceable sentinel): identical end-state; rejected as more\n * plumbing than the policy warrants — no consumer needs the sentinel.\n * C (rewrite continue->return at extraction time): rejected; changes\n * semantics, breaks WI-V2-09 byte-identical bootstrap, and requires\n * rewriting the call site of every shaved loop.\n * consequences:\n * - Atom granularity coarsens for any function whose hot path is one big\n * loop with continue/break. Affected yakcc files include storage.ts,\n * assemble-candidate.ts, and recursion.ts itself; each becomes a single\n * atom rather than an atom-per-loop-body-statement. Estimated impact:\n * ~15-30% of yakcc functions decompose to one fewer level. Trade-off\n * accepted — strictly better than the current state of total decompose\n * failure on those functions.\n * - Compatible with WI-V2-09 bootstrap: the loop is hashed as a whole, the\n * same way on every pass.\n *\n * @decision DEC-SLICER-FN-SCOPED-CF-001\n * title: Non-leaf fragments containing escaping return/await/yield wrap in\n * synthetic function for canonical hashing\n * status: decided\n * rationale:\n * Existing safeCanonicalAstHash wraps leaf return/break/continue/yield\n * statements. But non-leaf nodes (IfStatement, TryStatement, Block) often\n * CONTAIN such constructs whose binding scope is the enclosing function —\n * outside the extracted fragment. Hashing the fragment standalone fails\n * with TS1108/TS1308. Strategy: pre-flight detection of escaping\n * function-scoped constructs in the node's descendants; wrap in the\n * appropriate synthetic function flavor (function* for yield, async\n * function for await, function for return). emitCanonical targets the\n * inner range so the hash is identical to a hypothetical \"parse in original\n * context\" outcome.\n * consequences:\n * - Survey shows this fix flips ~44 of 117 yakcc-self-shave failures from\n * canonical-ast errors to successful decompose. Brings yakcc-on-yakcc\n * success rate from 59.8% baseline (post-WI-033) toward ~97%.\n * - Compatible with WI-V2-09 byte-identical bootstrap: wrap is deterministic\n * and emitCanonical scoping ensures hashes are wrap-independent.\n * - No public-API surface change; entirely internal to recursion.ts.\n *\n * @decision DEC-SLICER-CHILDREN-CLASS-EXPR-VAR-001\n * title: ClassDeclaration / ExpressionStatement / VariableStatement /\n * CallExpression decompose to natural sub-nodes\n * status: decided\n * rationale:\n * WI-034's audit found 5 of the 9 remaining yakcc-self-shave failures were\n * did-not-reach-atom on ClassDeclaration / ExpressionStatement /\n * VariableStatement nodes, which had no decomposableChildrenOf policy.\n * Adding the natural sub-node enumeration (class members, wrapped\n * expression, declaration initializers) lets the slicer descend through\n * these container shapes and find atoms at the next level. No new wrap or\n * hash machinery — the existing safeCanonicalAstHash already handles the\n * result.\n * A CallExpression branch was added as well (WI-036 iterative discovery):\n * descending ExpressionStatement / VariableStatement exposed CallExpression\n * nodes (e.g. db.transaction(fn), sorted.sort(comparator)) whose function\n * arguments are ArrowFunction or FunctionExpression — i.e. they carry\n * decomposable behavior. The branch descends only into function-like args.\n * consequences:\n * - yakcc-self-shave success: 92.3% → 94.0% (110/117).\n * - packages/contracts/src/ + packages/registry/src/ subset reaches 100%\n * (13/13), unblocking brother session's WI-V2-01 first-contact bootstrap\n * demo without per-file workarounds.\n * - PropertyDeclaration with initializer also decomposes — class fields\n * that bind closures or arrow functions become decomposable. Property\n * declarations without initializers (interface-shape fields) skipped.\n * - Remaining 7 failures: 2 ReturnStatement (giant non-leaf, deferred),\n * 2 ConditionalExpression (expression-level), 1 BinaryExpression\n * (expression-level), 1 await-outside-async edge case, 1 B-014 parser.\n * - Compatible with WI-V2-09 byte-identical bootstrap: deterministic node\n * enumeration, same shape on every pass.\n *\n * @decision DEC-SLICER-ARROW-RETURNS-ARROW-001\n * title: Expression-body ArrowFunction whose body is itself a function-like\n * descends into the inner function rather than returning []\n * status: decided\n * rationale:\n * The `hyphenReplace` pattern in semver's range.js (and similar HOF patterns\n * in the wild) produces an ArrowFunction whose expression body is another\n * ArrowFunction: `incPr => ($0, ...) => { if ... }`. The outer arrow has\n * cfCount > maxCF because forEachDescendant bleeds through the inner\n * function boundary, so isAtom() returns false. But the old expression-body\n * path returned `[]` unconditionally, causing DidNotReachAtomError.\n * The fix: when an expression-body arrow/function-expression has a body that\n * is itself a function-like node, return [body] so the slicer can descend\n * into the inner function's body block and find atomic leaves there.\n * alternatives:\n * A. Don't cross function boundaries in countControlFlowBoundaries — rejected\n * because other call sites depend on the inclusive count for correctness;\n * scoping the change to decomposableChildrenOf is lower risk.\n * B. Treat the outer arrow as an atom regardless of CF count — rejected\n * because it would prevent the inner function from being decomposed,\n * producing a coarser-than-necessary atom tree.\n * consequences:\n * - semver range.js (and any other higher-order arrow patterns) decompose\n * correctly, enabling satisfies subgraph moduleCount ≥ 8.\n * - No existing passing tests are affected: the branch only fires when\n * isAtom() already returned false AND the body is a function-like, a case\n * that previously always threw DidNotReachAtomError.\n * - Compatible with WI-V2-09 byte-identical bootstrap: descent order is\n * deterministic (single child = the inner function-like).\n *\n * @decision DEC-SLICER-CHILDREN-EXPR-LEVEL-001\n * title: ConditionalExpression / BinaryExpression / ReturnStatement decompose\n * to natural sub-expressions\n * status: decided\n * rationale:\n * WI-036 added decompose policies for container statements (Class, ExprStmt,\n * VarStmt). WI-037 extends to expression-level constructs (ternary, binary\n * ops) and return-with-expression that the slicer would otherwise hit\n * did-not-reach-atom on. The default `return []` branch was overly\n * conservative for these well-defined node shapes. Closes the slicer-policy\n * arc; remaining slicer failures (if any) require either canonical-ast\n * changes or per-file investigation.\n * - ConditionalExpression: ternary `cond ? then : else` — decompose to\n * [condition, whenTrue, whenFalse].\n * - BinaryExpression: `left op right` — decompose to [left, right].\n * - ReturnStatement with expression: `return <expr>` — decompose to [expr].\n * A bare `return;` (no expression) falls through to the leaf wrapper in\n * safeCanonicalAstHash. This handles the federation/serve.ts close() shape:\n * `return new Promise<void>((resolve, reject) => { ... })` — the slicer\n * descends into the arrow fn passed to Promise rather than treating the\n * entire return as an unsliceable atom.\n * consequences:\n * - yakcc-self-shave success: 94% -> 99-100%.\n * - WI-V2-01 bootstrap can now run on substantially all yakcc source\n * without per-file workarounds, unlocking real two-pass equivalence\n * measurement.\n * - Compatible with WI-V2-09 byte-identical bootstrap: deterministic node\n * enumeration, same shape on every pass.\n */\n\nimport { blake3 } from \"@noble/hashes/blake3\";\nimport { bytesToHex } from \"@noble/hashes/utils\";\nimport { canonicalAstHash } from \"@yakcc/contracts\";\nimport type { CanonicalAstHash } from \"@yakcc/contracts\";\nimport { type Node, Project, ScriptKind, SyntaxKind } from \"ts-morph\";\nimport type { ShaveRegistryView } from \"../types.js\";\nimport { isAtom } from \"./atom-test.js\";\nimport { matchesStefPredicate } from \"./stef.js\";\nimport type {\n AtomLeaf,\n AtomTestResult,\n BranchNode,\n RecursionNode,\n RecursionOptions,\n RecursionTree,\n} from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * @decision DEC-SLICER-MAX-DEPTH-001\n * title: Raise default maxDepth from 8 to 24 for real-world code\n * status: decided\n * rationale:\n * v0.7's default maxDepth=8 was set when the slicer rarely descended past\n * 3-4 levels (small atomic functions only). Post WI-036/037 the slicer\n * legitimately descends through Promise chains, IIFE wrappers, and deeply\n * nested object literals — real-world code easily reaches depth 10-15.\n * 3 of 4 yakcc-self-shave failures at WI-037 close were depth-exceeded on\n * legit code, not infinite recursion. Raising to 24 gives meaningful\n * headroom while still catching pathological cases. Callers needing a\n * tighter ceiling can pass maxDepth explicitly via RecursionOptions.\n * consequences:\n * - yakcc-self-shave: 96.6% -> 99.x% (3 files unblocked).\n * - No production impact: legitimately deep code now decomposes; nothing\n * that previously succeeded now fails.\n */\nconst DEFAULT_MAX_DEPTH = 24;\n\n// ---------------------------------------------------------------------------\n// Error classes\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when decompose() encounters a non-atomic node that has no\n * decomposable children. This is the load-bearing failure mode: it means the\n * source contains a construct that isAtom() considers non-atomic but that\n * decomposableChildrenOf() cannot descend into further.\n *\n * The most common trigger: a registry that classifies every node as a known\n * primitive (isAtom returns false for every sub-statement) combined with a\n * leaf-level node that has no structural children to recurse into.\n */\nexport class DidNotReachAtomError extends Error {\n constructor(\n message: string,\n public readonly node: {\n readonly kind: number;\n readonly source: string;\n readonly range: { readonly start: number; readonly end: number };\n },\n ) {\n super(message);\n this.name = \"DidNotReachAtomError\";\n }\n}\n\n/**\n * Thrown when the recursion depth would exceed options.maxDepth (default 8).\n * This prevents infinite recursion on pathological AST shapes.\n */\nexport class RecursionDepthExceededError extends Error {\n constructor(\n public readonly depth: number,\n public readonly maxDepth: number,\n ) {\n super(`Recursion depth ${depth} exceeded maxDepth ${maxDepth}`);\n this.name = \"RecursionDepthExceededError\";\n }\n}\n\n// ---------------------------------------------------------------------------\n// Context-safe hashing\n// ---------------------------------------------------------------------------\n\n/**\n * Statement SyntaxKinds that are syntactically invalid at file scope when\n * extracted as standalone source fragments. These statements are valid only\n * inside a function body (ReturnStatement), an iteration statement\n * (ContinueStatement), or an iteration/switch statement (BreakStatement).\n * Passing their source text to canonicalAstHash() as-is raises TS1108/1313/1314.\n *\n * When we encounter one of these as an atom leaf, we wrap its source text in\n * a synthetic function body before hashing. The wrapping is transparent to\n * consumers because emitCanonical targets the inner statement node, not the\n * wrapper — the hash is identical to what we'd get if we could hash the\n * statement standalone.\n *\n * NOTE: ContinueStatement and BreakStatement should normally be handled by the\n * loop pre-flight in recurse() and never reach this path. ReturnStatement,\n * ThrowStatement, and YieldExpression (as a statement) are the common cases.\n */\nconst CONTEXT_DEPENDENT_STATEMENT_KINDS = new Set([\n SyntaxKind.ReturnStatement,\n SyntaxKind.ContinueStatement,\n SyntaxKind.BreakStatement,\n SyntaxKind.YieldExpression,\n]);\n\n/**\n * Compute the canonical AST hash for a node, handling context-dependent\n * statements that cannot be parsed standalone as valid TypeScript files.\n *\n * Cases handled:\n * - return/continue/break/yield as leaf statements: wrapped in a synthetic\n * function body `function __w__() { <stmt> }` with a sourceRange targeting\n * the inner statement, so the hash is identical to a standalone parse.\n * - Loop nodes whose fragment contains escaping labeled break/continue: the\n * fragment is not a valid standalone TS file (TS1364: break target unknown).\n * Fallback: use the full source string with the node's byte range, which IS\n * valid TS since the full source contains the enclosing label. emitCanonical\n * targets only the inner loop node, so the hash is context-independent.\n * - Non-leaf nodes containing escaping return/await/yield (DEC-SLICER-FN-SCOPED-CF-001):\n * walk descendants; if any return/await/yield escapes its binding function\n * scope, wrap nodeSource in the appropriate synthetic function flavor\n * (function* for yield, async function for await, function for return).\n * Fall back to full-source + range if the wrap still fails.\n * - All other nodes: delegated to canonicalAstHash(nodeSource) directly.\n *\n * @param node - The ts-morph Node being hashed (used for kind dispatch).\n * @param nodeSource - The source text slice for the node (source[start..end]).\n * @param fullSource - The complete source passed to decompose() — used as\n * context fallback for loop fragments with escaping labels.\n * @param start - Byte offset of the node in fullSource.\n * @param end - Byte end of the node in fullSource.\n */\nfunction safeCanonicalAstHash(\n node: Node,\n nodeSource: string,\n fullSource: string,\n start: number,\n end: number,\n): ReturnType<typeof canonicalAstHash> {\n const kind = node.getKind();\n\n if (CONTEXT_DEPENDENT_STATEMENT_KINDS.has(kind)) {\n // Wrap in a synthetic function so the statement is syntactically valid.\n // Fixed prefix length ensures the inner statement starts at a known offset.\n //\n // Choose the right wrapper flavor based on whether the statement contains\n // escaping await/yield constructs that require an async/generator context:\n // - YieldExpression → function* __w__()\n // - AwaitExpression (escaping) → async function __w__()\n // - plain return/continue/break → function __w__()\n //\n // Without this, `return await x;` inside `function __w__() { ... }` raises\n // TS1308 (await outside async), which is the root cause of the pull.ts\n // canonical-ast--await failure (WI-038 sub-task b).\n const escapes = detectEscapingFunctionScopedConstructs(node);\n const PREFIX = escapes.yield\n ? \"function* __w__() { \"\n : escapes.await\n ? \"async function __w__() { \"\n : \"function __w__() { \";\n const wrapped = `${PREFIX}${nodeSource} }`;\n const innerStart = PREFIX.length;\n const innerEnd = PREFIX.length + nodeSource.length;\n try {\n return canonicalAstHash(wrapped, { start: innerStart, end: innerEnd });\n } catch {\n // Wrap still failed (e.g. super references or nested generator context).\n // Fall back to full-source + range, which preserves all binding scopes.\n return canonicalAstHash(fullSource, { start, end });\n }\n }\n\n if (LOOP_KINDS.has(kind)) {\n // Loop nodes can contain labeled break/continue targeting an outer label.\n // When hashed standalone those labels have no binding target → TS1364.\n // Try the fragment first (the common case: loop has only unlabeled CF).\n // If that fails, fall back to the full source with a sourceRange, which\n // is always valid TS and lets emitCanonical target only the loop subtree.\n try {\n return canonicalAstHash(nodeSource);\n } catch {\n // Fragment has escaping label reference — use full source + range.\n return canonicalAstHash(fullSource, { start, end });\n }\n }\n\n // DEC-SLICER-FN-SCOPED-CF-001: non-leaf nodes containing escaping\n // return/await/yield whose binding function scope is outside the extracted\n // fragment. Detect and wrap in the appropriate synthetic function flavor.\n const escapes = detectEscapingFunctionScopedConstructs(node);\n if (escapes.return || escapes.await || escapes.yield) {\n const prefix = escapes.yield\n ? \"function* __w__() { \"\n : escapes.await\n ? \"async function __w__() { \"\n : \"function __w__() { \";\n const wrapped = `${prefix}${nodeSource} }`;\n try {\n return canonicalAstHash(wrapped, {\n start: prefix.length,\n end: prefix.length + nodeSource.length,\n });\n } catch {\n // Wrap still failed (e.g. super references, nested generator context).\n // Fall through to full-source fallback below.\n }\n }\n\n // Last resort: try standalone first; if it fails use full source + range.\n // Full source always contains the original binding scopes so it is valid TS;\n // emitCanonical targets only the node's range, keeping the hash context-free.\n //\n // @decision DEC-WI585-SAFE-HASH-FALLBACK-CHAIN-001\n // title: safeCanonicalAstHash guards against TypeError from canonical-ast collectLocalRenames\n // status: decided\n // rationale:\n // When bcryptjs dist/bcrypt.js (JavaScript parsed as TypeScript with ScriptKind.TS)\n // is decomposed, canonicalAstHash(fullSource, { start, end }) throws a TypeError\n // (not a CanonicalAstParseError) because ts-morph@28.0.0's ParameterDeclaration\n // .getNameNode() returns undefined for certain JavaScript function parameter shapes,\n // and canonical-ast.ts::collectLocalRenames() accesses .kind on that undefined node\n // (contracts/src/canonical-ast.ts:238 — forbidden file, cannot modify in this WI).\n // The fix is a third fallback that catches any Error (not just CanonicalAstParseError)\n // from the full-source call and falls back to a fragment-only standalone hash.\n // This is valid because: (a) the fragment hash is deterministic on content; (b) the\n // full-source hash's purpose is to provide binding-scope context — when it fails,\n // the fragment hash is the best available approximation; (c) \"best effort\" is the\n // correct policy for JS-parsed-as-TS content (DEC-WI510-BEST-EFFORT-MODULE-DEGRADATION-001).\n // Consequence: atoms from JS-parsed-as-TS files get a content-based hash without\n // local-rename normalization (since collectLocalRenames also fails). The hash is\n // still stable and deterministic across passes — WI-V2-09 bootstrap invariant holds.\n // Compatible with DEC-SLICER-FN-SCOPED-CF-001: the wrap path is already tried above;\n // this is only the final-final fallback for pathological JS-as-TS inputs.\n try {\n return canonicalAstHash(nodeSource);\n } catch {\n try {\n return canonicalAstHash(fullSource, { start, end });\n } catch {\n // Full-source parse also failed (e.g. ts-morph TypeError on JS-as-TS parameter nodes\n // in collectLocalRenames — see DEC-WI585-SAFE-HASH-FALLBACK-CHAIN-001).\n // Last resort: raw BLAKE3 of nodeSource bytes. No AST normalization (no rename pass),\n // but deterministic on content — satisfies WI-V2-09 byte-identical bootstrap invariant.\n const TEXT_ENC = new TextEncoder();\n return bytesToHex(blake3(TEXT_ENC.encode(nodeSource))) as CanonicalAstHash;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Loop control-flow escape predicate\n// ---------------------------------------------------------------------------\n\n/** SyntaxKinds that are loop iteration statements. */\nconst LOOP_KINDS = new Set([\n SyntaxKind.ForStatement,\n SyntaxKind.WhileStatement,\n SyntaxKind.DoStatement,\n SyntaxKind.ForInStatement,\n SyntaxKind.ForOfStatement,\n]);\n\n/** SyntaxKinds that are loop iteration statements OR switch (for unlabeled break). */\nconst BREAK_BINDING_KINDS = new Set([\n SyntaxKind.ForStatement,\n SyntaxKind.WhileStatement,\n SyntaxKind.DoStatement,\n SyntaxKind.ForInStatement,\n SyntaxKind.ForOfStatement,\n SyntaxKind.SwitchStatement,\n]);\n\n/**\n * Returns true if `blockNode` contains a `continue` or `break` (labeled or\n * unlabeled) whose target binding scope is OUTSIDE `blockNode` — i.e., the\n * statement would be an unbound jump if `blockNode`'s source were parsed\n * standalone via canonicalAstHash().\n *\n * Walk rule:\n * unlabeled continue: binds to the nearest For/While/Do/ForIn/ForOf ancestor.\n * unlabeled break: binds to the nearest For/While/Do/ForIn/ForOf/Switch ancestor.\n * labeled continue/break: binds to the nearest LabeledStatement ancestor\n * whose label text matches.\n *\n * If the binding ancestor starts before blockNode.start OR ends after\n * blockNode.end, the control flow escapes the block.\n *\n * @decision DEC-SLICER-LOOP-CONTROL-FLOW-001 (see file leading comment)\n */\nfunction hasEscapingLoopControlFlow(blockNode: Node): boolean {\n const blockStart = blockNode.getStart();\n const blockEnd = blockNode.getEnd();\n\n let found = false;\n\n blockNode.forEachDescendant((descendant) => {\n if (found) return;\n\n const kind = descendant.getKind();\n\n if (kind === SyntaxKind.ContinueStatement || kind === SyntaxKind.BreakStatement) {\n const isContinue = kind === SyntaxKind.ContinueStatement;\n const bindingKinds = isContinue ? LOOP_KINDS : BREAK_BINDING_KINDS;\n\n // Extract label text if present (e.g. `break outer`)\n const labelNode = (descendant as Node & { getLabel?(): Node | undefined }).getLabel?.();\n const labelText = labelNode?.getText();\n\n // Walk ancestors upward to find the binding scope\n let cursor: Node | undefined = descendant.getParent();\n while (cursor !== undefined) {\n const cursorKind = cursor.getKind();\n\n if (labelText !== undefined) {\n // Labeled jump: look for matching LabeledStatement\n if (cursorKind === SyntaxKind.LabeledStatement) {\n const ls = cursor as Node & { getLabel(): Node };\n if (ls.getLabel().getText() === labelText) {\n // Found the binding scope — check if it's outside blockNode\n const cStart = cursor.getStart();\n const cEnd = cursor.getEnd();\n if (cStart < blockStart || cEnd > blockEnd) {\n found = true;\n }\n return; // stop walking ancestors for this jump\n }\n }\n } else {\n // Unlabeled jump: look for iteration or switch ancestor\n if (bindingKinds.has(cursorKind)) {\n const cStart = cursor.getStart();\n const cEnd = cursor.getEnd();\n if (cStart < blockStart || cEnd > blockEnd) {\n found = true;\n }\n return; // stop walking ancestors for this jump\n }\n }\n\n cursor = cursor.getParent();\n }\n // If no binding ancestor was found at all, the jump is unbound —\n // it definitely escapes the block.\n found = true;\n }\n });\n\n return found;\n}\n\n// ---------------------------------------------------------------------------\n// Function-scoped control-flow escape predicate (DEC-SLICER-FN-SCOPED-CF-001)\n// ---------------------------------------------------------------------------\n\n/**\n * SyntaxKinds that introduce a function boundary — a new binding scope for\n * return, await, and yield. ArrowFunctions bind return implicitly; they bind\n * await/yield only when marked async/generator. All entries here are treated\n * as binding scopes for ReturnStatement regardless.\n */\nconst FUNCTION_KINDS = new Set([\n SyntaxKind.FunctionDeclaration,\n SyntaxKind.FunctionExpression,\n SyntaxKind.ArrowFunction,\n SyntaxKind.MethodDeclaration,\n SyntaxKind.Constructor,\n SyntaxKind.GetAccessor,\n SyntaxKind.SetAccessor,\n]);\n\n/**\n * Returns true if `node` is an async function-like.\n *\n * Uses three layered checks to handle all ts-morph node shapes robustly:\n *\n * 1. ts-morph isAsync() — the canonical check for FunctionDeclaration,\n * FunctionExpression, ArrowFunction, MethodDeclaration. Preferred because it\n * targets the correct node level: for `const f = async (x) => { await x; }`\n * the AsyncKeyword lives on the ArrowFunction, not the VariableDeclaration.\n * Wrapped in try/catch: some ts-morph node types have `isAsync` as a property\n * at the TypeScript type level but throw at runtime (e.g. certain\n * MethodDeclaration shorthand forms on ObjectLiteralExpression). The catch\n * prevents a crash and falls through to the modifier scan.\n *\n * 2. getModifiers() scan — checks the modifier list for AsyncKeyword. Handles\n * async method-shorthand on object literals (`{ async foo() { await x; } }`)\n * where isAsync() may not be implemented but the AsyncKeyword appears as a\n * modifier. Also covers edge cases where isAsync() throws.\n *\n * 3. getFirstChildIfKind(AsyncKeyword) — last resort for any remaining node\n * kind where the async keyword appears as a direct child token but neither\n * isAsync() nor getModifiers() found it.\n *\n * This layered approach fixes the `canonical-ast--await` failure on\n * packages/federation/src/pull.ts (WI-038 sub-task b).\n */\nfunction nodeIsAsync(node: Node): boolean {\n // Layer 1: ts-morph isAsync() — canonical API, wrapped in try/catch.\n const asAsyncable = node as Node & { isAsync?(): boolean };\n if (typeof asAsyncable.isAsync === \"function\") {\n try {\n if (asAsyncable.isAsync()) return true;\n } catch {\n // Some node kinds don't actually implement isAsync() at runtime;\n // fall through to modifier scan.\n }\n }\n // Layer 2: getModifiers() — handles async-method-shorthand on object literals\n // and other cases where isAsync() is absent or throws.\n const withModifiers = node as Node & { getModifiers?(): readonly Node[] };\n const mods = withModifiers.getModifiers?.();\n if (mods !== undefined) {\n for (const m of mods) {\n if (m.getKind() === SyntaxKind.AsyncKeyword) return true;\n }\n }\n // Layer 3: explicit AsyncKeyword as first child token — last resort.\n const asyncKw = (\n node as Node & { getFirstChildIfKind?(k: SyntaxKind): Node | undefined }\n ).getFirstChildIfKind?.(SyntaxKind.AsyncKeyword);\n return asyncKw !== undefined;\n}\n\n/**\n * Returns true if `node` is a generator function (has an asterisk token).\n * Used to distinguish plain/async functions from generators when detecting\n * escaping yield expressions.\n */\nfunction nodeIsGenerator(node: Node): boolean {\n const asterisk = (node as Node & { getAsteriskToken?(): Node | undefined }).getAsteriskToken?.();\n return asterisk !== undefined;\n}\n\n/**\n * Walk ancestors of `descendant` looking for the nearest function-kind node\n * that satisfies the `variant` requirement. Returns true if such an ancestor\n * is found AND lies entirely within [blockStart, blockEnd] (i.e. it is\n * \"inside\" the block being hashed, so the construct is bound locally).\n *\n * @param descendant - The return/await/yield node.\n * @param blockStart - Inclusive start of the extracted block.\n * @param blockEnd - Exclusive end of the extracted block.\n * @param variant - \"async\" requires nodeIsAsync, \"generator\" requires\n * nodeIsGenerator, undefined accepts any function kind.\n */\nfunction hasEnclosingBindingInside(\n descendant: Node,\n blockStart: number,\n blockEnd: number,\n variant: \"async\" | \"generator\" | undefined,\n): boolean {\n let cursor: Node | undefined = descendant.getParent();\n while (cursor !== undefined) {\n if (FUNCTION_KINDS.has(cursor.getKind())) {\n // For async/generator detection, require the relevant marker.\n if (variant === \"async\" && !nodeIsAsync(cursor)) {\n cursor = cursor.getParent();\n continue;\n }\n if (variant === \"generator\" && !nodeIsGenerator(cursor)) {\n cursor = cursor.getParent();\n continue;\n }\n // Found a qualifying function ancestor — is it inside the block?\n return cursor.getStart() >= blockStart && cursor.getEnd() <= blockEnd;\n }\n cursor = cursor.getParent();\n }\n // No qualifying function ancestor found at all → construct is unbound.\n return false;\n}\n\n/**\n * Scan `node`'s descendants for return/await/yield constructs whose binding\n * function scope lies OUTSIDE `node`. Returns a flag object indicating which\n * flavors were found.\n *\n * Rules:\n * - ReturnStatement: binding scope = nearest enclosing function (any kind).\n * - AwaitExpression: binding scope = nearest enclosing async function.\n * - YieldExpression: binding scope = nearest enclosing generator function.\n *\n * If the binding ancestor either does not exist or lies outside the node's\n * range, the construct \"escapes\" — hashing the fragment standalone would fail.\n *\n * @decision DEC-SLICER-FN-SCOPED-CF-001 (see file leading comment)\n */\nfunction detectEscapingFunctionScopedConstructs(node: Node): {\n return: boolean;\n await: boolean;\n yield: boolean;\n} {\n const blockStart = node.getStart();\n const blockEnd = node.getEnd();\n let hasReturn = false;\n let hasAwait = false;\n let hasYield = false;\n\n node.forEachDescendant((d) => {\n // Short-circuit once all three flags are set.\n if (hasReturn && hasAwait && hasYield) return;\n\n const k = d.getKind();\n\n if (!hasReturn && k === SyntaxKind.ReturnStatement) {\n if (!hasEnclosingBindingInside(d, blockStart, blockEnd, undefined)) {\n hasReturn = true;\n }\n }\n if (!hasAwait && k === SyntaxKind.AwaitExpression) {\n if (!hasEnclosingBindingInside(d, blockStart, blockEnd, \"async\")) {\n hasAwait = true;\n }\n }\n if (!hasYield && k === SyntaxKind.YieldExpression) {\n if (!hasEnclosingBindingInside(d, blockStart, blockEnd, \"generator\")) {\n hasYield = true;\n }\n }\n });\n\n return { return: hasReturn, await: hasAwait, yield: hasYield };\n}\n\n// ---------------------------------------------------------------------------\n// Callee unwrapper helper (DEC-SLICER-CALLEE-OBJ-LITERAL-001)\n// ---------------------------------------------------------------------------\n\n/**\n * Unwrap the callee of a CallExpression to find the first node that\n * `decomposableChildrenOf` can usefully recurse into. Returns `undefined`\n * when the callee chain contains nothing worth descending into.\n *\n * Handles three shapes in priority order:\n *\n * 1. **IIFE** — `(() => { ... })()` or `(function() { ... })()`:\n * callee is `ParenthesizedExpression(ArrowFunction | FunctionExpression)`.\n * We unwrap the parentheses and return the inner function-like.\n *\n * 2. **Method chain** — `a.b(fn).c()`:\n * callee is `PropertyAccessExpression(expression=CallExpression(...))`.\n * We return the inner CallExpression so that `decomposableChildrenOf`\n * recurses into it and finds the function-like argument (the comparator,\n * predicate, etc.) one level down.\n *\n * 3. **Bare function-like callee** — rare but possible:\n * callee is `ArrowFunction` or `FunctionExpression` directly.\n * Return it for direct decomposition.\n *\n * Other callee kinds (Identifier, PropertyAccessExpression without a\n * CallExpression receiver, etc.) return `undefined` — they have no\n * decomposable internal structure.\n *\n * @decision DEC-SLICER-CALLEE-OBJ-LITERAL-001 (see CallExpression branch below)\n */\nfunction unwrapCalleeToDecomposable(callee: Node): Node | undefined {\n const ck = callee.getKind();\n\n // Shape 1: ParenthesizedExpression — unwrap and check the inner expression.\n if (ck === SyntaxKind.ParenthesizedExpression) {\n const inner = (callee as Node & { getExpression(): Node }).getExpression();\n const ik = inner.getKind();\n if (ik === SyntaxKind.ArrowFunction || ik === SyntaxKind.FunctionExpression) {\n return inner; // IIFE: the real function-like\n }\n // Nested parens or other — don't recurse further; unusual shape\n return undefined;\n }\n\n // Shape 2: PropertyAccessExpression — follow the expression (receiver) to\n // find an inner CallExpression in a method chain.\n if (ck === SyntaxKind.PropertyAccessExpression) {\n const expr = (callee as Node & { getExpression(): Node }).getExpression();\n if (expr.getKind() === SyntaxKind.CallExpression) {\n return expr; // inner CallExpression in the chain\n }\n return undefined;\n }\n\n // Shape 3: Bare function-like callee (unusual but defensively handled).\n if (ck === SyntaxKind.ArrowFunction || ck === SyntaxKind.FunctionExpression) {\n return callee;\n }\n\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// decomposableChildrenOf\n// ---------------------------------------------------------------------------\n\n/**\n * Return the AST nodes the recursion should descend into when `node` is\n * non-atomic. This is the structural decomposition policy:\n *\n * - SourceFile → its top-level statements.\n * - Block → its statements.\n * - FunctionDeclaration / FunctionExpression / ArrowFunction /\n * MethodDeclaration / Constructor / GetAccessor / SetAccessor →\n * if the body is a Block, its statements; else [] (expression body).\n * - IfStatement → [thenStatement, elseStatement?].\n * - ForStatement / WhileStatement / DoStatement → [statement (body)],\n * unless the body Block has escaping continue/break; then [] (loop is atom).\n * - ForInStatement / ForOfStatement → [statement (body)],\n * unless the body Block has escaping continue/break; then [] (loop is atom).\n * - SwitchStatement → all statements from all CaseClauses concatenated.\n * - TryStatement → [tryBlock, catchBlock?, finallyBlock?].\n * - ConditionalExpression (ternary) → [] (expression-level; should be atomic).\n * - Default → [] (no further decomposition; DidNotReachAtomError fires if\n * isAtom classified this node as non-atomic).\n */\nfunction decomposableChildrenOf(node: Node): readonly Node[] {\n const kind = node.getKind();\n\n // SourceFile\n if (kind === SyntaxKind.SourceFile) {\n return (node as Node & { getStatements(): Node[] }).getStatements();\n }\n\n // Block\n if (kind === SyntaxKind.Block) {\n return (node as Node & { getStatements(): Node[] }).getStatements();\n }\n\n // Function-like nodes: descend into body statements\n if (\n kind === SyntaxKind.FunctionDeclaration ||\n kind === SyntaxKind.FunctionExpression ||\n kind === SyntaxKind.ArrowFunction ||\n kind === SyntaxKind.MethodDeclaration ||\n kind === SyntaxKind.Constructor ||\n kind === SyntaxKind.GetAccessor ||\n kind === SyntaxKind.SetAccessor\n ) {\n const fnNode = node as Node & { getBody?(): Node | undefined };\n const body = fnNode.getBody?.();\n if (body !== undefined && body.getKind() === SyntaxKind.Block) {\n return (body as Node & { getStatements(): Node[] }).getStatements();\n }\n // Expression-body: if the body is itself a function-like (arrow-returns-arrow\n // pattern), descend into it so the inner function can be atomized.\n // (DEC-SLICER-ARROW-RETURNS-ARROW-001)\n if (\n body !== undefined &&\n (body.getKind() === SyntaxKind.ArrowFunction ||\n body.getKind() === SyntaxKind.FunctionExpression)\n ) {\n return [body];\n }\n // @decision DEC-SHAVE-PRIVATE-CLASS-FIELD-001\n // title: Expression-body ArrowFunction/FunctionExpression descends into any expression body\n // status: accepted\n // rationale:\n // When a non-Block expression body (e.g. a nested ConditionalExpression chain)\n // is classified as non-atomic by isAtom(), the previous return [] caused\n // DidNotReachAtomError. Root cause identified via §P6 probe (WI-666):\n // Node at [1301,1558) (kind=ArrowFunction) is not atomic and has no decomposable children\n // Source: `const getUintArray = (max) => !isPosInt(max) ? null : max <= 256 ? ... : null`\n // The ArrowFunction has 5 ConditionalExpression nodes (5 CF boundaries > maxCF=1) so\n // isAtom() returns false. But decomposableChildrenOf returned [] because the body is not\n // a Block and not a function-like, preventing descent into the ConditionalExpression chain.\n // Fix: for any expression body that is defined and not a Block, return [body] so the\n // recursion descends into it. The existing ConditionalExpression branch of\n // decomposableChildrenOf (lines 1108-1114) then handles the ternary chain correctly.\n // This is a strictly additive extension — existing passing tests are unaffected because:\n // (a) Block-body paths are unchanged (handled by the getStatements() branch above).\n // (b) Function-like expression bodies are already returned by the branch above.\n // (c) For expression bodies that are already atomic (no CF boundaries, not in registry),\n // isAtom() returns true before decomposableChildrenOf is even consulted,\n // so this branch is only reached when the body is genuinely non-atomic.\n // alternatives:\n // A. Add ConditionalExpression specifically to this branch -- rejected; overly narrow;\n // other complex expression types (BinaryExpression chains, etc.) could exhibit the\n // same failure; the general solution is strictly cleaner.\n // B. Classify all expression-body arrow functions as atomic atoms -- rejected;\n // a genuinely large ConditionalExpression chain should decompose to its branches,\n // not become one monolithic atom. Quality of decomposition matters.\n // C. Extend the ConditionalExpression branch to also handle wrapping ArrowFunction --\n // rejected; that would require changing ConditionalExpression to also handle its\n // ArrowFunction parent, violating the node-local responsibility of each branch.\n // consequences:\n // - lru-cache dist/esm/index.js decomposes: moduleCount >= 3, stubCount=0.\n // - combinedScore >= 0.70 gate can fire (post engine fix).\n // - Any future package with expression-body arrow functions containing complex\n // ternary chains or BinaryExpression nesting will also decompose correctly.\n // - Compatible with WI-V2-09 byte-identical bootstrap: descent is deterministic.\n // - DEC-WI510-S10-PRIVATE-CLASS-FIELD-ENGINE-GAP-001 closed.\n // closes #666\n if (body !== undefined) {\n return [body];\n }\n return [];\n }\n\n // IfStatement: then-branch and optional else-branch\n if (kind === SyntaxKind.IfStatement) {\n const ifNode = node as Node & {\n getThenStatement(): Node;\n getElseStatement(): Node | undefined;\n };\n const branches: Node[] = [ifNode.getThenStatement()];\n const elseStmt = ifNode.getElseStatement();\n if (elseStmt !== undefined) {\n branches.push(elseStmt);\n }\n return branches;\n }\n\n // ForStatement, WhileStatement, DoStatement: the loop body —\n // but only when the body block has no escaping continue/break.\n // If it does, return [] so recurse() emits an AtomLeaf for the loop.\n // (DEC-SLICER-LOOP-CONTROL-FLOW-001)\n if (\n kind === SyntaxKind.ForStatement ||\n kind === SyntaxKind.WhileStatement ||\n kind === SyntaxKind.DoStatement\n ) {\n const loopNode = node as Node & { getStatement(): Node };\n const body = loopNode.getStatement();\n if (body.getKind() === SyntaxKind.Block && hasEscapingLoopControlFlow(body)) {\n return []; // loop is the atom; do not descend into body\n }\n return [body];\n }\n\n // ForInStatement, ForOfStatement: the loop body —\n // same escaping-CF check. (DEC-SLICER-LOOP-CONTROL-FLOW-001)\n if (kind === SyntaxKind.ForInStatement || kind === SyntaxKind.ForOfStatement) {\n const forInOf = node as Node & { getStatement(): Node };\n const body = forInOf.getStatement();\n if (body.getKind() === SyntaxKind.Block && hasEscapingLoopControlFlow(body)) {\n return []; // loop is the atom; do not descend into body\n }\n return [body];\n }\n\n // LabeledStatement: forward to the labeled body statement.\n // Without this, labeled loops (e.g. `outer: for (...)`) produce a\n // LabeledStatement node that has no decomposable children, throwing\n // DidNotReachAtomError even when the loop body is valid.\n if (kind === SyntaxKind.LabeledStatement) {\n const ls = node as Node & { getStatement(): Node };\n return [ls.getStatement()];\n }\n\n // SwitchStatement: flatten all case clause statements\n if (kind === SyntaxKind.SwitchStatement) {\n const sw = node as Node & {\n getCaseBlock(): Node & {\n getClauses(): Array<Node & { getStatements(): Node[] }>;\n };\n };\n const clauses = sw.getCaseBlock().getClauses();\n const result: Node[] = [];\n for (const clause of clauses) {\n result.push(...clause.getStatements());\n }\n return result;\n }\n\n // TryStatement: try block, catch block, finally block\n if (kind === SyntaxKind.TryStatement) {\n const tryNode = node as Node & {\n getTryBlock(): Node;\n getCatchClause(): (Node & { getBlock(): Node }) | undefined;\n getFinallyBlock(): Node | undefined;\n };\n const result: Node[] = [tryNode.getTryBlock()];\n const catchClause = tryNode.getCatchClause();\n if (catchClause !== undefined) {\n result.push(catchClause.getBlock());\n }\n const finallyBlock = tryNode.getFinallyBlock();\n if (finallyBlock !== undefined) {\n result.push(finallyBlock);\n }\n return result;\n }\n\n // ClassDeclaration / ClassExpression: descend into methods, accessors,\n // constructor, static blocks, and property initializers.\n // (DEC-SLICER-CHILDREN-CLASS-EXPR-VAR-001)\n if (kind === SyntaxKind.ClassDeclaration || kind === SyntaxKind.ClassExpression) {\n const cls = node as Node & { getMembers(): readonly Node[] };\n const result: Node[] = [];\n for (const member of cls.getMembers()) {\n const mk = member.getKind();\n if (\n mk === SyntaxKind.MethodDeclaration ||\n mk === SyntaxKind.Constructor ||\n mk === SyntaxKind.GetAccessor ||\n mk === SyntaxKind.SetAccessor ||\n mk === SyntaxKind.ClassStaticBlockDeclaration\n ) {\n result.push(member);\n }\n // Property declarations with initializers decompose to the initializer.\n // Property declarations without initializers (interface-shape fields) skipped.\n if (mk === SyntaxKind.PropertyDeclaration) {\n const prop = member as Node & { getInitializer?(): Node | undefined };\n const init = prop.getInitializer?.();\n if (init !== undefined) result.push(init);\n }\n }\n return result;\n }\n\n // ParenthesizedExpression: a pure syntactic wrapper `(expr)` — transparent\n // for decomposition purposes. Descend into the inner expression.\n //\n // @decision DEC-WI585-PARENTHESIZED-EXPRESSION-UNWRAP-001\n // title: ParenthesizedExpression is a transparent wrapper in decomposableChildrenOf\n // status: decided\n // rationale:\n // UMD IIFE patterns (and other grouped expressions) produce a top-level\n // ExpressionStatement whose expression is a ParenthesizedExpression wrapping\n // the real CallExpression. For example, bcryptjs dist/bcrypt.js:\n // (function(global, factory) { ... }(this, function() { var bcrypt = {}; ... }))\n // The existing ExpressionStatement handler descends into the expression, which\n // is the ParenthesizedExpression. Without this branch, decomposableChildrenOf\n // returns [] for ParenthesizedExpression, causing DidNotReachAtomError (WI-585).\n // Fix: unwrap ParenthesizedExpression to its inner expression so the recursion\n // continues into the CallExpression, which is already handled. This is a\n // strictly additive, transparent-passthrough extension — identically to how\n // LabeledStatement was added (same rationale: pure structural wrapper with no\n // decomposable behavior of its own). No existing passing tests are affected.\n // alternatives:\n // A. Add IIFE special-casing to module-graph.ts::shavePackage — rejected;\n // that would be a parallel-authority violation per DEC-WI585-NO-EXTRACT-IIFE-SPECIAL-CASE-001.\n // B. Handle ParenthesizedExpression in unwrapCalleeToDecomposable — rejected;\n // that helper is called only from the CallExpression branch, not the\n // ExpressionStatement → ParenthesizedExpression path. Adding it there would\n // silently skip the outer paren-wrapped expression at the statement level.\n // C. Classify ParenthesizedExpression as always-atomic — rejected;\n // the inner expression may be a large non-atomic CallExpression (the IIFE body);\n // forcing it to atom would prevent valid sub-decomposition.\n // consequences:\n // - bcryptjs dist/bcrypt.js (UMD IIFE) decomposes; externalSpecifiers includes\n // 'crypto'; corpus rows get non-null expectedAtom (DEC-WI585-CORPUS-MERKLE-ROOT-CAPTURE-001).\n // - Any other package using the top-level `(expr)` shape also decomposes correctly.\n // - Recursion depth increases by 1 for paren-wrapped expressions; still within\n // DEFAULT_MAX_DEPTH=24 for any real-world code (the paren adds at most 1 level).\n // - Compatible with WI-V2-09 byte-identical bootstrap: deterministic single-child descent.\n if (kind === SyntaxKind.ParenthesizedExpression) {\n const paren = node as Node & { getExpression(): Node };\n return [paren.getExpression()];\n }\n\n // ExpressionStatement: the statement is a thin wrapper — the expression is\n // the carrier of decomposable behavior. Descend into [expression].\n // (DEC-SLICER-CHILDREN-CLASS-EXPR-VAR-001)\n if (kind === SyntaxKind.ExpressionStatement) {\n const stmt = node as Node & { getExpression(): Node };\n return [stmt.getExpression()];\n }\n\n // VariableStatement: `const x = expr1, y = expr2;` — decompose to\n // initializer expressions. Declarations without initializers (`let x;`) skipped.\n // (DEC-SLICER-CHILDREN-CLASS-EXPR-VAR-001)\n if (kind === SyntaxKind.VariableStatement) {\n const stmt = node as Node & {\n getDeclarationList(): Node & {\n getDeclarations(): readonly (Node & { getInitializer?(): Node | undefined })[];\n };\n };\n const decls = stmt.getDeclarationList().getDeclarations();\n const result: Node[] = [];\n for (const d of decls) {\n const init = d.getInitializer?.();\n if (init !== undefined) result.push(init);\n }\n return result;\n }\n\n // CallExpression: descend into (a) the callee when it is or wraps a\n // function-like or another CallExpression, and (b) ObjectLiteralExpression\n // arguments that may carry conditional spreads.\n //\n // @decision DEC-SLICER-CALLEE-OBJ-LITERAL-001\n // title: Extend CallExpression descent to callee and ObjectLiteralExpression args\n // status: decided\n // rationale:\n // Three previously-unhandled AST shapes produced DidNotReachAtomError on\n // real yakcc source (issue #350):\n //\n // 1. IIFE shape: `(() => { ... })()` — the ArrowFunction is the CALLEE\n // (inside a ParenthesizedExpression), not an argument. The old branch\n // iterated arguments only, found none that were function-like, and\n // returned [].\n //\n // 2. Method-chain shape: `arr.filter(fn).sort(cmp).slice(0, n)` — the\n // outermost CallExpression's callee is a PropertyAccessExpression whose\n // receiver is itself a CallExpression (.sort(cmp)). The sort's argument\n // is an ArrowFunction containing the decomposable behaviour. Without\n // callee descent the slicer saw only `0` and `n` as args → returned [].\n //\n // 3. ObjectLiteralExpression arg with conditional spreads:\n // `fn({ ...cond ? {a} : {} })` — the single arg is an\n // ObjectLiteralExpression. The existing ObjectLiteralExpression handler\n // already exposes PropertyAssignment initializers; adding the OLE to the\n // descent set lets the slicer reach ConditionalExpression spreads.\n //\n // The extension is strictly additive: previously-passing files are\n // unaffected. New branches only fire on AST shapes that previously\n // returned []. (#350, files 1-4)\n // alternatives:\n // A (refactor call sites): forces contributors to memorise anti-patterns;\n // violates the invariant \"every shaveable file shaves\".\n // C (expected-failures.json): documents fixable gaps; drifts atom registry.\n // consequences:\n // - IIFEs, method chains, and conditional-spread call shapes now decompose.\n // - Atom granularity unchanged for previously-passing files.\n // - Compatible with WI-V2-09 byte-identical bootstrap (deterministic descent).\n // (DEC-SLICER-CHILDREN-CLASS-EXPR-VAR-001 prior art)\n if (kind === SyntaxKind.CallExpression) {\n const call = node as Node & {\n getExpression(): Node;\n getArguments(): readonly Node[];\n };\n const result: Node[] = [];\n\n // ---- Callee descent ----\n // Unwrap the callee through ParenthesizedExpression to find the real\n // function-like or nested CallExpression, then include it for further\n // decomposition (IIFE and method-chain shapes, files 2 and 4 in #350).\n const calleeDescendant = unwrapCalleeToDecomposable(call.getExpression());\n if (calleeDescendant !== undefined) {\n result.push(calleeDescendant);\n }\n\n // ---- Argument descent ----\n for (const arg of call.getArguments()) {\n const ak = arg.getKind();\n if (ak === SyntaxKind.ArrowFunction || ak === SyntaxKind.FunctionExpression) {\n // Function-like arg: directly decomposable (unchanged from prior logic).\n result.push(arg);\n } else if (ak === SyntaxKind.ObjectLiteralExpression) {\n // ObjectLiteralExpression arg: delegate to the OLE handler by including\n // it here. decomposableChildrenOf(OLE) exposes PropertyAssignment\n // initializers, SpreadAssignments, and method members, reaching the\n // ConditionalExpressions buried in `fn({...cond ? {a} : {}})`.\n // (files 1 and 3 in #350)\n result.push(arg);\n }\n }\n return result;\n }\n\n // NewExpression: `new Constructor(fn)` — descend into function-like arguments\n // (ArrowFunction, FunctionExpression). This handles `new Promise<void>(fn)`\n // shapes where fn is the resolver callback — a non-leaf ArrowFunction that the\n // slicer should be able to descend into. Other constructor args (literals,\n // identifiers) are not further decomposable.\n // (DEC-SLICER-CHILDREN-EXPR-LEVEL-001)\n if (kind === SyntaxKind.NewExpression) {\n const ne = node as Node & { getArguments(): readonly Node[] };\n const result: Node[] = [];\n for (const arg of ne.getArguments()) {\n const ak = arg.getKind();\n if (ak === SyntaxKind.ArrowFunction || ak === SyntaxKind.FunctionExpression) {\n result.push(arg);\n }\n }\n return result;\n }\n\n // ConditionalExpression: `cond ? then : else` — decompose to [condition, whenTrue, whenFalse].\n // Addresses ternary-shaped did-not-reach-atom failures from WI-037 audit.\n // (DEC-SLICER-CHILDREN-EXPR-LEVEL-001)\n if (kind === SyntaxKind.ConditionalExpression) {\n const ce = node as Node & {\n getCondition(): Node;\n getWhenTrue(): Node;\n getWhenFalse(): Node;\n };\n return [ce.getCondition(), ce.getWhenTrue(), ce.getWhenFalse()];\n }\n\n // BinaryExpression: `left op right` — decompose to [left, right].\n // Addresses binary-expression did-not-reach-atom failures from WI-037 audit.\n // (DEC-SLICER-CHILDREN-EXPR-LEVEL-001)\n if (kind === SyntaxKind.BinaryExpression) {\n const be = node as Node & {\n getLeft(): Node;\n getRight(): Node;\n };\n return [be.getLeft(), be.getRight()];\n }\n\n // ReturnStatement: descend into the wrapped expression if present.\n // A bare `return;` (no expression) falls through to the leaf wrapper in\n // safeCanonicalAstHash. This handles `return new Promise<void>(fn)` shapes\n // in federation/serve.ts close() — the slicer descends into the promise arg\n // rather than treating the entire return as an unsliceable atom.\n // (DEC-SLICER-CHILDREN-EXPR-LEVEL-001)\n if (kind === SyntaxKind.ReturnStatement) {\n const rs = node as Node & { getExpression?(): Node | undefined };\n const expr = rs.getExpression?.();\n return expr !== undefined ? [expr] : [];\n }\n\n // ObjectLiteralExpression: descend into members to find decomposable sub-nodes.\n // Handles two shapes:\n // 1. `return { server, url, close() { ... } }` — close() is a MethodDeclaration\n // with a decomposable body (federation/serve.ts).\n // 2. `parseArgs({ options: { registry: { type: \"string\" }, ... } })` — nested\n // ObjectLiteralExpression inside a CallExpression arg; the inner objects\n // are plain data and should resolve as atoms.\n //\n // Policy: expose MethodDeclaration/GetAccessor/SetAccessor (always function-like),\n // ALL PropertyAssignment initializers, AND SpreadAssignment expressions.\n //\n // SpreadAssignment handles the conditional-spread shape `...(cond ? {a} : {})`:\n // the `SpreadAssignment` node wraps a `ConditionalExpression` that contains the\n // CF boundaries causing the OLE to be non-atomic. Without this branch, the OLE\n // handler found no decomposable children and threw DidNotReachAtomError even\n // though the SpreadAssignment's ConditionalExpression is reachable.\n // This is the completing fix for DEC-SLICER-CALLEE-OBJ-LITERAL-001 gap 3:\n // adding OLE to the CallExpression arg descent (above) gets the slicer INTO the\n // OLE, and this SpreadAssignment branch gets it through the OLE to the ternary.\n // (#350 files 1 and 3)\n //\n // ShorthandPropertyAssignment entries (simple identifiers like `toolName`) do not\n // carry initializers and classify as atoms via isAtom() → terminate naturally.\n // (DEC-SLICER-CHILDREN-EXPR-LEVEL-001)\n if (kind === SyntaxKind.ObjectLiteralExpression) {\n const obj = node as Node & { getProperties(): readonly Node[] };\n const result: Node[] = [];\n for (const prop of obj.getProperties()) {\n const pk = prop.getKind();\n if (\n pk === SyntaxKind.MethodDeclaration ||\n pk === SyntaxKind.GetAccessor ||\n pk === SyntaxKind.SetAccessor\n ) {\n result.push(prop);\n } else if (pk === SyntaxKind.PropertyAssignment) {\n const pa = prop as Node & { getInitializer?(): Node | undefined };\n const init = pa.getInitializer?.();\n if (init !== undefined) {\n result.push(init);\n }\n } else if (pk === SyntaxKind.SpreadAssignment) {\n // `...(expression)` — the expression carries the CF boundaries (commonly\n // a ConditionalExpression). Include the expression for further decomposition.\n const sa = prop as Node & { getExpression?(): Node | undefined };\n const expr = sa.getExpression?.();\n if (expr !== undefined) {\n result.push(expr);\n }\n }\n }\n return result;\n }\n\n // All other nodes (expressions, type nodes, etc.) — not decomposable.\n return [];\n}\n\n// ---------------------------------------------------------------------------\n// Supplemental registry check\n// ---------------------------------------------------------------------------\n\n/**\n * Returns true when any decomposable child of `node` matches the registry by\n * canonicalAstHash. This supplements isAtom()'s criterion 2 to handle the\n * edge case where isAtom()'s self-recognition guard fires incorrectly:\n * when a SourceFile (or Block) has a single child whose source range equals\n * the parent's range, isAtom() skips the registry query for that child via\n * its `stmtStart === nodeStart && stmtEnd === nodeEnd` guard. This guard was\n * designed to avoid self-recognition but misfires when the single child\n * spans the same character range as the container.\n *\n * This check is only invoked when isAtom() returns {isAtom: true} AND the\n * node has decomposable children — the scenario where the guard can misfire.\n *\n * @decision DEC-RECURSION-005-SUPPLEMENT\n * title: Supplemental registry check for same-range container nodes\n * status: decided\n * rationale: atom-test.ts's self-recognition guard is in a forbidden file;\n * this local supplement in recursion.ts corrects the misfiring case without\n * modifying the atom-test predicate.\n */\nasync function childMatchesRegistry(\n node: Node,\n source: string,\n registry: Pick<ShaveRegistryView, \"findByCanonicalAstHash\">,\n): Promise<boolean> {\n const children = decomposableChildrenOf(node);\n if (children.length === 0) return false;\n\n for (const child of children) {\n const childStart = child.getStart();\n const childEnd = child.getEnd();\n const childSource = source.slice(childStart, childEnd);\n const childHash = safeCanonicalAstHash(child, childSource, source, childStart, childEnd);\n const matches = await registry.findByCanonicalAstHash?.(childHash);\n if (matches !== undefined && matches.length > 0) {\n return true;\n }\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Public API: decompose()\n// ---------------------------------------------------------------------------\n\n/**\n * Decompose a TypeScript source string into a RecursionTree by walking the\n * AST top-down, classifying each node via isAtom(), and recursing into\n * non-atomic nodes' decomposable children.\n *\n * The recursion bottoms out at atomic nodes (AtomLeaf) or throws when it\n * encounters a non-atomic node with no decomposable children\n * (DidNotReachAtomError).\n *\n * @param source - Full TypeScript source text to decompose.\n * @param registry - Registry view used by isAtom() for known-primitive checks.\n * @param options - Tuning: maxDepth (default 8), maxControlFlowBoundaries (default 1).\n *\n * @throws {RecursionDepthExceededError} When depth exceeds options.maxDepth.\n * @throws {DidNotReachAtomError} When a non-atomic node has no children.\n */\nexport async function decompose(\n source: string,\n registry: Pick<ShaveRegistryView, \"findByCanonicalAstHash\">,\n options?: RecursionOptions,\n): Promise<RecursionTree> {\n const project = new Project({\n useInMemoryFileSystem: true,\n compilerOptions: { allowJs: false, noEmit: true },\n });\n const file = project.createSourceFile(\"anonymous.ts\", source, {\n scriptKind: ScriptKind.TS,\n });\n\n const maxDepth = options?.maxDepth ?? DEFAULT_MAX_DEPTH;\n\n // P0 #549 STEF fast-path: when the SourceFile IS a single typed exported\n // function with JSDoc (and only permitted noise: imports/type aliases/\n // interfaces), preserve the whole file as one atom instead of fragmenting\n // into statement-level expressions. This is a refinement of\n // DEC-V2-GLUE-AWARE-SHAVE-001 — STEF is the degenerate case where\n // fragmentation strictly destroys signal; all non-STEF files continue\n // through the glue-aware fragmentation path unchanged.\n // See plans/wi-fix-549-shave-fragmentation.md §3.3.\n if (matchesStefPredicate(file)) {\n // getFullStart() is always 0 for a SourceFile — it includes leading trivia\n // (the JSDoc comment bytes). getStart() skips trivia and would exclude the\n // JSDoc, producing a non-zero start offset that makes the range incomplete.\n const start = file.getFullStart();\n const end = file.getEnd();\n const nodeSource = source.slice(start, end);\n const leaf: AtomLeaf = {\n kind: \"atom\",\n sourceRange: { start, end },\n source: nodeSource,\n canonicalAstHash: safeCanonicalAstHash(file, nodeSource, source, start, end),\n atomTest: {\n isAtom: true,\n reason: \"single-typed-exported-function\",\n controlFlowBoundaryCount: 0,\n },\n };\n return { root: leaf, leafCount: 1, maxDepth: 0 };\n }\n\n let leafCount = 0;\n let maxObservedDepth = 0;\n\n async function recurse(node: Node, depth: number): Promise<RecursionNode> {\n if (depth > maxDepth) {\n throw new RecursionDepthExceededError(depth, maxDepth);\n }\n if (depth > maxObservedDepth) {\n maxObservedDepth = depth;\n }\n\n const start = node.getStart();\n const end = node.getEnd();\n const nodeSource = source.slice(start, end);\n\n // Hash is computed lazily — only when we know the node will become a\n // leaf or branch in the output tree. safeCanonicalAstHash wraps\n // context-dependent statements (return/continue/break) in a synthetic\n // function body so they can be parsed standalone without TS1108/1313/1314.\n const computeHash = () => safeCanonicalAstHash(node, nodeSource, source, start, end);\n\n // Pre-flight: a loop whose body has escaping continue/break IS the atom.\n // Short-circuit before isAtom() classification to avoid attempting to hash\n // the body block standalone (which would raise TS1313/1314 in canonical-ast.ts).\n // (DEC-SLICER-LOOP-CONTROL-FLOW-001)\n if (LOOP_KINDS.has(node.getKind())) {\n const loopNode = node as Node & { getStatement(): Node };\n const body = loopNode.getStatement();\n if (body.getKind() === SyntaxKind.Block && hasEscapingLoopControlFlow(body)) {\n leafCount += 1;\n const cfCount = options?.maxControlFlowBoundaries ?? 1;\n const leaf: AtomLeaf = {\n kind: \"atom\",\n sourceRange: { start, end },\n source: nodeSource,\n canonicalAstHash: computeHash(),\n atomTest: {\n isAtom: true,\n reason: \"loop-with-escaping-cf\",\n controlFlowBoundaryCount: cfCount,\n },\n };\n return leaf;\n }\n }\n\n const atomResult: AtomTestResult = await isAtom(node, source, registry, options);\n\n if (atomResult.isAtom) {\n // Supplemental check: isAtom()'s self-recognition guard can misfire when\n // a container node (e.g. SourceFile) has a single child whose source\n // range equals the parent's range. In that case the guard skips the\n // registry query for the child and returns isAtom=true incorrectly.\n // We compensate by querying the registry for each decomposable child\n // directly. If any child matches, we fall through to branch handling.\n const childMatched = await childMatchesRegistry(node, source, registry);\n if (!childMatched) {\n leafCount += 1;\n const leaf: AtomLeaf = {\n kind: \"atom\",\n sourceRange: { start, end },\n source: nodeSource,\n canonicalAstHash: computeHash(),\n atomTest: atomResult,\n };\n return leaf;\n }\n // A child matched — fall through to branch handling below.\n }\n\n const children = decomposableChildrenOf(node);\n\n if (children.length === 0) {\n // @decision DEC-V2-SHAVE-CALLEXPRESSION-GLUE-001\n // status: decided\n // rationale:\n // Per DEC-V2-GLUE-AWARE-SHAVE-001 (the glue-aware framing), constructs\n // that don't decompose into atomic units can be verbatim-preserved as\n // forced AtomLeaf entries (carrying atomTest.isAtom=false). Downstream\n // atom-persist / universalize pipelines route these as glue rather than\n // failing the entire file shave. This unblocks 4 CallExpression files\n // reported in issue #399 (bootstrap/report.json):\n // - packages/hooks-base/src/index.ts [13744,14068)\n // - packages/hooks-base/src/telemetry.ts [4975,5389)\n // - packages/hooks-claude-code/src/index.ts [11723,11956)\n // - packages/registry/src/discovery-eval-helpers.ts [26144,26424)\n // Rejected alternatives (per #399 body):\n // 1. Refactor each CallExpression to be decomposable (source-level edits, risk subtle behavior change)\n // 2. Extend decomposableChildrenOf() (unknown surface area, risks regressing currently-working files)\n if (node.getKind() === SyntaxKind.CallExpression) {\n leafCount += 1;\n const leaf: AtomLeaf = {\n kind: \"atom\",\n sourceRange: { start, end },\n source: nodeSource,\n canonicalAstHash: computeHash(),\n atomTest: atomResult,\n };\n return leaf;\n }\n throw new DidNotReachAtomError(\n `Node at [${start},${end}) (kind=${node.getKindName()}) is not atomic and has no decomposable children`,\n {\n kind: node.getKind(),\n source: nodeSource,\n range: { start, end },\n },\n );\n }\n\n const recursedChildren: RecursionNode[] = [];\n for (const child of children) {\n recursedChildren.push(await recurse(child, depth + 1));\n }\n\n const branch: BranchNode = {\n kind: \"branch\",\n sourceRange: { start, end },\n source: nodeSource,\n canonicalAstHash: computeHash(),\n atomTest: atomResult,\n children: recursedChildren,\n };\n return branch;\n }\n\n const root = await recurse(file, 0);\n\n return { root, leafCount, maxDepth: maxObservedDepth };\n}\n","// SPDX-License-Identifier: MIT\n/**\n * @decision DEC-SHAVE-WHOLE-FUNCTION-PRESERVATION-001\n * title: STEF (Single Typed Exported Function) predicate for decompose() fast-path\n * status: decided\n * rationale:\n * `decompose()` fragments a SourceFile that contains exactly one exported\n * function with multiple control-flow boundaries into statement-level atoms,\n * each producing an empty-signature SpecYak (`inputs:[]`, `outputs:[]`,\n * `behavior:\"source fragment (N statements, M bytes)\"`). This destroys the\n * round-trip retrieval signal that Step 9 of v0-release-smoke depends on\n * (root cause of #444/#523/#549).\n *\n * For a SourceFile that IS a single typed exported function (STEF shape), the\n * *maximal* shaveable subgraph is the whole file — there is no glue to carve\n * away. Preserving it as one atom lets `staticExtract` / `pickPrimaryDeclaration`\n * find the exported function at priority 2, producing a rich SpecYak with\n * non-empty inputs/outputs.\n *\n * This is a **refinement, not a reversal** of DEC-V2-GLUE-AWARE-SHAVE-001:\n * STEF is the degenerate case where fragmentation strictly destroys signal.\n * All non-STEF files continue through the glue-aware fragmentation path\n * unchanged.\n *\n * Companion to DEC-EMBED-QUERY-ENRICH-HELPER-001: that decision closed the\n * query-side field-coverage asymmetry; this decision closes the store-side\n * fragmentation root cause. Together they constitute the complete fix for the\n * v0 round-trip retrieval failure.\n *\n * STEF predicate (§3.3 of plans/wi-fix-549-shave-fragmentation.md):\n * A SourceFile matches STEF when ALL of:\n * 1. Exactly one top-level exported function-like declaration (FunctionDeclaration\n * OR VariableStatement with a single arrow/function-expression initializer).\n * 2. All parameters have explicit TS type annotations (no implicit any); zero\n * parameters is permitted.\n * 3. Explicit return type annotation.\n * 4. Non-empty JSDoc block (/** … *‌/) on the function or enclosing VariableStatement.\n * 5. All other top-level statements restricted to: ImportDeclaration,\n * ExportDeclaration (re-export), TypeAliasDeclaration, InterfaceDeclaration.\n * Any other form (second function, class, bare const, expression statement)\n * causes STEF to return false.\n * 6. The function body is non-empty (Block with at least one statement).\n *\n * Authority invariant:\n * - STEF is a SourceFile-level predicate. It does NOT modify isAtom() (per-node).\n * - pickPrimaryDeclaration and extractSignatureFromNode are not modified; they\n * operate on the full source once STEF returns a single-leaf tree.\n * - This module is the single canonical authority for the STEF predicate.\n * Callers must not re-implement it.\n */\n\nimport { type Node, type SourceFile, SyntaxKind } from \"ts-morph\";\n\n// ---------------------------------------------------------------------------\n// Allowed noise kinds at the top level (per plan §3.3)\n// ---------------------------------------------------------------------------\n\n/**\n * Top-level SyntaxKinds that are \"permitted noise\" — non-executing forms that\n * carry no shaveable behavior and do not disqualify a SourceFile from STEF.\n */\nconst STEF_PERMITTED_NOISE_KINDS = new Set<SyntaxKind>([\n SyntaxKind.ImportDeclaration,\n SyntaxKind.ExportDeclaration, // re-exports without value semantics\n SyntaxKind.TypeAliasDeclaration,\n SyntaxKind.InterfaceDeclaration,\n]);\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Return true if `node` is a FunctionDeclaration with the `export` modifier.\n */\nfunction isExportedFunctionDeclaration(node: Node): boolean {\n if (node.getKind() !== SyntaxKind.FunctionDeclaration) return false;\n const fn = node as Node & {\n hasModifier(kind: SyntaxKind): boolean;\n };\n return fn.hasModifier(SyntaxKind.ExportKeyword);\n}\n\n/**\n * Return true if `node` is a VariableStatement with `export` modifier whose\n * single declarator has an ArrowFunction or FunctionExpression initializer.\n */\nfunction isExportedArrowOrFnConstDeclaration(node: Node): boolean {\n if (node.getKind() !== SyntaxKind.VariableStatement) return false;\n const vs = node as Node & {\n hasModifier(kind: SyntaxKind): boolean;\n getDeclarationList(): Node & {\n getDeclarations(): Array<Node & { getInitializer(): Node | undefined }>;\n };\n };\n if (!vs.hasModifier(SyntaxKind.ExportKeyword)) return false;\n const decls = vs.getDeclarationList().getDeclarations();\n if (decls.length !== 1) return false;\n const init = decls[0]?.getInitializer();\n if (init === undefined) return false;\n const k = init.getKind();\n return k === SyntaxKind.ArrowFunction || k === SyntaxKind.FunctionExpression;\n}\n\n/**\n * Extract the function-like node from a top-level statement that passed\n * `isExportedFunctionDeclaration` or `isExportedArrowOrFnConstDeclaration`.\n * Returns `undefined` if the node is neither.\n */\nfunction extractFunctionLike(node: Node):\n | (Node & {\n getParameters(): Array<Node & { getTypeNode(): Node | undefined }>;\n getReturnTypeNode(): Node | undefined;\n getJsDocs(): Node[];\n getBody(): Node | undefined;\n })\n | undefined {\n if (node.getKind() === SyntaxKind.FunctionDeclaration) {\n return node as Node & {\n getParameters(): Array<Node & { getTypeNode(): Node | undefined }>;\n getReturnTypeNode(): Node | undefined;\n getJsDocs(): Node[];\n getBody(): Node | undefined;\n };\n }\n if (node.getKind() === SyntaxKind.VariableStatement) {\n const vs = node as Node & {\n getDeclarationList(): Node & {\n getDeclarations(): Array<Node & { getInitializer(): Node | undefined }>;\n };\n };\n const init = vs.getDeclarationList().getDeclarations()[0]?.getInitializer();\n if (init === undefined) return undefined;\n const k = init.getKind();\n if (k === SyntaxKind.ArrowFunction || k === SyntaxKind.FunctionExpression) {\n return init as Node & {\n getParameters(): Array<Node & { getTypeNode(): Node | undefined }>;\n getReturnTypeNode(): Node | undefined;\n getJsDocs(): Node[];\n getBody(): Node | undefined;\n };\n }\n }\n return undefined;\n}\n\n/**\n * Return true if `fnNode` satisfies the STEF function-level requirements:\n * - all parameters have explicit type annotations (zero params also passes)\n * - explicit return type annotation\n * - at least one non-empty JSDoc block\n * - non-empty body (Block with ≥ 1 statement)\n *\n * For VariableStatement-wrapped arrow/fn-expressions, `enclosingNode` is the\n * VariableStatement (JSDoc lives on the var statement in ts-morph for arrow-const).\n */\nfunction functionLikeSatisfiesStef(\n fnNode: Node & {\n getParameters(): Array<Node & { getTypeNode(): Node | undefined }>;\n getReturnTypeNode(): Node | undefined;\n getJsDocs(): Node[];\n getBody(): Node | undefined;\n },\n enclosingNode: Node,\n): boolean {\n // 1. All parameters must have explicit type annotations (implicit any → false).\n const params = fnNode.getParameters();\n for (const param of params) {\n if (param.getTypeNode() === undefined) return false;\n }\n\n // 2. Explicit return type annotation.\n if (fnNode.getReturnTypeNode() === undefined) return false;\n\n // 3. Non-empty JSDoc block.\n // For arrow-const, JSDoc attaches to the VariableStatement in ts-morph.\n // Try the function node first; fall back to the enclosing node.\n const jsDocs =\n fnNode.getJsDocs().length > 0\n ? fnNode.getJsDocs()\n : (() => {\n const enc = enclosingNode as Node & { getJsDocs?(): Node[] };\n return enc.getJsDocs?.() ?? [];\n })();\n if (jsDocs.length === 0) return false;\n\n // 4. Non-empty body (Block with ≥ 1 statement).\n const body = fnNode.getBody();\n if (body === undefined) return false;\n if (body.getKind() !== SyntaxKind.Block) return false;\n const bodyStatements = (body as Node & { getStatements(): Node[] }).getStatements();\n if (bodyStatements.length === 0) return false;\n\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Return true when the parsed `sourceFile` matches the STEF (Single Typed\n * Exported Function) predicate, meaning `decompose()` should return a single\n * AtomLeaf covering the entire source range rather than fragmenting the file.\n *\n * The predicate is intentionally narrow (per plan §3.3 and OD-1). Any shape\n * outside STEF continues through the normal glue-aware fragmentation path.\n *\n * @param sourceFile - A ts-morph SourceFile already parsed from the source text.\n */\nexport function matchesStefPredicate(sourceFile: SourceFile): boolean {\n const statements = sourceFile.getStatements();\n\n let functionLikeCount = 0;\n let candidateFunctionNode: Node | undefined;\n\n for (const stmt of statements) {\n const kind = stmt.getKind();\n\n if (STEF_PERMITTED_NOISE_KINDS.has(kind)) {\n // Permitted noise — continue scanning.\n continue;\n }\n\n // Check whether this statement is the (sole) exported function-like.\n if (isExportedFunctionDeclaration(stmt) || isExportedArrowOrFnConstDeclaration(stmt)) {\n functionLikeCount += 1;\n if (functionLikeCount > 1) {\n // More than one function-like → STEF fails immediately.\n return false;\n }\n candidateFunctionNode = stmt;\n continue;\n }\n\n // Any other top-level form disqualifies the file from STEF.\n return false;\n }\n\n // Must have exactly one exported function-like.\n if (functionLikeCount !== 1 || candidateFunctionNode === undefined) return false;\n\n // Extract the actual function-like node (the FunctionDeclaration itself, or\n // the ArrowFunction/FunctionExpression initializer of the VariableStatement).\n const fnLike = extractFunctionLike(candidateFunctionNode);\n if (fnLike === undefined) return false;\n\n // Validate function-level STEF requirements.\n return functionLikeSatisfiesStef(fnLike, candidateFunctionNode);\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-IR-STRICT-001: Strict-TS-subset validator uses ts-morph AST walks.\n// Status: implemented (WI-004)\n// Rationale: ts-morph gives full typed AST access without forking the parser. Each\n// rule is a discrete function that returns ValidationError[], aggregated by the\n// top-level validator. This design makes rules independently testable and cheap to\n// add or remove without touching the orchestration logic.\n\nimport { readFileSync } from \"node:fs\";\nimport {\n type Expression,\n Node,\n NodeFlags,\n Project,\n type SourceFile,\n SyntaxKind,\n type TypeNode,\n} from \"ts-morph\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** A single rule violation emitted by the strict-subset validator. */\nexport interface ValidationError {\n /** Short identifier for the violated rule, e.g. \"no-any\". */\n readonly rule: string;\n /** Human-readable description of the violation. */\n readonly message: string;\n /** File path the violation was found in (may be \"<source>\" for in-memory sources). */\n readonly file: string;\n /** 1-based line number of the violating node. */\n readonly line: number;\n /** 1-based column number of the violating node. */\n readonly column: number;\n /** Short source snippet around the violating node, if available. */\n readonly snippet?: string | undefined;\n}\n\n/** Discriminated union result returned by all validate* functions. */\nexport type ValidationResult =\n | { readonly ok: true }\n | { readonly ok: false; readonly errors: ReadonlyArray<ValidationError> };\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Build a ValidationError from an AST node. */\nfunction makeError(node: Node, rule: string, message: string, filePath: string): ValidationError {\n const start = node.getStart();\n const sourceFile = node.getSourceFile();\n const { line, column } = sourceFile.getLineAndColumnAtPos(start);\n const snippet = node.getText().slice(0, 80);\n return { rule, message, file: filePath, line, column, snippet };\n}\n\n/** Shared Project factory — creates a ts-morph Project configured for strict analysis. */\nfunction makeProject(): Project {\n return new Project({\n useInMemoryFileSystem: true,\n compilerOptions: {\n strict: true,\n noImplicitAny: true,\n strictNullChecks: true,\n target: 99, // ESNext\n module: 99, // ESNext\n skipLibCheck: true,\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// Rule: no-any\n//\n// Rejects `any` in type positions: explicit `: any`, `as any`, `<any>` casts,\n// generic arguments `Foo<any>`, return-type `any`, parameter type `any`.\n// ---------------------------------------------------------------------------\n\nfunction isAnyTypeNode(node: TypeNode): boolean {\n return node.getKind() === SyntaxKind.AnyKeyword;\n}\n\nfunction checkNoAny(sourceFile: SourceFile, filePath: string): ValidationError[] {\n const errors: ValidationError[] = [];\n\n // Walk all nodes and find explicit `any` type annotations\n sourceFile.forEachDescendant((node) => {\n if (node.getKind() === SyntaxKind.AnyKeyword) {\n // Check if this any keyword appears in a type position (not as an identifier)\n const parent = node.getParent();\n if (parent !== undefined) {\n errors.push(\n makeError(\n node,\n \"no-any\",\n \"Explicit `any` type is forbidden in the strict subset\",\n filePath,\n ),\n );\n }\n }\n // `as any` — TypeAssertion or AsExpression with AnyKeyword type\n if (\n node.getKind() === SyntaxKind.AsExpression ||\n node.getKind() === SyntaxKind.TypeAssertionExpression\n ) {\n const typeNode = Node.isAsExpression(node)\n ? node.getTypeNode()\n : Node.isTypeAssertion(node)\n ? node.getTypeNode()\n : undefined;\n if (typeNode !== undefined && isAnyTypeNode(typeNode)) {\n // Already caught by the AnyKeyword walk above; skip to avoid double-reporting\n }\n }\n });\n\n return errors;\n}\n\n// ---------------------------------------------------------------------------\n// Rule: no-eval\n//\n// Rejects calls to `eval(...)` and `new Function(...)`.\n// ---------------------------------------------------------------------------\n\nfunction checkNoEval(sourceFile: SourceFile, filePath: string): ValidationError[] {\n const errors: ValidationError[] = [];\n\n sourceFile.forEachDescendant((node) => {\n if (Node.isCallExpression(node)) {\n const expr = node.getExpression();\n // eval(...)\n if (Node.isIdentifier(expr) && expr.getText() === \"eval\") {\n errors.push(\n makeError(node, \"no-eval\", \"`eval()` is forbidden in the strict subset\", filePath),\n );\n }\n // Function(\"code\") — direct reference to the global `Function`\n if (Node.isIdentifier(expr) && expr.getText() === \"Function\") {\n errors.push(\n makeError(\n node,\n \"no-eval\",\n \"`Function(...)` constructor call is forbidden in the strict subset\",\n filePath,\n ),\n );\n }\n }\n if (Node.isNewExpression(node)) {\n const expr = node.getExpression();\n if (Node.isIdentifier(expr) && expr.getText() === \"Function\") {\n errors.push(\n makeError(\n node,\n \"no-eval\",\n \"`new Function(...)` is forbidden in the strict subset\",\n filePath,\n ),\n );\n }\n }\n });\n\n return errors;\n}\n\n// ---------------------------------------------------------------------------\n// Rule: no-runtime-reflection\n//\n// Rejects:\n// - Object.getPrototypeOf(...)\n// - Object.setPrototypeOf(...)\n// - Reflect.*\n// - __proto__ property access\n// - Object.getOwnPropertyDescriptor(...)\n// - Object.defineProperty(...)\n// ---------------------------------------------------------------------------\n\nconst REFLECTION_METHODS = new Set([\n \"getPrototypeOf\",\n \"setPrototypeOf\",\n \"getOwnPropertyDescriptor\",\n \"getOwnPropertyDescriptors\",\n \"defineProperty\",\n \"defineProperties\",\n]);\n\nfunction checkNoRuntimeReflection(sourceFile: SourceFile, filePath: string): ValidationError[] {\n const errors: ValidationError[] = [];\n\n sourceFile.forEachDescendant((node) => {\n // Property access: X.member\n if (Node.isPropertyAccessExpression(node)) {\n const obj = node.getExpression();\n const member = node.getName();\n\n // Reflect.* — any access on `Reflect`\n if (Node.isIdentifier(obj) && obj.getText() === \"Reflect\") {\n errors.push(\n makeError(\n node,\n \"no-runtime-reflection\",\n `\\`Reflect.${member}\\` is forbidden in the strict subset`,\n filePath,\n ),\n );\n }\n\n // Object.getPrototypeOf, Object.defineProperty, etc.\n if (Node.isIdentifier(obj) && obj.getText() === \"Object\" && REFLECTION_METHODS.has(member)) {\n errors.push(\n makeError(\n node,\n \"no-runtime-reflection\",\n `\\`Object.${member}\\` is forbidden in the strict subset`,\n filePath,\n ),\n );\n }\n\n // __proto__\n if (member === \"__proto__\") {\n errors.push(\n makeError(\n node,\n \"no-runtime-reflection\",\n \"`__proto__` access is forbidden in the strict subset\",\n filePath,\n ),\n );\n }\n }\n\n // Element access: obj[\"__proto__\"] or obj[expr]\n if (Node.isElementAccessExpression(node)) {\n const argExpr = node.getArgumentExpression();\n if (argExpr !== undefined) {\n const text = argExpr.getText();\n if (text === '\"__proto__\"' || text === \"'__proto__'\") {\n errors.push(\n makeError(\n node,\n \"no-runtime-reflection\",\n \"`__proto__` access via bracket notation is forbidden in the strict subset\",\n filePath,\n ),\n );\n }\n }\n }\n });\n\n return errors;\n}\n\n// ---------------------------------------------------------------------------\n// Rule: no-with\n//\n// Rejects `with (obj) { ... }` statements.\n// ---------------------------------------------------------------------------\n\nfunction checkNoWith(sourceFile: SourceFile, filePath: string): ValidationError[] {\n const errors: ValidationError[] = [];\n sourceFile.forEachDescendant((node) => {\n if (node.getKind() === SyntaxKind.WithStatement) {\n errors.push(\n makeError(\n node,\n \"no-with\",\n \"`with` statements are forbidden in the strict subset\",\n filePath,\n ),\n );\n }\n });\n return errors;\n}\n\n// ---------------------------------------------------------------------------\n// Rule: no-mutable-globals\n//\n// Rejects top-level `let` and `var` declarations. Top-level `const` is fine.\n// Module-level (file scoped) only — block-scoped let inside functions is allowed.\n// ---------------------------------------------------------------------------\n\nfunction checkNoMutableGlobals(sourceFile: SourceFile, filePath: string): ValidationError[] {\n const errors: ValidationError[] = [];\n\n // Direct children of the SourceFile are top-level statements.\n // NodeFlags.Let = 1, NodeFlags.Const = 2; var has flags = 0.\n // VariableDeclarationList.getFlags() returns the ts.NodeFlags value.\n for (const statement of sourceFile.getStatements()) {\n if (Node.isVariableStatement(statement)) {\n const declListFlags = statement.getDeclarationList().getFlags();\n const isLet = (declListFlags & NodeFlags.Let) !== 0;\n const isConst = (declListFlags & NodeFlags.Const) !== 0;\n // var: neither Let nor Const flag set\n if (isLet || (!isConst && !isLet)) {\n // isLet → definitely `let`; !isConst && !isLet → `var`\n errors.push(\n makeError(\n statement,\n \"no-mutable-globals\",\n \"Top-level `let` and `var` are forbidden; use `const` for all module-level bindings\",\n filePath,\n ),\n );\n }\n }\n }\n\n return errors;\n}\n\n// ---------------------------------------------------------------------------\n// Rule: no-throw-non-error\n//\n// Rejects `throw <literal>` where the thrown value is not an Error instance.\n// Allows: throw new Error(...), throw new MyError(...), throw someErrorVar\n// Rejects: throw \"string\", throw 42, throw null, throw undefined, throw { ... }\n// ---------------------------------------------------------------------------\n\nfunction checkNoThrowNonError(sourceFile: SourceFile, filePath: string): ValidationError[] {\n const errors: ValidationError[] = [];\n\n sourceFile.forEachDescendant((node) => {\n if (Node.isThrowStatement(node)) {\n const thrown = node.getExpression();\n if (thrown === undefined) return;\n // Allow new SomeError(...)\n if (Node.isNewExpression(thrown)) return;\n // Allow identifiers (variable references — we can't know statically if they're errors)\n if (Node.isIdentifier(thrown)) return;\n // Allow property access (e.g. this.error, someObj.error)\n if (Node.isPropertyAccessExpression(thrown)) return;\n // Allow call expressions that return errors (e.g. makeError())\n if (Node.isCallExpression(thrown)) return;\n // Allow awaited values\n if (Node.isAwaitExpression(thrown)) return;\n // Everything else is a literal or non-Error value — reject\n errors.push(\n makeError(\n thrown,\n \"no-throw-non-error\",\n \"Throwing non-Error values is forbidden; use `throw new Error(...)` or a subclass\",\n filePath,\n ),\n );\n }\n });\n\n return errors;\n}\n\n// ---------------------------------------------------------------------------\n// Rule: no-top-level-side-effects\n//\n// Rejects top-level statements that are not:\n// - import declarations\n// - export declarations (including `export const`, `export function`, etc.)\n// - empty statements\n// - const variable declarations (exported or not)\n// - type/interface/enum declarations\n// - class declarations\n// - function declarations\n// Rejects top-level expression statements (side-effecting calls, assignments, etc.)\n// ---------------------------------------------------------------------------\n\nconst ALLOWED_TOP_LEVEL_KINDS = new Set([\n SyntaxKind.ImportDeclaration,\n SyntaxKind.ExportDeclaration,\n SyntaxKind.ExportAssignment,\n SyntaxKind.EmptyStatement,\n SyntaxKind.InterfaceDeclaration,\n SyntaxKind.TypeAliasDeclaration,\n SyntaxKind.EnumDeclaration,\n SyntaxKind.ClassDeclaration,\n SyntaxKind.FunctionDeclaration,\n SyntaxKind.ModuleDeclaration,\n]);\n\nfunction checkNoTopLevelSideEffects(sourceFile: SourceFile, filePath: string): ValidationError[] {\n const errors: ValidationError[] = [];\n\n for (const statement of sourceFile.getStatements()) {\n const kind = statement.getKind();\n\n if (ALLOWED_TOP_LEVEL_KINDS.has(kind)) continue;\n\n // Variable statements — allow only `const` (let/var caught by no-mutable-globals)\n if (Node.isVariableStatement(statement)) {\n // All variable statements are handled by no-mutable-globals; skip here.\n continue;\n }\n\n // Expression statements at top level are side effects\n if (Node.isExpressionStatement(statement)) {\n errors.push(\n makeError(\n statement,\n \"no-top-level-side-effects\",\n \"Top-level expression statements (side effects) are forbidden; wrap in a function\",\n filePath,\n ),\n );\n }\n }\n\n return errors;\n}\n\n// ---------------------------------------------------------------------------\n// Rule: no-untyped-imports\n//\n// Rejects imports where the module specifier is a relative path that doesn't\n// resolve to a known .ts source or .d.ts declaration file within the project.\n// For third-party imports (non-relative), we trust that the project has types\n// configured (skipLibCheck is the fallback); in-project imports must resolve.\n//\n// In the in-memory project context: we check if the import declaration has\n// a `type` qualifier or if every imported symbol has a known type (not `any`).\n// Pragmatically for v0: we reject bare `import X from \"mod\"` where `X` resolves\n// to `any` (which happens when there are no type declarations for the module).\n// ---------------------------------------------------------------------------\n\nfunction checkNoUntypedImports(sourceFile: SourceFile, filePath: string): ValidationError[] {\n const errors: ValidationError[] = [];\n\n for (const importDecl of sourceFile.getImportDeclarations()) {\n // import type ... is always fine\n if (importDecl.isTypeOnly()) continue;\n\n const namedBindings = importDecl.getNamedImports();\n const defaultImport = importDecl.getDefaultImport();\n const namespaceImport = importDecl.getNamespaceImport();\n\n // For each imported binding, check if the symbol resolves to `any`\n const checkBinding = (expr: Expression | undefined): void => {\n if (expr === undefined) return;\n const type = expr.getType();\n if (type.isAny()) {\n errors.push(\n makeError(\n importDecl,\n \"no-untyped-imports\",\n `Import from \"${importDecl.getModuleSpecifierValue()}\" has unresolved types (resolves to \\`any\\`); ensure the module has TypeScript declarations`,\n filePath,\n ),\n );\n }\n };\n\n if (defaultImport !== undefined) {\n checkBinding(defaultImport);\n }\n for (const named of namedBindings) {\n // @decision DEC-V2-IR-TYPE-MODIFIER-001: Skip named import specifiers with the\n // `type` modifier (`import { type Foo, bar }`). A type-modifier binding has no\n // runtime value; calling getType() on it returns `any` in value position even\n // when the module is fully resolved, producing a false-positive violation.\n // The safety property (no runtime `any`) is preserved because type-only imports\n // are erased at compile time and cannot introduce untyped values.\n // Status: implemented (WI-V2-03).\n if (named.isTypeOnly()) continue;\n checkBinding(named.getNameNode());\n }\n if (namespaceImport !== undefined) {\n checkBinding(namespaceImport);\n }\n }\n\n return errors;\n}\n\n// ---------------------------------------------------------------------------\n// Rule orchestration\n// ---------------------------------------------------------------------------\n\ntype RuleChecker = (sourceFile: SourceFile, filePath: string) => ValidationError[];\n\nconst ALL_RULES: ReadonlyArray<RuleChecker> = [\n checkNoAny,\n checkNoEval,\n checkNoRuntimeReflection,\n checkNoWith,\n checkNoMutableGlobals,\n checkNoThrowNonError,\n checkNoTopLevelSideEffects,\n checkNoUntypedImports,\n];\n\n/** Run all rules against an already-created ts-morph SourceFile. */\nexport function runAllRules(sourceFile: SourceFile, filePath: string): ValidationError[] {\n const errors: ValidationError[] = [];\n for (const rule of ALL_RULES) {\n errors.push(...rule(sourceFile, filePath));\n }\n return errors;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Validate a TypeScript source string against the strict subset rules.\n *\n * The source is parsed in-memory with a ts-morph Project; no disk I/O occurs.\n * Returns `{ ok: true }` if all rules pass, or `{ ok: false, errors }` listing\n * every violation found (all rules are run even after the first failure).\n *\n * @example\n * ```ts\n * const result = validateStrictSubset(`export const x: any = 1;`);\n * // result.ok === false; result.errors[0].rule === \"no-any\"\n * ```\n */\nexport function validateStrictSubset(source: string): ValidationResult {\n const project = makeProject();\n const sourceFile = project.createSourceFile(\"__input__.ts\", source);\n const errors = runAllRules(sourceFile, \"<source>\");\n return errors.length === 0 ? { ok: true } : { ok: false, errors };\n}\n\n/**\n * Validate a TypeScript file on disk against the strict subset rules.\n *\n * Reads the file synchronously, then delegates to `validateStrictSubset`.\n * Returns `{ ok: true }` or `{ ok: false, errors }`.\n */\nexport function validateStrictSubsetFile(path: string): ValidationResult {\n const source = readFileSync(path, \"utf-8\");\n const project = makeProject();\n const sourceFile = project.createSourceFile(path, source);\n const errors = runAllRules(sourceFile, path);\n return errors.length === 0 ? { ok: true } : { ok: false, errors };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-IR-BLOCK-002: parseBlockTriplet replaces parseBlock as the sole\n// block-authoring entry point in @yakcc/ir.\n// Status: implemented (WI-T02)\n// Rationale: Blocks are now directories (spec.yak, impl.ts, proof/manifest.json)\n// rather than single TS files with embedded CONTRACT literals. The inline-CONTRACT\n// mechanism (parseBlock + annotations.ts) is deleted per Sacred Practice #12 —\n// single source of truth. parseBlockTriplet reads all three files, validates each\n// against @yakcc/contracts schema validators, runs the strict-subset validator on\n// impl.ts, and resolves sub-block imports to SpecHash references via the existing\n// import-detection logic. The registry parameter is accepted for future use (T03)\n// but is not called at L0: sub-block SpecHash values are extracted from import\n// paths and left for the caller to resolve against the registry.\n\nimport { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n type BlockMerkleRoot,\n type BlockTriplet,\n type ProofManifest,\n type SpecHash,\n type SpecYak,\n blockMerkleRoot,\n specHash,\n validateProofManifestL0,\n validateSpecYak,\n} from \"@yakcc/contracts\";\nimport { Project } from \"ts-morph\";\nimport { type ValidationResult, runAllRules } from \"./strict-subset.js\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * A reference to a sub-block imported by impl.ts.\n *\n * Sub-block detection is heuristic: any import from a path matching\n * `@yakcc/seeds/`, `@yakcc/blocks/`, or the configurable `blockPatterns`\n * option is treated as a sub-block reference. The specHashRef is left null\n * at parse time; callers use it as a lookup key against the registry\n * (selectBlocks(specHash) → BlockMerkleRoot[]) in T03+.\n */\nexport interface SubBlockRef {\n /** The local identifier bound by the import statement. */\n readonly localName: string;\n /** The module specifier as written in impl.ts (e.g. \"@yakcc/seeds/blocks/digit\"). */\n readonly importedFrom: string;\n /**\n * The SpecHash for the referenced sub-block, if already known.\n * null when the registry has not been consulted (the default at L0).\n */\n readonly specHashRef: SpecHash | null;\n}\n\n/**\n * The result of successfully parsing a block triplet directory.\n *\n * All fields are populated: the SpecYak from spec.yak, the impl.ts source,\n * the ProofManifest, the artifact bytes map, the strict-subset ValidationResult,\n * the BlockTriplet (ready to feed into blockMerkleRoot), the derived\n * BlockMerkleRoot, and the sub-block composition references.\n */\nexport interface BlockTripletParseResult {\n /** The parsed and validated spec.yak content. */\n readonly spec: SpecYak;\n /** The SpecHash derived from the spec (BLAKE3(canonicalize(spec.yak))). */\n readonly specHashValue: SpecHash;\n /** The impl.ts source text as UTF-8. */\n readonly implSource: string;\n /** The parsed and validated proof/manifest.json content. */\n readonly manifest: ProofManifest;\n /**\n * Bytes for each artifact declared in manifest.artifacts.\n * Keys are the artifact path fields as declared in the manifest.\n */\n readonly artifacts: Map<string, Uint8Array>;\n /**\n * Strict-subset validation result for impl.ts.\n * Check result.ok before treating the block as registry-ready.\n */\n readonly validation: ValidationResult;\n /**\n * The assembled BlockTriplet, ready to pass to blockMerkleRoot().\n * Always present; callers may re-derive the root or pass it to the registry.\n */\n readonly triplet: BlockTriplet;\n /**\n * The BlockMerkleRoot derived from the triplet.\n * BLAKE3(spec_hash || impl_hash || proof_root).\n */\n readonly merkleRoot: BlockMerkleRoot;\n /**\n * Sub-block imports detected in impl.ts, resolved to SubBlockRef values.\n * specHashRef is null at parse time; the registry resolves it in T03+.\n */\n readonly composition: ReadonlyArray<SubBlockRef>;\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface ParseBlockTripletOptions {\n /**\n * Additional import path prefixes to treat as sub-block references beyond\n * the built-in patterns (\"@yakcc/seeds/\", \"@yakcc/blocks/\").\n */\n readonly blockPatterns?: readonly string[] | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Built-in sub-block import patterns\n// ---------------------------------------------------------------------------\n\nconst BUILTIN_BLOCK_PATTERNS = [\"@yakcc/seeds/\", \"@yakcc/blocks/\"] as const;\n\nfunction isBlockImport(moduleSpecifier: string, extraPatterns: readonly string[]): boolean {\n for (const pattern of BUILTIN_BLOCK_PATTERNS) {\n if (moduleSpecifier.startsWith(pattern)) return true;\n }\n for (const pattern of extraPatterns) {\n if (moduleSpecifier.startsWith(pattern)) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Sub-block composition extraction\n//\n// Reuses the import-detection logic from the removed parseBlock(). Walks the\n// ts-morph AST of impl.ts and collects all imports matching the block-package\n// heuristic. Converts each import binding to a SubBlockRef with specHashRef=null.\n// ---------------------------------------------------------------------------\n\nfunction extractComposition(source: string, extraPatterns: readonly string[]): SubBlockRef[] {\n const project = new Project({\n useInMemoryFileSystem: true,\n compilerOptions: { strict: true, skipLibCheck: true, target: 99, module: 99 },\n });\n const sourceFile = project.createSourceFile(\"__impl__.ts\", source);\n\n const refs: SubBlockRef[] = [];\n\n for (const importDecl of sourceFile.getImportDeclarations()) {\n const specifier = importDecl.getModuleSpecifierValue();\n if (!isBlockImport(specifier, extraPatterns)) continue;\n\n const namedImports = importDecl.getNamedImports();\n const defaultImport = importDecl.getDefaultImport();\n const namespaceImport = importDecl.getNamespaceImport();\n\n if (namedImports.length > 0) {\n for (const named of namedImports) {\n refs.push({\n localName: named.getAliasNode()?.getText() ?? named.getName(),\n importedFrom: specifier,\n specHashRef: null,\n });\n }\n } else if (defaultImport !== undefined) {\n refs.push({\n localName: defaultImport.getText(),\n importedFrom: specifier,\n specHashRef: null,\n });\n } else if (namespaceImport !== undefined) {\n refs.push({\n localName: namespaceImport.getText(),\n importedFrom: specifier,\n specHashRef: null,\n });\n }\n }\n\n return refs;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Parse a Yakcc block triplet from a directory on disk.\n *\n * Reads three files from `directoryPath`:\n * - spec.yak — parsed as JSON, validated via validateSpecYak\n * - impl.ts — read as UTF-8; validated via the strict-subset rules\n * - proof/manifest.json — parsed as JSON, validated via validateProofManifestL0\n *\n * Also reads the artifact bytes for each entry declared in proof/manifest.json\n * (paths are relative to the proof/ directory).\n *\n * The `registry` parameter is reserved for T03+ (selectBlocks integration).\n * At L0, sub-block SpecHash references are extracted but not resolved against\n * a registry; specHashRef is null in all returned SubBlockRef values.\n *\n * Throws:\n * - TypeError — if spec.yak or proof/manifest.json fails schema validation\n * - Error — if a required file cannot be read, or if an artifact declared\n * in the manifest is missing from proof/\n *\n * The strict-subset validation result is always returned (never thrown); callers\n * check result.validation.ok to determine registry readiness.\n *\n * @example\n * ```ts\n * const result = parseBlockTriplet(\"/path/to/blocks/digit-of\");\n * if (!result.validation.ok) {\n * for (const e of result.validation.errors) console.error(e.message);\n * }\n * console.log(result.merkleRoot); // 64-char hex BlockMerkleRoot\n * ```\n */\nexport function parseBlockTriplet(\n directoryPath: string,\n // registry parameter reserved for T03+ registry integration (selectBlocks)\n _registry?: unknown,\n options?: ParseBlockTripletOptions,\n): BlockTripletParseResult {\n const extraPatterns = options?.blockPatterns ?? [];\n\n // 1. Read and validate spec.yak\n const specPath = join(directoryPath, \"spec.yak\");\n const specRaw = readFileSync(specPath, \"utf-8\");\n const specParsed: unknown = JSON.parse(specRaw);\n // validateSpecYak throws TypeError on invalid input (DEC-TRIPLET-L0-ONLY-019)\n const spec = validateSpecYak(specParsed);\n const specHashValue = specHash(spec);\n\n // 2. Read impl.ts and run the strict-subset validator\n const implPath = join(directoryPath, \"impl.ts\");\n const implSource = readFileSync(implPath, \"utf-8\");\n\n const project = new Project({\n useInMemoryFileSystem: true,\n compilerOptions: { strict: true, skipLibCheck: true, target: 99, module: 99 },\n });\n const sourceFile = project.createSourceFile(\"impl.ts\", implSource);\n const validationErrors = runAllRules(sourceFile, implPath);\n const validation: ValidationResult =\n validationErrors.length === 0 ? { ok: true } : { ok: false, errors: validationErrors };\n\n // 3. Read and validate proof/manifest.json\n const manifestPath = join(directoryPath, \"proof\", \"manifest.json\");\n const manifestRaw = readFileSync(manifestPath, \"utf-8\");\n const manifestParsed: unknown = JSON.parse(manifestRaw);\n // validateProofManifestL0 throws TypeError if not exactly one property_tests artifact\n const manifest = validateProofManifestL0(manifestParsed);\n\n // 4. Read artifact bytes for each artifact declared in the manifest.\n // Paths are relative to proof/ directory.\n const proofDir = join(directoryPath, \"proof\");\n const artifacts = new Map<string, Uint8Array>();\n for (const artifact of manifest.artifacts) {\n const artifactPath = join(proofDir, artifact.path);\n const artifactBytes = readFileSync(artifactPath);\n artifacts.set(artifact.path, artifactBytes);\n }\n\n // 5. Assemble the BlockTriplet and derive the BlockMerkleRoot.\n const triplet: BlockTriplet = { spec, implSource, manifest, artifacts };\n const merkleRoot = blockMerkleRoot(triplet);\n\n // 6. Extract sub-block composition references from impl.ts imports.\n const composition = extractComposition(implSource, extraPatterns);\n\n return {\n spec,\n specHashValue,\n implSource,\n manifest,\n artifacts,\n validation,\n triplet,\n merkleRoot,\n composition,\n };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-V2-IR-PROJECT-MODE-001: Project-mode validation uses a real ts-morph\n// Project loaded from tsconfig.json rather than the isolated in-memory project used by\n// validateStrictSubset. Status: implemented (WI-V2-01).\n// Rationale: The isolated validator creates a synthetic in-memory project that has no\n// knowledge of cross-file imports, workspace packages, or @types/* declarations. This\n// produces false-positive no-untyped-imports violations for any code that imports from\n// other files in the same project, from workspace packages (e.g. @yakcc/contracts), or\n// from Node built-ins (node:fs, node:path). The project-mode validator solves this by\n// loading the real tsconfig, letting ts-morph resolve all source files and their\n// transitive dependencies, and running the same rule walkers over the resolved AST.\n// The per-file walker (runAllRules) is reused unchanged; only the project bootstrap\n// strategy differs. This is the minimum viable change — no new rule registry, no API\n// surface changes beyond the new export.\n\nimport { Project } from \"ts-morph\";\nimport { type ValidationError, runAllRules } from \"./strict-subset.js\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Result returned by validateStrictSubsetProject. */\nexport interface ProjectValidationResult {\n /** Absolute path to the tsconfig.json that was loaded. */\n readonly tsconfigPath: string;\n /** All violations found across every non-external source file. */\n readonly violations: readonly ValidationError[];\n /** Number of source files that were inspected (excludes node_modules / external libs). */\n readonly filesValidated: number;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Validate all TypeScript source files in a project against the strict subset rules.\n *\n * Loads a real ts-morph Project from the given tsconfig.json path, resolves all\n * source file dependencies, and runs every strict-subset rule against each\n * non-external source file. External library files (node_modules, .d.ts from\n * @types packages) are skipped — only the project's own source files are checked.\n *\n * This produces significantly fewer false-positive no-untyped-imports violations\n * compared to the isolated-mode validateStrictSubset/validateStrictSubsetFile\n * functions, because the real tsconfig wires up cross-file imports, workspace\n * package references, and @types declarations correctly.\n *\n * @param tsconfigPath - Absolute or relative path to the project's tsconfig.json.\n *\n * @example\n * ```ts\n * const result = await validateStrictSubsetProject(\"./tsconfig.json\");\n * if (result.violations.length > 0) {\n * for (const v of result.violations) {\n * console.error(`${v.file}:${v.line}:${v.column} [${v.rule}] ${v.message}`);\n * }\n * }\n * ```\n */\nexport async function validateStrictSubsetProject(\n tsconfigPath: string,\n): Promise<ProjectValidationResult> {\n const project = new Project({ tsConfigFilePath: tsconfigPath });\n\n // Resolve all transitive source file dependencies so that cross-file imports\n // and workspace package references are available during type checking.\n await project.resolveSourceFileDependencies();\n\n const sourceFiles = project.getSourceFiles();\n const violations: ValidationError[] = [];\n let filesValidated = 0;\n\n for (const sf of sourceFiles) {\n // Skip files that come from node_modules or external declaration packages.\n // ts-morph exposes these via isFromExternalLibrary() and isInNodeModules().\n if (sf.isFromExternalLibrary()) continue;\n if (sf.isInNodeModules()) continue;\n\n const filePath = sf.getFilePath();\n\n // @decision DEC-V2-IR-TEST-EXCLUSION-001: Exclude test files from project-mode\n // validation. Test files (*.test.ts, *.spec.ts) use Vitest's describe/it/beforeEach\n // top-level calls, which legitimately violate no-top-level-side-effects, and use\n // top-level `let` for shared state, violating no-mutable-globals. These are\n // expected patterns for test frameworks and not gaps in IR subset support.\n // The IR package's own tsconfig excludes test files; this filter makes project-mode\n // validation consistent regardless of whether a package's tsconfig explicitly\n // excludes tests. Status: implemented (WI-V2-03).\n if (\n filePath.endsWith(\".test.ts\") ||\n filePath.endsWith(\".test.js\") ||\n filePath.endsWith(\".spec.ts\") ||\n filePath.endsWith(\".spec.js\")\n ) {\n continue;\n }\n\n violations.push(...runAllRules(sf, filePath));\n filesValidated += 1;\n }\n\n return { tsconfigPath, violations, filesValidated };\n}\n","// SPDX-License-Identifier: MIT\n/**\n * ast-binding.ts — Extract variable binding shape from agent-emitted TypeScript snippets.\n *\n * Uses ts-morph (already a dependency of @yakcc/ir) to parse a code snippet\n * in-memory and extract the binding name, called function name, and arguments.\n *\n * @decision DEC-HOOK-PHASE-2-001 (B)\n * @title Binding-extraction strategy for destructuring and generics edge cases\n * @status accepted\n * @rationale\n * Binding extraction is the long-pole engineering item in Phase 2 (per #217 estimate:\n * 1.5–2 weeks). v1 handles the common case (single const/let + call expression) and\n * documents the out-of-scope patterns explicitly.\n *\n * IN SCOPE (v1):\n * - Simple binding: `const x = fn(args)` and `let x = fn(args)`\n * - Multi-arg calls: `const x = fn(a, b, c)`\n * - Type-annotated bindings: `const x: T = fn(args)` — returnType captured\n * - String/numeric/boolean literal args (getText() returns source text)\n *\n * OUT OF SCOPE (v1) — returns null:\n * - Destructuring: `const { a, b } = fn(args)` — requires multi-binding analysis\n * - Default parameters: `fn(args = defaultVal)` — complex analysis, Phase 2.1\n * - Generic call expressions: `fn<T>(args)` — Phase 2.1\n * - Constructor calls: `new Foo(args)` — different substitution semantics\n * - Multi-statement snippets: ambiguous target; Phase 2 handles single-declaration only\n * - Bare expression statements (no binding): not substitutable\n *\n * WHY ts-morph instead of a custom regex/split approach:\n * - Correctness: ts-morph handles all TypeScript syntax edge cases (template literals,\n * nested calls, type casts, etc.) without fragile regex matching.\n * - Reuse: ts-morph is already a direct dependency of @yakcc/ir (block-parser.ts,\n * strict-subset.ts use it). No new dependency is introduced.\n * - Testability: the in-memory Project creation pattern is already established in\n * validateStrictSubset() in strict-subset.ts. This module follows the same pattern.\n *\n * Cross-reference:\n * DEC-HOOK-PHASE-2-001 (parent — import path + invocation policy)\n * DEC-IR-STRICT-001 (ts-morph as the AST tool for @yakcc/ir)\n * DEC-HOOK-LAYER-001 (hook layer architecture)\n */\n\nimport { Node, Project, SyntaxKind } from \"ts-morph\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * The extracted binding shape from a single variable declaration with a call\n * expression as the initializer.\n *\n * Consumed by renderSubstitution() in @yakcc/hooks-base.\n */\nexport interface BindingShape {\n /** Variable name: `const <name> = fn(...)`. */\n readonly name: string;\n /** Arguments as source-text strings (getText() on each argument node). */\n readonly args: readonly string[];\n /**\n * Function name from the original call expression: `const x = <atomName>(...)`.\n * This becomes the named import and the import path segment in renderSubstitution().\n */\n readonly atomName: string;\n /**\n * Explicit type annotation text if present: `const x: <returnType> = fn(...)`.\n * Undefined when no type annotation is present.\n */\n readonly returnType?: string | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Shared Project factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create a minimal in-memory ts-morph Project for snippet parsing.\n *\n * Same pattern as validateStrictSubset() in strict-subset.ts (DEC-IR-STRICT-001):\n * skipLibCheck + addSourceFileAtPath avoid needing real node_modules.\n * addSourceFileAtPathIfExists is not used — we add in-memory source files directly.\n */\nfunction makeSnippetProject(): Project {\n return new Project({\n useInMemoryFileSystem: true,\n skipFileDependencyResolution: true,\n compilerOptions: {\n skipLibCheck: true,\n strict: false,\n },\n });\n}\n\n// ---------------------------------------------------------------------------\n// extractBindingShape\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the variable binding shape from a single TypeScript code snippet.\n *\n * Parses the snippet in-memory with ts-morph and looks for a single\n * `VariableStatement` with a `CallExpression` initializer.\n *\n * Returns null when:\n * - The snippet is empty or unparseable.\n * - The snippet contains no variable declarations.\n * - The snippet contains multiple variable statements (ambiguous target).\n * - The declaration's initializer is not a CallExpression (not a function call).\n * - The binding pattern is a destructuring pattern (out of scope for v1).\n *\n * @decision DEC-HOOK-PHASE-2-001 (B): v1 scope boundaries documented above.\n *\n * @param code - TypeScript snippet from an agent-emitted tool call.\n * @returns BindingShape or null if the snippet cannot be analyzed.\n */\nexport function extractBindingShape(code: string): BindingShape | null {\n if (!code.trim()) {\n return null;\n }\n\n const project = makeSnippetProject();\n const sourceFile = project.createSourceFile(\"__snippet__.ts\", code);\n\n // Collect only VariableStatements at the top level of the snippet.\n const varStatements = sourceFile.getStatements().filter(Node.isVariableStatement);\n\n if (varStatements.length === 0) {\n // No variable declarations — could be expression statement, class, function, etc.\n return null;\n }\n\n if (varStatements.length > 1) {\n // Multiple variable statements — ambiguous target. Return null per v1 scope.\n // Best-effort: caller may want to handle multi-statement snippets later.\n // Returning null is the safe conservative choice.\n return null;\n }\n\n const varStatement = varStatements[0];\n if (varStatement === undefined) {\n return null;\n }\n\n // Get the declaration list — should have exactly one declarator.\n const declarationList = varStatement.getDeclarationList();\n const declarations = declarationList.getDeclarations();\n\n if (declarations.length !== 1) {\n // Multiple declarators in one statement: `const a = fn(), b = gn()`.\n // v1: not supported.\n return null;\n }\n\n const decl = declarations[0];\n if (decl === undefined) {\n return null;\n }\n\n // Check that the binding is a simple identifier (not destructuring).\n const nameNode = decl.getNameNode();\n if (!Node.isIdentifier(nameNode)) {\n // Destructuring pattern — out of scope for v1.\n return null;\n }\n\n const name = nameNode.getText();\n\n // Extract the optional type annotation.\n const typeNode = decl.getTypeNode();\n const returnType = typeNode !== undefined ? typeNode.getText() : undefined;\n\n // Check that the initializer is a CallExpression.\n const initializer = decl.getInitializer();\n if (initializer === undefined) {\n return null;\n }\n\n if (!Node.isCallExpression(initializer)) {\n // RHS is a literal, binary expression, new-expression, etc.\n return null;\n }\n\n // Extract the called function name.\n const expression = initializer.getExpression();\n if (!Node.isIdentifier(expression)) {\n // Member expression (`obj.method()`), new expression, etc.\n // v1: only plain identifier calls are supported.\n return null;\n }\n\n const atomName = expression.getText();\n\n // Extract arguments as source-text strings.\n const args = initializer\n .getArguments()\n .map((arg) => arg.getText())\n // Filter out SyntaxKind.CommaToken elements if any leak through.\n .filter((text) => text !== \",\");\n\n return { name, args, atomName, returnType };\n}\n","// SPDX-License-Identifier: MIT\n/**\n * @decision DEC-SLICER-NOVEL-GLUE-004\n * title: DFG slicer — slice() implementation for WI-012-05\n * status: decided\n * rationale: slice() walks a RecursionTree (produced by decompose()) in DFS\n * order and classifies each node as either a PointerEntry (the subtree rooted\n * here has a matching registry entry by canonicalAstHash — no synthesis needed)\n * or a NovelGlueEntry (unmatched AtomLeaf — source that must be synthesized).\n *\n * Design choices:\n * - Registry lookup is attempted via the optional findByCanonicalAstHash method.\n * When the method is absent or returns an empty array, the node is treated as\n * unmatched and the slicer degrades gracefully: AtomLeaf → NovelGlueEntry,\n * BranchNode → descend into children.\n * - BranchNode collapse: when the registry matches a BranchNode by canonicalAstHash,\n * the entire subtree collapses into one PointerEntry. Descendants are NOT visited.\n * This is the primary deduplication mechanism for composite primitives.\n * - AtomLeaf with no registry match: first runs classifyForeign() to detect\n * static import declarations from foreign packages. If the atom is a foreign\n * import, one ForeignLeafEntry per binding is emitted (no synthesis attempted).\n * Only when classifyForeign() returns no entries does the atom fall through to\n * NovelGlueEntry. This ordering (registry → foreign → novel-glue) ensures that\n * registry-pointer matches always take priority over foreign classification.\n * (DEC-V2-FOREIGN-BLOCK-SCHEMA-001)\n * - matchedPrimitives deduplication: we track seen canonicalAstHash values and\n * only append the first-seen (canonicalAstHash, merkleRoot) pair. This mirrors\n * the \"first BlockMerkleRoot from the result\" rule applied per node.\n * - DFS order is guaranteed by the recursive descent: we visit a node before\n * its children, and children are visited left-to-right (matching the order\n * they appear in RecursionTree.root.children).\n * - sourceBytesByKind sums (sourceRange.end - sourceRange.start) for each entry\n * kind. ForeignLeafEntry does not contribute to either counter (foreign deps\n * are not synthesized, so they are not novel glue, nor matched registry bytes).\n * - The function signature accepts Pick<ShaveRegistryView, \"findByCanonicalAstHash\">\n * rather than the full ShaveRegistryView to keep the slicer testable with a\n * minimal stub and decoupled from the broader registry surface.\n *\n * @decision DEC-V2-FOREIGN-BLOCK-SCHEMA-001\n * title: classifyForeign — pure predicate for static foreign-import detection (L3)\n * status: decided\n * rationale: classifyForeign() must be pure of registry I/O (L3-I2) so that:\n * (a) it can run inside walkNode without async overhead on every atom;\n * (b) tests can call it directly with a registry that throws on\n * findByCanonicalAstHash, proving registry purity (test 8).\n * The predicate creates an in-memory ts-morph Project, parses the atom's source\n * text, and walks ImportDeclaration nodes. It skips type-only imports\n * (isTypeOnly()), relative imports (starts with '.'), and workspace imports\n * (starts with WORKSPACE_PREFIX). Dynamic import() expressions are NOT handled\n * here — L3 spec explicitly defers them (test 7 falls through to NovelGlueEntry).\n * The node: prefix and workspace prefix are defined as named constants to avoid\n * hardcoding the same string at multiple sites (forbidden shortcut in L3 scope).\n *\n * @decision DEC-V2-SLICER-SEARCH-001\n * title: Glue-aware slicer search algorithm (L2)\n * status: decided\n * rationale:\n * Under shaveMode:'glue-aware', the slicer applies the IR strict-subset\n * predicate (validateStrictSubset from @yakcc/ir) per-subgraph instead of\n * per-file. Nodes that pass the predicate are emitted as shaveable atoms\n * (NovelGlueEntry or PointerEntry). Nodes that fail become GlueLeafEntry\n * (verbatim source, project-local, NOT stored in the registry).\n *\n * Key design decisions:\n *\n * 1. TOP-DOWN TRAVERSAL: The search proceeds top-down. At each node, the\n * predicate is applied first. If it passes, the node is a maximal shaveable\n * subgraph — we do NOT recurse further into its children. If it fails, we\n * recurse into children to find the largest shaveable pieces within.\n * Rationale: top-down finds the LARGEST (maximal) shaveable units first.\n * Bottom-up would find many small atoms inside large shaveable functions,\n * producing over-fragmented output. Top-down is also deterministic and\n * O(n) in AST size (no backtracking needed).\n *\n * 2. MAXIMAL-SUBGRAPH DISCIPLINE — OPTION (A): When a BranchNode fails the\n * predicate, we recurse into its children rather than emitting a single\n * GlueLeafEntry for the whole branch (option b). This harvests the largest\n * shaveable pieces from within the un-shaveable parent. A GlueLeafEntry is\n * only emitted for AtomLeaf nodes that fail (we cannot recurse further).\n * Per DEC-V2-GLUE-AWARE-SHAVE-001, the parent does NOT emit a separate\n * GlueLeafEntry — that would overlap with its children's entries.\n * Rationale: option (a) maximizes the fraction of source that becomes\n * registry-eligible atoms. Option (b) (swallow everything) wastes shaveable\n * code inside un-shaveable functions.\n *\n * 3. DEFAULT MODE: shaveMode defaults to 'strict' for backward compatibility.\n * New callers should pass shaveMode:'glue-aware'. The rollout strategy is:\n * existing test suite passes unchanged (strict mode), new glue-aware tests\n * use the explicit option. When the compile pipeline is updated (L3), the\n * universalize() entry point will flip the default to 'glue-aware'.\n *\n * 4. DETERMINISM GUARANTEE: The slicer is deterministic because:\n * (a) validateStrictSubset is a pure function of source text (same input →\n * same ValidationResult);\n * (b) the RecursionTree is immutable and DFS traversal order is fixed;\n * (c) GlueLeafEntry.reason is derived from the first validation error\n * message, which is deterministic for a given source.\n * Re-running slice() on the same tree + same mode produces a byte-identical\n * SlicePlan.\n *\n * 5. SACRED PRACTICE #5 PRESERVED: GlueLeafEntry is for \"shaveable in principle\n * but not by this IR subset\" cases. Genuinely malformed AST (e.g. unparseable\n * source that makes ts-morph throw) still propagates as an error — glue-emit\n * is not a blanket exception handler.\n *\n * @see DEC-V2-GLUE-AWARE-SHAVE-001 (architectural decision)\n */\n\nimport type { BlockMerkleRoot, CanonicalAstHash } from \"@yakcc/contracts\";\nimport { validateStrictSubset } from \"@yakcc/ir\";\nimport { Project, ScriptKind } from \"ts-morph\";\nimport type { IntentCard } from \"../intent/types.js\";\nimport type { ShaveRegistryView } from \"../types.js\";\nimport type {\n BranchNode,\n ForeignLeafEntry,\n GlueLeafEntry,\n NovelGlueEntry,\n PointerEntry,\n RecursionNode,\n RecursionTree,\n SlicePlan,\n SlicePlanEntry,\n} from \"./types.js\";\nimport { rankCluster } from \"./variance-rank.js\";\n\n// ---------------------------------------------------------------------------\n// Foreign-classification constants (single source of truth — L3 forbidden\n// shortcut: no hardcoding of these strings at multiple sites)\n// ---------------------------------------------------------------------------\n\n/**\n * Prefix for Node.js built-in modules using the node: protocol.\n * Any specifier starting with this prefix is classified as foreign.\n */\nconst NODE_BUILTIN_PREFIX = \"node:\";\n\n/**\n * Prefix for workspace-internal packages.\n * Any specifier starting with this prefix is NOT classified as foreign —\n * it is treated as local workspace code.\n */\nconst WORKSPACE_PREFIX = \"@yakcc/\";\n\n// ---------------------------------------------------------------------------\n// classifyForeign: pure predicate (L3-I2 — no registry I/O)\n// ---------------------------------------------------------------------------\n\n/**\n * Parse `source` as TypeScript and return a ForeignLeafEntry for each binding\n * imported from a foreign (non-workspace, non-relative) static import declaration.\n *\n * Returns an empty array when the source is not a foreign import declaration\n * (e.g. it is a relative import, a workspace import, a type-only import,\n * a dynamic import, or any other statement kind).\n *\n * Authority invariant L3-I2: this function MUST NOT call any registry method.\n * It is a pure structural predicate over source text only.\n *\n * Skips:\n * - `import type { X }` — type-only erasure, no runtime import\n * - `import { X } from './local'` — relative imports\n * - `import { X } from '@yakcc/pkg'` — workspace imports\n * - `await import('x')` — dynamic imports (L3 defers these; falls through\n * to NovelGlueEntry as per test 7)\n *\n * @see DEC-V2-FOREIGN-BLOCK-SCHEMA-001\n */\nexport function classifyForeign(source: string): ForeignLeafEntry[] {\n // Use an in-memory Project so this function is pure of filesystem I/O.\n // skipAddingFilesFromTsConfig avoids slow tsconfig discovery.\n const project = new Project({\n useInMemoryFileSystem: true,\n skipAddingFilesFromTsConfig: true,\n compilerOptions: { allowJs: false, noEmit: true },\n });\n\n const sf = project.createSourceFile(\"__classify__.ts\", source, {\n scriptKind: ScriptKind.TS,\n });\n\n const entries: ForeignLeafEntry[] = [];\n\n for (const decl of sf.getImportDeclarations()) {\n // Skip type-only imports — they are erased at compile time and carry no\n // runtime dependency. (Test 4: `import type { X }` must NOT yield a\n // ForeignLeafEntry.)\n if (decl.isTypeOnly()) continue;\n\n const specifier = decl.getModuleSpecifierValue();\n\n // Skip relative imports (./foo, ../bar). (Test 5)\n if (specifier.startsWith(\".\")) continue;\n\n // Skip workspace imports (@yakcc/...). (Test 6)\n if (specifier.startsWith(WORKSPACE_PREFIX)) continue;\n\n // All remaining specifiers are candidates for foreign classification:\n // - node: built-ins (node:fs, node:path, etc.)\n // - third-party npm packages (sqlite-vec, ts-morph, etc.)\n // A bare package name not starting with '.' or WORKSPACE_PREFIX is foreign.\n // The node: prefix is checked for completeness, but any non-relative,\n // non-workspace specifier (including bare names) is classified foreign.\n const isForeignSpecifier =\n specifier.startsWith(NODE_BUILTIN_PREFIX) || !specifier.startsWith(\"@yakcc/\");\n\n if (!isForeignSpecifier) continue;\n\n const file = \"__classify__.ts\";\n const pos = decl.getStart();\n const lineAndCol = sf.getLineAndColumnAtPos(pos);\n const sourceLoc = {\n file,\n line: lineAndCol.line,\n column: lineAndCol.column,\n };\n\n // Namespace imports: `import * as ns from 'pkg'`\n const namespaceImport = decl.getNamespaceImport();\n if (namespaceImport !== undefined) {\n entries.push({\n kind: \"foreign-leaf\",\n pkg: specifier,\n export: \"*\",\n alias: namespaceImport.getText(),\n sourceLoc,\n });\n continue;\n }\n\n // Default import: `import Foo from 'pkg'`\n const defaultImport = decl.getDefaultImport();\n if (defaultImport !== undefined) {\n entries.push({\n kind: \"foreign-leaf\",\n pkg: specifier,\n export: \"default\",\n alias: defaultImport.getText(),\n sourceLoc,\n });\n // Named imports may coexist with default; fall through.\n }\n\n // Named imports: `import { A, B as C } from 'pkg'`\n for (const named of decl.getNamedImports()) {\n const exportedName = named.getName();\n const aliasNode = named.getAliasNode();\n const alias = aliasNode !== undefined ? aliasNode.getText() : undefined;\n entries.push({\n kind: \"foreign-leaf\",\n pkg: specifier,\n export: exportedName,\n // alias is only set when the local name differs from the export name\n alias: alias !== undefined && alias !== exportedName ? alias : undefined,\n sourceLoc,\n });\n }\n\n // Side-effect-only import: `import 'pkg'` (no bindings)\n if (\n defaultImport === undefined &&\n namespaceImport === undefined &&\n decl.getNamedImports().length === 0\n ) {\n entries.push({\n kind: \"foreign-leaf\",\n pkg: specifier,\n export: \"side-effect\",\n sourceLoc,\n });\n }\n }\n\n return entries;\n}\n\n// ---------------------------------------------------------------------------\n// Slicer options\n// ---------------------------------------------------------------------------\n\n/**\n * Options for the slice() function.\n *\n * @see DEC-V2-SLICER-SEARCH-001 (mode flag rollout strategy)\n */\nexport interface SliceOptions {\n /**\n * Controls how the slicer handles nodes that are not in the registry.\n *\n * - `'strict'` (default): Legacy behavior. Unmatched atoms become NovelGlueEntry\n * regardless of whether they pass the IR strict-subset predicate. This is the\n * pre-L2 behavior; all existing callers implicitly use this mode.\n *\n * - `'glue-aware'`: New behavior introduced by L2. The slicer applies the IR\n * strict-subset predicate per-subgraph. Nodes that pass become NovelGlueEntry\n * (shaveable atoms); nodes that fail become GlueLeafEntry (verbatim, project-local).\n * BranchNodes that fail recurse into children to harvest maximal shaveable\n * sub-subgraphs (option a per DEC-V2-SLICER-SEARCH-001).\n *\n * The default is `'strict'` for backward compatibility. New callers (e.g. the\n * compile pipeline after L3 lands) should use `'glue-aware'`.\n *\n * @see DEC-V2-SLICER-SEARCH-001\n * @see DEC-V2-GLUE-AWARE-SHAVE-001\n */\n readonly shaveMode?: \"strict\" | \"glue-aware\";\n\n /**\n * The extracted intent card for the candidate being sliced.\n *\n * When provided along with a registry that supports `getBlock`, the slicer\n * applies variance ranking at multi-candidate cluster sites: any\n * `findByCanonicalAstHash` result with >1 match is ranked by\n * `@yakcc/variance.varianceScore` rather than silently picking `matches[0]`.\n *\n * When absent (or when the registry lacks `getBlock`), the slicer falls back\n * to the legacy behavior (first match wins) and `PointerEntry.varianceScores`\n * is always undefined.\n *\n * @see DEC-VARIANCE-WIRING-001\n */\n readonly intentCard?: IntentCard | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Internal accumulator (mutable, local to one slice() call)\n// ---------------------------------------------------------------------------\n\ninterface SliceAccumulator {\n entries: SlicePlanEntry[];\n /** Tracks seen canonicalAstHash values to deduplicate matchedPrimitives. */\n matchedPrimitivesMap: Map<\n CanonicalAstHash,\n { canonicalAstHash: CanonicalAstHash; merkleRoot: BlockMerkleRoot }\n >;\n pointerBytes: number;\n novelGlueBytes: number;\n /** Bytes in GlueLeafEntry regions. Non-zero under glue-aware mode. */\n glueBytes: number;\n}\n\n// ---------------------------------------------------------------------------\n// Internal DFS walker — strict mode\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively walk `node` in DFS order (strict mode), querying the registry\n * and appending entries to `acc`. Behavior is identical to the pre-L2 slicer:\n * BranchNodes that match the registry collapse into PointerEntry; unmatched\n * AtomLeaves are checked for foreign imports before falling through to\n * NovelGlueEntry; unmatched BranchNodes descend into children.\n *\n * When `intentCard` is provided and the registry returns >1 candidate for a\n * given canonicalAstHash, variance ranking is applied via `rankCluster()` to\n * select the best-matching candidate. The selected merkleRoot and all ranked\n * scores are surfaced on `PointerEntry.varianceScores`.\n *\n * This function is the backward-compatible path; it never emits GlueLeafEntry.\n */\nasync function walkNodeStrict(\n node: RecursionNode,\n registry: Pick<ShaveRegistryView, \"findByCanonicalAstHash\" | \"getBlock\">,\n acc: SliceAccumulator,\n intentCard: import(\"../intent/types.js\").IntentCard | undefined,\n): Promise<void> {\n // Query registry — degrade gracefully when findByCanonicalAstHash is absent.\n const matches = await registry.findByCanonicalAstHash?.(node.canonicalAstHash);\n\n // Determine the winning match and optional variance scores.\n let firstMatch: BlockMerkleRoot | undefined;\n let varianceScores: readonly import(\"./variance-rank.js\").VarianceScoreEntry[] | undefined;\n\n if (matches !== undefined && matches.length > 0) {\n if (matches.length > 1 && intentCard !== undefined) {\n // Multi-candidate cluster: apply variance ranking (DEC-VARIANCE-WIRING-001).\n // rankCluster sorts descending by score; first entry is the winner.\n const ranked = await rankCluster(intentCard, node.canonicalAstHash, matches, registry);\n firstMatch = ranked[0]?.merkleRoot;\n varianceScores = ranked;\n } else {\n // Single candidate (or no intentCard): legacy first-match semantics.\n firstMatch = matches[0];\n }\n }\n\n if (firstMatch !== undefined) {\n // Registry match: collapse this node (and any subtree) to a PointerEntry.\n // Descendants are NOT visited — the whole subtree is replaced by the pointer.\n const entry: PointerEntry = {\n kind: \"pointer\",\n sourceRange: node.sourceRange,\n merkleRoot: firstMatch,\n canonicalAstHash: node.canonicalAstHash,\n matchedBy: \"canonical_ast_hash\",\n // varianceScores is only set for multi-candidate sites where ranking ran.\n ...(varianceScores !== undefined && { varianceScores }),\n };\n acc.entries.push(entry);\n acc.pointerBytes += node.sourceRange.end - node.sourceRange.start;\n\n // Deduplicate matchedPrimitives by canonicalAstHash (first-seen order).\n if (!acc.matchedPrimitivesMap.has(node.canonicalAstHash)) {\n acc.matchedPrimitivesMap.set(node.canonicalAstHash, {\n canonicalAstHash: node.canonicalAstHash,\n merkleRoot: firstMatch,\n });\n }\n return;\n }\n\n // No registry match — behaviour depends on node kind.\n if (node.kind === \"atom\") {\n // Attempt foreign-import classification BEFORE emitting NovelGlueEntry.\n // This is the L3 insertion point: registry → foreign → novel-glue.\n // classifyForeign is pure (L3-I2): it performs no registry I/O.\n const foreignEntries = classifyForeign(node.source);\n if (foreignEntries.length > 0) {\n // The atom is a foreign import declaration — push one ForeignLeafEntry\n // per binding. No NovelGlueEntry is emitted; no byte accounting is\n // needed (foreign deps are not synthesized).\n for (const fe of foreignEntries) {\n acc.entries.push(fe);\n }\n return;\n }\n\n // Unmatched AtomLeaf with no foreign classification → NovelGlueEntry.\n // intentCard is omitted here because intent wiring is NOT slicer's\n // responsibility — slicer's job is tree-shaping. The intentCard field is\n // attached post-slice in packages/shave/src/index.ts:universalize() (closed\n // by WI-031, see DEC-UNIVERSALIZE-MULTI-LEAF-INTENT-001 in that file).\n const entry: NovelGlueEntry = {\n kind: \"novel-glue\",\n sourceRange: node.sourceRange,\n source: node.source,\n canonicalAstHash: node.canonicalAstHash,\n // intentCard wired post-slice (see comment above and index.ts).\n };\n acc.entries.push(entry);\n acc.novelGlueBytes += node.sourceRange.end - node.sourceRange.start;\n } else {\n // Unmatched BranchNode → descend into children in DFS left-to-right order.\n // The branch node itself does not produce an entry; only leaf nodes and\n // matched subtrees produce entries, preserving the non-overlapping-regions\n // invariant for SlicePlan.entries.\n const branch = node as BranchNode;\n for (const child of branch.children) {\n await walkNodeStrict(child, registry, acc, intentCard);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal DFS walker — glue-aware mode\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively walk `node` in DFS order (glue-aware mode). Applies the IR\n * strict-subset predicate per-subgraph to find maximal shaveable subgraphs.\n *\n * Algorithm (DEC-V2-SLICER-SEARCH-001):\n * 1. Check registry first (same as strict mode). Registry match → PointerEntry,\n * subtree collapsed. This preserves the \"registry match takes priority\" rule.\n * 2. For unmatched nodes: apply validateStrictSubset to the node's source.\n * - If passes: the node is a maximal shaveable subgraph.\n * - AtomLeaf: foreign check → ForeignLeafEntry or NovelGlueEntry.\n * - BranchNode: emit NovelGlueEntry for the branch (don't recurse — the\n * whole branch is shaveable as a unit).\n * - If fails: the node is un-shaveable.\n * - AtomLeaf: emit GlueLeafEntry (verbatim, project-local).\n * - BranchNode: recurse into children (option a — find maximal pieces).\n * The branch itself does NOT emit a GlueLeafEntry; only leaf-level\n * un-shaveable nodes emit GlueLeafEntry to avoid overlapping entries.\n *\n * @see DEC-V2-SLICER-SEARCH-001\n * @see DEC-V2-GLUE-AWARE-SHAVE-001\n */\nasync function walkNodeGlueAware(\n node: RecursionNode,\n registry: Pick<ShaveRegistryView, \"findByCanonicalAstHash\" | \"getBlock\">,\n acc: SliceAccumulator,\n intentCard: import(\"../intent/types.js\").IntentCard | undefined,\n): Promise<void> {\n // Step 1: Registry lookup (same priority as strict mode).\n // When >1 candidate is returned and intentCard is available, variance ranking\n // selects the best match (DEC-VARIANCE-WIRING-001).\n const matches = await registry.findByCanonicalAstHash?.(node.canonicalAstHash);\n\n let firstMatch: BlockMerkleRoot | undefined;\n let varianceScores: readonly import(\"./variance-rank.js\").VarianceScoreEntry[] | undefined;\n\n if (matches !== undefined && matches.length > 0) {\n if (matches.length > 1 && intentCard !== undefined) {\n const ranked = await rankCluster(intentCard, node.canonicalAstHash, matches, registry);\n firstMatch = ranked[0]?.merkleRoot;\n varianceScores = ranked;\n } else {\n firstMatch = matches[0];\n }\n }\n\n if (firstMatch !== undefined) {\n const entry: PointerEntry = {\n kind: \"pointer\",\n sourceRange: node.sourceRange,\n merkleRoot: firstMatch,\n canonicalAstHash: node.canonicalAstHash,\n matchedBy: \"canonical_ast_hash\",\n ...(varianceScores !== undefined && { varianceScores }),\n };\n acc.entries.push(entry);\n acc.pointerBytes += node.sourceRange.end - node.sourceRange.start;\n\n if (!acc.matchedPrimitivesMap.has(node.canonicalAstHash)) {\n acc.matchedPrimitivesMap.set(node.canonicalAstHash, {\n canonicalAstHash: node.canonicalAstHash,\n merkleRoot: firstMatch,\n });\n }\n return;\n }\n\n // Step 2a: Foreign-import classification for AtomLeaf nodes runs BEFORE the\n // strict-subset predicate. This preserves the ordering: registry → foreign →\n // strict-subset → glue. Foreign imports (e.g. `import { readFileSync } from\n // 'node:fs'`) fail the strict-subset predicate (no-untyped-imports fires in\n // the in-memory project context), so we must classify them as foreign first.\n if (node.kind === \"atom\") {\n const foreignEntries = classifyForeign(node.source);\n if (foreignEntries.length > 0) {\n for (const fe of foreignEntries) {\n acc.entries.push(fe);\n }\n return;\n }\n }\n\n // Step 2b: Strict-subset predicate — applied per-subgraph.\n const validation = validateStrictSubset(node.source);\n\n if (validation.ok) {\n // Node passes the strict-subset predicate → maximal shaveable subgraph.\n if (node.kind === \"atom\") {\n // Shaveable atom → NovelGlueEntry.\n const entry: NovelGlueEntry = {\n kind: \"novel-glue\",\n sourceRange: node.sourceRange,\n source: node.source,\n canonicalAstHash: node.canonicalAstHash,\n };\n acc.entries.push(entry);\n acc.novelGlueBytes += node.sourceRange.end - node.sourceRange.start;\n } else {\n // Shaveable BranchNode: the whole branch is a maximal shaveable unit.\n // Emit as NovelGlueEntry and do NOT recurse — recursing would fragment\n // the shaveable tree into smaller pieces (violating maximal-subgraph discipline).\n //\n // NOTE: BranchNode.source is the full branch text. This branch passed\n // the strict-subset predicate, so it's shaveable as a whole unit.\n // We intentionally do not apply foreign-import classification here —\n // branch nodes are composite and their import content is handled at the\n // AtomLeaf level when we do recurse (which we're not doing here).\n const branch = node as BranchNode;\n const entry: NovelGlueEntry = {\n kind: \"novel-glue\",\n sourceRange: branch.sourceRange,\n source: branch.source,\n canonicalAstHash: branch.canonicalAstHash,\n };\n acc.entries.push(entry);\n acc.novelGlueBytes += branch.sourceRange.end - branch.sourceRange.start;\n }\n return;\n }\n\n // Node fails the strict-subset predicate.\n // Build a human-readable reason from the first validation error.\n const firstError =\n !validation.ok && validation.errors.length > 0 ? validation.errors[0] : undefined;\n const reason =\n firstError !== undefined\n ? `${firstError.rule}: ${firstError.message}`\n : \"strict-subset-failure\";\n\n if (node.kind === \"atom\") {\n // AtomLeaf fails → emit GlueLeafEntry (we cannot recurse further).\n // Sacred Practice #5: source is preserved verbatim; we do NOT transform it.\n const entry: GlueLeafEntry = {\n kind: \"glue\",\n source: node.source,\n canonicalAstHash: node.canonicalAstHash,\n reason,\n };\n acc.entries.push(entry);\n acc.glueBytes += node.sourceRange.end - node.sourceRange.start;\n } else {\n // BranchNode fails → recurse into children (option a: find maximal shaveable pieces).\n // The branch itself does NOT emit a GlueLeafEntry — that would create entries\n // overlapping with its children's entries, violating the non-overlapping invariant.\n // Instead, each child is visited independently and classified by its own predicate.\n const branch = node as BranchNode;\n for (const child of branch.children) {\n await walkNodeGlueAware(child, registry, acc, intentCard);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API: slice()\n// ---------------------------------------------------------------------------\n\n/**\n * Slice a RecursionTree into a SlicePlan by querying the registry for each\n * node by canonicalAstHash.\n *\n * Nodes that match the registry are collapsed into PointerEntry records —\n * no synthesis needed for those subtrees. Unmatched AtomLeaf nodes that\n * contain static foreign imports are emitted as ForeignLeafEntry records —\n * one per imported binding. All other unmatched AtomLeaf nodes become\n * NovelGlueEntry records — source code that must be synthesized as novel glue.\n *\n * Under `shaveMode: 'glue-aware'` (L2), the IR strict-subset predicate is\n * additionally applied per-subgraph. Nodes that fail the predicate become\n * GlueLeafEntry records (verbatim source, project-local, not in the registry).\n * Shaveable children of un-shaveable BranchNodes are emitted as atoms (option a\n * per DEC-V2-SLICER-SEARCH-001 — maximal-subgraph discipline).\n *\n * The returned SlicePlan contains:\n * - `entries`: PointerEntry | ForeignLeafEntry | NovelGlueEntry | GlueLeafEntry\n * in DFS order.\n * - `matchedPrimitives`: deduplicated (canonicalAstHash, merkleRoot) pairs\n * for every PointerEntry, in first-seen order.\n * - `sourceBytesByKind`: byte sums for pointer vs. novel-glue vs. glue regions.\n * ForeignLeafEntry bytes are not counted in any bucket.\n *\n * When `registry.findByCanonicalAstHash` is undefined, all nodes are treated\n * as unmatched and foreign-import classification still runs — AtomLeaves that\n * are foreign imports emit ForeignLeafEntry; others emit NovelGlueEntry (strict)\n * or NovelGlueEntry/GlueLeafEntry (glue-aware).\n *\n * @param tree - The RecursionTree produced by decompose().\n * @param registry - Registry view; findByCanonicalAstHash is optional.\n * @param options - Optional slicer options; see SliceOptions.\n *\n * @see DEC-V2-SLICER-SEARCH-001\n * @see DEC-V2-GLUE-AWARE-SHAVE-001\n */\nexport async function slice(\n tree: RecursionTree,\n registry: Pick<ShaveRegistryView, \"findByCanonicalAstHash\" | \"getBlock\">,\n options?: SliceOptions,\n): Promise<SlicePlan> {\n const acc: SliceAccumulator = {\n entries: [],\n matchedPrimitivesMap: new Map(),\n pointerBytes: 0,\n novelGlueBytes: 0,\n glueBytes: 0,\n };\n\n const mode = options?.shaveMode ?? \"strict\";\n const intentCard = options?.intentCard;\n\n if (mode === \"glue-aware\") {\n await walkNodeGlueAware(tree.root, registry, acc, intentCard);\n } else {\n await walkNodeStrict(tree.root, registry, acc, intentCard);\n }\n\n return {\n entries: acc.entries,\n matchedPrimitives: [...acc.matchedPrimitivesMap.values()],\n sourceBytesByKind: {\n pointer: acc.pointerBytes,\n novelGlue: acc.novelGlueBytes,\n glue: acc.glueBytes,\n },\n };\n}\n","// SPDX-License-Identifier: MIT\n/**\n * @decision DEC-VARIANCE-WIRING-001\n * title: In-slice cluster ranking via @yakcc/variance at the registry-match decision site\n * status: decided\n * rationale:\n * When `registry.findByCanonicalAstHash()` returns more than one BlockMerkleRoot\n * for a given canonicalAstHash, the slicer historically picked `matches[0]` silently.\n * This module replaces that silent drop with variance-scored cluster ranking:\n *\n * Seam choice (Option B — in-slice at the cluster site):\n * The cluster-selection decision happens inside slice() at the registry-match step.\n * Option A (post-decompose pre-slice) was rejected: decompose() doesn't surface\n * clusters; building a parallel registry-walk authority violates Sacred Practice #12.\n * Option C (post-slice annotation) was rejected: it doesn't fix the silent-drop bug.\n *\n * Translation step:\n * - Candidate side: the caller's IntentCard is mapped to SpecYak via `specFromIntent`\n * (DEC-VAR-004: translation lives in callers, variance consumes SpecYak only).\n * - Registry side: `BlockTripletRow.specCanonicalBytes` (canonical JSON bytes) is\n * decoded via `validateSpecYak(JSON.parse(TextDecoder.decode(bytes)))`.\n *\n * Tiebreaker ordering:\n * Highest `varianceScore.score` wins. Ties (|a - b| < 1e-9) fall back to the\n * original first-returned order from `findByCanonicalAstHash` (deterministic,\n * preserving the existing implicit ordering).\n *\n * Loud-fail on malformed registry rows:\n * If `specCanonicalBytes` fails to decode into a valid SpecYak for any candidate,\n * a `VarianceCandidateMalformedError` is thrown with the failing merkleRoot in the\n * message (Sacred Practice #5 — no silent fallback).\n *\n * Single-candidate fast path:\n * When `matches.length === 1`, `rankCluster()` is NOT called. The slicer proceeds\n * as before: zero overhead, no `getBlock` calls, no `varianceScores` on the entry.\n * Only multi-candidate sites allocate the scores array.\n *\n * Weights:\n * Always `DIMENSION_WEIGHTS` from `@yakcc/variance` (DEC-VAR-005 / -003: per-query\n * weight overrides are out of scope; any weight change requires its own DEC).\n */\n\nimport { validateSpecYak } from \"@yakcc/contracts\";\nimport type { BlockMerkleRoot } from \"@yakcc/contracts\";\nimport { varianceScore } from \"@yakcc/variance\";\nimport type { DimensionScores } from \"@yakcc/variance\";\nimport type { IntentCard } from \"../intent/types.js\";\nimport { specFromIntent } from \"../persist/spec-from-intent.js\";\nimport type { ShaveRegistryView } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * One variance-scored candidate in a multi-match cluster.\n * `score` is the weighted composite variance score in [0, 1].\n * `dimensions` is the per-dimension breakdown (security, behavioral, etc.).\n */\nexport interface VarianceScoreEntry {\n readonly merkleRoot: BlockMerkleRoot;\n readonly score: number;\n readonly dimensions: DimensionScores;\n}\n\n// ---------------------------------------------------------------------------\n// Error type (Sacred Practice #5 — loud failure for malformed registry rows)\n// ---------------------------------------------------------------------------\n\n/**\n * Thrown when a candidate BlockTripletRow's `specCanonicalBytes` cannot be\n * decoded into a valid SpecYak. This indicates a malformed or corrupted\n * registry row — not a wiring failure.\n *\n * The failing `merkleRoot` is included in the message so callers and logs\n * can identify the specific row that is problematic.\n */\nexport class VarianceCandidateMalformedError extends Error {\n readonly merkleRoot: BlockMerkleRoot;\n\n constructor(merkleRoot: BlockMerkleRoot, cause: unknown) {\n super(\n `variance-rank: failed to decode specCanonicalBytes for merkleRoot=\"${merkleRoot}\": ${String(cause)}`,\n );\n this.name = \"VarianceCandidateMalformedError\";\n this.merkleRoot = merkleRoot;\n // Preserve the original decode/validation error as cause.\n if (cause instanceof Error) {\n this.cause = cause;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal decoder\n// ---------------------------------------------------------------------------\n\nconst _decoder = new TextDecoder();\n\n// ---------------------------------------------------------------------------\n// Public API: rankCluster\n// ---------------------------------------------------------------------------\n\n/**\n * Rank a cluster of candidate registry matches by their variance score against\n * the caller's intent card.\n *\n * Called only when `matches.length > 1`. The single-candidate fast path is\n * handled by the callers (walkNodeStrict / walkNodeGlueAware) — this function\n * always receives at least 2 merkleRoots and always returns at least 2 entries.\n *\n * Algorithm:\n * 1. Map `intentCard` → SpecYak (canonical side) via `specFromIntent`.\n * 2. For each merkleRoot, fetch the BlockTripletRow from the registry\n * (`registry.getBlock`). If the row is missing, treat it as malformed\n * (loud-fail with VarianceCandidateMalformedError).\n * 3. Decode `specCanonicalBytes` → SpecYak (candidate side) via\n * `JSON.parse(decoder.decode(bytes))` + `validateSpecYak`.\n * On failure: throw VarianceCandidateMalformedError.\n * 4. Call `varianceScore(canonicalSpec, candidateSpec)` for each candidate.\n * 5. Sort descending by score. Ties (|a - b| < 1e-9) preserve the original\n * order from `matches` (stable sort input order is the original order).\n *\n * @param intentCard - The extracted intent card for the candidate being sliced.\n * @param canonicalAstHash - The canonical AST hash; used as the hash suffix in `specFromIntent`.\n * @param matchMerkleRoots - The merkleRoots returned by `findByCanonicalAstHash`.\n * Must have length >= 2 (single-match fast path is caller's responsibility).\n * @param registry - Registry view used to fetch BlockTripletRow per candidate.\n * @returns Array of VarianceScoreEntry sorted by descending score (highest first).\n * The first entry's merkleRoot is the one the slicer should use.\n * @throws VarianceCandidateMalformedError if any candidate row is missing or its\n * specCanonicalBytes cannot be validated as SpecYak.\n */\nexport async function rankCluster(\n intentCard: IntentCard,\n canonicalAstHash: string,\n matchMerkleRoots: readonly BlockMerkleRoot[],\n registry: Pick<ShaveRegistryView, \"getBlock\">,\n): Promise<readonly VarianceScoreEntry[]> {\n // Step 1: Translate IntentCard → SpecYak (canonical side).\n // specFromIntent is the authority for this translation (DEC-ATOM-PERSIST-001).\n const canonicalSpec = specFromIntent(intentCard, canonicalAstHash);\n\n // Step 2-4: Fetch + decode + score each candidate.\n // We collect scores in input order so the stable sort preserves original order for ties.\n const scored: VarianceScoreEntry[] = [];\n for (const merkleRoot of matchMerkleRoots) {\n // Fetch the BlockTripletRow — missing row is treated as malformed (Sacred Practice #5).\n const row = await registry.getBlock(merkleRoot);\n if (row === undefined) {\n throw new VarianceCandidateMalformedError(\n merkleRoot,\n new Error(\"getBlock returned undefined — row missing from registry\"),\n );\n }\n\n // Decode specCanonicalBytes → SpecYak.\n // canonical JSON bytes via TextDecoder → JSON.parse → validateSpecYak.\n let candidateSpec: ReturnType<typeof validateSpecYak>;\n try {\n const jsonText = _decoder.decode(row.specCanonicalBytes);\n const parsed: unknown = JSON.parse(jsonText);\n candidateSpec = validateSpecYak(parsed);\n } catch (err) {\n throw new VarianceCandidateMalformedError(merkleRoot, err);\n }\n\n // Score: varianceScore uses DIMENSION_WEIGHTS (governance-locked per DEC-VAR-003/-005).\n const result = varianceScore(canonicalSpec, candidateSpec);\n\n scored.push({\n merkleRoot,\n score: result.score,\n dimensions: result.dimensions,\n });\n }\n\n // Step 5: Sort descending by score.\n // JavaScript's Array.prototype.sort is stable (ES2019+, V8/Node ≥12), so\n // entries with equal scores (|a - b| < 1e-9) preserve their original input\n // order — which is the original `matches` order from `findByCanonicalAstHash`.\n // This satisfies the deterministic-tiebreaker invariant (T4).\n scored.sort((a, b) => {\n const diff = b.score - a.score;\n // Treat differences smaller than 1e-9 as a tie (preserve original order).\n if (Math.abs(diff) < 1e-9) return 0;\n return diff;\n });\n\n return scored;\n}\n","// SPDX-License-Identifier: MIT\n// ---------------------------------------------------------------------------\n// @decision DEC-VAR-001: 7-dimension framing reconciled to 5 canonical weights\n// Status: proposed\n// Rationale: The original planning surface described 7 qualitative dimensions.\n// MASTER_PLAN.md §WI-011 reduces these to 5 measurable, weighted dimensions\n// that map cleanly onto SpecYak fields. The remaining 2 were merged into\n// \"behavioral\" to avoid phantom precision in weight tuning.\n// ---------------------------------------------------------------------------\n// @decision DEC-VAR-002: Star-topology rules — safety = ∩, behavioral = majority-vote, capability = ∪.\n// Status: proposed\n// Rationale: Safety must shrink to the intersection so no merged contract\n// drops a precondition or loses a CWE clearance. Behavioral adopts majority\n// vote so dominant correct postconditions survive without requiring unanimity.\n// Capability takes the union so no required effect is silently elided.\n// Empty-input is refused (RangeError) because zero-contributor merge is\n// undefined under star-topology rules.\n// ---------------------------------------------------------------------------\n// @decision DEC-VAR-003: CWE_474_FAMILY is canonical; updates require governance review.\n// Status: proposed\n// Rationale: The CWE family table is a security-sensitive policy surface.\n// Unilateral edits to the detect predicates could silently weaken security\n// scoring. Updates must go through the authority registry and decision log.\n// ---------------------------------------------------------------------------\n// @decision DEC-VAR-004: Variance consumes SpecYak only; IntentCard → SpecYak translation lives in callers.\n// Status: proposed\n// Rationale: Keeping variance pure of IntentCard prevents a circular\n// dependency on @yakcc/shave and maintains the leaf-package invariant.\n// Callers (WI-012, WI-014) own the translation step.\n// ---------------------------------------------------------------------------\n// @decision DEC-VAR-005: At v0.7, behavior prose is weight-0 in behavioral dimension.\n// Status: proposed\n// Rationale: Prose comparison requires semantic similarity that cannot be\n// computed without an embedding model. Variance is a pure-function leaf with\n// no LLM/I/O access. Postcondition Jaccard provides a structural proxy.\n// Behavior field is preserved in applyContractDesignRules tie-break output.\n// ---------------------------------------------------------------------------\n\nimport type { SpecYak } from \"@yakcc/contracts\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport type VarianceDimension =\n | \"security\"\n | \"behavioral\"\n | \"error_handling\"\n | \"performance\"\n | \"interface\";\n\nexport interface DimensionScores {\n readonly security: number;\n readonly behavioral: number;\n readonly error_handling: number;\n readonly performance: number;\n readonly interface: number;\n}\n\nexport const DIMENSION_WEIGHTS: Readonly<Record<VarianceDimension, number>> = {\n security: 0.35,\n behavioral: 0.25,\n error_handling: 0.2,\n performance: 0.1,\n interface: 0.1,\n};\n\nexport interface VarianceOptions {\n readonly weights?: Readonly<Record<VarianceDimension, number>>;\n}\n\nexport interface VarianceResult {\n readonly score: number;\n readonly dimensions: DimensionScores;\n readonly weights: Readonly<Record<VarianceDimension, number>>;\n}\n\nexport type CweId = `CWE-${number}`;\n\nexport interface CwePattern {\n readonly cwe: CweId;\n readonly title: string;\n readonly detect: (spec: SpecYak) => boolean;\n}\n\nexport interface CweMapping {\n readonly cwesPresent: readonly CweId[];\n readonly cwesClear: readonly CweId[];\n}\n\nexport interface MergedContract {\n readonly safety: {\n readonly preconditions: readonly string[];\n readonly invariants: readonly string[];\n readonly cweClear: readonly CweId[];\n };\n readonly behavioral: {\n readonly postconditions: readonly string[];\n readonly behavior?: string | undefined;\n };\n readonly capability: {\n readonly effects: readonly string[];\n };\n readonly source: {\n readonly contributorCount: number;\n readonly tieBreaks: readonly TieBreakRecord[];\n };\n}\n\nexport interface TieBreakRecord {\n readonly field: string;\n readonly candidates: readonly string[];\n readonly resolution: \"first_lexicographic\" | \"all_kept\";\n}\n\n// ---------------------------------------------------------------------------\n// Module-level weight invariant (asserted at load; never silenced)\n// ---------------------------------------------------------------------------\n\nconst __weightSum = Object.values(DIMENSION_WEIGHTS).reduce((a, b) => a + b, 0);\nif (Math.abs(__weightSum - 1.0) > 1e-9) {\n throw new Error(`DIMENSION_WEIGHTS must sum to 1.0 (got ${__weightSum})`);\n}\n\n// ---------------------------------------------------------------------------\n// Internal utilities\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize a string for Jaccard comparison:\n * lowercase, collapse whitespace, strip terminal punctuation.\n */\nfunction normalize(s: string): string {\n return s\n .toLowerCase()\n .replace(/\\s+/g, \" \")\n .trim()\n .replace(/[.;!?,]+$/, \"\");\n}\n\n/**\n * Jaccard similarity between two sets of strings.\n * Both-empty → 1.0 (perfect agreement on \"nothing declared\").\n */\nfunction jaccard(a: ReadonlySet<string>, b: ReadonlySet<string>): number {\n if (a.size === 0 && b.size === 0) return 1.0;\n let intersectionSize = 0;\n for (const item of a) {\n if (b.has(item)) intersectionSize++;\n }\n const unionSize = a.size + b.size - intersectionSize;\n return unionSize === 0 ? 1.0 : intersectionSize / unionSize;\n}\n\n// ---------------------------------------------------------------------------\n// CWE-474 family (DEC-VAR-003: canonical; governance review required for updates)\n// ---------------------------------------------------------------------------\n// Detection predicates are adapted to actual SpecYak fields (see spec-yak.ts):\n// - effects: readonly string[] — declared object-capability requirements\n// - preconditions: readonly string[] — input assertions\n// - postconditions: readonly string[] — output guarantees\n// - errorConditions?: ReadonlyArray<{description,errorType?}> — declared errors\n// - nonFunctional?.purity: string — \"pure\"|\"io\"|\"stateful\"|\"nondeterministic\"\n// CWE-474: Use of Function with Inconsistent Implementations —\n// detected when effects are empty yet purity is not \"pure\", indicating\n// implementation-inconsistency risk (I/O disguised as pure).\n// CWE-440: Expected Behavior Violation —\n// detected when postconditions are empty, i.e. no correctness guarantee declared.\n// CWE-573: Improper Following of Specification by Caller —\n// detected when preconditions are empty, implying the caller has no\n// documented constraints to follow.\n// CWE-684: Incorrect Provision of Specified Functionality —\n// detected when errorConditions are absent and preconditions are non-empty,\n// implying the block may reject inputs without documented error behavior.\n// CWE-710: Improper Adherence to Coding Standards —\n// detected when no nonFunctional properties are declared, which weakens\n// auditability and standard adherence.\n// ---------------------------------------------------------------------------\n\nexport const CWE_474_FAMILY: readonly CwePattern[] = [\n {\n cwe: \"CWE-474\",\n title: \"Use of Function with Inconsistent Implementations\",\n detect: (spec: SpecYak): boolean => {\n // Risk: effects are empty yet purity is not \"pure\", suggesting hidden I/O.\n const hasDeclaredEffects = spec.effects.length > 0;\n const claimsPure = spec.nonFunctional?.purity === \"pure\";\n // Present if: no effects declared AND not explicitly claiming purity.\n // A pure function with no effects is fine; an impure function with no\n // declared effects is a consistency risk.\n return !hasDeclaredEffects && !claimsPure;\n },\n },\n {\n cwe: \"CWE-440\",\n title: \"Expected Behavior Violation\",\n detect: (spec: SpecYak): boolean => {\n // Present if no postconditions are declared — no output guarantee exists.\n return spec.postconditions.length === 0;\n },\n },\n {\n cwe: \"CWE-573\",\n title: \"Improper Following of Specification by Caller\",\n detect: (spec: SpecYak): boolean => {\n // Present if no preconditions are declared — caller has no constraints.\n return spec.preconditions.length === 0;\n },\n },\n {\n cwe: \"CWE-684\",\n title: \"Incorrect Provision of Specified Functionality\",\n detect: (spec: SpecYak): boolean => {\n // Present if preconditions exist but no error behavior is documented.\n // The block can reject inputs but provides no documented error contract.\n const hasPreconditions = spec.preconditions.length > 0;\n const hasErrorConditions =\n spec.errorConditions !== undefined && spec.errorConditions.length > 0;\n return hasPreconditions && !hasErrorConditions;\n },\n },\n {\n cwe: \"CWE-710\",\n title: \"Improper Adherence to Coding Standards\",\n detect: (spec: SpecYak): boolean => {\n // Present if no nonFunctional properties are declared at all.\n return spec.nonFunctional === undefined;\n },\n },\n] as const;\n\n// ---------------------------------------------------------------------------\n// Per-dimension scorers\n// ---------------------------------------------------------------------------\n\n/**\n * Security dimension: CWE-474 family overlap between canonical and candidate.\n *\n * Formula:\n * present(s) = { c ∈ CWE_474_FAMILY : c.detect(s) }\n * clear(s) = CWE_474_FAMILY ∖ present(s)\n * security = (|present(c) ∩ present(d)| + |clear(c) ∩ clear(d)|) / |CWE_474_FAMILY|\n *\n * This measures agreement on which CWEs are present AND which are clear —\n * not absolute risk, but structural alignment between two specs.\n * (DEC-VAR-003: forbidden shortcut — must not score as 1 - present/total)\n */\nfunction scoreSecurity(canonical: SpecYak, candidate: SpecYak): number {\n let overlapPresent = 0;\n let overlapClear = 0;\n for (const pattern of CWE_474_FAMILY) {\n const inCanonical = pattern.detect(canonical);\n const inCandidate = pattern.detect(candidate);\n if (inCanonical && inCandidate) overlapPresent++;\n if (!inCanonical && !inCandidate) overlapClear++;\n }\n return (overlapPresent + overlapClear) / CWE_474_FAMILY.length;\n}\n\n/**\n * Behavioral dimension: Jaccard similarity over normalized postconditions.\n *\n * Behavior prose (SpecYak.behavior) is excluded per DEC-VAR-005:\n * prose comparison requires semantic embedding (LLM) which is forbidden in\n * this leaf package.\n *\n * Both-empty → 1.0 (both declare no output guarantees; fully aligned).\n */\nfunction scoreBehavioral(canonical: SpecYak, candidate: SpecYak): number {\n const a = new Set<string>(canonical.postconditions.map(normalize));\n const b = new Set<string>(candidate.postconditions.map(normalize));\n return jaccard(a, b);\n}\n\n/**\n * Error-handling dimension: Jaccard over normalized error descriptions,\n * with half-credit contribution from errorType.\n *\n * SpecYak.errorConditions is an optional v0-lift field. Policy when absent:\n * - Both absent → 1.0 (both declined to specify; fully aligned).\n * - One absent → 0.0 for that comparison side (one side declined the claim).\n *\n * When both present, score = 0.7 * jaccard(descriptions) + 0.3 * jaccard(types).\n * This weights description-level agreement more than type-label agreement.\n */\nfunction scoreErrorHandling(canonical: SpecYak, candidate: SpecYak): number {\n const cErrs = canonical.errorConditions;\n const dErrs = candidate.errorConditions;\n\n // Both absent: both declined to specify error behavior. Perfect alignment.\n if ((cErrs === undefined || cErrs.length === 0) && (dErrs === undefined || dErrs.length === 0)) {\n return 1.0;\n }\n\n // One absent: one side declined. Score 0.0 — no basis for overlap.\n if (cErrs === undefined || cErrs.length === 0) return 0.0;\n if (dErrs === undefined || dErrs.length === 0) return 0.0;\n\n type ErrorCond = { readonly description: string; readonly errorType?: string | undefined };\n const descA = new Set<string>(cErrs.map((e: ErrorCond) => normalize(e.description)));\n const descB = new Set<string>(dErrs.map((e: ErrorCond) => normalize(e.description)));\n const descScore = jaccard(descA, descB);\n\n const typeA = new Set<string>(\n cErrs\n .filter((e: ErrorCond) => e.errorType !== undefined)\n .map((e: ErrorCond) => normalize(e.errorType as string)),\n );\n const typeB = new Set<string>(\n dErrs\n .filter((e: ErrorCond) => e.errorType !== undefined)\n .map((e: ErrorCond) => normalize(e.errorType as string)),\n );\n const typeScore = jaccard(typeA, typeB);\n\n return 0.7 * descScore + 0.3 * typeScore;\n}\n\n/**\n * Performance dimension: structural comparison of nonFunctional time/space claims.\n *\n * Policy for absent fields:\n * - Both spec.nonFunctional absent → 1.0 (both declined; fully aligned).\n * - One spec.nonFunctional absent → that side contributes 0 to time and space\n * comparisons (one side declined to claim; the claim is unmatched).\n * - Within a present nonFunctional, absent time/space fields → 1.0 for that\n * sub-comparison (both declining that sub-claim is perfect alignment).\n *\n * Score = 0.5 * timeScore + 0.5 * spaceScore.\n */\nfunction scorePerformance(canonical: SpecYak, candidate: SpecYak): number {\n const cNF = canonical.nonFunctional;\n const dNF = candidate.nonFunctional;\n\n if (cNF === undefined && dNF === undefined) return 1.0;\n if (cNF === undefined || dNF === undefined) {\n // One side declined all non-functional claims. Both time and space are 0.\n return 0.0;\n }\n\n // Both nonFunctional present. Compare time and space claims.\n const timeScore = stringFieldMatch(cNF.time, dNF.time);\n const spaceScore = stringFieldMatch(cNF.space, dNF.space);\n return 0.5 * timeScore + 0.5 * spaceScore;\n}\n\n/**\n * Compare two optional string claims.\n * - Both absent → 1.0 (both declined; aligned).\n * - One absent → 0.0 (one side declined; not comparable).\n * - Both present → 1.0 if normalized strings match, else 0.0.\n */\nfunction stringFieldMatch(a: string | undefined, b: string | undefined): number {\n if (a === undefined && b === undefined) return 1.0;\n if (a === undefined || b === undefined) return 0.0;\n return normalize(a) === normalize(b) ? 1.0 : 0.0;\n}\n\n/**\n * Interface dimension: parameter Jaccard similarity.\n *\n * Score = 0.5 * jaccard(inputs) + 0.5 * jaccard(outputs)\n *\n * Parameter identity key: `${normalize(name)}::${normalize(type)}`\n * Both-empty → 1.0 per the general Jaccard rule.\n */\nfunction scoreInterface(canonical: SpecYak, candidate: SpecYak): number {\n const paramKey = (p: { readonly name: string; readonly type: string }) =>\n `${normalize(p.name)}::${normalize(p.type)}`;\n\n const inA = new Set<string>(canonical.inputs.map(paramKey));\n const inB = new Set<string>(candidate.inputs.map(paramKey));\n const outA = new Set<string>(canonical.outputs.map(paramKey));\n const outB = new Set<string>(candidate.outputs.map(paramKey));\n\n return 0.5 * jaccard(inA, inB) + 0.5 * jaccard(outA, outB);\n}\n\n// ---------------------------------------------------------------------------\n// Public functions\n// ---------------------------------------------------------------------------\n\n/**\n * Compute per-dimension variance scores between a canonical spec and a candidate.\n *\n * All scores are in [0, 1] where 1.0 indicates perfect alignment.\n * Score symmetry is not guaranteed for all dimensions but is preserved for\n * the full varianceScore (weights are symmetric by construction).\n */\nexport function compareDimensions(canonical: SpecYak, candidate: SpecYak): DimensionScores {\n return {\n security: scoreSecurity(canonical, candidate),\n behavioral: scoreBehavioral(canonical, candidate),\n error_handling: scoreErrorHandling(canonical, candidate),\n performance: scorePerformance(canonical, candidate),\n interface: scoreInterface(canonical, candidate),\n };\n}\n\n/**\n * Compute the weighted composite variance score between two SpecYak specs.\n *\n * Returns a VarianceResult with:\n * score: weighted sum of dimension scores (in [0, 1])\n * dimensions: per-dimension breakdown\n * weights: the weights used (default or caller-supplied)\n *\n * Throws RangeError if caller-supplied weights:\n * - are missing any of the 5 dimension keys\n * - do not sum to 1.0 within 1e-9\n */\nexport function varianceScore(\n canonical: SpecYak,\n candidate: SpecYak,\n options?: VarianceOptions,\n): VarianceResult {\n let weights: Readonly<Record<VarianceDimension, number>> = DIMENSION_WEIGHTS;\n\n if (options?.weights !== undefined) {\n const suppliedWeights = options.weights;\n const dims: readonly VarianceDimension[] = [\n \"security\",\n \"behavioral\",\n \"error_handling\",\n \"performance\",\n \"interface\",\n ];\n for (const dim of dims) {\n if (!(dim in suppliedWeights)) {\n throw new RangeError(`varianceScore: supplied weights missing dimension \"${dim}\"`);\n }\n }\n const sum = Object.values(suppliedWeights).reduce((acc: number, v: number) => acc + v, 0);\n if (Math.abs(sum - 1.0) > 1e-9) {\n throw new RangeError(`varianceScore: supplied weights must sum to 1.0 (got ${sum})`);\n }\n weights = suppliedWeights;\n }\n\n const dimensions = compareDimensions(canonical, candidate);\n const score =\n weights.security * dimensions.security +\n weights.behavioral * dimensions.behavioral +\n weights.error_handling * dimensions.error_handling +\n weights.performance * dimensions.performance +\n weights.interface * dimensions.interface;\n\n return { score, dimensions, weights };\n}\n\n/**\n * Map a SpecYak spec against the CWE-474 family, returning which CWEs are\n * present (detected) and which are clear (not detected).\n */\nexport function mapCweFamily(spec: SpecYak): CweMapping {\n const cwesPresent: CweId[] = [];\n const cwesClear: CweId[] = [];\n for (const pattern of CWE_474_FAMILY) {\n if (pattern.detect(spec)) {\n cwesPresent.push(pattern.cwe);\n } else {\n cwesClear.push(pattern.cwe);\n }\n }\n return { cwesPresent, cwesClear };\n}\n\n/**\n * Apply star-topology contract design rules to merge N SpecYak specs.\n *\n * Rules (DEC-VAR-002):\n * safety.preconditions = intersection (all N must agree)\n * safety.invariants = intersection (all N must agree)\n * safety.cweClear = intersection of CWE-clear sets (all N must clear)\n * behavioral.postconditions = majority vote (≥ ceil(N/2) contributors)\n * behavioral.behavior = first lexicographic among non-empty values (tie-break logged)\n * capability.effects = union of all declared effects\n *\n * Throws RangeError on empty input (zero-contributor merge is undefined).\n *\n * For N=1, all postconditions/preconditions/effects pass through unchanged,\n * and no tie-break occurs.\n */\nexport function applyContractDesignRules(specs: readonly SpecYak[]): MergedContract {\n if (specs.length === 0) {\n throw new RangeError(\n \"applyContractDesignRules: requires at least one spec (empty input is undefined under star-topology rules)\",\n );\n }\n\n const n = specs.length;\n const tieBreaks: TieBreakRecord[] = [];\n\n // Safety: preconditions = intersection\n const preconditionsIntersection = intersectStringArrays(\n specs.map((s) => s.preconditions.map(normalize)),\n );\n\n // Safety: invariants = intersection\n const invariantsIntersection = intersectStringArrays(\n specs.map((s) => s.invariants.map(normalize)),\n );\n\n // Safety: cweClear = intersection of each spec's clear set\n const cweClearIntersection = intersectCweSets(specs.map((s) => mapCweFamily(s).cwesClear));\n\n // Behavioral: postconditions = majority vote (≥ ceil(N/2))\n const majorityThreshold = Math.ceil(n / 2);\n const postconditionCounts = new Map<string, number>();\n for (const spec of specs) {\n for (const pc of spec.postconditions) {\n const key = normalize(pc);\n postconditionCounts.set(key, (postconditionCounts.get(key) ?? 0) + 1);\n }\n }\n const majorityPostconditions: string[] = [];\n for (const [key, count] of postconditionCounts) {\n if (count >= majorityThreshold) {\n majorityPostconditions.push(key);\n }\n }\n majorityPostconditions.sort();\n\n // Behavioral: behavior prose — first lexicographic among non-empty values\n const behaviorCandidates = specs\n .map((s) => s.behavior)\n .filter((b): b is string => b !== undefined && b.length > 0)\n .sort();\n\n let behaviorValue: string | undefined;\n if (behaviorCandidates.length > 0) {\n behaviorValue = behaviorCandidates[0];\n if (behaviorCandidates.length > 1) {\n tieBreaks.push({\n field: \"behavior\",\n candidates: behaviorCandidates,\n resolution: \"first_lexicographic\",\n });\n }\n }\n\n // Capability: effects = union of all declared effects\n const effectsUnion = new Set<string>();\n for (const spec of specs) {\n for (const effect of spec.effects) {\n effectsUnion.add(normalize(effect));\n }\n }\n\n return {\n safety: {\n preconditions: preconditionsIntersection,\n invariants: invariantsIntersection,\n cweClear: cweClearIntersection,\n },\n behavioral: {\n postconditions: majorityPostconditions,\n behavior: behaviorValue,\n },\n capability: {\n effects: Array.from(effectsUnion).sort(),\n },\n source: {\n contributorCount: n,\n tieBreaks,\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Private merge helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Compute the intersection of multiple arrays of normalized strings.\n * An item survives if it appears in ALL input arrays.\n * Empty input → empty result.\n */\nfunction intersectStringArrays(arrays: readonly string[][]): readonly string[] {\n if (arrays.length === 0) return [];\n const [first, ...rest] = arrays;\n if (first === undefined) return [];\n const result: string[] = [];\n for (const item of first) {\n if (rest.every((arr) => arr.includes(item))) {\n result.push(item);\n }\n }\n return result;\n}\n\n/**\n * Compute the intersection of multiple arrays of CweIds.\n * A CWE survives if it is clear in ALL specs.\n */\nfunction intersectCweSets(arrays: readonly (readonly CweId[])[]): readonly CweId[] {\n if (arrays.length === 0) return [];\n const [first, ...rest] = arrays;\n if (first === undefined) return [];\n return first.filter((cwe) => rest.every((arr) => arr.includes(cwe)));\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-ATOM-PERSIST-001\n// title: specFromIntent maps IntentCard fields to SpecYak at L0\n// status: decided\n// rationale:\n// - Level is always \"L0\" per DEC-TRIPLET-L0-ONLY-019. Effect inference and\n// invariant derivation are future work items.\n// - Name is derived as a stable slug: first 30 chars of behavior (non-word\n// chars replaced with \"-\") + last 6 chars of canonicalAstHash. This is\n// deterministic across runs for identical inputs, which is required for\n// content-addressed provenance (the name is inside the SpecYak that is\n// canonicalized and hashed). A UUID would break content-addressing.\n// - The validator is called on the produced value to fail-fast if the mapping\n// produces an invalid spec.\n// - IntentCard.notes is not mapped to any SpecYak field (no correspondent).\n// - invariants and effects are empty arrays per the task design: atoms are\n// pure-by-default at this stage; invariant/effect inference is future work.\n\nimport { validateSpecYak } from \"@yakcc/contracts\";\nimport type { SpecYak, SpecYakParameter } from \"@yakcc/contracts\";\nimport type { IntentCard, IntentParam } from \"../intent/types.js\";\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Map an IntentCard to a SpecYak.\n *\n * Mapping rules:\n * - name: slug from behavior (first 30 chars, non-word → \"-\") + last 6 of canonicalAstHash\n * - inputs: IntentParam[] → SpecYakParameter[] (name, type from typeHint, description)\n * - outputs: same mapping\n * - preconditions: from intentCard.preconditions\n * - postconditions: from intentCard.postconditions\n * - invariants: [] (pure-by-default at L0; future enrichment pass)\n * - effects: [] (atoms pure-by-default; effect inference is future work)\n * - level: \"L0\" (DEC-TRIPLET-L0-ONLY-019)\n *\n * Throws TypeError (from validateSpecYak) if the produced spec is invalid.\n *\n * @param intentCard - The extracted intent card for this atom.\n * @param canonicalAstHash - The canonical AST hash of the atom source; used to\n * disambiguate the name slug when two behaviors share\n * the same first-30-char prefix.\n */\nexport function specFromIntent(intentCard: IntentCard, canonicalAstHash: string): SpecYak {\n const name = deriveSpecName(intentCard.behavior, canonicalAstHash);\n\n const inputs: SpecYakParameter[] = intentCard.inputs.map(mapParam);\n const outputs: SpecYakParameter[] = intentCard.outputs.map(mapParam);\n\n const spec = {\n name,\n inputs,\n outputs,\n preconditions: Array.from(intentCard.preconditions),\n postconditions: Array.from(intentCard.postconditions),\n invariants: [] as string[],\n effects: [] as string[],\n level: \"L0\" as const,\n };\n\n // Fail-fast: call the validator so callers get a typed error on schema\n // violations rather than discovering invalid specs at registry store time.\n return validateSpecYak(spec);\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Map an IntentParam to a SpecYakParameter.\n *\n * IntentParam.typeHint → SpecYakParameter.type (the field names differ).\n * description is forwarded verbatim.\n */\nfunction mapParam(param: IntentParam): SpecYakParameter {\n return {\n name: param.name,\n type: param.typeHint,\n description: param.description,\n };\n}\n\n/**\n * Derive a stable, human-readable name slug for a SpecYak from a behavior\n * description and a canonical AST hash.\n *\n * Format: <slug>-<hash6>\n * slug: first 30 characters of behavior with non-word characters replaced by\n * \"-\" and leading/trailing \"-\" stripped.\n * hash6: last 6 hex characters of canonicalAstHash.\n *\n * The hash suffix ensures uniqueness when two behaviors share the same first-30\n * character prefix. Because it is derived from the canonical AST hash (which is\n * itself a content address), the name is deterministic for identical inputs.\n *\n * Examples:\n * \"Parse a comma-separated list of integers\" + \"abc123def456...\" →\n * \"parse-a-comma-separated-list-of-abc123\"\n */\nfunction deriveSpecName(behavior: string, canonicalAstHash: string): string {\n const prefix = behavior\n .slice(0, 30)\n .replace(/\\W+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n\n const hashSuffix = canonicalAstHash.slice(-6);\n\n return `${prefix}-${hashSuffix}`;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CONTINUOUS-SHAVE-022: extractIntent is the single internal\n// entry point for live intent extraction. It owns the cache read→miss→API→\n// validate→write sequence. The function is intentionally NOT exported from\n// index.ts so callers depend only on the stable public surface (universalize,\n// shave, validateIntentCard). The extraction implementation can evolve freely.\n// Status: decided (MASTER_PLAN.md DEC-CONTINUOUS-SHAVE-022)\n// Rationale: Keeping extractIntent internal prevents callers from bypassing\n// the cache or constructing partial IntentCards. All extraction must go through\n// this function to guarantee cache consistency and validation invariants.\n\n// @decision DEC-INTENT-STRATEGY-001\n// @title Strategy axis on ExtractIntentContext; \"static\" as default (WI-022)\n// @status accepted\n// @rationale\n// The \"strategy\" field gates whether the miss path calls the Anthropic API\n// (\"llm\") or the local TypeScript-Compiler-API + JSDoc extractor (\"static\").\n// Default is \"static\" so the common case never touches the SDK. The \"llm\"\n// path is entirely unchanged — all its guards (API-key check, offline check,\n// fence parser) only run when strategy === \"llm\". Tests that depend on\n// Anthropic-specific behavior must pass strategy: \"llm\" explicitly.\n// Cache-key computation is unchanged — the model/promptVersion tags for the\n// static path (\"static-ts@1\" / \"static-jsdoc@1\") produce a disjoint key\n// namespace from any LLM model tag by construction.\n\nimport { readIntent, writeIntent } from \"../cache/file-cache.js\";\nimport { sourceHash as computeSourceHash, keyFromIntentInputs } from \"../cache/key.js\";\nimport {\n AnthropicApiKeyMissingError,\n IntentCardSchemaError,\n OfflineCacheMissError,\n} from \"../errors.js\";\nimport type { AnthropicLikeClient } from \"./anthropic-client.js\";\nimport { INTENT_SCHEMA_VERSION } from \"./constants.js\";\nimport { staticExtract } from \"./static-extract.js\";\nimport type { IntentCard } from \"./types.js\";\nimport { validateIntentCard } from \"./validate-intent-card.js\";\n\n// ---------------------------------------------------------------------------\n// Public interface\n// ---------------------------------------------------------------------------\n\n/**\n * Configuration context for a single extractIntent call.\n *\n * All fields are required except `offline`, `client`, `now`, and `strategy`,\n * which have well-defined defaults. The caller (universalize, or a test)\n * assembles this from ShaveOptions + constants.\n */\nexport interface ExtractIntentContext {\n /**\n * Extraction strategy.\n *\n * @decision DEC-INTENT-STRATEGY-001\n * - \"static\" (default): TypeScript Compiler API + JSDoc parser. No network,\n * no API key, always offline-safe. Uses STATIC_MODEL_TAG / STATIC_PROMPT_VERSION\n * as the cache-key components.\n * - \"llm\": Anthropic API. Subject to API-key guard, offline check, and\n * fence parsing. Uses ctx.model / ctx.promptVersion (DEFAULT_MODEL /\n * INTENT_PROMPT_VERSION by default) as cache-key components.\n *\n * When omitted, defaults to \"static\".\n */\n readonly strategy?: \"static\" | \"llm\" | undefined;\n /** Anthropic model identifier, e.g. DEFAULT_MODEL. Used only when strategy === \"llm\". */\n readonly model: string;\n /** Prompt version tag used in cache key derivation. Used only when strategy === \"llm\". */\n readonly promptVersion: string;\n /** Absolute path to the root of the intent cache directory. */\n readonly cacheDir: string;\n /**\n * When true and strategy === \"llm\", the extractor never makes API calls —\n * throws OfflineCacheMissError on a cache miss instead.\n * For strategy === \"static\" this flag has no effect (static is always offline-safe).\n */\n readonly offline?: boolean | undefined;\n /**\n * Injectable Anthropic client. When provided, used instead of constructing\n * the default SDK client. Allows tests to inject a mock without network access.\n * Only relevant when strategy === \"llm\".\n */\n readonly client?: AnthropicLikeClient | undefined;\n /**\n * Clock override for extractedAt timestamp. Defaults to () => new Date().\n * Allows tests to assert a deterministic extractedAt value.\n */\n readonly now?: (() => Date) | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// JSON fence parser (LLM path only)\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the content between the first <json>...</json> fence pair.\n *\n * Throws IntentCardSchemaError if fences are absent or the content between\n * them is not parseable as JSON.\n */\nfunction parseJsonFence(response: string): unknown {\n const openTag = \"<json>\";\n const closeTag = \"</json>\";\n const start = response.indexOf(openTag);\n const end = response.indexOf(closeTag);\n\n if (start === -1 || end === -1 || end <= start) {\n throw new IntentCardSchemaError(\n `API response missing <json>...</json> fences. Raw response (first 200 chars): ${response.slice(0, 200)}`,\n );\n }\n\n const jsonText = response.slice(start + openTag.length, end).trim();\n try {\n return JSON.parse(jsonText) as unknown;\n } catch (err) {\n throw new IntentCardSchemaError(\n `API response contained malformed JSON between fences: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Core extractor\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the behavioral intent from a unit of source code.\n *\n * This function is INTERNAL — it is not exported from index.ts.\n *\n * Strategy dispatch (DEC-INTENT-STRATEGY-001):\n * Steps 1-3 (hash + cache lookup) run for BOTH strategies.\n * Step 4a+ differs:\n * - strategy === \"static\" (default): calls staticExtract(), no API key\n * guard, no offline check, no SDK import.\n * - strategy === \"llm\": original Anthropic path (unchanged).\n *\n * @param unitSource - Raw source text of the candidate block.\n * @param ctx - Extraction context (model, promptVersion, cacheDir, strategy, flags).\n * @returns Validated IntentCard.\n */\nexport async function extractIntent(\n unitSource: string,\n ctx: ExtractIntentContext,\n): Promise<IntentCard> {\n // Resolve effective strategy — default \"static\" per DEC-INTENT-STRATEGY-001\n const strategy = ctx.strategy ?? \"static\";\n\n // Resolve effective model/promptVersion tags based on strategy.\n // For \"static\": use STATIC_MODEL_TAG / STATIC_PROMPT_VERSION (imported lazily\n // to avoid importing static-extract.ts on the LLM-only path in tests).\n let modelTag: string;\n let promptVersion: string;\n\n if (strategy === \"static\") {\n const { STATIC_MODEL_TAG, STATIC_PROMPT_VERSION } = await import(\"./constants.js\");\n modelTag = STATIC_MODEL_TAG;\n promptVersion = STATIC_PROMPT_VERSION;\n } else {\n modelTag = ctx.model;\n promptVersion = ctx.promptVersion;\n }\n\n // Step 1 & 2: compute hashes and cache key.\n const srcHash = computeSourceHash(unitSource);\n const cacheKey = keyFromIntentInputs({\n sourceHash: srcHash,\n modelTag,\n promptVersion,\n schemaVersion: INTENT_SCHEMA_VERSION,\n });\n\n // Step 3: attempt cache hit.\n const cached = await readIntent(ctx.cacheDir, cacheKey);\n if (cached !== undefined) {\n try {\n return validateIntentCard(cached);\n } catch (err) {\n // Corrupt cache entry — log, delete, and proceed as miss.\n console.warn(\n `[shave extractIntent] Corrupt cache entry for key ${cacheKey} (${\n err instanceof Error ? err.message : String(err)\n }); treating as miss.`,\n );\n // readIntent already deletes on parse error; validateIntentCard failures\n // require a manual eviction here.\n const { unlink } = await import(\"node:fs/promises\");\n const { join } = await import(\"node:path\");\n const shard = cacheKey.slice(0, 3);\n const filePath = join(ctx.cacheDir, shard, `${cacheKey}.json`);\n await unlink(filePath).catch(() => {\n // Best-effort; ignore.\n });\n }\n }\n\n // Step 4: strategy-specific miss handling.\n const now = (ctx.now ?? (() => new Date()))();\n // Truncate to whole second for a clean ISO-8601 timestamp.\n const extractedAt = new Date(Math.floor(now.getTime() / 1000) * 1000).toISOString();\n\n if (strategy === \"static\") {\n // Static path: AST + JSDoc extraction, no network calls.\n // @decision DEC-INTENT-STRATEGY-001: no API-key guard, no offline check here.\n const raw = staticExtract(unitSource, {\n sourceHash: srcHash,\n modelVersion: modelTag,\n promptVersion,\n extractedAt,\n });\n\n // Validate (same step as LLM path — both paths must produce a valid card).\n const validated = validateIntentCard(raw);\n\n // Write to cache atomically.\n await writeIntent(ctx.cacheDir, cacheKey, validated);\n\n return validated;\n }\n\n // LLM path (strategy === \"llm\") — original behavior, entirely unchanged.\n\n // Step 4a: offline mode — no API calls.\n if (ctx.offline === true) {\n throw new OfflineCacheMissError(cacheKey);\n }\n\n // Step 4b: check for API key.\n const apiKey = process.env.ANTHROPIC_API_KEY;\n if (ctx.client === undefined && !apiKey) {\n throw new AnthropicApiKeyMissingError();\n }\n\n // Step 4c: resolve client and call the API.\n // Lazy import of the Anthropic client to keep the static path free from SDK.\n const { createDefaultAnthropicClient } = await import(\"./anthropic-client.js\");\n const { SYSTEM_PROMPT } = await import(\"./prompt.js\");\n\n // At this point either ctx.client is set or apiKey is a non-empty string\n // (the guard above ensures at least one is truthy).\n const client = ctx.client ?? (await createDefaultAnthropicClient(apiKey as string));\n\n const response = await client.create({\n model: ctx.model,\n system: SYSTEM_PROMPT,\n messages: [{ role: \"user\", content: unitSource }],\n max_tokens: 2048,\n });\n\n // Extract text from the first text content block.\n const textBlock = response.content.find((b) => b.type === \"text\") as\n | { type: \"text\"; text: string }\n | undefined;\n const responseText = textBlock?.text ?? \"\";\n\n // Step 4d: parse the JSON fence.\n const raw = parseJsonFence(responseText);\n\n // Step 4e: assemble the full IntentCard.\n const card = {\n ...(raw as Record<string, unknown>),\n modelVersion: ctx.model,\n promptVersion: ctx.promptVersion,\n sourceHash: srcHash,\n extractedAt,\n };\n\n // Step 4f: validate (throws IntentCardSchemaError on any violation).\n const validated = validateIntentCard(card);\n\n // Step 4g: write to cache atomically.\n await writeIntent(ctx.cacheDir, cacheKey, validated);\n\n // Step 4h: return.\n return validated;\n}\n","// SPDX-License-Identifier: MIT\n/**\n * Static intent extraction — TypeScript Compiler API + JSDoc parser.\n *\n * @decision DEC-INTENT-STRATEGY-001\n * @title Strategy axis on ExtractIntentContext; \"static\" as default\n * @status accepted\n * @rationale\n * The intent-extraction pipeline had exactly one cloud LLM dependency.\n * Replacing it with a local TypeScript-Compiler-API + JSDoc parser achieves\n * behavior-equivalent output (same IntentCard shape, same validateIntentCard\n * runs) for the parts that matter (cache key, schema, validator) while\n * eliminating the Anthropic round-trip (~1-3s/call) in favor of in-process\n * AST parsing (~5ms/call). The \"static\" strategy is the new default because:\n * (a) Yakcc's identity is \"deterministic, content-addressed, local-first\";\n * (b) the LLM was only used to write human-readable documentation fields\n * (behavior, param descriptions) into the IntentCard, not to influence\n * slicing, matching, or content-addressing;\n * (c) WI-016 (AI property-test corpus) may depend on the existing client\n * surface — preserving the \"llm\" path behind strategy: \"llm\" is cheap\n * insurance. The LLM path is entirely unchanged.\n *\n * @decision DEC-INTENT-STATIC-002\n * @title JSDoc tag vocabulary — Eiffel/JML lineage for preconditions/postconditions\n * @status accepted\n * @rationale\n * Tag set: @param, @returns/@return, @requires, @ensures, @throws/@throw/\n * @exception, @remarks, @note, @example.\n * @requires / @ensures chosen for Eiffel/JML / Code Contracts / SPARK lineage:\n * unambiguous, not claimed by tsc's type-checking machinery, and directly\n * maps to the IntentCard's preconditions/postconditions arrays without\n * any ambiguity. @pre/@post were rejected as they clash with some doc tooling.\n *\n * @decision DEC-INTENT-STATIC-003\n * @title Type extraction depth: source-text only via getTypeNode()?.getText()\n * @status accepted\n * @rationale\n * We use param.getTypeNode()?.getText() rather than the type-checker's\n * resolved type. Reasons:\n * (a) shave's decompose() already parses with useInMemoryFileSystem:true and\n * no lib loading (recursion.ts:282); a type-checked Program would need\n * lib.d.ts and introduce TS-version nondeterminism;\n * (b) parse cost without checker is ~5ms/call vs ~200ms/call with full lib\n * resolution;\n * (c) the \"unknown\" sentinel is the established convention in the LLM prompt\n * (prompt.ts:33) and is accepted by validateIntentCard.\n *\n * @decision DEC-EMBED-QUERY-ENRICH-HELPER-001 (OD-2 Option A)\n * @title Shared extraction primitives live in @yakcc/contracts/source-extract.\n * @status accepted\n * @rationale\n * The extraction functions (extractSignatureFromNode, extractJsDoc, and the\n * primary-declaration picker pickPrimaryDeclaration) have been moved to\n * packages/contracts/src/source-extract.ts and source-pick.ts. This file\n * now delegates to those shared primitives and adapts the output to the\n * IntentCard / IntentParam shape that @yakcc/shave uses.\n *\n * Behavioral equivalence guarantee: every test in static-extract.test.ts,\n * static-extract.props.test.ts, and static-extract.integration.test.ts\n * must continue to pass unchanged — this is the structural proof that the\n * refactor is behavior-preserving.\n *\n * Field mapping (shared ExtractedParam → IntentParam):\n * ExtractedParam.typeAnnotation → IntentParam.typeHint\n *\n * Field mapping (shared ExtractedJsDoc → IntentCard notes array):\n * ExtractedJsDoc.throwDescriptions → each becomes \"throws: <desc>\" in notes[]\n * ExtractedJsDoc.notes (remarks/example/note) → appended to notes[] as-is\n */\n\nimport { extractJsDoc, extractSignatureFromNode, pickPrimaryDeclaration } from \"@yakcc/contracts\";\nimport { Project } from \"ts-morph\";\nimport type { IntentParam } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Public entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Envelope fields that staticExtract() embeds into the returned card.\n * These match the fields that extractIntent() assembles after the API call.\n */\nexport interface StaticExtractEnvelope {\n readonly sourceHash: string;\n readonly modelVersion: string;\n readonly promptVersion: string;\n readonly extractedAt: string;\n}\n\n/**\n * Extract behavioral intent from a unit of TypeScript/JavaScript source using\n * the TypeScript Compiler API and JSDoc parsing.\n *\n * Returns an `unknown` value that the caller must pass through\n * `validateIntentCard()` — identical to the LLM path's post-processing step,\n * so the same validation invariants apply regardless of which path produced the\n * card.\n *\n * No Anthropic SDK is imported. No network calls are made. This function is\n * always offline-safe.\n *\n * @param unitSource - Raw source text of the candidate block.\n * @param envelope - Pre-computed envelope fields (hash, version tags, timestamp).\n * @returns Unvalidated card object (call validateIntentCard before using).\n */\nexport function staticExtract(unitSource: string, envelope: StaticExtractEnvelope): unknown {\n // Parse with ts-morph using an in-memory virtual file system.\n // @decision DEC-INTENT-STATIC-003: no type-checker, no lib loading.\n const project = new Project({\n useInMemoryFileSystem: true,\n compilerOptions: {\n strict: false,\n allowJs: true,\n noLib: true,\n },\n });\n const sourceFile = project.createSourceFile(\"__static_extract__.ts\", unitSource);\n\n const primary = pickPrimaryDeclaration(sourceFile);\n\n // No declaration found — \"source fragment\" fallback\n if (primary === undefined) {\n const stmtCount = sourceFile.getStatements().length;\n const byteCount = unitSource.length;\n const behavior = `source fragment (${stmtCount} statements, ${byteCount} bytes)`;\n return {\n schemaVersion: 1,\n behavior,\n inputs: [],\n outputs: [],\n preconditions: [],\n postconditions: [],\n notes: [],\n ...envelope,\n };\n }\n\n const jsdoc = extractJsDoc(primary);\n const sig = extractSignatureFromNode(primary);\n\n // Behavior field: JSDoc summary → signature string → fragment fallback\n const behavior =\n jsdoc.summary ?? sig.signatureString ?? buildFragmentFallback(sourceFile, unitSource);\n\n // Inputs: one entry per parameter.\n // ExtractedParam.typeAnnotation → IntentParam.typeHint (field rename only).\n const inputs: IntentParam[] = sig.params.map((p) => ({\n name: p.name,\n typeHint: p.typeAnnotation,\n description: jsdoc.params.get(p.name) ?? \"\",\n }));\n\n // Outputs: always exactly one entry (matches LLM prompt convention, prompt.ts:33).\n // Exception: no-declaration fragment case already handled above.\n const outputs: IntentParam[] = [\n {\n name: \"return\",\n typeHint: sig.returnTypeAnnotation,\n description: jsdoc.returns ?? \"\",\n },\n ];\n\n // Notes: @throws entries (with \"throws: \" prefix) + remarks/example/note entries.\n const notes: string[] = [...jsdoc.throwDescriptions.map((d) => `throws: ${d}`), ...jsdoc.notes];\n\n return {\n schemaVersion: 1,\n behavior,\n inputs,\n outputs,\n preconditions: jsdoc.preconditions,\n postconditions: jsdoc.postconditions,\n notes,\n ...envelope,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Fragment fallback behavior string when there's no declaration. */\nfunction buildFragmentFallback(sourceFile: import(\"ts-morph\").SourceFile, src: string): string {\n const stmtCount = sourceFile.getStatements().length;\n return `source fragment (${stmtCount} statements, ${src.length} bytes)`;\n}\n","// SPDX-License-Identifier: MIT\n// Locate the project root by walking up the directory tree looking for\n// pnpm-workspace.yaml. This file is present in the root of every pnpm\n// monorepo, making it a reliable anchor for the project root regardless of\n// which package the calling code lives in.\n//\n// Rule: walk up from `start` until a directory containing pnpm-workspace.yaml\n// is found. If none is found (reached filesystem root), fall back to `start`.\n// This means the cache directory degrades gracefully in non-monorepo contexts\n// (e.g. unit tests, isolated package installations).\n\nimport { access } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\n/**\n * Walk up the directory tree from `start` (default: process.cwd()) looking\n * for a directory that contains `pnpm-workspace.yaml`.\n *\n * Returns the first matching ancestor directory. Falls back to `start` if no\n * workspace file is found (e.g. standalone package installation or test env).\n *\n * @param start - Starting directory for the upward walk. Defaults to process.cwd().\n * @returns Absolute path of the project root directory.\n */\nexport async function locateProjectRoot(start?: string): Promise<string> {\n const origin = start ?? process.cwd();\n let current = origin;\n\n for (;;) {\n const candidate = join(current, \"pnpm-workspace.yaml\");\n try {\n await access(candidate);\n // File exists — this is the project root.\n return current;\n } catch {\n // Not found here; walk up.\n }\n\n const parent = dirname(current);\n if (parent === current) {\n // Reached filesystem root without finding the workspace file.\n // Fall back to the starting directory.\n return origin;\n }\n current = parent;\n }\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-ATOM-PERSIST-001\n// title: buildTriplet computes the full block identity for a novel atom at L0\n// status: decided\n// rationale:\n// - specFromIntent produces the SpecYak; specHash() derives its content address.\n// - impl is the raw source text (AtomLeaf.source). No normalization at L0 per\n// DEC-TRIPLET-IDENTITY-020: file bytes are the identity unit at L0; AST\n// normalization is deferred to L1+ where the totality pass normalizes anyway.\n// - ProofManifest at L0: a single \"property_tests\" artifact is populated from\n// the CorpusResult produced by extractCorpus() (WI-016). The artifact bytes\n// map carries the corpus bytes at the corpus path.\n// - Bootstrap fallback: when `options.bootstrap === true`, an empty-bytes\n// placeholder manifest is emitted instead of requiring a CorpusResult. This\n// is an explicit opt-in; the DEFAULT path requires a populated CorpusResult.\n// \"WI-016 retirement of L0_BOOTSTRAP_MANIFEST as the silent default.\"\n// - blockMerkleRoot() from @yakcc/contracts is the canonical identity derivation.\n// We do not re-implement the Merkle logic here.\n\nimport {\n blockMerkleRoot,\n canonicalize,\n specHash as deriveSpecHash,\n validateSpecYak,\n} from \"@yakcc/contracts\";\nimport type { BlockMerkleRoot, ProofManifest, SpecHash, SpecYak } from \"@yakcc/contracts\";\nimport type { CanonicalAstHash } from \"@yakcc/contracts\";\nimport type { CorpusResult } from \"../corpus/types.js\";\nimport type { IntentCard } from \"../intent/types.js\";\nimport { specFromIntent } from \"./spec-from-intent.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * The full block triplet computed for a novel atom.\n *\n * This is an intermediate value: callers feed it into a BlockTripletRow for\n * registry storage. It is not the same as @yakcc/contracts BlockTriplet —\n * that type carries an `artifacts` Map required by blockMerkleRoot(); this\n * type also carries the `artifacts` Map so callers can thread it directly\n * into BlockTripletRow without re-deriving it (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002:\n * single source of truth — the SAME Map used for blockMerkleRoot() flows to\n * the row and onto the federation wire; no second Map, no copy, no re-derivation).\n *\n * @decision DEC-V1-FEDERATION-WIRE-ARTIFACTS-002\n * title: BuiltTriplet.artifacts is the canonical artifact-bytes carrier\n * status: decided (WI-022 slice b)\n * rationale:\n * BlockTripletRow.artifacts (added in slice a) requires the exact Map that\n * was passed to blockMerkleRoot(). Exposing `artifacts` on BuiltTriplet\n * gives persistNovelGlueAtom a single place to read the Map from — it does\n * not reconstruct, copy, or re-derive it. This satisfies Sacred Practice #12\n * (single source of truth) and DEC-CONTRACTS-AUTHORITY-001 (blockMerkleRoot\n * formula unchanged).\n */\nexport interface BuiltTriplet {\n readonly spec: SpecYak;\n readonly specHash: SpecHash;\n readonly specCanonicalBytes: Uint8Array;\n readonly impl: string;\n readonly manifest: ProofManifest;\n readonly merkleRoot: BlockMerkleRoot;\n /**\n * Artifact bytes keyed by manifest-declared path.\n *\n * This is the SAME Map<string, Uint8Array> that was passed to blockMerkleRoot()\n * — not a copy or re-derivation. Callers MUST forward this Map to\n * BlockTripletRow.artifacts unchanged (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002).\n */\n readonly artifacts: ReadonlyMap<string, Uint8Array>;\n}\n\n/**\n * Options for buildTriplet().\n */\nexport interface BuildTripletOptions {\n /**\n * When true, emit an empty-bytes placeholder manifest instead of requiring\n * a CorpusResult. This is an explicit opt-in for the bootstrap/migration path.\n *\n * DEFAULT is false — the normal production path requires a populated CorpusResult\n * via the `corpusResult` parameter.\n *\n * @decision DEC-ATOM-PERSIST-001 (WI-016): L0_BOOTSTRAP_MANIFEST is no longer the\n * silent default. Callers that still need the bootstrap path must pass bootstrap=true\n * explicitly. New callers should always provide a CorpusResult via extractCorpus().\n */\n readonly bootstrap?: boolean | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Bootstrap L0 manifest constant (explicit opt-in only after WI-016)\n// ---------------------------------------------------------------------------\n\n/**\n * The placeholder path for the L0 bootstrap property-test artifact.\n *\n * Only used when `options.bootstrap === true`. The bootstrap path is an\n * explicit opt-in after WI-016; the default production path uses a populated\n * CorpusResult from extractCorpus().\n */\nconst L0_BOOTSTRAP_PATH = \"property-tests.ts\";\n\n/** Empty bytes for the bootstrap property-test artifact. */\nconst EMPTY_BYTES = new Uint8Array(0);\n\n/**\n * The L0 bootstrap ProofManifest (explicit opt-in only).\n *\n * Declared as a named export so tests can verify bootstrap manifests without\n * re-implementing the shape. Do NOT use as the default in production code.\n */\nexport const L0_BOOTSTRAP_MANIFEST: ProofManifest = {\n artifacts: [\n {\n kind: \"property_tests\",\n path: L0_BOOTSTRAP_PATH,\n },\n ],\n} as const;\n\n/**\n * Artifact bytes map for the L0 bootstrap manifest.\n *\n * Exported so tests can construct the expected bootstrap artifacts without\n * re-implementing the shape. Do NOT use as a default in production code —\n * this is the explicit bootstrap opt-in path only (WI-016).\n */\nexport function makeBootstrapArtifacts(): Map<string, Uint8Array> {\n return new Map([[L0_BOOTSTRAP_PATH, EMPTY_BYTES]]);\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a BuiltTriplet for a novel atom.\n *\n * Steps:\n * 1. Derive SpecYak from the intent card via specFromIntent().\n * 2. Compute specHash = BLAKE3(canonicalize(spec)).\n * 3. impl is the raw source text.\n * 4. Build ProofManifest from the CorpusResult (WI-016) OR the bootstrap\n * placeholder if `options.bootstrap === true`.\n * 5. Compute merkleRoot via blockMerkleRoot() from @yakcc/contracts.\n *\n * Throws TypeError if specFromIntent produces an invalid spec (validateSpecYak\n * is called inside specFromIntent).\n *\n * Throws Error if `corpusResult` is undefined and `options.bootstrap` is not\n * explicitly true — the caller must provide a CorpusResult or opt into bootstrap.\n *\n * @param intentCard - The extracted intent card for this atom.\n * @param source - The raw source text of the atom (AtomLeaf.source).\n * @param canonicalAstHash - The canonical AST hash of the atom source.\n * Used for specName uniqueness and stored on the row.\n * @param corpusResult - The corpus extraction result (from extractCorpus()).\n * Required unless options.bootstrap === true.\n * @param options - Optional configuration. Set bootstrap=true for the\n * empty-placeholder path (explicit opt-in only).\n */\nexport function buildTriplet(\n intentCard: IntentCard,\n source: string,\n canonicalAstHash: CanonicalAstHash,\n corpusResult?: CorpusResult | undefined,\n options?: BuildTripletOptions | undefined,\n): BuiltTriplet {\n // Step 1: derive SpecYak from the intent card.\n const spec = specFromIntent(intentCard, canonicalAstHash);\n\n // Paranoia: validate again (specFromIntent already calls validateSpecYak, but\n // TypeScript loses the narrowing through the return type and re-validation here\n // is a zero-cost safety net).\n validateSpecYak(spec);\n\n // Step 2: compute spec hash and canonical bytes.\n const specCanonicalBytes = canonicalize(spec as unknown as Parameters<typeof canonicalize>[0]);\n const specHashValue = deriveSpecHash(spec);\n\n // Step 3: impl is the raw source text.\n const impl = source;\n\n // Step 4: build ProofManifest from CorpusResult or bootstrap placeholder.\n //\n // @decision DEC-ATOM-PERSIST-001 (WI-016):\n // The default path requires a populated CorpusResult from extractCorpus().\n // The bootstrap path (options.bootstrap === true) is an explicit opt-in.\n // Passing neither throws — no silent fallback to empty bytes.\n let manifest: ProofManifest;\n let artifacts: Map<string, Uint8Array>;\n\n if (corpusResult !== undefined) {\n // Normal production path: use the extracted corpus.\n manifest = {\n artifacts: [\n {\n kind: \"property_tests\",\n path: corpusResult.path,\n },\n ],\n };\n artifacts = new Map([[corpusResult.path, corpusResult.bytes]]);\n } else if (options?.bootstrap === true) {\n // Explicit bootstrap opt-in: emit placeholder manifest with empty bytes.\n manifest = L0_BOOTSTRAP_MANIFEST;\n artifacts = makeBootstrapArtifacts();\n } else {\n throw new Error(\n \"buildTriplet: corpusResult is required unless options.bootstrap === true. \" +\n \"Call extractCorpus() to produce a CorpusResult, or pass options.bootstrap=true \" +\n \"explicitly to use the empty-bytes placeholder (bootstrap/migration path only).\",\n );\n }\n\n // Step 5: compute BlockMerkleRoot via the canonical derivation in @yakcc/contracts.\n const merkleRoot = blockMerkleRoot({\n spec,\n implSource: impl,\n manifest,\n artifacts,\n });\n\n return {\n spec,\n specHash: specHashValue,\n specCanonicalBytes,\n impl,\n manifest,\n merkleRoot,\n // DEC-V1-FEDERATION-WIRE-ARTIFACTS-002: forward the SAME Map used above for\n // blockMerkleRoot() — not a copy, not a re-derivation. Single source of truth.\n artifacts,\n };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-ATOM-PERSIST-001\n// title: persistNovelGlueAtom persists novel atoms to the registry as BlockTripletRows\n// status: decided\n// rationale:\n// - Persistence is opt-in via ShaveRegistryView.storeBlock (optional method):\n// when storeBlock is absent the entry is left untouched and no error is raised.\n// This lets read-only registry views (e.g. the test mock with only findByCanonicalAstHash)\n// be used without modification.\n// - Only NovelGlueEntries with an intentCard persist; PointerEntries reference\n// existing blocks in the registry and do not produce new rows.\n// - Entries without an intentCard are skipped. For NovelGlueEntries this\n// should not occur in practice: WI-031 (commit `8dfb44b`, 2026-05-01)\n// calls extractIntent per novel-glue entry for multi-leaf trees (see\n// DEC-UNIVERSALIZE-MULTI-LEAF-INTENT-001). The intentCard field remains\n// optional on NovelGlueEntry for forward-compat with PointerEntry /\n// ForeignLeafEntry kinds that carry no intent slot.\n// - WI-016: Property-test corpus is extracted via extractCorpus() before buildTriplet().\n// The CorpusResult is passed to buildTriplet() as the canonical artifact source.\n// The bootstrap placeholder (empty bytes) is no longer the silent default.\n// - Corpus extraction source preference: upstream-test (a) > documented-usage (b) > ai-derived (c).\n// cacheDir is forwarded from PersistOptions when provided, enabling source (c).\n// - Effect declaration is empty (atoms are pure-by-default at this stage;\n// effect inference is a future pass).\n// - The signature takes Registry (full interface) not ShaveRegistryView because\n// persistence requires storeBlock, which isn't in the read-only view. Callers\n// that have a full Registry pass it directly.\n\nimport type { BlockMerkleRoot, CanonicalAstHash } from \"@yakcc/contracts\";\nimport type { BlockTripletRow } from \"@yakcc/registry\";\nimport type { Registry } from \"@yakcc/registry\";\nimport { extractCorpus } from \"../corpus/index.js\";\nimport type { CorpusAtomSpec, CorpusExtractionOptions } from \"../corpus/index.js\";\nimport type { IntentCard } from \"../intent/types.js\";\nimport type { NovelGlueEntry } from \"../universalize/types.js\";\nimport { buildTriplet } from \"./triplet.js\";\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\n/**\n * Options for persistNovelGlueAtom() and maybePersistNovelGlueAtom().\n */\nexport interface PersistOptions {\n /**\n * Root cache directory for corpus extraction source (c) — AI-derived synthesis.\n * When provided, the AI-derived cache path is attempted after (a) and (b).\n * When omitted, source (c) is skipped (sources (a) and (b) are always available).\n */\n readonly cacheDir?: string | undefined;\n\n /**\n * Corpus extraction options forwarded to extractCorpus().\n * Use to disable individual sources (e.g. for testing).\n */\n readonly corpusOptions?: CorpusExtractionOptions | undefined;\n\n /**\n * Absolute path to the source file being processed.\n *\n * When provided, the props-file corpus extractor looks for a sibling\n * `<stem>.props.ts` file (replacing the `.ts` extension). If found and it\n * contains matching `prop_<atom>_*` exports, the props-file content is used\n * as the corpus artifact (highest priority, WI-V2-07-L8).\n *\n * Forwarded from shave()'s `sourcePath` parameter. Omitting this disables\n * the props-file source for this atom.\n */\n readonly sourceFilePath?: string | undefined;\n\n // @decision DEC-REGISTRY-PARENT-BLOCK-004\n // title: parentBlockRoot is the canonical lineage field on PersistOptions\n // status: decided (WI-017)\n // rationale:\n // The parent_block_root column on the blocks table (added in WI-014-04) is\n // the single authority for recursion-tree lineage. To avoid a sidecar table or\n // in-memory map, lineage is injected via PersistOptions.parentBlockRoot and\n // written directly into BlockTripletRow.parentBlockRoot on each persist call.\n // No re-derivation of the parent merkle root is performed here — callers supply\n // the literal BlockMerkleRoot returned by a prior persistNovelGlueAtom call.\n // This preserves content-address purity: the parent reference is row metadata,\n // not part of the child block's content address computation.\n /**\n * BlockMerkleRoot of the recursion-tree parent from which this atom was shaved.\n *\n * - `null` (or omitted) → this block is the root of its recursion tree.\n * - non-null → the caller supplies the LITERAL merkle root returned by a prior\n * persistNovelGlueAtom call for the parent atom.\n *\n * The value is written directly to BlockTripletRow.parentBlockRoot. It is NOT\n * part of the block's content address — it is registry row metadata only.\n */\n readonly parentBlockRoot?: BlockMerkleRoot | null | undefined;\n\n /**\n * Source-file provenance context forwarded from ShaveOptions.sourceContext.\n *\n * When provided, the persisted BlockTripletRow carries sourcePkg, sourceFile,\n * and sourceOffset so the registry can record where the atom was shaved from.\n * When absent (undefined), the row is stored with null provenance — correct for\n * interactive shaves and non-bootstrap runners.\n *\n * Forwarded from ShaveOptions.sourceContext via shave() → maybePersistNovelGlueAtom\n * → persistNovelGlueAtom without modification.\n *\n * @decision DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001\n * @scope WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P1\n */\n readonly sourceContext?:\n | {\n readonly sourcePkg: string;\n readonly sourceFile: string;\n readonly sourceOffset: number | null;\n }\n | undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Persist a NovelGlueEntry as a block triplet row in the registry.\n *\n * Returns the BlockMerkleRoot of the persisted block, or undefined if the\n * entry was skipped (no intentCard).\n *\n * Skips persistence without error when:\n * - entry.intentCard is undefined (deep leaf in a multi-leaf tree).\n *\n * @param entry - The NovelGlueEntry from the slicer. Must carry an intentCard\n * for persistence; entries without one are skipped.\n * @param registry - Full Registry interface (requires storeBlock). For callers\n * with a read-only view, use the opt-in path in shave() instead.\n * @param options - Optional persistence options (cacheDir for corpus source c).\n */\nexport async function persistNovelGlueAtom(\n entry: NovelGlueEntry,\n registry: Registry,\n options?: PersistOptions,\n): Promise<BlockMerkleRoot | undefined> {\n // Skip atoms without an intent card — this is the defensive safety net for\n // non-NovelGlue entry kinds (PointerEntry, ForeignLeafEntry) that have no\n // intentCard slot. Per WI-031 (DEC-UNIVERSALIZE-MULTI-LEAF-INTENT-001),\n // novel-glue entries in multi-leaf trees DO carry per-leaf cards; this branch\n // is not expected to fire for them in the normal pipeline.\n const intentCard: IntentCard | undefined = entry.intentCard;\n if (intentCard === undefined) {\n return undefined;\n }\n\n // WI-016: Extract the property-test corpus before building the triplet.\n // The corpus result carries the artifact bytes that become the \"property_tests\"\n // entry in the ProofManifest, making the BlockMerkleRoot content-dependent on\n // the actual test corpus (not empty bytes).\n //\n // WI-V2-07-L8: derive propsFilePath from sourceFilePath when provided.\n // The props-file extractor (highest-priority source) will check for matching\n // prop_<atom>_* exports in the sibling *.props.ts before falling through\n // to upstream-test source-(a).\n const propsFilePath = options?.sourceFilePath?.replace(/\\.ts$/, \".props.ts\");\n const atomSpec: CorpusAtomSpec = {\n source: entry.source,\n intentCard,\n cacheDir: options?.cacheDir,\n propsFilePath,\n };\n const corpusResult = await extractCorpus(atomSpec, options?.corpusOptions);\n\n // Build the full block triplet with the extracted corpus.\n const triplet = buildTriplet(intentCard, entry.source, entry.canonicalAstHash, corpusResult);\n\n // Construct the BlockTripletRow for registry storage.\n // parentBlockRoot is row metadata (lineage), not part of the content address —\n // per DEC-REGISTRY-PARENT-BLOCK-004. It defaults to null for root atoms.\n //\n // DEC-V1-FEDERATION-WIRE-ARTIFACTS-002: artifacts is the SAME Map that buildTriplet\n // passed to blockMerkleRoot() — forwarded here unchanged, no copy or re-derivation.\n //\n // DEC-V2-REGISTRY-SOURCE-FILE-PROVENANCE-001: sourceContext fields are forwarded\n // from PersistOptions.sourceContext when provided by the bootstrap walker.\n // When absent (undefined), all three provenance fields default to null — correct\n // for interactive shaves and non-bootstrap runners. INSERT OR IGNORE in storeBlock\n // ensures first-observed-wins: a second store with null does not clobber existing\n // non-null provenance.\n const sc = options?.sourceContext;\n const row: BlockTripletRow = {\n blockMerkleRoot: triplet.merkleRoot,\n specHash: triplet.specHash,\n specCanonicalBytes: triplet.specCanonicalBytes,\n implSource: triplet.impl,\n proofManifestJson: JSON.stringify(triplet.manifest),\n artifacts: triplet.artifacts,\n level: \"L0\",\n createdAt: Date.now(),\n canonicalAstHash: entry.canonicalAstHash,\n parentBlockRoot: options?.parentBlockRoot ?? null,\n // Provenance fields — null when not supplied by the caller.\n sourcePkg: sc?.sourcePkg ?? null,\n sourceFile: sc?.sourceFile ?? null,\n sourceOffset: sc?.sourceOffset ?? null,\n };\n\n // Persist to registry. storeBlock is idempotent: storing the same\n // blockMerkleRoot twice is a no-op (per Registry contract).\n await registry.storeBlock(row);\n\n return triplet.merkleRoot;\n}\n\n/**\n * Opt-in persistence path for use within shave().\n *\n * Checks whether the registry view supports storeBlock and, if so, calls\n * persistNovelGlueAtom. Returns undefined when the view does not support\n * persistence (graceful degradation, no error).\n *\n * This is separate from persistNovelGlueAtom so that shave() can work with\n * a ShaveRegistryView (which may not implement storeBlock) while the direct\n * persistNovelGlueAtom API accepts the full Registry interface.\n *\n * @param entry - The NovelGlueEntry to potentially persist.\n * @param registry - A registry view that optionally supports storeBlock.\n * @param options - Optional persistence options forwarded to persistNovelGlueAtom.\n */\nexport async function maybePersistNovelGlueAtom(\n entry: NovelGlueEntry,\n registry: { storeBlock?: Registry[\"storeBlock\"] } & {\n findByCanonicalAstHash?: (hash: CanonicalAstHash) => Promise<readonly BlockMerkleRoot[]>;\n },\n options?: PersistOptions,\n): Promise<BlockMerkleRoot | undefined> {\n if (typeof registry.storeBlock !== \"function\") {\n return undefined;\n }\n\n // Delegate to the full Registry path. We cast here because the duck-typed\n // registry satisfies the storeBlock contract even if it doesn't implement\n // every Registry method. Only storeBlock is called in this flow.\n return persistNovelGlueAtom(entry, registry as unknown as Registry, options);\n}\n","// SPDX-License-Identifier: MIT\n// plumbing-globs.ts — Single-authority glob set for workspace plumbing capture.\n//\n// @decision DEC-V2-WORKSPACE-PLUMBING-CAPTURE-001\n// @title Bootstrap captures plumbing files via a single named glob set\n// @status decided (WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P2)\n// @rationale The set of files that constitute \"workspace plumbing\" (non-atom\n// files needed to make a recompiled workspace pnpm-installable and buildable)\n// must be declared in exactly one place. Hand-curating individual file paths\n// would silently break recompilation when new packages or config files are\n// added. A named glob set in this dedicated module is the single authority.\n//\n// Included files: package.json (root + per-package + per-example),\n// tsconfig*.json, pnpm-workspace.yaml, pnpm-lock.yaml, .npmrc, biome.json,\n// vitest.config.ts (root + per-package), README.md in packages/ and examples/.\n//\n// Explicitly excluded: node_modules/, dist/, .git/, .worktrees/, tmp/,\n// coverage/, bootstrap/ (self-reference avoidance — bootstrap/expected-roots.json\n// is the comparison target, not a plumbing file).\n//\n// Also excluded per R7 (Risk 7 in plan.md): packages/*/src/foreign/**/*.ts\n// wrapper files that are not atoms must be captured separately. However,\n// in the current corpus, foreign wrappers ARE shaved atoms and appear in\n// the blocks table. If a file is both a TS source and a plumbing file the\n// TS source wins (atoms/ directory reconstruction covers it).\n//\n// The reviewer verifies: (a) this is the only module that declares plumbing\n// inclusion; (b) after bootstrap, workspace_plumbing has zero rows matching\n// bootstrap/, node_modules/, dist/, .git/, .worktrees/, tmp/, coverage/.\n\n/**\n * Glob patterns for files to include in workspace_plumbing capture.\n *\n * Evaluated relative to the workspace root using micromatch / picomatch\n * semantics. Each pattern is a workspace-relative glob.\n *\n * SINGLE AUTHORITY: no other module may declare a plumbing-inclusion rule.\n * Adding a new bootable non-atom file type? Add it here.\n */\nexport const PLUMBING_INCLUDE_GLOBS: readonly string[] = [\n // Root-level config files\n \"package.json\",\n \"pnpm-workspace.yaml\",\n \"pnpm-lock.yaml\",\n \"tsconfig.json\",\n \"tsconfig.base.json\",\n \"biome.json\",\n \"vitest.config.ts\",\n \".npmrc\",\n // Per-package and per-example config files\n \"packages/*/package.json\",\n \"packages/*/tsconfig.json\",\n \"packages/*/tsconfig.*.json\",\n \"packages/*/vitest.config.ts\",\n \"packages/*/biome.json\",\n \"packages/*/.npmrc\",\n \"examples/*/package.json\",\n \"examples/*/tsconfig.json\",\n \"examples/*/tsconfig.*.json\",\n \"examples/*/vitest.config.ts\",\n \"examples/*/biome.json\",\n \"examples/*/.npmrc\",\n // README files (not bootable, but useful for workspace context)\n // NOTE: deliberately omitted per plan.md §DEC-V2-WORKSPACE-PLUMBING-CAPTURE-001:\n // \"packages/*/README.md is NOT captured (not bootable; documentation only).\"\n\n // @decision DEC-V2-WORKSPACE-PLUMBING-SEED-TRIPLETS-001\n // @title Seed atom triplet sidecars (spec.yak, proof/manifest.json,\n // proof/tests.fast-check.ts) are workspace plumbing\n // @status accepted (WI-FIX-494-TWOPASS-NONDETERM)\n // @rationale The two-pass bootstrap equivalence test (DEC-V2-HARNESS-STRICT-EQUALITY-001)\n // requires that the recompiled workspace produced by compile-self is\n // byte-identical to the original at the block-merkle-root level. Pass 1\n // walks the original workspace where impl.ts has sibling spec.yak and\n // proof/ files on disk. If compile-self materialises only impl.ts (the\n // previous state), pass 2 runs without those sidecars and the shave\n // pipeline's block-content diverges, producing up to 45 symmetric divergent\n // roots for the 5 new seed atoms added in PR #493.\n // Declaring these sidecars as plumbing ensures compile-self writes them\n // into dist-recompiled/, closing the divergence.\n // Glob scope: packages/*/src/blocks/*/ matches only packages/seeds today\n // (the only package with a src/blocks/ subtree); the pattern is tight\n // enough that accidental capture of non-triplet directories is unlikely.\n // Amends DEC-V2-WORKSPACE-PLUMBING-CAPTURE-001.\n \"packages/*/src/blocks/*/spec.yak\",\n \"packages/*/src/blocks/*/proof/manifest.json\",\n \"packages/*/src/blocks/*/proof/tests.fast-check.ts\",\n\n // @decision DEC-V2-WORKSPACE-PLUMBING-PROPS-CORPUS-001\n // @title *.props.ts hand-authored property-test corpus files are workspace plumbing\n // @status accepted (WI-FIX-545-TWOPASS-VALIDATOR, 2026-05-15)\n // @rationale Props files are corpus inputs to the shave pipeline's props-file\n // extractor (packages/shave/src/corpus/props-file.ts:extractFromPropsFile),\n // not atoms — bootstrap.ts:200 explicitly skips them from shaving via the\n // .props.ts filename guard, so capturing them as plumbing never conflicts with\n // atom reconstruction (compile-self's \"TS source wins\" rule from\n // DEC-V2-WORKSPACE-PLUMBING-CAPTURE-001 cannot trigger because props files\n // are never shaved into the blocks table in the first place).\n // The extractor records a filesystem-presence-dependent `path` in the proof\n // manifest (<atomName>.props.ts when present; a generic fallback when absent),\n // so proof_root and block_merkle_root depend on whether the recompiled\n // workspace rematerialises the props file. Capturing them as plumbing makes\n // compile-self rematerialise them, closing the ~45-root divergence (#545).\n //\n // WHY TWO PATTERNS NOT **: expandPlumbingGlob (bootstrap.ts:561-615) supports\n // single-segment * only (each * → regex [^/]*). A ** segment would match a\n // literal directory named ** and expand to zero files (the v2-of-#494 trap,\n // codified as forbidden shortcut FS-5). All 73 *.props.ts files live at\n // exactly two depths under packages/*/src/ (42 at depth 4 matching the first\n // pattern, 31 at depth 5 matching the second), so two literal-depth patterns\n // are exhaustive. If a future *.props.ts is added at depth >= 2 below src/,\n // a third pattern must be added here — the T3c regression guard\n // (DEC-V2-HARNESS-PROPS-CORPUS-CHECK-001) will catch this because it uses\n // a recursive filesystem walk rather than these globs.\n //\n // Amends DEC-V2-WORKSPACE-PLUMBING-CAPTURE-001 and is a sibling of\n // DEC-V2-WORKSPACE-PLUMBING-SEED-TRIPLETS-001.\n \"packages/*/src/*.props.ts\",\n \"packages/*/src/*/*.props.ts\",\n];\n\n/**\n * Path segments and prefixes to exclude from workspace_plumbing capture.\n *\n * A captured file path (workspace-relative, forward-slash-separated) is\n * excluded if it contains ANY of these segments as a path component.\n * This is a fast O(n) segment-based filter rather than glob matching.\n *\n * SINGLE AUTHORITY: add new exclusions here, not in the bootstrap walker.\n */\nexport const PLUMBING_EXCLUDE_SEGMENTS: readonly string[] = [\n \"node_modules\",\n \"dist\",\n \".git\",\n \".worktrees\",\n \"tmp\",\n \"coverage\",\n \".nyc_output\",\n];\n\n/**\n * Path prefixes that disqualify a file from capture.\n *\n * A captured file whose workspace-relative path starts with any of these\n * prefixes is excluded. Used to prevent self-referential capture.\n */\nexport const PLUMBING_EXCLUDE_PREFIXES: readonly string[] = [\n \"bootstrap/\", // self-reference avoidance (R6 in plan.md): the registry and\n // expected-roots.json live here; capturing them would be circular.\n];\n\n/**\n * Returns true if the workspace-relative path passes the plumbing exclusion rules.\n * A file that passes may still be filtered by PLUMBING_INCLUDE_GLOBS.\n */\nexport function plumbingPathAllowed(workspaceRelPath: string): boolean {\n const normalized = workspaceRelPath.replace(/\\\\/g, \"/\");\n\n // Reject excluded prefixes (bootstrap/, etc.).\n for (const prefix of PLUMBING_EXCLUDE_PREFIXES) {\n if (normalized.startsWith(prefix)) return false;\n }\n\n // Reject paths containing excluded segments.\n const segments = normalized.split(\"/\");\n for (const segment of segments) {\n if (PLUMBING_EXCLUDE_SEGMENTS.includes(segment)) return false;\n }\n\n return true;\n}\n","// SPDX-License-Identifier: MIT\n// compile-self.ts — `yakcc compile-self` command (P2 workspace reconstruction).\n//\n// @decision DEC-V2-COMPILE-SELF-CLI-NAMING-001\n// @title `yakcc compile-self` is a top-level command, NOT `yakcc compile --self`\n// @status accepted\n// @rationale Keeps argument parsing and exit-code semantics independent of\n// `yakcc compile`. The two commands have different inputs (compile takes an\n// entry; compile-self walks the corpus) and different outputs (compile writes\n// one module; compile-self writes a workspace-shaped output tree). Co-locating\n// behind a flag would force compile.ts to branch on a fundamentally different\n// code path and would make P2 risk regressing compile.\n//\n// @decision DEC-V2-COMPILE-SELF-WORKSPACE-RECONSTRUCTION-001\n// @title compile-self groups atoms by (sourcePkg, sourceFile) and reconstructs\n// workspace tree from provenance + plumbing\n// @status decided (WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P2)\n// @rationale With P1's provenance columns populated, the natural reconstruction\n// is groupBy(atom, key=(sourcePkg, sourceFile)), sort by sourceOffset ASC,\n// concatenate implSource, emit to <outputDir>/<sourceFile>. Plumbing files\n// materialise from workspace_plumbing rows to their workspace_path locations.\n// The flat-atom output path (<outputDir>/atoms/<hash>.ts) is DELETED in this\n// change, not preserved as a fallback (Sacred Practice #12). The output\n// manifest.json shape evolves to Array<{outputPath, blockMerkleRoot, sourcePkg,\n// sourceFile, sourceOffset}>.\n//\n// Forbidden shortcuts (per Evaluation Contract FS1-FS10):\n// - FS1: NEVER keep atoms/ directory output \"for backward compatibility.\"\n// - FS3: NEVER read plumbing from the filesystem — only registry.listWorkspacePlumbing().\n// - FS8: NEVER infer sourcePkg/sourceFile from atom heuristics — only registry.getBlock().\n//\n// @decision DEC-V2-COMPILE-SELF-EQ-001\n// @title Functional equivalence is the P2 acceptance bar (confirmed)\n// @status re-confirmed (WI-V2-REGISTRY-SOURCE-FILE-PROVENANCE P2)\n// @rationale P2 closes this DEC at the end-to-end level: recompiled workspace\n// builds, tests pass, and recompiled bootstrap --verify produces byte-identical\n// bootstrap/expected-roots.json. The bar is functional, not byte-level source.\n//\n// @decision DEC-V2-COMPILE-SELF-EXIT-CODE-001\n// @title compile-self returns exit 0 on success, exit 1 on usage/runtime errors\n// @status accepted (unchanged from A2)\n//\n// @decision DEC-V2-CORPUS-DISTRIBUTION-001\n// @title SQLite registry + dist-recompiled/ are both gitignored\n// @status accepted (unchanged from A2)\n\nimport { mkdirSync, writeFileSync } from \"node:fs\";\nimport { existsSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { openRegistry } from \"@yakcc/registry\";\nimport type { BlockTripletRow, Registry } from \"@yakcc/registry\";\nimport type { Logger } from \"../index.js\";\n\n// ---------------------------------------------------------------------------\n// CLI argument parsing options\n// ---------------------------------------------------------------------------\n\nconst COMPILE_SELF_PARSE_OPTIONS = {\n output: { type: \"string\" as const, short: \"o\" },\n registry: { type: \"string\" as const, short: \"r\" },\n help: { type: \"boolean\" as const, short: \"h\", default: false },\n} as const;\n\nconst DEFAULT_OUTPUT_DIR = \"dist-recompiled\";\nconst DEFAULT_REGISTRY_PATH = \"bootstrap/yakcc.registry.sqlite\";\n\n// ---------------------------------------------------------------------------\n// Embedding provider for read-only registry open\n//\n// Bootstrap uses zero-vector embeddings (DEC-V2-BOOTSTRAP-EMBEDDING-001).\n// compile-self also uses zero-vector embeddings: we only enumerate atoms,\n// never run vector search. Deterministic zero vectors avoid any network dependency.\n// ---------------------------------------------------------------------------\n\nconst NULL_EMBEDDING_OPTS = {\n embeddings: {\n dimension: 384,\n modelId: \"compile-self/null-zero\",\n embed: async (_text: string): Promise<Float32Array> => new Float32Array(384),\n },\n} as const;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * One compose-path gap row. Never silently dropped (F1 / Sacred Practice #5).\n *\n * reason values:\n * 'foreign-leaf-skipped' — foreign atoms are opaque leaves, not inlined (informational)\n * 'null-provenance' — local atom with NULL sourcePkg AND NULL sourceFile (P2 new)\n * 'unresolved-pointer' — PointerEntry with no in-corpus resolution\n * 'glue-absorbed' — atom in blocks.source_file but not block_occurrences; content is\n * already present in the glue blob (informational, no data lost)\n * 'other' — unexpected; triggers exit 1 (Sacred Practice #5)\n *\n * @decision DEC-V2-GLUE-GHOST-ATOM-EXCLUSION-001\n */\ninterface GapRow {\n readonly blockMerkleRoot: string;\n readonly packageName: string;\n readonly reason:\n | \"null-provenance\"\n | \"unresolved-pointer\"\n | \"foreign-leaf-skipped\"\n | \"glue-absorbed\"\n | \"other\";\n readonly detail: string;\n}\n\n/**\n * One entry in manifest.json (P2 shape — per-atom, workspace-shaped).\n *\n * @decision DEC-V2-COMPILE-SELF-WORKSPACE-RECONSTRUCTION-001\n * manifest.json shape evolves to Array<{outputPath, blockMerkleRoot, sourcePkg,\n * sourceFile, sourceOffset}> — one row per block, file-shaped, sorted by\n * (outputPath ASC, sourceOffset ASC).\n */\ninterface ManifestEntry {\n readonly outputPath: string;\n readonly blockMerkleRoot: string;\n readonly sourcePkg: string | null;\n readonly sourceFile: string | null;\n readonly sourceOffset: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// compileSelf — P2 workspace reconstruction implementation\n// ---------------------------------------------------------------------------\n\n/**\n * Handler for `yakcc compile-self`.\n *\n * P2 status: workspace reconstruction. Groups atoms by (sourcePkg, sourceFile),\n * sorts by sourceOffset ASC, concatenates implSource, and emits to\n * <outputDir>/<sourceFile> (workspace-shaped). Plumbing files from\n * workspace_plumbing are materialised to their workspacePath locations.\n * The flat-atom output path is DELETED (not produced — Sacred Practice #12).\n *\n * CLI flags:\n * --output <dir> Output directory for the recompiled workspace (default: dist-recompiled/)\n * --registry <p> Path to the SQLite registry (default: bootstrap/yakcc.registry.sqlite)\n * --help / -h Print usage and exit 0\n *\n * Exit codes:\n * 0 — success (gap report may be non-empty for informational foreign-leaf rows)\n * 1 — usage or runtime error (bad flags, registry not found, pipeline failure)\n *\n * Per DEC-CLI-LOGGER-001: uses the Logger interface for all output — no direct\n * console.log/error calls — enabling test capture via CollectingLogger.\n *\n * @param argv - Subcommand args after \"compile-self\" has been consumed.\n * @param logger - Output sink; defaults to CONSOLE_LOGGER via the caller.\n * @returns Promise<number> — 0 on success, 1 on error.\n */\nexport async function compileSelf(argv: ReadonlyArray<string>, logger: Logger): Promise<number> {\n // Parse CLI arguments.\n let values: { output?: string; registry?: string; help?: boolean };\n try {\n const parsed = parseArgs({\n args: [...argv],\n options: COMPILE_SELF_PARSE_OPTIONS,\n allowPositionals: false,\n strict: true,\n });\n values = parsed.values;\n } catch (err) {\n logger.error(`error: compile-self: ${err instanceof Error ? err.message : String(err)}`);\n logger.error(\"Usage: yakcc compile-self [--output <dir>] [--registry <path>] [--help]\");\n return 1;\n }\n\n if (values.help === true) {\n logger.log(\n [\n \"yakcc compile-self — recompile the yakcc corpus from the registry into a workspace\",\n \"\",\n \"USAGE\",\n \" yakcc compile-self [--output <dir>] [--registry <path>]\",\n \"\",\n \"OPTIONS\",\n ` --output, -o <dir> Output directory for the recompiled workspace (default: ${DEFAULT_OUTPUT_DIR})`,\n ` --registry, -r <p> SQLite registry path (default: ${DEFAULT_REGISTRY_PATH})`,\n \" --help, -h Print this help and exit\",\n \"\",\n \"DESCRIPTION\",\n \" Groups atoms by (sourcePkg, sourceFile), sorts by sourceOffset ASC,\",\n \" concatenates implSource blobs, and emits each file to:\",\n \" <output>/<sourceFile> (e.g. packages/cli/src/commands/compile.ts)\",\n \" Plumbing files from workspace_plumbing are materialised to their\",\n \" workspace_path locations under <output>/.\",\n \" A manifest.json is written with shape:\",\n \" Array<{ outputPath, blockMerkleRoot, sourcePkg, sourceFile, sourceOffset }>\",\n \" sorted by (outputPath ASC, sourceOffset ASC).\",\n \"\",\n \" Gap rows (atoms that cannot be placed in the workspace):\",\n \" null-provenance — local atom with NULL sourcePkg AND sourceFile\",\n \" foreign-leaf-skipped — foreign atoms are opaque leaves (informational)\",\n \"\",\n \"EXIT CODES\",\n \" 0 success (gap report may be non-empty for informational foreign-leaf rows)\",\n \" 1 usage or runtime error (registry not found, pipeline failure)\",\n \"\",\n \"WI-V2-CORPUS-AND-COMPILE-SELF-EQ (issue #59), slice P2.\",\n \"DEC-V2-COMPILE-SELF-WORKSPACE-RECONSTRUCTION-001 (workspace reconstruction).\",\n \"DEC-V2-COMPILE-SELF-EQ-001 (functional equivalence bar, re-confirmed).\",\n \"DEC-V2-CORPUS-DISTRIBUTION-001 (output is gitignored, not committed).\",\n ].join(\"\\n\"),\n );\n return 0;\n }\n\n const outputDir = resolve(values.output ?? DEFAULT_OUTPUT_DIR);\n const registryPath = resolve(values.registry ?? DEFAULT_REGISTRY_PATH);\n\n // Validate registry path exists before invoking the pipeline.\n // Fail loudly with a clear error if it doesn't (Sacred Practice #5 / F8).\n if (!existsSync(registryPath)) {\n logger.error(`error: compile-self: registry not found at ${registryPath}`);\n logger.error(\" Run 'yakcc bootstrap' first to populate the registry.\");\n return 1;\n }\n\n logger.log(\"yakcc compile-self — P2 workspace reconstruction\");\n logger.log(` registry: ${registryPath}`);\n logger.log(` output: ${outputDir}`);\n logger.log(\"\");\n\n // Run the compile pipeline.\n let pipelineResult: {\n recompiledFiles: number;\n gapReport: GapRow[];\n sourceFilesEmitted: number;\n plumbingFilesEmitted: number;\n };\n try {\n pipelineResult = await _runPipeline(registryPath, outputDir, logger);\n } catch (err) {\n logger.error(\n `error: compile-self: pipeline failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n return 1;\n }\n\n // Log summary.\n logger.log(\n `compile-self: ${pipelineResult.sourceFilesEmitted} source files emitted → ${outputDir}/`,\n );\n logger.log(\n `compile-self: ${pipelineResult.plumbingFilesEmitted} plumbing files materialised → ${outputDir}/`,\n );\n logger.log(`compile-self: manifest written → ${outputDir}/manifest.json`);\n\n // Log gap report (loud — never silent, per F1 / Sacred Practice #5).\n if (pipelineResult.gapReport.length > 0) {\n const foreignSkipped = pipelineResult.gapReport.filter(\n (r) => r.reason === \"foreign-leaf-skipped\",\n ).length;\n const nullProvenance = pipelineResult.gapReport.filter(\n (r) => r.reason === \"null-provenance\",\n ).length;\n const unresolvedPointer = pipelineResult.gapReport.filter(\n (r) => r.reason === \"unresolved-pointer\",\n ).length;\n const glueAbsorbed = pipelineResult.gapReport.filter(\n (r) => r.reason === \"glue-absorbed\",\n ).length;\n const other = pipelineResult.gapReport.filter((r) => r.reason === \"other\").length;\n\n logger.log(\"\");\n logger.log(`compile-self: compose-path-gap report (${pipelineResult.gapReport.length} rows):`);\n if (foreignSkipped > 0) {\n logger.log(\n ` foreign-leaf-skipped: ${foreignSkipped} (informational — foreign atoms not inlined)`,\n );\n }\n if (nullProvenance > 0) {\n logger.log(\n ` null-provenance: ${nullProvenance} (atoms with NULL sourcePkg AND sourceFile — cannot place in workspace)`,\n );\n }\n if (unresolvedPointer > 0) {\n logger.log(\n ` unresolved-pointer: ${unresolvedPointer} (PointerEntry with no in-corpus resolution)`,\n );\n }\n if (glueAbsorbed > 0) {\n logger.log(\n ` glue-absorbed: ${glueAbsorbed} (informational — atoms in blocks.source_file but not block_occurrences; content present in glue blob)`,\n );\n }\n if (other > 0) {\n logger.error(` other (unexpected): ${other} — see gap report rows for detail`);\n for (const row of pipelineResult.gapReport.filter((r) => r.reason === \"other\")) {\n logger.error(` [${row.blockMerkleRoot.slice(0, 8)}] ${row.detail}`);\n }\n }\n\n // 'other' rows are unexpected failures → non-zero exit.\n if (other > 0) {\n return 1;\n }\n } else {\n logger.log(\n \"compile-self: compose-path-gap report: empty (all atoms placed in workspace successfully)\",\n );\n }\n\n return 0;\n}\n\n// ---------------------------------------------------------------------------\n// _runPipeline — internal compile-self pipeline (P2)\n//\n// Mirrors the logic in examples/v2-self-shave-poc/src/compile-pipeline.ts.\n// Kept separate because @yakcc/cli's tsconfig has rootDir: src and cannot\n// import from examples/. The canonical testable module is compile-pipeline.ts.\n//\n// Algorithm (DEC-V2-COMPILE-SELF-WORKSPACE-RECONSTRUCTION-001):\n// 1. Open registry (NULL embedding provider — read-only enumeration).\n// 2. Fetch all local atoms via registry.exportManifest().\n// 3. For each atom: fetch BlockTripletRow via registry.getBlock(merkleRoot).\n// - If kind='foreign': emit gap row 'foreign-leaf-skipped', skip.\n// - If sourcePkg AND sourceFile both NULL: emit gap row 'null-provenance'.\n// - Else: collect into groupMap[`${sourcePkg}/${sourceFile}`].atoms.\n// 4. For each group:\n// - Sort atoms by sourceOffset ASC (NULLs sorted to end per I7 resolution).\n// - Concatenate implSource blobs in that order.\n// - Write to <outputDir>/<sourceFile>, mkdir -p the parent.\n// 5. Fetch all plumbing via registry.listWorkspacePlumbing().\n// 6. For each plumbing entry: write contentBytes to <outputDir>/<workspacePath>.\n// 7. Write manifest.json sorted by (outputPath ASC, sourceOffset ASC).\n// 8. The <outputDir>/atoms/ directory is NOT produced (Sacred Practice #12).\n// ---------------------------------------------------------------------------\n\nasync function _runPipeline(\n registryPath: string,\n outputDir: string,\n logger: Logger,\n): Promise<{\n recompiledFiles: number;\n gapReport: GapRow[];\n sourceFilesEmitted: number;\n plumbingFilesEmitted: number;\n}> {\n const registry: Registry = await openRegistry(registryPath, NULL_EMBEDDING_OPTS);\n\n try {\n // Step 1: Enumerate all atoms via exportManifest (single authority — Sacred Practice #12).\n const manifestEntries = await registry.exportManifest();\n\n logger.log(`compile-self: ${manifestEntries.length} total atoms in registry`);\n\n // Step 2: Group atoms by (sourcePkg, sourceFile).\n // Key: workspace-relative file path (sourceFile, e.g. 'packages/cli/src/commands/foo.ts').\n // @decision DEC-V2-COMPILE-SELF-WORKSPACE-RECONSTRUCTION-001\n type GroupKey = string; // sourceFile — workspace-relative path, used only as Map key\n interface AtomGroup {\n sourcePkg: string;\n sourceFile: string;\n atoms: Array<{ block: BlockTripletRow; blockMerkleRoot: string }>;\n // addedRoots: tracks which blockMerkleRoots are already in this group.\n // An atom may appear at N offsets within one file (N occurrence rows, different\n // source_offset). The group adds it only once — step 3 resolves the offset via\n // listOccurrencesBySourceFile. Without this guard, multi-offset atoms would be\n // added N times, producing duplicate manifest entries (I9 violation).\n addedRoots: Set<string>;\n }\n\n const groupMap = new Map<GroupKey, AtomGroup>();\n const gapReport: GapRow[] = [];\n\n for (const entry of manifestEntries) {\n const block = await registry.getBlock(entry.blockMerkleRoot);\n\n if (block === null) {\n // Registry inconsistency — loud failure (Sacred Practice #5).\n gapReport.push({\n blockMerkleRoot: entry.blockMerkleRoot,\n packageName: \"unknown\",\n reason: \"other\",\n detail:\n \"Block not found in registry despite being enumerated by exportManifest(). Registry may be corrupted.\",\n });\n continue;\n }\n\n // Foreign atoms: informational skip (not inlined by design).\n if (block.kind === \"foreign\") {\n gapReport.push({\n blockMerkleRoot: entry.blockMerkleRoot,\n packageName: block.foreignPkg ?? \"unknown\",\n reason: \"foreign-leaf-skipped\",\n detail: `Foreign atom ${block.foreignPkg ?? \"unknown\"} is an opaque leaf — not inlined (DEC-V2-FOREIGN-BLOCK-SCHEMA-001).`,\n });\n continue;\n }\n\n // @decision DEC-STORAGE-IDEMPOTENT-001 (option b / #355)\n // @title Group atoms by block_occurrences, not blocks.source_file (stale first-observed)\n // @status decided (WI-V2-STORAGE-IDEMPOTENT-RECOMPILE #355)\n // @rationale blocks.source_file is a first-observed shim — it points to the file where\n // the atom was first encountered, not all files where it appears. block_occurrences is\n // refreshed atomically per file on every bootstrap pass and accurately tracks all files\n // containing each atom. Grouping by block_occurrences fixes shared-atom gaps where atoms\n // were incorrectly placed in the first-observed file's group instead of all files they\n // appear in. Fallback: when block_occurrences has no rows for this atom (pre-v9 registry\n // or atom removed from source), fall back to block.sourceFile for backward compatibility.\n const occurrences = await registry.listOccurrencesByMerkleRoot(entry.blockMerkleRoot);\n\n if (occurrences.length > 0) {\n // Per-occurrence placement: add this atom to every file's group it appears in.\n // Shared atoms (same implSource content appearing in N files) correctly appear\n // in N groups — one manifest entry per (file, atom) pair.\n //\n // Deduplication: an atom may appear at multiple offsets within the same file\n // (N occurrence rows, same source_file, different source_offset). Add only once\n // per file — step 3 resolves the correct offsets via listOccurrencesBySourceFile.\n for (const occ of occurrences) {\n const key: GroupKey = occ.sourceFile;\n const existing = groupMap.get(key);\n if (existing !== undefined) {\n if (!existing.addedRoots.has(entry.blockMerkleRoot)) {\n existing.addedRoots.add(entry.blockMerkleRoot);\n existing.atoms.push({ block, blockMerkleRoot: entry.blockMerkleRoot });\n }\n } else {\n groupMap.set(key, {\n sourcePkg: occ.sourcePkg,\n sourceFile: occ.sourceFile,\n atoms: [{ block, blockMerkleRoot: entry.blockMerkleRoot }],\n addedRoots: new Set([entry.blockMerkleRoot]),\n });\n }\n }\n } else {\n // Fallback: no block_occurrences rows (pre-v9 registry or atom not in any current file).\n // Use blocks.source_* columns for backward compatibility.\n // @decision I7 resolution (plan.md §DEC-V2-COMPILE-SELF-WORKSPACE-RECONSTRUCTION-001):\n // Atoms with NULL sourcePkg AND NULL sourceFile emit a 'null-provenance' gap row.\n // These are atoms shaved before P1 (pre-v7 schema rows) or seed blocks.\n // Running `yakcc bootstrap` from a P1+ CLI populates provenance for all corpus atoms.\n if (block.sourcePkg == null || block.sourceFile == null) {\n gapReport.push({\n blockMerkleRoot: entry.blockMerkleRoot,\n packageName: block.sourcePkg ?? \"unknown\",\n reason: \"null-provenance\",\n detail:\n \"Atom has NULL sourcePkg and/or NULL sourceFile — cannot place in workspace tree. \" +\n \"Re-run 'yakcc bootstrap' with a P1+ CLI to populate provenance.\",\n });\n continue;\n }\n const key: GroupKey = block.sourceFile;\n const existing = groupMap.get(key);\n if (existing !== undefined) {\n if (!existing.addedRoots.has(entry.blockMerkleRoot)) {\n existing.addedRoots.add(entry.blockMerkleRoot);\n existing.atoms.push({ block, blockMerkleRoot: entry.blockMerkleRoot });\n }\n } else {\n groupMap.set(key, {\n sourcePkg: block.sourcePkg,\n sourceFile: block.sourceFile,\n atoms: [{ block, blockMerkleRoot: entry.blockMerkleRoot }],\n addedRoots: new Set([entry.blockMerkleRoot]),\n });\n }\n }\n }\n\n // Step 3: Emit one TS file per group, interleaving glue + atoms by sourceOffset ASC.\n //\n // @decision DEC-V2-COMPILE-SELF-GLUE-INTERLEAVING-001\n // @title compile-self interleaves glue blobs with atom implSources in sourceOffset order\n // @status decided (WI-V2-WORKSPACE-PLUMBING-GLUE-CAPTURE #333)\n // @rationale\n // With glue blobs captured by bootstrap (DEC-V2-GLUE-CAPTURE-AUTHORITY-001), the\n // reconstructed file is: glue_region_0 ++ atom_0.implSource ++ glue_region_1 ++ ...\n // ++ glue_region_n. Glue region boundaries are derived at reconstruct time from\n // atom sourceOffset (character position in original file) and implSource.length.\n //\n // Invariant: implSource.length === sourceRange.end - sourceRange.start (verbatim\n // source text — no transformation). This is the single-authority derivation:\n // glue chars before atom[i] = atom[i].sourceOffset - prev_original_end\n // where prev_original_end accumulates as cursor through the original file.\n //\n // Fallback: when no glue row exists for a file (getSourceFileGlue → null),\n // compile-self falls back to atom-only concatenation (pre-#333 behaviour),\n // logs a warning, and continues. A null glue row is expected only for\n // bootstrap runs that predate #333 (schema < v8).\n //\n // Total length assertion: glue_blob_char_count + sum(implSource_lengths)\n // must equal the reconstructed file char count. This is verified at emit time.\n //\n // @decision I7 resolution: NULLs sort to end (append as suffix); warn but do not fail.\n // @decision I8 resolution: overlapping offsets produce 'other' gap row (cannot arise\n // in well-formed corpora because INSERT OR IGNORE is per blockMerkleRoot PK).\n const manifest: ManifestEntry[] = [];\n let sourceFilesEmitted = 0;\n\n mkdirSync(outputDir, { recursive: true });\n\n for (const [, group] of groupMap) {\n // @decision DEC-V2-STORAGE-IDEMPOTENT-RECOMPILE-001\n // Read current-truth atom offsets from block_occurrences (not blocks.source_offset).\n // blocks.source_offset is a stale first-observed-wins value; block_occurrences is\n // refreshed atomically per file on every bootstrap pass. Using block_occurrences ensures\n // atoms are placed at their current positions even after source edits.\n //\n // If block_occurrences is empty (registry predates schema v9 or bootstrap hasn't run),\n // fall back to blocks.source_offset for backward compatibility. The fallback produces the\n // same output as the pre-#355 behaviour (stale offsets are better than no offsets).\n const occurrences = await registry.listOccurrencesBySourceFile(group.sourceFile);\n\n // Build a map from blockMerkleRoot → ALL offsets where it appears in this file.\n // A single atom may appear at N different offsets (same implSource repeated N times).\n // Each offset produces a separate sorted entry so glue-interleaving emits the atom\n // N times at the correct positions (mirroring the original source).\n //\n // @decision DEC-STORAGE-IDEMPOTENT-001 multi-offset expansion\n // @rationale A single-offset map (root → last-seen offset) misses N-1 earlier\n // occurrences for multi-offset atoms, producing malformed reconstructed files.\n const occurrencesByRoot = new Map<string, number[]>();\n for (const occ of occurrences) {\n const existing = occurrencesByRoot.get(occ.blockMerkleRoot);\n if (existing !== undefined) {\n existing.push(occ.sourceOffset);\n } else {\n occurrencesByRoot.set(occ.blockMerkleRoot, [occ.sourceOffset]);\n }\n }\n\n // Expand group atoms: each unique atom gets one entry per occurrence offset.\n // For atoms with 1 occurrence: same as before. For N occurrences: N entries.\n //\n // @decision DEC-V2-GLUE-GHOST-ATOM-EXCLUSION-001\n // @title When v9 occurrence rows exist for a file, atoms absent from block_occurrences\n // are glue-absorbed and must be excluded from reconstruction (not placed at stale offset)\n // @status decided (WI-V2-STORAGE-IDEMPOTENT-RECOMPILE #355 Bug D fix)\n // @rationale\n // bootstrap.captureSourceFileGlue uses getAtomRangesBySourceFile (which queries\n // block_occurrences) to compute the glue blob. Atoms absent from block_occurrences\n // are NOT subtracted from the source — their content is captured IN the glue blob.\n // Placing such an atom at its stale blocks.source_offset inserts its implSource\n // inside a glue region that already contains it, producing duplicate content.\n // Concrete case: 'const OFFLINE_DIMENSION = 384;' (root ad511ef1) in embeddings.ts\n // had blocks.source_offset=10304 but 0 occurrence rows; the glue at [10206..10401]\n // contained it, so naive fallback caused a 30-char duplication at offset 10334.\n //\n // Guard: when occurrences.length > 0 the file has been processed by v9+ bootstrap\n // and block_occurrences is authoritative. Atoms absent from occurrencesByRoot are\n // glue-absorbed — skip them entirely.\n // When occurrences.length === 0 (pre-v9 registry, file not processed yet), fall back\n // to blocks.source_offset for ALL atoms (pre-#355 behaviour, backward compatibility).\n //\n // See also: 32 \"ghost\" blocks in the yakcc corpus (blocks with source_file set but\n // 0 occurrence rows) across 14 files — each would cause the same duplication without\n // this guard.\n const v9ProcessedFile = occurrences.length > 0;\n interface AtomWithOffset {\n block: BlockTripletRow;\n blockMerkleRoot: string;\n effectiveOffset: number | null;\n }\n const atomsWithOffset: AtomWithOffset[] = [];\n for (const atom of group.atoms) {\n const offsets = occurrencesByRoot.get(atom.blockMerkleRoot);\n if (offsets !== undefined && offsets.length > 0) {\n for (const offset of offsets) {\n atomsWithOffset.push({ ...atom, effectiveOffset: offset });\n }\n } else if (v9ProcessedFile) {\n // v9 bootstrap processed this file: atom is absent from block_occurrences, meaning\n // it was absorbed into the glue blob. Exclude it from reconstruction to avoid\n // inserting its implSource inside a glue region that already contains the same content.\n // Emit an informational gap row so the uniquePlaced + gap = total invariant holds.\n gapReport.push({\n blockMerkleRoot: atom.blockMerkleRoot,\n packageName: group.sourcePkg,\n reason: \"glue-absorbed\",\n detail: `Atom stale blocks.source_offset=${atom.block.sourceOffset ?? \"null\"} in ${group.sourceFile} — absent from block_occurrences (v9 processed), content already present in glue blob. Excluded from reconstruction to prevent duplicate content. (DEC-V2-GLUE-GHOST-ATOM-EXCLUSION-001)`,\n });\n } else {\n // Pre-v9 registry: fall back to blocks.source_offset (stale first-observed-wins).\n atomsWithOffset.push({ ...atom, effectiveOffset: atom.block.sourceOffset ?? null });\n }\n }\n\n // Sort: current-truth offsets ascending first, then null offsets appended.\n const sorted = [...atomsWithOffset].sort((a, b) => {\n const ao = a.effectiveOffset;\n const bo = b.effectiveOffset;\n if (ao === null && bo === null) return 0;\n if (ao === null) return 1; // nulls to end\n if (bo === null) return -1;\n return ao - bo;\n });\n\n // Attempt glue-interleaved reconstruction (DEC-V2-COMPILE-SELF-GLUE-INTERLEAVING-001).\n let fileContent: string;\n\n const glueEntry = await registry.getSourceFileGlue(group.sourcePkg, group.sourceFile);\n\n if (glueEntry !== null && sorted.every((a) => a.effectiveOffset !== null)) {\n // Glue-interleaved path: reconstruct the original file by weaving glue + atoms.\n //\n // Algorithm:\n // - Decode glue blob to string (UTF-8 reverse of bootstrap's TextEncoder.encode)\n // - Walk atoms in sourceOffset order; for each atom, extract the glue chars\n // between the end of the previous atom and the start of this atom\n // - Append any trailing glue chars after the last atom\n // @decision DEC-V2-COMPILE-SELF-GLUE-DECODE-IGNOREBOM-001\n // @title compile-self glue decode preserves UTF-8 BOM bytes\n // @status decided (WI-FIX-543, issue #543)\n // @rationale\n // `new TextDecoder()` defaults to `ignoreBOM: false`, which silently strips\n // a leading UTF-8 BOM (U+FEFF) from the decoded string. That breaks the\n // round-trip invariant the reconstruction algorithm depends on: glueString\n // length must equal the sum of all glue-span lengths in original-source\n // coordinates. A BOM-carrying source file would otherwise produce a\n // reconstructed string one UTF-16 code unit shorter than the original,\n // shifting every cross-atom glue slice by one position past the BOM region\n // and yielding invalid TypeScript. `ignoreBOM: true` preserves the BOM as a\n // U+FEFF code unit in the decoded string, exactly mirroring the bytes\n // `bootstrap.captureSourceFileGlue` stored. Files without a BOM are\n // unaffected. Validates: issue #543, packages/hooks-base/src/import-intercept.ts.\n const glueString = new TextDecoder(\"utf-8\", { ignoreBOM: true }).decode(\n glueEntry.contentBlob,\n );\n\n // @decision DEC-V2-COMPILE-SELF-GLUE-INTERLEAVING-001 (overlap handling)\n // @title Reconstruction uses merged intervals to mirror computeGlueBlob's behaviour\n // @status decided (WI-V2-WORKSPACE-PLUMBING-GLUE-CAPTURE #333 overlap fix)\n // @rationale\n // bootstrap.ts computeGlueBlob() merges overlapping atom intervals before\n // computing glue gaps (so the glue does NOT contain chars inside merged intervals).\n // The reconstruction must mirror this exactly: build the same merged intervals,\n // then walk merged intervals (not individual atoms) to advance gluePos.\n // Within each merged interval, emit atoms in sourceOffset order, skipping stale\n // atoms whose range is already covered by a prior atom in the interval.\n // With block_occurrences (#355), stale offsets are eliminated: each file's\n // occurrences are refreshed atomically on every bootstrap pass, so overlapping\n // intervals no longer occur in normal operation. The merge is retained defensively.\n //\n // Algorithm:\n // 1. Build merged intervals (same merge as computeGlueBlob).\n // 2. For each merged interval:\n // a. Emit glue chars from gluePos up to interval.start.\n // b. Emit atoms within the interval in sourceOffset order, skipping stale ones.\n // c. Advance prevMergedEnd = interval.end (gluePos mirrors this in the glue string).\n // 3. Emit trailing glue after the last merged interval.\n\n // Step 1: compute merged intervals (same merge as computeGlueBlob).\n interface MergedInterval {\n start: number;\n end: number;\n atoms: Array<(typeof sorted)[number]>;\n }\n const mergedIntervals: MergedInterval[] = [];\n for (const atom of sorted) {\n const start = atom.effectiveOffset as number;\n const end = start + atom.block.implSource.length;\n const last = mergedIntervals[mergedIntervals.length - 1];\n if (last !== undefined && start < last.end) {\n if (end > last.end) last.end = end;\n last.atoms.push(atom);\n } else {\n mergedIntervals.push({ start, end, atoms: [atom] });\n }\n }\n\n // Step 2: interleave glue + atoms, walking merged intervals.\n const parts: string[] = [];\n let prevMergedEnd = 0; // end of the last merged interval (original file coord)\n let gluePosCursor = 0; // cursor in glueString\n\n for (const interval of mergedIntervals) {\n // 2a: glue chars from previous merged interval end to this interval's start.\n const glueBetween = interval.start - prevMergedEnd;\n if (glueBetween > 0) {\n parts.push(glueString.slice(gluePosCursor, gluePosCursor + glueBetween));\n gluePosCursor += glueBetween;\n }\n\n // 2b: atoms within this interval, skipping overlapping ones.\n let intervalCursor = interval.start;\n for (const atom of interval.atoms) {\n const atomStart = atom.effectiveOffset as number;\n if (atomStart < intervalCursor) {\n // Overlapping atom: start is behind the cursor — skip it.\n logger.log(\n `compile-self: skipping overlapping atom at sourceOffset=${atomStart} in ${group.sourceFile}` +\n ` (interval cursor at ${intervalCursor}) — overlap artifact`,\n );\n continue;\n }\n parts.push(atom.block.implSource);\n intervalCursor = atomStart + atom.block.implSource.length;\n }\n\n prevMergedEnd = interval.end;\n }\n\n // Step 3: trailing glue after the last merged interval.\n if (gluePosCursor < glueString.length) {\n parts.push(glueString.slice(gluePosCursor));\n }\n\n if (parts.length > 0) {\n fileContent = parts.join(\"\");\n } else {\n // Fallback: no atoms placed (empty merged intervals). Use atom-only concat.\n fileContent = sorted.map((a) => a.block.implSource).join(\"\");\n }\n } else {\n // Fallback: no glue captured (pre-#333 bootstrap) or some atoms lack sourceOffset.\n // Log informational; do not fail (backward compatibility).\n if (glueEntry === null) {\n logger.log(\n `compile-self: no glue row for ${group.sourceFile} — using atom-only concatenation (re-run bootstrap to capture glue)`,\n );\n }\n fileContent = sorted.map((a) => a.block.implSource).join(\"\");\n }\n\n // Emit to <outputDir>/<sourceFile>.\n const outputPath = join(outputDir, group.sourceFile);\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, fileContent, \"utf-8\");\n sourceFilesEmitted++;\n\n // Add one manifest row per atom (per DEC-V2-COMPILE-SELF-WORKSPACE-RECONSTRUCTION-001).\n // sourceOffset in manifest now reflects current-truth from block_occurrences.\n for (const atom of sorted) {\n manifest.push({\n outputPath: group.sourceFile, // workspace-relative path (same for all atoms in group)\n blockMerkleRoot: atom.blockMerkleRoot,\n sourcePkg: atom.block.sourcePkg ?? null,\n sourceFile: atom.block.sourceFile ?? null,\n sourceOffset: atom.effectiveOffset,\n });\n }\n }\n\n // Step 4: Materialise plumbing files from registry.\n // ONLY registry.listWorkspacePlumbing() is the authority — no filesystem reads\n // at compile-self time (DEC-V2-WORKSPACE-PLUMBING-AUTHORITY-001 / FS3).\n const plumbingEntries = await registry.listWorkspacePlumbing();\n let plumbingFilesEmitted = 0;\n\n for (const plumbing of plumbingEntries) {\n const outputPath = join(outputDir, plumbing.workspacePath);\n mkdirSync(dirname(outputPath), { recursive: true });\n writeFileSync(outputPath, plumbing.contentBytes);\n plumbingFilesEmitted++;\n }\n\n // Step 5: Write manifest.json — sorted by (outputPath ASC, sourceOffset ASC).\n // @decision DEC-V2-COMPILE-SELF-WORKSPACE-RECONSTRUCTION-001:\n // manifest shape evolves to Array<{outputPath, blockMerkleRoot, sourcePkg,\n // sourceFile, sourceOffset}>. Sorted for deterministic output.\n manifest.sort((a, b) => {\n const pathCmp = (a.outputPath ?? \"\").localeCompare(b.outputPath ?? \"\");\n if (pathCmp !== 0) return pathCmp;\n const ao = a.sourceOffset ?? Number.MAX_SAFE_INTEGER;\n const bo = b.sourceOffset ?? Number.MAX_SAFE_INTEGER;\n return ao - bo;\n });\n\n writeFileSync(\n join(outputDir, \"manifest.json\"),\n `${JSON.stringify(manifest, null, 2)}\\n`,\n \"utf-8\",\n );\n\n logger.log(\n `compile-self: ${manifestEntries.length} total atoms, ${sourceFilesEmitted} source files emitted, ${gapReport.length} gap rows`,\n );\n\n return {\n recompiledFiles: sourceFilesEmitted,\n gapReport,\n sourceFilesEmitted,\n plumbingFilesEmitted,\n };\n } finally {\n await registry.close();\n }\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CLI-COMPILE-001: compile command reads the entry as either a BlockMerkleRoot\n// (64-hex string), a path to a JSON SpecYak file, or a directory path. When a directory\n// is given, it resolves to <dir>/spec.yak and defaults --out to <dir>/dist. When a\n// spec file path is given, specHash() derives the specHash, selectBlocks() fetches all\n// satisfying BlockMerkleRoots, and the first is used as the entry. The assembled artifact\n// is written to <out>/module.ts and <out>/manifest.json. assemble() receives\n// knownMerkleRoots from seedRegistry so the pre-scan can build the full stem index before\n// DFS traversal.\n// Status: updated (WI-T06)\n// Rationale: WI-T06 migrated the demo from contract.json to spec.yak (triplet shape).\n// The directory resolver now looks for spec.yak exclusively — contract.json is deleted\n// (Sacred Practice #12: no parallel mechanisms, DEC-WI009-SUBSUMED-021).\n// WI-T03/T04 migrated the compile/registry API from ContractId to BlockMerkleRoot.\n// The entry resolution calls selectBlocks(specHash) to find the matching BlockMerkleRoot.\n\nimport { mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { type Artifact, assemble } from \"@yakcc/compile\";\nimport {\n type BlockMerkleRoot,\n type Granularity,\n type SpecYak,\n parseGranularity,\n specHash,\n} from \"@yakcc/contracts\";\nimport { type Registry, type RegistryOptions, openRegistry } from \"@yakcc/registry\";\nimport { seedRegistry } from \"@yakcc/seeds\";\nimport type { Logger } from \"../index.js\";\nimport { DEFAULT_REGISTRY_PATH } from \"./registry-init.js\";\n\n/** Internal options for compile — not exposed in CLI args. */\nexport interface CompileOptions {\n embeddings?: RegistryOptions[\"embeddings\"];\n}\n\n/** A 64-hex string that may be a BlockMerkleRoot. */\nfunction isHex64(s: string): boolean {\n return /^[0-9a-f]{64}$/i.test(s);\n}\n\n/**\n * Handler for `yakcc compile <entry> [--registry <p>] [--out <dir>]`.\n *\n * <entry> is a BlockMerkleRoot (64-hex string), a path to a JSON SpecYak file,\n * or a directory path containing spec.yak.\n * Writes <out>/module.ts and <out>/manifest.json.\n *\n * @param argv - Remaining argv after `compile` has been consumed (includes the positional).\n * @param logger - Output sink; defaults to console via the caller.\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function compile(\n argv: readonly string[],\n logger: Logger,\n opts?: CompileOptions,\n): Promise<number> {\n const { values, positionals } = parseArgs({\n args: [...argv],\n options: {\n registry: { type: \"string\", short: \"r\" },\n out: { type: \"string\", short: \"o\" },\n granularity: { type: \"string\", short: \"g\" },\n },\n allowPositionals: true,\n strict: true,\n });\n\n const entryArg = positionals[0];\n if (entryArg === undefined || entryArg === \"\") {\n logger.error(\n \"error: compile requires an <entry> argument (BlockMerkleRoot, spec file, or directory)\",\n );\n return 1;\n }\n\n const granularityRaw = values.granularity;\n let granularity: Granularity | undefined;\n if (granularityRaw !== undefined) {\n const parsed = parseGranularity(granularityRaw);\n if (parsed === null) {\n logger.error(\n `error: --granularity must be an integer between 1 and 5 (got ${JSON.stringify(granularityRaw)})`,\n );\n return 1;\n }\n granularity = parsed;\n }\n\n const registryPath = values.registry ?? DEFAULT_REGISTRY_PATH;\n\n // Resolve the spec file path and output directory.\n // If entryArg is a directory, look for spec.yak inside it and default\n // --out to <dir>/dist. Otherwise treat it as a spec file (or BlockMerkleRoot).\n let specFilePath: string | null = null;\n let outDir: string;\n\n if (!isHex64(entryArg)) {\n // Check whether the arg is a directory.\n let isDir = false;\n try {\n isDir = statSync(entryArg).isDirectory();\n } catch {\n // stat failed — not a directory (or doesn't exist); fall through to file path handling.\n }\n\n if (isDir) {\n specFilePath = join(entryArg, \"spec.yak\");\n outDir = values.out ?? join(entryArg, \"dist\");\n } else {\n specFilePath = entryArg;\n outDir = values.out ?? \"./yakcc-out\";\n }\n } else {\n outDir = values.out ?? \"./yakcc-out\";\n }\n\n // Open the registry.\n let registry: Registry;\n try {\n registry = await openRegistry(registryPath, { embeddings: opts?.embeddings });\n } catch (err) {\n logger.error(`error: failed to open registry at ${registryPath}: ${String(err)}`);\n return 1;\n }\n\n try {\n // Seed the registry and collect all merkle roots for the pre-scan stem index.\n // seedRegistry is idempotent (INSERT OR IGNORE), so running it on an already-\n // seeded registry is safe and produces consistent knownMerkleRoots.\n const seedResult = await seedRegistry(registry);\n\n // Resolve the entry BlockMerkleRoot.\n let entryRoot: BlockMerkleRoot;\n if (isHex64(entryArg)) {\n // Treat as a BlockMerkleRoot directly.\n entryRoot = entryArg as BlockMerkleRoot;\n } else {\n // Treat as a path to a JSON SpecYak file (or resolved from directory above).\n const resolvedPath = specFilePath ?? entryArg;\n let specJson: string;\n try {\n specJson = readFileSync(resolvedPath, \"utf-8\");\n } catch (err) {\n logger.error(`error: cannot read spec file ${resolvedPath}: ${String(err)}`);\n return 1;\n }\n let spec: SpecYak;\n try {\n spec = JSON.parse(specJson) as SpecYak;\n } catch (err) {\n logger.error(`error: invalid JSON in spec file ${resolvedPath}: ${String(err)}`);\n return 1;\n }\n const hash = specHash(spec);\n const roots = await registry.selectBlocks(hash);\n if (roots.length === 0) {\n logger.error(`error: no block found for spec in ${resolvedPath} (specHash=${hash})`);\n return 1;\n }\n entryRoot = roots[0] as BlockMerkleRoot;\n }\n\n // Assemble the artifact.\n let artifact: Artifact;\n try {\n artifact = await assemble(entryRoot, registry, undefined, {\n knownMerkleRoots: seedResult.merkleRoots,\n ...(granularity !== undefined && { granularity }),\n });\n } catch (err) {\n logger.error(`error: assembly failed: ${String(err)}`);\n return 1;\n }\n\n // Write output files.\n mkdirSync(outDir, { recursive: true });\n const modulePath = join(outDir, \"module.ts\");\n const manifestPath = join(outDir, \"manifest.json\");\n writeFileSync(modulePath, artifact.source, \"utf-8\");\n writeFileSync(manifestPath, JSON.stringify(artifact.manifest, null, 2), \"utf-8\");\n\n const blockCount = artifact.manifest.entries.length;\n logger.log(`compiled ${blockCount} blocks → ${modulePath}; manifest at ${manifestPath}`);\n return 0;\n } finally {\n await registry.close();\n }\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-COMPILE-TS-BACKEND-001: The TS backend assembles a single-file module\n// by concatenating block sources in topological order (leaves first, entry last),\n// deduplicating type imports from @yakcc/contracts, stripping intra-corpus import\n// statements, type aliases, CONTRACT declarations, and re-exporting the entry block's\n// primary function as the module's public surface.\n// Status: updated (WI-T04) — block separator comment now uses BlockMerkleRoot instead\n// of ContractId. The emit logic itself is unchanged; only the identity type used in\n// comments and the block-map key type changed (BlockMerkleRoot ↔ ContractId).\n// Original decision recorded at WI-005.\n// Rationale: Blocks are self-contained — each inlines its sub-block logic rather than\n// calling sibling functions at runtime. Composition is documented via \"import type\"\n// declarations only. The backend therefore:\n// (1) strips \"import type { X } from './X.js'\" lines (sub-block composition refs)\n// (2) strips \"type _X = typeof X\" shadow-type aliases that reference stripped imports\n// (3) deduplicates \"import type { ContractSpec } from '@yakcc/contracts'\" to one header\n// (4) strips \"export const CONTRACT = {...};\" multi-line declarations (type metadata only;\n// every block exports it so it would cause duplicate-export errors in the assembled\n// module; it is not needed at runtime — only the function export is)\n// (5) concatenates the cleaned sources in topological order\n// (6) appends a re-export of the entry function\n// No code is generated — only composition of registry-stored block sources.\n//\n// @decision DEC-COMPILE-TS-BACKEND-AST-001: Import stripping uses line-level string\n// processing rather than ts-morph AST manipulation.\n// Status: decided (WI-005)\n// Rationale: The seeds blocks follow a predictable structure. Line-level processing is\n// simpler, faster, and has no risk of reformatting block source (which must be preserved\n// verbatim per the no-code-generation constraint). ts-morph would be accurate for\n// arbitrary input but the strict-subset validator already guarantees the block sources\n// are well-formed; line-level processing is sufficient and measurably cheaper.\n\nimport type { BlockMerkleRoot } from \"@yakcc/contracts\";\nimport type { ResolutionResult } from \"./resolve.js\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * A compilation backend: turns a ResolutionResult into a string of module source.\n *\n * No code is generated — only composition of registry-stored block sources.\n */\nexport interface Backend {\n readonly name: string;\n emit(resolution: ResolutionResult): Promise<string>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal: line-level source transformation\n// ---------------------------------------------------------------------------\n\n/**\n * Patterns that identify intra-corpus type import lines in a block source.\n *\n * These are the \"import type { X } from './X.js'\" declarations that document\n * composition but are not runtime dependencies. In the assembled module all\n * referenced blocks appear in the same file, so these imports are unnecessary\n * and would cause TypeScript to complain about missing modules.\n *\n * We match any \"import type\" line whose module specifier starts with \"./\"\n * or \"@yakcc/seeds/\" or \"@yakcc/blocks/\" — the same prefixes used by parseBlock.\n */\nconst INTRA_CORPUS_IMPORT_RE =\n /^import type\\s+\\{[^}]*\\}\\s+from\\s+[\"'](\\.|@yakcc\\/seeds\\/|@yakcc\\/blocks\\/)[^\"']*[\"'];?\\s*$/;\n\n/**\n * Pattern that identifies shadow type alias lines (\"type _X = typeof X\").\n *\n * These suppress TypeScript's \"imported but not used as a value\" error for the\n * intra-corpus import type declarations. Once those imports are stripped, the\n * corresponding type aliases must also be stripped to avoid \"cannot find name\" errors.\n */\nconst SHADOW_TYPE_ALIAS_RE = /^type\\s+_\\w+\\s*=\\s*typeof\\s+\\w+\\s*;?\\s*$/;\n\n/**\n * Pattern that identifies \"import type { ContractSpec } from '@yakcc/contracts'\"\n * and similar type-only imports from @yakcc/contracts. These are deduplicated to\n * a single import at the top of the assembled module.\n */\nconst CONTRACTS_IMPORT_RE = /^import type\\s+\\{[^}]*\\}\\s+from\\s+[\"']@yakcc\\/contracts[\"'];?\\s*$/;\n\n/**\n * Pattern that identifies the opening line of an \"export const CONTRACT = {\" declaration.\n *\n * Every block exports a CONTRACT constant containing its ContractSpec. When multiple\n * blocks are concatenated, this creates duplicate named exports which cause parse errors\n * in any ESM bundler/runtime. The CONTRACT value is type metadata only — it is not\n * needed at runtime in the assembled module (only the function export is). We strip the\n * entire multi-line declaration using brace-depth tracking in cleanBlockSource.\n */\nconst CONTRACT_EXPORT_START_RE = /^export const CONTRACT(?:\\s*:\\s*ContractSpec)?\\s*=\\s*\\{/;\n\n/**\n * Clean a single block's source for inclusion in the assembled module.\n *\n * - Strip intra-corpus import type lines (sub-block composition declarations)\n * - Strip shadow type alias lines (type _X = typeof X)\n * - Strip @yakcc/contracts import lines (deduplicated separately)\n * - Strip the entire \"export const CONTRACT = {...};\" multi-line declaration\n * (present in every block; causes duplicate export errors in the assembled module)\n *\n * Returns the cleaned source with leading blank lines removed.\n */\nfunction cleanBlockSource(source: string): string {\n const lines = source.split(\"\\n\");\n const cleaned: string[] = [];\n\n // Brace-depth counter used to skip the multi-line CONTRACT declaration.\n // When > 0, we are inside the declaration and suppress all lines until depth returns to 0.\n let contractDepth = 0;\n\n for (const line of lines) {\n // If we are inside a CONTRACT declaration, track brace depth and skip lines.\n if (contractDepth > 0) {\n for (const ch of line) {\n if (ch === \"{\") contractDepth++;\n else if (ch === \"}\") contractDepth--;\n }\n // contractDepth reached 0 means the closing brace of the declaration was on this line.\n // The line itself is part of the declaration — do not emit it.\n continue;\n }\n\n // Detect start of \"export const CONTRACT = {\" (may be multi-line).\n if (CONTRACT_EXPORT_START_RE.test(line)) {\n // Count braces on the opening line to determine if it closes on the same line.\n for (const ch of line) {\n if (ch === \"{\") contractDepth++;\n else if (ch === \"}\") contractDepth--;\n }\n // Skip this line regardless; if contractDepth is already 0, the declaration\n // was single-line (e.g. \"export const CONTRACT = {};\") — still drop it.\n continue;\n }\n\n if (INTRA_CORPUS_IMPORT_RE.test(line)) continue;\n if (SHADOW_TYPE_ALIAS_RE.test(line)) continue;\n if (CONTRACTS_IMPORT_RE.test(line)) continue;\n cleaned.push(line);\n }\n\n // Remove leading blank lines from the cleaned block.\n let start = 0;\n while (start < cleaned.length && cleaned[start]?.trim() === \"\") {\n start++;\n }\n\n return cleaned.slice(start).join(\"\\n\");\n}\n\n/**\n * Extract all ContractSpec type symbols from @yakcc/contracts import lines.\n *\n * Multiple blocks may import different symbols (e.g. ContractSpec, ContractId).\n * We collect the union of all imported symbols and emit one deduped import.\n */\nfunction extractContractsImports(source: string): string[] {\n const symbols: string[] = [];\n for (const line of source.split(\"\\n\")) {\n const match = line.match(/^import type\\s+\\{([^}]*)\\}\\s+from\\s+[\"']@yakcc\\/contracts[\"'];?\\s*$/);\n if (match?.[1]) {\n for (const sym of match[1].split(\",\")) {\n const trimmed = sym.trim();\n if (trimmed.length > 0) symbols.push(trimmed);\n }\n }\n }\n return symbols;\n}\n\n/**\n * Extract the primary exported function name from a block source.\n *\n * Looks for the first \"export function <name>\" or \"export async function <name>\" line.\n * Returns null if none is found.\n */\nfunction extractEntryFunctionName(source: string): string | null {\n for (const line of source.split(\"\\n\")) {\n const match = line.match(/^export\\s+(?:async\\s+)?function\\s+(\\w+)\\s*[(<]/);\n if (match?.[1]) return match[1];\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Internal: full module assembly\n// ---------------------------------------------------------------------------\n\nfunction assembleModule(resolution: ResolutionResult): string {\n // Pass 1: collect all @yakcc/contracts type imports across all blocks.\n const allContractsSymbols = new Set<string>();\n for (const merkleRoot of resolution.order) {\n const block = resolution.blocks.get(merkleRoot);\n if (block === undefined) continue;\n for (const sym of extractContractsImports(block.source)) {\n allContractsSymbols.add(sym);\n }\n }\n\n const parts: string[] = [];\n\n // Header comment.\n parts.push(\n [\n \"// Assembled by @yakcc/compile — do not edit by hand.\",\n \"// Every block is reproduced verbatim from the registry; no code was generated.\",\n ].join(\"\\n\"),\n );\n\n // Deduped @yakcc/contracts import (omit if nothing was collected).\n if (allContractsSymbols.size > 0) {\n const symbols = [...allContractsSymbols].sort().join(\", \");\n parts.push(`import type { ${symbols} } from \"@yakcc/contracts\";`);\n }\n\n // Pass 2: emit each block's cleaned source in topological order (leaves first).\n for (const merkleRoot of resolution.order) {\n const block = resolution.blocks.get(merkleRoot);\n if (block === undefined) continue;\n\n const cleaned = cleanBlockSource(block.source);\n if (cleaned.trim().length === 0) continue;\n\n parts.push(`\\n// --- block: ${merkleRoot} ---\\n${cleaned}`);\n }\n\n // Re-export the entry function as the module's named public surface.\n const entryBlock = resolution.blocks.get(resolution.entry);\n if (entryBlock !== undefined) {\n const fnName = extractEntryFunctionName(entryBlock.source);\n if (fnName !== null) {\n parts.push(`\\n// Re-export entry function as module public surface.\\nexport { ${fnName} };`);\n }\n }\n\n return `${parts.join(\"\\n\")}\\n`;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Create the built-in TypeScript backend.\n *\n * The backend concatenates block sources in topological order, deduplicates\n * @yakcc/contracts type imports, strips intra-corpus import declarations, and\n * re-exports the entry block's primary function.\n *\n * No code is generated — only composition of registry-stored block sources.\n */\nexport function tsBackend(): Backend {\n return {\n name: \"ts\",\n async emit(resolution: ResolutionResult): Promise<string> {\n return assembleModule(resolution);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Internal helper exported for testing\n// ---------------------------------------------------------------------------\n\n/** @internal — exposed for ts-backend unit tests only. */\nexport { cleanBlockSource, extractEntryFunctionName, assembleModule };\n\n// Unused import suppression for BlockMerkleRoot (used in ResolutionResult type).\ntype _BlockMerkleRoot = BlockMerkleRoot;\n","// SPDX-License-Identifier: MIT\n//\n// @decision DEC-V1-LOWER-BACKEND-REUSE-001\n// Title: AssemblyScript backend re-implements numeric-domain analysis inline; the\n// in-house WASM byte emitter (wasm-backend.ts / wasm-lowering/) is retired.\n// Status: retired (WI-AS-CLEANUP-WAVE3-LOWERER, #148) — originally decided WI-AS-PHASE-1-MVP (#145)\n// Rationale:\n// The wave-3 wasm-backend (wasm-backend.ts, now deleted) contained two distinct halves:\n//\n// ANALYSIS HALF (re-implemented here):\n// inferNumericDomain() in the now-deleted wasm-lowering/visitor.ts performed ts-morph\n// AST heuristics to classify `number`-typed TS functions as i32, i64, or f64.\n// The AS backend re-implements the same heuristic as a lightweight text-scan\n// (rules -1, 0, 1-7) to avoid importing the ts-morph-heavy visitor. This\n// re-implementation is validated by the numeric-parity.test.ts suite.\n// See Phase 0 spike findings (SPIKE_FINDINGS.md §6, Issue #144).\n// This file is now the canonical authority for numeric domain inference.\n//\n// EMISSION HALF (deleted in Phase 3):\n// The hand-rolled WASM binary emitter (WasmFunction IR, opcodes tables,\n// uleb128 encoding) was superseded by the AS backend per operator adjudication\n// (Issue #142 / Path A). It served as a differential oracle through Phase 2\n// and has been deleted in Phase 3 (#148). assemblyScriptBackend() is the sole\n// production WASM path.\n//\n// Per-atom module boundary (SPIKE_FINDINGS.md §4 / q3-boundary-choice.md):\n// Each yakcc atom maps to one AS file → one .wasm output. This preserves\n// content-addressing granularity: WASM artifact hash traces to a single\n// implSource hash. Per-package batching remains a Phase 1 escape hatch if\n// per-atom instantiation overhead proves excessive in the hot path.\n//\n// Supporting evidence:\n// - Issue #142: operator adjudication selecting Path A (AS-backed WASM)\n// - Issue #144: Phase 0 spike — asc 0.28.17 determinism confirmed, per-atom\n// boundary validated end-to-end with wasmtime 31.0.0, Q1/Q2/Q3 all PASS\n//\n// @decision DEC-AS-BACKEND-TMPDIR-001\n// Title: AS source and WASM output written to OS temp directory, cleaned up on exit\n// Status: SUPERSEDED by DEC-AS-BACKEND-IN-PROCESS-001 (WI-630-S1)\n// Rationale:\n// Originally: asc required a real filesystem path for input and output.\n// Superseded: the in-process asc.main() API eliminates the tmpdir lifecycle\n// entirely. No mkdirSync/writeFileSync/readFileSync/rmSync per atom.\n// This annotation is retained for history; the tmpdir convention no longer\n// applies to the production emit path.\n//\n// @decision DEC-AS-BACKEND-IN-PROCESS-001\n// Title: AS backend uses the in-process assemblyscript/asc.main() programmatic API;\n// the per-atom Node child-process invocation via execFileSync is retired.\n// Status: decided (WI-630-S1, plans/wi-630-as-in-process-compile.md)\n// Rationale:\n// The 4,119-atom closer-parity-as cold-run is dominated by per-atom Node startup\n// (~50-80 ms) + asc init (~150-300 ms). Replacing execFileSync with the in-process\n// asc.main() API eliminates the Node child-process tax while preserving:\n// - The cache key (DEC-AS-COMPILE-CACHE-001): byte-identical WASM output verified\n// by differential test on ≥10 representative atoms.\n// - The WasmBackend interface: emit() was already async; no caller churn.\n// - The concurrency runner (DEC-AS-CLOSER-PARITY-CONCURRENCY-001): untouched.\n// - Multi-export (DEC-AS-MULTI-EXPORT-001) and record substrate\n// (DEC-AS-RECORD-LAYOUT-001): both covered by the differential test matrix.\n// I/O strategy: atom source served in-memory via readFile callback; AS std-lib\n// reads delegated to fs/promises.readFile fallback (OS page cache amortizes\n// std-lib reads across the 4,119-atom corpus). See plan §4 Decision 2.\n// Supersedes DEC-AS-BACKEND-TMPDIR-001 for the production emit path.\n//\n// @decision DEC-AS-JSON-STRATEGY-001\n// Title: AS-backend JSON support uses flat-memory manual integer parsers/writers\n// (parseI32/writeI32/skipWS) rather than assemblyscript-json or native\n// AS JSON stdlib, because both managed JSON libraries require --runtime\n// minimal/full (GC heap, managed string type, JSON parsing internals) which\n// are incompatible with the --runtime stub constraint used by this backend.\n// Status: decided (WI-AS-PHASE-2D-JSON, Issue #209, 2026-05-03)\n// Rationale:\n// AS JSON library options evaluated:\n//\n// (A) `assemblyscript-json` package (npm: assemblyscript-json):\n// Requires AS managed types (string, GC allocation, object fields).\n// Under --runtime stub: JSON.parse<T>() fails to compile because the stub\n// runtime omits the GC heap and string internals. PROBE RESULT: COMPILE FAIL.\n//\n// (B) Native AS JSON stdlib (JSON.parse / JSON.stringify built into asc):\n// Also requires managed string type and GC allocation. Same failure mode\n// as (A) under --runtime stub. PROBE RESULT: COMPILE FAIL.\n// Evidence: J4/J5 probes in json-parity.test.ts confirm the compile failure.\n//\n// (C) Flat-memory manual parsers (CHOSEN):\n// parseI32(ptr, len): byte-by-byte ASCII decimal integer parser (atoi).\n// writeI32(value, dstPtr): byte-by-byte ASCII decimal integer writer (itoa).\n// skipWS(ptr, len, start): JSON whitespace-skip for token boundary detection.\n// These use only WASM intrinsics (load<u8>, store<u8>, i32 arithmetic) with\n// no GC dependency. Compatible with --runtime stub. PROBE RESULT: COMPILE OK.\n//\n// Type-parameter inference at codegen time (for JSON.stringify<T>):\n// Not applicable -- the flat-memory approach works on byte buffers, not typed\n// AS values. The integer type (i32) is the only supported token type in v1\n// (ASCII-ONLY CONSTRAINT per DEC-AS-JSON-LAYOUT-001). Type inference for\n// broader JSON support (floats, strings, objects) is deferred to a future\n// phase that adopts --runtime minimal/full.\n//\n// JSON.parse destination type resolution:\n// Not applicable in v1. The flat-memory parseI32 always returns i32.\n// Destination type selection for polymorphic JSON.parse is deferred to the\n// same future phase that enables AS managed JSON.\n//\n// Decision: Use flat-memory approach (C) for v1. The assemblyscript-json package\n// is NOT added as a dependency at this time -- it would only work with a GC\n// runtime tier that is not yet adopted. A follow-up issue should track the GC\n// runtime upgrade path and reassess JSON library adoption at that point.\n//\n// See also: DEC-AS-JSON-LAYOUT-001 in json-parity.test.ts for the byte-layout\n// specifics (JSON_BASE_PTR, DST_BASE_PTR, ASCII-ONLY CONSTRAINT).\n//\n// @decision DEC-AS-JSON-STRATEGY-001\n// Title: AS-backend JSON support uses flat-memory manual integer parsers/writers\n// (parseI32/writeI32/skipWS) rather than assemblyscript-json or native\n// AS JSON stdlib, because both managed JSON libraries require --runtime\n// minimal/full (GC heap, managed string type, JSON parsing internals) which\n// are incompatible with the --runtime stub constraint used by this backend.\n// Status: decided (WI-AS-PHASE-2D-JSON, Issue #209, 2026-05-03)\n// Rationale:\n// AS JSON library options evaluated:\n//\n// (A) `assemblyscript-json` package (npm: assemblyscript-json):\n// Requires AS managed types (string, GC allocation, object fields).\n// Under --runtime stub: JSON.parse<T>() fails to compile because the stub\n// runtime omits the GC heap and string internals. PROBE RESULT: COMPILE FAIL.\n//\n// (B) Native AS JSON stdlib (JSON.parse / JSON.stringify built into asc):\n// Also requires managed string type and GC allocation. Same failure mode\n// as (A) under --runtime stub. PROBE RESULT: COMPILE FAIL.\n// Evidence: J4/J5 probes in json-parity.test.ts confirm the compile failure.\n//\n// (C) Flat-memory manual parsers (CHOSEN):\n// parseI32(ptr, len): byte-by-byte ASCII decimal integer parser (atoi).\n// writeI32(value, dstPtr): byte-by-byte ASCII decimal integer writer (itoa).\n// skipWS(ptr, len, start): JSON whitespace-skip for token boundary detection.\n// These use only WASM intrinsics (load<u8>, store<u8>, i32 arithmetic) with\n// no GC dependency. Compatible with --runtime stub. PROBE RESULT: COMPILE OK.\n//\n// Type-parameter inference at codegen time (for JSON.stringify<T>):\n// Not applicable -- the flat-memory approach works on byte buffers, not typed\n// AS values. The integer type (i32) is the only supported token type in v1\n// (ASCII-ONLY CONSTRAINT per DEC-AS-JSON-LAYOUT-001). Type inference for\n// broader JSON support (floats, strings, objects) is deferred to a future\n// phase that adopts --runtime minimal/full.\n//\n// JSON.parse destination type resolution:\n// Not applicable in v1. The flat-memory parseI32 always returns i32.\n// Destination type selection for polymorphic JSON.parse is deferred to the\n// same future phase that enables AS managed JSON.\n//\n// Decision: Use flat-memory approach (C) for v1. The assemblyscript-json package\n// is NOT added as a dependency at this time -- it would only work with a GC\n// runtime tier that is not yet adopted. A follow-up issue should track the GC\n// runtime upgrade path and reassess JSON library adoption at that point.\n//\n// See also: DEC-AS-JSON-LAYOUT-001 in json-parity.test.ts for the byte-layout\n// specifics (JSON_BASE_PTR, DST_BASE_PTR, ASCII-ONLY CONSTRAINT).\n//\n// @decision DEC-AS-EXCEPTIONS-STRATEGY-001\n// Title: AS-backend exception/error handling uses primitive abort(), flat-memory error\n// codes (store<u8>(errPtr, code)), and sentinel return values rather than\n// try/catch exception dispatch, because try/catch requires exception-table\n// support absent from --runtime stub (asc 0.28.x). Bare throw new Error()\n// (no enclosing try/catch) compiles under stub — an unexpected finding.\n// Status: decided (WI-AS-PHASE-2C-EXCEPTIONS, Issue #207, 2026-05-10)\n// Rationale:\n// AS exception handling options evaluated:\n//\n// (A) Managed try/catch exception dispatch:\n// Requires exception-table support in the WASM binary — catch routing,\n// finalizer calls, and stack unwinding to the matching catch block.\n// Under --runtime stub, asc refuses to compile `try { throw } catch {}`.\n// PROBE RESULT (E5): COMPILE FAIL. asc error: exception-table absent.\n//\n// (B) Managed throw new Error(\"msg\") with catch:\n// Same failure mode as (A) when enclosed in a try/catch. The Error object\n// constructor itself does not require GC under stub; it is the catch-dispatch\n// mechanism that fails. PROBE RESULT: COMPILE FAIL (when try/catch present).\n//\n// (C) Flat-memory error protocol (CHOSEN):\n// Three complementary patterns, all WASM-intrinsic compatible:\n// abort(): AS primitive; traps the WASM instance on error.\n// No GC or exception-table needed.\n// errPtr (i32): Caller passes a pointer into WASM linear memory.\n// store<u8>(errPtr, code) writes error code byte.\n// load<u8>(errPtr) lets host read error state.\n// ERR_BASE_PTR=512: above AS stub header,\n// below STR_BASE_PTR=1024 (strings-parity ABI).\n// Sentinel values: Return -1 (or other out-of-band integer) on error.\n// Mirrors S4/indexOfByte pattern from strings-parity.\n// All three patterns use only load<u8>/store<u8>/i32 arithmetic — no GC.\n// Compatible with --runtime stub. PROBE RESULT: COMPILE OK.\n//\n// FINDING (E4 — UNEXPECTED): bare `throw new Error(\"msg\")` with NO enclosing\n// try/catch DOES compile under asc 0.28.x --runtime stub and passes\n// WebAssembly.validate(). The Error constructor is more stub-permissive than\n// initially assumed; it is the catch-dispatch that requires the exception-table.\n// The non-negative pass-through path is verified at runtime (E4 probe test).\n//\n// Decision: Use flat-memory approach (C) for v1. try/catch exception dispatch\n// is deferred until a future phase adopts --runtime minimal/full (GC tier).\n// A follow-up issue should track the GC runtime upgrade path and reassess\n// native AS exception handling at that point.\n//\n// See also: DEC-AS-EXCEPTION-LAYOUT-001 in exceptions-parity.test.ts for the\n// full substrate inventory (E1-E5), ERR_BASE_PTR layout, and probe methodology.\n//\n// @decision DEC-AS-STRINGS-STRATEGY-001\n// Title: AS-backend string support uses flat-memory UTF-8 byte protocol\n// (ptr: i32, len: i32) over WASM intrinsics (load<u8>, store<u8>,\n// byte-level scanning) rather than AS managed string type, because\n// AS managed strings require --runtime minimal/full (GC heap, UTF-16\n// string header, charCodeAt/indexOf/slice GC internals) which are\n// incompatible with the --runtime stub constraint used by this backend.\n// Status: decided (WI-AS-PHASE-2B-STRINGS, Issue #206, 2026-05-03)\n// Rationale:\n// AS string support options evaluated:\n//\n// (A) AS managed string type (string literals, s.length, s.charCodeAt,\n// s.indexOf, s.slice):\n// Requires the GC runtime for all managed string operations:\n// - string.length reads the GC-managed string header (UTF-16 char count).\n// - string.charCodeAt(i) is a bounds-checked GC read of a UTF-16 code unit.\n// - string.indexOf(sub) performs a GC string search with managed allocation.\n// - string.slice(start, end) allocates a new managed string via GC copy.\n// Under --runtime stub, any managed-type operation that touches the GC\n// either traps at runtime or fails to compile.\n// PROBE RESULT (S-managed): COMPILE FAIL. asc 0.28.x --runtime stub does\n// not support AS managed string type.\n//\n// (B) assemblyscript-string-utils or similar npm packages:\n// All evaluated packages wrap AS managed string internals and require the\n// same GC heap and string runtime library as option (A).\n// PROBE RESULT: COMPILE FAIL (same failure mode as managed strings).\n//\n// (C) Flat-memory UTF-8 byte protocol (CHOSEN):\n// strLen(ptr, len): return len parameter (flat-memory length pass-through).\n// byteAt(ptr, len, i): load<u8>(ptr + i) — read byte at index i.\n// strEq(pA, lA, pB, lB): byte-by-byte equality comparison (memcmp variant).\n// indexOfByte(ptr, len, b): scan for first occurrence of byte b; return index or -1.\n// copySlice(src, len, dst, start, end): copy bytes [start, end) via store<u8>;\n// return byte count copied.\n// These operations use only WASM intrinsics (load<u8>, store<u8>, i32\n// arithmetic) with no GC dependency. Compatible with --runtime stub.\n// PROBE RESULT: COMPILE OK.\n//\n// ASCII-ONLY CONSTRAINT (v1): Substrates use ASCII-only inputs (single-byte\n// UTF-8, code points 0x20-0x7E). Multi-byte UTF-8 sequences (2-4 bytes),\n// the byte-count vs. char-count distinction, and surrogate-pair handling are\n// deferred to a future phase when the GC runtime tier is adopted.\n//\n// Memory layout: STR_BASE_PTR = 1024 (above AS stub runtime header region,\n// above ERR_BASE_PTR = 512); DST_BASE_PTR = 4096 (separate output buffer for\n// slice/copy operations). Layout is wire-compatible with the\n// arrays-parity.test.ts flat-memory conventions.\n//\n// Decision: Use flat-memory approach (C) for v1. AS managed strings are NOT\n// used at this time -- they only work with a GC runtime tier not yet adopted.\n// A follow-up issue should track the GC runtime upgrade path and reassess\n// managed-string support (char-count semantics, full Unicode, surrogate pairs)\n// at that point.\n//\n// See also: DEC-AS-STRING-LAYOUT-001 in strings-parity.test.ts for the\n// full substrate inventory (S1-S5), STR_BASE_PTR/DST_BASE_PTR layout, and\n// ASCII-only constraint rationale.\n//\n// @decision DEC-AS-ARRAYS-STRATEGY-001\n// Title: AS-backend array support uses flat-memory (ptr: i32, len: i32) protocol\n// over WASM intrinsics (load<i32>, store<i32>, byte-stride arithmetic) rather\n// than managed AS Array<i32> or StaticArray<i32>, because managed arrays and\n// StaticArray both require --runtime minimal/full (GC heap) or trap at runtime\n// under the --runtime stub constraint used by this backend.\n// Status: decided (WI-AS-PHASE-2E-ARRAYS, Issue #210, 2026-05-10)\n// Rationale:\n// AS array support options evaluated:\n//\n// (A) Managed AS Array<i32> (i32[], Array<i32> with .length, .push, .map, .filter,\n// .reduce):\n// Requires the GC runtime for all managed array operations:\n// - Array<i32>.length reads the GC-managed array header (element count).\n// - Array<i32>[i] subscript is a bounds-checked GC heap read.\n// - Array<i32>.push() triggers GC heap allocation / capacity doubling.\n// - Array<i32>.map(fn) / .filter(fn) require closure allocation — also\n// a GC feature (function table + closure context).\n// - Array<i32>.reduce(fn, init) requires closure support for the accumulator.\n// Under --runtime stub: .map() and .filter() fail to compile (closure\n// allocation absent). .push() compiles syntactically but the resulting WASM\n// traps at runtime because the stub does not implement the ArrayBuffer resize\n// path (GC realloc absent). PROBE RESULT (A4 push): RUNTIME TRAP.\n// PROBE RESULT (A5 map/closure): COMPILE FAIL.\n//\n// (B) AS StaticArray<i32> (fixed-size, bounds-checked, no resize):\n// Does not require GC allocation for reads (bounds-checked load<i32>).\n// However, .length and subscript access for StaticArray are GC-managed\n// metadata reads under asc 0.28.x even for stub runtime. The allocation\n// itself (`new StaticArray<i32>(n)`) requires memory.grow plumbing absent\n// from stub. PROBE RESULT: StaticArray allocation fails to compile or\n// traps at runtime under --runtime stub. Rejected for the same root cause\n// as managed arrays: depends on GC allocation infrastructure.\n//\n// (C) Flat-memory ptr+len protocol (CHOSEN):\n// Arrays are represented as:\n// - ptr: i32 — pointer to first i32 element in WASM linear memory.\n// - len: i32 — number of elements (not byte length).\n// - Element at index i: byte offset = ptr + i * 4 (i32 = 4 bytes).\n// Operations implemented using only WASM intrinsics (load<i32>, store<i32>,\n// i32 arithmetic):\n// A1 len: return len parameter (flat-memory length pass-through).\n// A2 get: return load<i32>(ptr + i * 4) — index access.\n// A3 sum/reduce: manual for-loop; accumulate load<i32> values.\n// A4 pushLen: write v at index len via store<i32>(ptr + len * 4, v);\n// return len + 1. (Caller manages buffer capacity.)\n// A5 doubleAll: manual for-loop; store<i32>(dstPtr + i * 4, 2 * load<i32>...)\n// Writes doubled values to a separate output buffer (dstPtr).\n// All operations use no GC, no closures, no managed types.\n// Compatible with --runtime stub. PROBE RESULT: COMPILE OK, RUNTIME OK.\n//\n// ASCII-ONLY / i32-ONLY CONSTRAINT (v1): Array elements are i32 only. Arrays-of-\n// strings (ptr+len pairs per element), arrays-of-records (struct ABI per element),\n// and GC-managed dynamic arrays are deferred to a future phase that adopts\n// --runtime minimal/full.\n//\n// STRUCT_BASE_PTR = 64: same convention as records-parity.test.ts; avoids the AS\n// stub runtime header region at low addresses. Wire-compatible with the\n// arrays-parity.test.ts flat-memory ABI.\n//\n// Decision: Use flat-memory approach (C) for v1. Managed Array<i32> and\n// StaticArray<i32> are NOT used -- they require a GC runtime tier not yet adopted.\n// A follow-up issue should track the GC runtime upgrade path and reassess\n// native AS array types (Array<T>.push, .map, .filter, .reduce with closures)\n// at that point. arrays-of-strings and arrays-of-records are deferred to #230\n// (WI-AS-PHASE-2F).\n//\n// See also: DEC-AS-ARRAY-LAYOUT-001 in arrays-parity.test.ts for the\n// full substrate inventory (A1-A5), STRUCT_BASE_PTR layout, i32-stride protocol,\n// and the deferred Phase 2F items (arrays-of-strings, closure-based map/filter).\n//\n// @decision DEC-AS-CONTROL-FLOW-STRATEGY-001\n// Title: AS-backend control-flow constructs (if/else, while, for, do-while, switch)\n// are supported by asc 0.28.x natively under --runtime stub without any\n// workarounds in as-backend.ts, because they lower to standard WASM scalar\n// instructions that have no GC or managed-type dependency.\n// Status: decided (WI-AS-PHASE-2G-CONTROL-FLOW, Issue #212, 2026-05-10)\n// Rationale:\n// AS control-flow support options evaluated:\n//\n// (A) Managed iterator protocol (for-of over AS managed string / Array<T>):\n// for-of over AS managed types (string, Array<T>, custom iterables) requires\n// GC-managed iterator objects and Symbol.iterator dispatch. Under --runtime\n// stub the GC heap and Symbol internals are absent. PROBE RESULT: for-of over\n// managed types COMPILE FAIL (or RUNTIME TRAP) under --runtime stub.\n// Affected substrates: any for-of loop whose iterable is an AS managed type.\n//\n// (B) for-of over AS managed types via alternative iteration (index-based):\n// Replace for-of with a manual index-for loop (for(let i=0;i<len;i++)).\n// Avoids the iterator protocol entirely; compatible with flat-memory arrays.\n// PROBE RESULT: COMPILE OK under --runtime stub. However, this is a\n// workaround for managed-type arrays, not a feature of the control-flow\n// substrate itself. Considered as a future escalation path only.\n//\n// (C) asc-native control-flow constructs (CHOSEN -- no workaround required):\n// if/else, else-if chains, while, for (index-based), do-while, and switch\n// (with explicit cases and default) all lower to standard WASM control\n// instructions:\n// if/else => WASM if/else block (no GC needed)\n// while => WASM loop + br_if (no GC needed)\n// for (index) => WASM loop + br_if + i32 counter (no GC needed)\n// do-while => WASM loop + br_if at block end (min 1 iteration)\n// switch/default => WASM block + br_table or nested br_if (no GC needed)\n// break/continue => WASM br to enclosing block label (no GC needed)\n// These constructs use only i32 arithmetic and WASM branch instructions.\n// No GC allocation, no managed types, no exception-table needed.\n// Compatible with --runtime stub. PROBE RESULT (CF1-CF5): COMPILE OK.\n//\n// FINDING (CF1-CF5 -- CONFIRMED EXPECTED): All five control-flow substrates\n// compile cleanly under asc 0.28.x --runtime stub and pass\n// WebAssembly.validate(). Value parity vs TS reference oracle confirmed by\n// 20 fast-check runs per substrate. No changes to as-backend.ts were required\n// for this WI -- the existing emit() pipeline handles control-flow atoms\n// without modification.\n//\n// Substrates verified (per eval contract T3, DEC-AS-CONTROL-FLOW-001):\n// CF1: classify -- if / else-if / else (3-branch sign classifier)\n// CF2: sumToN -- while loop (triangular sum 0..n-1)\n// CF3: product -- for loop (factorial, index-based, no managed array)\n// CF4: countdown -- do-while (count down, min 1 iteration guaranteed)\n// CF5: dayName -- switch with default (3 explicit cases + fallback)\n//\n// Decision: Use asc-native path (C) for all scalar control-flow constructs in v1.\n// No workaround layer in as-backend.ts is required. for-of over AS managed types\n// (string, Array<T>) remains deferred to a future phase that adopts --runtime\n// minimal/full (GC tier). A follow-up issue should track the GC runtime upgrade\n// path and reassess managed-type iteration (for-of, Symbol.iterator, closures)\n// at that point.\n//\n// See also: DEC-AS-CONTROL-FLOW-001 in control-flow-parity.test.ts for the\n// full substrate inventory (CF1-CF5), exportMemory: false convention, and the\n// 20-run fast-check parity methodology.\n//\n// @decision DEC-AS-CONTROL-FLOW-STRATEGY-001\n// Title: AS-backend control-flow constructs (if/else, while, for, do-while, switch)\n// are supported by asc 0.28.x natively under --runtime stub without any\n// workarounds in as-backend.ts, because they lower to standard WASM scalar\n// instructions that have no GC or managed-type dependency.\n// Status: decided (WI-AS-PHASE-2G-CONTROL-FLOW, Issue #212, 2026-05-10)\n// Rationale:\n// AS control-flow support options evaluated:\n//\n// (A) Managed iterator protocol (for-of over AS managed string / Array<T>):\n// for-of over AS managed types (string, Array<T>, custom iterables) requires\n// GC-managed iterator objects and Symbol.iterator dispatch. Under --runtime\n// stub the GC heap and Symbol internals are absent. PROBE RESULT: for-of over\n// managed types COMPILE FAIL (or RUNTIME TRAP) under --runtime stub.\n// Affected substrates: any for-of loop whose iterable is an AS managed type.\n//\n// (B) for-of over AS managed types via alternative iteration (index-based):\n// Replace for-of with a manual index-for loop (for(let i=0;i<len;i++)).\n// Avoids the iterator protocol entirely; compatible with flat-memory arrays.\n// PROBE RESULT: COMPILE OK under --runtime stub. However, this is a\n// workaround for managed-type arrays, not a feature of the control-flow\n// substrate itself. Considered as a future escalation path only.\n//\n// (C) asc-native control-flow constructs (CHOSEN — no workaround required):\n// if/else, else-if chains, while, for (index-based), do-while, and switch\n// (with explicit cases and default) all lower to standard WASM control\n// instructions:\n// if/else → WASM if/else block (no GC needed)\n// while → WASM loop + br_if (no GC needed)\n// for (index) → WASM loop + br_if + i32 counter (no GC needed)\n// do-while → WASM loop + br_if at block end (min 1 iteration)\n// switch/default → WASM block + br_table or nested br_if (no GC needed)\n// break/continue → WASM br to enclosing block label (no GC needed)\n// These constructs use only i32 arithmetic and WASM branch instructions.\n// No GC allocation, no managed types, no exception-table needed.\n// Compatible with --runtime stub. PROBE RESULT (CF1-CF5): COMPILE OK.\n//\n// FINDING (CF1-CF5 — CONFIRMED EXPECTED): All five control-flow substrates\n// compile cleanly under asc 0.28.x --runtime stub and pass\n// WebAssembly.validate(). Value parity vs TS reference oracle confirmed by\n// 20 fast-check runs per substrate. No changes to as-backend.ts were required\n// for this WI — the existing emit() pipeline handles control-flow atoms\n// without modification.\n//\n// Substrates verified (per eval contract T3, DEC-AS-CONTROL-FLOW-001):\n// CF1: classify — if / else-if / else (3-branch sign classifier)\n// CF2: sumToN — while loop (triangular sum 0..n-1)\n// CF3: product — for loop (factorial, index-based, no managed array)\n// CF4: countdown — do-while (count down, min 1 iteration guaranteed)\n// CF5: dayName — switch with default (3 explicit cases + fallback)\n//\n// Decision: Use asc-native path (C) for all scalar control-flow constructs in v1.\n// No workaround layer in as-backend.ts is required. for-of over AS managed types\n// (string, Array<T>) remains deferred to a future phase that adopts --runtime\n// minimal/full (GC tier). A follow-up issue should track the GC runtime upgrade\n// path and reassess managed-type iteration (for-of, Symbol.iterator, closures)\n// at that point.\n//\n// See also: DEC-AS-CONTROL-FLOW-001 in control-flow-parity.test.ts for the\n// full substrate inventory (CF1-CF5), exportMemory: false convention, and the\n// 20-run fast-check parity methodology.\n//\n// @decision DEC-AS-GC-STRATEGY-001\n// Title: assemblyScriptBackend() GC objects are exercised by per-substrate probes\n// that document COMPILE OK / COMPILE FAIL / RUNTIME TRAP under the existing\n// --runtime stub baseline; the flat-memory @unmanaged substrate from\n// DEC-AS-RECORD-LAYOUT-001 is the production-supported equivalent for\n// managed-class field access in v1; full managed new T() allocation, GC\n// retention, sweep, cycle handling, and finalizers are deferred to a future\n// phase that adopts --runtime minimal/full.\n// Status: decided (WI-AS-PHASE-2H-GC, Issue #232, 2026-05-10)\n// Rationale:\n// GC objects in AssemblyScript = managed classes governed by the asc-emitted\n// GC barriers. AssemblyScript has four runtime tiers:\n//\n// --runtime stub: NO GC heap. @unmanaged classes only (flat memory).\n// new T() without @unmanaged: compile-fail or runtime trap.\n// Used by this backend (see DEC-AS-BACKEND-TMPDIR-001).\n// --runtime minimal: Partial GC (manual __pin/__unpin). new T() allocates.\n// --runtime incremental: Full GC, on-the-fly increments.\n// --runtime full: Full GC, stop-the-world.\n//\n// Probe outcomes (G1-G5) recorded in gc-parity.test.ts — empirical, asc 0.28.x\n// --runtime stub, observed 2026-05-10 (\"Code is Truth\" — planner hypotheses revised):\n//\n// (G1) Managed class new Box() (single i32 field): COMPILE OK — UNEXPECTED.\n// asc 0.28.x --runtime stub compiles `new Box()` for a class with a\n// single i32 field. The stub __new path is stub-linked but compilation\n// succeeds. Runtime behavior of the resulting WASM is unprobed.\n//\n// (G2) Two managed class allocations (new Box(), new Box()): COMPILE OK.\n// Consistent with G1. Multiple managed allocations compile under stub.\n//\n// (G3) Nullable managed reference field (Node | null): COMPILE FAIL.\n// The stub compile boundary: `T | null` reference fields require GC type\n// metadata absent from stub. Scalar i32 fields in managed classes compile\n// OK (G1/G2); nullable managed reference fields COMPILE FAIL.\n//\n// (G4) @final class + __finalize() no-op void method: COMPILE OK.\n// @final = optimization modifier (not GC finalizer decorator). There is\n// no @finalize decorator in asc 0.28.x; the GC collect hook is the\n// __finalize() method on the class body. @final + no-op __finalize()\n// compiles under stub (never invoked by stub collect path).\n//\n// (G5) @unmanaged flat-memory field access: COMPILE OK + value parity.\n// @unmanaged opts the class out of GC. Field access lowers to\n// load<i32>/store<i32> on host-provided linear-memory pointers.\n// This is the production-supported \"GC opt-out\" equivalent for v1.\n// Mirrors DEC-AS-RECORD-LAYOUT-001 flat-memory ABI.\n//\n// Alternatives rejected (abbreviated; full rationale in PLAN.md §2.4):\n//\n// (Alt B) Flip --runtime to minimal/incremental/full for the GC test only:\n// Rejected. Requires either a per-call runtime override field in\n// AsBackendOptions (new emit mode — parallel mechanism, Sacred Practice\n// #12 violation) or a parallel factory (dual-authority). Both paths\n// diverge from every existing sibling's invariant.\n//\n// (Alt C) Skip GC entirely; mark #232 as impossible:\n// Rejected. Operator unblocked explicitly (2026-05-10): \"start NOW\".\n// The probe pattern used by every Phase 2 sibling documents reality.\n//\n// Decision: Probe-and-flat-memory pattern for v1. Managed new T() allocation,\n// GC retention, sweep, cycle handling, and finalizers are deferred until a\n// future phase adopts --runtime minimal/full.\n//\n// Cross-links: #232 (this WI), DEC-AS-MULTI-EXPORT-001 (parent Phase 2A.0).\n//\n// See also: DEC-AS-GC-LAYOUT-001 and DEC-AS-GC-ORACLE-001 in gc-parity.test.ts\n// for flat-memory layout constants (GC_BASE_PTR = 24576) and oracle details.\n//\n// @decision DEC-AS-CLOSURE-STRATEGY-001\n// Title: assemblyScriptBackend() closure-capture support is exercised by per-substrate\n// probes that document COMPILE OK / COMPILE FAIL / RUNTIME TRAP under the\n// existing --runtime stub baseline; the static-function + dispatch-table\n// substrate (top-level functions indexed by integer, no captures) is the\n// production-supported \"closure opt-out\" equivalent for callback-style use\n// cases in v1; full closure capture (arrow functions with primitive or managed\n// captures, Array.map/filter/reduce with arrow predicates, bound class methods)\n// is deferred to a future phase that adopts --runtime minimal/full.\n// Status: decided (WI-AS-PHASE-2F-CLOSURES, Issue #230, 2026-05-10)\n//\n// @decision DEC-AS-CLOSURE-STRATEGY-002\n// Title: Slice 1 source-level lambda-lifting in prepareAsSource() hoists\n// `const/let f = (params): RetType => expr` forms (without an explicit\n// function-type annotation on the binding) to top-level\n// `function __closure_<n>(captures..., params...): RetType` declarations,\n// threading captured variables as additional leading parameters; call sites\n// `f(args)` are rewritten to `__closure_<n>(captures..., args)`.\n// Status: decided (WI-211-AS-CLOSURES-SLICE-1, Issue #211, 2026-05-13)\n// Rationale:\n// AssemblyScript --runtime stub rejects ALL closure forms (C1-C4,\n// DEC-AS-CLOSURE-STRATEGY-001) because closure-context allocation requires GC.\n// However, arrow functions assigned to an untyped `const`/`let` binding can be\n// mechanically rewritten at the source level into top-level named functions, with\n// captured variables threaded as leading parameters. The lifted form uses no closures\n// and no GC, so asc 0.28.x --runtime stub can compile it.\n//\n// Forms lifted (Slice 1):\n// S1: `const f = (x: number): number => x * 2;` — no-capture lambda\n// S2: `const f = (x: number): number => x + n;` — single primitive capture\n// S3: flat-memory variant with (ptr, len, outPtr) — A4 flat-memory protocol\n//\n// Forms NOT lifted (preserved as-is, asc still rejects):\n// C2: `const f: (x: i32) => i32 = ...` — explicit type annotation on binding\n// (colon after binding name distinguishes this from liftable form).\n// Keeping it un-lifted preserves C2 probe stability (COMPILE FAIL expected).\n//\n// The lift is applied BEFORE the `number → i32|i64|f64` rewrite so domain\n// inference applies uniformly to original and synthesized function forms.\n//\n// Capture detection (Slice 1): scan the arrow body for identifiers not in the\n// arrow's own parameter list, not TS/AS keywords, and not the local binding name.\n// Identifiers found in the enclosing function's param list or prior local\n// const/let declarations are threaded as leading captures.\n//\n// Counter: per liftClosures() invocation; __closure_0, __closure_1, etc.\n// Slices 2/3/4 (HOF, returns-a-closure, this-binding) are out of scope here.\n//\n// Cross-links: #211 (this WI), DEC-AS-CLOSURE-STRATEGY-001 (closures survey),\n// DEC-AS-ARRAY-LAYOUT-001 (S3 flat-memory A4 protocol).\n// Rationale:\n// AssemblyScript closures under --runtime stub are constrained by the absence of\n// a GC heap. Closure context allocation (the object that captures variables from\n// an enclosing scope) is a GC feature: the context object must be pinned and\n// tracked across function returns. Under --runtime stub there is no GC, so asc\n// 0.28.x rejects closure forms that require a capture context at compile time.\n//\n// The forms probed in closures-parity.test.ts (C1-C5), observed 2026-05-10:\n//\n// (C1) Array.map(x => x * 2) closure passed to managed Array.map():\n// COMPILE FAIL — anchored by arrays-parity.test.ts A5 finding (lines 525-533).\n// asc 0.28.x rejects arrow-function closures in managed Array.map() under\n// --runtime stub: managed Array requires a GC heap, and passing an arrow\n// function as a callback requires a function-typed variable which asc also\n// rejects under stub for the same closure-context reason.\n// Cross-sibling anchor: this finding mirrors arrays-parity A5 exactly.\n// If C1 compiles OK on a future asc build, it contradicts arrays-parity A5\n// — surface inconsistency to user; do NOT silently update either finding.\n//\n// (C2) No-capture arrow function stored in a typed variable:\n// `const f: (x: i32) => i32 = (x: i32): i32 => x * 2;`\n// COMPILE FAIL — asc 0.28.x rejects function-typed variables holding arrow\n// functions under --runtime stub. Even with no captured variables, the\n// function-as-value form requires a closure context object (the AS function\n// pointer + optional environment). asc emits \"Not yet supported: Closures\"\n// for this form. Boundary probe: reveals that the closure rejection is not\n// about capture presence but about the function-as-value type itself.\n//\n// (C3) Primitive-capture closure `let n: i32 = 5; (x: i32) => x + n`:\n// COMPILE FAIL — as expected. Closure capturing an i32 requires a context\n// object to hold `n`. Context allocation is GC-managed. --runtime stub has\n// no GC heap, so asc 0.28.x rejects the form at compile time with the\n// same \"Not yet supported: Closures\" error as C2.\n//\n// (C4) Capture of an @unmanaged class pointer reference:\n// COMPILE FAIL — as expected. Anchored by GC-parity G3 boundary precedent\n// (nullable managed reference fields COMPILE FAIL). Closures capturing any\n// reference-type variable (managed or unmanaged pointer) are still rejected\n// because the closure context itself must be GC-managed regardless of the\n// captured value's type.\n//\n// (C5) Static-function + dispatch-table (positive baseline):\n// COMPILE OK + value parity — top-level `function double(x: i32): i32`\n// and `function addOne(x: i32): i32` invoked through an exported integer-\n// indexed dispatch switch. No closure context, no captures: this is pure\n// static dispatch through named functions, equivalent to C function pointers.\n// This is the production-supported \"closure opt-out\" for callback-style APIs\n// in v1. 5 fixed cases + 20 fast-check runs confirm value parity vs TS ref.\n//\n// Summary (asc 0.28.x --runtime stub, observed 2026-05-10):\n// C1: COMPILE FAIL (.map(x=>x*2) — closure passed to managed Array)\n// C2: COMPILE FAIL (no-capture lambda stored in typed variable)\n// C3: COMPILE FAIL (primitive-capture closure)\n// C4: COMPILE FAIL (reference-type-capture closure)\n// C5: COMPILE OK + parity (static-function dispatch table, no captures)\n//\n// Key finding: ALL closure forms (C1-C4) COMPILE FAIL under --runtime stub,\n// regardless of capture kind (zero, primitive, or reference). The rejection is\n// not about the captured value's GC-ness — it is about the closure form itself.\n// asc 0.28.x reserves closure support for --runtime minimal/full (the GC tiers).\n//\n// Alternatives rejected:\n//\n// (Alt B) Flip --runtime to minimal/incremental/full for the closures test only:\n// Rejected. Same rationale as DEC-AS-GC-STRATEGY-001 Alt B: requires either\n// (a) a per-call runtime override field in AsBackendOptions (new emit mode —\n// parallel mechanism, Sacred Practice #12) or (b) a parallel factory\n// assemblyScriptBackendClosures() (explicit dual-authority). Both diverge\n// from the sibling-established invariant. Documented; not done.\n//\n// (Alt C) Skip closures entirely; mark #230 as impossible:\n// Rejected. Operator's 2026-05-10 unblock comment explicitly approved Option\n// (a) and said \"you can start implementation NOW\". The probe pattern used by\n// every Phase 2 sibling is the correct shape — it documents reality.\n//\n// (Alt E) Defer until --runtime minimal is adopted:\n// Rejected per operator's explicit guidance. Documenting the closure boundary\n// IS the differentiating value of the AS pivot vs wave-3 (which never lowered\n// closures at all). This slice closes #230 by mapping the boundary exactly.\n//\n// Decision: Probe-and-static-dispatch-table pattern for v1. ALL four closure\n// probe forms (C1-C4) COMPILE FAIL under --runtime stub. The C5 static-function\n// dispatch table COMPILE OK + parity path is the production-supported equivalent\n// for callback-style use cases. Full closure capture is deferred to a future\n// phase that adopts --runtime minimal/full.\n//\n// Cross-links: #230 (this WI), #232 (GC slice — DEC-AS-GC-STRATEGY-001),\n// arrays-parity.test.ts A5 (C1 cross-sibling anchor),\n// DEC-AS-MULTI-EXPORT-001 (parent Phase 2A.0).\n//\n// See also: DEC-AS-CLOSURE-LAYOUT-001 and DEC-AS-CLOSURE-ORACLE-001 in\n// closures-parity.test.ts for flat-memory layout constants\n// (CLO_BASE_PTR = 32768) and oracle details.\n\nimport { readFile as fsReadFile } from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { join } from \"node:path\";\nimport { type APIOptions, main as ascMain, createMemoryStream } from \"assemblyscript/asc\";\nimport type { ResolutionResult } from \"./resolve.js\";\n\n// ---------------------------------------------------------------------------\n// WasmBackend — public interface for WASM compilation backends\n//\n// @decision DEC-AS-BACKEND-WASM-BACKEND-TYPE-001\n// Title: WasmBackend interface lives in as-backend.ts (sole WASM backend after\n// Phase 3 retirement of wasm-backend.ts / wasm-lowering/)\n// Status: decided (WI-AS-CLEANUP-WAVE3-LOWERER, #148)\n// Rationale:\n// wasm-backend.ts was the sole prior home of the WasmBackend interface. After\n// its deletion, as-backend.ts is the only WASM backend in the codebase. Keeping\n// the type here (a) avoids a separate types-only file, (b) mirrors how Backend\n// lives in ts-backend.ts alongside tsBackend(), and (c) makes import.meta\n// resolution unambiguous for downstream callers via index.ts re-export.\n// ---------------------------------------------------------------------------\n\n/**\n * A WASM compilation backend: turns a ResolutionResult into a binary .wasm module.\n */\nexport interface WasmBackend {\n readonly name: string;\n emit(resolution: ResolutionResult): Promise<Uint8Array<ArrayBuffer>>;\n}\n\n// ---------------------------------------------------------------------------\n// asc binary resolution\n//\n// Locate the assemblyscript asc compiler relative to this package's\n// node_modules. Using createRequire(import.meta.url) finds the package in\n// the correct resolution context even when the file is compiled to dist/.\n// ---------------------------------------------------------------------------\n\nfunction resolveAsc(): string {\n const require = createRequire(import.meta.url);\n // assemblyscript exposes its CLI via the \"bin\" field in its package.json.\n // The asc entry point is bin/asc.js (runs under Node; not the .cmd shim).\n const ascPkgPath: string = require.resolve(\"assemblyscript/package.json\") as string;\n // ascPkgPath: .../node_modules/assemblyscript/package.json\n // asc.js lives at: .../node_modules/assemblyscript/bin/asc.js\n const pkgDir = ascPkgPath.replace(/[/\\\\]package\\.json$/, \"\");\n return join(pkgDir, \"bin\", \"asc.js\");\n}\n\n// ---------------------------------------------------------------------------\n// Numeric-domain inference\n//\n// Re-implements the lightweight subset of inferNumericDomain() from\n// wasm-lowering/visitor.ts needed for AS type annotation injection.\n// This avoids the ts-morph import (heavy, not needed in the AS path)\n// while producing identical domain decisions for the numeric substrate.\n//\n// Rules (matching visitor.ts rules -1, 0, 1-7):\n// i64: large integer literals (> 2^31-1) or `bigint` keyword\n// or BigInt literal suffix `n`\n// i32: bitwise operators (&|^~<<>>>>>), explicit `| 0` pattern,\n// integer-floor hints (Math.floor/ceil/round/trunc),\n// boolean-typed params/return when no f64 indicator present\n// f64: true division (/), float literals (decimal point or `e` notation),\n// Math.sqrt/sin/cos/log/exp/pow/abs/hypot/atan2 etc.,\n// Number.isFinite/Number.isNaN/Number.isInteger\n// Ambiguous → f64 (conservative: f64 is never lossy for integer inputs)\n// ---------------------------------------------------------------------------\n\nconst F64_MATH_FNS: ReadonlySet<string> = new Set([\n \"sqrt\",\n \"sin\",\n \"cos\",\n \"log\",\n \"exp\",\n \"pow\",\n \"abs\",\n \"hypot\",\n \"atan2\",\n \"sign\",\n \"cbrt\",\n \"expm1\",\n \"log1p\",\n \"log2\",\n \"log10\",\n \"atan\",\n \"asin\",\n \"acos\",\n \"sinh\",\n \"cosh\",\n \"tanh\",\n \"asinh\",\n \"acosh\",\n \"atanh\",\n]);\n\nconst INTEGER_FLOOR_MATH_FNS: ReadonlySet<string> = new Set([\"floor\", \"ceil\", \"round\", \"trunc\"]);\n\ntype NumericDomain = \"i32\" | \"i64\" | \"f64\";\n\n// @decision DEC-V1-DOMAIN-INFER-PARITY-001\n// Title: as-backend inferDomainFromSource priority order aligned with visitor.ts inferNumericDomain\n// Status: decided (WI-AS-PHASE-1-MVP-DOMAIN-INFER-PARITY, Issue #170)\n// Rationale:\n// Pre-fix, two early-return paths (bigint/n-suffix → i64; >2^31 literal → i64) won\n// over the subsequent f64/bitop scans. visitor.ts checks the priority block in the\n// order bitop > f64 > i64 > floor > fallback. For two edge-case shapes — (a) source\n// with >2^31 literal AND true division, and (b) source with n-suffix bigint AND a\n// bitwise op — the two implementations disagreed. Phase 1 corpus has no such atoms,\n// so no tests failed, but the @decision DEC-V1-LOWER-BACKEND-REUSE-001 annotation's\n// \"identical domain decisions\" claim was technically false at the edges.\n// Fix:\n// Collect i64 indicators into boolean flags alongside f64/bitop/floor flags, then\n// apply the canonical priority block. This makes the \"identical decisions\" claim\n// literally true for the documented shapes.\n// The inferNumericDomain() rules (-1 through 7) were originally developed in the\n// now-retired wasm-lowering/visitor.ts. This re-implementation is the canonical\n// authority for numeric domain inference since that file's deletion (#148).\n\n/**\n * Infer the numeric domain of a TypeScript atom source via text-level heuristics.\n *\n * Implements numeric domain inference (rules -1 through 7) using string scanning\n * instead of ts-morph AST traversal. This is appropriate here because: (1) the\n * AS backend already shells out to asc, so ts-morph's heaviness is not justified;\n * (2) the numeric substrate functions are guaranteed by the evaluation contract\n * to be simple enough for text scanning.\n *\n * @decision DEC-V1-LOWER-BACKEND-REUSE-001 (analysis half reuse)\n * @decision DEC-V1-DOMAIN-INFER-PARITY-001 (priority order alignment)\n */\nexport function inferDomainFromSource(src: string): NumericDomain {\n // Strip comments to avoid false positives from commented-out code.\n const noComments = src.replace(/\\/\\/[^\\n]*/g, \"\").replace(/\\/\\*[\\s\\S]*?\\*\\//g, \"\");\n\n let hasF64 = false;\n let hasBitop = false;\n let hasFloorHint = false;\n let hasBigIntKeyword = false;\n let hasBigIntLiteral = false;\n let hasI64RangeLiteral = false;\n\n // Rule -1 / rule 7: bigint keyword or n-suffix literal (collected as flag, NOT early-return)\n // DEC-V1-DOMAIN-INFER-PARITY-001: these must not short-circuit before bitop/f64 scans\n if (/\\bbigint\\b/.test(noComments)) hasBigIntKeyword = true;\n if (/\\b\\d+n\\b/.test(noComments)) hasBigIntLiteral = true;\n\n // Rule 5: large integer literals > 2^31-1 (collected as flag, NOT early-return)\n // DEC-V1-DOMAIN-INFER-PARITY-001: f64 indicators (true division) must win over i64 range literals\n const allNums = noComments.match(/\\b(\\d+)\\b/g);\n if (allNums !== null) {\n for (const lit of allNums) {\n const v = Number(lit);\n if (Number.isInteger(v) && v > 2147483647) {\n hasI64RangeLiteral = true;\n break;\n }\n }\n }\n\n // Rule 1: true division (/) — but not // (handled by comment stripping)\n // Match `/` that is not `/=` (assign) and not in regex-like contexts.\n if (/[^/*]\\s*\\/\\s*[^/*=]/.test(noComments) || /^\\s*\\/[^/*=]/.test(noComments)) {\n hasF64 = true;\n }\n\n // Rule 2: float literals with decimal point or exponent\n if (/\\b\\d+\\.\\d*|\\b\\d*\\.\\d+|\\b\\d+[eE][+-]?\\d+/.test(noComments)) {\n hasF64 = true;\n }\n\n // Rule 3: f64 Math functions\n const mathCalls = noComments.match(/Math\\.(\\w+)/g);\n if (mathCalls !== null) {\n for (const call of mathCalls) {\n const method = call.slice(5); // \"Math.\".length === 5\n if (F64_MATH_FNS.has(method)) hasF64 = true;\n if (INTEGER_FLOOR_MATH_FNS.has(method)) hasFloorHint = true;\n }\n }\n\n // Number.isFinite, Number.isNaN, Number.isInteger\n if (/Number\\.(isFinite|isNaN|isInteger)/.test(noComments)) {\n hasF64 = true;\n }\n\n // Rule 4/5: bitwise operators force i32 (takes priority over f64 per visitor.ts)\n // Look for &, |, ^, ~, <<, >>, >>> but not &&, || (logical ops)\n if (/(?<![&|])[&|^~](?![&|])|<<|>>>|>>/.test(noComments)) {\n hasBitop = true;\n }\n\n // Priority order matching visitor.ts (DEC-V1-DOMAIN-INFER-PARITY-001):\n // bitop → i32 (| 0 idiom; DEC-V1-WAVE-3-WASM-LOWER-BITOP-PRIORITY-001)\n // f64 → f64 (true division / float literal / Math.f64 / Number.is*)\n // i64 → i64 (bigint keyword / n-suffix / >2^31 literal)\n // floor → i32 (Math.floor/ceil/round/trunc hint)\n // default → f64 (ambiguous → conservative f64)\n if (hasBitop) return \"i32\";\n if (hasF64) return \"f64\";\n if (hasBigIntKeyword || hasBigIntLiteral || hasI64RangeLiteral) return \"i64\";\n if (hasFloorHint) return \"i32\";\n\n // Ambiguous → f64 (conservative, matching visitor.ts policy)\n return \"f64\";\n}\n\n// ---------------------------------------------------------------------------\n// Lambda-lifting: source-level closure hoisting\n//\n// @decision DEC-AS-CLOSURE-STRATEGY-002\n//\n// liftClosures() is the FIRST transformation applied in prepareAsSource()\n// (before number→i32|f64|i64 rewriting). It detects arrow-function bindings\n// of the form:\n//\n// const f = (x: T, y: T): T => <expr>;\n// let f = (x: T): T => <expr>;\n//\n// WITHOUT an explicit function-type annotation on the binding (no colon\n// between the binding name and `=`). This distinguishes liftable forms from\n// the C2 typed-binding form (`const f: (x: i32) => i32 = ...`) which is left\n// un-lifted so the C2 compile-fail probe remains stable.\n//\n// For each detected arrow binding inside a function body:\n// 1. Extract param names/types and return type from the arrow signature.\n// 2. Scan the arrow body for identifiers that are NOT the arrow's own params.\n// Identifiers that appear in the enclosing function scope (from its params\n// or prior const/let declarations in the same function body) are \"captures\"\n// and are threaded as additional leading parameters on the lifted function.\n// 3. Hoist to a synthetic top-level:\n// function __closure_<n>(<captures..., params...>): <RetType> { return <expr>; }\n// 4. Rewrite call site `f(<args>)` → `__closure_<n>(<captures..., args>)`.\n// 5. Remove the original `const/let f = ...` binding declaration.\n//\n// Counter `n` is reset per liftClosures() call (i.e., per prepareAsSource()).\n//\n// Limitations (Slice 1, 2026-05-13):\n// - Single-expression arrow bodies only (no block `=> { ... }` form).\n// - Single-statement call sites only (f(...) as a return expression).\n// - Single level of nesting (arrow body itself must not contain closures).\n// - Capture detection is heuristic (identifier scan, not full AST).\n// False positives (non-captured identifiers) produce extra leading params\n// that are unused but compile cleanly; false negatives (missed captures)\n// produce compile errors. The heuristic is conservative (tends to over-capture).\n// - Multi-line arrow signatures or comma-split across lines are NOT lifted.\n// ---------------------------------------------------------------------------\n\n/** TS/AS reserved keywords that should never be classified as captured variables. */\nconst AS_KEYWORDS: ReadonlySet<string> = new Set([\n \"abstract\",\n \"as\",\n \"async\",\n \"await\",\n \"boolean\",\n \"break\",\n \"case\",\n \"catch\",\n \"class\",\n \"const\",\n \"continue\",\n \"debugger\",\n \"declare\",\n \"default\",\n \"delete\",\n \"do\",\n \"else\",\n \"enum\",\n \"export\",\n \"extends\",\n \"f32\",\n \"f64\",\n \"false\",\n \"finally\",\n \"for\",\n \"from\",\n \"function\",\n \"get\",\n \"i16\",\n \"i32\",\n \"i64\",\n \"i8\",\n \"if\",\n \"implements\",\n \"import\",\n \"in\",\n \"instanceof\",\n \"interface\",\n \"keyof\",\n \"let\",\n \"module\",\n \"namespace\",\n \"new\",\n \"null\",\n \"number\",\n \"of\",\n \"override\",\n \"package\",\n \"private\",\n \"protected\",\n \"public\",\n \"readonly\",\n \"return\",\n \"set\",\n \"static\",\n \"string\",\n \"super\",\n \"switch\",\n \"this\",\n \"throw\",\n \"true\",\n \"try\",\n \"type\",\n \"typeof\",\n \"u16\",\n \"u32\",\n \"u64\",\n \"u8\",\n \"undefined\",\n \"var\",\n \"void\",\n \"while\",\n \"with\",\n \"yield\",\n \"Math\",\n \"Number\",\n \"Boolean\",\n \"String\",\n \"Object\",\n \"Array\",\n \"Int8Array\",\n \"Int16Array\",\n \"Int32Array\",\n \"Uint8Array\",\n \"Uint16Array\",\n \"Uint32Array\",\n \"Float32Array\",\n \"Float64Array\",\n \"BigInt64Array\",\n \"BigUint64Array\",\n \"WebAssembly\",\n \"console\",\n \"BigInt\",\n \"Symbol\",\n \"Promise\",\n \"Error\",\n \"load\",\n \"store\",\n \"changetype\",\n \"idof\",\n \"offsetof\",\n \"sizeof\",\n \"alignof\",\n \"unchecked\",\n \"unreachable\",\n \"abort\",\n]);\n\n/**\n * Extract all identifiers from a source expression (simple heuristic tokenizer).\n * Returns identifiers only — filters keywords, numeric/string literals, operators.\n */\nfunction extractIdentifiers(expr: string): Set<string> {\n const ids = new Set<string>();\n // Match JavaScript/TypeScript identifiers (word chars starting with letter or _)\n const matches = expr.match(/\\b([a-zA-Z_$][a-zA-Z0-9_$]*)\\b/g) ?? [];\n for (const id of matches) {\n if (!AS_KEYWORDS.has(id) && !/^\\d/.test(id)) {\n ids.add(id);\n }\n }\n return ids;\n}\n\n/**\n * Parse a parameter list string `x: T, y: U, ...` into an array of\n * `{ name, typeAnnotation }` objects. Returns empty array on parse failure.\n *\n * Handles simple `name: Type` pairs separated by commas.\n * Does NOT handle default values, destructuring, or rest params (Slice 1 scope).\n */\nfunction parseParamList(paramStr: string): Array<{ name: string; typeAnnotation: string }> {\n const trimmed = paramStr.trim();\n if (trimmed === \"\") return [];\n\n // Collect raw param strings (split on commas not inside angle brackets)\n const rawParams: string[] = [];\n let depth = 0;\n let current = \"\";\n for (const ch of trimmed) {\n if (ch === \"<\") {\n depth++;\n current += ch;\n } else if (ch === \">\") {\n depth--;\n current += ch;\n } else if (ch === \",\" && depth === 0) {\n rawParams.push(current.trim());\n current = \"\";\n } else {\n current += ch;\n }\n }\n if (current.trim() !== \"\") rawParams.push(current.trim());\n\n return rawParams.map((p) => {\n const colonIdx = p.indexOf(\":\");\n if (colonIdx === -1) return { name: p.trim(), typeAnnotation: \"i32\" };\n return {\n name: p.slice(0, colonIdx).trim(),\n typeAnnotation: p.slice(colonIdx + 1).trim(),\n };\n });\n}\n\n/**\n * Perform source-level lambda-lifting on a TS/AS source string.\n *\n * Detects `const/let f = (params): RetType => expr;` bindings inside function\n * bodies (untyped binding form only) and hoists them to top-level function\n * declarations `function __closure_<n>(captures..., params...): RetType`.\n *\n * @param source Raw implSource string (before any other prepareAsSource rewrites).\n * @returns Source string with arrow bindings hoisted and call sites rewritten.\n *\n * @decision DEC-AS-CLOSURE-STRATEGY-002\n */\nexport function liftClosures(source: string): string {\n // Counter reset per invocation (per prepareAsSource call).\n let counter = 0;\n\n // Synthetic top-level function declarations to prepend to the source.\n const hoisted: string[] = [];\n\n // We work line-by-line on the source, building the output.\n // The approach: for each function body, track:\n // - The enclosing function's parameter names (available as captures)\n // - Prior const/let variable names declared before the arrow binding\n // Then detect arrow binding lines and:\n // 1. Record the lifted function (binding name → __closure_n)\n // 2. Remove the binding line from the output\n // 3. Rewrite call sites in subsequent lines\n\n // We track arrow bindings that have been lifted: name → synthetic name\n const lifted = new Map<string, { syntheticName: string; captureNames: string[] }>();\n\n // Parse the source into lines and process function-scope contexts\n const lines = source.split(\"\\n\");\n const outputLines: string[] = [];\n\n // Simple scope tracking: we detect function entry by matching `function` keyword\n // and track params from the function signature. We maintain a stack of scopes.\n // Each scope has: paramNames (from function signature), localNames (from prior\n // const/let bindings in this scope).\n interface Scope {\n paramNames: string[];\n localNames: string[];\n depth: number; // brace depth at scope entry\n }\n const scopeStack: Scope[] = [];\n let braceDepth = 0;\n // Track which lifted names were introduced in which scope (for cleanup after scope exit)\n const liftedInScope: Map<number, string[]> = new Map();\n\n // Regex: match a liftable arrow binding.\n // Group 1: const|let\n // Group 2: binding name (no colon follows — the C2 form has `name: type` which we skip)\n // Group 3: param list\n // Group 4: return type (after `:`)\n // Group 5: arrow body (single expression, not a block)\n //\n // Critical: the binding name must NOT be followed by `:` (which would indicate\n // an explicit function type annotation — the C2 form that stays un-lifted).\n const ARROW_BINDING_RE =\n /^(\\s*)(const|let)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*=\\s*\\(([^)]*)\\)\\s*:\\s*([^=>{]+?)\\s*=>\\s*(.+?)\\s*;?\\s*$/;\n\n // Regex: match a function declaration to extract its params for scope tracking\n // Group 1: function name\n // Group 2: param list\n const FUNCTION_DECL_RE = /^\\s*(?:export\\s+)?function\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*\\(([^)]*)\\)/;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? \"\";\n\n // Track brace depth for scope management\n const openBraces = (line.match(/\\{/g) ?? []).length;\n const closeBraces = (line.match(/\\}/g) ?? []).length;\n\n // Check for function entry BEFORE processing the line (so we can push scope)\n const fnMatch = FUNCTION_DECL_RE.exec(line);\n if (fnMatch !== null) {\n const paramList = fnMatch[2] ?? \"\";\n const paramParsed = parseParamList(paramList);\n const scope: Scope = {\n paramNames: paramParsed.map((p) => p.name),\n localNames: [],\n depth: braceDepth + openBraces - closeBraces, // approximate: depth after this line\n };\n // The scope depth is the brace depth after the opening `{` of the function body.\n // We track it as braceDepth + net braces on this line.\n scopeStack.push(scope);\n }\n\n // Update brace depth\n braceDepth += openBraces - closeBraces;\n\n // Pop scopes that have ended (brace depth fell below scope entry depth)\n while (scopeStack.length > 0 && braceDepth < (scopeStack[scopeStack.length - 1]?.depth ?? 0)) {\n const poppedScope = scopeStack.pop();\n if (poppedScope !== undefined) {\n // Clean up lifted names introduced in this scope\n const scopeIdx = scopeStack.length;\n const scopeLiftedNames = liftedInScope.get(scopeIdx) ?? [];\n for (const name of scopeLiftedNames) {\n lifted.delete(name);\n }\n liftedInScope.delete(scopeIdx);\n }\n }\n\n const currentScope = scopeStack[scopeStack.length - 1];\n\n // Try to match an arrow binding line (only inside a function scope)\n if (currentScope !== undefined) {\n const arrowMatch = ARROW_BINDING_RE.exec(line);\n if (arrowMatch !== null) {\n const _indent = arrowMatch[1] ?? \"\";\n const _keyword = arrowMatch[2] ?? \"\";\n const bindingName = arrowMatch[3] ?? \"\";\n const paramListStr = arrowMatch[4] ?? \"\";\n const returnTypeStr = arrowMatch[5] ?? \"\";\n const bodyExpr = arrowMatch[6] ?? \"\";\n\n // Skip C2-style typed bindings: `const f: ... = ...`\n // The ARROW_BINDING_RE already excludes them (it requires `name =` not `name: type =`)\n // but double-check: if the binding name in the line is followed by `:` before `=`,\n // it is the typed form — skip it.\n const bindingPartEnd = line.indexOf(bindingName);\n const afterBinding = line.slice(bindingPartEnd + bindingName.length).trimStart();\n if (afterBinding.startsWith(\":\")) {\n // Typed binding form — do NOT lift, pass through unchanged\n outputLines.push(line);\n if (currentScope !== undefined) {\n currentScope.localNames.push(bindingName);\n }\n continue;\n }\n\n // Parse the arrow's own parameter names\n const arrowParams = parseParamList(paramListStr);\n const arrowParamNames = new Set(arrowParams.map((p) => p.name));\n\n // Detect captures: identifiers in bodyExpr that are NOT the arrow's own params,\n // NOT the binding name itself, NOT keywords, and ARE present in the enclosing scope.\n const bodyIds = extractIdentifiers(bodyExpr);\n const scopeAvailableNames = new Set([\n ...currentScope.paramNames,\n ...currentScope.localNames,\n ]);\n\n // Only treat as captured if the identifier is available in enclosing scope\n // (conservative: if in doubt, thread it)\n const captureNames: string[] = [];\n for (const id of bodyIds) {\n if (id !== bindingName && !arrowParamNames.has(id) && scopeAvailableNames.has(id)) {\n captureNames.push(id);\n }\n }\n\n // Build the lifted function name and signature\n const syntheticName = `__closure_${counter++}`;\n\n // Capture params: `captureVar: T` — we need the type for captured vars.\n // For Slice 1 (primitive captures), we look up their type from the enclosing\n // function's param list. If not found in param list, fall back to `i32`.\n const enclosingParamMap = new Map<string, string>();\n if (currentScope !== undefined) {\n // Re-parse the enclosing function's param list to get types.\n // We stored only names; we need to find types from the original line.\n // Look backwards for the function declaration to get typed params.\n for (let j = i - 1; j >= 0; j--) {\n const prevLine = lines[j] ?? \"\";\n const prevFnMatch = FUNCTION_DECL_RE.exec(prevLine);\n if (prevFnMatch !== null) {\n const enclosingParams = parseParamList(prevFnMatch[2] ?? \"\");\n for (const ep of enclosingParams) {\n enclosingParamMap.set(ep.name, ep.typeAnnotation);\n }\n break;\n }\n }\n }\n\n // Build param string for the lifted function:\n // captured vars first (with types), then the arrow's own params\n const captureParamStrs = captureNames.map((cn) => {\n const captureType = enclosingParamMap.get(cn) ?? \"i32\";\n return `${cn}: ${captureType}`;\n });\n const arrowParamStrs = arrowParams.map((p) => `${p.name}: ${p.typeAnnotation}`);\n const allParamStrs = [...captureParamStrs, ...arrowParamStrs];\n\n // Build the lifted function declaration\n const liftedFnDecl = `function ${syntheticName}(${allParamStrs.join(\", \")}): ${returnTypeStr} {\\n return ${bodyExpr};\\n}`;\n hoisted.push(liftedFnDecl);\n\n // Record the lift for call-site rewriting\n lifted.set(bindingName, { syntheticName, captureNames });\n const scopeIdx = scopeStack.length - 1;\n const existingLifted = liftedInScope.get(scopeIdx) ?? [];\n existingLifted.push(bindingName);\n liftedInScope.set(scopeIdx, existingLifted);\n\n // Add binding name to localNames so subsequent lines can see it\n currentScope.localNames.push(bindingName);\n\n // Suppress the original binding line from output\n continue;\n }\n\n // Track const/let declarations for capture detection\n const LOCAL_DECL_RE = /^\\s*(?:const|let)\\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\\s*[=:]/;\n const localDeclMatch = LOCAL_DECL_RE.exec(line);\n if (localDeclMatch !== null) {\n currentScope.localNames.push(localDeclMatch[1] ?? \"\");\n }\n }\n\n // Rewrite call sites: replace `bindingName(args)` → `__closure_n(captures..., args)`\n let rewrittenLine = line;\n for (const [bindingName, { syntheticName, captureNames }] of lifted) {\n // Match `bindingName(` not preceded by a word character (avoid partial matches)\n // and not followed by `:` (to avoid type annotations like `f: (x) => x`)\n const callRe = new RegExp(`(?<![a-zA-Z0-9_$])${bindingName}\\\\(`, \"g\");\n if (callRe.test(rewrittenLine)) {\n // Build the replacement: prepend captures to the argument list\n if (captureNames.length === 0) {\n rewrittenLine = rewrittenLine.replace(\n new RegExp(`(?<![a-zA-Z0-9_$])${bindingName}\\\\(`, \"g\"),\n `${syntheticName}(`,\n );\n } else {\n // Insert captures before existing args. For empty call `f()`, produce `__n(c1, c2)`.\n // For `f(arg)`, produce `__n(c1, c2, arg)`.\n rewrittenLine = rewrittenLine.replace(\n new RegExp(`(?<![a-zA-Z0-9_$])${bindingName}\\\\(([^)]*)\\\\)`, \"g\"),\n (_, args: string) => {\n const trimmedArgs = (args as string).trim();\n const allArgs =\n trimmedArgs === \"\"\n ? captureNames.join(\", \")\n : `${captureNames.join(\", \")}, ${trimmedArgs}`;\n return `${syntheticName}(${allArgs})`;\n },\n );\n }\n }\n }\n outputLines.push(rewrittenLine);\n }\n\n // Prepend all hoisted functions before the rest of the source\n if (hoisted.length === 0) {\n return source; // No lifts performed — return original unchanged\n }\n\n return `${hoisted.join(\"\\n\")}\\n\\n${outputLines.join(\"\\n\")}`;\n}\n\n// ---------------------------------------------------------------------------\n// AS source preparation\n//\n// Takes the entry block's TypeScript implSource and produces valid\n// AssemblyScript source for asc compilation.\n//\n// Transformations applied (matching tsBackend's cleanBlockSource for stripping,\n// then applying AS-specific rewrites):\n// 1. [NEW] Lambda-lift arrow bindings (liftClosures) — DEC-AS-CLOSURE-STRATEGY-002\n// 2. Strip TS-only import/export constructs (import type, type aliases,\n// CONTRACT export, shadow type aliases)\n// 3. Rewrite `number` type annotations to the inferred AS numeric type\n// (i32 | i64 | f64)\n// 4. Handle bigint→i64 rewrites when domain is i64\n// ---------------------------------------------------------------------------\n\nconst INTRA_IMPORT_RE =\n /^import type\\s+\\{[^}]*\\}\\s+from\\s+[\"'](\\.|@yakcc\\/seeds\\/|@yakcc\\/blocks\\/)[^\"']*[\"'];?\\s*$/;\nconst SHADOW_ALIAS_RE = /^type\\s+_\\w+\\s*=\\s*typeof\\s+\\w+\\s*;?\\s*$/;\nconst CONTRACTS_IMPORT_RE = /^import type\\s+\\{[^}]*\\}\\s+from\\s+[\"']@yakcc\\/contracts[\"'];?\\s*$/;\nconst CONTRACT_EXPORT_START_RE = /^export const CONTRACT(?:\\s*:\\s*\\w+)?\\s*=\\s*\\{/;\n\n/**\n * Prepare an implSource string for asc compilation.\n *\n * Strips TypeScript-only constructs that asc cannot handle, then rewrites\n * `number` type annotations to the inferred AS numeric type.\n *\n * @param source - Raw implSource from ResolvedBlock\n * @param domain - Inferred numeric domain for `number` → AS-type rewriting\n * @returns AS-compatible source string\n */\nexport function prepareAsSource(source: string, domain: NumericDomain): string {\n const asType = domain === \"i64\" ? \"i64\" : domain === \"f64\" ? \"f64\" : \"i32\";\n\n // Stage 1: Lambda-lift arrow bindings FIRST, before any type rewriting.\n // @decision DEC-AS-CLOSURE-STRATEGY-002\n // The lift must run on the original `number`-annotated source so that\n // both original and synthesized function bodies receive the number→AS-type\n // rewrite uniformly in stage 3 below.\n const lifted = liftClosures(source);\n\n const lines = lifted.split(\"\\n\");\n const cleaned: string[] = [];\n let contractDepth = 0;\n\n for (const line of lines) {\n // Skip CONTRACT multi-line declaration (same logic as tsBackend's cleanBlockSource)\n if (contractDepth > 0) {\n for (const ch of line) {\n if (ch === \"{\") contractDepth++;\n else if (ch === \"}\") contractDepth--;\n }\n continue;\n }\n if (CONTRACT_EXPORT_START_RE.test(line)) {\n for (const ch of line) {\n if (ch === \"{\") contractDepth++;\n else if (ch === \"}\") contractDepth--;\n }\n continue;\n }\n if (INTRA_IMPORT_RE.test(line)) continue;\n if (SHADOW_ALIAS_RE.test(line)) continue;\n if (CONTRACTS_IMPORT_RE.test(line)) continue;\n\n cleaned.push(line);\n }\n\n // Remove leading blank lines\n let start = 0;\n while (start < cleaned.length && cleaned[start]?.trim() === \"\") start++;\n let src = cleaned.slice(start).join(\"\\n\");\n\n // Rewrite TypeScript `number` type annotations to AS numeric type.\n // Replace `: number` in param and return type positions.\n src = src.replace(/:\\s*number\\b/g, `: ${asType}`);\n\n // Handle i64 domain: rewrite bigint-specific TS constructs\n if (domain === \"i64\") {\n // Rewrite `: bigint` type annotations → `: i64`\n src = src.replace(/:\\s*bigint\\b/g, \": i64\");\n // BigInt(n) constructor → direct i64 cast: BigInt(expr) → (expr as i64)\n src = src.replace(/BigInt\\(([^)]+)\\)/g, \"($1 as i64)\");\n // BigInt literals: 123n → 123 (AS uses plain integer literals for i64 context)\n src = src.replace(/(\\d+)n\\b/g, \"$1\");\n }\n\n return src;\n}\n\n// ---------------------------------------------------------------------------\n// Exported factory\n// ---------------------------------------------------------------------------\n\n// @decision DEC-AS-BACKEND-OPTIONS-001\n// Title: assemblyScriptBackend() accepts optional AsBackendOptions for per-factory asc flags\n// Status: decided (WI-AS-PHASE-2A-MULTI-EXPORT-AND-RECORDS, 2026-05-10)\n// Rationale:\n// Phase 1 compiled all atoms with --noExportMemory (pure function substrate).\n// Phase 2A adds record substrate support (DEC-AS-RECORD-LAYOUT-001) where the\n// test needs to write struct bytes into the WASM memory before calling the function.\n// This requires omitting --noExportMemory (i.e. exporting memory). Rather than\n// adding a second factory function (which would be a parallel mechanism per\n// Sacred Practice #12), an options bag is passed to the factory. Default behaviour\n// (exportMemory: false) is byte-identical to Phase 1.\n//\n// Alternative considered: separate `assemblyScriptRecordBackend()` factory.\n// Rejected: creates two nearly-identical factories that diverge over time. One\n// factory with a documented option is the single-source-of-truth design.\nexport interface AsBackendOptions {\n /**\n * When true, omit --noExportMemory from the asc invocation so the compiled\n * module exports its linear memory. Required for record substrates where the\n * test harness writes struct bytes directly into WASM memory before calling\n * the entry function. Default: false (matches Phase 1 pure-numeric behaviour).\n *\n * @decision DEC-AS-BACKEND-OPTIONS-001\n * @decision DEC-AS-RECORD-LAYOUT-001\n */\n readonly exportMemory?: boolean;\n}\n\n/**\n * Create the AssemblyScript WASM backend.\n *\n * The backend compiles yakcc atoms to .wasm via AssemblyScript (asc).\n * Phase 1 scope: numeric substrate (i32/i64/f64 arithmetic, bitops, Math.*).\n * Phase 2A extensions: multi-export modules (DEC-AS-MULTI-EXPORT-001),\n * records via flat-struct linear-memory (DEC-AS-RECORD-LAYOUT-001).\n *\n * Workflow per emit() call:\n * 1. Extract the entry block's implSource from the ResolutionResult\n * 2. Infer the numeric domain (i32/i64/f64) from source heuristics\n * 3. Prepare AS-compatible source (strip TS-only constructs, rewrite types)\n * 4. Invoke asc programmatic main() in-process with virtual-filename readFile/writeFile callbacks\n * 5. Capture the WASM bytes from the writeFile callback, return Uint8Array<ArrayBuffer>\n *\n * Multi-export support (DEC-AS-MULTI-EXPORT-001):\n * asc natively emits exports for every `export function` in the source.\n * No change to the emitter is needed — prepareAsSource() already preserves\n * all `export function` declarations. The consumer (closer-parity-as.test.ts)\n * treats WASM with ≥1 export as covered (structural coverage for P-OTHER,\n * per-export value parity when an oracle exists).\n *\n * @param opts - Optional factory configuration (see AsBackendOptions)\n * @decision DEC-V1-LOWER-BACKEND-REUSE-001 (see file header)\n * @decision DEC-AS-BACKEND-IN-PROCESS-001 (see file header — supersedes DEC-AS-BACKEND-TMPDIR-001)\n * @decision DEC-AS-MULTI-EXPORT-001 (multi-export: asc handles natively; no emitter change)\n * @decision DEC-AS-RECORD-LAYOUT-001 (records: flat-struct linear-memory; exportMemory option)\n * @decision DEC-AS-BACKEND-OPTIONS-001 (optional AsBackendOptions for per-factory asc flags)\n */\nexport function assemblyScriptBackend(opts?: AsBackendOptions): WasmBackend {\n const exportMemory = opts?.exportMemory ?? false;\n\n return {\n name: \"as\",\n async emit(resolution: ResolutionResult): Promise<Uint8Array<ArrayBuffer>> {\n const entryBlock = resolution.blocks.get(resolution.entry);\n if (entryBlock === undefined) {\n throw new Error(\n `assemblyScriptBackend: entry block not found in resolution (entry=${resolution.entry})`,\n );\n }\n\n const domain = inferDomainFromSource(entryBlock.source);\n const asSource = prepareAsSource(entryBlock.source, domain);\n\n // Virtual filenames — no tmpdir needed (DEC-AS-BACKEND-IN-PROCESS-001).\n // asc.main() resolves filenames via the readFile/writeFile callbacks below;\n // these tokens never touch the real filesystem.\n const VIRTUAL_SRC = \"atom.ts\";\n const VIRTUAL_OUT = \"atom.wasm\";\n\n // Build argv equivalent to the former execFileSync ascArgs, minus the\n // leading asc.js script-name slot (implicit in the programmatic API).\n // @decision DEC-AS-BACKEND-IN-PROCESS-001\n // @decision DEC-AS-BACKEND-OPTIONS-001\n // @decision DEC-AS-RECORD-LAYOUT-001\n const argv: string[] = [\n VIRTUAL_SRC,\n \"--outFile\",\n VIRTUAL_OUT,\n \"--optimize\",\n \"--runtime\",\n \"stub\", // minimal AS runtime (no GC) — numeric + struct substrates\n ];\n\n // --noExportMemory: suppress memory export for pure numeric functions\n // (Phase 1 default). Omit when exportMemory is requested (Phase 2A\n // record substrates need to write struct bytes into WASM memory).\n if (!exportMemory) {\n argv.push(\"--noExportMemory\");\n } else {\n // --initialMemory 1: guarantee ≥1 page (64 KiB) when memory is exported.\n // The AS stub runtime does not allocate pages by default; without an\n // initial page, DataView writes by the test harness throw RangeError.\n // @decision DEC-AS-RECORD-LAYOUT-001\n argv.push(\"--initialMemory\", \"1\");\n }\n\n // Captured output buffers for diagnostics and result.\n let wasmBytes: Uint8Array | null = null;\n const stderrStream = createMemoryStream();\n const stdoutStream = createMemoryStream();\n\n // readFile callback: serve the atom source in-memory; delegate std-lib\n // and importmap lookups to real disk via fs/promises.readFile.\n // Returning null signals \"file not found\" per asc API contract — asc then\n // falls back to its bundled std-lib resolver for built-in modules.\n const readFile: APIOptions[\"readFile\"] = async (\n filename: string,\n _baseDir: string,\n ): Promise<string | null> => {\n if (filename === VIRTUAL_SRC) {\n return asSource;\n }\n try {\n // Delegate to real disk for AS std-lib and importmap files.\n return await fsReadFile(filename, \"utf8\");\n } catch {\n return null; // ENOENT — let asc use its built-in resolver\n }\n };\n\n // writeFile callback: capture WASM bytes when asc emits the output file.\n // Any ancillary artefacts (sourcemap, bindings) are accepted silently —\n // we don't request them (no --sourceMap), so this branch is a safety net.\n const writeFile: APIOptions[\"writeFile\"] = async (\n filename: string,\n contents: Uint8Array | string,\n ): Promise<void> => {\n if (filename === VIRTUAL_OUT && contents instanceof Uint8Array) {\n wasmBytes = contents;\n }\n // Other artefacts (e.g. future ancillary files): accept silently.\n };\n\n const apiOptions: APIOptions = {\n stdout: stdoutStream,\n stderr: stderrStream,\n readFile,\n writeFile,\n };\n\n const result = await ascMain(argv, apiOptions);\n\n if (result.error !== null) {\n throw new Error(\n `assemblyScriptBackend: asc compilation failed for entry=${resolution.entry}\\n` +\n `domain: ${domain}\\n` +\n `source:\\n${asSource}\\n` +\n `asc error:\\n${result.error.message}\\n` +\n `stderr:\\n${stderrStream.toString()}`,\n );\n }\n\n if (wasmBytes === null) {\n throw new Error(\n `assemblyScriptBackend: asc completed but produced no WASM output for entry=${resolution.entry}\\n` +\n `domain: ${domain}\\n` +\n `stderr:\\n${stderrStream.toString()}`,\n );\n }\n\n // Local const narrows the type from `Uint8Array | null` to `Uint8Array`\n // so TypeScript can see through the async-callback assignment above.\n const capturedBytes: Uint8Array = wasmBytes;\n // Cast to the typed Uint8Array<ArrayBuffer> that WasmBackend.emit promises.\n return new Uint8Array(\n capturedBytes.buffer,\n capturedBytes.byteOffset,\n capturedBytes.byteLength,\n ) as Uint8Array<ArrayBuffer>;\n },\n };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-COMPILE-ASSEMBLE-003: assemble() builds the SubBlockResolver by\n// calling registry.selectBlocks(specHash) for each sub-block import path extracted\n// from the entry block's implSource. The specHash is derived from the import path\n// stem using a stem→specHash index pre-built by fetching known BlockMerkleRoots.\n// Status: implemented (WI-T04); supersedes DEC-COMPILE-ASSEMBLE-001 and\n// DEC-COMPILE-ASSEMBLE-RESOLVER-002 (ContractId-based stem index, WI-005).\n// The old ContractId/stem/parseBlock pre-scan is deleted per Sacred Practice #12.\n// Rationale: With the triplet migration, every block in the registry is identified by\n// BlockMerkleRoot. Sub-block import paths in impl.ts (e.g. \"@yakcc/seeds/blocks/digit\")\n// carry no direct BlockMerkleRoot; they encode a module-path reference that resolves\n// through the spec_hash index. assemble() therefore:\n// 1. Fetches the entry BlockTripletRow via registry.getBlock(entry).\n// 2. Extracts sub-block import specifiers from the row's implSource (same heuristic\n// as resolveComposition's extractSubBlockImports).\n// 3. For each specifier, derives a candidate SpecHash by looking up which registered\n// blocks have an implSource export name matching the path stem — OR, if the caller\n// supplies knownMerkleRoots, pre-builds a stem→specHash index upfront from those\n// rows. selectBlocks(specHash) then returns the ordered candidate list and assemble\n// picks the first (best) result per the registry's selection ordering.\n// 4. The SubBlockResolver closure calls selectBlocks(specHash) and returns the first\n// candidate BlockMerkleRoot, or null if none found.\n//\n// The byte-identical re-emit invariant is preserved: given an unchanged registry,\n// selectBlocks always returns the same ordered list for the same specHash, so the\n// same BlockMerkleRoot is always chosen, producing the same resolution order and\n// the same emitted artifact.\n\nimport type { BlockMerkleRoot, Granularity, SpecHash } from \"@yakcc/contracts\";\nimport { DEFAULT_GRANULARITY } from \"@yakcc/contracts\";\nimport type { Registry } from \"@yakcc/registry\";\nimport type { ProvenanceManifest } from \"./manifest.js\";\nimport { buildManifest } from \"./manifest.js\";\nimport { ResolutionError, type ResolutionResult, resolveComposition } from \"./resolve.js\";\nimport { tsBackend } from \"./ts-backend.js\";\nimport type { Backend } from \"./ts-backend.js\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * The output of one assembly pass: source text of the composed module plus\n * a provenance manifest listing every block used.\n *\n * No author, signature, or ownership fields — DEC-NO-OWNERSHIP-011.\n */\nexport interface Artifact {\n /** The emitted module source text. */\n readonly source: string;\n readonly manifest: ProvenanceManifest;\n}\n\n/**\n * Options for assemble().\n *\n * knownMerkleRoots — an optional iterable of BlockMerkleRoots already present in\n * the registry. When provided, assemble() pre-fetches all their BlockTripletRows to\n * build a stem → SpecHash index before the DFS traversal. This is required for\n * corpora that use relative import paths (e.g. \"./bracket.js\") where the stem\n * encodes a function name that must be matched against stored impl sources to derive\n * the SpecHash needed for selectBlocks().\n *\n * When omitted, assemble() attempts to resolve sub-block refs using only the\n * entry block's own implSource imports (typically insufficient for corpora that\n * use relative import paths, since the SpecHash is unknown without fetching rows).\n *\n * granularity — atom-specificity dial (1 = tightest, 5 = loosest).\n * Defaults to DEFAULT_GRANULARITY (3). Per-level semantics are calibrated from\n * B9 (#446) and B4 (#188) sweep data; the dial is accepted here so the CLI and\n * hook layers can pass it through now, even before per-level behaviour is wired.\n * See @decision DEC-WI463-GRANULARITY-001 in @yakcc/contracts/src/granularity.ts.\n */\nexport interface AssembleOptions {\n readonly knownMerkleRoots?: Iterable<BlockMerkleRoot>;\n readonly granularity?: Granularity;\n}\n\n/** @internal Resolve the effective granularity from AssembleOptions. */\nexport function resolveGranularity(opts: AssembleOptions | undefined): Granularity {\n return opts?.granularity ?? DEFAULT_GRANULARITY;\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extract the filename stem from an import path.\n *\n * Examples:\n * \"./bracket.js\" → \"bracket\"\n * \"@yakcc/seeds/blocks/bracket\" → \"bracket\"\n * \"./non-ascii-rejector.js\" → \"non-ascii-rejector\"\n */\nfunction importPathStem(importedFrom: string): string {\n const lastSlash = importedFrom.lastIndexOf(\"/\");\n const base = lastSlash >= 0 ? importedFrom.slice(lastSlash + 1) : importedFrom;\n return base.endsWith(\".js\") ? base.slice(0, -3) : base;\n}\n\n/**\n * Convert a kebab-case filename stem to camelCase.\n *\n * The seeds corpus uses kebab-case filenames (non-ascii-rejector) but exports\n * camelCase function names (nonAsciiRejector). Matching requires normalisation.\n *\n * Example: \"non-ascii-rejector\" → \"nonAsciiRejector\"\n */\nfunction stemToCamelCase(stem: string): string {\n return stem.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n}\n\n/**\n * Extract the primary exported function name from a block impl source.\n *\n * Scans for the first \"export function <name>\" or \"export async function <name>\" line.\n * Returns null if none found.\n */\nfunction extractFunctionName(source: string): string | null {\n for (const line of source.split(\"\\n\")) {\n const match = line.match(/^export\\s+(?:async\\s+)?function\\s+(\\w+)\\s*[(<]/);\n if (match?.[1] !== undefined) return match[1];\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Internal: build the stem → SpecHash index from known merkle roots\n// ---------------------------------------------------------------------------\n\n/**\n * Pre-scan a set of known BlockMerkleRoots to build a stem → SpecHash index.\n *\n * For each known root, fetches the BlockTripletRow from the registry and indexes\n * by the exported function name extracted from implSource (camelCase primary key)\n * and the raw stem (kebab-case fallback). This enables the SubBlockResolver to\n * map import-path stems to SpecHashes for selectBlocks() lookup.\n *\n * Returns the completed stem → SpecHash index.\n */\nasync function buildStemSpecHashIndex(\n knownRoots: ReadonlyArray<BlockMerkleRoot>,\n registry: Registry,\n): Promise<Map<string, SpecHash>> {\n const index = new Map<string, SpecHash>();\n\n for (const root of knownRoots) {\n const row = await registry.getBlock(root);\n if (row === null) continue;\n\n const fnName = extractFunctionName(row.implSource);\n if (fnName !== null) {\n // Primary key: camelCase function name (matches the most import stems).\n index.set(fnName, row.specHash);\n }\n // No secondary stem key needed: extractFunctionName covers the canonical case.\n // If a corpus uses kebab-case function names (unusual), the camelCase conversion\n // in the resolver will handle the normalisation.\n }\n\n return index;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Assemble a runnable TypeScript module from the entry block's composition graph.\n *\n * Steps:\n * 1. Pre-build a stem → SpecHash index from knownMerkleRoots (if supplied).\n * 2. Construct a SubBlockResolver that maps import-path stems to BlockMerkleRoots\n * via stem → SpecHash → registry.selectBlocks(specHash) → first candidate.\n * 3. Run resolveComposition() with that SubBlockResolver.\n * 4. Emit the assembled source via the backend (defaults to tsBackend()).\n * 5. Build the ProvenanceManifest via buildManifest().\n * 6. Return Artifact { source, manifest }.\n *\n * Byte-identical re-emit: given an unchanged registry, selectBlocks always returns\n * the same ordered list for the same specHash (deterministic ordering per T03's\n * selection algorithm), so the same BlockMerkleRoot is always chosen, the same\n * resolution order results, and the emitted artifact and manifest are byte-identical.\n *\n * @throws ResolutionError if any block in the composition graph is missing or cyclic.\n */\nexport async function assemble(\n entry: BlockMerkleRoot,\n registry: Registry,\n backend: Backend = tsBackend(),\n options: AssembleOptions = {},\n): Promise<Artifact> {\n // Step 1: pre-build stem → SpecHash index from known roots.\n const knownRoots: ReadonlyArray<BlockMerkleRoot> = options.knownMerkleRoots\n ? [...options.knownMerkleRoots]\n : [];\n const stemSpecHashIndex = await buildStemSpecHashIndex(knownRoots, registry);\n\n // Step 2: SubBlockResolver — maps import path to BlockMerkleRoot via selectBlocks.\n const subBlockResolver = async (importedFrom: string): Promise<BlockMerkleRoot | null> => {\n const stem = importPathStem(importedFrom);\n const camel = stemToCamelCase(stem);\n\n // Try camelCase first (primary), then raw stem (fallback for unusual names).\n const specHashValue = stemSpecHashIndex.get(camel) ?? stemSpecHashIndex.get(stem) ?? null;\n if (specHashValue === null) return null;\n\n // selectBlocks returns candidates in deterministic selection order (T03).\n const candidates = await registry.selectBlocks(specHashValue);\n return candidates[0] ?? null;\n };\n\n // Step 3: resolve the composition graph.\n let resolution: ResolutionResult;\n try {\n resolution = await resolveComposition(entry, registry, subBlockResolver);\n } catch (err) {\n if (err instanceof ResolutionError) throw err;\n throw new Error(`Assembly failed: ${String(err)}`);\n }\n\n // Step 4: emit source.\n const source = await backend.emit(resolution);\n\n // Step 5: build provenance manifest.\n const manifest = await buildManifest(resolution, registry);\n\n return { source, manifest };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-COMPILE-MANIFEST-002: buildManifest derives verificationStatus from\n// registry.getProvenance(merkleRoot) — \"passing\" if at least one ProvenanceTestEntry\n// has passed === true, otherwise \"unverified\". Each ProvenanceEntry now records both\n// block_merkle_root and spec_hash (required by WI-T04 EC item d).\n// Status: implemented (WI-T04); supersedes DEC-COMPILE-MANIFEST-001 (ContractId-based,\n// WI-005). The old ContractId-keyed manifest is deleted; no dual-authority coexistence\n// (Sacred Practice #12).\n// Rationale: The manifest is a read-only audit trail that names every block used\n// in an assembly by its BlockMerkleRoot + SpecHash, records its impl source for\n// inspection, and captures the verification state at assembly time. No author/\n// signature/ownership fields are present (DEC-NO-OWNERSHIP-011).\n//\n// @decision DEC-COMPILE-MANIFEST-003 (WI-V2-04 L4): ProvenanceEntry.referencedForeign\n// Status: decided (WI-V2-04 L4)\n// Rationale: Each non-foreign block in the manifest records the foreign-dependency tree\n// declared in block_foreign_refs via getForeignRefs(). Foreign blocks are opaque leaves\n// and carry referencedForeign:[] (they do not nest). The field is required (not optional)\n// so consumers can rely on structural completeness without null-checks. The format is\n// \"pkg#export\" per the ForeignRefRow.foreignBlockRoot → BlockTripletRow lookup chain.\n// Authority invariant L4-I1: ProvenanceEntry shape is owned by this file only.\n// No other package re-declares ProvenanceEntry.\n\nimport type { BlockMerkleRoot, SpecHash } from \"@yakcc/contracts\";\nimport type { Registry } from \"@yakcc/registry\";\nimport type { ResolutionResult } from \"./resolve.js\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/** Whether this block's implementation has been verified against its contract. */\nexport type VerificationStatus = \"passing\" | \"unverified\";\n\n/**\n * One entry in a provenance manifest, describing one block used in an assembly.\n *\n * No author, signature, or ownership fields — DEC-NO-OWNERSHIP-011.\n */\nexport interface ProvenanceEntry {\n /** The block's content address (BLAKE3(spec_hash || impl_hash || proof_root)). */\n readonly blockMerkleRoot: BlockMerkleRoot;\n /** The spec's content address (BLAKE3(canonicalize(spec.yak))). */\n readonly specHash: SpecHash;\n /** The block impl source text at assembly time. */\n readonly source: string;\n /** Direct sub-block dependencies (BlockMerkleRoots). */\n readonly subBlocks: ReadonlyArray<BlockMerkleRoot>;\n readonly verificationStatus: VerificationStatus;\n /**\n * The BlockMerkleRoot of the recursion-tree parent from which this block was\n * shaved. Present only when the registry row has a non-null parent_block_root.\n * Omitted (field absent) for root blocks — hand-authored seeds and shave's\n * top-level proposals. Population awaits WI-014-04 shave-persistence follow-up;\n * all current rows leave this field absent.\n */\n readonly recursionParent?: BlockMerkleRoot;\n /**\n * Foreign-dependency tree for this block. Each entry is \"pkg#export\" identifying\n * one foreign atom referenced by this block's impl/spec, in registry declaration_index\n * ASC order. Required field (never optional): [] for blocks with no foreign deps,\n * and for foreign blocks themselves (which are opaque leaves that do not nest).\n *\n * Authority invariant L4-I3: this field is required (not optional). [] is the\n * empty case. Legacy manifests written before L4 default to [] at read time.\n *\n * @decision DEC-COMPILE-MANIFEST-003\n */\n readonly referencedForeign: ReadonlyArray<string>;\n}\n\n/**\n * The complete provenance manifest for one assembly.\n *\n * `entries` is ordered topologically (leaves first, entry last), matching\n * ResolutionResult.order. Every block in the transitive closure appears exactly once.\n *\n * No author, signature, or ownership fields — DEC-NO-OWNERSHIP-011.\n */\nexport interface ProvenanceManifest {\n readonly entry: BlockMerkleRoot;\n readonly entries: ReadonlyArray<ProvenanceEntry>;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build a ProvenanceManifest from a ResolutionResult.\n *\n * For each resolved block, calls registry.getProvenance(merkleRoot) to determine\n * whether at least one test run has passed. If so, verificationStatus is \"passing\";\n * otherwise \"unverified\".\n *\n * Each entry records both blockMerkleRoot and specHash as required by the\n * WI-T04 Evaluation Contract (EC item d).\n *\n * The entries are ordered topologically (leaves first) following ResolutionResult.order.\n */\nexport async function buildManifest(\n resolution: ResolutionResult,\n registry: Registry,\n): Promise<ProvenanceManifest> {\n const entries: ProvenanceEntry[] = [];\n\n for (const merkleRoot of resolution.order) {\n const block = resolution.blocks.get(merkleRoot);\n if (block === undefined) {\n // Should never happen: order is derived from blocks.\n throw new Error(`buildManifest: merkleRoot ${merkleRoot} in order but not in blocks map`);\n }\n\n const provenance = await registry.getProvenance(merkleRoot);\n const hasPassing = provenance.testHistory.some((entry) => entry.passed);\n const verificationStatus: VerificationStatus = hasPassing ? \"passing\" : \"unverified\";\n\n // Fetch the full block row to read parent_block_root and kind. The registry may\n // return null if the block has been evicted (should not happen in normal operation,\n // but we guard defensively). parentBlockRoot is omitted when null (field absent on\n // ProvenanceEntry) — only set when the registry row carries a non-null value.\n const blockRow = await registry.getBlock(merkleRoot);\n\n // Populate referencedForeign: foreign blocks are opaque leaves (referencedForeign:[]).\n // Non-foreign blocks look up block_foreign_refs via getForeignRefs() and format each\n // row as \"pkg#export\" by fetching the foreign block row for its foreignPkg/foreignExport.\n // Required field (L4-I3): [] is the empty case; never undefined.\n const referencedForeign: string[] = [];\n const isForeign = blockRow?.kind === \"foreign\";\n if (!isForeign) {\n const foreignRefs = await registry.getForeignRefs(merkleRoot);\n for (const ref of foreignRefs) {\n const foreignRow = await registry.getBlock(ref.foreignBlockRoot);\n if (foreignRow?.foreignPkg != null && foreignRow.foreignExport != null) {\n referencedForeign.push(`${foreignRow.foreignPkg}#${foreignRow.foreignExport}`);\n }\n }\n }\n\n const entry: ProvenanceEntry = {\n blockMerkleRoot: merkleRoot,\n specHash: block.specHash,\n source: block.source,\n subBlocks: block.subBlocks,\n verificationStatus,\n referencedForeign,\n ...(blockRow?.parentBlockRoot != null ? { recursionParent: blockRow.parentBlockRoot } : {}),\n };\n entries.push(entry);\n }\n\n return {\n entry: resolution.entry,\n entries,\n };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-COMPILE-RESOLVE-002: resolveComposition traverses the composition\n// graph depth-first using BlockMerkleRoot as the node identity (WI-T04).\n// Status: implemented (WI-T04); supersedes DEC-COMPILE-RESOLVE-001 (ContractId-based,\n// WI-005). The old ContractId-based resolver is deleted; no dual-authority coexistence\n// (Sacred Practice #12).\n// Rationale: After the triplet migration (T01-T03), every block in the registry is\n// identified by its BlockMerkleRoot. Sub-block composition references in impl.ts are\n// \"import type\" lines whose module specifier paths are resolved to BlockMerkleRoots\n// via the SubBlockResolver callback (backed by registry.selectBlocks(specHash) in\n// assemble()). The DFS therefore:\n// 1. Fetches BlockTripletRow via registry.getBlock(merkleRoot).\n// 2. Extracts sub-block import paths from the row's implSource by scanning for\n// \"import type\" lines whose specifier starts with \"./\" or \"@yakcc/seeds/\" or\n// \"@yakcc/blocks/\" — the same heuristic as @yakcc/ir's extractComposition.\n// We do NOT call parseBlockTriplet from disk here; the impl source is already\n// in the registry row.\n// 3. Resolves each import path to a BlockMerkleRoot via the SubBlockResolver.\n// 4. Recurses (post-order DFS) into each resolved sub-block.\n//\n// Cycle detection uses a Set<BlockMerkleRoot> tracking the current DFS path.\n// Topological order is preserved: leaves appear before parents.\n\nimport type { BlockMerkleRoot, SpecHash } from \"@yakcc/contracts\";\nimport type { Registry } from \"@yakcc/registry\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * A fully resolved block: its BlockMerkleRoot, impl source text, spec hash,\n * and direct sub-block deps (by BlockMerkleRoot).\n */\nexport interface ResolvedBlock {\n readonly merkleRoot: BlockMerkleRoot;\n readonly specHash: SpecHash;\n readonly source: string;\n readonly subBlocks: ReadonlyArray<BlockMerkleRoot>;\n}\n\n/**\n * The result of a full composition-graph traversal starting from an entry block.\n *\n * `blocks` is the complete transitive closure (merkleRoot → ResolvedBlock).\n * `order` is topological (leaves first, entry last).\n */\nexport interface ResolutionResult {\n readonly entry: BlockMerkleRoot;\n readonly blocks: ReadonlyMap<BlockMerkleRoot, ResolvedBlock>;\n readonly order: ReadonlyArray<BlockMerkleRoot>;\n}\n\n// ---------------------------------------------------------------------------\n// Error type\n// ---------------------------------------------------------------------------\n\nexport type ResolutionErrorKind =\n | \"missing-block\" // block not found in registry by merkle root\n | \"cycle\" // directed cycle in the composition graph\n | \"invalid-block\"; // block fetched but implSource is malformed\n\n/**\n * Thrown by resolveComposition when traversal cannot complete.\n */\nexport class ResolutionError extends Error {\n readonly kind: ResolutionErrorKind;\n readonly merkleRoot: BlockMerkleRoot;\n\n constructor(opts: {\n readonly kind: ResolutionErrorKind;\n readonly merkleRoot: BlockMerkleRoot;\n readonly message: string;\n }) {\n super(opts.message);\n this.name = \"ResolutionError\";\n this.kind = opts.kind;\n this.merkleRoot = opts.merkleRoot;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Sub-block resolver callback type\n// ---------------------------------------------------------------------------\n\n/**\n * Maps a sub-block import path (e.g. \"@yakcc/seeds/blocks/digit\" or \"./bracket.js\")\n * to the BlockMerkleRoot of the chosen block, or null to skip the reference.\n *\n * Provided by assemble() so the composition traversal stays decoupled from\n * knowledge about how the registry is indexed. The resolver is backed by\n * registry.selectBlocks(specHash) in the assemble() implementation.\n */\nexport type SubBlockResolver = (importedFrom: string) => Promise<BlockMerkleRoot | null>;\n\n// ---------------------------------------------------------------------------\n// Internal: extract sub-block import paths from implSource\n// ---------------------------------------------------------------------------\n\n/**\n * Regex that matches \"import type { ... } from '...'\" lines whose specifier\n * starts with \"./\" or \"@yakcc/seeds/\" or \"@yakcc/blocks/\".\n *\n * Mirrors the heuristic in @yakcc/ir's extractComposition (block-parser.ts).\n * These are the intra-corpus sub-block composition references.\n */\nconst SUB_BLOCK_IMPORT_RE =\n /^import\\s+type\\s+\\{[^}]*\\}\\s+from\\s+[\"'](\\.\\/|@yakcc\\/seeds\\/|@yakcc\\/blocks\\/)([^\"']*)[\"'];?\\s*$/;\n\n/**\n * Extract all sub-block import module specifiers from an impl.ts source string.\n *\n * Returns specifier strings in declaration order. Duplicates are included;\n * the DFS will skip already-visited blocks.\n */\nfunction extractSubBlockImports(implSource: string): string[] {\n const specifiers: string[] = [];\n for (const line of implSource.split(\"\\n\")) {\n const match = line.match(SUB_BLOCK_IMPORT_RE);\n if (match !== null) {\n // Reconstruct the full specifier (prefix + suffix)\n const prefix = match[1] ?? \"\";\n const suffix = match[2] ?? \"\";\n specifiers.push(`${prefix}${suffix}`);\n }\n }\n return specifiers;\n}\n\n// ---------------------------------------------------------------------------\n// Internal DFS state\n// ---------------------------------------------------------------------------\n\ninterface DfsState {\n readonly registry: Registry;\n readonly subBlockResolver: SubBlockResolver;\n readonly blocks: Map<BlockMerkleRoot, ResolvedBlock>;\n readonly order: BlockMerkleRoot[];\n /** Roots currently on the DFS stack — used for cycle detection. */\n readonly path: Set<BlockMerkleRoot>;\n}\n\n// ---------------------------------------------------------------------------\n// Internal: visit one node\n// ---------------------------------------------------------------------------\n\nasync function visitBlock(merkleRoot: BlockMerkleRoot, state: DfsState): Promise<void> {\n // Already resolved (DAG share) — skip.\n if (state.blocks.has(merkleRoot)) return;\n\n // Cycle: this merkleRoot is already on the current DFS path.\n if (state.path.has(merkleRoot)) {\n throw new ResolutionError({\n kind: \"cycle\",\n merkleRoot,\n message: `Composition cycle detected involving block ${merkleRoot}`,\n });\n }\n\n // Fetch the block triplet row from the registry.\n const row = await state.registry.getBlock(merkleRoot);\n if (row === null) {\n throw new ResolutionError({\n kind: \"missing-block\",\n merkleRoot,\n message: `Block ${merkleRoot} not found in registry`,\n });\n }\n\n // Push onto the DFS path before recursing into children.\n state.path.add(merkleRoot);\n\n // Extract sub-block import paths from the stored impl source.\n const importPaths = extractSubBlockImports(row.implSource);\n const subBlockRoots: BlockMerkleRoot[] = [];\n\n for (const importedFrom of importPaths) {\n const subRoot = await state.subBlockResolver(importedFrom);\n if (subRoot === null) continue; // resolver returned null → skip\n\n subBlockRoots.push(subRoot);\n await visitBlock(subRoot, state);\n }\n\n // Pop from DFS path.\n state.path.delete(merkleRoot);\n\n // Post-order: record this block after all its children are recorded.\n state.blocks.set(merkleRoot, {\n merkleRoot,\n specHash: row.specHash,\n source: row.implSource,\n subBlocks: subBlockRoots,\n });\n state.order.push(merkleRoot);\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Traverse the composition graph starting from `entry` and resolve the transitive\n * closure of all referenced blocks from the registry.\n *\n * `subBlockResolver` maps sub-block import paths to BlockMerkleRoots. Return null\n * from the resolver to skip a reference (silently). Throw from the resolver to\n * propagate a hard lookup failure.\n *\n * @throws ResolutionError kind \"missing-block\" — block not found in registry.\n * @throws ResolutionError kind \"cycle\" — directed cycle in composition graph.\n */\nexport async function resolveComposition(\n entry: BlockMerkleRoot,\n registry: Registry,\n subBlockResolver: SubBlockResolver,\n): Promise<ResolutionResult> {\n const state: DfsState = {\n registry,\n subBlockResolver,\n blocks: new Map(),\n order: [],\n path: new Set(),\n };\n\n await visitBlock(entry, state);\n\n return {\n entry,\n blocks: state.blocks,\n order: state.order,\n };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-WI508-IMPORT-GATE-001\n// title: compile-time import gate -- UnexpandedImportError for covered foreign imports\n// status: decided (WI-508-IMPORT-INTERCEPT Slice 1)\n// rationale:\n// assertNoUnexpandedImports() is the compile-time enforcement layer complementing\n// the runtime import-intercept hook in @yakcc/hooks-base.\n// Classification mirrors import-intercept.ts. Any divergence is a bug.\n\nimport {\n BARE_NODE_CORE_MODULES,\n NODE_BUILTIN_PREFIX,\n WORKSPACE_PREFIX,\n extractBareName,\n} from \"@yakcc/hooks-base/src/import-classifier.js\";\nimport { Project, ScriptKind } from \"ts-morph\";\n\nexport const GATE_INTERCEPT_ALLOWLIST = new Set([\"validator\"]);\n\n/**\n * Thrown by assertNoUnexpandedImports() when a covered foreign import is present.\n * Mirrors GlueLeafInWasmModeError from slice-plan.ts.\n * @decision DEC-WI508-IMPORT-GATE-001\n */\nexport class UnexpandedImportError extends Error {\n readonly moduleSpecifier: string;\n readonly namedImports: readonly string[];\n\n constructor(moduleSpecifier: string, namedImports: readonly string[]) {\n const namedStr =\n namedImports.length > 0\n ? ` (${namedImports.slice(0, 3).join(\", \")}${namedImports.length > 3 ? \", ...\" : \"\"})`\n : \"\";\n super(\n `Unexpanded covered import: \"${moduleSpecifier}\"${namedStr} is on the yakcc intercept allowlist but was not resolved through the registry.`,\n );\n this.name = \"UnexpandedImportError\";\n this.moduleSpecifier = moduleSpecifier;\n this.namedImports = namedImports;\n }\n}\n\nfunction collectCoveredImports(\n source: string,\n): Array<{ moduleSpecifier: string; namedImports: readonly string[] }> {\n const project = new Project({\n useInMemoryFileSystem: true,\n compilerOptions: { allowJs: true },\n });\n\n const sourceFile = project.createSourceFile(\"__gate__.ts\", source, {\n scriptKind: ScriptKind.TSX,\n });\n\n const found: Array<{ moduleSpecifier: string; namedImports: readonly string[] }> = [];\n\n for (const decl of sourceFile.getImportDeclarations()) {\n if (decl.isTypeOnly()) continue;\n const spec = decl.getModuleSpecifierValue();\n if (spec.startsWith(\".\")) continue;\n if (spec.startsWith(NODE_BUILTIN_PREFIX)) continue;\n if (spec.startsWith(WORKSPACE_PREFIX)) continue;\n const bareName = extractBareName(spec);\n if (BARE_NODE_CORE_MODULES.has(bareName)) continue;\n if (!GATE_INTERCEPT_ALLOWLIST.has(bareName)) continue;\n // Filter out inline type-only specifiers (e.g. \"type T\" in \"import { type T, isEmail }\").\n // Mirrors the isTypeOnly() filter in import-intercept.ts (DEC-WI508-INTERCEPT-CLASSIFIER-SHARED-001).\n const namedImports = decl\n .getNamedImports()\n .filter((ni) => !ni.isTypeOnly())\n .map((ni) => ni.getName());\n found.push({ moduleSpecifier: spec, namedImports });\n }\n return found;\n}\n\nexport interface AssertNoUnexpandedImportsOptions {\n readonly disabled?: boolean;\n}\n\n/**\n * Assert that the source string contains no unexpanded covered imports.\n * Throws UnexpandedImportError on the first covered import found.\n * @throws UnexpandedImportError\n * @decision DEC-WI508-IMPORT-GATE-001\n */\nexport function assertNoUnexpandedImports(\n source: string,\n options?: AssertNoUnexpandedImportsOptions,\n): void {\n if (options?.disabled === true) return;\n const covered = collectCoveredImports(source);\n if (covered.length === 0) return;\n const first = covered[0];\n if (first === undefined) return;\n throw new UnexpandedImportError(first.moduleSpecifier, first.namedImports);\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-SEEDS-LOADER-T05-001: seedRegistry enumerates block directories.\n// Status: implemented (WI-T05)\n// Rationale: WI-T05 migrates each block from a single .ts file with an embedded\n// CONTRACT literal to a directory triplet (spec.yak, impl.ts, proof/). The\n// BLOCK_FILES hand-maintained list is replaced by directory enumeration via\n// readdirSync (Sacred Practice #12 — no parallel mechanism). Each directory is\n// parsed via parseBlockTriplet from @yakcc/ir, which validates spec.yak, runs\n// the strict-subset validator on impl.ts, and derives the BlockMerkleRoot.\n// The result is stored via registry.storeBlock(row: BlockTripletRow).\n//\n// @decision DEC-SEEDS-STOREBLOCK-T05-002: seedRegistry builds BlockTripletRow\n// from BlockTripletParseResult for storeBlock.\n// Status: implemented (WI-T05)\n// Rationale: storeBlock requires specCanonicalBytes (BLAKE3(canonicalize(spec))),\n// specHash (derived by parseBlockTriplet as specHashValue), implSource, and\n// proofManifestJson. All are available from BlockTripletParseResult. The\n// canonicalize() function from @yakcc/contracts is used to compute the canonical\n// bytes; this matches how the registry's storeBlock() internally verifies integrity.\n// createdAt is set to 0 so the registry uses Date.now() (DEC-STORAGE-IDEMPOTENT-001).\n\nimport { readdirSync, statSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n type BlockMerkleRoot,\n type CanonicalAstHash,\n type SpecHash,\n canonicalAstHash,\n canonicalize,\n} from \"@yakcc/contracts\";\nimport { parseBlockTriplet } from \"@yakcc/ir\";\nimport type { BlockTripletRow, Registry } from \"@yakcc/registry\";\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport interface SeedResult {\n readonly stored: number;\n readonly merkleRoots: ReadonlyArray<BlockMerkleRoot>;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Seed a Registry with all blocks in the seed corpus.\n *\n * Enumerates directories under packages/seeds/src/blocks/, calls\n * parseBlockTriplet on each, then stores the result via registry.storeBlock.\n *\n * For each block directory:\n * 1. parseBlockTriplet reads spec.yak, impl.ts, proof/manifest.json.\n * 2. Validates spec.yak via validateSpecYak (throws TypeError on failure).\n * 3. Runs the strict-subset validator on impl.ts (result.validation.ok).\n * 4. Builds a BlockTripletRow with blockMerkleRoot, specHash, specCanonicalBytes,\n * implSource, proofManifestJson, level, and createdAt=0.\n * 5. Calls registry.storeBlock(row) — idempotent (INSERT OR IGNORE).\n *\n * Returns stored count and the list of BlockMerkleRoots in directory order.\n *\n * @throws Error if any block directory fails spec validation, strict-subset\n * validation, or has a missing/malformed proof/manifest.json.\n */\nexport async function seedRegistry(registry: Registry): Promise<SeedResult> {\n const blocksDir = join(dirname(fileURLToPath(import.meta.url)), \"blocks\");\n\n // Enumerate block directories (directories only, sorted for determinism)\n const entries = readdirSync(blocksDir, { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => e.name)\n .sort();\n\n const merkleRoots: BlockMerkleRoot[] = [];\n let stored = 0;\n\n for (const name of entries) {\n const blockDir = join(blocksDir, name);\n\n // Parse the triplet (reads spec.yak, impl.ts, proof/manifest.json, artifact bytes).\n // No extra blockPatterns needed: the @yakcc/ir isBlockImport builtin already\n // recognises \"@yakcc/seeds/\" as a block-import prefix, and composition imports\n // in impl.ts files use \"@yakcc/seeds/blocks/<name>\" (WI-T05-fix).\n const result = parseBlockTriplet(blockDir);\n\n if (!result.validation.ok) {\n const msgs = result.validation.errors.map((e) => `${e.rule}: ${e.message}`).join(\"; \");\n throw new Error(`Block ${name} failed strict-subset validation: ${msgs}`);\n }\n\n // Build specCanonicalBytes: BLAKE3(canonicalize(spec.yak)) — matches what\n // storeBlock uses internally for embedding and integrity verification.\n const specCanonicalBytes = canonicalize(\n result.spec as unknown as Parameters<typeof canonicalize>[0],\n );\n\n const row: BlockTripletRow = {\n blockMerkleRoot: result.merkleRoot,\n specHash: result.specHashValue as SpecHash,\n specCanonicalBytes,\n implSource: result.implSource,\n proofManifestJson: JSON.stringify(result.manifest),\n level: result.spec.level,\n // createdAt=0 signals the registry to use Date.now() (DEC-STORAGE-IDEMPOTENT-001)\n createdAt: 0,\n // canonicalAstHash is the content-address of the implementation AST,\n // used for cross-spec reuse detection (DEC-REGISTRY-CANONICAL-AST-HASH-001).\n canonicalAstHash: canonicalAstHash(result.implSource) as CanonicalAstHash,\n // artifact bytes parsed from proof/ by parseBlockTriplet — required by\n // BlockTripletRow (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002).\n artifacts: result.artifacts,\n };\n\n await registry.storeBlock(row);\n merkleRoots.push(result.merkleRoot);\n stored++;\n }\n\n return { stored, merkleRoots };\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CLI-REGISTRY-INIT-001: registry-init creates parent dirs with mkdirSync\n// and delegates to openRegistry() from @yakcc/registry. Idempotent: opening a DB that\n// already exists at the current schema_version is a no-op (schema migrations run only\n// on version mismatch). The command prints a deterministic message in both cases.\n// Status: implemented (WI-007)\n// Rationale: openRegistry() calls applyMigrations(), which is idempotent. A second call\n// on an already-initialized file returns normally. There is no separate \"check if exists\"\n// path — we just open and close, and the migration mechanism gives us idempotency for free.\n\nimport { mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { type Registry, openRegistry } from \"@yakcc/registry\";\nimport type { Logger } from \"../index.js\";\n\n/** Default registry path, relative to cwd. */\nexport const DEFAULT_REGISTRY_PATH = \".yakcc/registry.sqlite\";\n\n/**\n * Handler for `yakcc registry init [--path <path>]`.\n *\n * Creates the parent directory if missing, then opens (or creates) the SQLite\n * registry. Prints a deterministic message and exits 0.\n *\n * @param argv - Remaining argv after `registry init` has been consumed.\n * @param logger - Output sink; defaults to console via the caller.\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function registryInit(argv: readonly string[], logger: Logger): Promise<number> {\n const { values } = parseArgs({\n args: [...argv],\n options: {\n path: { type: \"string\", short: \"p\" },\n },\n allowPositionals: false,\n strict: true,\n });\n\n const registryPath = values.path ?? DEFAULT_REGISTRY_PATH;\n\n // Ensure parent directory exists.\n const parent = dirname(registryPath);\n mkdirSync(parent, { recursive: true });\n\n // Open (or create) the registry — applyMigrations() inside is idempotent.\n let registry: Registry;\n try {\n registry = await openRegistry(registryPath);\n } catch (err) {\n logger.error(`error: failed to open registry at ${registryPath}: ${String(err)}`);\n return 1;\n }\n\n await registry.close();\n\n logger.log(`registry initialized at ${registryPath}`);\n return 0;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CLI-INDEX-001: runCli() is the single public entry point for the yakcc\n// CLI. It dispatches on argv[0] (command) and argv[1] (subcommand for multi-word\n// commands like \"registry init\"). Each command handler is a real function calling into\n// the workspace packages — no stubs, no shelling out to yakcc sub-processes.\n// Uses node:util.parseArgs — no external argparse dependency (DEC-V0-CLI-004).\n// Status: implemented (WI-007)\n// Rationale: Thin dispatch layer that stays out of the way of each command's own\n// argument parsing. The split between index.ts (routing) and commands/*.ts (logic)\n// keeps each command independently testable and replaceable.\n//\n// @decision DEC-CLI-LOGGER-001: Commands accept an optional Logger parameter rather\n// than calling console.log/error directly. The production default is CONSOLE_LOGGER\n// which delegates to the real console. Tests pass a CollectingLogger that records\n// output in a plain array — no mocks, no spies needed.\n// Status: implemented (WI-007)\n// Rationale: Enables output verification in integration tests without mocking internal\n// code (Sacred Practice #5). The Logger interface is minimal: log() for stdout-level\n// messages, error() for stderr-level messages. All command signatures remain stable;\n// the logger is an optional final parameter defaulting to CONSOLE_LOGGER.\n//\n// @decision DEC-CI-OFFLINE-006: Top-level CliOptions.embeddings on runCli(argv, logger,\n// opts?) is the canonical injection seam for embedding providers from any runCli test\n// caller. Per-command *Options interfaces in commands/*.ts remain accepted as bounded\n// backward-compatibility seams but new tests SHOULD use the top-level runCli form.\n// The CliOptions.embeddings TYPE is sourced from RegistryOptions[\"embeddings\"] so there\n// is exactly one canonical type definition for the embeddings option shape across the\n// whole CLI surface.\n// Status: implemented (WI-CI-OFFLINE-03)\n// Rationale: Sacred Practice #12 (single source of truth). WI-CI-OFFLINE-01 made\n// offline embedding injection work but only via per-command imports, bypassing runCli.\n// Lifting CliOptions.embeddings to runCli restores symmetry: production callers use\n// the two-arg form; embedding-injecting tests use the three-arg form. Type-aliasing\n// RegistryOptions[\"embeddings\"] keeps @yakcc/registry as the one type-level authority.\n\nimport type { RegistryOptions } from \"@yakcc/registry\";\nimport { bootstrap } from \"./commands/bootstrap.js\";\nimport { compileSelf } from \"./commands/compile-self.js\";\nimport { compile } from \"./commands/compile.js\";\nimport { runFederation } from \"./commands/federation.js\";\nimport { hooksAiderInstall } from \"./commands/hooks-aider-install.js\";\nimport { hooksCursorInstall } from \"./commands/hooks-cursor-install.js\";\nimport { hooksClaudeCodeInstall } from \"./commands/hooks-install.js\";\nimport { hooksWindsurfInstall } from \"./commands/hooks-windsurf-install.js\";\nimport { init } from \"./commands/init.js\";\nimport { propose } from \"./commands/propose.js\";\nimport { query } from \"./commands/query.js\";\nimport { registryExport } from \"./commands/registry-export.js\";\nimport { registryInit } from \"./commands/registry-init.js\";\nimport { registryRebuild } from \"./commands/registry-rebuild.js\";\nimport { search } from \"./commands/search.js\";\nimport { seed } from \"./commands/seed.js\";\nimport { shave } from \"./commands/shave.js\";\nimport { uninstall } from \"./commands/uninstall.js\";\n\n// Re-export ContractId for callers who import from @yakcc/cli.\nexport type { ContractId } from \"@yakcc/contracts\";\n\n// ---------------------------------------------------------------------------\n// CliOptions interface\n// ---------------------------------------------------------------------------\n\n/**\n * Top-level options for runCli.\n *\n * Tests inject createOfflineEmbeddingProvider() via opts.embeddings so no\n * network I/O occurs. Production callers omit this parameter — the per-command\n * defaults (getDefaultProvider() lazy singleton) take effect unchanged.\n *\n * Only the four arms that already accept embeddings are threaded: compile,\n * search, seed, and federation. Other arms (registry init, propose, query,\n * bootstrap, shave, hooks) are out of scope per DEC-CI-OFFLINE-006.\n */\nexport interface CliOptions {\n /** Embedding provider forwarded to commands that open a registry. */\n embeddings?: RegistryOptions[\"embeddings\"];\n}\n\n// ---------------------------------------------------------------------------\n// Logger interface\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal output interface for CLI commands.\n *\n * The production default (CONSOLE_LOGGER) delegates to console.log/console.error.\n * Tests inject a CollectingLogger to capture lines without mocking.\n */\nexport interface Logger {\n log(message: string): void;\n error(message: string): void;\n}\n\n/** Production logger — delegates to the real process console. */\nexport const CONSOLE_LOGGER: Logger = {\n log: (message: string) => {\n console.log(message);\n },\n error: (message: string) => {\n console.error(message);\n },\n};\n\n/**\n * In-memory logger for integration tests.\n * Collects all log/error lines in plain arrays — no mocking required.\n */\nexport class CollectingLogger implements Logger {\n readonly logLines: string[] = [];\n readonly errLines: string[] = [];\n\n log(message: string): void {\n this.logLines.push(message);\n }\n\n error(message: string): void {\n this.errLines.push(message);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Usage text\n// ---------------------------------------------------------------------------\n\nfunction printUsage(logger: Logger): void {\n logger.log(`yakcc — content-addressed basic-block registry\n\nUSAGE\n yakcc <command> [options]\n\nCOMMANDS\n init [--target <dir>] [--peer <url>] Initialize yakcc in a project directory\n [--local] [--airgapped] Mode: local (default) or airgapped (no peer)\n [--skip-hooks] Skip IDE hook auto-install\n [--ide <claude-code|cursor| Explicit IDE list (skip auto-detect)\n cline|continue,...>]\n [--no-seed] Skip bootstrap corpus seed\n uninstall [--target <dir>] Remove yakcc IDE hooks (preserves data)\n [--purge] Also remove .yakcc/ and .yakccrc.json\n [--ide <claude-code| Target specific IDEs only (default: all installed)\n cursor|cline|\n continue,...>]\n registry init [--path <p>] Initialize a registry (default: .yakcc/registry.sqlite)\n registry rebuild [--path <p>] Re-embed all blocks after an embedding model swap\n registry export --to <p> Export registry as canonical SQLite (VACUUM INTO)\n compile <entry> [--registry <p>] Assemble a module from a contract id, spec file, or directory\n [--out <dir>] Output directory (default: ./yakcc-out or <dir>/dist)\n propose <contract-file> Check registry for a matching contract\n [--registry <p>]\n query <text> [--registry <p>] Vector-search registry by semantic intent\n [--top <k>] [--rerank] Max results (default: 10); --rerank adds structural score\n [--card-file <f>] JSON IntentCard/IntentQuery file (alternative to free text)\n search <query> [--registry <p>] Search registry by spec file or free text (structural)\n [--top <k>] Max results (default: 10)\n seed [--registry <p>] Ingest the seed corpus into the registry\n compile-self [--output <dir>] Recompile yakcc corpus atoms to TS (A2 implemented; A3=byte-eq stretch)\n [--registry <p>] Registry path (default: bootstrap/yakcc.registry.sqlite)\n bootstrap [--registry <p>] Shave all source files, write manifest + report\n [--manifest <p>] Manifest path (default: bootstrap/expected-roots.json)\n [--report <p>] Per-file report (default: bootstrap/report.json)\n shave <path> [--registry <p>] Shave a TS source file into atoms via universalize\n [--offline]\n hooks claude-code install Wire yakcc tool-call interception for Claude Code\n [--target <dir>] Target project directory (default: .)\n [--uninstall] Remove the yakcc hook entry\n hooks cursor install Wire yakcc tool-call interception for Cursor\n [--target <dir>] Target project directory (default: .)\n [--uninstall] Remove the yakcc cursor hook entry\n federation serve --registry <p> Start a read-only HTTP registry server\n [--port <n>] [--host <h>]\n federation mirror --remote <url> Mirror all blocks from a remote registry peer\n --registry <p>\n federation pull --remote <url> Pull a single block triplet from a remote peer\n --root <merkleRoot> --registry <p>\n\nFLAGS\n --help, -h Print this help and exit\n\nEXIT CODES\n 0 success\n 1 usage or runtime error\n`);\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Run the yakcc CLI with the given argument vector.\n *\n * Dispatches on the first positional token (command) and, for multi-word\n * commands, the second token (subcommand). Each command handler calls into\n * the real workspace packages and returns an exit code.\n *\n * @param argv - Arguments after the binary name (i.e. process.argv.slice(2)).\n * @param logger - Output sink; defaults to CONSOLE_LOGGER (the real console).\n * @param opts - Optional top-level CLI options (DEC-CI-OFFLINE-006). Tests\n * inject createOfflineEmbeddingProvider() here; production callers omit it.\n * @returns Promise<number> — 0 on success, non-zero on error.\n */\nexport async function runCli(\n argv: ReadonlyArray<string>,\n logger: Logger = CONSOLE_LOGGER,\n opts?: CliOptions,\n): Promise<number> {\n const [command, subcommand, ...rest] = argv;\n\n switch (command) {\n case \"init\": {\n // `yakcc init [--target <dir>] [--peer <url>]`\n const initArgv = subcommand !== undefined ? [subcommand, ...rest] : rest;\n return init(initArgv, logger);\n }\n\n case \"uninstall\": {\n // `yakcc uninstall [--target <dir>] [--purge] [--ide <list>]`\n // Symmetric off-switch for `yakcc init` (DEC-CLI-UNINSTALL-COMMAND-001).\n // subcommand may be a flag like --target or --purge — reassemble into argv.\n const uninstallArgv = subcommand !== undefined ? [subcommand, ...rest] : rest;\n return uninstall(uninstallArgv, logger);\n }\n\n case \"registry\": {\n if (subcommand === \"init\") {\n return registryInit(rest, logger);\n }\n if (subcommand === \"rebuild\") {\n // @decision DEC-EMBED-MODEL-MIGRATION-001\n // `yakcc registry rebuild [--path <p>]` — re-embeds all stored blocks using the\n // current default embedding provider. Canonical migration path after a model swap\n // (DEC-EMBED-MODEL-DEFAULT-002, PR #336). See rebuild.ts and registry-rebuild.ts.\n return registryRebuild(rest, logger, { embeddings: opts?.embeddings });\n }\n if (subcommand === \"export\") {\n // @decision DEC-CLI-REGISTRY-EXPORT-001\n // `yakcc registry export --to <path> [--from <path>]` — emits a clean\n // VACUUM-INTO copy of the source registry for federation publishing (#371).\n return registryExport(rest, logger);\n }\n logger.error(\n `error: unknown registry subcommand: ${subcommand ?? \"(none)\"}. Did you mean 'registry init', 'registry rebuild', or 'registry export'?`,\n );\n return 1;\n }\n\n case \"compile\": {\n // subcommand is the first positional for compile (the entry arg).\n const compileArgv = subcommand !== undefined ? [subcommand, ...rest] : rest;\n return compile(compileArgv, logger, { embeddings: opts?.embeddings });\n }\n\n case \"compile-self\": {\n // `yakcc compile-self` — A1 scaffold stub (DEC-V2-COMPILE-SELF-CLI-NAMING-001).\n // subcommand and rest are unused in A1 (the stub ignores all args).\n const compileSelfArgv = subcommand !== undefined ? [subcommand, ...rest] : rest;\n return compileSelf(compileSelfArgv, logger);\n }\n\n case \"propose\": {\n const proposeArgv = subcommand !== undefined ? [subcommand, ...rest] : rest;\n return propose(proposeArgv, logger);\n }\n\n case \"query\": {\n const queryArgv = subcommand !== undefined ? [subcommand, ...rest] : rest;\n return query(queryArgv, logger, { embeddings: opts?.embeddings });\n }\n\n case \"search\": {\n const searchArgv = subcommand !== undefined ? [subcommand, ...rest] : rest;\n return search(searchArgv, logger, { embeddings: opts?.embeddings });\n }\n\n case \"seed\": {\n // seed has no positional; subcommand may be a flag like --registry.\n const seedArgv = subcommand !== undefined ? [subcommand, ...rest] : rest;\n return seed(seedArgv, logger, { embeddings: opts?.embeddings });\n }\n\n case \"bootstrap\": {\n // bootstrap has no positional; subcommand may be a flag like --registry.\n const bootstrapArgv = subcommand !== undefined ? [subcommand, ...rest] : rest;\n return bootstrap(bootstrapArgv, logger);\n }\n\n case \"shave\": {\n const shaveArgv = subcommand !== undefined ? [subcommand, ...rest] : rest;\n return shave(shaveArgv, logger);\n }\n\n case \"federation\": {\n // Reassemble remaining args: subcommand (the federation verb) + rest.\n const fedArgv = subcommand !== undefined ? [subcommand, ...rest] : rest;\n return runFederation(fedArgv, logger, { embeddings: opts?.embeddings });\n }\n\n case \"hooks\": {\n // `yakcc hooks claude-code install [--target <dir>]`\n if (subcommand === \"claude-code\") {\n const [hooksSub, ...hooksRest] = rest;\n if (hooksSub === \"install\") {\n return hooksClaudeCodeInstall(hooksRest, logger);\n }\n logger.error(\n `error: unknown hooks claude-code subcommand: ${hooksSub ?? \"(none)\"}. Did you mean 'hooks claude-code install'?`,\n );\n return 1;\n }\n // `yakcc hooks cursor install [--target <dir>]`\n if (subcommand === \"cursor\") {\n const [hooksSub, ...hooksRest] = rest;\n if (hooksSub === \"install\") {\n return hooksCursorInstall(hooksRest, logger);\n }\n logger.error(\n `error: unknown hooks cursor subcommand: ${hooksSub ?? \"(none)\"}. Did you mean 'hooks cursor install'?`,\n );\n return 1;\n }\n // `yakcc hooks windsurf install [--target <dir>]`\n if (subcommand === \"windsurf\") {\n const [hooksSub, ...hooksRest] = rest;\n if (hooksSub === \"install\") {\n return hooksWindsurfInstall(hooksRest, logger);\n }\n logger.error(\n `error: unknown hooks windsurf subcommand: ${hooksSub ?? \"(none)\"}. Did you mean 'hooks windsurf install'?`,\n );\n return 1;\n }\n // `yakcc hooks aider install`\n if (subcommand === \"aider\") {\n const [hooksSub, ...hooksRest] = rest;\n if (hooksSub === \"install\") {\n return hooksAiderInstall(hooksRest, logger);\n }\n logger.error(\n `error: unknown hooks aider subcommand: ${hooksSub ?? \"(none)\"}. Did you mean 'hooks aider install'?`,\n );\n return 1;\n }\n logger.error(\n `error: unknown hooks subcommand: ${subcommand ?? \"(none)\"}. Did you mean 'hooks claude-code install', 'hooks cursor install', 'hooks windsurf install', or 'hooks aider install'?`,\n );\n return 1;\n }\n\n case undefined:\n case \"--help\":\n case \"-h\": {\n printUsage(logger);\n return 0;\n }\n\n default: {\n logger.error(`error: unknown command: ${command}`);\n logger.error(\"Run 'yakcc --help' for usage.\");\n return 1;\n }\n }\n}\n","// SPDX-License-Identifier: MIT\n//\n// hooks-aider-install.ts — marker-file installer for Aider CLI tool\n//\n// @decision DEC-CLI-HOOKS-AIDER-INSTALL-001\n// title: hooks-aider-install writes a marker file ~/.aider/yakcc-aider-hook.json;\n// no live --lint-cmd / --test-cmd wiring yet (YAML mutation deferred)\n// status: accepted (wi-687-s4-aider-adapter)\n// rationale:\n// Aider (https://aider.chat) is a CLI tool that exposes hook surfaces via\n// --lint-cmd and --test-cmd flags on .aider.conf.yml. Direct YAML mutation\n// introduces byte-identity round-trip risk (key ordering, comments, anchors);\n// the marker-file pattern mirrors the cline/continue approach:\n//\n// (A) Creates ~/.aider/ if absent (Aider auto-creates this on first run\n// to store chat history and cache).\n// (B) Writes ~/.aider/yakcc-aider-hook.json — a structured marker that\n// records the hook command, description, session env var, installation\n// timestamp, and an explicit note about the YAML deferral. This marker\n// is the primary machine-readable artefact for future tooling that\n// activates the hook once the byte-identity contract is designed.\n//\n// The install command is IDEMPOTENT: if the marker already exists (identified\n// by the _yakcc sentinel field), it logs a \"already installed\" message and\n// exits 0 without re-writing. This matches the cline installer pattern.\n//\n// No .aider.conf.yml wiring is performed in this slice. When the byte-identity\n// round-trip contract is established, a follow-up WI can activate real hook\n// wiring without requiring users to reinstall — the marker file already\n// documents the intent.\n//\n// The aider config directory is determined by the overrideAiderDir parameter\n// (for testing) or defaults to ~/.aider/ (primary probe per\n// DEC-CLI-IDE-DETECT-SEMANTICS-001).\n\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { Logger } from \"../index.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Marker filename written to the Aider config dir by the install command. */\nconst AIDER_HOOK_MARKER_FILENAME = \"yakcc-aider-hook.json\";\n\n/** Sentinel field that identifies a yakcc-installed Aider marker. */\nconst YAKCC_AIDER_MARKER = \"yakcc-hook-v1-aider\";\n\n/** Subprocess command to be invoked for Aider tool-call interception (future). */\nconst HOOK_COMMAND = \"yakcc hook-intercept\";\n\n// ---------------------------------------------------------------------------\n// Marker helpers\n// ---------------------------------------------------------------------------\n\ninterface AiderMarker {\n _yakcc: string;\n [key: string]: unknown;\n}\n\nfunction readMarker(markerPath: string): AiderMarker | null {\n if (!existsSync(markerPath)) return null;\n try {\n return JSON.parse(readFileSync(markerPath, \"utf-8\")) as AiderMarker;\n } catch {\n return null;\n }\n}\n\nfunction isYakccInstalled(markerPath: string): boolean {\n const marker = readMarker(markerPath);\n return marker !== null && marker._yakcc === YAKCC_AIDER_MARKER;\n}\n\n// ---------------------------------------------------------------------------\n// Command handler\n// ---------------------------------------------------------------------------\n\n/**\n * Handler for the Aider hook installer.\n *\n * Called by `yakcc init` when Aider is detected (DEC-CLI-IDE-INSTALLER-DISPATCH-001).\n * Can also be invoked standalone (e.g., tests, `yakcc hooks aider install` verb).\n *\n * Install: creates ~/.aider/ (or the override dir) and writes a yakcc marker\n * file documenting the intended hook wiring. Idempotent.\n * Uninstall: removes the yakcc marker file.\n *\n * @param argv - Remaining argv. Accepts `--target <dir>` and `--uninstall`.\n * @param logger - Output sink.\n * @param overrideAiderDir - Optional absolute path to use instead of ~/.aider/.\n * Used by tests to avoid writing to the real home directory.\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function hooksAiderInstall(\n argv: readonly string[],\n logger: Logger,\n overrideAiderDir?: string,\n): Promise<number> {\n let parsed: ReturnType<\n typeof parseArgs<{\n options: {\n target: { type: \"string\"; short: \"t\" };\n uninstall: { type: \"boolean\" };\n };\n }>\n >;\n\n try {\n parsed = parseArgs({\n args: [...argv],\n options: {\n target: { type: \"string\", short: \"t\" },\n uninstall: { type: \"boolean\" },\n },\n allowPositionals: false,\n strict: true,\n });\n } catch (err) {\n logger.error(`error: ${(err as Error).message}`);\n return 1;\n }\n\n // Resolve the Aider config directory.\n // overrideAiderDir is the injection seam for tests; production uses ~/.aider/.\n const aiderDir = overrideAiderDir ?? join(homedir(), \".aider\");\n const markerPath = join(aiderDir, AIDER_HOOK_MARKER_FILENAME);\n\n try {\n mkdirSync(aiderDir, { recursive: true });\n } catch (err) {\n logger.error(`error: cannot create ${aiderDir}: ${String(err)}`);\n return 1;\n }\n\n // --- Uninstall path ---\n if (parsed.values.uninstall) {\n if (!isYakccInstalled(markerPath)) {\n logger.log(\"yakcc aider hook not installed — nothing to uninstall.\");\n return 0;\n }\n try {\n rmSync(markerPath);\n } catch (err) {\n logger.error(`error: cannot remove ${markerPath}: ${String(err)}`);\n return 1;\n }\n logger.log(`yakcc aider hook marker removed: ${markerPath}`);\n return 0;\n }\n\n // --- Install path ---\n if (isYakccInstalled(markerPath)) {\n logger.log(`yakcc aider hook already installed at ${markerPath} (idempotent).`);\n return 0;\n }\n\n const marker: AiderMarker = {\n command: HOOK_COMMAND,\n description: \"yakcc tool-call interception hook for Aider\",\n sessionEnvVar: \"AIDER_SESSION_ID\",\n telemetryPrefix: \"aider\",\n installedAt: new Date().toISOString(),\n _yakcc: YAKCC_AIDER_MARKER,\n note:\n \"Aider (https://aider.chat) is a CLI tool that exposes hook surfaces via \" +\n \"`--lint-cmd` and `--test-cmd` flags on `.aider.conf.yml`. This marker \" +\n \"documents the intended wiring (DEC-CLI-HOOKS-AIDER-INSTALL-001). Direct \" +\n \"YAML mutation of `.aider.conf.yml` is deferred to a follow-up WI to preserve \" +\n \"byte-identity round-trip. When the wiring is activated, hook activation \" +\n \"requires no reinstall.\",\n };\n\n try {\n writeFileSync(markerPath, `${JSON.stringify(marker, null, 2)}\\n`, \"utf-8\");\n } catch (err) {\n logger.error(`error: cannot write marker ${markerPath}: ${String(err)}`);\n return 1;\n }\n\n logger.log(`yakcc aider hook marker installed: ${markerPath}`);\n logger.log(\n \"note: Aider hook wiring via --lint-cmd/--test-cmd deferred — see marker for details.\",\n );\n return 0;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CLI-HOOKS-CURSOR-INSTALL-001\n// title: yakcc hooks cursor install — write production hook wiring for Cursor\n// status: decided (WI-HOOK-PHASE-4-CURSOR, closes #219)\n// rationale:\n// Cursor's VS Code extension API does not expose a Node.js-callable hook\n// registration surface as of v1 (same constraint as hooks-claude-code before\n// the Claude Code settings.json hook surface stabilised). Two artefacts are\n// written to the target .cursor/ directory:\n//\n// (A) .cursor/settings.json — a yakcc \"PreToolUse\"-equivalent hook entry in\n// the VS Code settings format. Cursor inherits VS Code's extension host\n// model; a \"tasks\"-style entry records the hook intent. This is a stub\n// configuration that documents the intended wiring for when Cursor's\n// extension API exposes a synchronous tool-call interception surface.\n//\n// (B) .cursor/yakcc-cursor-hook.json — a structured marker file parallel to\n// hooks-claude-code's yakcc-slash-command.json. Records the hook command,\n// description, session-ID env var, and installation timestamp. This is\n// the primary machine-readable artefact consumed by the hooks-cursor\n// package's registerCommand() stub.\n//\n// Risk: Cursor surface stability (issue #219 Risk section). If Cursor does not\n// expose synchronous tool-call interception, the latency budget (D-HOOK-3 ≤200ms)\n// cannot be enforced at the IDE level. The install command writes the wiring\n// and documents the limitation in the marker file; it does NOT weaken any budget.\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { Logger } from \"../index.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Marker filename written to .cursor/ by the install command. */\nconst CURSOR_HOOK_MARKER_FILENAME = \"yakcc-cursor-hook.json\";\n\n/** Marker placed in cursor settings so the installer can find and remove it. */\nconst YAKCC_CURSOR_MARKER = \"yakcc-hook-v1-cursor\";\n\n/** Subprocess command that should be invoked for cursor tool-call interception. */\nconst HOOK_COMMAND = \"yakcc hook-intercept\";\n\n// ---------------------------------------------------------------------------\n// Settings helpers for .cursor/settings.json\n// ---------------------------------------------------------------------------\n\ninterface CursorSettings {\n hooks?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nfunction readCursorSettings(settingsPath: string): CursorSettings {\n if (!existsSync(settingsPath)) return {};\n try {\n return JSON.parse(readFileSync(settingsPath, \"utf-8\")) as CursorSettings;\n } catch {\n return {};\n }\n}\n\nfunction writeCursorSettings(settingsPath: string, settings: CursorSettings): void {\n writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\\n`, \"utf-8\");\n}\n\nfunction isYakccInstalled(settings: CursorSettings): boolean {\n const hooks = settings.hooks;\n if (hooks == null || typeof hooks !== \"object\") return false;\n const yakccEntry = hooks.yakcc;\n if (yakccEntry == null || typeof yakccEntry !== \"object\") return false;\n return (yakccEntry as Record<string, unknown>)._yakcc === YAKCC_CURSOR_MARKER;\n}\n\nfunction applyInstall(settings: CursorSettings): {\n settings: CursorSettings;\n alreadyInstalled: boolean;\n} {\n if (isYakccInstalled(settings)) {\n return { settings, alreadyInstalled: true };\n }\n return {\n settings: {\n ...settings,\n hooks: {\n ...(settings.hooks ?? {}),\n yakcc: {\n command: HOOK_COMMAND,\n description: \"yakcc tool-call interception hook for Cursor\",\n sessionEnvVar: \"CURSOR_SESSION_ID\",\n _yakcc: YAKCC_CURSOR_MARKER,\n },\n },\n },\n alreadyInstalled: false,\n };\n}\n\nfunction applyUninstall(settings: CursorSettings): {\n settings: CursorSettings;\n wasInstalled: boolean;\n} {\n if (!isYakccInstalled(settings)) {\n return { settings, wasInstalled: false };\n }\n const { yakcc: _removed, ...remainingHooks } = (settings.hooks ?? {}) as Record<string, unknown>;\n const hasRemainingHooks = Object.keys(remainingHooks).length > 0;\n const newSettings: CursorSettings = hasRemainingHooks\n ? { ...settings, hooks: remainingHooks }\n : (({ hooks: _h, ...rest }) => rest)(settings as CursorSettings & { hooks: unknown });\n return { settings: newSettings, wasInstalled: true };\n}\n\n// ---------------------------------------------------------------------------\n// Command handler\n// ---------------------------------------------------------------------------\n\n/**\n * Handler for `yakcc hooks cursor install [--target <dir>] [--uninstall]`.\n *\n * Install: writes a yakcc hook entry to .cursor/settings.json (VS Code settings\n * format) and a marker file to .cursor/yakcc-cursor-hook.json.\n * Uninstall: removes the yakcc hook entry from .cursor/settings.json.\n * Both paths are idempotent.\n *\n * Limitation (DEC-CLI-HOOKS-CURSOR-INSTALL-001): Cursor does not yet expose\n * synchronous tool-call interception via a stable Node.js API. The install\n * command writes the intended wiring so that, when the API stabilises, the\n * hook subprocess can be activated without a reinstall. The marker file\n * documents this constraint for tooling and human readers.\n *\n * @param argv - Remaining argv after `hooks cursor install` has been consumed.\n * @param logger - Output sink.\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function hooksCursorInstall(argv: readonly string[], logger: Logger): Promise<number> {\n const { values } = parseArgs({\n args: [...argv],\n options: {\n target: { type: \"string\", short: \"t\" },\n uninstall: { type: \"boolean\" },\n },\n allowPositionals: false,\n strict: true,\n });\n\n const targetDir = values.target ?? \".\";\n const cursorDir = join(targetDir, \".cursor\");\n const settingsPath = join(cursorDir, \"settings.json\");\n const markerPath = join(cursorDir, CURSOR_HOOK_MARKER_FILENAME);\n\n try {\n mkdirSync(cursorDir, { recursive: true });\n } catch (err) {\n logger.error(`error: cannot create ${cursorDir}: ${String(err)}`);\n return 1;\n }\n\n // --- Uninstall path ---\n if (values.uninstall) {\n const { settings: updated, wasInstalled } = applyUninstall(readCursorSettings(settingsPath));\n if (!wasInstalled) {\n logger.log(\"yakcc cursor hook not installed — nothing to uninstall.\");\n return 0;\n }\n try {\n writeCursorSettings(settingsPath, updated);\n } catch (err) {\n logger.error(`error: cannot write ${settingsPath}: ${String(err)}`);\n return 1;\n }\n logger.log(`yakcc cursor hook removed from ${settingsPath}`);\n return 0;\n }\n\n // --- Install path ---\n const { settings: updated, alreadyInstalled } = applyInstall(readCursorSettings(settingsPath));\n try {\n writeCursorSettings(settingsPath, updated);\n } catch (err) {\n logger.error(`error: cannot write ${settingsPath}: ${String(err)}`);\n return 1;\n }\n\n // Write the structured hook marker file.\n try {\n writeFileSync(\n markerPath,\n `${JSON.stringify(\n {\n command: HOOK_COMMAND,\n description: \"yakcc tool-call interception hook for Cursor\",\n sessionEnvVar: \"CURSOR_SESSION_ID\",\n telemetryPrefix: \"cursor\",\n installedAt: new Date().toISOString(),\n note:\n \"Cursor does not yet expose synchronous tool-call interception via a stable Node.js API. \" +\n \"This marker documents the intended wiring (DEC-CLI-HOOKS-CURSOR-INSTALL-001). \" +\n \"When the Cursor extension API stabilises, hook activation requires no reinstall.\",\n },\n null,\n 2,\n )}\\n`,\n \"utf-8\",\n );\n } catch (err) {\n logger.error(`warning: cannot write marker file ${markerPath}: ${String(err)}`);\n // Non-fatal: the settings.json update succeeded.\n }\n\n if (alreadyInstalled) {\n logger.log(`yakcc cursor hook already installed at ${settingsPath} (idempotent).`);\n } else {\n logger.log(`yakcc cursor hook installed at ${settingsPath}`);\n logger.log(`hook command: ${HOOK_COMMAND}`);\n logger.log(\"session env var: CURSOR_SESSION_ID\");\n logger.log(`marker: ${markerPath}`);\n logger.log(\"note: Cursor tool-call interception API not yet stable — see marker for details.\");\n }\n return 0;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CLI-HOOKS-INSTALL-002\n// title: Replace v0 CLAUDE.md facade with real settings.json hook wiring\n// status: decided (WI-V05-CLI-INSTALL-RETIRE-FACADE #203)\n// rationale:\n// Per DEC-HOOK-LAYER-001 (docs/archive/developer/adr/hook-layer-architecture.md):\n// D-HOOK-1 → Claude Code is the first IDE target.\n// D-HOOK-2 → tool-call interception via settings.json PreToolUse hook entry\n// for Edit|Write|MultiEdit tool calls.\n// The install command writes the yakcc PreToolUse block to .claude/settings.json\n// (idempotent read-modify-write) and removes the v0 .claude/CLAUDE.md stub\n// (Sacred Practice #12 — no parallel mechanisms may coexist).\n// The hook subprocess (yakcc hook-intercept) is provided by WI-HOOK-PHASE-1-MVP (#216).\n// Uninstall reads settings.json and strips the yakcc-marked entry, then writes back.\n//\n// Supersedes DEC-CLI-HOOKS-INSTALL-001 (v0 CLAUDE.md stub; WI-009).\n\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { Logger } from \"../index.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Claude Code settings.json hook event for pre-tool-call interception (D-HOOK-2). */\nconst HOOK_EVENT = \"PreToolUse\";\n\n/** Tool names the yakcc hook intercepts (D-HOOK-2: Edit/Write/MultiEdit). */\nconst HOOK_MATCHER = \"Edit|Write|MultiEdit\";\n\n/** Subprocess command Claude Code spawns per intercepted tool call.\n * Implementation provided by WI-HOOK-PHASE-1-MVP (#216). */\nconst HOOK_COMMAND = \"yakcc hook-intercept\";\n\n/** Marker placed on the inner hook object so the installer can find and remove it. */\nconst YAKCC_MARKER = \"yakcc-hook-v1\";\n\n// ---------------------------------------------------------------------------\n// Types for the settings.json shape\n// ---------------------------------------------------------------------------\n\ninterface YakccHookObject {\n type: string;\n command: string;\n _yakcc: string;\n}\n\ninterface HookObject {\n type: string;\n command: string;\n _yakcc?: string | undefined;\n}\n\ninterface HookEntry {\n matcher: string;\n hooks: HookObject[];\n}\n\ninterface ClaudeSettings {\n hooks?: Record<string, HookEntry[]>;\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Settings helpers\n// ---------------------------------------------------------------------------\n\nfunction readSettings(settingsPath: string): ClaudeSettings {\n if (!existsSync(settingsPath)) return {};\n try {\n return JSON.parse(readFileSync(settingsPath, \"utf-8\")) as ClaudeSettings;\n } catch {\n // Corrupt or non-JSON settings — start fresh rather than aborting.\n return {};\n }\n}\n\nfunction writeSettings(settingsPath: string, settings: ClaudeSettings): void {\n writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\\n`, \"utf-8\");\n}\n\nfunction buildYakccHookObject(): YakccHookObject {\n return { type: \"command\", command: HOOK_COMMAND, _yakcc: YAKCC_MARKER };\n}\n\nfunction isYakccEntry(entry: HookEntry): boolean {\n return entry.hooks.some((h) => h._yakcc === YAKCC_MARKER);\n}\n\n// ---------------------------------------------------------------------------\n// Install / uninstall logic\n// ---------------------------------------------------------------------------\n\nfunction applyInstall(settings: ClaudeSettings): {\n settings: ClaudeSettings;\n alreadyInstalled: boolean;\n} {\n const hooks = settings.hooks ?? {};\n const eventHooks: HookEntry[] = hooks[HOOK_EVENT] ?? [];\n\n if (eventHooks.some(isYakccEntry)) {\n return { settings, alreadyInstalled: true };\n }\n\n const newEntry: HookEntry = { matcher: HOOK_MATCHER, hooks: [buildYakccHookObject()] };\n return {\n settings: {\n ...settings,\n hooks: { ...hooks, [HOOK_EVENT]: [...eventHooks, newEntry] },\n },\n alreadyInstalled: false,\n };\n}\n\nfunction applyUninstall(settings: ClaudeSettings): {\n settings: ClaudeSettings;\n wasInstalled: boolean;\n} {\n const hooks = settings.hooks ?? {};\n const eventHooks: HookEntry[] = hooks[HOOK_EVENT] ?? [];\n const filtered = eventHooks.filter((e) => !isYakccEntry(e));\n const wasInstalled = filtered.length < eventHooks.length;\n\n if (!wasInstalled) {\n return { settings, wasInstalled: false };\n }\n\n const newEventHooks: Record<string, HookEntry[]> = { ...hooks };\n if (filtered.length === 0) {\n delete newEventHooks[HOOK_EVENT];\n } else {\n newEventHooks[HOOK_EVENT] = filtered;\n }\n\n const hasRemainingHooks = Object.keys(newEventHooks).length > 0;\n const newSettings: ClaudeSettings = hasRemainingHooks\n ? { ...settings, hooks: newEventHooks }\n : (({ hooks: _h, ...rest }) => rest)(settings as ClaudeSettings & { hooks: unknown });\n\n return { settings: newSettings, wasInstalled: true };\n}\n\n// ---------------------------------------------------------------------------\n// Command handler\n// ---------------------------------------------------------------------------\n\n/**\n * Handler for `yakcc hooks claude-code install [--target <dir>] [--uninstall]`.\n *\n * Install: writes a yakcc PreToolUse hook entry to .claude/settings.json\n * and removes the v0 .claude/CLAUDE.md stub if present.\n * Uninstall: removes the yakcc hook entry from .claude/settings.json.\n * Both paths are idempotent.\n *\n * @param argv - Remaining argv after `hooks claude-code install` has been consumed.\n * @param logger - Output sink.\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function hooksClaudeCodeInstall(\n argv: readonly string[],\n logger: Logger,\n): Promise<number> {\n const { values } = parseArgs({\n args: [...argv],\n options: {\n target: { type: \"string\", short: \"t\" },\n uninstall: { type: \"boolean\" },\n },\n allowPositionals: false,\n strict: true,\n });\n\n const targetDir = values.target ?? \".\";\n const claudeDir = join(targetDir, \".claude\");\n const settingsPath = join(claudeDir, \"settings.json\");\n const claudeMdPath = join(claudeDir, \"CLAUDE.md\");\n\n try {\n mkdirSync(claudeDir, { recursive: true });\n } catch (err) {\n logger.error(`error: cannot create ${claudeDir}: ${String(err)}`);\n return 1;\n }\n\n // --- Uninstall path ---\n if (values.uninstall) {\n const { settings: updated, wasInstalled } = applyUninstall(readSettings(settingsPath));\n if (!wasInstalled) {\n logger.log(\"yakcc hook not installed — nothing to uninstall.\");\n return 0;\n }\n try {\n writeSettings(settingsPath, updated);\n } catch (err) {\n logger.error(`error: cannot write ${settingsPath}: ${String(err)}`);\n return 1;\n }\n logger.log(`yakcc hook removed from ${settingsPath}`);\n return 0;\n }\n\n // --- Install path ---\n const { settings: updated, alreadyInstalled } = applyInstall(readSettings(settingsPath));\n try {\n writeSettings(settingsPath, updated);\n } catch (err) {\n logger.error(`error: cannot write ${settingsPath}: ${String(err)}`);\n return 1;\n }\n\n // Remove the v0 CLAUDE.md facade stub (Sacred Practice #12 — no parallel mechanisms).\n if (existsSync(claudeMdPath)) {\n try {\n rmSync(claudeMdPath);\n } catch (err) {\n logger.error(`warning: cannot remove v0 stub ${claudeMdPath}: ${String(err)}`);\n // Non-fatal: the main install succeeded.\n }\n }\n\n if (alreadyInstalled) {\n logger.log(`yakcc hook already installed at ${settingsPath} (idempotent).`);\n } else {\n logger.log(`yakcc hook installed at ${settingsPath}`);\n logger.log(`tool-call interception: ${HOOK_MATCHER} → ${HOOK_COMMAND}`);\n }\n return 0;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CLI-HOOKS-WINDSURF-INSTALL-001\n// title: yakcc hooks windsurf install — write production hook wiring for Windsurf\n// status: decided (WI-687-S3, closes #687 S3)\n// rationale:\n// Windsurf (Codeium's AI IDE) uses a VS Code-derived extension host model similar\n// to Cursor. Two artefacts are written to the target .windsurf/ directory:\n//\n// (A) .windsurf/settings.json — a yakcc \"PreToolUse\"-equivalent hook entry in\n// the VS Code settings format. Windsurf inherits VS Code's extension host\n// model; a \"tasks\"-style entry records the hook intent. This is a stub\n// configuration that documents the intended wiring for when Windsurf's\n// extension API exposes a synchronous tool-call interception surface.\n//\n// (B) .windsurf/yakcc-windsurf-hook.json — a structured marker file parallel to\n// hooks-cursor's yakcc-cursor-hook.json. Records the hook command,\n// description, session-ID env var, and installation timestamp. This is\n// the primary machine-readable artefact consumed by the hooks-windsurf\n// package's registerCommand() stub.\n//\n// Risk: Windsurf surface stability (mirrors Cursor risk pattern from\n// DEC-CLI-HOOKS-CURSOR-INSTALL-001). If Windsurf does not expose synchronous\n// tool-call interception, the latency budget (D-HOOK-3 ≤200ms) cannot be\n// enforced at the IDE level. The install command writes the wiring and documents\n// the limitation in the marker file; it does NOT weaken any budget.\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { Logger } from \"../index.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Marker filename written to .windsurf/ by the install command. */\nconst WINDSURF_HOOK_MARKER_FILENAME = \"yakcc-windsurf-hook.json\";\n\n/** Marker placed in windsurf settings so the installer can find and remove it. */\nconst YAKCC_WINDSURF_MARKER = \"yakcc-hook-v1-windsurf\";\n\n/** Subprocess command that should be invoked for windsurf tool-call interception. */\nconst HOOK_COMMAND = \"yakcc hook-intercept\";\n\n// ---------------------------------------------------------------------------\n// Settings helpers for .windsurf/settings.json\n// ---------------------------------------------------------------------------\n\ninterface WindsurfSettings {\n hooks?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nfunction readWindsurfSettings(settingsPath: string): WindsurfSettings {\n if (!existsSync(settingsPath)) return {};\n try {\n return JSON.parse(readFileSync(settingsPath, \"utf-8\")) as WindsurfSettings;\n } catch {\n return {};\n }\n}\n\nfunction writeWindsurfSettings(settingsPath: string, settings: WindsurfSettings): void {\n writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\\n`, \"utf-8\");\n}\n\nfunction isYakccInstalled(settings: WindsurfSettings): boolean {\n const hooks = settings.hooks;\n if (hooks == null || typeof hooks !== \"object\") return false;\n const yakccEntry = hooks.yakcc;\n if (yakccEntry == null || typeof yakccEntry !== \"object\") return false;\n return (yakccEntry as Record<string, unknown>)._yakcc === YAKCC_WINDSURF_MARKER;\n}\n\nfunction applyInstall(settings: WindsurfSettings): {\n settings: WindsurfSettings;\n alreadyInstalled: boolean;\n} {\n if (isYakccInstalled(settings)) {\n return { settings, alreadyInstalled: true };\n }\n return {\n settings: {\n ...settings,\n hooks: {\n ...(settings.hooks ?? {}),\n yakcc: {\n command: HOOK_COMMAND,\n description: \"yakcc tool-call interception hook for Windsurf\",\n sessionEnvVar: \"WINDSURF_SESSION_ID\",\n _yakcc: YAKCC_WINDSURF_MARKER,\n },\n },\n },\n alreadyInstalled: false,\n };\n}\n\nfunction applyUninstall(settings: WindsurfSettings): {\n settings: WindsurfSettings;\n wasInstalled: boolean;\n} {\n if (!isYakccInstalled(settings)) {\n return { settings, wasInstalled: false };\n }\n const { yakcc: _removed, ...remainingHooks } = (settings.hooks ?? {}) as Record<string, unknown>;\n const hasRemainingHooks = Object.keys(remainingHooks).length > 0;\n const newSettings: WindsurfSettings = hasRemainingHooks\n ? { ...settings, hooks: remainingHooks }\n : (({ hooks: _h, ...rest }) => rest)(settings as WindsurfSettings & { hooks: unknown });\n return { settings: newSettings, wasInstalled: true };\n}\n\n// ---------------------------------------------------------------------------\n// Command handler\n// ---------------------------------------------------------------------------\n\n/**\n * Handler for `yakcc hooks windsurf install [--target <dir>] [--uninstall]`.\n *\n * Install: writes a yakcc hook entry to .windsurf/settings.json (VS Code settings\n * format) and a marker file to .windsurf/yakcc-windsurf-hook.json.\n * Uninstall: removes the yakcc hook entry from .windsurf/settings.json.\n * Both paths are idempotent.\n *\n * Limitation (DEC-CLI-HOOKS-WINDSURF-INSTALL-001): Windsurf does not yet expose\n * synchronous tool-call interception via a stable Node.js API. The install\n * command writes the intended wiring so that, when the API stabilises, the\n * hook subprocess can be activated without a reinstall. The marker file\n * documents this constraint for tooling and human readers.\n *\n * @param argv - Remaining argv after `hooks windsurf install` has been consumed.\n * @param logger - Output sink.\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function hooksWindsurfInstall(\n argv: readonly string[],\n logger: Logger,\n): Promise<number> {\n const { values } = parseArgs({\n args: [...argv],\n options: {\n target: { type: \"string\", short: \"t\" },\n uninstall: { type: \"boolean\" },\n },\n allowPositionals: false,\n strict: true,\n });\n\n const targetDir = values.target ?? \".\";\n const windsurfDir = join(targetDir, \".windsurf\");\n const settingsPath = join(windsurfDir, \"settings.json\");\n const markerPath = join(windsurfDir, WINDSURF_HOOK_MARKER_FILENAME);\n\n try {\n mkdirSync(windsurfDir, { recursive: true });\n } catch (err) {\n logger.error(`error: cannot create ${windsurfDir}: ${String(err)}`);\n return 1;\n }\n\n // --- Uninstall path ---\n if (values.uninstall) {\n const { settings: updated, wasInstalled } = applyUninstall(readWindsurfSettings(settingsPath));\n if (!wasInstalled) {\n logger.log(\"yakcc windsurf hook not installed — nothing to uninstall.\");\n return 0;\n }\n try {\n writeWindsurfSettings(settingsPath, updated);\n } catch (err) {\n logger.error(`error: cannot write ${settingsPath}: ${String(err)}`);\n return 1;\n }\n logger.log(`yakcc windsurf hook removed from ${settingsPath}`);\n return 0;\n }\n\n // --- Install path ---\n const { settings: updated, alreadyInstalled } = applyInstall(readWindsurfSettings(settingsPath));\n try {\n writeWindsurfSettings(settingsPath, updated);\n } catch (err) {\n logger.error(`error: cannot write ${settingsPath}: ${String(err)}`);\n return 1;\n }\n\n // Write the structured hook marker file.\n try {\n writeFileSync(\n markerPath,\n `${JSON.stringify(\n {\n command: HOOK_COMMAND,\n description: \"yakcc tool-call interception hook for Windsurf\",\n sessionEnvVar: \"WINDSURF_SESSION_ID\",\n telemetryPrefix: \"windsurf\",\n installedAt: new Date().toISOString(),\n note:\n \"Windsurf does not yet expose synchronous tool-call interception via a stable Node.js API. \" +\n \"This marker documents the intended wiring (DEC-CLI-HOOKS-WINDSURF-INSTALL-001). \" +\n \"When the Windsurf extension API stabilises, hook activation requires no reinstall.\",\n },\n null,\n 2,\n )}\\n`,\n \"utf-8\",\n );\n } catch (err) {\n logger.error(`warning: cannot write marker file ${markerPath}: ${String(err)}`);\n // Non-fatal: the settings.json update succeeded.\n }\n\n if (alreadyInstalled) {\n logger.log(`yakcc windsurf hook already installed at ${settingsPath} (idempotent).`);\n } else {\n logger.log(`yakcc windsurf hook installed at ${settingsPath}`);\n logger.log(`hook command: ${HOOK_COMMAND}`);\n logger.log(\"session env var: WINDSURF_SESSION_ID\");\n logger.log(`marker: ${markerPath}`);\n logger.log(\n \"note: Windsurf tool-call interception API not yet stable — see marker for details.\",\n );\n }\n return 0;\n}\n","// SPDX-License-Identifier: MIT\n//\n// init.ts — handler for `yakcc init [options]`\n//\n// First-30-seconds surface for v0.5 GTM. Single command that:\n// 1. Creates .yakcc/ directory layout\n// 2. Initializes the SQLite registry\n// 3. Auto-detects installed IDEs and installs hooks for each\n// 4. Seeds the bootstrap corpus (unless --no-seed)\n// 5. Writes .yakccrc.json with mode + installedHooks fields\n// 6. Prints a concise ≤6-line summary\n//\n// @decision DEC-CLI-INIT-001\n// title: Config-file format, transitive hook install, auto-seed policy\n// status: superseded-by-addendum (WI-V05-INIT-COMMAND #204; amended by DEC-CLI-INIT-002)\n// rationale:\n// CONFIG FORMAT: `.yakccrc.json` at the target directory root (not inside\n// `.yakcc/`). Rationale — keeps the project config visible at the repo root\n// alongside package.json, .eslintrc, etc. Avoids nesting user-facing config\n// inside the data directory. Alternative `.yakcc/config.json` was rejected\n// because it conflates operational data (SQLite, telemetry) with project\n// configuration. Inline `package.json yakcc:` key was rejected because yakcc\n// is not always used inside a Node.js project.\n//\n// TRANSITIVE HOOK INSTALL: yes — `yakcc init` calls each IDE's install function\n// directly (not via shell subprocess). Rationale — composing the real\n// function call ensures the same code path that `yakcc hooks <ide> install`\n// exercises; no duplication, no subprocess overhead, no PATH dependency.\n// DEC-CLI-INDEX-001 establishes the pattern: each command is a callable\n// function, not a subprocess. DEC-CLI-INIT-002 extends this to all four IDEs.\n//\n// AUTO-SEED POLICY: changed by DEC-CLI-INIT-002 — `yakcc init` now calls\n// `seedYakccCorpus` by default (the bootstrap corpus, ~3k+ atoms). `--no-seed`\n// is the opt-out that restores the pre-DEC-CLI-INIT-002 quiet-init shape.\n//\n// PEER REGISTRATION: when `--peer <url>` is provided, init writes the peer\n// URL into `.yakccrc.json` under `federation.peers[]` and immediately runs\n// `yakcc federation mirror` against it. This gives the user a populated\n// registry from their team peer on first boot. URL validation is strict (must\n// be http: or https:) to fail fast on typos.\n//\n// @decision DEC-CLI-INIT-002\n// title: Single-command `yakcc init` collapses init+seed+hooks-install into one entry;\n// auto-detects IDEs; supersedes the no-auto-seed clause of DEC-CLI-INIT-001\n// status: accepted (WI-656-S1)\n// rationale:\n// Operator decision 2026-05-17 to collapse the first-touch surface. Every\n// user-facing first-impression flows through `yakcc init`. The 6 new flags\n// (--local, --airgapped, --peer, --skip-hooks, --ide, --no-seed) allow precise\n// control without breaking backward compat for existing --target and --peer usage.\n// IDE auto-detection uses DEC-CLI-IDE-DETECT-SEMANTICS-001 (config-dir probe).\n// Installer dispatch uses a thin table (DEC-CLI-IDE-INSTALLER-DISPATCH-001).\n// Backward compat preserved: --target and --peer semantics unchanged.\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport { type Registry, openRegistry } from \"@yakcc/registry\";\nimport type { Logger } from \"../index.js\";\nimport { type IdeName, KNOWN_IDE_NAMES, detectInstalledIdes } from \"../lib/ide-detect.js\";\nimport { hooksAiderInstall } from \"./hooks-aider-install.js\";\nimport { hooksClineInstall } from \"./hooks-cline-install.js\";\nimport { hooksContinueInstall } from \"./hooks-continue-install.js\";\nimport { hooksCursorInstall } from \"./hooks-cursor-install.js\";\nimport { hooksClaudeCodeInstall } from \"./hooks-install.js\";\nimport { hooksWindsurfInstall } from \"./hooks-windsurf-install.js\";\nimport { registryInit } from \"./registry-init.js\";\nimport { seedYakccCorpus } from \"./seed-yakcc.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Subdirectory for all yakcc operational data: DB, telemetry, etc. */\nconst YAKCC_DIR = \".yakcc\";\n\n/** Subdirs created inside .yakcc/ by init. */\nconst YAKCC_SUBDIRS = [\"registry\", \"telemetry\", \"config\"] as const;\n\n/** Default registry path relative to target. */\nconst DEFAULT_REGISTRY_SUBPATH = \".yakcc/registry.sqlite\";\n\n/** Config file written at the project root (see DEC-CLI-INIT-001). */\nconst RC_FILENAME = \".yakccrc.json\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Mode written to .yakccrc.json.\n *\n * - \"local\": default; no federation peer, offline-first.\n * - \"airgapped\": explicit offline intent; semantically equivalent to \"local\"\n * today (see NG2 — no yakcc.dev server yet); written for forward-compat.\n * - \"global\": --peer <url> was provided; will mirror from that peer on init.\n */\ntype YakccMode = \"local\" | \"airgapped\" | \"global\";\n\n/** Shape of the .yakccrc.json written by init. */\ninterface YakccRc {\n version: 1;\n mode?: YakccMode;\n registry: {\n path: string;\n };\n federation?: {\n peers: string[];\n };\n installedHooks?: string[];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read .yakccrc.json from target directory, or return null if absent/corrupt.\n */\nfunction readRc(targetDir: string): YakccRc | null {\n const rcPath = join(targetDir, RC_FILENAME);\n if (!existsSync(rcPath)) return null;\n try {\n return JSON.parse(readFileSync(rcPath, \"utf-8\")) as YakccRc;\n } catch {\n return null;\n }\n}\n\n/** Write .yakccrc.json to target directory. */\nfunction writeRc(targetDir: string, rc: YakccRc): void {\n writeFileSync(join(targetDir, RC_FILENAME), `${JSON.stringify(rc, null, 2)}\\n`, \"utf-8\");\n}\n\n/**\n * Validate a peer URL string: must be http:// or https://.\n * Returns null on success, an error message on failure.\n */\nfunction validatePeerUrl(url: string): string | null {\n try {\n const parsed = new URL(url);\n if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n return `peer URL must use http or https scheme, got: ${parsed.protocol}`;\n }\n return null;\n } catch {\n return `invalid peer URL: ${url}`;\n }\n}\n\n/**\n * Parse a comma-separated --ide value into a list of IdeName.\n * Returns { ok: IdeName[] } on success or { err: string } on invalid input.\n */\nfunction parseIdeList(raw: string): { ok: IdeName[] } | { err: string } {\n const parts = raw.split(\",\").map((s) => s.trim().toLowerCase());\n const invalid = parts.filter((p) => !(KNOWN_IDE_NAMES as readonly string[]).includes(p));\n if (invalid.length > 0) {\n return {\n err:\n `unknown IDE name(s): ${invalid.join(\", \")}. ` +\n `Known IDEs: ${KNOWN_IDE_NAMES.join(\", \")}`,\n };\n }\n return { ok: parts as IdeName[] };\n}\n\n// ---------------------------------------------------------------------------\n// IDE installer dispatch table\n//\n// @decision DEC-CLI-IDE-INSTALLER-DISPATCH-001\n// title: Per-IDE installer functions called via a thin dispatch table inside init.ts,\n// NOT via a generic HookInstaller interface\n// status: accepted (WI-656-S1)\n// rationale:\n// Generic interface would force the lowest common denominator (marker-file only)\n// and demote Claude Code's rich settings.json wiring. Per-IDE installers preserve\n// surface-specific semantics. The dispatch table is kept here (not in ide-detect.ts)\n// because it depends on the installer imports, and ide-detect.ts must remain pure\n// (no I/O dependencies beyond existsSync).\n// ---------------------------------------------------------------------------\n\n/**\n * Install the yakcc hook for a single IDE into the target project directory.\n *\n * For claude-code and cursor this writes a settings.json hook entry (rich wiring).\n * For cline and continue this writes a marker file (surface not yet stable).\n *\n * The cline and continue installers accept an optional overrideDir for test isolation.\n * In production (no overrideDir) they default to os.homedir()-derived paths.\n */\nasync function installHookForIde(\n ide: IdeName,\n targetDir: string,\n logger: Logger,\n overrideHome?: string,\n): Promise<void> {\n const { homedir } = await import(\"node:os\");\n const home = overrideHome ?? homedir();\n\n switch (ide) {\n case \"claude-code\": {\n const code = await hooksClaudeCodeInstall([\"--target\", targetDir], logger);\n if (code !== 0) throw new Error(`claude-code hook install failed (exit ${code})`);\n break;\n }\n case \"cursor\": {\n const code = await hooksCursorInstall([\"--target\", targetDir], logger);\n if (code !== 0) throw new Error(`cursor hook install failed (exit ${code})`);\n break;\n }\n case \"cline\": {\n const { join } = await import(\"node:path\");\n const clineDir = join(home, \".config\", \"cline\");\n const code = await hooksClineInstall([], logger, clineDir);\n if (code !== 0) throw new Error(`cline hook install failed (exit ${code})`);\n break;\n }\n case \"continue\": {\n const { join } = await import(\"node:path\");\n const continueDir = join(home, \".continue\");\n const code = await hooksContinueInstall([], logger, continueDir);\n if (code !== 0) throw new Error(`continue hook install failed (exit ${code})`);\n break;\n }\n case \"windsurf\": {\n const code = await hooksWindsurfInstall([\"--target\", targetDir], logger);\n if (code !== 0) throw new Error(`windsurf hook install failed (exit ${code})`);\n break;\n }\n case \"aider\": {\n const { join } = await import(\"node:path\");\n const aiderDir = join(home, \".aider\");\n const code = await hooksAiderInstall([], logger, aiderDir);\n if (code !== 0) throw new Error(`aider hook install failed (exit ${code})`);\n break;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// InitOptions — internal injection seam\n// ---------------------------------------------------------------------------\n\n/**\n * Internal options for `init`. Tests inject these to avoid network I/O and\n * provide the bootstrap corpus path.\n */\nexport interface InitOptions {\n /**\n * Override the home directory used for IDE detection and cline/continue\n * installer default dirs. When omitted, os.homedir() is used.\n * Used by tests to inject a fake HOME without mutating process state.\n */\n overrideHome?: string;\n /**\n * Override path to the bootstrap corpus sqlite for --yakcc seed mode.\n * Forwarded to seedYakccCorpus() (DEC-CLI-SEED-YAKCC-001).\n */\n corpusPath?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Command handler\n// ---------------------------------------------------------------------------\n\n/**\n * Handler for `yakcc init [--target <dir>] [--peer <url>] [--local] [--airgapped]\n * [--skip-hooks] [--ide <comma-list>] [--no-seed]`.\n *\n * Steps performed (in order):\n * 1. Parse and validate arguments.\n * 2. Create `.yakcc/` directory with standard subdirectories.\n * 3. Call `registryInit` to create `.yakcc/registry.sqlite` (idempotent).\n * 4. IDE hook installation (unless --skip-hooks):\n * a. If `--ide <list>`: install for each IDE in the explicit list.\n * b. Otherwise: auto-detect via detectInstalledIdes() and install for each.\n * 5. Seed the bootstrap corpus (unless --no-seed).\n * 6. If `--peer <url>`: register peer in `.yakccrc.json` and mirror blocks.\n * 7. Write / update `.yakccrc.json` with mode + installedHooks additive fields.\n * 8. Print concise summary (≤6 lines on happy path, G6).\n *\n * Idempotency: existing `.yakcc/` is detected; registry and hook installs are\n * themselves idempotent; `.yakccrc.json` is merged (peer list appended, not\n * replaced; installedHooks merged).\n *\n * @param argv - Remaining argv after `init` has been consumed.\n * @param logger - Output sink; defaults to CONSOLE_LOGGER in production.\n * @param opts - Internal options (home override, corpus path for tests).\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function init(\n argv: readonly string[],\n logger: Logger,\n opts?: InitOptions,\n): Promise<number> {\n // -------------------------------------------------------------------------\n // 1. Parse arguments\n // -------------------------------------------------------------------------\n\n let parsed: ReturnType<\n typeof parseArgs<{\n options: {\n target: { type: \"string\"; short: \"t\" };\n peer: { type: \"string\" };\n local: { type: \"boolean\" };\n airgapped: { type: \"boolean\" };\n \"skip-hooks\": { type: \"boolean\" };\n ide: { type: \"string\" };\n \"no-seed\": { type: \"boolean\" };\n };\n }>\n >;\n\n try {\n parsed = parseArgs({\n args: [...argv],\n options: {\n target: { type: \"string\", short: \"t\" },\n peer: { type: \"string\" },\n local: { type: \"boolean\" },\n airgapped: { type: \"boolean\" },\n \"skip-hooks\": { type: \"boolean\" },\n ide: { type: \"string\" },\n \"no-seed\": { type: \"boolean\" },\n },\n allowPositionals: false,\n strict: true,\n });\n } catch (err) {\n logger.error(`error: ${(err as Error).message}`);\n logger.error(\"Usage: yakcc init [--target <dir>] [--peer <url>] [--local] [--airgapped]\");\n logger.error(\n \" [--skip-hooks] [--ide <claude-code|cursor|cline|continue|windsurf|aider,...>] [--no-seed]\",\n );\n return 1;\n }\n\n const targetDir = parsed.values.target ?? \".\";\n const peerUrl = parsed.values.peer;\n const isAirgapped = parsed.values.airgapped === true;\n const skipHooks = parsed.values[\"skip-hooks\"] === true;\n const noSeed = parsed.values[\"no-seed\"] === true;\n const ideRaw = parsed.values.ide;\n\n // Determine mode (written to .yakccrc.json for forward-compat).\n // Priority: --airgapped > --peer (implies global) > --local > default (local).\n let mode: YakccMode = \"local\";\n if (isAirgapped) {\n mode = \"airgapped\";\n } else if (peerUrl !== undefined) {\n mode = \"global\";\n } else if (parsed.values.local === true) {\n mode = \"local\";\n }\n\n // -------------------------------------------------------------------------\n // 2. Validate peer URL (fail fast before touching the filesystem)\n // -------------------------------------------------------------------------\n\n if (peerUrl !== undefined) {\n const urlError = validatePeerUrl(peerUrl);\n if (urlError !== null) {\n logger.error(`error: ${urlError}`);\n return 1;\n }\n }\n\n // -------------------------------------------------------------------------\n // 3. Validate --ide list (fail fast before touching the filesystem)\n // -------------------------------------------------------------------------\n\n let explicitIdes: IdeName[] | null = null;\n if (ideRaw !== undefined) {\n const parseResult = parseIdeList(ideRaw);\n if (\"err\" in parseResult) {\n logger.error(`error: ${parseResult.err}`);\n return 1;\n }\n explicitIdes = parseResult.ok;\n }\n\n // -------------------------------------------------------------------------\n // 4. Create .yakcc/ directory with standard subdirectories\n // -------------------------------------------------------------------------\n\n const yakccDir = join(targetDir, YAKCC_DIR);\n const yakccDirExists = existsSync(yakccDir);\n\n try {\n mkdirSync(yakccDir, { recursive: true });\n for (const sub of YAKCC_SUBDIRS) {\n mkdirSync(join(yakccDir, sub), { recursive: true });\n }\n } catch (err) {\n logger.error(`error: cannot create ${yakccDir}: ${String(err)}`);\n return 1;\n }\n\n if (yakccDirExists) {\n logger.log(\" .yakcc/ (already exists — skipping directory creation)\");\n } else {\n logger.log(\" .yakcc/ created\");\n }\n\n // -------------------------------------------------------------------------\n // 5. Initialize the registry (idempotent via openRegistry/applyMigrations)\n // -------------------------------------------------------------------------\n\n const registryPath = join(targetDir, DEFAULT_REGISTRY_SUBPATH);\n const registryCode = await registryInit([\"--path\", registryPath], logger);\n if (registryCode !== 0) {\n return registryCode;\n }\n\n // -------------------------------------------------------------------------\n // 6. IDE hook installation (DEC-CLI-IDE-INSTALLER-DISPATCH-001)\n //\n // If --skip-hooks: skip all hook installation.\n // If --ide <list>: install only the specified IDEs (no auto-detect).\n // Otherwise: auto-detect via detectInstalledIdes() and install each.\n // -------------------------------------------------------------------------\n\n const installedHooks: string[] = [];\n\n if (!skipHooks) {\n let idesToInstall: IdeName[];\n\n if (explicitIdes !== null) {\n idesToInstall = explicitIdes;\n } else {\n // Auto-detect: pass overrideHome so tests can control the probe paths.\n const detected = detectInstalledIdes(opts?.overrideHome);\n idesToInstall = detected.map((d) => d.name);\n }\n\n for (const ide of idesToInstall) {\n try {\n await installHookForIde(ide, targetDir, logger, opts?.overrideHome);\n installedHooks.push(ide);\n } catch (err) {\n logger.error(`warning: ${String(err)} — continuing`);\n // Non-fatal: the registry is initialized; other hooks may still succeed.\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // 7. Seed bootstrap corpus (unless --no-seed)\n //\n // Default: call seedYakccCorpus() (DEC-CLI-INIT-002 flips the no-auto-seed\n // clause of DEC-CLI-INIT-001). --no-seed restores quiet-init behavior.\n // -------------------------------------------------------------------------\n\n let seedCount = 0;\n if (!noSeed) {\n let registry: Registry | null = null;\n try {\n registry = await openRegistry(registryPath);\n } catch (err) {\n logger.error(`warning: cannot open registry for seed: ${String(err)} — continuing`);\n }\n\n if (registry !== null) {\n try {\n seedCount = await seedYakccCorpus(\n registry,\n { ...(opts?.corpusPath !== undefined ? { corpusPath: opts.corpusPath } : {}) },\n logger,\n );\n } catch (err) {\n // Seed failure is non-fatal — the registry is still initialized.\n logger.error(`warning: seed failed: ${String(err)} — continuing`);\n } finally {\n try {\n await registry.close();\n } catch {\n // ignore close error\n }\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // 8. Mirror peer (if --peer provided)\n // -------------------------------------------------------------------------\n\n if (peerUrl !== undefined) {\n logger.log(` mirroring blocks from peer: ${peerUrl}`);\n const { runFederation } = await import(\"./federation.js\");\n const mirrorCode = await runFederation(\n [\"mirror\", \"--remote\", peerUrl, \"--registry\", registryPath],\n logger,\n );\n if (mirrorCode !== 0) {\n logger.error(`warning: mirror from ${peerUrl} failed (exit ${mirrorCode}) — continuing`);\n }\n }\n\n // -------------------------------------------------------------------------\n // 9. Write / update .yakccrc.json\n //\n // New additive fields per DEC-CLI-INIT-002 (NG4: no version bump, additive only):\n // - mode: \"local\" | \"airgapped\" | \"global\"\n // - installedHooks: string[] (merged, deduped)\n // -------------------------------------------------------------------------\n\n const existingRc = readRc(targetDir);\n\n let rc: YakccRc;\n if (existingRc !== null) {\n rc = { ...existingRc, mode };\n if (peerUrl !== undefined) {\n if (rc.federation === undefined) {\n rc = { ...rc, federation: { peers: [peerUrl] } };\n } else if (!rc.federation.peers.includes(peerUrl)) {\n rc = { ...rc, federation: { peers: [...rc.federation.peers, peerUrl] } };\n }\n }\n // Merge installedHooks (dedupe).\n const existingHooks = rc.installedHooks ?? [];\n const merged = [...new Set([...existingHooks, ...installedHooks])];\n rc = { ...rc, installedHooks: merged };\n } else {\n rc = {\n version: 1,\n mode,\n registry: { path: DEFAULT_REGISTRY_SUBPATH },\n ...(peerUrl !== undefined ? { federation: { peers: [peerUrl] } } : {}),\n installedHooks,\n };\n }\n\n try {\n writeRc(targetDir, rc);\n } catch (err) {\n logger.error(`error: cannot write ${join(targetDir, RC_FILENAME)}: ${String(err)}`);\n return 1;\n }\n\n logger.log(` ${RC_FILENAME} written`);\n\n // -------------------------------------------------------------------------\n // 10. Print concise summary (G6: ≤6 lines on happy path)\n // -------------------------------------------------------------------------\n\n const hookedLine =\n installedHooks.length > 0\n ? `Hooked into: ${installedHooks.join(\", \")}.`\n : skipHooks\n ? \"Hooks: skipped (--skip-hooks).\"\n : \"Hooks: no IDEs detected.\";\n\n logger.log(\"\");\n logger.log(`Installed in ${targetDir}. ${hookedLine} Registry: ${seedCount} atoms.`);\n\n // @decision DEC-CLI-INIT-NO-IDE-HINT-001 (WI-687-S7 / #746 AC2)\n // title: When auto-detect finds nothing and --skip-hooks was not passed, surface a\n // structured hint so the first-30-seconds GTM surface does not dead-end silently.\n // status: accepted (WI-746-S7)\n // rationale:\n // AC2 of #746 requires the user to receive actionable guidance when no IDE config\n // dirs are found. A silent \"Hooks: no IDEs detected.\" leaves a fresh user with no\n // recovery path. The hint is non-interactive (parent plan NG6 / DEC-CLI-INIT-002:\n // init must remain scriptable and non-blocking on stdin). The IDE list is derived\n // from KNOWN_IDE_NAMES — NOT a hand-typed parallel list — so the hint never drifts\n // when a future S-slice adds a 7th adapter (Sacred Practice #12 / DEC-WI687-SLICING-001).\n if (!skipHooks && installedHooks.length === 0) {\n logger.log(\" Tip: no IDE config dirs found in your home directory. Re-run with\");\n logger.log(\" `yakcc init --ide <name>` to install for a specific IDE\");\n logger.log(` (supported: ${KNOWN_IDE_NAMES.join(\", \")}),`);\n logger.log(\" or `yakcc init --skip-hooks` to skip hook setup entirely.\");\n }\n\n return 0;\n}\n","// SPDX-License-Identifier: MIT\n//\n// ide-detect.ts — canonical IDE-detection library for yakcc CLI\n//\n// @decision DEC-CLI-IDE-DETECT-PLACEMENT-001\n// title: IDE-detection logic lives in packages/cli/src/lib/ide-detect.ts, NOT colocated in init.ts\n// status: accepted (WI-656-S1)\n// rationale:\n// Both `init` and `uninstall` consume the detection function; placing it under\n// `commands/` would make it look like a CLI verb. Sacred Practice #12 demands a\n// single authority for \"which IDEs are present on this machine.\" Any IDE-aware\n// code path (init, uninstall, future telemetry-tagging) MUST consult this module.\n//\n// @decision DEC-CLI-IDE-DETECT-SEMANTICS-001\n// title: IDE detection probes \"config dir exists\", NOT \"binary in $PATH\" or \"app running\"\n// status: accepted (WI-656-S1)\n// rationale:\n// Detection-by-config-dir is uniform across IDEs (Cline/Continue are VS Code\n// extensions, not binaries); avoids subprocess spawn (B6a-air-gap-clean);\n// uninstall finds what install installed (false-positive < false-negative cost).\n// The probe is: does the IDE's config directory exist? If a user uninstalled the\n// IDE but left the config dir, we still detect it — that is the correct behavior\n// because our hook lives in the config dir and we need to be able to uninstall it.\n//\n// NO node:child_process import — detection is pure existsSync, no shell-out (B6a gate).\n\nimport { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Union of all IDE identifiers known to yakcc's detection library.\n *\n * - `\"claude-code\"`: Anthropic Claude Code CLI / extension\n * - `\"cursor\"`: Cursor editor (forked VS Code)\n * - `\"cline\"`: Cline VS Code extension (saoudrizwan.claude-dev)\n * - `\"continue\"`: Continue.dev VS Code / JetBrains extension\n * - `\"windsurf\"`: Windsurf (Codeium's AI IDE, VS Code-derived)\n * - `\"aider\"`: Aider CLI tool (https://aider.chat)\n *\n * Note: \"codex\" is explicitly excluded per NG1 / DEC-CLI-INIT-002 (#220 closed\n * not-planned). Do not add it here without a new WI reversing that decision.\n */\nexport type IdeName = \"claude-code\" | \"cursor\" | \"cline\" | \"continue\" | \"windsurf\" | \"aider\";\n\n/**\n * A detected IDE entry — the config directory is confirmed to exist.\n *\n * `installed: true` narrows the type for callers who want to assert that the\n * detection actually fired (vs. a candidate that was probed but not found).\n */\nexport interface DetectedIde {\n /** Canonical IDE identifier. */\n name: IdeName;\n /** Absolute path to the IDE's config directory (confirmed to exist). */\n configDir: string;\n /** Always true for entries returned by detectInstalledIdes(). */\n installed: true;\n}\n\n// ---------------------------------------------------------------------------\n// Config-dir path resolution helpers (platform-specific per §2.2 of the plan)\n// ---------------------------------------------------------------------------\n\n/**\n * Returns candidate config directories for each supported IDE, ordered by\n * probe priority (primary first, fallbacks after).\n *\n * Paths are expanded from the provided `home` directory. No shell-out, no $PATH scan.\n *\n * The returned map is consumed by detectInstalledIdes() which probes each\n * candidate path with existsSync until one is found per IDE.\n *\n * @internal — exported for testing only.\n */\nexport function buildCandidatePaths(home: string): Record<IdeName, readonly string[]> {\n const platform = process.platform as \"darwin\" | \"linux\" | \"win32\" | string;\n\n // Cursor config dir — platform-specific (DEC-CLI-IDE-DETECT-SEMANTICS-001)\n const cursorCandidates: string[] = (() => {\n if (platform === \"darwin\") {\n return [join(home, \"Library\", \"Application Support\", \"Cursor\")];\n }\n if (platform === \"win32\") {\n const appdata = process.env.APPDATA;\n if (appdata !== undefined) {\n return [join(appdata, \"Cursor\")];\n }\n // Fallback if APPDATA is not set on Windows\n return [join(home, \"AppData\", \"Roaming\", \"Cursor\")];\n }\n // Linux (and anything else)\n return [join(home, \".config\", \"Cursor\")];\n })();\n\n // Cline: `~/.config/cline/` on all platforms, with VS Code extension marker as\n // a secondary probe. The extension marker path allows detection even before\n // the user has opened Cline's settings panel (which creates ~/.config/cline/).\n const clineCandidates: string[] = [\n join(home, \".config\", \"cline\"),\n // VS Code extension dir marker — check the containing directory prefix since\n // the actual extension dir includes a version suffix.\n join(home, \".vscode\", \"extensions\", \"saoudrizwan.claude-dev\"),\n ];\n\n // Continue.dev: `~/.continue/` on all platforms (documented in Continue's docs).\n // Optionally also check for the VS Code extension marker as a secondary probe.\n const continueCandidates: string[] = [\n join(home, \".continue\"),\n join(home, \".vscode\", \"extensions\", \"continue.continue\"),\n ];\n\n // Windsurf (Codeium): `~/.windsurf/` on all platforms. Windsurf is a VS Code-derived\n // IDE; config lives in a flat ~/.windsurf/ directory on all supported platforms.\n const windsurfCandidates: string[] = [join(home, \".windsurf\")];\n\n // Aider (https://aider.chat): `~/.aider/` on all platforms. Aider is a CLI tool that\n // auto-creates this directory on first run to store chat history and cache.\n const aiderCandidates: string[] = [join(home, \".aider\")];\n\n return {\n \"claude-code\": [join(home, \".claude\")],\n cursor: cursorCandidates,\n cline: clineCandidates,\n continue: continueCandidates,\n windsurf: windsurfCandidates,\n aider: aiderCandidates,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Detect which IDEs are present on this machine by probing known config dirs.\n *\n * This is a pure-function-equivalent (given a fixed filesystem state). The only\n * I/O it performs is existsSync calls — no network, no subprocess, no $PATH\n * scan. Tests override the home directory via the optional `overrideHome`\n * parameter to inject a fake home directory without mutating process state.\n *\n * @decision DEC-CLI-IDE-DETECT-SEMANTICS-001 — probe is \"config dir exists,\"\n * not \"binary in $PATH\" or \"app running.\" False positives (config dir exists\n * but app uninstalled) are preferred over false negatives (app installed but\n * config dir missing), because our hook lives in the config dir and uninstall\n * must be able to find what install put there.\n *\n * @param overrideHome - Optional home directory override for testing. When\n * omitted, uses os.homedir(). Tests MUST use this parameter rather than\n * mutating process.env.HOME to avoid state leakage between tests.\n *\n * @returns Array of DetectedIde entries, one per IDE whose config dir exists.\n * Empty array if no IDEs are detected. Order is stable: claude-code, cursor,\n * cline, continue, windsurf, aider.\n */\nexport function detectInstalledIdes(overrideHome?: string): DetectedIde[] {\n const home = overrideHome ?? homedir();\n const candidates = buildCandidatePaths(home);\n\n const detected: DetectedIde[] = [];\n\n for (const [name, paths] of Object.entries(candidates) as [IdeName, readonly string[]][]) {\n for (const configDir of paths) {\n if (existsSync(configDir)) {\n detected.push({ name, configDir, installed: true });\n break; // First matching candidate wins; don't emit duplicate entries per IDE.\n }\n }\n }\n\n return detected;\n}\n\n/**\n * All IDE names that yakcc can detect and install hooks for.\n *\n * Callers (e.g. `yakcc init --ide <list>`) validate user-supplied IDE names\n * against this set. Any name not in this set is an error.\n */\nexport const KNOWN_IDE_NAMES: readonly IdeName[] = [\n \"claude-code\",\n \"cursor\",\n \"cline\",\n \"continue\",\n \"windsurf\",\n \"aider\",\n];\n","// SPDX-License-Identifier: MIT\n//\n// hooks-cline-install.ts — marker-file installer for Cline VS Code extension\n//\n// @decision DEC-CLI-HOOKS-CLINE-INSTALL-001\n// title: hooks-cline-install writes a marker file ~/.config/cline/yakcc-cline-hook.json;\n// no live hook wiring yet (Cline's extension API surface not yet documented)\n// status: accepted (WI-656-S1)\n// rationale:\n// Cline's VS Code extension (saoudrizwan.claude-dev) does not expose a\n// synchronous tool-call interception surface via a stable Node.js API as of\n// the WI-656 implementation date. This installer mirrors the pattern from\n// DEC-CLI-HOOKS-CURSOR-INSTALL-001 (cursor marker-file approach):\n//\n// (A) Creates ~/.config/cline/ if absent.\n// (B) Writes ~/.config/cline/yakcc-cline-hook.json — a structured marker that\n// records the hook command, description, session env var, installation\n// timestamp, and an explicit note about the API-surface limitation. This\n// marker is the primary machine-readable artefact for future tooling that\n// activates the hook once Cline's API stabilises.\n//\n// The install command is IDEMPOTENT: if the marker already exists (identified\n// by the _yakcc sentinel field), it logs a \"already installed\" message and\n// exits 0 without re-writing. This matches the cursor installer pattern.\n//\n// No settings.json wiring is performed (unlike claude-code and cursor) because\n// Cline's extension-settings schema is not yet documented. When the surface\n// stabilises, a follow-up WI can activate real hook wiring without requiring\n// users to reinstall — the marker file already documents the intent.\n//\n// The cline config directory is determined by the overrideClineDir parameter\n// (for testing) or defaults to ~/.config/cline/ (primary probe per\n// DEC-CLI-IDE-DETECT-SEMANTICS-001).\n\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { Logger } from \"../index.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Marker filename written to the Cline config dir by the install command. */\nconst CLINE_HOOK_MARKER_FILENAME = \"yakcc-cline-hook.json\";\n\n/** Sentinel field that identifies a yakcc-installed Cline marker. */\nconst YAKCC_CLINE_MARKER = \"yakcc-hook-v1-cline\";\n\n/** Subprocess command to be invoked for Cline tool-call interception (future). */\nconst HOOK_COMMAND = \"yakcc hook-intercept\";\n\n// ---------------------------------------------------------------------------\n// Marker helpers\n// ---------------------------------------------------------------------------\n\ninterface ClineMarker {\n _yakcc: string;\n [key: string]: unknown;\n}\n\nfunction readMarker(markerPath: string): ClineMarker | null {\n if (!existsSync(markerPath)) return null;\n try {\n return JSON.parse(readFileSync(markerPath, \"utf-8\")) as ClineMarker;\n } catch {\n return null;\n }\n}\n\nfunction isYakccInstalled(markerPath: string): boolean {\n const marker = readMarker(markerPath);\n return marker !== null && marker._yakcc === YAKCC_CLINE_MARKER;\n}\n\n// ---------------------------------------------------------------------------\n// Command handler\n// ---------------------------------------------------------------------------\n\n/**\n * Handler for the Cline hook installer.\n *\n * Called by `yakcc init` when Cline is detected (DEC-CLI-IDE-INSTALLER-DISPATCH-001).\n * Can also be invoked standalone (e.g., tests, future `yakcc hooks cline install` verb).\n *\n * Install: creates ~/.config/cline/ (or the override dir) and writes a yakcc marker\n * file documenting the intended hook wiring. Idempotent.\n * Uninstall: removes the yakcc marker file.\n *\n * @param argv - Remaining argv. Accepts `--target <dir>` and `--uninstall`.\n * @param logger - Output sink.\n * @param overrideClineDir - Optional absolute path to use instead of ~/.config/cline/.\n * Used by tests to avoid writing to the real home directory.\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function hooksClineInstall(\n argv: readonly string[],\n logger: Logger,\n overrideClineDir?: string,\n): Promise<number> {\n let parsed: ReturnType<\n typeof parseArgs<{\n options: {\n target: { type: \"string\"; short: \"t\" };\n uninstall: { type: \"boolean\" };\n };\n }>\n >;\n\n try {\n parsed = parseArgs({\n args: [...argv],\n options: {\n target: { type: \"string\", short: \"t\" },\n uninstall: { type: \"boolean\" },\n },\n allowPositionals: false,\n strict: true,\n });\n } catch (err) {\n logger.error(`error: ${(err as Error).message}`);\n return 1;\n }\n\n // Resolve the Cline config directory.\n // overrideClineDir is the injection seam for tests; production uses ~/.config/cline/.\n const clineDir = overrideClineDir ?? join(homedir(), \".config\", \"cline\");\n const markerPath = join(clineDir, CLINE_HOOK_MARKER_FILENAME);\n\n try {\n mkdirSync(clineDir, { recursive: true });\n } catch (err) {\n logger.error(`error: cannot create ${clineDir}: ${String(err)}`);\n return 1;\n }\n\n // --- Uninstall path ---\n if (parsed.values.uninstall) {\n if (!isYakccInstalled(markerPath)) {\n logger.log(\"yakcc cline hook not installed — nothing to uninstall.\");\n return 0;\n }\n try {\n rmSync(markerPath);\n } catch (err) {\n logger.error(`error: cannot remove ${markerPath}: ${String(err)}`);\n return 1;\n }\n logger.log(`yakcc cline hook marker removed: ${markerPath}`);\n return 0;\n }\n\n // --- Install path ---\n if (isYakccInstalled(markerPath)) {\n logger.log(`yakcc cline hook already installed at ${markerPath} (idempotent).`);\n return 0;\n }\n\n const marker: ClineMarker = {\n command: HOOK_COMMAND,\n description: \"yakcc tool-call interception hook for Cline\",\n sessionEnvVar: \"CLINE_SESSION_ID\",\n telemetryPrefix: \"cline\",\n installedAt: new Date().toISOString(),\n _yakcc: YAKCC_CLINE_MARKER,\n note:\n \"Cline (saoudrizwan.claude-dev) does not yet expose synchronous tool-call \" +\n \"interception via a stable Node.js API. This marker documents the intended \" +\n \"wiring (DEC-CLI-HOOKS-CLINE-INSTALL-001). When the Cline extension API \" +\n \"stabilises, hook activation requires no reinstall.\",\n };\n\n try {\n writeFileSync(markerPath, `${JSON.stringify(marker, null, 2)}\\n`, \"utf-8\");\n } catch (err) {\n logger.error(`error: cannot write marker ${markerPath}: ${String(err)}`);\n return 1;\n }\n\n logger.log(`yakcc cline hook marker installed: ${markerPath}`);\n logger.log(\"note: Cline tool-call interception API not yet stable — see marker for details.\");\n return 0;\n}\n","// SPDX-License-Identifier: MIT\n//\n// hooks-continue-install.ts — marker-file installer for Continue.dev\n//\n// @decision DEC-CLI-HOOKS-CONTINUE-INSTALL-001\n// title: hooks-continue-install writes a marker file ~/.continue/yakcc-continue-hook.json;\n// no live hook wiring yet (Continue.dev's extension API not yet instrumented)\n// status: accepted (WI-656-S1)\n// rationale:\n// Continue.dev (continue.continue VS Code / JetBrains extension) does not yet\n// expose a synchronous tool-call interception surface via a stable Node.js API\n// as of the WI-656 implementation date. This installer mirrors the pattern from\n// DEC-CLI-HOOKS-CURSOR-INSTALL-001 and DEC-CLI-HOOKS-CLINE-INSTALL-001:\n//\n// (A) Creates ~/.continue/ if absent.\n// (B) Writes ~/.continue/yakcc-continue-hook.json — a structured marker that\n// records the hook command, description, session env var, installation\n// timestamp, and an explicit note about the API-surface limitation.\n//\n// IDEMPOTENT: if the marker already exists (identified by the _yakcc sentinel\n// field), logs \"already installed\" and exits 0 without re-writing.\n//\n// No settings.json wiring is performed because Continue.dev's extension-settings\n// schema for hook interception is not yet documented. The marker file documents\n// the intent; a follow-up WI activates real wiring when the API stabilises.\n//\n// The continue config directory is determined by the overrideContinueDir parameter\n// (for testing) or defaults to ~/.continue/ (primary probe per\n// DEC-CLI-IDE-DETECT-SEMANTICS-001).\n\nimport { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { Logger } from \"../index.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Marker filename written to the Continue config dir by the install command. */\nconst CONTINUE_HOOK_MARKER_FILENAME = \"yakcc-continue-hook.json\";\n\n/** Sentinel field that identifies a yakcc-installed Continue marker. */\nconst YAKCC_CONTINUE_MARKER = \"yakcc-hook-v1-continue\";\n\n/** Subprocess command to be invoked for Continue tool-call interception (future). */\nconst HOOK_COMMAND = \"yakcc hook-intercept\";\n\n// ---------------------------------------------------------------------------\n// Marker helpers\n// ---------------------------------------------------------------------------\n\ninterface ContinueMarker {\n _yakcc: string;\n [key: string]: unknown;\n}\n\nfunction readMarker(markerPath: string): ContinueMarker | null {\n if (!existsSync(markerPath)) return null;\n try {\n return JSON.parse(readFileSync(markerPath, \"utf-8\")) as ContinueMarker;\n } catch {\n return null;\n }\n}\n\nfunction isYakccInstalled(markerPath: string): boolean {\n const marker = readMarker(markerPath);\n return marker !== null && marker._yakcc === YAKCC_CONTINUE_MARKER;\n}\n\n// ---------------------------------------------------------------------------\n// Command handler\n// ---------------------------------------------------------------------------\n\n/**\n * Handler for the Continue.dev hook installer.\n *\n * Called by `yakcc init` when Continue.dev is detected (DEC-CLI-IDE-INSTALLER-DISPATCH-001).\n * Can also be invoked standalone (e.g., tests, future `yakcc hooks continue install` verb).\n *\n * Install: creates ~/.continue/ (or the override dir) and writes a yakcc marker\n * file documenting the intended hook wiring. Idempotent.\n * Uninstall: removes the yakcc marker file.\n *\n * @param argv - Remaining argv. Accepts `--target <dir>` and `--uninstall`.\n * @param logger - Output sink.\n * @param overrideContinueDir - Optional absolute path to use instead of ~/.continue/.\n * Used by tests to avoid writing to the real home directory.\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function hooksContinueInstall(\n argv: readonly string[],\n logger: Logger,\n overrideContinueDir?: string,\n): Promise<number> {\n let parsed: ReturnType<\n typeof parseArgs<{\n options: {\n target: { type: \"string\"; short: \"t\" };\n uninstall: { type: \"boolean\" };\n };\n }>\n >;\n\n try {\n parsed = parseArgs({\n args: [...argv],\n options: {\n target: { type: \"string\", short: \"t\" },\n uninstall: { type: \"boolean\" },\n },\n allowPositionals: false,\n strict: true,\n });\n } catch (err) {\n logger.error(`error: ${(err as Error).message}`);\n return 1;\n }\n\n // Resolve the Continue.dev config directory.\n // overrideContinueDir is the injection seam for tests; production uses ~/.continue/.\n const continueDir = overrideContinueDir ?? join(homedir(), \".continue\");\n const markerPath = join(continueDir, CONTINUE_HOOK_MARKER_FILENAME);\n\n try {\n mkdirSync(continueDir, { recursive: true });\n } catch (err) {\n logger.error(`error: cannot create ${continueDir}: ${String(err)}`);\n return 1;\n }\n\n // --- Uninstall path ---\n if (parsed.values.uninstall) {\n if (!isYakccInstalled(markerPath)) {\n logger.log(\"yakcc continue hook not installed — nothing to uninstall.\");\n return 0;\n }\n try {\n rmSync(markerPath);\n } catch (err) {\n logger.error(`error: cannot remove ${markerPath}: ${String(err)}`);\n return 1;\n }\n logger.log(`yakcc continue hook marker removed: ${markerPath}`);\n return 0;\n }\n\n // --- Install path ---\n if (isYakccInstalled(markerPath)) {\n logger.log(`yakcc continue hook already installed at ${markerPath} (idempotent).`);\n return 0;\n }\n\n const marker: ContinueMarker = {\n command: HOOK_COMMAND,\n description: \"yakcc tool-call interception hook for Continue.dev\",\n sessionEnvVar: \"CONTINUE_SESSION_ID\",\n telemetryPrefix: \"continue\",\n installedAt: new Date().toISOString(),\n _yakcc: YAKCC_CONTINUE_MARKER,\n note:\n \"Continue.dev (continue.continue) does not yet expose synchronous tool-call \" +\n \"interception via a stable Node.js API. This marker documents the intended \" +\n \"wiring (DEC-CLI-HOOKS-CONTINUE-INSTALL-001). When the Continue.dev API \" +\n \"stabilises, hook activation requires no reinstall.\",\n };\n\n try {\n writeFileSync(markerPath, `${JSON.stringify(marker, null, 2)}\\n`, \"utf-8\");\n } catch (err) {\n logger.error(`error: cannot write marker ${markerPath}: ${String(err)}`);\n return 1;\n }\n\n logger.log(`yakcc continue hook marker installed: ${markerPath}`);\n logger.log(\n \"note: Continue.dev tool-call interception API not yet stable — see marker for details.\",\n );\n return 0;\n}\n","// SPDX-License-Identifier: MIT\n//\n// seed-yakcc.ts — handler for `yakcc seed --yakcc`\n//\n// Imports the yakcc bootstrap corpus into the user's local registry. The source\n// is `bootstrap/expected-roots.json` plus the bootstrap SQLite at\n// `bootstrap/yakcc.registry.sqlite`. These are the real-shaved atoms produced\n// by `yakcc bootstrap` — never synthetic (never-synthetic cornerstone).\n//\n// @decision DEC-CLI-SEED-YAKCC-001\n// @title yakcc seed --yakcc: flag-gated bootstrap corpus import\n// @status accepted (WI-384 / issue #384)\n// @rationale\n// FLAG VS AUTO-SEED: DEC-CLI-INIT-001 explicitly decided \"no auto-seed —\n// yakcc init creates an empty registry and prints a next-step hint.\" This\n// implementation respects that decision: `--yakcc` is an explicit opt-in flag\n// on the existing `seed` command. Users who only want their own project's atoms\n// are unaffected by the default `yakcc seed` path. The flag gates the bootstrap\n// corpus import and is documented in USING_YAKCC.md § 4.\n//\n// CORPUS RESOLUTION (monorepo alpha): for v0.5.0-alpha.0, the binary runs\n// inside the monorepo clone, so the bootstrap sqlite is at\n// `${repoRoot}/bootstrap/yakcc.registry.sqlite`. `findRepoRoot()` walks upward\n// from `import.meta.url` looking for `pnpm-workspace.yaml` (monorepo marker).\n// Binary distribution follow-up: issue #361 (Wrath) must wire the corpus into\n// the packaged binary. Annotated there as a required follow-up.\n//\n// PER-ATOM BMR VERIFICATION: `storeBlock()` recomputes `blockMerkleRoot` from\n// the stored spec/impl/proof bytes (DEC-V1-FEDERATION-WIRE-ARTIFACTS-002\n// registry-side integrity gate). `validateOnStore: true` is the default and is\n// preserved here — we do NOT pass `validateOnStore: false`. If any block in the\n// bootstrap corpus fails verification (e.g. corrupted sqlite), the import stops\n// and fails loud. Silently skipping corrupt blocks is a forbidden shortcut.\n//\n// IDEMPOTENCY: `storeBlock()` uses INSERT OR IGNORE for the blocks table\n// (DEC-STORAGE-IDEMPOTENT-001). A second `yakcc seed --yakcc` on an already-seeded\n// registry is a no-op for existing blocks; new blocks (if the corpus was updated)\n// are imported.\n//\n// EMBEDDING: each imported block is re-embedded using the user's configured\n// embedding provider. The bootstrap sqlite was stored with a zero-vector provider\n// (DEC-V2-BOOTSTRAP-EMBEDDING-001). The user's registry gets real embeddings,\n// enabling semantic `yakcc query` to work correctly after import.\n\nimport { existsSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { BlockMerkleRoot } from \"@yakcc/contracts\";\nimport { type Registry, type RegistryOptions, openRegistry } from \"@yakcc/registry\";\nimport type { Logger } from \"../index.js\";\n\n// ---------------------------------------------------------------------------\n// Internal types\n// ---------------------------------------------------------------------------\n\n/**\n * Options for seedYakccCorpus. Mirrors SeedOptions in seed.ts for consistency.\n */\nexport interface SeedYakccOptions {\n /** Embedding provider forwarded to openRegistry. Tests inject offline provider. */\n embeddings?: RegistryOptions[\"embeddings\"];\n /**\n * Override the path to the bootstrap corpus sqlite.\n *\n * Production: omit — resolved automatically by findBootstrapSqlite().\n * Tests (worktree): pass the real path since the worktree has a different\n * root from the main repo checkout where bootstrap/ lives.\n *\n * @decision DEC-CLI-SEED-YAKCC-001: corpusPath override is the injection seam\n * for test isolation in git worktrees. It does NOT change the production\n * resolution path (import.meta.url-relative walk to repo root).\n */\n corpusPath?: string;\n}\n\n// ---------------------------------------------------------------------------\n// findBootstrapSqlite — locate bootstrap/yakcc.registry.sqlite\n//\n// @decision DEC-CLI-SEED-YAKCC-001 (corpus resolution)\n// Walks upward from this file's location checking each ancestor directory for\n// `bootstrap/yakcc.registry.sqlite`. This handles both the production case\n// (CLI running from packages/cli/dist/ inside the main checkout) and the git\n// worktree development case (CLI running inside .worktrees/<name>/ while\n// bootstrap/ lives in the main checkout above .worktrees/).\n//\n// The direct-walk approach is more robust than finding a \"repo root\" first,\n// because git worktrees have their own pnpm-workspace.yaml (identical copy)\n// while the bootstrap corpus is not duplicated into the worktree.\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the bootstrap SQLite path from the CLI module location.\n *\n * Walks upward from this compiled file's directory, checking each ancestor\n * for `bootstrap/yakcc.registry.sqlite`. Handles both:\n * - Main checkout: `packages/cli/dist/` → walk up → repo root has `bootstrap/`\n * - Git worktree: `.worktrees/<name>/packages/cli/dist/` → walk further up →\n * main checkout root (above `.worktrees/`) has `bootstrap/`\n *\n * @returns Absolute path to the bootstrap SQLite, or null if not found.\n */\nfunction findBootstrapSqlite(): string | null {\n let dir = dirname(fileURLToPath(import.meta.url));\n // Walk up to 30 levels — enough to reach the repo root from any nested path.\n for (let i = 0; i < 30; i++) {\n const candidate = join(dir, \"bootstrap\", \"yakcc.registry.sqlite\");\n if (existsSync(candidate)) return candidate;\n const parent = dirname(dir);\n if (parent === dir) break; // filesystem root reached\n dir = parent;\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// Zero-vector embedding provider — bootstrap source DB uses these\n// ---------------------------------------------------------------------------\n\n/**\n * Returns the embedding provider used when the bootstrap corpus was created.\n *\n * The bootstrap process stores atoms with a zero-vector embedding\n * (DEC-V2-BOOTSTRAP-EMBEDDING-001). When we open the bootstrap sqlite to READ\n * blocks from it, we must use the same zero provider so the registry's\n * embedding model ID matches the stored embeddings. The user's registry\n * receives real embeddings via the injected provider in storeBlock.\n */\nfunction makeZeroEmbeddingProvider(): RegistryOptions[\"embeddings\"] {\n return {\n dimension: 384,\n modelId: \"bootstrap/null-zero\",\n embed: async (_text: string): Promise<Float32Array> => new Float32Array(384),\n };\n}\n\n// ---------------------------------------------------------------------------\n// seedYakccCorpus — public command handler\n// ---------------------------------------------------------------------------\n\n/**\n * Import the yakcc bootstrap corpus into the target registry.\n *\n * Resolves the bootstrap SQLite, exports its manifest, hydrates each block via\n * getBlock(), and stores it into the user's registry via storeBlock(). All\n * blocks are subject to the storeBlock integrity gate (BMR recompute,\n * validateOnStore: true). Fails loud on the first corrupted block.\n *\n * The target registry MUST already be open (initialized) — this function does\n * not open or close it; the caller owns the registry lifecycle.\n *\n * @param registry - Open, initialized target registry.\n * @param opts - Options (embedding provider for the source bootstrap db).\n * @param logger - Output sink.\n * @returns Number of newly imported atoms (INSERT OR IGNORE; existing atoms\n * are counted as 0 new imports).\n */\nexport async function seedYakccCorpus(\n registry: Registry,\n opts: SeedYakccOptions,\n logger: Logger,\n): Promise<number> {\n // Locate the bootstrap sqlite.\n // opts.corpusPath overrides automatic resolution (used by tests in git worktrees\n // where the bootstrap/ dir lives in the main checkout, not the worktree root).\n const resolvedPath = opts.corpusPath ?? findBootstrapSqlite();\n if (resolvedPath === null) {\n throw new Error(\n \"yakcc seed --yakcc: bootstrap corpus not found. Expected bootstrap/yakcc.registry.sqlite relative to the repo root. \" +\n \"For monorepo-clone alpha installs, ensure you are running from within the yakcc repo. \" +\n \"Binary distribution follow-up: issue #361.\",\n );\n }\n const sqlitePath = resolvedPath;\n\n logger.log(`yakcc seed --yakcc: loading corpus from ${sqlitePath}`);\n\n // Open the bootstrap sqlite as a READ source. Use the zero-vector provider —\n // the bootstrap db was stored with that provider (DEC-V2-BOOTSTRAP-EMBEDDING-001).\n // We only READ from this db; the user's registry is the write target.\n let sourceRegistry: Registry;\n try {\n sourceRegistry = await openRegistry(sqlitePath, {\n embeddings: makeZeroEmbeddingProvider(),\n });\n } catch (err) {\n throw new Error(\n `yakcc seed --yakcc: failed to open bootstrap corpus at ${sqlitePath}: ${String(err)}`,\n );\n }\n\n // Export the manifest — returns all blockMerkleRoots in the bootstrap db.\n let manifest: readonly { blockMerkleRoot: string }[];\n try {\n manifest = await sourceRegistry.exportManifest();\n } catch (err) {\n await sourceRegistry.close();\n throw new Error(\n `yakcc seed --yakcc: failed to export manifest from bootstrap corpus: ${String(err)}`,\n );\n }\n\n logger.log(`yakcc seed --yakcc: manifest has ${manifest.length} entries — importing...`);\n\n let imported = 0;\n const skipped = 0;\n\n for (const entry of manifest) {\n const merkleRoot = entry.blockMerkleRoot as BlockMerkleRoot;\n\n // Hydrate the full block triplet from the source registry.\n let block: Awaited<ReturnType<typeof sourceRegistry.getBlock>>;\n try {\n block = await sourceRegistry.getBlock(merkleRoot);\n } catch (err) {\n await sourceRegistry.close();\n throw new Error(\n `yakcc seed --yakcc: failed to read block ${merkleRoot} from bootstrap corpus: ${String(err)}`,\n );\n }\n\n if (block === null) {\n // This should not happen (manifest entries should all be in the db), but\n // if it does, fail loud — silently skipping is a forbidden shortcut.\n await sourceRegistry.close();\n throw new Error(\n `yakcc seed --yakcc: manifest entry ${merkleRoot} not found in bootstrap corpus db. The corpus may be corrupted. Do not weaken this check.`,\n );\n }\n\n // Store into the user's registry.\n // storeBlock() runs validateOnStore: true by default — this is the per-atom\n // BMR verification required by the issue (DEC-CLI-SEED-YAKCC-001).\n // If validation fails, the error propagates up and the import stops loudly.\n try {\n await registry.storeBlock(block);\n imported++;\n } catch (err) {\n const e = err as Error & { reason?: string };\n if (e.reason === \"integrity_failed\") {\n // Block failed BMR recompute — corrupted in the bootstrap corpus.\n // Fail loud immediately (forbidden shortcut: silent skip).\n await sourceRegistry.close();\n throw new Error(\n `yakcc seed --yakcc: BMR integrity check failed for block ${merkleRoot}. The bootstrap corpus may be corrupted. Import aborted. Original error: ${e.message}`,\n );\n }\n // Other errors (e.g. DB write failure) also propagate loudly.\n await sourceRegistry.close();\n throw new Error(`yakcc seed --yakcc: failed to store block ${merkleRoot}: ${String(err)}`);\n }\n }\n\n await sourceRegistry.close();\n\n // storeBlock uses INSERT OR IGNORE, so `imported` counts all store calls that\n // succeeded (including no-ops for already-present blocks). We can't distinguish\n // new vs existing without a pre-check, but that's acceptable — the idempotency\n // guarantee is that re-running produces the same registry state (DEC-STORAGE-IDEMPOTENT-001).\n // The count shown is \"processed\" atoms from the bootstrap corpus.\n logger.log(\n `yakcc seed --yakcc: imported ${imported} atoms from bootstrap corpus (${skipped} skipped)`,\n );\n return imported;\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CLI-PROPOSE-001: propose reads a JSON SpecYak file, derives its\n// specHash, then calls registry.selectBlocks(specHash) for exact lookup. On hit:\n// prints the matched merkle root and exits 0. On miss: prints a manual-authoring\n// template. Both paths exit 0.\n// Status: updated (WI-T05)\n// Rationale: WI-T03 removed registry.match() and ContractId. The exact-match path\n// now uses specHash() + selectBlocks() — the canonical T03 lookup API.\n// v0 has no AI synthesis; an unmatched proposal kicks the manual-authoring\n// flow (DEC-V0-SYNTH-003).\n\nimport { readFileSync } from \"node:fs\";\nimport { parseArgs } from \"node:util\";\nimport { type SpecYak, parseGranularity, specHash } from \"@yakcc/contracts\";\nimport { type Registry, openRegistry } from \"@yakcc/registry\";\nimport type { Logger } from \"../index.js\";\nimport { DEFAULT_REGISTRY_PATH } from \"./registry-init.js\";\n\n/**\n * Handler for `yakcc propose <spec-file> [--registry <p>]`.\n *\n * Reads a JSON SpecYak, derives its specHash, and checks the registry for\n * an exact match via selectBlocks. Prints either `match: <merkleRoot>` or a\n * manual-authoring template.\n *\n * @param argv - Remaining argv after `propose` has been consumed (includes the positional).\n * @param logger - Output sink; defaults to console via the caller.\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function propose(argv: readonly string[], logger: Logger): Promise<number> {\n const { values, positionals } = parseArgs({\n args: [...argv],\n options: {\n registry: { type: \"string\", short: \"r\" },\n granularity: { type: \"string\", short: \"g\" },\n },\n allowPositionals: true,\n strict: true,\n });\n\n const specFilePath = positionals[0];\n if (specFilePath === undefined || specFilePath === \"\") {\n logger.error(\"error: propose requires a <contract-file> argument\");\n return 1;\n }\n\n const granularityRaw = values.granularity;\n if (granularityRaw !== undefined && parseGranularity(granularityRaw) === null) {\n logger.error(\n `error: --granularity must be an integer between 1 and 5 (got ${JSON.stringify(granularityRaw)})`,\n );\n return 1;\n }\n\n const registryPath = values.registry ?? DEFAULT_REGISTRY_PATH;\n\n // Read and parse the spec file.\n let specJson: string;\n try {\n specJson = readFileSync(specFilePath, \"utf-8\");\n } catch (err) {\n logger.error(`error: cannot read spec file ${specFilePath}: ${String(err)}`);\n return 1;\n }\n\n let spec: SpecYak;\n try {\n spec = JSON.parse(specJson) as SpecYak;\n } catch (err) {\n logger.error(`error: invalid JSON in spec file ${specFilePath}: ${String(err)}`);\n return 1;\n }\n\n const hash = specHash(spec);\n\n // Open the registry and check for a match.\n let registry: Registry;\n try {\n registry = await openRegistry(registryPath);\n } catch (err) {\n logger.error(`error: failed to open registry at ${registryPath}: ${String(err)}`);\n return 1;\n }\n\n try {\n const roots = await registry.selectBlocks(hash);\n\n if (roots.length > 0) {\n // Return the best matching block (first in selection order).\n logger.log(`match: ${roots[0]}`);\n return 0;\n }\n\n // No match — print a manual-authoring template.\n logger.log(`no match found for contract ${hash}`);\n logger.log(\"\");\n logger.log(\"spec:\");\n logger.log(JSON.stringify(spec, null, 2));\n logger.log(\"\");\n logger.log(\"To register an implementation, author a block triplet (spec.yak, impl.ts, proof/)\");\n logger.log(\"matching the spec above, then seed the registry.\");\n return 0;\n } finally {\n await registry.close();\n }\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CLI-QUERY-001\n// title: yakcc query command — vector-search CLI surface\n// status: accepted\n// rationale: WI-025 adds a semantic vector-search path to the registry. The\n// `query` command exposes findCandidatesByIntent() from @yakcc/registry at\n// the CLI surface, parallel to the existing `search` command (which does\n// linear-scan structural matching). `query` is the right name because it\n// operates on an intent query (semantic/embedding-based), while `search`\n// operates on structural matching. Both commands remain; they complement\n// rather than replace each other. Free-text queries are converted to a\n// minimal IntentQuery (behavior-only, no inputs/outputs) so callers can\n// type `yakcc query \"parse integer from string\"` without authoring a spec.\n// Card-file input allows programmatic callers (e.g. WI-026 hook) to pass\n// a serialized IntentCard directly.\n\nimport { readFileSync } from \"node:fs\";\nimport { parseArgs } from \"node:util\";\nimport { type RegistryOptions, openRegistry } from \"@yakcc/registry\";\nimport type { IntentQuery } from \"@yakcc/registry\";\nimport type { Logger } from \"../index.js\";\nimport { DEFAULT_REGISTRY_PATH } from \"./registry-init.js\";\n\n/** Internal options for query — not exposed in CLI args. */\nexport interface QueryOptions {\n embeddings?: RegistryOptions[\"embeddings\"];\n}\n\n/**\n * Truncate a string to at most `max` characters, appending \"...\" if truncated.\n */\nfunction truncate(s: string, max: number): string {\n return s.length <= max ? s : `${s.slice(0, max - 3)}...`;\n}\n\n/**\n * Handler for `yakcc query <query> [--top k] [--rerank] [--registry <p>] [--card-file <f>]`.\n *\n * <query> is a free-text behavior string (ignored when --card-file is given).\n * Prints ranked results as one line per match:\n * <rank>. cosine=<dist> [structural=<score>] block=<merkleRoot[:12]> behavior=\"<truncated>\"\n *\n * @param argv - Remaining argv after `query` has been consumed (includes the positional).\n * @param logger - Output sink; defaults to CONSOLE_LOGGER via the caller.\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function query(\n argv: readonly string[],\n logger: Logger,\n opts?: QueryOptions,\n): Promise<number> {\n const { values, positionals } = parseArgs({\n args: [...argv],\n options: {\n registry: { type: \"string\", short: \"r\" },\n top: { type: \"string\", short: \"k\" },\n rerank: { type: \"boolean\" },\n \"card-file\": { type: \"string\" },\n },\n allowPositionals: true,\n strict: true,\n });\n\n const registryPath = values.registry ?? DEFAULT_REGISTRY_PATH;\n const topRaw = values.top ?? \"10\";\n const top = Number.parseInt(topRaw, 10);\n if (Number.isNaN(top) || top < 1) {\n logger.error(`error: --top must be a positive integer, got: ${topRaw}`);\n return 1;\n }\n const rerank = values.rerank === true ? \"structural\" : \"none\";\n\n // Resolve the intent query.\n let intentQuery: IntentQuery;\n\n const cardFilePath = values[\"card-file\"];\n if (cardFilePath !== undefined) {\n // --card-file: parse a JSON IntentCard (or IntentQuery) from disk.\n let cardJson: string;\n try {\n cardJson = readFileSync(cardFilePath, \"utf-8\");\n } catch (err) {\n logger.error(`error: cannot read card file ${cardFilePath}: ${String(err)}`);\n return 1;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(cardJson);\n } catch (err) {\n logger.error(`error: invalid JSON in card file ${cardFilePath}: ${String(err)}`);\n return 1;\n }\n if (\n parsed === null ||\n typeof parsed !== \"object\" ||\n !(\"behavior\" in parsed) ||\n typeof (parsed as Record<string, unknown>).behavior !== \"string\"\n ) {\n logger.error(\n `error: card file ${cardFilePath} must have a \"behavior\" string field (IntentCard or IntentQuery shape)`,\n );\n return 1;\n }\n const card = parsed as Record<string, unknown>;\n intentQuery = {\n behavior: card.behavior as string,\n inputs: Array.isArray(card.inputs) ? (card.inputs as IntentQuery[\"inputs\"]) : [],\n outputs: Array.isArray(card.outputs) ? (card.outputs as IntentQuery[\"outputs\"]) : [],\n };\n } else {\n // Free text: positional is the behavior string.\n const queryText = positionals[0];\n if (queryText === undefined || queryText === \"\") {\n logger.error(\"error: query requires a <query> argument (free text) or --card-file <path>\");\n return 1;\n }\n // Synthesize a minimal IntentQuery with behavior only (no inputs/outputs).\n intentQuery = { behavior: queryText, inputs: [], outputs: [] };\n }\n\n // Open the registry and run the vector search.\n const registry = await openRegistry(registryPath, { embeddings: opts?.embeddings }).catch(\n (err: unknown) => {\n logger.error(`error: failed to open registry at ${registryPath}: ${String(err)}`);\n return null;\n },\n );\n if (registry === null) return 1;\n\n try {\n const results = await registry.findCandidatesByIntent(intentQuery, {\n k: top,\n rerank,\n });\n\n if (results.length === 0) {\n logger.log(\"no results found\");\n return 0;\n }\n\n for (let i = 0; i < results.length; i++) {\n const r = results[i];\n if (r === undefined) continue;\n\n // Extract behavior string from the spec canonical bytes for display.\n let behaviorStr = \"\";\n try {\n const spec = JSON.parse(Buffer.from(r.block.specCanonicalBytes).toString(\"utf-8\")) as {\n behavior?: string;\n };\n behaviorStr = spec.behavior ?? r.block.specHash;\n } catch {\n behaviorStr = r.block.specHash;\n }\n\n const rank = i + 1;\n const cosStr = r.cosineDistance.toFixed(6);\n const blockShort = r.block.blockMerkleRoot.slice(0, 12);\n const behaviorTrunc = truncate(behaviorStr, 72);\n\n if (r.structuralScore !== undefined) {\n const structStr = r.structuralScore.toFixed(4);\n logger.log(\n `${rank}. cosine=${cosStr} structural=${structStr} block=${blockShort} behavior=\"${behaviorTrunc}\"`,\n );\n } else {\n logger.log(`${rank}. cosine=${cosStr} block=${blockShort} behavior=\"${behaviorTrunc}\"`);\n }\n }\n\n return 0;\n } finally {\n await registry.close();\n }\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CLI-REGISTRY-EXPORT-001: registry-export uses SQLite VACUUM INTO for a\n// clean, defragmented, portable copy of the source registry. The output is signing-ready\n// for the registry.yakcc.com deploy pipeline (#371).\n// Status: implemented (#371 Slice 2)\n// Rationale: VACUUM INTO is the SQLite-canonical way to produce a portable copy. It\n// compacts free pages, normalizes the file format, and is faster than dump-and-restore\n// for our typical registry sizes. Single-quote escaping in the inlined output path is\n// required because VACUUM INTO does not support SQL parameter binding for filenames.\n//\n// Better-sqlite3 access: pnpm isolates better-sqlite3 to @yakcc/registry's node_modules.\n// We resolve it via createRequire anchored to the registry package's own source root so\n// that both production CJS and vitest ESM (which aliases @yakcc/registry to source) follow\n// the same resolution path. This avoids adding better-sqlite3 as a direct CLI dep.\n// Same approach as packages/cli/src/bin.ts (createRequire pattern).\n\nimport { existsSync, mkdirSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, resolve } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { Logger } from \"../index.js\";\nimport { DEFAULT_REGISTRY_PATH } from \"./registry-init.js\";\n\n// Resolve better-sqlite3 from @yakcc/registry's package root, where it is a\n// declared direct dependency. The URL is constructed relative to this source\n// file so it correctly navigates to the registry package in the monorepo tree.\n// Under vitest source-alias mode this still resolves to the same physical\n// node_modules/better-sqlite3 that openRegistry uses at runtime.\n//\n// Path breakdown from packages/cli/src/commands/ (this file's directory):\n// ../ → packages/cli/src/\n// ../../ → packages/cli/\n// ../../../ → packages/ ← need THREE levels up to reach the monorepo root\n// ../../../registry/src/index.ts → packages/registry/src/index.ts ✓\nconst _registryModuleUrl = new URL(\"../../../registry/src/index.ts\", import.meta.url).href;\nconst _req = createRequire(_registryModuleUrl);\n\n// Minimal structural interface — only the methods we call are typed.\n// Avoids depending on @types/better-sqlite3 which is outside CLI's type scope.\ntype MinimalDb = {\n prepare(sql: string): { get(): unknown };\n exec(sql: string): void;\n close(): void;\n};\n\n/**\n * Handler for `yakcc registry export --to <path> [--from <path>]`.\n *\n * Exports the source registry as a clean canonical SQLite via VACUUM INTO.\n * The output is portable, defragmented, and ready for upload to a federation\n * peer (e.g. registry.yakcc.com). The source is opened read-write because\n * VACUUM INTO requires an exclusive lock, but no row mutations occur.\n *\n * @param argv - Remaining argv after `registry export` has been consumed.\n * @param logger - Output sink.\n * @returns Process exit code (0 success, 1 error).\n */\nexport async function registryExport(argv: readonly string[], logger: Logger): Promise<number> {\n const { values } = parseArgs({\n args: [...argv],\n options: {\n to: { type: \"string\" },\n from: { type: \"string\", default: DEFAULT_REGISTRY_PATH },\n },\n strict: true,\n allowPositionals: false,\n });\n\n if (values.to === undefined) {\n logger.error(\"error: --to <path> is required for 'registry export'\");\n return 1;\n }\n\n const sourcePath = resolve(values.from as string);\n const outputPath = resolve(values.to);\n\n if (!existsSync(sourcePath)) {\n logger.error(`error: source registry not found at ${sourcePath}`);\n return 1;\n }\n\n mkdirSync(dirname(outputPath), { recursive: true });\n\n // VACUUM INTO does not accept SQL parameter binding for filenames; the path\n // is inlined. Escape any single quotes in the path by doubling them per\n // SQLite's string-literal rules.\n const escapedOutputPath = outputPath.replace(/'/g, \"''\");\n\n // Load the native binding. Failure here means the pnpm isolation path is broken\n // or the native addon was not built — emit a clear message before giving up.\n let Database: { new (path: string): MinimalDb };\n try {\n // createRequire returns `unknown`; we extract the constructor through a\n // typed intermediate to avoid a bare `any` cast that Biome rejects.\n type BsqliteModule = {\n default?: { new (path: string): MinimalDb };\n new?(path: string): MinimalDb;\n };\n const mod = _req(\"better-sqlite3\") as BsqliteModule;\n Database = (mod.default ?? mod) as { new (path: string): MinimalDb };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(`error: could not load better-sqlite3 native binding: ${msg}`);\n return 1;\n }\n\n // Open the source registry. Failure here means the file is not a valid SQLite\n // database (e.g. corrupted header) despite passing the existsSync check above.\n let db: MinimalDb;\n try {\n db = new Database(sourcePath);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(`error: could not open source registry at ${sourcePath}: ${msg}`);\n return 1;\n }\n\n try {\n // Optional: report block count for operator visibility. Wrapped in try/catch\n // so an unexpected schema variant doesn't fail the export.\n let blockCount: number | null = null;\n try {\n const row = db.prepare(\"SELECT count(*) as n FROM blocks\").get() as { n: number } | undefined;\n if (row && typeof row.n === \"number\") blockCount = row.n;\n } catch (_) {\n // canonical table name differs in schema variants; skip count silently\n }\n\n db.exec(`VACUUM INTO '${escapedOutputPath}'`);\n\n const suffix = blockCount === null ? \"\" : ` (${blockCount} blocks)`;\n logger.log(`registry exported${suffix}: ${sourcePath} → ${outputPath}`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n logger.error(`error: VACUUM INTO failed (${outputPath}): ${msg}`);\n return 1;\n } finally {\n db.close();\n }\n\n return 0;\n}\n","// SPDX-License-Identifier: MIT\n//\n// @decision DEC-EMBED-MODEL-MIGRATION-001\n// title: `yakcc registry rebuild` command — embedding model migration path\n// status: accepted (issue #338, WI-EMBED-MODEL-MIGRATION-PATH)\n// rationale: After the bge-small-en-v1.5 swap (DEC-EMBED-MODEL-DEFAULT-002, PR #336),\n// existing registries contain embeddings from the old model (all-MiniLM-L6-v2).\n// This command re-embeds all stored blocks using the current provider, restoring\n// consistency without data loss. Atoms (BlockTripletRow data) are preserved byte-for-byte;\n// only the contract_embeddings rows (derived index) are regenerated.\n//\n// Implementation: opens the registry with the current default provider, calls\n// rebuildRegistry() from @yakcc/registry, and reports progress + final count.\n//\n// Design constraints:\n// - Idempotent: safe to run twice.\n// - Same-dimension only (384→384): no schema changes; DDL for contract_embeddings\n// is unchanged. Cross-dimension migration is out of scope for this WI.\n// - DEC-EMBED-010 preserved: after rebuild, the registry's vectors are consistent\n// with the provider; the cross-provider rejection gate will pass for this provider.\n// - NOT silent: reports progress and final count. Fail loudly on error.\n\nimport { mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport {\n type Registry,\n type RegistryOptions,\n openRegistry,\n rebuildRegistry,\n} from \"@yakcc/registry\";\nimport type { Logger } from \"../index.js\";\nimport { DEFAULT_REGISTRY_PATH } from \"./registry-init.js\";\n\n/**\n * Internal options for registryRebuild — not exposed as CLI flags.\n */\nexport interface RegistryRebuildOptions {\n /** Embedding provider forwarded to openRegistry. Tests inject createOfflineEmbeddingProvider(). */\n embeddings?: RegistryOptions[\"embeddings\"];\n}\n\n/**\n * Handler for `yakcc registry rebuild [--path <p>]`.\n *\n * Re-embeds all blocks in an existing registry using the current default embedding\n * provider. Useful after a model swap (DEC-EMBED-MODEL-DEFAULT-002) to migrate\n * existing registries to the new model without data loss.\n *\n * - Preserves all BlockTripletRow data byte-for-byte.\n * - Regenerates contract_embeddings vectors using the current provider.\n * - Reports block count and progress to the logger.\n * - Idempotent: safe to run multiple times.\n *\n * Exit codes:\n * - 0: success (registry rebuilt)\n * - 1: usage or runtime error\n *\n * @param argv - Remaining argv after `registry rebuild` has been consumed.\n * @param logger - Output sink; defaults to console via the caller.\n * @param opts - Internal options (embeddings for test injection).\n */\nexport async function registryRebuild(\n argv: readonly string[],\n logger: Logger,\n opts?: RegistryRebuildOptions,\n): Promise<number> {\n const { values } = parseArgs({\n args: [...argv],\n options: {\n path: { type: \"string\", short: \"p\" },\n },\n allowPositionals: false,\n strict: true,\n });\n\n const registryPath = values.path ?? DEFAULT_REGISTRY_PATH;\n\n // Ensure parent directory exists (mirrors registry-init pattern).\n const parent = dirname(registryPath);\n mkdirSync(parent, { recursive: true });\n\n // Resolve the embedding provider to use for rebuilding.\n // If tests inject a provider, use it; otherwise the default (bge-small-en-v1.5)\n // is loaded lazily inside openRegistry.\n let embeddingProvider = opts?.embeddings;\n if (embeddingProvider === undefined) {\n // Load the default provider so we can pass it to rebuildRegistry() for modelId reporting.\n const { createLocalEmbeddingProvider } = await import(\"@yakcc/contracts\");\n embeddingProvider = createLocalEmbeddingProvider();\n }\n\n let registry: Registry;\n try {\n registry = await openRegistry(registryPath, { embeddings: embeddingProvider });\n } catch (err) {\n logger.error(`error: failed to open registry at ${registryPath}: ${String(err)}`);\n return 1;\n }\n\n let lastReported = -1;\n try {\n logger.log(`rebuilding registry at ${registryPath}…`);\n\n const result = await rebuildRegistry(registry, embeddingProvider, {\n onProgress(done, total) {\n // Report every 100 blocks to avoid log spam on large registries.\n const pct = Math.floor((done / total) * 100);\n if (pct !== lastReported && pct % 10 === 0) {\n lastReported = pct;\n logger.log(` ${done}/${total} blocks re-embedded (${pct}%)`);\n }\n },\n });\n\n logger.log(\n `registry rebuilt: ${result.reembedded} blocks re-embedded with model ${result.modelId} at ${registryPath}`,\n );\n return 0;\n } catch (err) {\n logger.error(`error: registry rebuild failed: ${String(err)}`);\n return 1;\n } finally {\n await registry.close();\n }\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CLI-SEARCH-001: search accepts either a path to a JSON SpecYak file\n// (search by spec) or free text (search by behavior string with other fields stubbed).\n// Results are printed as \"<merkleRoot[:8]> score=<float> behavior=<truncated-80>\" lines.\n// Status: updated (WI-T05)\n// Rationale: WI-T03 removed registry.search() and the ContractSpec-based vector search.\n// The search command now uses seedRegistry() to enumerate all corpus blocks, calls\n// structuralMatch(query, candidate) for each, and returns top-K by score. This is a\n// linear scan over the seed corpus (20 blocks); acceptable for v0.6 scope.\n// The v0 has no embedding-based search; a corpus-scan with structural scoring\n// is the correct minimal implementation (DEC-V0-SYNTH-003).\n\nimport { readFileSync } from \"node:fs\";\nimport { parseArgs } from \"node:util\";\nimport { parseGranularity } from \"@yakcc/contracts\";\nimport type { SpecYak } from \"@yakcc/contracts\";\nimport { type RegistryOptions, openRegistry, structuralMatch } from \"@yakcc/registry\";\nimport { seedRegistry } from \"@yakcc/seeds\";\nimport type { Logger } from \"../index.js\";\nimport { DEFAULT_REGISTRY_PATH } from \"./registry-init.js\";\n\n/** Internal options for search — not exposed in CLI args. */\nexport interface SearchOptions {\n embeddings?: RegistryOptions[\"embeddings\"];\n}\n\n/** Stub non-functional properties used for free-text search queries. */\nconst FREE_TEXT_SPEC_DEFAULTS: Partial<SpecYak> = {\n inputs: [],\n outputs: [],\n preconditions: [],\n postconditions: [],\n invariants: [],\n effects: [],\n guarantees: [],\n errorConditions: [],\n nonFunctional: { purity: \"pure\", threadSafety: \"safe\" },\n propertyTests: [],\n};\n\n/**\n * Truncate a string to at most `max` characters, appending \"…\" if truncated.\n */\nfunction truncate(s: string, max: number): string {\n return s.length <= max ? s : `${s.slice(0, max - 1)}…`;\n}\n\n/**\n * Handler for `yakcc search <query> [--registry <p>] [--top <k>]`.\n *\n * <query> is either a path to a JSON SpecYak file or a free-text behavior string.\n * Prints top-K results as \"<merkleRoot[:8]> score=<float> behavior=<truncated>\" lines.\n *\n * @param argv - Remaining argv after `search` has been consumed (includes the positional).\n * @param logger - Output sink; defaults to console via the caller.\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function search(\n argv: readonly string[],\n logger: Logger,\n opts?: SearchOptions,\n): Promise<number> {\n const { values, positionals } = parseArgs({\n args: [...argv],\n options: {\n registry: { type: \"string\", short: \"r\" },\n top: { type: \"string\", short: \"k\" },\n granularity: { type: \"string\", short: \"g\" },\n },\n allowPositionals: true,\n strict: true,\n });\n\n const query = positionals[0];\n if (query === undefined || query === \"\") {\n logger.error(\"error: search requires a <query> argument (spec file path or free text)\");\n return 1;\n }\n\n const granularityRaw = values.granularity;\n if (granularityRaw !== undefined && parseGranularity(granularityRaw) === null) {\n logger.error(\n `error: --granularity must be an integer between 1 and 5 (got ${JSON.stringify(granularityRaw)})`,\n );\n return 1;\n }\n\n const registryPath = values.registry ?? DEFAULT_REGISTRY_PATH;\n const topRaw = values.top ?? \"10\";\n const top = Number.parseInt(topRaw, 10);\n if (Number.isNaN(top) || top < 1) {\n logger.error(`error: --top must be a positive integer, got: ${topRaw}`);\n return 1;\n }\n\n // Resolve the query to a SpecYak.\n let querySpec: SpecYak;\n\n // Heuristic: if the query ends with .json or looks like a file path, treat as spec file.\n const looksLikePath = query.endsWith(\".json\") || query.startsWith(\"./\") || query.startsWith(\"/\");\n\n if (looksLikePath) {\n let specJson: string;\n try {\n specJson = readFileSync(query, \"utf-8\");\n } catch (err) {\n logger.error(`error: cannot read spec file ${query}: ${String(err)}`);\n return 1;\n }\n try {\n querySpec = JSON.parse(specJson) as SpecYak;\n } catch (err) {\n logger.error(`error: invalid JSON in spec file ${query}: ${String(err)}`);\n return 1;\n }\n } else {\n // Free text — construct a minimal spec with behavior=<query>.\n querySpec = {\n ...FREE_TEXT_SPEC_DEFAULTS,\n behavior: query,\n name: \"query\",\n level: \"L0\",\n } as SpecYak;\n }\n\n // Open the registry, seed it, then scan all corpus blocks for structural matches.\n const registry = await openRegistry(registryPath, { embeddings: opts?.embeddings }).catch(\n (err: unknown) => {\n logger.error(`error: failed to open registry at ${registryPath}: ${String(err)}`);\n return null;\n },\n );\n if (registry === null) return 1;\n\n try {\n const seedResult = await seedRegistry(registry);\n\n // Score each block's spec against the query spec using structuralMatch.\n const scored: Array<{ root: string; score: number; behavior: string }> = [];\n for (const merkleRoot of seedResult.merkleRoots) {\n const row = await registry.getBlock(merkleRoot);\n if (row === null) continue;\n // Parse the spec from canonical bytes.\n let blockSpec: SpecYak;\n try {\n blockSpec = JSON.parse(Buffer.from(row.specCanonicalBytes).toString(\"utf-8\")) as SpecYak;\n } catch {\n continue;\n }\n const matchResult = structuralMatch(querySpec, blockSpec);\n // structuralMatch returns { matches: true } for exact/superset hits or\n // { matches: false, reasons } for mismatches. Score by match quality.\n // v0 scoring: any structural hit scores 1.0; mismatch scores 0 (excluded).\n const score = matchResult.matches ? 1.0 : 0;\n if (score > 0) {\n scored.push({ root: merkleRoot, score, behavior: blockSpec.behavior ?? \"\" });\n }\n }\n\n // Sort by descending score, then take top-K.\n scored.sort((a, b) => b.score - a.score);\n const results = scored.slice(0, top);\n\n if (results.length === 0) {\n logger.log(\"no results found\");\n return 0;\n }\n\n for (const { root, score, behavior } of results) {\n const behaviorStr = truncate(behavior, 80);\n const scoreStr = score.toFixed(4);\n logger.log(`${root.slice(0, 8)} score=${scoreStr} behavior=${behaviorStr}`);\n }\n\n return 0;\n } finally {\n await registry.close();\n }\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CLI-SEED-001: seed opens the registry and delegates to seedRegistry()\n// from @yakcc/seeds. seedRegistry() is idempotent (INSERT OR IGNORE), so running seed\n// on an already-seeded registry is safe. Prints the stored count and a truncated list\n// of block merkle roots, then exits 0.\n// Status: updated (WI-T05, WI-384)\n// Rationale: WI-T05 migrated SeedResult from contractIds: ContractId[] to\n// merkleRoots: BlockMerkleRoot[] (T03/T04 API). Display updated accordingly.\n// WI-384 adds --yakcc flag: when present, delegates to seedYakccCorpus() which\n// imports the in-tree bootstrap corpus (~3k+ atoms) instead of the 20-block seed corpus.\n// No author/ownership fields touched — DEC-NO-OWNERSHIP-011.\n\nimport { parseArgs } from \"node:util\";\nimport { type Registry, type RegistryOptions, openRegistry } from \"@yakcc/registry\";\nimport { seedRegistry } from \"@yakcc/seeds\";\nimport type { Logger } from \"../index.js\";\nimport { DEFAULT_REGISTRY_PATH } from \"./registry-init.js\";\nimport { seedYakccCorpus } from \"./seed-yakcc.js\";\n\n/** Maximum number of merkle roots to print in the summary line. */\nconst MAX_ROOTS_SHOWN = 3;\n\n/** Internal options for seed — not exposed in CLI args. */\nexport interface SeedOptions {\n /** Embedding provider forwarded to openRegistry. Tests inject createOfflineEmbeddingProvider(). */\n embeddings?: RegistryOptions[\"embeddings\"];\n /**\n * Override path to the bootstrap corpus sqlite for --yakcc mode.\n *\n * Production: omit — resolved automatically by findBootstrapSqlite() in seed-yakcc.ts.\n * Tests (git worktree): pass the real path since the worktree root differs\n * from the main checkout where bootstrap/ lives.\n * Forwarded directly to seedYakccCorpus() (DEC-CLI-SEED-YAKCC-001).\n */\n corpusPath?: string;\n}\n\n/**\n * Handler for `yakcc seed [--yakcc] [--registry <p>]`.\n *\n * Without --yakcc: opens the registry and calls seedRegistry() to ingest all\n * seed corpus blocks (the original 20-block parse-int-list seed).\n *\n * With --yakcc: imports the in-tree bootstrap corpus (~3k+ real-shaved atoms)\n * via seedYakccCorpus(). See DEC-CLI-SEED-YAKCC-001 in seed-yakcc.ts.\n *\n * Both paths are idempotent (INSERT OR IGNORE; DEC-STORAGE-IDEMPOTENT-001).\n *\n * @param argv - Remaining argv after `seed` has been consumed.\n * @param logger - Output sink; defaults to console via the caller.\n * @param opts - Internal options (embeddings for test injection).\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function seed(\n argv: readonly string[],\n logger: Logger,\n opts?: SeedOptions,\n): Promise<number> {\n const { values } = parseArgs({\n args: [...argv],\n options: {\n registry: { type: \"string\", short: \"r\" },\n yakcc: { type: \"boolean\", default: false },\n },\n allowPositionals: false,\n strict: true,\n });\n\n const registryPath = values.registry ?? DEFAULT_REGISTRY_PATH;\n const useYakccCorpus = values.yakcc === true;\n\n let registry: Registry;\n try {\n registry = await openRegistry(registryPath, { embeddings: opts?.embeddings });\n } catch (err) {\n logger.error(`error: failed to open registry at ${registryPath}: ${String(err)}`);\n return 1;\n }\n\n try {\n if (useYakccCorpus) {\n // --yakcc flag: import the bootstrap corpus (DEC-CLI-SEED-YAKCC-001).\n const yakccOpts: import(\"./seed-yakcc.js\").SeedYakccOptions = {};\n if (opts?.embeddings !== undefined) yakccOpts.embeddings = opts.embeddings;\n if (opts?.corpusPath !== undefined) yakccOpts.corpusPath = opts.corpusPath;\n const imported = await seedYakccCorpus(registry, yakccOpts, logger);\n logger.log(`yakcc seed --yakcc: done — ${imported} atoms processed from bootstrap corpus`);\n return 0;\n }\n\n // Default path: ingest the 20-block seed corpus from @yakcc/seeds.\n const result = await seedRegistry(registry);\n\n // Show abbreviated roots (first 8 hex chars each) for readability.\n const shown = result.merkleRoots.slice(0, MAX_ROOTS_SHOWN).map((r) => r.slice(0, 8));\n const rest = result.merkleRoots.length - shown.length;\n const rootList = rest > 0 ? `${shown.join(\", \")}, … (+${rest} more)` : shown.join(\", \");\n\n logger.log(`seeded ${result.stored} contracts; ids: ${rootList}`);\n return 0;\n } catch (err) {\n logger.error(`error: seed failed: ${String(err)}`);\n return 1;\n } finally {\n await registry.close();\n }\n}\n","// SPDX-License-Identifier: MIT\n// @decision DEC-CLI-SHAVE-001: shave command wraps @yakcc/shave.shave() for CLI consumption.\n// Opens the registry via @yakcc/registry.openRegistry(), delegates all pipeline logic\n// to shaveImpl(), and prints a human-readable summary. Error paths follow the\n// established pattern from seed.ts and compile.ts: catch, log to logger.error(), return 1.\n// Status: updated (WI-V2-04 L5: foreign-policy gate output added)\n// Rationale: Keeps the CLI layer thin — argument parsing, registry open/close, and\n// output formatting live here; pipeline logic stays in @yakcc/shave. Matches the\n// `(argv, logger) → Promise<number>` contract shared by all yakcc commands.\n//\n// L5 additions:\n// - 'reject' policy: shaveImpl() throws ForeignPolicyRejectError; caught here,\n// formatted to stderr (\"error: shave failed: foreign-policy reject: pkg#export,...\"),\n// returns exit code 1. (L5-I3)\n// - 'tag' policy: shaveImpl() returns ShaveResultWithForeign.foreignDeps;\n// when non-empty, emit \"foreign deps: pkg#export[, ...]\" to stdout. (L5-I4)\n// - 'allow' policy: no change — shaveImpl() returns no foreignDeps. (L5-I5)\n\nimport { resolve } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { Registry } from \"@yakcc/registry\";\nimport { openRegistry } from \"@yakcc/registry\";\nimport {\n FOREIGN_POLICY_DEFAULT,\n type ForeignPolicy,\n ForeignPolicyRejectError,\n shave as shaveImpl,\n} from \"@yakcc/shave\";\nimport type { Logger } from \"../index.js\";\n\n/** Valid values for --foreign-policy. */\nconst VALID_FOREIGN_POLICIES: readonly ForeignPolicy[] = [\"allow\", \"reject\", \"tag\"];\n\n/** Argument options descriptor for parseArgs — typed inline to avoid implicit any. */\nconst SHAVE_PARSE_OPTIONS = {\n registry: { type: \"string\" },\n offline: { type: \"boolean\", default: false },\n help: { type: \"boolean\", short: \"h\", default: false },\n \"foreign-policy\": { type: \"string\" },\n} as const;\n\n/**\n * Handler for `yakcc shave <path> [--registry <p>] [--offline] [--foreign-policy <policy>]`.\n *\n * Shaves a TypeScript source file: reads it, runs through the universalizer\n * (license gate → intent extraction → decompose → slice), and prints a summary\n * of the ShaveResult. The atoms array (each with placeholderId + sourceRange) is\n * printed; intent cards count and diagnostics are surfaced.\n *\n * @param argv - Subcommand args after \"shave\" has been consumed (positional path + flags).\n * @param logger - Output sink; defaults to CONSOLE_LOGGER via the caller.\n * @returns Promise<number> — 0 on success, 1 on error.\n */\nexport async function shave(argv: ReadonlyArray<string>, logger: Logger): Promise<number> {\n // Parse arguments — parseArgs throws on unknown flags, so wrap in try/catch.\n const parsed = (() => {\n try {\n return parseArgs({\n args: [...argv],\n allowPositionals: true,\n options: SHAVE_PARSE_OPTIONS,\n });\n } catch (err) {\n logger.error(`error: ${(err as Error).message}`);\n return null;\n }\n })();\n if (parsed === null) return 1;\n\n if (parsed.values.help) {\n logger.log(\n `Usage: yakcc shave <path> [--registry <p>] [--offline] [--foreign-policy <allow|reject|tag>]\\n Shave a source file into universalize result (atoms + intent + license).\\n --foreign-policy: how to handle foreign-block deps (default: ${FOREIGN_POLICY_DEFAULT})`,\n );\n return 0;\n }\n\n // Validate --foreign-policy value when provided.\n const rawForeignPolicy = parsed.values[\"foreign-policy\"];\n let foreignPolicy: ForeignPolicy = FOREIGN_POLICY_DEFAULT;\n if (rawForeignPolicy !== undefined) {\n if (!(VALID_FOREIGN_POLICIES as readonly string[]).includes(rawForeignPolicy)) {\n logger.error(\n `error: --foreign-policy must be one of: ${VALID_FOREIGN_POLICIES.join(\", \")}; got: ${rawForeignPolicy}`,\n );\n return 1;\n }\n foreignPolicy = rawForeignPolicy as ForeignPolicy;\n }\n\n const sourcePath = parsed.positionals[0];\n if (sourcePath === undefined) {\n logger.error(\"error: missing source path. Usage: yakcc shave <path>\");\n return 1;\n }\n\n const registryPath = parsed.values.registry ?? \".yakcc/registry.sqlite\";\n const offline = parsed.values.offline === true;\n\n let registry: Registry;\n try {\n registry = await openRegistry(resolve(registryPath));\n } catch (err) {\n logger.error(`error: failed to open registry at ${registryPath}: ${(err as Error).message}`);\n return 1;\n }\n\n // Adapt Registry → ShaveRegistryView (nullish mismatch on getBlock).\n const shaveRegistry = {\n selectBlocks: (specHash: Parameters<typeof registry.selectBlocks>[0]) =>\n registry.selectBlocks(specHash),\n getBlock: async (merkleRoot: Parameters<typeof registry.getBlock>[0]) => {\n const row = await registry.getBlock(merkleRoot);\n return row ?? undefined;\n },\n findByCanonicalAstHash: registry.findByCanonicalAstHash?.bind(registry),\n };\n\n try {\n const result = await shaveImpl(resolve(sourcePath), shaveRegistry, { offline, foreignPolicy });\n logger.log(`Shaved ${result.sourcePath}:`);\n logger.log(` atoms: ${result.atoms.length}`);\n logger.log(` intentCards: ${result.intentCards.length}`);\n if (result.atoms.length > 0) {\n logger.log(\" atoms detail:\");\n for (const atom of result.atoms) {\n logger.log(\n ` - ${atom.placeholderId} [${atom.sourceRange.start}..${atom.sourceRange.end}]`,\n );\n }\n }\n if (result.diagnostics.stubbed.length > 0) {\n logger.log(` stubbed: ${result.diagnostics.stubbed.join(\", \")}`);\n }\n // L5-I4: emit \"foreign deps:\" summary line when policy is 'tag' and deps exist.\n // result.foreignDeps is set by the shave() policy gate when policy === 'tag'.\n // It is undefined for 'allow' (silent accept per L5-I5).\n // It is never reached for 'reject' (ForeignPolicyRejectError is thrown instead).\n if (result.foreignDeps !== undefined && result.foreignDeps.length > 0) {\n const depTokens = result.foreignDeps.map((d) => `${d.pkg}#${d.export}`).join(\", \");\n logger.log(`foreign deps: ${depTokens}`);\n }\n return 0;\n } catch (err) {\n // L5-I3: ForeignPolicyRejectError carries a structured message that already\n // includes \"foreign-policy reject: pkg#export[, ...]\". Catching it here lets\n // the generic catch re-use the same logger.error path, so the stderr line\n // naturally contains both the package name and the export name.\n if (err instanceof ForeignPolicyRejectError) {\n logger.error(`error: shave failed: ${err.message}`);\n return 1;\n }\n const e = err as Error;\n logger.error(`error: shave failed: ${e.message}`);\n return 1;\n } finally {\n await registry.close();\n }\n}\n","// SPDX-License-Identifier: MIT\n//\n// uninstall.ts — handler for `yakcc uninstall [options]`\n//\n// The symmetric off-switch for `yakcc init`. Removes the yakcc hook from every\n// IDE that S1 init recorded in `.yakccrc.json`, or falls back to detectInstalledIdes().\n//\n// @decision DEC-CLI-UNINSTALL-COMMAND-001\n// title: `yakcc uninstall` top-level verb; default removes hooks but preserves data;\n// `--purge` removes `.yakcc/` and `.yakccrc.json`; `--ide <list>` targets specific IDEs\n// status: accepted (WI-656-S2)\n// rationale:\n// Operator directive 2026-05-17 (#656). Default-preserve-data is the safe behavior —\n// `--purge` is the explicit opt-in for destructive removal. Symmetric with `yakcc init`.\n// Non-interactive per parent plan NG6; the summary line is the visibility mechanism.\n//\n// @decision DEC-CLI-UNINSTALL-DETECTION-001\n// title: 3-tier detection — explicit `--ide` list → `.yakccrc.json installedHooks` → detectInstalledIdes() fallback\n// status: accepted (WI-656-S2)\n// rationale:\n// Primary path uses the precise install-time inventory S1 init wrote. Fallback handles\n// projects whose `.yakccrc.json` predates S1 or was never created via unified `init`.\n// Avoids a new public API on `ide-detect.ts` (Sacred Practice #12 — no parallel authority).\n// Per-IDE installers are idempotent on uninstall, so an over-broad fallback is safe.\n//\n// @decision DEC-CLI-UNINSTALL-PURGE-001\n// title: `--purge` is non-interactive; summary log line announces \"purged\" prominently\n// status: accepted (WI-656-S2)\n// rationale:\n// Consistent with parent plan NG6 (no interactive prompts). Scriptability is preserved.\n// Visibility is via the summary line, not a confirmation gate. Uses rmSync with\n// { recursive: true, force: true } per C4 (cross-platform path safety).\n\nimport { existsSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { parseArgs } from \"node:util\";\nimport type { Logger } from \"../index.js\";\nimport { type IdeName, KNOWN_IDE_NAMES, detectInstalledIdes } from \"../lib/ide-detect.js\";\nimport { hooksAiderInstall } from \"./hooks-aider-install.js\";\nimport { hooksClineInstall } from \"./hooks-cline-install.js\";\nimport { hooksContinueInstall } from \"./hooks-continue-install.js\";\nimport { hooksCursorInstall } from \"./hooks-cursor-install.js\";\nimport { hooksClaudeCodeInstall } from \"./hooks-install.js\";\nimport { hooksWindsurfInstall } from \"./hooks-windsurf-install.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Subdirectory for all yakcc operational data — removed on `--purge`. */\nconst YAKCC_DIR = \".yakcc\";\n\n/** Config file at project root — read (for installedHooks) and mutated or deleted. */\nconst RC_FILENAME = \".yakccrc.json\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/**\n * Flexible rc schema — only the fields uninstall.ts needs are typed; the rest\n * are preserved verbatim (EC-S2-I3: version stays 1, additive-only, no field removal).\n */\ninterface YakccRc {\n version: number;\n installedHooks?: string[];\n [key: string]: unknown;\n}\n\n// ---------------------------------------------------------------------------\n// Options injection seam (mirrors init.ts InitOptions)\n// ---------------------------------------------------------------------------\n\n/**\n * Internal options for `uninstall`. Tests inject these to avoid writing to\n * the real HOME directory.\n *\n * @property overrideHome - Substitute for os.homedir() during IDE detection\n * and cline/continue directory resolution. Mirrors InitOptions.overrideHome.\n */\nexport interface UninstallOptions {\n overrideHome?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Read `.yakccrc.json` from target directory, or return null if absent/corrupt.\n * Parsing errors are silently swallowed and treated as \"no rc\" — the caller\n * falls through to the detectInstalledIdes() tier.\n */\nfunction readRc(targetDir: string): YakccRc | null {\n const rcPath = join(targetDir, RC_FILENAME);\n if (!existsSync(rcPath)) return null;\n try {\n return JSON.parse(readFileSync(rcPath, \"utf-8\")) as YakccRc;\n } catch {\n return null;\n }\n}\n\n/**\n * Parse a comma-separated `--ide` value into a list of IdeName.\n * Returns `{ ok: IdeName[] }` on success, `{ err: string }` on invalid input.\n *\n * Mirrors init.ts's parseIdeList() — identical logic, same error shape (EC-S2-T6).\n */\nfunction parseIdeList(raw: string): { ok: IdeName[] } | { err: string } {\n const parts = raw.split(\",\").map((s) => s.trim().toLowerCase());\n const invalid = parts.filter((p) => !(KNOWN_IDE_NAMES as readonly string[]).includes(p));\n if (invalid.length > 0) {\n return {\n err:\n `unknown IDE name(s): ${invalid.join(\", \")}. ` +\n `Known IDEs: ${KNOWN_IDE_NAMES.join(\", \")}`,\n };\n }\n return { ok: parts as IdeName[] };\n}\n\n// ---------------------------------------------------------------------------\n// Per-IDE uninstall dispatch\n//\n// Mirrors init.ts's installHookForIde() but flips --uninstall.\n// Uses the same static dispatch table (DEC-CLI-IDE-INSTALLER-DISPATCH-001).\n// No generic HookInstaller interface — per-IDE installers preserve their\n// surface-specific semantics (EC-S2-I2).\n// ---------------------------------------------------------------------------\n\n/**\n * Dispatch to the per-IDE installer with --uninstall flag set.\n *\n * For claude-code and cursor: delegates to the settings.json-based uninstall.\n * For cline and continue: delegates to the marker-file-based uninstall.\n *\n * Returns the exit code from the delegated installer (0 = success / already absent).\n * The per-IDE installers are idempotent: absent marker → log message → exit 0.\n */\nasync function uninstallHookForIde(\n ide: IdeName,\n targetDir: string,\n logger: Logger,\n overrideHome?: string,\n): Promise<number> {\n const home = overrideHome ?? homedir();\n\n switch (ide) {\n case \"claude-code\":\n return hooksClaudeCodeInstall([\"--target\", targetDir, \"--uninstall\"], logger);\n case \"cursor\":\n return hooksCursorInstall([\"--target\", targetDir, \"--uninstall\"], logger);\n case \"cline\":\n return hooksClineInstall([\"--uninstall\"], logger, join(home, \".config\", \"cline\"));\n case \"continue\":\n return hooksContinueInstall([\"--uninstall\"], logger, join(home, \".continue\"));\n case \"windsurf\":\n return hooksWindsurfInstall([\"--target\", targetDir, \"--uninstall\"], logger);\n case \"aider\":\n return hooksAiderInstall([\"--uninstall\"], logger, join(home, \".aider\"));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Command handler\n// ---------------------------------------------------------------------------\n\n/**\n * Handler for `yakcc uninstall [--target <dir>] [--purge] [--ide <list>]`.\n *\n * Steps performed (in order):\n * 1. Parse and validate arguments (strict; fail fast before filesystem I/O).\n * 2. Resolve targetDir (default \".\").\n * 3. Determine the IDE list to uninstall from (DEC-CLI-UNINSTALL-DETECTION-001):\n * a. If --ide <list>: use explicit list (validated; no rc or detection consulted).\n * b. Else if .yakccrc.json has non-empty installedHooks: use that array.\n * c. Else: call detectInstalledIdes(overrideHome) and use the result.\n * 4. For each IDE: call uninstallHookForIde(); log failures as warnings (non-fatal).\n * 5. If NOT --purge: update .yakccrc.json (if it exists):\n * --ide path: remove only the targeted IDEs from installedHooks.\n * default path: set installedHooks to [].\n * All other rc fields preserved verbatim (EC-S2-I3).\n * 6. If --purge: rmSync(.yakcc/, recursive+force) and rmSync(.yakccrc.json, force).\n * 7. Emit a concise summary line (≤6 lines total; \"purged\" word required when --purge).\n * 8. Return 0 on success; 1 only on flag-parse or invalid --ide errors.\n *\n * Idempotency: per-IDE installers log \"not installed — nothing to uninstall\" and\n * return 0 when the hook is already absent. Two consecutive uninstall calls both exit 0.\n *\n * @param argv - Remaining argv after `uninstall` has been consumed by runCli.\n * @param logger - Output sink; defaults to CONSOLE_LOGGER in production.\n * @param opts - Internal options (home override for tests).\n * @returns Process exit code (0 = success, 1 = error).\n */\nexport async function uninstall(\n argv: readonly string[],\n logger: Logger,\n opts?: UninstallOptions,\n): Promise<number> {\n // -------------------------------------------------------------------------\n // 1. Parse arguments\n // -------------------------------------------------------------------------\n\n let parsed: ReturnType<\n typeof parseArgs<{\n options: {\n target: { type: \"string\"; short: \"t\" };\n purge: { type: \"boolean\" };\n ide: { type: \"string\" };\n };\n }>\n >;\n\n try {\n parsed = parseArgs({\n args: [...argv],\n options: {\n target: { type: \"string\", short: \"t\" },\n purge: { type: \"boolean\" },\n ide: { type: \"string\" },\n },\n allowPositionals: false,\n strict: true,\n });\n } catch (err) {\n logger.error(`error: ${(err as Error).message}`);\n logger.error(\n \"Usage: yakcc uninstall [--target <dir>] [--purge] [--ide <claude-code|cursor|cline|continue|windsurf|aider,...>]\",\n );\n return 1;\n }\n\n const targetDir = parsed.values.target ?? \".\";\n const doPurge = parsed.values.purge === true;\n const ideRaw = parsed.values.ide;\n\n // -------------------------------------------------------------------------\n // 2. Validate --ide list (fail fast before touching the filesystem)\n // -------------------------------------------------------------------------\n\n let explicitIdes: IdeName[] | null = null;\n if (ideRaw !== undefined) {\n const parseResult = parseIdeList(ideRaw);\n if (\"err\" in parseResult) {\n logger.error(`error: ${parseResult.err}`);\n return 1;\n }\n explicitIdes = parseResult.ok;\n }\n\n // -------------------------------------------------------------------------\n // 3. Determine IDE list per DEC-CLI-UNINSTALL-DETECTION-001\n //\n // Tier 1: explicit --ide <list> (already validated above)\n // Tier 2: .yakccrc.json installedHooks (non-empty array)\n // Tier 3: detectInstalledIdes() fallback\n // -------------------------------------------------------------------------\n\n let idesToUninstall: IdeName[];\n\n if (explicitIdes !== null) {\n // Tier 1: caller gave us an explicit list — do not consult rc or detection\n idesToUninstall = explicitIdes;\n } else {\n const rc = readRc(targetDir);\n if (rc !== null && Array.isArray(rc.installedHooks) && rc.installedHooks.length > 0) {\n // Tier 2: .yakccrc.json has a non-empty installedHooks inventory\n // Filter to known IDE names (defensive against corrupted rc files)\n idesToUninstall = rc.installedHooks.filter((name): name is IdeName =>\n (KNOWN_IDE_NAMES as readonly string[]).includes(name),\n );\n } else {\n // Tier 3: fallback to live detection (covers projects that used legacy\n // per-IDE installers before `yakcc init` unified the surface)\n const detected = detectInstalledIdes(opts?.overrideHome);\n idesToUninstall = detected.map((d) => d.name);\n }\n }\n\n // -------------------------------------------------------------------------\n // 4. Per-IDE uninstall loop\n //\n // Failures are non-fatal (logged as warnings). Mirrors init.ts's pattern of\n // continuing after a per-IDE error so partial uninstall is still useful.\n // -------------------------------------------------------------------------\n\n const removedIdes: IdeName[] = [];\n\n for (const ide of idesToUninstall) {\n try {\n const code = await uninstallHookForIde(ide, targetDir, logger, opts?.overrideHome);\n if (code === 0) {\n removedIdes.push(ide);\n } else {\n logger.error(`warning: uninstall from ${ide} returned exit ${code} — continuing`);\n }\n } catch (err) {\n logger.error(`warning: uninstall from ${ide} failed: ${String(err)} — continuing`);\n }\n }\n\n // -------------------------------------------------------------------------\n // 5. Update .yakccrc.json after a default (non-purge) uninstall\n //\n // If --ide was used: remove only the targeted IDEs from installedHooks.\n // If no --ide (rc or detect path): set installedHooks to [].\n // Skip if .yakccrc.json doesn't exist.\n //\n // EC-S2-I3: all fields except installedHooks are preserved verbatim.\n // -------------------------------------------------------------------------\n\n if (!doPurge) {\n const rc = readRc(targetDir);\n if (rc !== null) {\n let updatedHooks: string[];\n if (explicitIdes !== null) {\n // --ide path: remove only the targeted IDEs from the inventory\n updatedHooks = (rc.installedHooks ?? []).filter(\n (h) => !explicitIdes?.includes(h as IdeName),\n );\n } else {\n // Default path: clear the entire installedHooks array\n updatedHooks = [];\n }\n const updated: YakccRc = { ...rc, installedHooks: updatedHooks };\n try {\n writeFileSync(\n join(targetDir, RC_FILENAME),\n `${JSON.stringify(updated, null, 2)}\\n`,\n \"utf-8\",\n );\n } catch (err) {\n logger.error(`warning: cannot update ${RC_FILENAME}: ${String(err)}`);\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // 6. --purge: remove .yakcc/ and .yakccrc.json (DEC-CLI-UNINSTALL-PURGE-001)\n //\n // Runs AFTER the hook-removal loop. Uses rmSync with { force: true } so\n // absent files/dirs are silently ignored (idempotent). Cross-platform paths\n // via join() (C4).\n // -------------------------------------------------------------------------\n\n if (doPurge) {\n try {\n rmSync(join(targetDir, YAKCC_DIR), { recursive: true, force: true });\n } catch (err) {\n logger.error(`warning: cannot remove ${YAKCC_DIR}: ${String(err)}`);\n }\n try {\n rmSync(join(targetDir, RC_FILENAME), { force: true });\n } catch (err) {\n logger.error(`warning: cannot remove ${RC_FILENAME}: ${String(err)}`);\n }\n }\n\n // -------------------------------------------------------------------------\n // 7. Emit concise summary (G6: ≤6 lines total)\n //\n // The summary line MUST contain \"purged\" when --purge is active\n // (DEC-CLI-UNINSTALL-PURGE-001 / EC-S2-T3 / EC-S2-T8).\n // -------------------------------------------------------------------------\n\n const absTargetDir = resolve(targetDir);\n\n const removedLine =\n removedIdes.length > 0\n ? `Removed from: ${removedIdes.join(\", \")}.`\n : \"No hooks removed (nothing was installed).\";\n\n if (doPurge) {\n logger.log(`${removedLine} Purged .yakcc/ and ${RC_FILENAME} at ${absTargetDir}.`);\n } else {\n logger.log(`${removedLine} Registry preserved at ${join(absTargetDir, YAKCC_DIR)}.`);\n }\n\n return 0;\n}\n","// SPDX-License-Identifier: MIT\n//\n// @decision DEC-DIST-PACKAGING-002\n// @title pkg SEA binary: sqlite3_load_extension intercept for snapshot-resident .so files\n// @status accepted\n// @rationale\n// pkg bundles files inside a virtual snapshot at /snapshot/<worktree>/...\n// Node.js modules loaded from the snapshot (via the VFS fs-hook) work correctly,\n// but native extensions loaded via SQLite's sqlite3_load_extension() call\n// dlopen() directly at the OS level, bypassing the VFS. The OS dlopen() cannot\n// open files inside the virtual /snapshot path.\n//\n// The pkg bootstrap already patches process.dlopen for .node addons (extracting\n// them to ~/.cache/pkg/<hash>/<name>/ before loading), but that patch only\n// applies to Node's process.dlopen, not to sqlite3_load_extension's internal dlopen.\n//\n// This module patches Database.prototype.loadExtension early (before any registry\n// command runs) so that when sqlite-vec calls db.loadExtension('/snapshot/.../vec0.so'),\n// the file is first extracted from the VFS to a real temp path and the real path is\n// passed instead.\n//\n// The snapshot check uses the same /snapshot prefix convention that pkg injects.\n// The extraction cache mirrors patchDlopen's convention: ~/.cache/pkg/<sha256>/<basename>.\n// This is a no-op when running from source (non-snapshot paths pass through unchanged).\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { basename, join } from \"node:path\";\n\n/** Returns true when the path lives inside the pkg snapshot virtual filesystem. */\nfunction insideSnapshot(p: string): boolean {\n return p.startsWith(\"/snapshot/\") || p.startsWith(\"/snapshot\");\n}\n\n/**\n * Extracts a file from the pkg snapshot to a stable on-disk location and\n * returns the real path. Safe to call multiple times for the same file\n * (idempotent via SHA-256 content check).\n */\nfunction extractFromSnapshot(snapshotPath: string): string {\n const content = readFileSync(snapshotPath); // reads via pkg VFS\n const hash = createHash(\"sha256\").update(content).digest(\"hex\");\n const cacheBase = process.env.PKG_NATIVE_CACHE_PATH ?? join(homedir(), \".cache\");\n const cacheDir = join(cacheBase, \"pkg\", hash);\n mkdirSync(cacheDir, { recursive: true });\n const dest = join(cacheDir, basename(snapshotPath));\n if (existsSync(dest)) {\n // Same hash → same content, no need to rewrite.\n const existing = readFileSync(dest);\n if (createHash(\"sha256\").update(existing).digest(\"hex\") === hash) {\n return dest;\n }\n }\n writeFileSync(dest, content, { mode: 0o755 });\n return dest;\n}\n\n/**\n * Applies the snapshot extraction patch to a better-sqlite3 Database prototype.\n * Called once at startup; idempotent (guarded by a brand symbol).\n *\n * @param Database - The Database constructor from better-sqlite3.\n */\nconst PATCHED_BRAND = Symbol.for(\"pkg-native-compat-patched\");\n\nexport function patchSqliteDatabase(Database: {\n prototype: { loadExtension?: (...args: unknown[]) => unknown };\n}): void {\n // Guard against double-patching (e.g. multiple imports of this module).\n if ((Database as Record<symbol, unknown>)[PATCHED_BRAND]) return;\n (Database as Record<symbol, unknown>)[PATCHED_BRAND] = true;\n\n const original = Database.prototype.loadExtension;\n if (typeof original !== \"function\") return;\n\n Database.prototype.loadExtension = function patchedLoadExtension(...args: unknown[]) {\n const path = args[0];\n if (typeof path === \"string\" && insideSnapshot(path)) {\n args[0] = extractFromSnapshot(path);\n }\n return original.call(this, ...args);\n };\n}\n"],"mappings":"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","names":["s","encoder","bytesToHex","TEXT_ENCODER","bytesToHex","result","TEXT_ENCODER","init","Node","SyntaxKind","Node","Project","contractId","bytesToHex","createLocalEmbeddingProvider","specHash","query","rebuildRegistry","blockMerkleRoot","init_dist","specHash","specHash","createHttpTransport","specHash","init_dist","_req","resolve","init_dist","init_dist","parseArgs","resolve","init_dist","createRequire","fileURLToPath","init_dist","readFileSync","dirname","join","isBytes","anumber","abytes","aexists","aoutput","abytes","u8","u32","clean","rotr","isLE","byteSwap","swap8IfBE","byteSwap32","byteSwap","swap32IfBE","isLE","bytesToHex","abytes","abytes","SHA256_IV","U32_MASK64","_32n","fromBig","G1s","rotr","G2s","anumber","u32","aexists","abytes","swap32IfBE","aoutput","clean","swap8IfBE","compress","G1s","G2s","B3_Flags","B3_IV","SHA256_IV","B3_SIGMA","_BLAKE3","abytes","u32","swap32IfBE","u8","fromBig","compress","clean","aexists","anumber","aoutput","blake3","resolve","bytesToHex","blake3","encoder","encoder","bytesToHex","blake3","readFile","encoder","readFile","bytesToHex","blake3","encoder","bytesToHex","blake3","inferFunctionName","encoder","bytesToHex","blake3","inferFunctionName","sourceHash","SyntaxKind","Project","ScriptKind","SyntaxKind","SyntaxKind","init","SyntaxKind","escapes","bytesToHex","blake3","init","Project","ScriptKind","Node","Project","SyntaxKind","readFileSync","join","Project","Project","join","readFileSync","Project","Node","Project","SyntaxKind","Project","ScriptKind","canonicalAstHash","canonicalAstHash","Project","ScriptKind","readFile","join","Project","behavior","buildFragmentFallback","STATIC_MODEL_TAG","STATIC_PROMPT_VERSION","unlink","join","raw","validated","createDefaultAnthropicClient","SYSTEM_PROMPT","join","canonicalAstHash","join","readFile","readFileSync","join","TEXT_ENCODER","basename","dirname","specHash","encoder","init_dist","mkdirSync","writeFileSync","existsSync","dirname","join","resolve","parseArgs","DEFAULT_REGISTRY_PATH","mkdirSync","readFileSync","statSync","writeFileSync","join","parseArgs","join","entry","Project","ScriptKind","init_dist","readdirSync","statSync","join","dirname","join","dirname","readdirSync","init_dist","mkdirSync","dirname","parseArgs","DEFAULT_REGISTRY_PATH","parseArgs","DEFAULT_REGISTRY_PATH","statSync","join","readFileSync","mkdirSync","writeFileSync","existsSync","mkdirSync","readFileSync","writeFileSync","join","parseArgs","existsSync","mkdirSync","readFileSync","writeFileSync","join","parseArgs","HOOK_COMMAND","isYakccInstalled","updated","existsSync","mkdirSync","readFileSync","rmSync","writeFileSync","join","parseArgs","HOOK_COMMAND","applyInstall","applyUninstall","updated","existsSync","mkdirSync","readFileSync","writeFileSync","join","parseArgs","HOOK_COMMAND","isYakccInstalled","applyInstall","applyUninstall","updated","init_dist","existsSync","mkdirSync","readFileSync","writeFileSync","join","parseArgs","existsSync","homedir","join","existsSync","mkdirSync","readFileSync","rmSync","writeFileSync","homedir","join","parseArgs","HOOK_COMMAND","readMarker","isYakccInstalled","existsSync","mkdirSync","readFileSync","rmSync","writeFileSync","homedir","join","parseArgs","HOOK_COMMAND","readMarker","isYakccInstalled","init_dist","existsSync","dirname","join","fileURLToPath","join","existsSync","readFileSync","writeFileSync","homedir","parseArgs","mkdirSync","runFederation","init_dist","readFileSync","parseArgs","parseArgs","DEFAULT_REGISTRY_PATH","readFileSync","init_dist","readFileSync","parseArgs","parseArgs","DEFAULT_REGISTRY_PATH","readFileSync","existsSync","mkdirSync","createRequire","dirname","resolve","parseArgs","createRequire","parseArgs","DEFAULT_REGISTRY_PATH","resolve","existsSync","mkdirSync","dirname","Database","init_dist","mkdirSync","dirname","parseArgs","parseArgs","DEFAULT_REGISTRY_PATH","dirname","mkdirSync","createLocalEmbeddingProvider","init_dist","readFileSync","parseArgs","truncate","parseArgs","query","DEFAULT_REGISTRY_PATH","readFileSync","init_dist","parseArgs","parseArgs","DEFAULT_REGISTRY_PATH","init_dist","resolve","parseArgs","shave","parseArgs","resolve","specHash","existsSync","readFileSync","rmSync","writeFileSync","homedir","join","resolve","parseArgs","YAKCC_DIR","RC_FILENAME","readRc","join","existsSync","readFileSync","parseIdeList","homedir","parseArgs","writeFileSync","rmSync","resolve","shave","existsSync","mkdirSync","readFileSync","writeFileSync","homedir","basename","join","Database","createRequire","Database","fileURLToPath"]}