@hawkeyexl/manni 4.3.1 → 4.4.1

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 (225) hide show
  1. package/README.md +9 -1
  2. package/dist/batch-GFNLTGOH.js +36 -0
  3. package/dist/build-COYIQ25L.js +31 -0
  4. package/dist/calibrate-MDBLJKEE.js +31 -0
  5. package/dist/capture-LRML2PKV.js +32 -0
  6. package/dist/check-ASPIHA6B.js +40 -0
  7. package/dist/check-ASPIHA6B.js.map +1 -0
  8. package/dist/check-EDLBYL5N.js +27 -0
  9. package/dist/check-EDLBYL5N.js.map +1 -0
  10. package/dist/check-HYNGXYJV.js +18 -0
  11. package/dist/check-HYNGXYJV.js.map +1 -0
  12. package/dist/chunk-2B7ZNTPV.js +230 -0
  13. package/dist/chunk-2B7ZNTPV.js.map +1 -0
  14. package/dist/chunk-2H7UOFLK.js +11 -0
  15. package/dist/chunk-2H7UOFLK.js.map +1 -0
  16. package/dist/chunk-37LP5HMZ.js +1042 -0
  17. package/dist/chunk-37LP5HMZ.js.map +1 -0
  18. package/dist/chunk-3VK37OR2.js +2957 -0
  19. package/dist/chunk-3VK37OR2.js.map +1 -0
  20. package/dist/chunk-4IUC2MNL.js +2165 -0
  21. package/dist/chunk-4IUC2MNL.js.map +1 -0
  22. package/dist/{chunk-3R23C5DT.js → chunk-4TSWU2SU.js} +2 -14
  23. package/dist/chunk-4TSWU2SU.js.map +1 -0
  24. package/dist/chunk-57NEQRVO.js +10921 -0
  25. package/dist/chunk-57NEQRVO.js.map +1 -0
  26. package/dist/chunk-6JSSEWBG.js +6737 -0
  27. package/dist/chunk-6JSSEWBG.js.map +1 -0
  28. package/dist/chunk-6OYEI2VV.js +88 -0
  29. package/dist/chunk-6OYEI2VV.js.map +1 -0
  30. package/dist/chunk-77AINBYM.js +19 -0
  31. package/dist/chunk-77AINBYM.js.map +1 -0
  32. package/dist/chunk-7UKO3ASZ.js +54 -0
  33. package/dist/chunk-7UKO3ASZ.js.map +1 -0
  34. package/dist/chunk-A5ULMVHQ.js +164 -0
  35. package/dist/chunk-A5ULMVHQ.js.map +1 -0
  36. package/dist/chunk-C4SQM22E.js +299 -0
  37. package/dist/chunk-C4SQM22E.js.map +1 -0
  38. package/dist/chunk-CK7SKVHR.js +1042 -0
  39. package/dist/chunk-CK7SKVHR.js.map +1 -0
  40. package/dist/{chunk-G5CM2VGQ.js → chunk-DXNZS72W.js} +8 -94
  41. package/dist/chunk-DXNZS72W.js.map +1 -0
  42. package/dist/chunk-EPH54CAY.js +374 -0
  43. package/dist/chunk-EPH54CAY.js.map +1 -0
  44. package/dist/{chunk-KJR7OVDX.js → chunk-FBEQCK47.js} +56 -137
  45. package/dist/chunk-FBEQCK47.js.map +1 -0
  46. package/dist/{chunk-IOYXPBCL.js → chunk-G3T47CFV.js} +1 -7
  47. package/dist/{chunk-IOYXPBCL.js.map → chunk-G3T47CFV.js.map} +1 -1
  48. package/dist/chunk-GCQRPUSB.js +1287 -0
  49. package/dist/chunk-GCQRPUSB.js.map +1 -0
  50. package/dist/chunk-GLP5PSXB.js +72 -0
  51. package/dist/chunk-GLP5PSXB.js.map +1 -0
  52. package/dist/chunk-I2AH3CJJ.js +1192 -0
  53. package/dist/chunk-I2AH3CJJ.js.map +1 -0
  54. package/dist/chunk-IDV6HJCB.js +388 -0
  55. package/dist/chunk-IDV6HJCB.js.map +1 -0
  56. package/dist/chunk-IKEWBMCN.js +98 -0
  57. package/dist/chunk-IKEWBMCN.js.map +1 -0
  58. package/dist/chunk-JCDXE76J.js +52 -0
  59. package/dist/chunk-JCDXE76J.js.map +1 -0
  60. package/dist/chunk-JXLTXMJ7.js +455 -0
  61. package/dist/chunk-JXLTXMJ7.js.map +1 -0
  62. package/dist/chunk-LJ2RDH3F.js +49 -0
  63. package/dist/chunk-LJ2RDH3F.js.map +1 -0
  64. package/dist/chunk-M2JCK5F6.js +805 -0
  65. package/dist/chunk-M2JCK5F6.js.map +1 -0
  66. package/dist/chunk-M3W7J6MU.js +4398 -0
  67. package/dist/chunk-M3W7J6MU.js.map +1 -0
  68. package/dist/chunk-MMTSSTG7.js +23 -0
  69. package/dist/chunk-MMTSSTG7.js.map +1 -0
  70. package/dist/chunk-MNH7QO4Z.js +15 -0
  71. package/dist/chunk-MNH7QO4Z.js.map +1 -0
  72. package/dist/chunk-MWJLZMBL.js +934 -0
  73. package/dist/chunk-MWJLZMBL.js.map +1 -0
  74. package/dist/chunk-MYMBCTDE.js +387 -0
  75. package/dist/chunk-MYMBCTDE.js.map +1 -0
  76. package/dist/chunk-NCPWXHDO.js +683 -0
  77. package/dist/chunk-NCPWXHDO.js.map +1 -0
  78. package/dist/chunk-NLXTXE5Y.js +1661 -0
  79. package/dist/chunk-NLXTXE5Y.js.map +1 -0
  80. package/dist/{chunk-CFDUZRM2.js → chunk-NXV7WE2D.js} +32 -240
  81. package/dist/chunk-NXV7WE2D.js.map +1 -0
  82. package/dist/chunk-OWBRDNRZ.js +16 -0
  83. package/dist/chunk-OWBRDNRZ.js.map +1 -0
  84. package/dist/{chunk-KTSJ2ZHA.js → chunk-PAU352SV.js} +5 -3
  85. package/dist/{chunk-KTSJ2ZHA.js.map → chunk-PAU352SV.js.map} +1 -1
  86. package/dist/chunk-PCE7QHXJ.js +68 -0
  87. package/dist/chunk-PCE7QHXJ.js.map +1 -0
  88. package/dist/chunk-PIKOVN6W.js +1651 -0
  89. package/dist/chunk-PIKOVN6W.js.map +1 -0
  90. package/dist/chunk-PNPQTHBT.js +17 -0
  91. package/dist/chunk-PNPQTHBT.js.map +1 -0
  92. package/dist/chunk-PU6DFJMY.js +1975 -0
  93. package/dist/chunk-PU6DFJMY.js.map +1 -0
  94. package/dist/chunk-Q35YIDBV.js +1202 -0
  95. package/dist/chunk-Q35YIDBV.js.map +1 -0
  96. package/dist/{chunk-5HNHEUD5.js → chunk-QQUKGCAJ.js} +3 -3
  97. package/dist/chunk-QRWGPQKO.js +198 -0
  98. package/dist/chunk-QRWGPQKO.js.map +1 -0
  99. package/dist/{chunk-UBMMXS2Y.js → chunk-QZ6LRSNV.js} +69 -48
  100. package/dist/{chunk-UBMMXS2Y.js.map → chunk-QZ6LRSNV.js.map} +1 -1
  101. package/dist/chunk-RL5XWQOL.js +3872 -0
  102. package/dist/chunk-RL5XWQOL.js.map +1 -0
  103. package/dist/chunk-RNVYGWIC.js +30 -0
  104. package/dist/chunk-RNVYGWIC.js.map +1 -0
  105. package/dist/chunk-SE4JN5XN.js +19 -0
  106. package/dist/chunk-SE4JN5XN.js.map +1 -0
  107. package/dist/chunk-SKSDRUUK.js +622 -0
  108. package/dist/chunk-SKSDRUUK.js.map +1 -0
  109. package/dist/chunk-TIUTIOFB.js +25 -0
  110. package/dist/chunk-TIUTIOFB.js.map +1 -0
  111. package/dist/chunk-TK3FC4RF.js +3090 -0
  112. package/dist/chunk-TK3FC4RF.js.map +1 -0
  113. package/dist/chunk-TU2WUBVZ.js +2 -0
  114. package/dist/chunk-TU2WUBVZ.js.map +1 -0
  115. package/dist/chunk-TZOYZK2E.js +98 -0
  116. package/dist/chunk-TZOYZK2E.js.map +1 -0
  117. package/dist/chunk-UJCVAWP7.js +178 -0
  118. package/dist/chunk-UJCVAWP7.js.map +1 -0
  119. package/dist/chunk-UKZNLVJW.js +17 -0
  120. package/dist/chunk-UKZNLVJW.js.map +1 -0
  121. package/dist/{chunk-HAPTXWU2.js → chunk-UQISLDAV.js} +4 -4
  122. package/dist/chunk-WB3QM6PP.js +5440 -0
  123. package/dist/chunk-WB3QM6PP.js.map +1 -0
  124. package/dist/chunk-YB4IBW3M.js +96 -0
  125. package/dist/chunk-YB4IBW3M.js.map +1 -0
  126. package/dist/chunk-YEUYKR62.js +72 -0
  127. package/dist/chunk-YEUYKR62.js.map +1 -0
  128. package/dist/chunk-YJOGUXGP.js +634 -0
  129. package/dist/chunk-YJOGUXGP.js.map +1 -0
  130. package/dist/chunk-YNHZHSSM.js +41 -0
  131. package/dist/chunk-YNHZHSSM.js.map +1 -0
  132. package/dist/chunk-YTURO7SI.js +979 -0
  133. package/dist/chunk-YTURO7SI.js.map +1 -0
  134. package/dist/chunk-ZXRAE55H.js +31 -0
  135. package/dist/chunk-ZXRAE55H.js.map +1 -0
  136. package/dist/cli-3BIBYHFK.js +473 -0
  137. package/dist/cli-3BIBYHFK.js.map +1 -0
  138. package/dist/cli-673OTRGE.js +28 -0
  139. package/dist/cli-673OTRGE.js.map +1 -0
  140. package/dist/cli-6HIDIFVT.js +1203 -0
  141. package/dist/cli-6HIDIFVT.js.map +1 -0
  142. package/dist/cli-6UB5N6CU.js +232 -0
  143. package/dist/cli-6UB5N6CU.js.map +1 -0
  144. package/dist/cli-LFP5ZGYS.js +346 -0
  145. package/dist/cli-LFP5ZGYS.js.map +1 -0
  146. package/dist/cli-MN7PYMBI.js +552 -0
  147. package/dist/cli-MN7PYMBI.js.map +1 -0
  148. package/dist/cli-PYPX6O6E.js +739 -0
  149. package/dist/cli-PYPX6O6E.js.map +1 -0
  150. package/dist/cli-RPM5PELX.js +626 -0
  151. package/dist/cli-RPM5PELX.js.map +1 -0
  152. package/dist/cli-VI7VHWNZ.js +423 -0
  153. package/dist/cli-VI7VHWNZ.js.map +1 -0
  154. package/dist/cli.d.ts +12 -2
  155. package/dist/cli.js +109 -7661
  156. package/dist/cli.js.map +1 -1
  157. package/dist/docmeta.js +15 -3
  158. package/dist/docmeta.js.map +1 -1
  159. package/dist/github-6QMYOHC3.js +27 -0
  160. package/dist/github-6QMYOHC3.js.map +1 -0
  161. package/dist/github-SXPGP5DB.js +32 -0
  162. package/dist/github-SXPGP5DB.js.map +1 -0
  163. package/dist/index.d.ts +65 -3
  164. package/dist/index.js +407 -337
  165. package/dist/index.js.map +1 -1
  166. package/dist/json-7WV7KL6H.js +17 -0
  167. package/dist/json-7WV7KL6H.js.map +1 -0
  168. package/dist/json-SY7KLNC7.js +35 -0
  169. package/dist/json-SY7KLNC7.js.map +1 -0
  170. package/dist/lint-ASMJLLPA.js +28 -0
  171. package/dist/lint-ASMJLLPA.js.map +1 -0
  172. package/dist/list-KZNVTNVC.js +31 -0
  173. package/dist/list-KZNVTNVC.js.map +1 -0
  174. package/dist/list-Q57YTFUQ.js +29 -0
  175. package/dist/list-Q57YTFUQ.js.map +1 -0
  176. package/dist/page-KSGV5RD4.js +41 -0
  177. package/dist/page-KSGV5RD4.js.map +1 -0
  178. package/dist/picker-3XF36MJF.js +14 -0
  179. package/dist/picker-3XF36MJF.js.map +1 -0
  180. package/dist/pretty-A5EGTN35.js +37 -0
  181. package/dist/pretty-A5EGTN35.js.map +1 -0
  182. package/dist/pretty-CXSDXNAS.js +43 -0
  183. package/dist/pretty-CXSDXNAS.js.map +1 -0
  184. package/dist/reporters-HUY7UXFS.js +42 -0
  185. package/dist/reporters-HUY7UXFS.js.map +1 -0
  186. package/dist/reporters-K2VUKSAK.js +71 -0
  187. package/dist/reporters-K2VUKSAK.js.map +1 -0
  188. package/dist/reporters-M6LJ2IYU.js +21 -0
  189. package/dist/reporters-M6LJ2IYU.js.map +1 -0
  190. package/dist/run-3DYJNRIG.js +33 -0
  191. package/dist/run-3DYJNRIG.js.map +1 -0
  192. package/dist/term-BIQUPMOR.js +1592 -0
  193. package/dist/term-BIQUPMOR.js.map +1 -0
  194. package/dist/types-Y6TXHM7M.js +10 -0
  195. package/dist/types-Y6TXHM7M.js.map +1 -0
  196. package/dist/validate-53RKM2XG.js +16 -0
  197. package/dist/validate-53RKM2XG.js.map +1 -0
  198. package/package.json +1 -1
  199. package/plugin/manni/.claude-plugin/plugin.json +9 -0
  200. package/plugin/manni/hooks/hooks.json +69 -0
  201. package/plugin/manni/hooks/manni.mjs +157 -0
  202. package/plugin/manni/skills/check/SKILL.md +20 -0
  203. package/plugin/manni/skills/evals/SKILL.md +27 -0
  204. package/plugin/manni/skills/fix/SKILL.md +56 -0
  205. package/plugin/manni/skills/setup/SKILL.md +41 -0
  206. package/dist/batch-A5HZKYIW.js +0 -18
  207. package/dist/calibrate-S5K6QHUY.js +0 -13
  208. package/dist/capture-OZEZDABQ.js +0 -15
  209. package/dist/chunk-3R23C5DT.js.map +0 -1
  210. package/dist/chunk-CFDUZRM2.js.map +0 -1
  211. package/dist/chunk-G5CM2VGQ.js.map +0 -1
  212. package/dist/chunk-KJR7OVDX.js.map +0 -1
  213. package/dist/chunk-V4KUVE47.js +0 -27170
  214. package/dist/chunk-V4KUVE47.js.map +0 -1
  215. package/dist/chunk-ZQBTK3DO.js +0 -26518
  216. package/dist/chunk-ZQBTK3DO.js.map +0 -1
  217. package/dist/picker-6HRMETE7.js +0 -12
  218. package/plugin/tracevals/.claude-plugin/plugin.json +0 -9
  219. package/plugin/tracevals/hooks/hooks.json +0 -17
  220. /package/dist/{batch-A5HZKYIW.js.map → batch-GFNLTGOH.js.map} +0 -0
  221. /package/dist/{calibrate-S5K6QHUY.js.map → build-COYIQ25L.js.map} +0 -0
  222. /package/dist/{capture-OZEZDABQ.js.map → calibrate-MDBLJKEE.js.map} +0 -0
  223. /package/dist/{picker-6HRMETE7.js.map → capture-LRML2PKV.js.map} +0 -0
  224. /package/dist/{chunk-5HNHEUD5.js.map → chunk-QQUKGCAJ.js.map} +0 -0
  225. /package/dist/{chunk-HAPTXWU2.js.map → chunk-UQISLDAV.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/docevals/commands/generate.ts","../src/docevals/commands/fill.ts","../src/docevals/fill/cache.ts","../src/docevals/fill/prompt.ts","../src/docevals/commands/promote.ts","../src/docevals/commands/review.ts","../src/docevals/commands/calibrate.ts","../src/docevals/commands/init.ts"],"sourcesContent":["/**\n * `manni docevals generate` — generate check scripts for command-graded evals that\n * have a plain-language assertion but no command yet (or whose assertion\n * changed since generation), without running any evals.\n */\nimport { loadRunConfig } from \"../core/config.js\";\nimport {\n discoverPages,\n documentSet,\n runConfigOptions,\n type DocumentInputOptions,\n} from \"../core/discover.js\";\nimport { withExternalMetadata } from \"../core/external.js\";\nimport { resolvePages } from \"../core/resolve.js\";\nimport { assertRegisteredGraders } from \"../core/feasibility.js\";\nimport { makeGenerateScripts } from \"../graders/scriptgen.js\";\nimport {\n assertProviderSelection,\n makeProvider,\n selectProvider,\n} from \"../judge/provider.js\";\nimport { sha256 } from \"../judge/cache.js\";\nimport type { InferenceProvider } from \"@hawkeyexl/inference\";\nimport type { GraderTarget } from \"../graders/types.js\";\nimport type { GenerationRefusal } from \"../core/engine.js\";\n\nexport interface GenerateOptions extends DocumentInputOptions {\n provider?: string;\n model?: string;\n /** Run inference on this machine, with llama-cpp, over any configured or eval-level provider. */\n local?: boolean;\n cwd?: string;\n /** Injectable provider for tests and programmatic use. */\n providerInstance?: InferenceProvider;\n}\n\nexport interface GenerateRun {\n generatedPaths: string[];\n targets: number;\n /**\n * Evals whose command reference had nowhere to go (proposal 0047): the\n * manifest that owns the page's evals joins on a field the page lacks. Each\n * counts against `targets`, so the run reports a partial generation, and the\n * sentence says which page and why.\n */\n refusals: GenerationRefusal[];\n}\n\nexport async function runGenerate(\n paths: string[],\n options: GenerateOptions = {},\n): Promise<GenerateRun> {\n const cwd = options.cwd ?? process.cwd();\n const config = loadRunConfig(runConfigOptions(paths, options), cwd);\n const flags = { provider: options.provider, model: options.model, local: options.local };\n // Checked before discovery, and even when nothing needs generating: a typo\n // is a usage error on every run, not only on the runs that reach a model.\n assertProviderSelection(selectProvider(config, flags));\n const pages = await withExternalMetadata(\n discoverPages(config, documentSet(paths, options, \"read\"), cwd),\n config,\n cwd,\n );\n const plans = resolvePages(pages, config);\n assertRegisteredGraders(plans, config);\n\n const targets: GraderTarget[] = [];\n // A config-defined eval is one eval however many pages reference it, and\n // `makeGenerateScripts` already treats it that way (its `doneConfigEvals`\n // set writes one script). Counting it per page made the two disagree, and\n // `src/cli.ts` reads that disagreement as a partial generation: it printed\n // \"Generated 1/2\" and exited 1 on a completely successful run. `runPromote`\n // has carried the same guard from the start.\n const seenConfigEvals = new Set<string>();\n for (const plan of plans) {\n if (plan.skip || plan.problems.some((p) => p.level === \"error\")) continue;\n for (const ev of plan.evals) {\n if (ev.skip || ev.grader !== \"command\") continue;\n const missing = !ev.command;\n const stale =\n ev.command != null &&\n ev.generatedAssertionHash != null &&\n ev.assertion != null &&\n ev.generatedAssertionHash !== sha256(ev.assertion);\n if ((missing || stale) && ev.assertion) {\n if (ev.source === \"config\") {\n if (seenConfigEvals.has(ev.name)) continue;\n seenConfigEvals.add(ev.name);\n }\n targets.push({ plan, eval: ev });\n }\n }\n }\n // Nothing to generate: return before a provider is built, so a corpus with\n // no outstanding scripts needs no API key to be told so.\n if (targets.length === 0) return { generatedPaths: [], targets: 0, refusals: [] };\n\n const provider: InferenceProvider =\n options.providerInstance ?? (await makeProvider(config, flags));\n const generate = makeGenerateScripts({ provider, root: cwd });\n const { generatedPaths, refusals } = await generate(targets, config, {});\n return { generatedPaths, targets: targets.length, refusals: refusals ?? [] };\n}\n","/**\n * `manni docevals fill` — ask an LLM provider to propose frontmatter evals for each\n * page, gate the proposals on self-reported confidence, and append the\n * survivors to the page's frontmatter. Proposals are ai-graded only and\n * deduplicated against the page's resolved plan; existing evals are never\n * touched. See ADR 01001.\n */\nimport { writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { palette } from \"../../shared/color.js\";\nimport type { ColorOptions } from \"../reporters/pretty.js\";\nimport type { Severity } from \"../types.js\";\nimport { loadRunConfig } from \"../core/config.js\";\nimport {\n discoverPages,\n documentSet,\n leadingFrontmatterFormat,\n runConfigOptions,\n type DocumentInputOptions,\n} from \"../core/discover.js\";\nimport { withExternalMetadata } from \"../core/external.js\";\nimport { resolvePages, type ResolvedPagePlan } from \"../core/resolve.js\";\nimport {\n appendPageEvals,\n entryObject,\n type NewEvalEntry,\n} from \"../core/frontmatter-edit.js\";\nimport {\n EVALS_KEY,\n EvalWriter,\n META_PROVENANCE_KEY,\n manifestEvalList,\n type WriteHome,\n} from \"../core/write-location.js\";\nimport {\n parseFormat,\n SUMMARY_FORMATS,\n type SummaryFormat,\n} from \"../reporters/format.js\";\nimport {\n assertProviderSelection,\n constructProvider,\n resolveProviderIdentity,\n selectProvider,\n} from \"../judge/provider.js\";\nimport type { InferenceProvider } from \"@hawkeyexl/inference\";\nimport { FillCache, fillCacheKey } from \"../fill/cache.js\";\nimport {\n FILL_SYSTEM_PROMPT,\n buildFillUser,\n isValidProposal,\n PROPOSAL_SCHEMA,\n} from \"../fill/prompt.js\";\nimport { looksLikeOverflow, splitBody } from \"../core/split.js\";\nimport { errorMessage } from \"../../shared/errors.js\";\nimport { warn } from \"../../shared/warn.js\";\nimport type { Confirm } from \"../../shared/prompt.js\";\nimport {\n mergeMetaProvenance,\n type ExternalWrite,\n type RelocateResult,\n} from \"../../meta/internal.js\";\n\nexport interface FillOptions extends DocumentInputOptions {\n cwd?: string;\n /** Characters of page per inference call; longer pages are split. */\n chunkChars?: number;\n /** Report proposals without writing frontmatter. */\n dryRun?: boolean;\n /** Minimum confidence to write; overrides config `fill.confidenceThreshold`. */\n confidence?: number;\n /** Stop after this many inference calls; overrides config `fill.maxTurns`. */\n maxTurns?: number;\n noCache?: boolean;\n provider?: string;\n model?: string;\n /** Run inference on this machine, with llama-cpp, over any configured or eval-level provider. */\n local?: boolean;\n /** Test seam: bypasses provider construction entirely. */\n providerInstance?: InferenceProvider;\n /**\n * P1 (proposal 0047): asked once per collection when evals would land on a\n * page whose schema prefers them in external metadata. The CLI passes\n * `terminalConfirm()`, so off a terminal there is no question and the\n * warning is the only output.\n */\n confirm?: Confirm;\n /** A stderr diagnostic; the caller adds the `manni docevals: ` prefix. */\n onNotice?: (message: string) => void;\n /** What an accepted relocation moved, for the caller to report. */\n onRelocated?: (result: RelocateResult) => void;\n}\n\nexport type FillStatus =\n | \"filled\"\n | \"proposed\"\n | \"nothing-proposed\"\n | \"skipped\"\n | \"skipped-budget\"\n | \"error\";\n\nexport interface ProposedEval {\n id: string;\n assertion: string;\n confidence: number;\n examples: { pass: string; fail: string };\n type?: \"capability\" | \"regression\";\n evidence?: string;\n severity?: Severity;\n rationale?: string;\n}\n\n/**\n * One `meta-provenance` entry after `fill` merged into it: the model, the ids\n * of the evals it proposed, and the confidence each was written at. An entry\n * `fill` merged into keeps whatever else it held, `fields` included.\n */\nexport interface EvalsMetaProvenanceEntry {\n \"generated-by\": string;\n evals: string[];\n confidence: Record<string, number>;\n [other: string]: unknown;\n}\n\n/**\n * What became of this page's `meta-provenance` entry. Never a failure. The\n * shape is `manni meta fill`'s, and so are its reasons: the key follows its\n * own location (proposal 0047), so a local manifest holds the entry, a URL\n * manifest cannot be written, and a manifest joining on a field the page\n * lacks has no entry to hold it.\n */\nexport type EvalsMetaProvenanceReport =\n | {\n written: true;\n entry: EvalsMetaProvenanceEntry;\n /** The manifest the entry went into, when one owns `meta-provenance`. */\n destination?: string;\n }\n /** The page's `meta-provenance` is not a list, so there is nothing to merge into. */\n | { written: false; skipReason: \"schema-mismatch\" }\n /** A URL manifest owns the key, and nothing fetched can be written. */\n | { written: false; skipReason: \"manifest-owned\"; manifest: string }\n /** The owning manifest joins on a field this page does not carry. */\n | { written: false; skipReason: \"unwritable\" };\n\nexport interface FillPageResult {\n file: string;\n status: FillStatus;\n /** Appended (or, in a dry run, would-append) proposals. */\n written: ProposedEval[];\n /** Proposals below the confidence threshold — reported, never written. */\n belowThreshold: ProposedEval[];\n /** Fresh proposals dropped for exceeding `fill.maxEvalsPerPage`. */\n capped: ProposedEval[];\n /** Proposed ids that already exist in the page's resolved plan. */\n duplicates: string[];\n cached: boolean;\n error?: string;\n /**\n * Where the evals went (proposal 0047): `\"page\"`, or the manifest that owns\n * them, as the run reports its path. Absent when nothing was written.\n */\n wroteTo?: string;\n /**\n * The `meta-provenance` entry recording the evals this run wrote. Absent\n * when none was written; reported, not written, under `--dry-run`.\n */\n metaProvenance?: EvalsMetaProvenanceReport;\n}\n\nexport interface FillReport {\n results: FillPageResult[];\n threshold: number;\n /** Inference calls actually made. One per proposed page; a cache hit is none. */\n turns: number;\n exitCode: 0 | 1;\n}\n\n/**\n * Proposals become inline evals with explicit grader/type. Confidence is no\n * longer report-only — it is written to `meta-provenance` alongside the model\n * that proposed it, so the page itself records what a machine wrote and how\n * sure it was, and a reviewer retires the entry when they have checked it.\n */\nfunction toEntry(p: ProposedEval): NewEvalEntry {\n return {\n id: p.id,\n assertion: p.assertion,\n type: p.type ?? \"regression\",\n grader: \"ai\",\n evidence: p.evidence,\n examples: p.examples,\n severity: p.severity,\n };\n}\n\nexport async function runFill(\n paths: string[],\n options: FillOptions = {},\n): Promise<FillReport> {\n const cwd = options.cwd ?? process.cwd();\n let config = loadRunConfig(runConfigOptions(paths, options), cwd);\n const flags = { provider: options.provider, model: options.model, local: options.local };\n // Checked up front, and regardless of an injected provider: it costs nothing,\n // and a typo must fail on a run where no page needs a model too.\n assertProviderSelection(selectProvider(config, flags));\n const load = async (): Promise<ResolvedPagePlan[]> =>\n resolvePages(\n await withExternalMetadata(\n discoverPages(config, documentSet(paths, options, \"fill\"), cwd),\n config,\n cwd,\n ),\n config,\n );\n let plans = await load();\n\n // Proposal 0047: where each page's evals go. Built before the first model\n // request, so P1 is asked — and an accepted relocation applied — while\n // nothing has been proposed yet, exactly as `manni meta fill` asks it\n // between preparing a file and inferring for it.\n const writerFor = (): EvalWriter => EvalWriter.for(config, cwd, paths);\n let writer = writerFor();\n if (options.confirm !== undefined && options.dryRun !== true) {\n const flagged: ExternalWrite[] = [];\n for (const plan of plans) {\n if (plan.skip || plan.problems.some((p) => p.level === \"error\")) continue;\n const home = await writer.homeFor(\n plan.page.file,\n plan.page.frontmatter.data,\n EVALS_KEY,\n );\n if (home.kind === \"page\" && home.prefersExternal) {\n flagged.push({ label: plan.page.file, key: EVALS_KEY, home: home.proposed });\n }\n }\n const applied = await writer.offer(flagged, {\n confirm: options.confirm,\n ...(options.onNotice === undefined ? {} : { onNotice: options.onNotice }),\n ...(options.onRelocated === undefined ? {} : { onRelocated: options.onRelocated }),\n });\n if (applied) {\n // The config now declares a manifest, the pages no longer carry what it\n // owns, and this writer's context predates both.\n config = loadRunConfig(runConfigOptions(paths, options), cwd);\n plans = await load();\n writer = writerFor();\n }\n }\n\n const threshold = options.confidence ?? config.fill.confidenceThreshold;\n const maxTurns = options.maxTurns ?? config.fill.maxTurns;\n const temperature = config.fill.temperature;\n const maxEvals = config.fill.maxEvalsPerPage;\n const chunkChars = options.chunkChars ?? config.fill.chunkChars;\n const cache = new FillCache(\n resolve(cwd, config.fill.cacheDir),\n !options.noCache,\n );\n\n // Identity is resolved without constructing the provider, so a fully cached\n // run needs no API key, and on first use, so an all-skipped run never detects\n // a provider under `auto`. Resolved once, and shared by every page.\n const injected = options.providerInstance;\n let resolving: ReturnType<typeof resolveProviderIdentity> | undefined;\n const resolveOnce = (): ReturnType<typeof resolveProviderIdentity> =>\n (resolving ??= resolveProviderIdentity(config, flags));\n const getIdentity = async (): Promise<{ provider: string; model: string }> =>\n injected\n ? { provider: injected.provider(), model: injected.modelName() }\n : resolveOnce();\n let provider = injected;\n const getProvider = async (): Promise<InferenceProvider> =>\n (provider ??= constructProvider(config, await resolveOnce()));\n let turns = 0;\n const results: FillPageResult[] = [];\n\n for (const plan of plans) {\n results.push(await fillOne(plan));\n }\n\n // W1 and W2 (0047): evals that landed on a page whose schema prefers them in\n // external metadata. One line per collection, not per page.\n for (const line of writer.warnings(options.dryRun === true)) warn(line);\n\n return {\n results,\n threshold,\n turns,\n exitCode: results.some((r) => r.status === \"error\") ? 1 : 0,\n };\n\n async function fillOne(plan: ResolvedPagePlan): Promise<FillPageResult> {\n const base: FillPageResult = {\n file: plan.page.file,\n status: \"nothing-proposed\",\n written: [],\n belowThreshold: [],\n capped: [],\n duplicates: [],\n cached: false,\n };\n if (plan.skip) return { ...base, status: \"skipped\" };\n const problem = plan.problems.find((p) => p.level === \"error\");\n if (problem) return { ...base, status: \"error\", error: problem.message };\n // Reject non-YAML frontmatter before spending an LLM call: it can't be\n // appended to (appendPageEvals would otherwise refuse) and there is\n // nothing to fill.\n const format = leadingFrontmatterFormat(plan.page.content);\n if (format === \"toml\" || format === \"json\") {\n return {\n ...base,\n status: \"error\",\n error: `only YAML frontmatter can be filled (found ${format} frontmatter)`,\n };\n }\n\n const existing = plan.evals.map((e) => ({\n id: e.name,\n assertion: e.assertion,\n }));\n const existingNames = existing.map((e) => e.id).sort();\n // The RESOLVED model, so a provider default that changes changes the key.\n const identity = await getIdentity();\n // Keyed on the budget the proposals were actually produced at, not the\n // one the run asked for. Halve-and-retry means those differ: the split\n // boundaries move, so the parts differ, so the proposals differ. Storing\n // a halved-budget result under the full-budget key lets a later run that\n // did *not* overflow replay proposals from a split it never performed.\n const keyFor = (budget: number): string =>\n fillCacheKey(\n identity.provider,\n identity.model,\n temperature,\n maxEvals,\n plan.page.body,\n existingNames,\n budget,\n );\n // Deliberately no fallback lookup at the halved budget. Serving a halved\n // result to a run that asked for the full one is the collision itself,\n // just spread over two keys — and a page that overflows does so\n // deterministically for the same provider and body, so the retry path\n // finds its own entry from the second run onward.\n const key = keyFor(chunkChars);\n let raw = cache.get(key);\n const cached = raw !== undefined;\n if (!raw) {\n // Claimed before the call, not tallied after it (ADR 01019). A cache\n // hit never reaches here, so replaying a cached corpus spends no turns.\n if (maxTurns !== null && turns >= maxTurns) {\n return { ...base, status: \"skipped-budget\" };\n }\n // A page longer than the budget is proposed against in parts rather\n // than truncated. Each part is its own call; the results merge by eval\n // id, keeping the highest confidence — the metadata tool's `mergeProposals`.\n let budget = chunkChars;\n // This page's own parts. `turns` is the run-wide budget counter and\n // includes every earlier page, so reporting it as \"N of M parts\" names\n // a number that has nothing to do with this page.\n let partsRead = 0;\n let merged: Map<string, ProposedEval> | undefined;\n let failure: string | undefined;\n for (let attempt = 0; attempt < 2 && merged === undefined; attempt++) {\n const chunks = splitBody(plan.page.body, budget);\n const collected = new Map<string, ProposedEval>();\n let overflowed = false;\n let outOfTurns = false;\n partsRead = 0;\n for (const [i, chunk] of chunks.entries()) {\n if (maxTurns !== null && turns >= maxTurns) {\n outOfTurns = true;\n break;\n }\n turns += 1;\n partsRead += 1;\n let response;\n try {\n response = await (await getProvider()).completeJSON({\n system: FILL_SYSTEM_PROMPT,\n user: buildFillUser(\n plan.page.file,\n chunk,\n existing,\n maxEvals,\n chunks.length > 1 ? { index: i, total: chunks.length } : undefined,\n ),\n schema: PROPOSAL_SCHEMA,\n temperature,\n });\n } catch (e) {\n const message = errorMessage(e);\n failure = message;\n if (looksLikeOverflow(message)) overflowed = true;\n break;\n }\n if (!isValidProposal(response.json)) {\n failure = \"provider returned a proposal that does not match the schema\";\n break;\n }\n for (const proposal of (response.json as { evals: ProposedEval[] }).evals) {\n const held = collected.get(proposal.id);\n if (held === undefined || proposal.confidence > held.confidence) {\n collected.set(proposal.id, proposal);\n }\n }\n if (i === chunks.length - 1) merged = collected;\n }\n if (outOfTurns) {\n // Never a silent partial: a page read in part proposed from part of\n // itself, and saying so is the difference between \"nothing to add\"\n // and \"we stopped early\".\n return {\n ...base,\n status: \"skipped-budget\",\n ...(chunks.length > 1\n ? {\n error:\n `--max-turns reached after ${String(partsRead)} of ` +\n `${String(chunks.length)} part(s) of this page`,\n }\n : {}),\n };\n }\n if (merged === undefined && overflowed && attempt === 0) {\n budget = Math.max(1, Math.floor(budget / 2));\n continue;\n }\n if (merged === undefined) break;\n }\n if (merged === undefined) {\n return {\n ...base,\n status: \"error\",\n error: failure ?? \"the provider returned no proposal\",\n };\n }\n raw = { evals: [...merged.values()] };\n cache.set(keyFor(budget), raw);\n }\n\n // Drop duplicates (against the resolved plan and within the batch) before\n // applying the per-page cap, so duplicate names never crowd out fresh\n // proposals.\n const seen = new Set(existingNames);\n const duplicates: string[] = [];\n const fresh: ProposedEval[] = [];\n for (const p of raw.evals as ProposedEval[]) {\n if (seen.has(p.id)) {\n duplicates.push(p.id);\n continue;\n }\n seen.add(p.id);\n fresh.push(p);\n }\n // Proposals beyond the per-page cap are reported as `capped` rather than\n // silently dropped — they may be high-confidence, so folding them into\n // belowThreshold would misreport why they weren't written.\n const capped = fresh.slice(maxEvals);\n const belowThreshold: ProposedEval[] = [];\n const written: ProposedEval[] = [];\n for (const p of fresh.slice(0, maxEvals)) {\n if (p.confidence >= threshold) written.push(p);\n else belowThreshold.push(p);\n }\n\n const result = {\n ...base,\n written,\n belowThreshold,\n capped,\n duplicates,\n cached,\n };\n if (written.length === 0) return result;\n\n // Proposal 0047: where each key goes, settled before a byte is written.\n const label = plan.page.file;\n const data = plan.page.frontmatter.data;\n let evalsHome: WriteHome;\n let metaHome: WriteHome;\n try {\n evalsHome = await writer.homeFor(label, data, EVALS_KEY);\n metaHome = await writer.homeFor(label, data, META_PROVENANCE_KEY);\n } catch (e) {\n return { ...result, status: \"error\", error: errorMessage(e), written: [] };\n }\n if (evalsHome.kind === \"url\") throw writer.urlRefusal(evalsHome, EVALS_KEY);\n if (evalsHome.kind === \"no-entry\") {\n // The manifest owns the evals and has no entry for this page, so there\n // is nowhere to put them. Writing the page instead would put the key in\n // both places, which is the collision the routing exists to prevent.\n return { ...result, status: \"error\", error: evalsHome.message, written: [] };\n }\n\n // The evals this run proposed, recorded in the same write. A record that\n // cannot be merged is reported rather than failing the page: a side\n // record that could block filling would be worse than none, and neither\n // is `meta-provenance`'s own location a reason to fail a fill.\n const merged =\n metaHome.kind === \"url\" || metaHome.kind === \"no-entry\"\n ? undefined\n : mergeMetaProvenance(\n data[META_PROVENANCE_KEY],\n identity.model,\n \"evals\",\n written.map((p) => ({ name: p.id, confidence: p.confidence })),\n );\n const metaManifest = metaHome.kind === \"manifest\" ? metaHome : undefined;\n let metaProvenance: EvalsMetaProvenanceReport;\n if (metaHome.kind === \"url\") {\n metaProvenance = { written: false, skipReason: \"manifest-owned\", manifest: metaHome.file };\n } else if (metaHome.kind === \"no-entry\") {\n metaProvenance = { written: false, skipReason: \"unwritable\" };\n } else if (merged === undefined) {\n metaProvenance = { written: false, skipReason: \"schema-mismatch\" };\n } else if (metaManifest !== undefined) {\n metaProvenance = {\n written: true,\n entry: { ...merged.entry, evals: merged.names },\n destination: metaManifest.file,\n };\n } else {\n metaProvenance = { written: true, entry: { ...merged.entry, evals: merged.names } };\n }\n\n const manifest = evalsHome.kind === \"manifest\" ? evalsHome : undefined;\n const pageEntries = manifest === undefined ? written.map(toEntry) : [];\n const pageMeta =\n metaProvenance.written && metaManifest === undefined ? merged?.list : undefined;\n try {\n // The page first, as `meta fill` writes it, so a refused manifest leaves\n // no entry pointing at a page that was never changed.\n if (pageEntries.length > 0 || pageMeta !== undefined) {\n const updated = appendPageEvals(\n plan.page.content,\n label,\n pageEntries,\n pageMeta,\n );\n if (!options.dryRun && updated !== plan.page.content) {\n writeFileSync(plan.page.absPath, updated);\n }\n }\n if (!options.dryRun && manifest !== undefined) {\n const held = manifestEvalList(\n await writer.readManifest(manifest, EVALS_KEY),\n manifest.file,\n manifest.entry,\n );\n await writer.writeManifest(manifest, EVALS_KEY, [\n ...held,\n ...written.map((p) => entryObject(toEntry(p))),\n ]);\n }\n if (!options.dryRun && metaManifest !== undefined && merged !== undefined) {\n await writer.writeManifest(metaManifest, META_PROVENANCE_KEY, merged.list);\n }\n } catch (e) {\n return {\n ...result,\n status: \"error\",\n error: errorMessage(e),\n written: [],\n };\n }\n if (evalsHome.kind === \"page\" && evalsHome.prefersExternal) {\n writer.stayedOnPage(label, EVALS_KEY, evalsHome.proposed);\n }\n return {\n ...result,\n status: options.dryRun ? \"proposed\" : \"filled\",\n wroteTo: manifest === undefined ? \"page\" : manifest.file,\n metaProvenance,\n };\n }\n}\n\nconst STATUS_LABELS: Record<FillStatus, string> = {\n filled: \"filled\",\n proposed: \"proposed\",\n \"nothing-proposed\": \"no-op\",\n skipped: \"skipped\",\n \"skipped-budget\": \"skipped\",\n error: \"error\",\n};\n\nexport function renderFill(\n report: FillReport,\n format: SummaryFormat,\n opts: ColorOptions = {},\n): string {\n // See renderList — same reasoning, same public exposure via src/index.ts.\n parseFormat(format, SUMMARY_FORMATS, \"format\");\n if (format === \"json\") return JSON.stringify(report, null, 2);\n const pc = palette(opts.color === true);\n const lines: string[] = [];\n const names = (evals: ProposedEval[]) =>\n evals.map((p) => `${p.id} ${p.confidence.toFixed(2)}`).join(\", \");\n for (const r of report.results) {\n const label = STATUS_LABELS[r.status].padEnd(8);\n const cachedTag = r.cached ? \" [cached]\" : \"\";\n switch (r.status) {\n case \"filled\":\n lines.push(\n `${pc.green(label)} ${r.file} +${r.written.length} evals (${names(r.written)})${cachedTag}`,\n );\n break;\n case \"proposed\":\n lines.push(\n `${pc.cyan(label)} ${r.file} +${r.written.length} evals (${names(r.written)})${cachedTag} — dry run, not written`,\n );\n break;\n case \"nothing-proposed\":\n lines.push(`${pc.dim(label)} ${r.file} (nothing new proposed)${cachedTag}`);\n break;\n case \"skipped\":\n lines.push(`${pc.dim(label)} ${r.file} (evals.skip)`);\n break;\n case \"skipped-budget\":\n lines.push(`${pc.yellow(label)} ${r.file} (turn budget exhausted)`);\n break;\n case \"error\":\n lines.push(`${pc.red(label)} ${r.file}: ${r.error ?? \"unknown error\"}`);\n break;\n }\n // Where the evals went, when it was not the page (proposal 0047).\n if (r.wroteTo !== undefined && r.wroteTo !== \"page\") {\n lines.push(` ${pc.cyan(\"evals\")} → ${r.wroteTo}`);\n }\n const record = r.metaProvenance;\n if (record?.written === true) {\n const destination = record.destination === undefined ? \"\" : ` → ${record.destination}`;\n lines.push(\n ` ${pc.cyan(\"meta-provenance\")} ${record.entry[\"generated-by\"]}: ${record.entry.evals.join(\", \")}${destination}`,\n );\n } else if (record?.skipReason === \"schema-mismatch\") {\n lines.push(\n pc.dim(\" meta-provenance not written: this page's schemas do not allow it\"),\n );\n } else if (record?.skipReason === \"manifest-owned\") {\n lines.push(\n pc.dim(\n ` meta-provenance not written: owned by manifest ${record.manifest}, which is fetched and cannot be written`,\n ),\n );\n } else if (record?.skipReason === \"unwritable\") {\n lines.push(\n pc.dim(\" meta-provenance not written: its manifest has no entry for this page\"),\n );\n }\n if (r.belowThreshold.length > 0) {\n lines.push(\n pc.dim(\n ` below ${report.threshold}: ${names(r.belowThreshold)}`,\n ),\n );\n }\n if (r.capped.length > 0) {\n lines.push(\n pc.dim(` over per-page cap: ${names(r.capped)}`),\n );\n }\n if (r.duplicates.length > 0) {\n lines.push(pc.dim(` duplicates: ${r.duplicates.join(\", \")}`));\n }\n }\n lines.push(\"\");\n lines.push(\n `Threshold: ${report.threshold} · inference calls: ${report.turns}`,\n );\n return lines.join(\"\\n\");\n}\n","/**\n * Fill proposal cache: content-addressed JSON files storing the raw,\n * pre-gating proposal — re-running with a different confidence threshold\n * re-gates from cache with zero API calls. Folding the existing eval-name set\n * into the key means a post-fill re-run misses (the set changed) and asks for\n * additional coverage instead of replaying stale proposals. Separate from the\n * judge cache: different key scheme and value shape.\n */\nimport { mkdirSync, readFileSync, writeFileSync, existsSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { sha256 } from \"../judge/cache.js\";\nimport { FILL_PROMPT_VERSION, isValidProposal } from \"./prompt.js\";\nimport { warn } from \"../../shared/warn.js\";\nimport { errorMessage } from \"../../shared/errors.js\";\n\nexport function fillCacheKey(\n provider: string,\n model: string,\n temperature: number,\n maxEvals: number,\n body: string,\n existingNames: string[],\n chunkChars: number,\n): string {\n return sha256(\n [\n provider,\n model,\n `fill-v${FILL_PROMPT_VERSION}`,\n `t${temperature}`,\n `n${maxEvals}`,\n // The chunk budget decides how the page was split, and so which\n // proposals came back. Without it, two runs at different budgets share\n // a key and the second silently replays the first — and halve-and-retry\n // makes that a real collision, not a hypothetical one.\n `chunk${chunkChars}`,\n sha256(body),\n existingNames.join(\",\"),\n ].join(\"|\"),\n );\n}\n\nexport class FillCache {\n /** Cache-write failures warn once per run, not once per page. */\n private warned = false;\n\n constructor(\n private readonly dir: string,\n private readonly enabled: boolean = true,\n ) {}\n\n get(key: string): Record<string, unknown> | undefined {\n if (!this.enabled) return undefined;\n const path = join(this.dir, `${key}.json`);\n if (!existsSync(path)) return undefined;\n try {\n const proposal = JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n // A schema-invalid entry (older format, corruption) is a miss, not an error.\n if (!isValidProposal(proposal)) return undefined;\n return proposal as Record<string, unknown>;\n } catch {\n return undefined; // Corrupt cache entry — treat as a miss.\n }\n }\n\n set(key: string, proposal: Record<string, unknown>): void {\n if (!this.enabled) return;\n // The cache is an optimization: a write failure must never abort a run\n // whose proposal already succeeded.\n try {\n mkdirSync(this.dir, { recursive: true });\n writeFileSync(\n join(this.dir, `${key}.json`),\n JSON.stringify(proposal, null, 2),\n );\n } catch (e) {\n if (!this.warned) {\n this.warned = true;\n warn(\n `could not write the fill cache at ${this.dir} (${errorMessage(e)}). Continuing without caching.`,\n );\n }\n }\n }\n}\n","/**\n * Fill prompt: asks the provider to propose ai-graded evals for a page, each\n * with a self-reported 0-1 confidence. Proposals are gated by the confidence\n * threshold downstream; the prompt itself only demands honesty and restraint.\n * `command` evals are deliberately out of scope — a command eval without a\n * command would seed script generation (and eventual execution) on the next\n * run, and determinism already flows through promote/generate.\n */\nimport { Ajv2020 } from \"ajv/dist/2020.js\";\nimport { partLabel } from \"../core/split.js\";\nimport { SEVERITIES } from \"../../shared/severity.js\";\n\n/** Part of the cache key: bump whenever the prompt or schema changes. */\nexport const FILL_PROMPT_VERSION = 4;\n\nexport const FILL_SYSTEM_PROMPT = [\n \"You propose evals for documentation pages. An eval is a plain-language\",\n \"assertion about a page that an LLM judge will grade against the page\",\n \"content on every future run — a durable quality contract, not a one-off\",\n \"observation.\",\n \"\",\n \"Rules for every proposal:\",\n \"- Assert durable, page-specific properties a maintainer would want\",\n \" guarded (structure, coverage, accuracy anchors) — never incidental\",\n \" phrasing, styling, or facts likely to change by design.\",\n \"- The assertion must be judgeable from the page content alone.\",\n \"- Ids are short kebab-case identifiers, unique on the page.\",\n \"- Provide examples.pass and examples.fail: one sentence each describing\",\n \" a state of the page that satisfies or violates the assertion.\",\n \"- Report an honest confidence between 0 and 1 that the assertion is\",\n \" correct, checkable, and worth guarding. Do not inflate it.\",\n \"- Do not duplicate or rephrase the page's existing evals.\",\n \"- Do NOT restate the page. An assertion derived from the page's own\",\n \" headings, or lifted from a style guide, passes by construction and keeps\",\n \" passing however the page rots. Anchor on what a reader would notice if it\",\n \" broke.\",\n \"- Prefer the cheapest check that can express the assertion. A verifiable\",\n \" fact — a string that must appear, a file that must exist — belongs in a\",\n \" deterministic eval; propose a judged assertion only for what genuinely\",\n \" needs reading comprehension.\",\n \"- A check lifted from a style rule is secondary: propose it at\",\n \" severity warning, never as a page's only guard.\",\n \"- Do not soften an assertion until it always passes. An eval that cannot\",\n \" fail is a vanity metric — if you cannot state a version that would catch\",\n \" a real regression, propose nothing for it.\",\n \"- Propose at most the requested number, fewer when the page offers\",\n \" little worth guarding, and nothing at all when nothing qualifies.\",\n].join(\"\\n\");\n\nexport const PROPOSAL_SCHEMA = {\n type: \"object\",\n required: [\"evals\"],\n properties: {\n evals: {\n type: \"array\",\n items: {\n type: \"object\",\n required: [\"id\", \"assertion\", \"confidence\", \"examples\"],\n properties: {\n id: { type: \"string\", pattern: \"^[a-z0-9][a-z0-9-]*$\" },\n assertion: { type: \"string\", minLength: 1 },\n type: { enum: [\"capability\", \"regression\"] },\n evidence: { type: \"string\" },\n examples: {\n type: \"object\",\n required: [\"pass\", \"fail\"],\n properties: {\n pass: { type: \"string\" },\n fail: { type: \"string\" },\n },\n additionalProperties: false,\n },\n severity: { enum: SEVERITIES },\n confidence: { type: \"number\", minimum: 0, maximum: 1 },\n rationale: { type: \"string\" },\n },\n additionalProperties: false,\n },\n },\n },\n additionalProperties: false,\n} as const;\n\nconst ajv = new Ajv2020({ allErrors: true });\nconst validateProposal = ajv.compile(\n PROPOSAL_SCHEMA as unknown as Record<string, unknown>,\n);\n\n/** True when `value` matches PROPOSAL_SCHEMA. */\nexport function isValidProposal(value: unknown): boolean {\n return validateProposal(value);\n}\n\nexport interface ExistingEval {\n id: string;\n assertion?: string;\n}\n\nexport function buildFillUser(\n file: string,\n body: string,\n existing: ExistingEval[],\n maxEvals: number,\n part?: { index: number; total: number },\n): string {\n const existingLines =\n existing.length === 0\n ? [\"(none)\"]\n : existing.map((e) =>\n e.assertion ? `- ${e.id}: ${e.assertion}` : `- ${e.id}`,\n );\n return [\n \"# Page path\",\n file,\n \"\",\n \"# Existing evals (do not duplicate these)\",\n ...existingLines,\n \"\",\n \"# Maximum proposals\",\n String(maxEvals),\n \"\",\n part === undefined\n ? \"# Page content\"\n : `# Page content (${partLabel(part.index, part.total)})`,\n \"\",\n // No cap. A page longer than one call is split and proposed against in\n // parts; truncating instead meant proposing evals for a page whose second\n // half the model never saw, with nothing in the output to say so.\n body,\n ].join(\"\\n\");\n}\n","/**\n * `manni docevals promote` — the grader hierarchy in tool form: review ai-graded\n * evals, ask the LLM which are expressible as deterministic checks, and (with\n * --write) rewrite them as command-graded evals backed by generated scripts.\n * Never runs automatically as part of `manni docevals run`.\n */\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, relative } from \"node:path\";\nimport { loadRunConfig } from \"../core/config.js\";\nimport {\n discoverPages,\n documentSet,\n runConfigOptions,\n type DocumentInputOptions,\n} from \"../core/discover.js\";\nimport { withExternalMetadata } from \"../core/external.js\";\nimport { resolvePages } from \"../core/resolve.js\";\nimport { assertRegisteredGraders } from \"../core/feasibility.js\";\nimport {\n hasEditableEval,\n updateConfigEval,\n updatePageEval,\n} from \"../core/frontmatter-edit.js\";\nimport {\n EVALS_KEY,\n EvalWriter,\n manifestEvalList,\n updateManifestEval,\n} from \"../core/write-location.js\";\nimport { sha256 } from \"../judge/cache.js\";\nimport {\n assertProviderSelection,\n makeProvider,\n selectProvider,\n} from \"../judge/provider.js\";\nimport type { InferenceProvider } from \"@hawkeyexl/inference\";\nimport {\n SCRIPTGEN_SYSTEM_PROMPT,\n buildScriptgenUser,\n scriptLocationFor,\n} from \"../graders/scriptgen.js\";\nimport type { GraderTarget } from \"../graders/types.js\";\nimport { errorMessage } from \"../../shared/errors.js\";\n\nconst PROMOTE_SCHEMA = {\n type: \"object\",\n required: [\"promotable\", \"rationale\"],\n properties: {\n promotable: {\n type: \"boolean\",\n description:\n \"True only when the assertion can be fully checked by a deterministic script with no semantic judgment.\",\n },\n rationale: { type: \"string\" },\n code: {\n type: \"string\",\n description: \"The check script source, required when promotable is true.\",\n },\n },\n additionalProperties: false,\n} as const;\n\nconst PROMOTE_SYSTEM = [\n \"You review documentation eval assertions and decide whether each can be\",\n \"verified by a deterministic script instead of LLM judgment. Promote only\",\n \"assertions that are fully checkable with string/regex/structural logic —\",\n \"anything requiring semantic interpretation stays with the LLM judge.\",\n \"\",\n SCRIPTGEN_SYSTEM_PROMPT,\n \"\",\n 'Respond with JSON: { \"promotable\": bool, \"rationale\": \"...\", \"code\": \"...\" }',\n \"(omit code when not promotable).\",\n].join(\"\\n\");\n\nexport interface PromoteOptions extends DocumentInputOptions {\n write?: boolean;\n provider?: string;\n model?: string;\n /** Run inference on this machine, with llama-cpp, over any configured or eval-level provider. */\n local?: boolean;\n cwd?: string;\n /** Injectable provider for tests and programmatic use. */\n providerInstance?: InferenceProvider;\n}\n\nexport interface PromoteProposal {\n file: string;\n evalName: string;\n source: \"page\" | \"config\";\n promotable: boolean;\n rationale: string;\n scriptPath?: string;\n applied: boolean;\n /**\n * Why a promotable eval could not be rewritten (proposal 0047): the\n * manifest that owns this page's evals joins on a field the page lacks, so\n * there is no entry to hold the promoted eval. `applied` stays false.\n */\n error?: string;\n}\n\nasync function assess(\n provider: InferenceProvider,\n target: GraderTarget,\n): Promise<{ promotable: boolean; rationale: string; code?: string }> {\n try {\n const response = await provider.completeJSON({\n system: PROMOTE_SYSTEM,\n user: buildScriptgenUser(\n target.eval.assertion ?? \"\",\n target.eval.name,\n target.plan.page.file,\n target.plan.page.body,\n ),\n schema: PROMOTE_SCHEMA,\n temperature: 0,\n });\n const json = response.json as {\n promotable?: unknown;\n rationale?: unknown;\n code?: unknown;\n };\n return {\n promotable: json.promotable === true,\n rationale: typeof json.rationale === \"string\" ? json.rationale : \"\",\n code: typeof json.code === \"string\" ? json.code : undefined,\n };\n } catch (e) {\n return {\n promotable: false,\n rationale: `assessment failed: ${errorMessage(e)}`,\n };\n }\n}\n\nexport async function runPromote(\n paths: string[],\n options: PromoteOptions = {},\n): Promise<PromoteProposal[]> {\n const cwd = options.cwd ?? process.cwd();\n const config = loadRunConfig(runConfigOptions(paths, options), cwd);\n const flags = { provider: options.provider, model: options.model, local: options.local };\n // Checked before discovery, and even when nothing is ai-graded.\n assertProviderSelection(selectProvider(config, flags));\n const pages = await withExternalMetadata(\n discoverPages(config, documentSet(paths, options, \"read\"), cwd),\n config,\n cwd,\n );\n const plans = resolvePages(pages, config);\n assertRegisteredGraders(plans, config);\n\n // Built on first use, not up front. A corpus with no ai-graded evals has\n // nothing to assess, and demanding an API key to be told so is the same\n // needless gate `fill` already avoids by resolving identity lazily.\n let provider: InferenceProvider | undefined = options.providerInstance;\n const getProvider = async (): Promise<InferenceProvider> =>\n (provider ??= await makeProvider(config, flags));\n\n const proposals: PromoteProposal[] = [];\n const seenConfigEvals = new Set<string>();\n // Proposal 0047: a promoted eval is rewritten where it lives. Built on\n // first use, like the provider.\n let writer: EvalWriter | undefined;\n const getWriter = (): EvalWriter => (writer ??= EvalWriter.for(config, cwd, paths));\n\n for (const plan of plans) {\n if (plan.skip || plan.problems.some((p) => p.level === \"error\")) continue;\n for (const ev of plan.evals) {\n if (ev.skip || ev.grader !== \"ai\" || !ev.assertion) continue;\n if (ev.source === \"config\") {\n if (seenConfigEvals.has(ev.name)) continue;\n seenConfigEvals.add(ev.name);\n }\n\n const target: GraderTarget = { plan, eval: ev };\n const assessment = await assess(await getProvider(), target);\n const proposal: PromoteProposal = {\n file: plan.page.file,\n evalName: ev.name,\n source: ev.source,\n promotable: assessment.promotable,\n rationale: assessment.rationale,\n applied: false,\n };\n\n if (assessment.promotable && assessment.code && options.write) {\n const location = scriptLocationFor(target, config, cwd);\n mkdirSync(dirname(location.scriptAbsPath), { recursive: true });\n writeFileSync(location.scriptAbsPath, assessment.code);\n proposal.scriptPath = relative(cwd, location.scriptAbsPath).replace(\n /\\\\/g,\n \"/\",\n );\n const updates = {\n grader: \"command\",\n command: location.command,\n // The kebab key `applyUpdates` actually reads. This was the\n // pre-1.0 `generated: { assertionHash }` wrapper, which the update\n // type does not consume, so promoted evals were written with no\n // hash and never went stale when their assertion changed\n // (renamed by ADR 01009).\n \"generated-assertion-hash\": sha256(ev.assertion),\n };\n // Where the eval lives decides where the rewrite goes. The page's own\n // text is only consulted for a page-owned eval: a page whose evals a\n // manifest owns carries none of them, so `hasEditableEval` is false\n // there and the promotion used to be dropped without a word.\n const home =\n ev.source === \"page\"\n ? await getWriter().homeFor(\n plan.page.file,\n plan.page.frontmatter.data,\n EVALS_KEY,\n )\n : undefined;\n if (home?.kind === \"url\") {\n throw getWriter().urlRefusal(home, EVALS_KEY);\n }\n if (home?.kind === \"no-entry\") {\n proposal.error = home.message;\n } else if (home?.kind === \"manifest\") {\n const at = getWriter();\n const list = manifestEvalList(\n await at.readManifest(home, EVALS_KEY),\n home.file,\n home.entry,\n );\n const next = updateManifestEval(list, ev.name, updates);\n if (next === undefined) {\n proposal.error = `${home.file}: eval \"${ev.name}\" not found in the entry for ${home.entry}`;\n } else {\n await at.writeManifest(home, EVALS_KEY, next);\n proposal.applied = true;\n }\n } else if (\n ev.source === \"page\" &&\n hasEditableEval(plan.page.content, ev.name)\n ) {\n const updated = updatePageEval(\n readFileSync(plan.page.absPath, \"utf8\"),\n plan.page.file,\n ev.name,\n updates,\n );\n writeFileSync(plan.page.absPath, updated);\n proposal.applied = true;\n } else if (ev.source === \"config\") {\n const updated = updateConfigEval(\n readFileSync(config.configPath, \"utf8\"),\n config.configPath,\n ev.name,\n updates,\n );\n writeFileSync(config.configPath, updated);\n proposal.applied = true;\n }\n }\n proposals.push(proposal);\n }\n }\n return proposals;\n}\n","/**\n * `manni docevals review` — record human verdicts for evals in the human-review\n * zone. A verdict binds to the page's current content hash; edits to the page\n * return the eval to needs-review on the next run.\n *\n * manni docevals review # list recorded reviews\n * manni docevals review <file> <eval> pass # record a verdict\n */\nimport { existsSync } from \"node:fs\";\nimport { resolve, relative } from \"node:path\";\nimport { palette } from \"../../shared/color.js\";\nimport type { ColorOptions } from \"../reporters/pretty.js\";\nimport { DocevalsError } from \"../types.js\";\nimport { readPage } from \"../core/discover.js\";\nimport {\n contentHash,\n loadReviews,\n recordReview,\n type ReviewEntry,\n} from \"../core/reviews.js\";\n\nexport interface ReviewOptions {\n reviewer?: string;\n note?: string;\n cwd?: string;\n}\n\nexport function listReviews(cwd = process.cwd()): ReviewEntry[] {\n return loadReviews(cwd);\n}\n\nexport function renderReviews(\n reviews: ReviewEntry[],\n opts: ColorOptions = {},\n): string {\n if (reviews.length === 0) return \"No recorded reviews.\";\n const pc = palette(opts.color === true);\n return reviews\n .map((r) => {\n const verdict =\n r.verdict === \"pass\" ? pc.green(r.verdict) : pc.red(r.verdict);\n const meta = [r.reviewer, r.date].filter(Boolean).join(\" \");\n return `${verdict} ${r.file} ${pc.bold(r.evalName)}${meta ? pc.dim(` (${meta})`) : \"\"}${r.note ? pc.dim(` — ${r.note}`) : \"\"}`;\n })\n .join(\"\\n\");\n}\n\nexport function runReview(\n file: string,\n evalName: string,\n verdict: string,\n options: ReviewOptions = {},\n): ReviewEntry {\n const cwd = options.cwd ?? process.cwd();\n if (verdict !== \"pass\" && verdict !== \"fail\") {\n throw new DocevalsError(`Verdict must be \"pass\" or \"fail\", got \"${verdict}\"`);\n }\n const absPath = resolve(cwd, file);\n if (!existsSync(absPath)) {\n throw new DocevalsError(`Page not found: ${absPath}`);\n }\n const page = readPage(absPath, cwd);\n const entry: ReviewEntry = {\n file: relative(cwd, absPath).replace(/\\\\/g, \"/\"),\n evalName,\n contentHash: contentHash(page.body),\n verdict,\n reviewer: options.reviewer,\n date: new Date().toISOString().slice(0, 10),\n note: options.note,\n };\n recordReview(cwd, entry);\n return entry;\n}\n","/**\n * `manni docevals calibrate` — measure judge agreement against a human-verified\n * golden set (the book's step 3: calibrate judges before trusting them).\n * Golden cases live in YAML files under .manni/docevals/golden/ by default:\n *\n * - file: docs/install.md\n * eval: no-future-promises # ai-graded eval resolvable on that page\n * expected: pass\n * rationale: Mentions only shipped features.\n *\n * Below 70% agreement the command exits 1 — the criteria (usually) need to be\n * more specific, not the grading mechanism. A false-positive rate above\n * judge.falsePositiveAlert flags retuning.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport fg from \"fast-glob\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport { palette } from \"../../shared/color.js\";\nimport type { ColorOptions } from \"../reporters/pretty.js\";\nimport { DocevalsError } from \"../types.js\";\nimport { loadConfig } from \"../core/config.js\";\nimport { readPage } from \"../core/discover.js\";\nimport { loadExternalReader } from \"../core/external.js\";\nimport { resolvePage } from \"../core/resolve.js\";\nimport { contentHash, loadReviews } from \"../core/reviews.js\";\nimport { makeJudge } from \"../judge/judge.js\";\nimport { isTurnBudgetSkip } from \"../judge/budget.js\";\nimport {\n assertProviderSelection,\n makeProvider,\n selectProvider,\n} from \"../judge/provider.js\";\nimport type { JudgeFn } from \"../core/engine.js\";\nimport type { GraderTarget } from \"../graders/types.js\";\n\nexport const AGREEMENT_THRESHOLD = 0.7;\n\n/** Default golden directory, relative to the working directory. */\nexport const GOLDEN_DIR = join(\".manni/docevals\", \"golden\");\n\n/** File `--seed` writes. Named so a hand-authored set beside it stays distinct. */\nexport const SEEDED_GOLDEN_FILE = \"from-reviews.yaml\";\n\nexport interface GoldenCase {\n file: string;\n eval: string;\n expected: \"pass\" | \"fail\";\n rationale?: string;\n /**\n * Whether a human has confirmed this case belongs in the golden set.\n *\n * Absent means `false`, deliberately. This is the instrument that measures\n * the judge, so a back-compatible default would silently bless every case\n * that already exists — including the seeded ones the bit exists to surface.\n */\n reviewed: boolean;\n /** Page body hash when the verdict was formed; mismatch means stale. */\n contentHash?: string;\n source?: \"review\" | \"manual\";\n reviewedBy?: string;\n}\n\nexport interface CalibrationCaseResult extends GoldenCase {\n judged?: \"pass\" | \"fail\";\n agrees?: boolean;\n error?: string;\n /**\n * Set when the turn budget, not the judge, is why this case has no verdict.\n * A flag rather than a substring of `error`: the message is user-facing and\n * interpolates a golden case's own `eval` value, so matching on it both\n * broke when the wording changed and miscounted a case named after it.\n */\n budgetSkipped?: boolean;\n /** The page no longer hashes to what this case was verified against. */\n stale?: boolean;\n}\n\nexport interface CalibrationReport {\n cases: CalibrationCaseResult[];\n total: number;\n agreements: number;\n agreementRate: number;\n falsePositives: number;\n falsePositiveRate: number;\n falseNegatives: number;\n /**\n * True when `agreementRate >= AGREEMENT_THRESHOLD` — a statement about\n * the cases that were *judged*, and nothing else.\n *\n * Read `unjudged === 0` alongside it. A budget-truncated or partly\n * unresolvable run can meet the rate on the sample it measured while\n * `unjudged > 0` says it measured less than the set. The CLI gates on both;\n * a consumer reading this field alone gets a pass on a partial run.\n */\n meetsThreshold: boolean;\n /** Judged cases a human marked `expected: pass`. */\n expectedPass: number;\n /** Judged cases a human marked `expected: fail`. */\n expectedFail: number;\n /**\n * True when both classes are represented among the judged cases.\n *\n * A golden set of only-passes certifies a judge that always answers pass:\n * agreement 100%, false negatives 0, threshold met. The measurement is\n * vacuous, not good. Kept out of `meetsThreshold` for the same reason\n * `unjudged` is (ADR 01018) — that stays a statement about agreement, and\n * this is a statement about whether the set could have detected a\n * disagreement at all. The CLI gates on all three.\n */\n balanced: boolean;\n fpAlert: boolean;\n /** Judged cases no human has confirmed. Counted, but reported. */\n unreviewed: number;\n /** Judged cases whose page has changed since verification. */\n stale: number;\n /** Cases the turn budget prevented from being judged at all. */\n budgetSkipped: number;\n /**\n * Cases that never reached a verdict, for any reason: a missing page, an\n * unresolvable eval, an errored judge, or the turn budget. The agreement\n * rate is over the rest, so a non-zero value means it measured a sample.\n *\n * Kept separate from `meetsThreshold` rather than folded into it, following\n * ADR 01018: the verdict stays a statement about agreement, and this stays a\n * statement about coverage. Overloading one boolean made the report print\n * \"refine your assertions\" at 100% agreement.\n */\n unjudged: number;\n}\n\nexport interface CalibrateOptions {\n config?: string;\n golden?: string;\n provider?: string;\n model?: string;\n /** Run inference on this machine, with llama-cpp, over any configured or eval-level provider. */\n local?: boolean;\n runs?: number;\n maxTurns?: number | null;\n noCache?: boolean;\n cwd?: string;\n /** Injectable judge for tests. */\n judge?: JudgeFn;\n}\n\n/** File keys are kebab-case (ADR 01010); everything downstream is camelCase. */\nfunction normalizeGoldenCase(raw: Record<string, unknown>): GoldenCase {\n return {\n file: raw.file as string,\n eval: raw.eval as string,\n expected: raw.expected as \"pass\" | \"fail\",\n ...(typeof raw.rationale === \"string\" ? { rationale: raw.rationale } : {}),\n reviewed: raw.reviewed === true,\n ...(typeof raw[\"content-hash\"] === \"string\"\n ? { contentHash: raw[\"content-hash\"] }\n : {}),\n ...(raw.source === \"review\" || raw.source === \"manual\"\n ? { source: raw.source }\n : {}),\n ...(typeof raw[\"reviewed-by\"] === \"string\"\n ? { reviewedBy: raw[\"reviewed-by\"] }\n : {}),\n };\n}\n\n/** camelCase back out to the kebab file vocabulary, dropping empty fields. */\nfunction serializeGoldenCase(c: GoldenCase): Record<string, unknown> {\n return {\n file: c.file,\n eval: c.eval,\n expected: c.expected,\n ...(c.rationale === undefined ? {} : { rationale: c.rationale }),\n reviewed: c.reviewed,\n ...(c.contentHash === undefined ? {} : { \"content-hash\": c.contentHash }),\n ...(c.source === undefined ? {} : { source: c.source }),\n ...(c.reviewedBy === undefined ? {} : { \"reviewed-by\": c.reviewedBy }),\n };\n}\n\nexport function loadGoldenCases(dir: string): GoldenCase[] {\n if (!existsSync(dir)) {\n throw new DocevalsError(\n `Golden set directory not found: ${dir} — seed it from recorded reviews with ` +\n `\\`manni docevals calibrate --seed\\`, or hand-author 20-50 verified cases`,\n );\n }\n const files = fg.sync(\"*.{yaml,yml}\", { cwd: dir, absolute: true });\n const cases: GoldenCase[] = [];\n for (const file of files) {\n const raw = parseYaml(readFileSync(file, \"utf8\")) as unknown;\n if (!Array.isArray(raw)) continue;\n for (const item of raw) {\n const c = item as Record<string, unknown>;\n if (\n typeof c.file === \"string\" &&\n typeof c.eval === \"string\" &&\n (c.expected === \"pass\" || c.expected === \"fail\")\n ) {\n cases.push(normalizeGoldenCase(c));\n } else {\n throw new DocevalsError(\n `Invalid golden case in ${file}: needs file, eval, expected: pass|fail`,\n );\n }\n }\n }\n if (cases.length === 0) {\n throw new DocevalsError(`No golden cases found in ${dir}`);\n }\n return cases;\n}\n\nexport interface SeedResult {\n path: string;\n added: number;\n updated: number;\n total: number;\n /** Cases in the written file still awaiting human confirmation. */\n unreviewed: number;\n}\n\n/**\n * Turn recorded human reviews into golden *candidates* (ADR 01016).\n *\n * Judges nothing and constructs no provider, which is what lets this run in CI\n * where there is no API key. Every case lands `reviewed: false`: a `review`\n * verdict is a call made on one page to clear a queue, and promoting it to\n * calibration ground truth without a second look conflates \"I unblocked this\n * build\" with \"this is what correct looks like\".\n */\nexport function seedGoldenCases(options: CalibrateOptions = {}): SeedResult {\n const cwd = options.cwd ?? process.cwd();\n const dir = resolve(cwd, options.golden ?? GOLDEN_DIR);\n const path = join(dir, SEEDED_GOLDEN_FILE);\n\n // An emptied or hand-broken seed file degrades to \"nothing recorded yet\"\n // rather than throwing, the same way `loadReviews` treats its own store: the\n // next seed then rebuilds it, where a throw would strand the user.\n const priorRaw: unknown = existsSync(path)\n ? parseYaml(readFileSync(path, \"utf8\"))\n : null;\n const existing: GoldenCase[] = Array.isArray(priorRaw)\n ? priorRaw\n .filter((i): i is Record<string, unknown> => typeof i === \"object\" && i !== null)\n .map(normalizeGoldenCase)\n : [];\n\n // NUL-separated, as the metadata tool's fingerprint keys are, and for the same reason:\n // a printable separator lets (\"a b\", \"c\") and (\"a\", \"b c\") collide into one\n // key. A file path may contain a space; nothing may contain a NUL.\n const byKey = new Map(existing.map((c) => [`${c.file}\\0${c.eval}`, c]));\n let added = 0;\n let updated = 0;\n\n for (const review of loadReviews(cwd)) {\n const key = `${review.file}\\0${review.evalName}`;\n const prior = byKey.get(key);\n // Re-seeding as reviews accumulate must not duplicate, and must not\n // silently un-review a case a human has already confirmed. But a confirmed\n // bit describes a *verdict*, not a filename: if the recorded verdict has\n // flipped since, this is materially a different case and has to be read\n // again. Carrying the bit across a flip would let `expected` change under a\n // human's signature.\n const confirmedStillApplies =\n prior?.reviewed === true && prior.expected === review.verdict;\n const next: GoldenCase = {\n file: review.file,\n eval: review.evalName,\n expected: review.verdict,\n // A review's note wins when it has one; otherwise keep whatever the\n // human wrote into the file. Rebuilding the entry from the review alone\n // erased the reasoning behind a case the same human had just marked\n // `reviewed: true` -- and it is the field renderCalibration prints on\n // every disagreement.\n ...(review.note !== undefined\n ? { rationale: review.note }\n : prior?.rationale !== undefined\n ? { rationale: prior.rationale }\n : {}),\n reviewed: confirmedStillApplies,\n contentHash: review.contentHash,\n source: \"review\",\n ...(confirmedStillApplies && prior.reviewedBy !== undefined\n ? { reviewedBy: prior.reviewedBy }\n : {}),\n };\n byKey.set(key, next);\n // A re-seed rewrites every case, but only a *changed* one is an update.\n // Counting every key that already existed made `updated` report movement\n // on a run that moved nothing — and the two counts are separate\n // (ADR 01016) precisely so the difference between runs is visible.\n if (!prior) added += 1;\n else if (\n JSON.stringify(serializeGoldenCase(prior)) !==\n JSON.stringify(serializeGoldenCase(next))\n ) {\n updated += 1;\n }\n }\n\n const cases = [...byKey.values()];\n if (cases.length > 0) {\n mkdirSync(dir, { recursive: true });\n writeFileSync(path, stringifyYaml(cases.map(serializeGoldenCase)));\n }\n return {\n path,\n added,\n updated,\n total: cases.length,\n unreviewed: cases.filter((c) => !c.reviewed).length,\n };\n}\n\nexport async function runCalibrate(\n options: CalibrateOptions = {},\n): Promise<CalibrationReport> {\n const cwd = options.cwd ?? process.cwd();\n const config = loadConfig(options.config, cwd);\n const flags = { provider: options.provider, model: options.model, local: options.local };\n // Checked before the golden set is read: a typo is a usage error on every run.\n assertProviderSelection(selectProvider(config, flags));\n const goldenDir = resolve(cwd, options.golden ?? GOLDEN_DIR);\n const cases = loadGoldenCases(goldenDir);\n\n // Resolve every case to a target first, then judge the whole set in one call\n // (ADR 01016). Judging case-by-case left the bounded-concurrency pool inside\n // makeJudge with one target at a time, and made a turn budget mean *per case*.\n const results: CalibrationCaseResult[] = [];\n const targets: GraderTarget[] = [];\n const targetIndex: number[] = [];\n // One load for the whole golden set, not one per case. The pages are named\n // so a collection that keeps its evals in one manifest per page (proposal\n // 0058) has a file to read; a case naming a page that is not there is\n // reported below rather than loaded.\n const external = await loadExternalReader(\n config,\n cwd,\n cases.map((c) => resolve(cwd, c.file)).filter((p) => existsSync(p)),\n );\n\n for (const goldenCase of cases) {\n const absPath = resolve(cwd, goldenCase.file);\n if (!existsSync(absPath)) {\n results.push({ ...goldenCase, error: \"page not found\" });\n continue;\n }\n const read = readPage(absPath, cwd);\n const page = external === null ? read : await external.forPage(read);\n const plan = resolvePage(page, config);\n const ev = plan.evals.find(\n (e) => e.name === goldenCase.eval && e.grader === \"ai\",\n );\n if (!ev) {\n results.push({\n ...goldenCase,\n error: `ai-graded eval \"${goldenCase.eval}\" not resolvable on this page`,\n });\n continue;\n }\n // A verdict is about the words a human read. Once the page no longer\n // hashes to what was verified, the case describes a document that no\n // longer exists — the same rule `findReview` applies to a review.\n const stale =\n goldenCase.contentHash !== undefined &&\n goldenCase.contentHash !== contentHash(plan.page.body);\n\n targetIndex.push(results.length);\n results.push({ ...goldenCase, ...(stale ? { stale } : {}) });\n targets.push({ plan, eval: ev });\n }\n\n if (targets.length > 0) {\n const judge =\n options.judge ??\n makeJudge({\n provider: await makeProvider(config, flags),\n root: cwd,\n });\n const judged = await judge(targets, config, {\n // The flags reach the judge too, so a golden case whose eval names its\n // own provider or model is overridden by them as it is under `run`.\n ...flags,\n runs: options.runs,\n noCache: options.noCache,\n maxTurns: options.maxTurns,\n });\n for (const [i, at] of targetIndex.entries()) {\n const slot = results[at];\n if (slot === undefined) {\n throw new DocevalsError(\n `calibrate: no golden case at index ${at} for judged target ${i}`,\n );\n }\n const consensus = judged[i]?.consensus;\n if (!consensus) {\n // Distinguish \"the budget ran out\" from \"the judge failed\": the\n // first is a coverage problem that must not be allowed to certify\n // anything, and it used to disappear into the same error bucket.\n const reason = judged[i]?.skipReason;\n const fromBudget = isTurnBudgetSkip(reason);\n results[at] = {\n ...slot,\n ...(fromBudget ? { budgetSkipped: true } : {}),\n error: fromBudget ? `not judged: ${reason}` : \"judge returned no consensus\",\n };\n continue;\n }\n const verdict = consensus.verdict === \"pass\" ? \"pass\" : \"fail\";\n results[at] = {\n ...slot,\n judged: verdict,\n agrees: verdict === slot.expected,\n };\n }\n }\n\n const judgedCases = results.filter((r) => r.judged != null);\n const agreements = judgedCases.filter((r) => r.agrees).length;\n const expectedPass = judgedCases.filter((r) => r.expected === \"pass\");\n // False positive: the judge flags a failure a human verified as passing.\n const falsePositives = expectedPass.filter((r) => r.judged === \"fail\").length;\n const expectedFailCases = judgedCases.filter((r) => r.expected === \"fail\");\n const falseNegatives = expectedFailCases.filter(\n (r) => r.judged === \"pass\",\n ).length;\n const agreementRate = judgedCases.length > 0 ? agreements / judgedCases.length : 0;\n const falsePositiveRate =\n expectedPass.length > 0 ? falsePositives / expectedPass.length : 0;\n\n // A budget-truncated run measured a sample, and the agreement rate is over\n // that sample — so the run must not certify. The guard is in\n // `src/cli.ts` (`meetsThreshold && unjudged === 0`), not in `meetsThreshold`\n // itself: ADR 01018 keeps the verdict a statement about agreement and this a\n // statement about coverage. This is the same silent-green shape `runEvals`\n // guards against; `calibrate` is the command whose whole output is a trust\n // claim, so there it fails the run rather than merely warning.\n const budgetSkipped = results.filter((r) => r.budgetSkipped === true).length;\n const unjudged = results.length - judgedCases.length;\n\n return {\n cases: results,\n budgetSkipped,\n unjudged,\n unreviewed: judgedCases.filter((r) => !r.reviewed).length,\n stale: judgedCases.filter((r) => r.stale === true).length,\n total: results.length,\n agreements,\n agreementRate,\n falsePositives,\n falsePositiveRate,\n falseNegatives,\n meetsThreshold: agreementRate >= AGREEMENT_THRESHOLD,\n expectedPass: expectedPass.length,\n expectedFail: expectedFailCases.length,\n balanced: expectedPass.length > 0 && expectedFailCases.length > 0,\n fpAlert: falsePositiveRate > config.judge.falsePositiveAlert,\n };\n}\n\nexport function renderCalibration(\n report: CalibrationReport,\n opts: ColorOptions = {},\n): string {\n const pc = palette(opts.color === true);\n const lines: string[] = [];\n for (const c of report.cases) {\n if (c.error) {\n lines.push(`${pc.red(\"error\")} ${c.file} ${c.eval}: ${c.error}`);\n continue;\n }\n const tag = c.agrees ? pc.green(\"agree\") : pc.red(\"DISAGREE\");\n const flags = [\n c.reviewed ? \"\" : pc.yellow(\" [unreviewed]\"),\n c.stale ? pc.yellow(\" [stale]\") : \"\",\n ].join(\"\");\n lines.push(\n `${tag} ${c.file} ${pc.bold(c.eval)}${flags}: judge=${c.judged ?? \"(no verdict)\"} human=${c.expected}` +\n (!c.agrees && c.rationale ? pc.dim(` — human: ${c.rationale}`) : \"\"),\n );\n }\n lines.push(\"\");\n // Denominator stated explicitly: the rate is over the cases that were\n // actually judged, which is not `total` when pages are missing or the budget\n // ran out. Printing \"1/30 (100%)\" left those two numbers contradicting.\n const judged = report.cases.filter((c) => c.judged != null).length;\n lines.push(\n `Agreement: ${report.agreements}/${judged} judged (${(report.agreementRate * 100).toFixed(0)}%) — threshold ${(AGREEMENT_THRESHOLD * 100).toFixed(0)}%` +\n (judged === report.total ? \"\" : ` — ${report.total - judged} of ${report.total} case(s) not judged`),\n );\n lines.push(\n `False positives: ${report.falsePositives} (${(report.falsePositiveRate * 100).toFixed(0)}% of human-passes), false negatives: ${report.falseNegatives}`,\n );\n if (report.unjudged > 0) {\n lines.push(\n pc.red(\n `\n${report.unjudged} of ${report.total} case(s) never reached a verdict, so the rate above ` +\n `is over a sample. A calibration measured on part of the set does not certify the judge.`,\n ),\n );\n }\n if (judged > 0 && !report.balanced) {\n const missing = report.expectedFail === 0 ? \"fail\" : \"pass\";\n const always = missing === \"fail\" ? \"pass\" : \"fail\";\n lines.push(\n pc.red(\n `\nNo \\`expected: ${missing}\\` cases among the ${judged} judged. A judge that answered ` +\n `\"${always}\" every time would score 100% here, so this run cannot certify anything. ` +\n `Add at least one case the judge should ${missing === \"fail\" ? \"reject\" : \"accept\"}.`,\n ),\n );\n }\n // Only when something was actually measured. \"Refine the eval criteria\" is\n // advice about a rate, and there is no rate over zero judged cases.\n if (judged > 0 && !report.meetsThreshold) {\n lines.push(\n pc.red(\n \"\\nAgreement is below threshold. Refine the eval criteria first — make assertions more specific — before changing the grading mechanism.\",\n ),\n );\n }\n if (report.budgetSkipped > 0) {\n lines.push(\n pc.red(\n `\n${report.budgetSkipped} case(s) were never judged: the turn budget ran out. ` +\n `A rate measured over the rest is not a calibration — raise --max-turns and re-run.`,\n ),\n );\n }\n if (report.fpAlert) {\n lines.push(\n pc.yellow(\n \"\\nFalse-positive rate exceeds judge.falsePositiveAlert — the judge is flagging content humans accept. Consider tightening assertions or examples.\",\n ),\n );\n }\n // Counted, but never quietly. The rate above is the artifact handed to a\n // skeptic; a reader has to be able to discount it by exactly this much\n // (ADR 01016).\n if (report.unreviewed > 0) {\n lines.push(\n pc.yellow(\n `\\n${report.unreviewed} of ${report.total} case(s) are unreviewed — they count toward the rate above, ` +\n `but no human has confirmed they belong in the golden set. Read them and set \\`reviewed: true\\`.`,\n ),\n );\n }\n if (report.stale > 0) {\n lines.push(\n pc.yellow(\n `\\n${report.stale} case(s) are stale: the page changed since the verdict was recorded, so the case ` +\n `describes a document that no longer exists. Re-verify and update \\`content-hash\\`.`,\n ),\n );\n }\n return lines.join(\"\\n\");\n}\n","/** `manni docevals init` — scaffold a starter manni.config.yaml. */\nimport { existsSync, writeFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { DocevalsError } from \"../types.js\";\nimport { DEFAULT_CONFIG_FILENAME } from \"../core/config.js\";\n\nconst STARTER_CONFIG = `# manni.config.yaml — shared configuration for the manni family of tools.\n# Each tool reads its own top-level key; manni docevals reads \"docevals:\".\n# Docs: https://hawkeyexl.github.io/manni/docevals/\n\n# The documents every manni tool reads, declared once. A bare\n# \\`manni docevals run\\` evaluates every collection listed here; pass\n# --collection <name> to pick one, or name paths on the command line instead.\n# node_modules and .git are never read.\ncollections:\n - name: site\n paths:\n - \"docs/**/*.{md,mdx}\"\n\n# The inference provider, declared once for every manni tool that sends\n# content to a model: manni docevals and manni meta fill. auto detects what\n# this machine can use: an Anthropic key, then an OpenAI key, then the Claude\n# CLI, then a local model. Name one to pin it. A tool's own provider key, or\n# --provider, still wins for that tool.\nproviders:\n provider: auto # auto | anthropic | openai | claude-cli | llama-cpp\n # model: <id> # needs a named provider; unset takes that provider's default\n # anthropic: { apiKeyEnv: ANTHROPIC_API_KEY }\n # openai: { baseUrl: http://localhost:11434/v1, apiKeyEnv: OPENAI_API_KEY }\n # claude-cli: { command: claude } # uses local CLI auth, no API key\n # llama-cpp: { modelsDir: .manni/models, thoughtTokens: 0 } # modelsDir is relative to this file\n\ndocevals:\n defaults:\n # Suite applied to pages without an eval-suite frontmatter key. Naming the\n # suite defined at the bottom of this file is what makes a fresh corpus\n # check anything at all: with \"null\", a page carrying no eval frontmatter\n # resolves zero evals, and a run over zero evals is a usage error rather\n # than a green build (ADR 01041).\n suite: default\n failFast: false\n concurrency: 4\n\n judge:\n ensembleRuns: 3 # 3 isolated runs per eval; agreement is signal\n # NOTE: defaults.suite attaches an ai eval to every discovered page, so a\n # keyed run on a large corpus issues ensembleRuns x pages requests. Set\n # maxTurns once you know what a full pass costs you — deliberately, and\n # high enough to cover the corpus. A budget set *below* what a full pass\n # needs stops early, reports the remaining pages as skipped, and still exits\n # 0 (ADR 01019): partial coverage that reads as success. Start with\n # --deterministic-only, which needs no provider at all.\n temperature: 0\n zones:\n autoPass: 0.8 # unanimous pass + mean confidence >= 0.8\n autoFail: 0.8\n falsePositiveAlert: 0.15\n cacheDir: .manni/docevals/cache\n\n execution:\n # Every grant holds unless this list narrows it. frontmatter-commands lets\n # command evals declared in page frontmatter run. Write [] if you do not\n # trust whoever can edit this corpus's pages.\n allow: [frontmatter-commands]\n\n scripts:\n dir: \"{docDir}/manni-docevals\" # generated check scripts live beside the docs\n configDir: manni-docevals-scripts\n\n evals:\n no-future-promises:\n type: regression\n assertion: The page makes no claims about unreleased or future functionality.\n grader: ai\n evidence: All prose sections\n examples:\n pass: Describes only shipped behavior.\n fail: Says \"coming soon\" or references an unreleased version.\n no-todo-markers:\n assertion: The page carries no TODO, TBD or FIXME markers.\n grader: tool:regex\n options:\n pattern: \"\\\\\\\\b(TODO|TBD|FIXME)\\\\\\\\b\"\n match: not-contains\n severity: error\n\n suites:\n default:\n target-pass-rate: 1.0 # regression suites target ~100%\n evals: [no-future-promises, no-todo-markers]\n`;\n\nexport function runInit(cwd = process.cwd()): string {\n const path = resolve(cwd, DEFAULT_CONFIG_FILENAME);\n if (existsSync(path)) {\n throw new DocevalsError(`${DEFAULT_CONFIG_FILENAME} already exists`);\n }\n writeFileSync(path, STARTER_CONFIG);\n return path;\n}\n"],"mappings":"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","names":["writeFileSync","merged","writeFileSync","mkdirSync","readFileSync","writeFileSync","mkdirSync","writeFileSync","readFileSync","existsSync","resolve","relative","resolve","existsSync","relative","existsSync","mkdirSync","readFileSync","writeFileSync","join","resolve","join","existsSync","readFileSync","resolve","mkdirSync","writeFileSync","existsSync","writeFileSync","resolve","resolve","existsSync","writeFileSync"]}
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/shared/sarif-location.ts
4
+ var URI_BASE_ID = "SRCROOT";
5
+ var DRIVE = /^[A-Za-z]:\//;
6
+ function toPosix(p) {
7
+ return p.replace(/\\/g, "/");
8
+ }
9
+ function isAbsolutePath(posix) {
10
+ return posix.startsWith("/") || DRIVE.test(posix);
11
+ }
12
+ function isWindowsPath(posix) {
13
+ return DRIVE.test(posix) || posix.startsWith("//");
14
+ }
15
+ function underRoot(posix, root) {
16
+ if (posix.startsWith(root)) return posix.slice(root.length);
17
+ if (isWindowsPath(posix) && isWindowsPath(root)) {
18
+ if (posix.toLowerCase().startsWith(root.toLowerCase())) {
19
+ return posix.slice(root.length);
20
+ }
21
+ }
22
+ return null;
23
+ }
24
+ function encodePath(p) {
25
+ return encodeURI(p).replace(/#/g, "%23").replace(/\?/g, "%3F");
26
+ }
27
+ function fileUri(posix) {
28
+ const encoded = encodePath(posix);
29
+ if (posix.startsWith("//")) return `file:${encoded}`;
30
+ if (posix.startsWith("/")) return `file://${encoded}`;
31
+ return `file:///${encoded}`;
32
+ }
33
+ function escapesRoot(posix) {
34
+ return posix === ".." || posix.startsWith("../");
35
+ }
36
+ function resolveFromRoot(root, rel) {
37
+ const parts = root.replace(/\/+$/, "").split("/");
38
+ for (const segment of rel.split("/")) {
39
+ if (segment === "" || segment === ".") continue;
40
+ if (segment === "..") {
41
+ if (parts.length > 1) parts.pop();
42
+ continue;
43
+ }
44
+ parts.push(segment);
45
+ }
46
+ const joined = parts.join("/");
47
+ return joined === "" ? "/" : joined;
48
+ }
49
+ function normalizeRoot(root) {
50
+ const posix = toPosix(root);
51
+ return posix.endsWith("/") ? posix : `${posix}/`;
52
+ }
53
+ function artifactLocation(file, root) {
54
+ const posix = toPosix(file);
55
+ if (!isAbsolutePath(posix)) {
56
+ if (escapesRoot(posix)) return { uri: fileUri(resolveFromRoot(root, posix)) };
57
+ return { uri: encodePath(posix), uriBaseId: URI_BASE_ID };
58
+ }
59
+ const rel = underRoot(posix, root);
60
+ if (rel !== null) return { uri: encodePath(rel), uriBaseId: URI_BASE_ID };
61
+ return { uri: fileUri(posix) };
62
+ }
63
+
64
+ export {
65
+ URI_BASE_ID,
66
+ toPosix,
67
+ underRoot,
68
+ fileUri,
69
+ normalizeRoot,
70
+ artifactLocation
71
+ };
72
+ //# sourceMappingURL=chunk-GLP5PSXB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/sarif-location.ts"],"sourcesContent":["/**\n * SARIF artifact locations, shared by every tool that writes SARIF over paths\n * it did not choose.\n *\n * Moved here from lint's reporter when tracevals needed the same mapping: a\n * tracevals finding points at the skill or instruction file that declared the\n * eval, which is often outside the checkout (`~/.claude/skills/...`), so the\n * absolute-path branch below is its ordinary case rather than an edge one.\n */\n\n/** The base every relative artifact URI resolves against. */\nexport const URI_BASE_ID = \"SRCROOT\";\n\nexport interface ArtifactLocation {\n uri: string;\n uriBaseId?: string;\n}\n\n/** Matches a Windows drive prefix on an already-forward-slashed path. */\nconst DRIVE = /^[A-Za-z]:\\//;\n\nexport function toPosix(p: string): string {\n return p.replace(/\\\\/g, \"/\");\n}\n\n/**\n * Path handling here is string-level rather than `node:path`, deliberately.\n *\n * `node:path` is platform-bound: on Linux, `isAbsolute(\"C:\\\\repo\\\\a.md\")` is\n * false and `relative()` treats the whole thing as one filename. That would\n * make this reporter's most important behaviour differ between the two CI\n * runners, and differ in the direction that fails silently - a Windows path\n * emitted verbatim as a URI resolves to no file in the repository. Doing the\n * work on strings makes the mapping identical everywhere.\n */\nfunction isAbsolutePath(posix: string): boolean {\n return posix.startsWith(\"/\") || DRIVE.test(posix);\n}\n\n/**\n * A path Windows resolves, and therefore compares without regard to case:\n * either a drive path or a UNC share. `//` is how `fileUri` recognises a share\n * too, so both places agree on what one looks like.\n */\nfunction isWindowsPath(posix: string): boolean {\n return DRIVE.test(posix) || posix.startsWith(\"//\");\n}\n\n/** `/repo/` + `/repo/docs/a.md` -> `docs/a.md`; null when not underneath. */\nexport function underRoot(posix: string, root: string): string | null {\n if (posix.startsWith(root)) return posix.slice(root.length);\n // Windows paths are case-insensitive: `C:/Repo` and `c:/repo` are one\n // directory, and so are `//server/Share` and `//SERVER/share` - a checkout on\n // a network share must relativize as readily as one on a drive, or its\n // findings upload as absolute URIs and attach to nothing. Retry this way only\n // when both sides are Windows-shaped - anywhere else, two paths differing in\n // case are two different files.\n if (isWindowsPath(posix) && isWindowsPath(root)) {\n // The comparison case-folds but the slice does not, which reads like a bug\n // and is not: case folding leaves length unchanged, so `root.length` still\n // indexes the same boundary, and slicing the original is what keeps the\n // reported path in the casing the author actually used.\n if (posix.toLowerCase().startsWith(root.toLowerCase())) {\n return posix.slice(root.length);\n }\n }\n return null;\n}\n\n/**\n * Percent-encode what a URI cannot carry raw. `encodeURI` leaves `/` and `:`\n * alone, which is what a path needs, but it also leaves `#` and `?` alone, and\n * either of those would be read as a fragment or a query rather than as part\n * of the name.\n *\n * This also covers labels that are not paths at all: the stdin document is\n * `<stdin>`, and `<`/`>` are not legal in a URI. It will not resolve to a file\n * anywhere, but an invalid URI can sink an entire upload, whereas a valid one\n * that resolves to nothing only sinks its own annotation.\n */\nfunction encodePath(p: string): string {\n return encodeURI(p).replace(/#/g, \"%23\").replace(/\\?/g, \"%3F\");\n}\n\n/** An absolute forward-slashed path as a `file:` URI. */\nexport function fileUri(posix: string): string {\n const encoded = encodePath(posix);\n if (posix.startsWith(\"//\")) return `file:${encoded}`; // UNC: file://host/share\n if (posix.startsWith(\"/\")) return `file://${encoded}`; // POSIX: file:///etc\n return `file:///${encoded}`; // Drive: file:///C:/repo\n}\n\n/** A relative path that climbs out of the directory it is relative to. */\nfunction escapesRoot(posix: string): boolean {\n return posix === \"..\" || posix.startsWith(\"../\");\n}\n\n/**\n * `C:/repo/` + `../sibling/a.md` -> `C:/sibling/a.md`.\n *\n * String-level, like everything else here, and one step per `..` rather than a\n * single strip: `../a.md` and `../../a.md` name two different files, and a\n * reporter that collapsed them would point an alert at the wrong one. The first\n * segment is never popped - it is the drive, the empty segment before a posix\n * root, or the first half of a UNC share - so a path cannot climb past the\n * filesystem root.\n */\nfunction resolveFromRoot(root: string, rel: string): string {\n const parts = root.replace(/\\/+$/, \"\").split(\"/\");\n for (const segment of rel.split(\"/\")) {\n if (segment === \"\" || segment === \".\") continue;\n if (segment === \"..\") {\n if (parts.length > 1) parts.pop();\n continue;\n }\n parts.push(segment);\n }\n const joined = parts.join(\"/\");\n return joined === \"\" ? \"/\" : joined;\n}\n\n/**\n * Forward-slashed with exactly one trailing slash, so a prefix match on it can\n * only ever land on a segment boundary.\n */\nexport function normalizeRoot(root: string): string {\n const posix = toPosix(root);\n return posix.endsWith(\"/\") ? posix : `${posix}/`;\n}\n\n/**\n * The artifact URI, which is the single most breakable thing in this file.\n *\n * SARIF URIs are URI references, not native paths. Relative plus `uriBaseId`\n * is what GitHub resolves against the checkout, so that is the default; a\n * backslashed path, or a bare absolute one, uploads fine and matches no file.\n *\n * A target outside the run root arrives as a **relative** path too - a `../`\n * chain, which is how `resolveTargets` reports it - and that is the one shape\n * a `uriBaseId` cannot carry. `SRCROOT` is the checkout, and code scanning\n * will not resolve above it: `../sibling/docs/a.md` uploads and annotates\n * nothing, which is this format's whole failure mode. So it is resolved\n * against the root and emitted the way the genuinely-absolute case below is,\n * with no base to resolve against.\n */\nexport function artifactLocation(file: string, root: string): ArtifactLocation {\n const posix = toPosix(file);\n if (!isAbsolutePath(posix)) {\n if (escapesRoot(posix)) return { uri: fileUri(resolveFromRoot(root, posix)) };\n return { uri: encodePath(posix), uriBaseId: URI_BASE_ID };\n }\n const rel = underRoot(posix, root);\n if (rel !== null) return { uri: encodePath(rel), uriBaseId: URI_BASE_ID };\n // Genuinely outside the run root. No relative URI reaches it, so say so with\n // an absolute `file:` URI rather than inventing a `../` chain that resolves\n // to nothing on the machine reading the report.\n return { uri: fileUri(posix) };\n}\n"],"mappings":";;;AAWO,IAAM,cAAc;AAQ3B,IAAM,QAAQ;AAEP,SAAS,QAAQ,GAAmB;AACzC,SAAO,EAAE,QAAQ,OAAO,GAAG;AAC7B;AAYA,SAAS,eAAe,OAAwB;AAC9C,SAAO,MAAM,WAAW,GAAG,KAAK,MAAM,KAAK,KAAK;AAClD;AAOA,SAAS,cAAc,OAAwB;AAC7C,SAAO,MAAM,KAAK,KAAK,KAAK,MAAM,WAAW,IAAI;AACnD;AAGO,SAAS,UAAU,OAAe,MAA6B;AACpE,MAAI,MAAM,WAAW,IAAI,EAAG,QAAO,MAAM,MAAM,KAAK,MAAM;AAO1D,MAAI,cAAc,KAAK,KAAK,cAAc,IAAI,GAAG;AAK/C,QAAI,MAAM,YAAY,EAAE,WAAW,KAAK,YAAY,CAAC,GAAG;AACtD,aAAO,MAAM,MAAM,KAAK,MAAM;AAAA,IAChC;AAAA,EACF;AACA,SAAO;AACT;AAaA,SAAS,WAAW,GAAmB;AACrC,SAAO,UAAU,CAAC,EAAE,QAAQ,MAAM,KAAK,EAAE,QAAQ,OAAO,KAAK;AAC/D;AAGO,SAAS,QAAQ,OAAuB;AAC7C,QAAM,UAAU,WAAW,KAAK;AAChC,MAAI,MAAM,WAAW,IAAI,EAAG,QAAO,QAAQ,OAAO;AAClD,MAAI,MAAM,WAAW,GAAG,EAAG,QAAO,UAAU,OAAO;AACnD,SAAO,WAAW,OAAO;AAC3B;AAGA,SAAS,YAAY,OAAwB;AAC3C,SAAO,UAAU,QAAQ,MAAM,WAAW,KAAK;AACjD;AAYA,SAAS,gBAAgB,MAAc,KAAqB;AAC1D,QAAM,QAAQ,KAAK,QAAQ,QAAQ,EAAE,EAAE,MAAM,GAAG;AAChD,aAAW,WAAW,IAAI,MAAM,GAAG,GAAG;AACpC,QAAI,YAAY,MAAM,YAAY,IAAK;AACvC,QAAI,YAAY,MAAM;AACpB,UAAI,MAAM,SAAS,EAAG,OAAM,IAAI;AAChC;AAAA,IACF;AACA,UAAM,KAAK,OAAO;AAAA,EACpB;AACA,QAAM,SAAS,MAAM,KAAK,GAAG;AAC7B,SAAO,WAAW,KAAK,MAAM;AAC/B;AAMO,SAAS,cAAc,MAAsB;AAClD,QAAM,QAAQ,QAAQ,IAAI;AAC1B,SAAO,MAAM,SAAS,GAAG,IAAI,QAAQ,GAAG,KAAK;AAC/C;AAiBO,SAAS,iBAAiB,MAAc,MAAgC;AAC7E,QAAM,QAAQ,QAAQ,IAAI;AAC1B,MAAI,CAAC,eAAe,KAAK,GAAG;AAC1B,QAAI,YAAY,KAAK,EAAG,QAAO,EAAE,KAAK,QAAQ,gBAAgB,MAAM,KAAK,CAAC,EAAE;AAC5E,WAAO,EAAE,KAAK,WAAW,KAAK,GAAG,WAAW,YAAY;AAAA,EAC1D;AACA,QAAM,MAAM,UAAU,OAAO,IAAI;AACjC,MAAI,QAAQ,KAAM,QAAO,EAAE,KAAK,WAAW,GAAG,GAAG,WAAW,YAAY;AAIxE,SAAO,EAAE,KAAK,QAAQ,KAAK,EAAE;AAC/B;","names":[]}