lazycodex-ai 5.1.45 → 5.1.46

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 (236) hide show
  1. package/README.de.md +0 -5
  2. package/README.es.md +0 -5
  3. package/README.fr.md +0 -5
  4. package/README.it.md +0 -5
  5. package/README.ja.md +0 -5
  6. package/README.ko.md +0 -5
  7. package/README.md +4 -5
  8. package/README.pt-br.md +0 -5
  9. package/README.ru.md +0 -6
  10. package/README.zh-cn.md +0 -5
  11. package/dist/cli/index.js +76 -156
  12. package/dist/cli-node/index.js +76 -156
  13. package/package.json +1 -1
  14. package/packages/lsp-daemon/dist/cli.js +3 -3
  15. package/packages/lsp-daemon/dist/client.js +3 -3
  16. package/packages/lsp-daemon/dist/index.js +3 -3
  17. package/packages/lsp-tools-mcp/dist/cli.js +3 -3
  18. package/packages/lsp-tools-mcp/dist/mcp.js +3 -3
  19. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  20. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +22 -12
  21. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  22. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  23. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  24. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  25. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  26. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  27. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  28. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  29. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  30. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  31. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  32. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  33. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  34. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  35. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  36. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  37. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  38. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  39. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  40. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  41. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  43. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  44. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  45. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  46. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  47. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  49. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  50. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  51. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  52. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  53. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  54. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  55. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  56. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  57. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  58. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  59. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  60. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  61. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  62. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  63. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  64. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  65. package/packages/omo-codex/plugin/package-lock.json +12 -12
  66. package/packages/omo-codex/plugin/package.json +1 -1
  67. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +8 -3
  68. package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +0 -2
  69. package/packages/omo-codex/plugin/skills/ast-grep/AGENTS.md +1 -1
  70. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  71. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +2 -2
  72. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +1 -1
  73. package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +14 -2
  74. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +36 -11
  75. package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +73 -13
  76. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +19 -1
  77. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +95 -0
  78. package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
  79. package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +21 -4
  80. package/packages/omo-codex/plugin/skills/ulw-research/references/report-visualization.md +2 -0
  81. package/packages/omo-codex/plugin/skills/ulw-research/references/unit-rules.md +157 -0
  82. package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
  83. package/packages/omo-codex/plugin/skills/ulw-research/scripts/cli-support.mjs +19 -5
  84. package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +27 -6
  85. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
  86. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
  87. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +239 -0
  88. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +66 -0
  89. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-expr.mjs +108 -0
  90. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +194 -0
  91. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +127 -13
  92. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-schema.mjs +33 -16
  93. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger.mjs +29 -9
  94. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +231 -0
  95. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
  96. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
  97. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
  98. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-brief.mjs +5 -0
  99. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
  100. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
  101. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
  102. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-state.mjs +2 -1
  103. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
  104. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
  105. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
  106. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +28 -8
  107. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +2 -2
  108. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +182 -0
  109. package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +193 -0
  110. package/packages/omo-codex/plugin/skills/ulw-research/scripts/meaning-check.mjs +196 -0
  111. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +6 -5
  112. package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +184 -0
  113. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +115 -0
  114. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-diff.mjs +31 -0
  115. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-project.mjs +125 -0
  116. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-rules.mjs +170 -0
  117. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +19 -8
  118. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +15 -0
  119. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +140 -0
  120. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
  121. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
  122. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-errors.mjs +23 -0
  123. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
  124. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
  125. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-precision.mjs +44 -0
  126. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
  127. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules.mjs +173 -0
  128. package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +4 -4
  129. package/packages/omo-codex/plugin/test/sync-skills-codex-compatibility.test.mjs +19 -0
  130. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +7 -3
  131. package/packages/omo-codex/scripts/install-dist/install-local.mjs +26 -16
  132. package/packages/shared-skills/skills/ast-grep/AGENTS.md +1 -1
  133. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  134. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +2 -2
  135. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +1 -1
  136. package/packages/shared-skills/skills/ulw-research/AGENTS.md +14 -2
  137. package/packages/shared-skills/skills/ulw-research/SKILL.md +37 -12
  138. package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +73 -13
  139. package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +19 -1
  140. package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +95 -0
  141. package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
  142. package/packages/shared-skills/skills/ulw-research/references/report-gates.md +21 -4
  143. package/packages/shared-skills/skills/ulw-research/references/report-visualization.md +2 -0
  144. package/packages/shared-skills/skills/ulw-research/references/unit-rules.md +157 -0
  145. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
  146. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +193 -0
  147. package/packages/shared-skills/skills/ulw-research/scripts/cli-support.mjs +19 -5
  148. package/packages/shared-skills/skills/ulw-research/scripts/cli-support.test.ts +13 -2
  149. package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +27 -6
  150. package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +3 -0
  151. package/packages/shared-skills/skills/ulw-research/scripts/copy-returns.test.ts +183 -0
  152. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +245 -0
  153. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
  154. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
  155. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +239 -0
  156. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +66 -0
  157. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +375 -0
  158. package/packages/shared-skills/skills/ulw-research/scripts/fact-expr.mjs +108 -0
  159. package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +194 -0
  160. package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.test.ts +221 -0
  161. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-grammar.test.ts +9 -2
  162. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +127 -13
  163. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +18 -3
  164. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +160 -0
  165. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-schema.mjs +33 -16
  166. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.mjs +29 -9
  167. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.test.ts +2 -0
  168. package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +231 -0
  169. package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.test.ts +118 -0
  170. package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
  171. package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.test.ts +167 -0
  172. package/packages/shared-skills/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
  173. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +409 -0
  174. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +388 -0
  175. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
  176. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.test.ts +81 -0
  177. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-brief.mjs +5 -0
  178. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
  179. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
  180. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.test.ts +117 -0
  181. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
  182. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-state.mjs +2 -1
  183. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
  184. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.test.ts +58 -0
  185. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
  186. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +270 -0
  187. package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
  188. package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.test.ts +73 -0
  189. package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +28 -8
  190. package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +2 -2
  191. package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +182 -0
  192. package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +193 -0
  193. package/packages/shared-skills/skills/ulw-research/scripts/md-lite.test.ts +271 -0
  194. package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.mjs +196 -0
  195. package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.test.ts +228 -0
  196. package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +6 -5
  197. package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +184 -0
  198. package/packages/shared-skills/skills/ulw-research/scripts/placeholder.test.ts +77 -0
  199. package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +115 -0
  200. package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.test.ts +363 -0
  201. package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.mjs +31 -0
  202. package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.test.ts +168 -0
  203. package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +187 -0
  204. package/packages/shared-skills/skills/ulw-research/scripts/polish-project.mjs +125 -0
  205. package/packages/shared-skills/skills/ulw-research/scripts/polish-rules.mjs +170 -0
  206. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +19 -8
  207. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +15 -0
  208. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +458 -2
  209. package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +140 -0
  210. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
  211. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +113 -0
  212. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/assets/share.csv +3 -0
  213. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/gen-legacy-dev.mjs +89 -0
  214. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-basic.json +223 -0
  215. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-legacy-dev.json +859 -0
  216. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-verify.json +420 -0
  217. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-bad.html +19 -0
  218. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-good.html +39 -0
  219. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/acronyms.json +3 -0
  220. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/meta.json +9 -0
  221. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/sentences.jsonl +7 -0
  222. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/text.txt +7 -0
  223. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4.html +19 -0
  224. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/acronyms.json +1 -0
  225. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/meta.json +9 -0
  226. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/sentences.jsonl +1 -0
  227. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/text.txt +1 -0
  228. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S9.txt +1 -0
  229. package/packages/shared-skills/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
  230. package/packages/shared-skills/skills/ulw-research/scripts/unit-errors.mjs +23 -0
  231. package/packages/shared-skills/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
  232. package/packages/shared-skills/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
  233. package/packages/shared-skills/skills/ulw-research/scripts/unit-precision.mjs +44 -0
  234. package/packages/shared-skills/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
  235. package/packages/shared-skills/skills/ulw-research/scripts/unit-rules.mjs +173 -0
  236. package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +4 -4
@@ -55,7 +55,7 @@ The research is done when all of these hold:
55
55
  - Every claim in the deliverable cites a source or a verification artifact.
56
56
  - Every asserted claim is represented in the claim graph, tied to an intent-vs-reality diff when an expected truth exists, and backed by observation manifest entries from independent observation groups or a documented single-source exception; convergence or exception status is explicit.
57
57
  - The deliverable lane and format were derived from the destination or asked through the empty-info interview without blocking collection, recorded in `brief.md` with `answered_by`, and the final materials match that record.
58
- - The delivered artifact passed the delivery gates in order (static gates, layout gates, visual QA, and the proofread pass where the harness provides one), each status is in `outcome.json`, and `outcome verify` passed.
58
+ - The delivered artifact passed the delivery gates in order (static gates, layout gates, fidelity gate, visual QA, and the proofread pass where the harness provides one), each status is in `outcome.json`, and `outcome verify` passed.
59
59
  - Every excursion opened during the run was closed by an EXIT rule, folded back into the claim or axis that triggered it, and recorded in both `excursion-log.md` and the ulw-loop ledger.
60
60
  - When the harness can show an HTML page inline, the report was shown inline before the artifact paths.
61
61
  - When the ask reproduces a visual source, the report carries side-by-side pairs of real source frames and the output rendered at the same moments, each with its critique, and the differences they surfaced were fixed or listed as gaps. A simple or well-understood topic shortens the research, never this comparison.
@@ -67,7 +67,7 @@ The research is done when all of these hold:
67
67
  Saturation is not just more searching; it is a knowledge-production protocol. The session journal must make the path from observation to claim to verdict auditable. The orchestrator owns these artifacts:
68
68
 
69
69
  - `intent-diff.md` — one row per expected truth derived from the user intent, design/spec text, branch history, or authoritative docs. Required fields: `intent_id`, expected truth, observed reality, diff, violated invariant, intent source, supporting observations, status (`true`, `violated`, or `unknown`), and linked claim ids.
70
- - `claim-graph.md` — the single claim store; one node per claim. Required fields: `claim_id`, statement, claim type, risk tier, scope, intent ids, supporting observations, contradicting observations, independent observation groups, convergence status, counter-search result, primary source backing, dependencies, status (`supported`, `partial`, `refuted`, or `unresolved`), and final synthesis location. High-risk non-code nodes that clear the Phase 3b gate are mirrored into a `verified-claims` digest section at the top of the file — the sole allowlist the synthesis draws non-code claims from.
70
+ - `claim-graph.md` — the single claim store; one node per claim. Required fields: `claim_id`, statement, claim type, risk tier, scope, intent ids, supporting observations, contradicting observations, independent observation groups, convergence status, counter-search result, primary source backing, dependencies, status (`supported`, `partial`, `refuted`, or `unresolved`), `fact_ids` (the ledger rows the claim rests on), and final synthesis location. High-risk non-code nodes that clear the Phase 3b gate are mirrored into a `verified-claims` digest section at the top of the file — the sole allowlist the synthesis draws non-code claims from.
71
71
  - `observation-manifest.md` — one row per observation. Required fields: `observation_id`, source path or URL, evidence layer, observer group, independence basis, observer, `observed_at`, `valid_at` or `claim_valid_at`, artifact path, quote or line anchor, and contamination notes.
72
72
  - `verification-economics.md` — one row per proof decision. Required fields: claim, risk, error cost, verification cost/time, chosen verification path, defer/verify decision, outcome, and residual risk.
73
73
  - `cause-disappearance.md` — one row per causal finding. Required fields: cause id, expected truth, previous observation, `last_seen`, disconfirming observation, replacement cause if any, current status, and whether the violation is no longer observed.
@@ -140,6 +140,7 @@ This is `$SESSION_DIR`. The orchestrator owns the journal: you write every file
140
140
 
141
141
  - `brief.md` — the analysis block, the axis list, the expected truths, and the `## Deliverable` record from the interview below (lane, state, formats, each field with `answered_by`).
142
142
  - `sources-ledger.md` — one line per source the moment it is read: `[S<n>] <url> — <what it is>`. The closing briefing counts sources and domains from this file.
143
+ - `sources/` and `fact-ledger.json` — the source snapshots and the verbatim fact ledger ([references/fact-ledger.md](references/fact-ledger.md)). Every number and every direct quotation in the report comes from a ledger row through a `{{F<n>:label}}` or `{{Q<n>:label}}` placeholder; nobody types a value into a report or into `SYNTHESIS.md`.
143
144
  - `wave-<N>-<kind>-<axis>.md` — your digest of each worker return: key findings, sources with URLs, and the worker's EXPAND markers verbatim.
144
145
  - `expansion-log.md` — per wave: workers spawned, markers gained, leads opened and closed.
145
146
  - `excursion-log.md` — one ENTER row and one EXIT row per excursion: `excursion_id`, parent claim or axis, ENTER trigger, depth, workers spent, the EXIT rule that closed it, what it changed in the top-level answer (`none` is a valid, required answer), and the ulw-loop steer/evidence id it was mirrored into.
@@ -205,7 +206,7 @@ End your reply with the ## EXPAND tail: '- LEAD: <discovery> — WHY: <why> —
205
206
 
206
207
  This loop is what makes the mode research rather than search. Collect returns as they land — and in team mode, act on each lead the moment a member raises it, never waiting for the full wave or a member's final reply:
207
208
 
208
- 1. Journal the return the moment it lands, never at the end of the wave: digest plus verbatim EXPAND markers into `wave-<N>-<kind>-<axis>.md`, appending each new source, quote, and number to the observation manifest in the same beat — after a compaction the journal, not your memory, is the state.
209
+ 1. Journal the return the moment it lands, never at the end of the wave: digest plus verbatim EXPAND markers into `wave-<N>-<kind>-<axis>.md`, appending each new source, quote, and number to the observation manifest in the same beat — after a compaction the journal, not your memory, is the state. Snapshot each source the moment it is read (`node "$SKILL_DIR/scripts/report-tools.mjs" source snapshot --id S<n> --url <url> --session-dir "$SESSION_DIR"`; a page read in the browser is saved and imported with `--html <file>`, recorded as lower-trust `model` extraction), then register every number and quote you will use from that snapshot: `fact add --source S<n> --find "<anchor text>" --label <label>` (or `--table <id> --row <label> --col <label>`, or `--page <n> --line <n>`) and `fact quote --source S<n> --match "<words>" --label <label>`. Read the printed row and its 60 characters of source context before using it; a wrong row is retracted (`fact retract F<n> --reason <why>`), never edited. The observation's quote or line anchor in the manifest is that fact id.
209
210
  2. Deduplicate new markers against `expansion-log.md` — every lead ever seen, not just confirmed ones, or rejected leads resurface each wave.
210
211
  3. Spawn an expansion worker immediately for each new unchecked lead:
211
212
 
@@ -287,6 +288,8 @@ Anything that fails goes to an `Unresolved` (insufficient evidence) or `Refuted`
287
288
  - CLAIM: <non-code assertion> — RISK: high|normal — SOURCES: <domain1, domain2> — COUNTER: <refutation search result> — PRIMARY: <primary source or none>
288
289
  ```
289
290
 
291
+ **The claim table is machine-read.** Keep the nodes of `claim-graph.md` in one Markdown table whose header names `claim_id | statement | risk tier | status` (plus `fact_ids`, the ledger rows the node rests on, and any other node fields as further columns), one row per node: ids `C<n>`, risk `high` or `normal`, status `supported`, `partial`, `refuted`, or `unresolved`. List the nodes that clear the Phase 3b gate under a `## verified-claims` heading as `- C<n>: <statement>`. `node "$SKILL_DIR/scripts/report-tools.mjs" claims index "$SESSION_DIR/claim-graph.md" --out "$SESSION_DIR/claims.json"` indexes it, and the fidelity gate reads `claims.json` to check every `data-claim` binding in the report.
292
+
290
293
  ## Phase 4 — Synthesize
291
294
 
292
295
  After convergence and all verifications, re-read the whole journal, start from `intent-diff.md`, `claim-graph.md`, and `observation-manifest.md`, then write `SYNTHESIS.md`:
@@ -308,6 +311,8 @@ Workers: <total> · Waves: <count> · Excursions: <count> · Sources: <count> (<
308
311
 
309
312
  `SYNTHESIS.md` is the citation source of truth for final materials: every claim carries inline `[Source N]` citations, and every high-risk non-code claim you assert must be a verified-claims row from Phase 3b. Assert nothing the gate left in the unresolved/refuted annex.
310
313
 
314
+ **Write numbers and quotes by reference.** `SYNTHESIS.md` and every report source carry placeholders, never typed values: `{{F12:span_len}}` renders the row's value and unit, `{{F12:span_len:value}}` or `:unit` one part, `:q` the value with its source qualifier (up to, at least, about); `{{Q7:completion}}` renders the whole quotation, `{{Q7:completion:lead=8}}` its first words, `{{Q7:completion:from="no earlier":to="2027"}}` one span of it. A derived number is computed by the script, never by hand: `fact derive --op sum|diff|growth|ratio|share|convert|expr ...` checks the operands' units, periods, and populations, refuses an incompatible operation, and prints the new row id the placeholder uses. A value nobody measured is registered with `fact add --assumed <value> --unit <unit> --rationale <why> --label <label>` and renders as ASSUMED. Every findings sentence binds to a fact, a quote, a `[S<n>]` citation, or a claim node (`data-claim="C3"`). Reviewers and the refinement team read the rendered text through `node "$SKILL_DIR/scripts/report-tools.mjs" fact fill "$SESSION_DIR/SYNTHESIS.md" --view --session-dir "$SESSION_DIR"`, never the raw placeholders.
315
+
311
316
  **Write the skeleton early and fill it as claims lock.** The moment the brief records the deliverable, create the deliverable file with its section headings and a `STATUS: draft — <n> sections open` line as line 2 (an HTML comment in HTML); that line is the partial-state marker `outcome state` reads, removed only when the deliverable is complete. An interrupted run must leave a partial report on disk, never an empty directory and a lost conversation.
312
317
 
313
318
  **Keep sourced numbers, assumptions, and derived results visibly apart.** Every quantitative claim carries its lineage: `MEASURED` (a number a source states, cited), `ASSUMED` (a coefficient, distribution, or scope you chose — say why), `DERIVED` (computed from those, showing the formula), plus a sensitivity line whenever the assumption moves the answer. Presenting a derived estimate with the confidence of a measured one is the most damaging thing this mode can ship.
@@ -325,13 +330,15 @@ The promised formats recorded in `brief.md` and `outcome.json` are binding; `pdf
325
330
  | Slides / deck | `uv run --with python-pptx python` — one claim per slide, a chart or diagram per claim. |
326
331
  | Standalone HTML / Markdown | The authored source itself. |
327
332
 
333
+ **Every reader-facing edition is filled and verified.** The main report, the inline edition, LaTeX sections, and slide sources are all filled by `fact fill` from the same placeholder source, and each is verified on its own by `fact verify`; PDF and DOCX are renders of a verified source (`pandoc <report.html> -o <out.docx>` when the DOCX comes from the filled HTML). LaTeX per-section `.tex` sources and slide sources are filled with `fact fill --format tex` or `--format md` and verified with `fact verify --format tex` or `--format md`; this stage verifies HTML only and refuses the other formats with exit 2, so until Markdown support lands record such an edition `blocked_capability` with that reason instead of delivering it unverified. Record a delivered edition with `outcome set <format> delivered --path <file> --verified-by <record>`, where `<record>` is the `fact verify` output whose `artifact_sha256` equals the sha256 of the delivered source; a PDF or DOCX adds `--rendered-from <source>` and is matched against that source. `outcome verify` refuses a delivered row without a matching record (`edition_unverified`).
334
+
328
335
  **Write `design-spec.md` the moment the deliverable is recorded — before any asset worker spawns.** When a document was pointed at, extract it instead of retyping its stylesheet: `node "$SKILL_DIR/scripts/report-tools.mjs" format-extract <reference> --out "$SESSION_DIR/design-spec.md"` (`--from-url` for a URL), then fill every `TODO: ask` from the interview answers, never by guessing. It is the one design contract every asset and assembly worker receives: template family (absent a reference, the clean analyst-report register — one accent over neutral tones, generous margins, styled section headings, no emoji, no clipart), palette tokens, body/heading fonts (a real gothic CJK webfont — Pretendard, Noto Sans KR — when the report language needs one, with `word-break: keep-all` for Korean), responsive breakpoints and HTML/CSS charts for web deliverables, the figure standard below, and the lineage mode on its own line: `Lineage: inline` (analyst default: every unit-bearing number tagged MEASURED / ASSUMED / DERIVED or cited in the same element) or `Lineage: section` (web and blog register: every section cited, lineage in the figure captions). The reference's section 6 lists the full defaults. One font family and one palette govern prose, charts, Mermaid, and generated images alike; a diagram rendering in a random default font inside a styled report is a defect, not a style choice.
329
336
 
330
337
  **The figure standard — binding for every image, chart, and diagram.** Each figure sits in a fixed-size container styled from the spec (border, background, caption); the image scales to fit entirely inside it with its original aspect ratio preserved — object-fit: contain semantics — never stretched, never cropped, never spilling out. Every chart carries a title, axis labels, units, and value labels in the report's language; a bare number the reader cannot name is a defect.
331
338
 
332
339
  Asset workers (background, parallel, each fed `design-spec.md`) — visuals are the DEFAULT deliverable of this phase, not garnish the user must ask for; a delivered report without figures is an incomplete run:
333
340
 
334
- - **Charts for every quantitative finding, computed from real data.** Pull the numbers into an actual table first (CSV/JSON under `$SESSION_DIR`), then plot from that table, never from prose. Follow the data-scientist tool doctrine — numpy always, Polars for filtering/sorting/transforms, DuckDB for joins/aggregations/window functions, never pandas — and load the `data-scientist` skill when this session has it: `uv run --with numpy --with polars --with duckdb --with pyarrow --with matplotlib python`. Keep `pyarrow` in that set — the DuckDB-to-Polars handoff (`.pl()`) fails without it, and `.df()` fails without pandas, so hand data across through `.pl()`, never `.df()`. Save to `$SESSION_DIR/assets/`.
341
+ - **Charts for every quantitative finding, computed from real data.** Pull the numbers into an actual table first (CSV/JSON under `$SESSION_DIR`), then plot from that table, never from prose. Follow the data-scientist tool doctrine — numpy always, Polars for filtering/sorting/transforms, DuckDB for joins/aggregations/window functions, never pandas — and load the `data-scientist` skill when this session has it: `uv run --with numpy --with polars --with duckdb --with pyarrow --with matplotlib python`. Keep `pyarrow` in that set — the DuckDB-to-Polars handoff (`.pl()`) fails without it, and `.df()` fails without pandas, so hand data across through `.pl()`, never `.df()`. Save to `$SESSION_DIR/assets/`. Plot ONLY from the ledger: export the rows with `node "$SKILL_DIR/scripts/report-tools.mjs" fact export --csv --out "$SESSION_DIR/assets/<chart>.csv" --session-dir "$SESSION_DIR"` and draw from that CSV; every chart figure carries `data-chart-source="assets/<chart>.csv"`, and its axis tick labels sit inside a `<g class="ticks" data-tick-step=".." data-tick-min=".." data-tick-max="..">` group, so the fidelity gate can check every plotted value and tick (the deliverable reference's section 6 tick rule).
335
342
  - **Mermaid graphs** for process, architecture, argument, timeline, and evidence-flow structure, themed to the spec's fonts and palette. Render each to SVG and confirm the file exists before the document references it.
336
343
  - **Generated visuals through the imagegen skill whenever the session has it:** a cover plus a concept illustration per major theme, prompted from the spec's style, palette, and mood — document-styled illustration, never generic stock art dropped into a designed page.
337
344
  - **Full-page screenshots** of the top 5-10 sources, saved by the web workers while reading, as provenance you can show.
@@ -342,27 +349,42 @@ Asset workers (background, parallel, each fed `design-spec.md`) — visuals are
342
349
 
343
350
  **Verify the asset manifest before rendering.** List every asset the document references, assert each file exists and is non-empty on disk, and re-render whatever is missing. A document that renders with three broken diagrams is a document you will publish twice.
344
351
 
345
- Assembly worker — `task(category="deep-low", load_skills=["frontend", "visual-qa", "open-design", "data-scientist", "imagegen", "ulw-loop"], run_in_background=true, ...)`: before writing, read every available design and visualization skill and apply it — the report is a designed artifact, not a text dump; the worker's prompt carries `design-spec.md`. Use the template the user approved; absent a stronger house style the default skeleton is executive summary → key findings by theme → detailed analysis (quotes under 20 words with attribution, charts, Mermaid graphs, generated visuals, SHA-pinned permalinks, verification results) → comparative analysis when options compete → numbered sources with access dates → methodology appendix (workers, waves, searches, verifications, debate rounds) → correction log naming what verification overturned. Write it long and specific: every claim cites `[Source N]`, and the sources section lists every source the run actually used rather than a curated few.
352
+ Assembly worker — `task(category="deep-low", load_skills=["frontend", "visual-qa", "open-design", "data-scientist", "imagegen", "ulw-loop"], run_in_background=true, ...)`: before writing, read every available design and visualization skill and apply it — the report is a designed artifact, not a text dump; the worker's prompt carries `design-spec.md`. Use the template the user approved; absent a stronger house style the default skeleton is executive summary → key findings by theme → detailed analysis (quotes under 20 words with attribution, charts, Mermaid graphs, generated visuals, SHA-pinned permalinks, verification results) → comparative analysis when options compete → numbered sources with access dates → methodology appendix (workers, waves, searches, verifications, debate rounds) → correction log naming what verification overturned. Write it long and specific: every claim cites `[Source N]`, and the sources section lists every source the run actually used rather than a curated few. The assembly worker writes `report.src.html` with placeholders, never typed values; the orchestrator runs `node "$SKILL_DIR/scripts/report-tools.mjs" fact fill "$SESSION_DIR/report.src.html" --out "$SESSION_DIR/report.html" --session-dir "$SESSION_DIR"` and gates the filled file. The inline edition for a PDF, DOCX, or oversized HTML deliverable comes from the same source (`fact fill "$SESSION_DIR/report.src.html" --inline --out "$SESSION_DIR/report.inline.html"`, which inlines local stylesheets and makes image paths absolute on the filled output), never from a hand copy.
346
353
 
347
354
  ### The delivery gates — every gate for the QA tier must PASS, in order
348
355
 
349
- Nothing reaches the user until the gates pass. Record each result with `node "$SKILL_DIR/scripts/report-tools.mjs" outcome gate <static|fidelity|layout|visual|proofread> <pass|fail|not_run> --session-dir "$SESSION_DIR"`; `fidelity` is recorded not_run until the fidelity verifier ships in this edition; a gate this harness does not provide is recorded `not_run`. A chat or post answer (light tier) runs the static gates and one render; every paginated or web deliverable runs them all (the reference's sections 3 and 7).
356
+ Nothing reaches the user until the gates pass. Record each result with `node "$SKILL_DIR/scripts/report-tools.mjs" outcome gate <static|fidelity|layout|visual|proofread> <pass|fail|not_run> --session-dir "$SESSION_DIR"`; a gate this harness does not provide is recorded `not_run --reason "<why>"` (`fidelity not_run` refuses without the reason). A chat or post answer (light tier) runs the static gates and one render; every paginated or web deliverable runs them all (the reference's sections 3 and 7).
350
357
 
351
358
  1. **Static gates (always, before anyone looks at pixels).** `node "$SKILL_DIR/scripts/report-tools.mjs" check "$SESSION_DIR/report.html" --design-spec "$SESSION_DIR/design-spec.md" > "$SESSION_DIR/defects.json"`: keep-all, palette tokens, emoji, em dashes, heading length, unsourced numbers, figure containers, chart text, a report with no visual, images without alt text, gothic-only fonts, citations, the closing and sources sections, broken assets. Codes and fixes: [references/report-gates.md](references/report-gates.md).
352
- 2. **Layout gates.** Print the probe with `node "$SKILL_DIR/scripts/report-tools.mjs" layout-probe --json`, evaluate its `source` in the rendered page through the `browser` skill's owned headless engine, save the result as `$SESSION_DIR/boxes.json`, and re-run `check` with `--layout "$SESSION_DIR/boxes.json"` (overflow, clipped text, distorted images, overlapping siblings).
353
- 3. **Visual QA.** Render the produced artifact back to images — PDF pages to PNG, the HTML through the browser skill at desktop and phone widths — and look at them: missing or broken figures, images stretched or spilling their containers, diagram or chart text rendered off the spec's font or palette, clipped tables, overflowing CJK text, blank pages, unlabeled chart values, wrong page breaks. Reading the source markup is not visual QA; inspect the pixels.
354
- 4. **Proofread gate: `task(category="writing", ...)`.** Hand the final text to a dedicated `writing` worker whose only job is language: grammar, spelling, punctuation, terminology consistency, and whether the prose reads NATIVELY in the report's own language. It proofreads only and never composes. It returns a defect list; fix every item and re-run the gate on the delta. Deliver only on a clean pass. This gate runs BEFORE the first delivery, not after the user finds the typo.
359
+ 2. **Fidelity gate.** `node "$SKILL_DIR/scripts/report-tools.mjs" fact verify "$SESSION_DIR/report.html" --session-dir "$SESSION_DIR"` checks every number, quote, chart value, and sentence binding against the ledger, the snapshots, `claims.json`, and the locked `## Fidelity` block of `brief.md`, and writes `$SESSION_DIR/fidelity-defects.json` with the verified file's `artifact_sha256`. Exit `1` on any blocker or major defect: record `outcome gate fidelity fail` and repair; minors alone exit `0` and are listed. Verify every other edition the same way into its own record (`--out "$SESSION_DIR/fidelity-inline.json"`). Codes and fixes: [references/report-gates.md](references/report-gates.md); grammar: [references/deliverable-phase.md](references/deliverable-phase.md), Fidelity gate.
360
+
361
+ **Meaning verifier (a separate session reads every sentence against its source).** When the brief's `meaning check` is `on`, run it after the fidelity gate passes; when it is `off`, `fact pairs` writes `{"skipped": true}` and the step ends there. `node "$SKILL_DIR/scripts/report-tools.mjs" fact pairs "$SESSION_DIR/report.html" --session-dir "$SESSION_DIR"` writes `$SESSION_DIR/pairs.json`: one pair per distinct source quote a findings sentence cites, per sentence bound only by `data-claim` (against the claim's statement), and per `data-translates` span, with `force_strong` set where the lexicon or the hedge position says a strong reader must look. Then spawn one reader worker on the cheapest category, `task(category="quick", ...)`, whose prompt carries ONLY the path of `pairs.json` and the verdict schema, in exactly this text:
362
+
363
+ ```text
364
+ TASK: meaning reader, <cheap|strong> tier. Read ONLY the pair file <SESSION_DIR>/<pairs.json or pairs-strong.json> and the verdict schema in this prompt. Do not open, search, or fetch any other file, page, or tool output: not the report, the ledger, the sources, or the claim graph.
365
+ For each pair, compare `sentence` with `quote` (with `statement` for a claim pair) and decide whether the sentence says something its source does not.
366
+ Write <SESSION_DIR>/meaning-<cheap|strong>.json as {"model_tier": "<cheap|strong>", "verdicts": [...]} with one verdict per pair_id: {"pair_id": "P<n>", "verdict": "ok" or "drift", "drift_kind": ..., "fragment": ..., "source_fragment": ..., "explanation": "<one sentence>"}.
367
+ drift_kind is exactly one of: subject_changed, scope_widened, hedge_dropped, certainty_added, causality_added, direction_flipped, population_changed, evidence_grade_raised, other.
368
+ fragment is copied verbatim from the sentence and source_fragment verbatim from the quote or statement: the shortest exact substrings that show the drift, never paraphrased, never empty. A verdict whose fragments are not found word for word is discarded.
369
+ A sentence that shortens, summarizes, or translates its source without changing subject, scope, certainty, causality, direction, population, or evidence grade is ok. Do not rewrite anything; return verdicts only.
370
+ ```
371
+
372
+ Next, write `$SESSION_DIR/pairs-strong.json` (the same file, keeping only the pairs the cheap reader flagged plus every `force_strong` pair) and spawn one reader on the strong category, `task(category="ultrabrain", ...)`, with the same prompt for the strong tier. Then `node "$SKILL_DIR/scripts/report-tools.mjs" fact meaning-check --pairs "$SESSION_DIR/pairs.json" --verdicts "$SESSION_DIR/meaning-cheap.json" --verdicts "$SESSION_DIR/meaning-strong.json" --session-dir "$SESSION_DIR"` writes `$SESSION_DIR/fidelity-meaning-defects.json`: both tiers naming the same `drift_kind` (never `other`) is `meaning_drift_confirmed` (major, exit `1`); one tier alone is `meaning_drift_warn` (minor); a verdict whose fragments are not in the pair is dropped and counted. Repair every confirmed drift by rewriting the sentence from its quote, re-fill, re-verify, and re-read only the changed sentences (`fact pairs ... --changed-since <the new fill map>`); or, when the sentence is right, dispute it with `--dispute P<n> --reason "<why>"`, which keeps the defect for this run and logs the dispute. Pass the record to `repair decide` as one more `--defects` file and list the warnings in the closing briefing. Each flagged pair adds one line to `$SESSION_DIR/fidelity-calibration.jsonl`, whose header records the promotion criterion: `meaning_drift_confirmed` moves from major to an integrity code only after at least 20 runs with disputed-to-confirmed below 10 percent and no more than 2 report-level false blocks in the last 20 runs. These are starting values, changed only with evidence; the scripts never call a model.
373
+
374
+ 3. **Layout gates.** Print the probe with `node "$SKILL_DIR/scripts/report-tools.mjs" layout-probe --json`, evaluate its `source` in the rendered page through the `browser` skill's owned headless engine, save the result as `$SESSION_DIR/boxes.json`, and re-run `check` with `--layout "$SESSION_DIR/boxes.json"` (overflow, clipped text, distorted images, overlapping siblings).
375
+ 4. **Visual QA.** Render the produced artifact back to images — PDF pages to PNG, the HTML through the browser skill at desktop and phone widths — and look at them: missing or broken figures, images stretched or spilling their containers, diagram or chart text rendered off the spec's font or palette, clipped tables, overflowing CJK text, blank pages, unlabeled chart values, wrong page breaks. Reading the source markup is not visual QA; inspect the pixels.
376
+ 5. **Proofread gate: `task(category="writing", ...)`.** Hand the final text to a dedicated `writing` worker whose only job is language: grammar, spelling, punctuation, terminology consistency, and whether the prose reads NATIVELY in the report's own language. It proofreads only and never composes. It returns a defect list; fix every item and re-run the gate on the delta. Deliver only on a clean pass. This gate runs BEFORE the first delivery, not after the user finds the typo.
355
377
 
356
- **Repair is bounded.** After every gate run that found defects, ask `node "$SKILL_DIR/scripts/report-tools.mjs" repair decide --state "$SESSION_DIR/repair-state.json" --defects "$SESSION_DIR/defects.json" --artifact-bytes <bytes> --renders <pages> --session-dir "$SESSION_DIR"` and obey it: `repair` means fix and re-run; `deliver` means stop and ship with the residual defects the manifest now lists; `block` means a content-integrity defect remains (or there is no usable artifact), so do not deliver: tell the user what blocks it and record the row as `failed` with the reason. Never repair past a stop decision (3 attempts, a 2-attempt plateau, an oscillation, or 15 minutes; the reference's section 8).
378
+ **Repair is bounded.** After every gate run that found defects, ask `node "$SKILL_DIR/scripts/report-tools.mjs" repair decide --state "$SESSION_DIR/repair-state.json" --defects "$SESSION_DIR/defects.json" --defects "$SESSION_DIR/fidelity-defects.json" --artifact-bytes <bytes> --renders <pages> --session-dir "$SESSION_DIR"` and obey it: `repair` means fix and re-run; `deliver` means stop and ship with the residual defects the manifest now lists; `block` means a content-integrity defect remains (or there is no usable artifact), so do not deliver: tell the user what blocks it and record the row as `failed` with the reason. Never repair past a stop decision (3 attempts, a 2-attempt plateau, an oscillation, or 15 minutes; the reference's section 8). Fidelity defects ride the same loop (`--defects "$SESSION_DIR/defects.json" --defects "$SESSION_DIR/fidelity-defects.json"`), but a `deliver` decision does not release them: while `fidelity-defects.json` lists `untraceable_number`, `value_mismatch`, `unit_mismatch`, `quote_not_verbatim`, `quote_unbound`, `unbound_sentence`, or `chart_value_untraceable`, `outcome verify` fails with `fidelity_unresolved` and `outcome finish` refuses, even with the repair budget spent. The only ways out are a ledger row, an `assumed` row with its rationale, or removing the sentence.
357
379
 
358
- Then resolve every manifest row (`outcome set <format> delivered --path <file>` or `blocked_capability` / `skipped` / `failed` with `--reason`) and deliver. **Show the report inline first when the harness can.** When this session has a tool that shows an HTML page inline in the conversation (`show_html_page`, `html_render`), the requester's first view of the result is the report itself: the gated `report.html` when it fits the tool, otherwise an inline edition built from the same synthesis and assets, with the reference-compare pairs when the run has them ([references/deliverable-phase.md](references/deliverable-phase.md), section 9, Showing the report inline). Then give the artifact paths and a compact chat-readable summary of what the report says — the answer in a few sentences, the numbers that matter, and what to look at first. This holds on every tier, a chat answer included, and when the thing built is itself a page (an app, a 3D scene), showing it does not replace the report page. Without an inline page tool, deliver the artifact paths plus that summary. The document is the deliverable; the summary is what gets it read.
380
+ Then resolve every manifest row (`outcome set <format> delivered --path <file> --verified-by <record>` (plus `--rendered-from <source>` for a PDF or DOCX) or `blocked_capability` / `skipped` / `failed` with `--reason`) and deliver. **Show the report inline first when the harness can.** When this session has a tool that shows an HTML page inline in the conversation (`show_html_page`, `html_render`), the requester's first view of the result is the report itself: the gated `report.html` when it fits the tool, otherwise an inline edition built from the same synthesis and assets, with the reference-compare pairs when the run has them ([references/deliverable-phase.md](references/deliverable-phase.md), section 9, Showing the report inline). Then give the artifact paths and a compact chat-readable summary of what the report says — the answer in a few sentences, the numbers that matter, and what to look at first. This holds on every tier, a chat answer included, and when the thing built is itself a page (an app, a 3D scene), showing it does not replace the report page. Without an inline page tool, deliver the artifact paths plus that summary. The document is the deliverable; the summary is what gets it read.
359
381
 
360
382
  ### The closing briefing — every run ends with it
361
383
 
362
384
  The last thing the user reads states, in one compact block, what the answer is made of:
363
385
 
364
386
  - **The printed block.** `node "$SKILL_DIR/scripts/report-tools.mjs" outcome verify --session-dir "$SESSION_DIR" && node "$SKILL_DIR/scripts/report-tools.mjs" outcome finish --session-dir "$SESSION_DIR" && node "$SKILL_DIR/scripts/report-tools.mjs" outcome briefing --session-dir "$SESSION_DIR"`: sources and distinct domains counted from `sources-ledger.md`, elapsed minutes from the session directory's own timestamp, every promised deliverable with its status, the gate results, the residual defects, and the repair summary. A failing `verify` means the run is not done.
365
- - **Effort.** Workers, waves, excursions, and verifications — the same counters as the `SYNTHESIS.md` header — plus how many sources were primary and how many claims went to the unresolved/refuted annex.
387
+ - **Effort.** Workers, waves, excursions, and verifications — the same counters as the `SYNTHESIS.md` header — plus how many sources were primary and how many claims went to the unresolved/refuted annex. `fact export` also prints a coverage line (`N of M sources snapshotted, by extractor: ...`); paste it into the briefing's effort line.
366
388
 
367
389
  Never ship the artifact without this block, and never fill it from memory — paste the printed block and read the rest off the journal.
368
390
 
@@ -410,6 +432,9 @@ High-yield combinations: official docs (`site:<docs domain>`), GitHub implementa
410
432
  | Batching findings into an end-of-run journal dump | Journal each return as it lands; the journal is what survives a compaction |
411
433
  | Ending the run because every worker finished | Reserve the final fifth of the run for synthesis and materials |
412
434
  | A derived estimate presented as a measured number | MEASURED / ASSUMED / DERIVED lineage on every quantitative claim, plus a sensitivity line |
435
+ | A number typed into the report or `SYNTHESIS.md` | Register it (`fact add`) and write `{{F<n>:label}}`; `fact verify` reports a typed number as `untraceable_number`, and `outcome verify` refuses delivery until it is a ledger row, an `assumed` row with its rationale, or gone |
436
+ | A filled value edited by hand | Never edit a filled value: fix the ledger row or the placeholder and re-run `fact fill`, then `fact verify` (`value_mismatch`, `unit_mismatch`) |
437
+ | A findings sentence bound to nothing | Bind it to a fact, a quote, a `[S<n>]` citation, or a claim node with `data-claim`; an `unbound_sentence` blocks delivery like an untraceable number |
413
438
  | Delivering before the delivery gates pass | Static and layout gates, visual QA on rendered pages, plus the harness's proofread gate — a typo the user finds means a gate did not run |
414
439
  | Skipping the static gates | `report-tools check` runs before any pixel review; its defects are cheaper than a visual pass |
415
440
  | Delivering with a pending manifest row | `outcome verify` must pass; every promised format ends `delivered`, `blocked_capability`, `skipped`, or `failed` with a reason |
@@ -50,7 +50,7 @@ The request and its destination usually answer the format question already. Deri
50
50
  | A prior report of the same kind in this workspace, not pointed at | that report's format, offered as the Q1 default | `template-vibe` | full |
51
51
  | A complete existing deliverable for this question (section 2) | its existing format | `edit-existing` | the original's tier |
52
52
 
53
- QA tiers: **light** runs the static gates and one render check of the answer, and records the fidelity gate as `not_run` until Markdown deliverable support ships for it. **Full** runs the static gates, the fidelity gate, the layout gates, visual QA of every page, and the proofread gate (section 7).
53
+ QA tiers: **light** runs the static gates and one render check of the answer, and records the fidelity gate as `not_run --reason "<why>"` (the reason is required) until Markdown deliverable support ships for it. **Full** runs the static gates, the fidelity gate, the layout gates, visual QA of every page, and the proofread gate (section 7).
54
54
 
55
55
  ## 4. The empty-info interview
56
56
 
@@ -105,7 +105,7 @@ Write `$SESSION_DIR/design-spec.md` the moment the deliverable fields are known,
105
105
  The extractor copies the CSS custom properties (light and dark), font stacks by role, the most-used colors, px breakpoints, the section skeleton, the heading numbering, the citation marker style, the lineage mode, and the figure containers, under the fixed sections `Extracted from`, `Tokens`, `Typography`, `Layout`, `Structure`, `Figures`, `Citations`, `Open questions`. A field it cannot determine reads `TODO: ask`. A PDF reference is unsupported: ask for its HTML or Markdown source.
106
106
  2. Fill every `TODO: ask` from the interview answers, then from the clean analyst register below. Never guess a value.
107
107
  3. Apply the binding figure standard and the defaults:
108
- - Every figure sits in a fixed-size container with a caption; the image scales inside it with contain-fit, its aspect ratio preserved, never stretched, cropped, or spilling out. Every chart carries a title, axis labels, units, and value labels as real text, with deterministic ticks (round values computed from the data range, never left to a renderer's auto-scaling).
108
+ - Every figure sits in a fixed-size container with a caption; the image scales inside it with contain-fit, its aspect ratio preserved, never stretched, cropped, or spilling out. Every chart carries a title, axis labels, units, and value labels as real text, with deterministic ticks (round values computed from the data range, never left to a renderer's auto-scaling). A chart is plotted only from a `fact export --csv` file saved under `$SESSION_DIR/assets/`, its figure carries `data-chart-source` pointing at that file, and its axis tick labels sit inside one `<g class="ticks" data-tick-step=".." data-tick-min=".." data-tick-max="..">` group, so the fidelity gate can check every value label against the ledger and every tick against the declared grid.
109
109
  - One accent color over neutral background, text, and rule tones. Every color in prose, charts, and diagrams is a spec token.
110
110
  - For web deliverables, HTML/CSS bars and inline SVG are preferred over image charts, and the page has responsive breakpoints so it reads on a phone, a tablet, and a desktop.
111
111
  - Gothic (sans) type only, as a real embedded webfont for the report language; Korean text uses `word-break: keep-all; overflow-wrap: anywhere`.
@@ -123,7 +123,11 @@ Nothing reaches the requester until the gates for its QA tier pass, in this orde
123
123
  node "$SKILL_DIR/scripts/report-tools.mjs" check "$SESSION_DIR/report.html" --design-spec "$SESSION_DIR/design-spec.md" > "$SESSION_DIR/defects.json"
124
124
  ```
125
125
  It checks keep-all, palette tokens, emoji, em dashes in prose (en dashes outside numeric ranges are advisory), heading length and generic headings, unsourced numbers (section 6), figure containers and captions, chart SVG text, a report with no visual at all, images without alt text, Korean serif fonts, sections without citations, the closing section, the sources section, broken local assets, and any `--require-section <regex>` the brief requires. Every code, severity, and fix is in [report-gates.md](report-gates.md).
126
- 2. **Fidelity gate.** The fidelity verifier checks every number, quote, and claim binding against the fact ledger and the source snapshots; its codes are the Fidelity Gate table in [report-gates.md](report-gates.md). It runs before layout and visual QA because a content failure would otherwise force an expensive re-render. Until the fidelity verifier ships in this edition, record `outcome gate fidelity not_run`.
126
+ 2. **Fidelity gate.** The fidelity verifier checks every number, quote, and claim binding against the fact ledger and the source snapshots; its codes are the Fidelity Gate table in [report-gates.md](report-gates.md). It runs before layout and visual QA because a content failure would otherwise force an expensive re-render.
127
+ ```bash
128
+ node "$SKILL_DIR/scripts/report-tools.mjs" fact verify "$SESSION_DIR/report.html" --session-dir "$SESSION_DIR" # writes fidelity-defects.json
129
+ ```
130
+ Exit `1` on any blocker or major defect: record `outcome gate fidelity fail`; minors alone exit `0` and are listed. The grammar, the fill step, and the derivations are in section 11; this stage verifies HTML only.
127
131
  3. **Layout gates.** Print the probe, evaluate it in the rendered page through the browser skill's owned headless engine, save the returned object as `$SESSION_DIR/boxes.json`, and re-run `check` with `--layout "$SESSION_DIR/boxes.json"`. It adds overflow past the container, clipped text, scroll containers, distorted images, and overlapping siblings.
128
132
  ```bash
129
133
  node "$SKILL_DIR/scripts/report-tools.mjs" layout-probe --json # .source is one async expression
@@ -131,20 +135,28 @@ Nothing reaches the requester until the gates for its QA tier pass, in this orde
131
135
  4. **Visual QA.** Render the artifact to images (PDF pages to PNG, the HTML in the browser skill's engine at desktop and phone widths) and look at the pixels for what a machine cannot see: composition, hierarchy, page breaks, figure legibility.
132
136
  5. **Proofread.** A dedicated writing lane proofreads the final text only (grammar, spelling, punctuation, terminology, native register); it never composes or rewrites content.
133
137
 
134
- The light tier runs gate 1 and one render (recorded as `visual`) and records `fidelity`, `layout`, and `proofread` as `not_run`; `fidelity` stays `not_run` in the light tier until Markdown deliverable support ships for the fidelity verifier. The full tier runs all five.
138
+ **Every reader-facing edition is verified.** The main report, the inline edition (section 9), LaTeX per-section sources, and slide sources are filled by `fact fill` from one placeholder source and each is verified by `fact verify` into its own record; PDF and DOCX are renders of a verified source. A delivered row is recorded with `outcome set <format> delivered --path <file> --verified-by <record>`, where the record's `artifact_sha256` equals the sha256 of the delivered source; a PDF or DOCX adds `--rendered-from <source>` and is matched against that source. In a session with a fact ledger, `outcome verify` refuses a delivered row without a matching record (`edition_unverified`, blocker, integrity). Markdown and LaTeX verification arrive with the Markdown stage; until then `fact verify --format md|tex` exits `2`, and such an edition is recorded `blocked_capability` with that reason rather than delivered unverified.
139
+
140
+ The light tier runs gate 1 and one render (recorded as `visual`) and records `fidelity`, `layout`, and `proofread` as `not_run` (`fidelity not_run` needs `--reason "<why>"`); `fidelity` stays `not_run` in the light tier until Markdown deliverable support ships for the fidelity verifier. The full tier runs all five.
135
141
 
136
142
  ## 8. Bounded repair loop
137
143
 
138
144
  After every gate run that produced defects, ask the tracker what to do next:
139
145
 
140
146
  ```bash
141
- node "$SKILL_DIR/scripts/report-tools.mjs" repair decide --state "$SESSION_DIR/repair-state.json" --defects "$SESSION_DIR/defects.json" --artifact-bytes <report bytes> --renders <rendered page count> --session-dir "$SESSION_DIR"
147
+ node "$SKILL_DIR/scripts/report-tools.mjs" repair decide --state "$SESSION_DIR/repair-state.json" --defects "$SESSION_DIR/defects.json" --defects "$SESSION_DIR/fidelity-defects.json" --artifact-bytes <report bytes> --renders <rendered page count> --session-dir "$SESSION_DIR"
142
148
  ```
143
149
 
150
+ `--defects` repeats: every file (a `check` result, a `fact verify` record, or a bare defect array) joins one defect list, so one decision covers both gates.
151
+
144
152
  - `action: repair`: fix the listed defects, re-render, re-run the gate, and ask again.
145
153
  - `action: deliver`: stop repairing and deliver. `reason` is `clean` or the stop condition that fired; the residual defects are written into the manifest and listed in the closing briefing.
146
154
  - `action: block` (exit `1`): do not deliver. Either a content-integrity defect remains (`missing_closing_section`, `missing_citation_section`, `broken_asset_reference`, `missing_required_section`, `missing_promised_deliverable`, or a fidelity integrity code: `unresolved_placeholder`, `value_mismatch`, `unit_mismatch`, `quote_not_verbatim`, `claim_status_invalid`, `incompatible_operands`, `pointer_label_mismatch`, `quote_anchor_unresolved`, `unit_duplicated`, `polish_diff_fail`, `fidelity_interview_incomplete`, `derived_not_allowed`, `source_outside_floor`, `snapshot_hash_mismatch`, `chart_value_untraceable`, `quote_unbound`, `edition_unverified`), or a stop fired without proof of a usable artifact (`--artifact-bytes` and `--renders`). Tell the requester what blocks delivery and record the affected rows as `failed` with the reason.
147
155
 
156
+ **The fidelity barrier is separate from the budget.** A `deliver` decision does not release fidelity defects: `outcome verify` and `outcome finish` use the fidelity gate's `blocksDelivery` predicate over `DEFECT_CODES` (integrity, blocker, or major), never a CLI code list or the row's claimed severity. Unknown codes refuse with `unknown defect code <code>`; known blockers produce `fidelity_unresolved`. Fix the defect and re-verify the edition before delivery. The table and the record schema in `scripts/fidelity-records.mjs` are the spec for the barrier. Every fidelity record carries one envelope (`record_kind`, `artifact_sha256`, `report`, `written_at`, `defects`) plus the fields its kind declares; a record with a missing, unknown, or ill-typed field refuses as malformed (`edition_unverified`). `FIDELITY_RECORDS` has one row per record kind, each rule a column: where its records are, when a delivered session must carry one, the envelope field that binds it to the delivered bytes (`artifact_sha256`), and which defects block. The rows are `fidelity` (`fact verify`, one record per delivered edition, named by `--verified-by`) and `meaning` (`fidelity-meaning-defects.json` from `fact meaning-check`, one per session, required while the locked brief's `meaning check` is `on` or its `## Fidelity` block is malformed; missing: `meaning gate not run`, the same `edition_unverified` refusal, released only by the logged `fidelity not_run` exemption in a session with no ledger or fidelity record). A record whose `artifact_sha256` matches no delivered edition's source bytes refuses as `is for a different edition`, so a meaning record from an earlier draft never passes a rewritten one: rerun `fact pairs` and the readers after every rewrite. A `meaning_drift_confirmed` or `meaning_unverified` in that record holds the barrier like any other major.
157
+
158
+ **After a dispute.** `--dispute P<n> --reason` records the disagreement in the calibration log; it does not clear the defect, so `fact meaning-check` keeps exiting `1` and the barrier keeps holding by design. Proceed through `repair decide` with `fidelity-meaning-defects.json` among its `--defects` (the dispute stays recorded on that run): the writer changes the sentence (rewrite it from the quote, or `fact fill --replace-failed` for a sentence that failed twice), then rerun `fact pairs` and the readers. There is no path that delivers a disputed `meaning_drift_confirmed` sentence unchanged.
159
+
148
160
  Defaults: 3 repair attempts, a plateau of 2 consecutive attempts that do not improve the (blocker, major, minor) counts compared in that order, and 15 minutes of wall clock from the first decision. A defect set that repeats an earlier attempt exactly stops the loop as an oscillation. When several conditions hold at once the reported reason follows `clean`, `blocking`, `oscillation`, `plateau`, `max_attempts`, `wall_clock`.
149
161
 
150
162
  ## 9. Outcome manifest and closing briefing
@@ -153,7 +165,7 @@ Defaults: 3 repair attempts, a plateau of 2 consecutive attempts that do not imp
153
165
 
154
166
  ```bash
155
167
  node "$SKILL_DIR/scripts/report-tools.mjs" outcome init --promised pdf,docx --lane template-vibe --session-dir "$SESSION_DIR"
156
- node "$SKILL_DIR/scripts/report-tools.mjs" outcome set pdf delivered --path "$SESSION_DIR/report.pdf" --pages 12 --session-dir "$SESSION_DIR"
168
+ node "$SKILL_DIR/scripts/report-tools.mjs" outcome set pdf delivered --path "$SESSION_DIR/report.pdf" --rendered-from "$SESSION_DIR/report.html" --verified-by "$SESSION_DIR/fidelity-defects.json" --pages 12 --session-dir "$SESSION_DIR"
157
169
  node "$SKILL_DIR/scripts/report-tools.mjs" outcome set docx blocked_capability --reason "no document converter in this session" --session-dir "$SESSION_DIR"
158
170
  node "$SKILL_DIR/scripts/report-tools.mjs" outcome render "$SESSION_DIR/renders/page-01.png" --page 1 --session-dir "$SESSION_DIR"
159
171
  node "$SKILL_DIR/scripts/report-tools.mjs" outcome verify --session-dir "$SESSION_DIR"
@@ -162,8 +174,9 @@ node "$SKILL_DIR/scripts/report-tools.mjs" outcome briefing --session-dir "$SESS
162
174
  ```
163
175
 
164
176
  - `init` runs when the brief is written (section 4): one `pending` row per promised format (`pdf`, `docx`, `html`, `md`, `latex`, `slides`, `post`).
165
- - `set` resolves a row: `delivered` (absolute path; bytes are read from disk; pages when paginated), `blocked_capability` (this session cannot produce it), `skipped` (the requester or the lane dropped it), or `failed`. Every status except `delivered` needs `--reason`.
166
- - `verify` runs before delivery and exits `1` while a row is `pending`, a status lacks its reason, or a delivered file is missing or empty.
177
+ - `set` resolves a row: `delivered` (absolute path; bytes are read from disk; pages when paginated), `blocked_capability` (this session cannot produce it), `skipped` (the requester or the lane dropped it), or `failed`. Every status except `delivered` needs `--reason`; `outcome set <format> delivered` records intent permissively, while the required `outcome verify` and `outcome finish` enforce the delivery barrier.
178
+ - `verify` runs before delivery and exits `1` while a row is `pending`, a status lacks its reason, a delivered file is missing or empty, a registry blocker remains (`fidelity_unresolved`), or a delivered edition has no matching verify record (`edition_unverified`, section 7). Delivered HTML/PDF/DOCX without any ledger or record also refuse with `fidelity gate not run`; explicitly record `outcome gate fidelity not_run --reason "<text>"` to exempt an unverified edition. With `meaning check: on`, or a malformed `## Fidelity` block (which cannot prove the check off), a delivered session without `fidelity-meaning-defects.json` refuses with `meaning gate not run` under the same rule; a meaning record for bytes no delivered edition has refuses as `is for a different edition`. This choice is logged in `gateHistory` with its timestamp and reason; it never releases an existing ledger requirement, record, or defect, and a subsequent fidelity gate status replaces the exemption.
179
+ - `finish` refuses (exit `1`, nothing stamped) while the fidelity barrier holds.
167
180
  - `finish` stamps the finish time, the elapsed minutes from the session directory's start stamp, and the source and domain counts from `sources-ledger.md`.
168
181
  - `briefing` prints the closing block from the manifest and the ledger: sources and distinct domains, elapsed minutes, every deliverable with its status, the gate results, the residual defects, and the repair summary. That text is the closing briefing, pasted as printed, never filled in from memory.
169
182
 
@@ -185,14 +198,61 @@ Invoke every command as `node "$SKILL_DIR/scripts/report-tools.mjs" <command>`.
185
198
  | --- | --- | --- |
186
199
  | `check <report.html>` | `--design-spec <spec>` (required), `--layout <boxes.json>`, `--require-section <regex>` (repeatable), `--max-defects N` | any blocker defect |
187
200
  | `layout-probe` | `--cap N` (default 400), `--root <selector>` (default `main, article`), `--json` | never |
188
- | `repair decide` | `--state <file>` and `--defects <json>` (required), `--artifact-bytes N`, `--renders N`, `--session-dir <dir>`, `--max-attempts N`, `--plateau-limit N`, `--wall-clock-ms N`, `--now <ISO>` | the decision is `block` |
201
+ | `repair decide` | `--state <file>` and `--defects <json>` (required; repeatable, the files are concatenated), `--artifact-bytes N`, `--renders N`, `--session-dir <dir>`, `--max-attempts N`, `--plateau-limit N`, `--wall-clock-ms N`, `--now <ISO>` | the decision is `block` |
189
202
  | `outcome init` | `--promised <list>` (required), `--lane <lane>`, `--session-dir <dir>`, `--now <ISO>` | never |
190
- | `outcome set <format> <status>` | `--path <file>`, `--pages N`, `--reason <text>`, `--session-dir <dir>`, `--now <ISO>` | never |
191
- | `outcome gate <gate> <status>` | `--session-dir <dir>` | never |
203
+ | `outcome set <format> <status>` | `--path <file>`, `--pages N`, `--reason <text>`, `--verified-by <fidelity record>`, `--rendered-from <source>`, `--session-dir <dir>`, `--now <ISO>` | never |
204
+ | `outcome gate <gate> <status>` | `--session-dir <dir>`, `--now <ISO>`, `--reason <text>` (required for `fidelity not_run`; logged in `gateHistory`) | never (a missing reason exits `2`) |
192
205
  | `outcome render <png>` | `--page N` (required), `--session-dir <dir>` | never |
193
206
  | `outcome state` | `--deliverable <path>` (required), `--session-dir <dir>` | never |
194
- | `outcome verify` | `--session-dir <dir>` | a promise is unresolved or a delivered file is missing or empty |
195
- | `outcome finish` | `--ledger <file>`, `--session-dir <dir>`, `--now <ISO>` | never |
207
+ | `outcome verify` | `--session-dir <dir>` | a promise is unresolved, a delivered file is missing or empty, `fidelity_unresolved`, or `edition_unverified` |
208
+ | `outcome finish` | `--ledger <file>`, `--session-dir <dir>`, `--now <ISO>` | the fidelity barrier holds (section 8) |
196
209
  | `outcome briefing` | `--json`, `--ledger <file>`, `--session-dir <dir>`, `--now <ISO>` | never |
197
210
  | `format-extract <reference>` | `--out <design-spec.md>` (required), `--from-url` | never (an unsupported reference exits `2`) |
198
211
  | `--help` | `--json` | never |
212
+ | `source snapshot` | `--id S<n>` (required), one of `--url <u>`, `--html <file>`, `--text <file>` (with optional `--extractor NAME@VERSION`), `--command '<argv json>' --cwd <dir>`; `--kind page\|command\|document`, `--provenance primary\|secondary`, `--revision <r>`, `--session-dir <dir>` | the id already holds different content (`snapshot_exists`) |
213
+ | `fact add` | `--source S<n>` with `--find <text>` (`--unit-expected <unit>`), or `--table <id> --row <label> --col <label>`, or `--page N --line N`; `--label`, `--entity`, `--valid-at`, `--population`, `--accounting-basis`, `--observation O<n>`, `--json`, `--session-dir <dir>`. Or `--assumed <value> --unit <unit> --rationale <text> --label <label>` | `fact_not_located`, `fact_ambiguous`, `unit_mismatch` (no row written) |
214
+ | `fact quote` | `--source S<n> --match <text>` (required), `--label`, `--json`, `--session-dir <dir>` | `fact_not_located`, `fact_ambiguous` |
215
+ | `fact retract <id>` | `--reason <text>` (required), `--replacement <id>`, `--session-dir <dir>` | never (an illegal transition exits `2`) |
216
+ | `fact export` | `--csv`, `--out <file>`, `--session-dir <dir>` | never |
217
+ | `fact fill <src>` | `--out <file>` or `--view`, `--format html\|md\|tex`, `--inline`, `--replace-failed`, `--session-dir <dir>` | a fill error (nothing is written) |
218
+ | `fact verify <report>` | `--format html\|md` (from the extension by default), `--sources <dir>`, `--claims <claims.json>`, `--fill-map <file>` (a Markdown edition reads `<report>.fill-map.json` by default), `--brief <brief.md>`, `--out <record>`, `--session-dir <dir>` | any blocker or major defect |
219
+ | `fact pairs <report>` | `--format html\|md`, `--ledger <fact-ledger.json>`, `--claims <claims.json>`, `--changed-since <fill-map.json>` (pair only the facts that map filled), `--brief <brief.md>`, `--out <pairs.json>`, `--session-dir <dir>` | the brief has no locked `## Fidelity` block (with `meaning check: off` it writes `{"skipped": true}` and exits `0`) |
220
+ | `fact meaning-check` | `--pairs <pairs.json>` and `--verdicts <file>` (required; repeatable, one per reader tier), `--dispute P<n> --reason <text>`, `--run-id <id>`, `--calibration <file>` (default `fidelity-calibration.jsonl`), `--out <record>` (default `fidelity-meaning-defects.json`), `--session-dir <dir>` | `meaning_drift_confirmed` (stderr carries one JSON line with the defect and dropped-verdict counts) |
221
+ | `fact derive` | `--op <op>` (required) with the operation's operands (section 11), `--unit`, `--to`, `--rationale`, `--disjoint`, `--label`, `--json`, `--session-dir <dir>` | `incompatible_operands` (no row written) |
222
+ | `claims index <claim-graph.md>` | `--out <claims.json>` (required) | never (a malformed claim table exits `2`) |
223
+
224
+ ## 11. Fidelity gate
225
+
226
+ Every number, quotation, and chart value in a reader-facing edition comes from the fact ledger (`$SESSION_DIR/fact-ledger.json`, schema and operations in [fact-ledger.md](fact-ledger.md)), which is built only from source snapshots (`$SESSION_DIR/sources/S<n>/`). Nobody types a value into a report. The commands, in the order a run uses them:
227
+
228
+ ```bash
229
+ node "$SKILL_DIR/scripts/report-tools.mjs" source snapshot --id S4 --url https://example.org/bridge --session-dir "$SESSION_DIR"
230
+ node "$SKILL_DIR/scripts/report-tools.mjs" fact add --source S4 --find "Span length" --label span_len --session-dir "$SESSION_DIR"
231
+ node "$SKILL_DIR/scripts/report-tools.mjs" fact quote --source S4 --match "no earlier than 2027" --label completion --session-dir "$SESSION_DIR"
232
+ node "$SKILL_DIR/scripts/report-tools.mjs" fact derive --op convert F1 --to mm --label span_mm --session-dir "$SESSION_DIR"
233
+ node "$SKILL_DIR/scripts/report-tools.mjs" claims index "$SESSION_DIR/claim-graph.md" --out "$SESSION_DIR/claims.json"
234
+ node "$SKILL_DIR/scripts/report-tools.mjs" fact fill "$SESSION_DIR/report.src.html" --out "$SESSION_DIR/report.html" --session-dir "$SESSION_DIR"
235
+ node "$SKILL_DIR/scripts/report-tools.mjs" fact verify "$SESSION_DIR/report.html" --session-dir "$SESSION_DIR"
236
+ ```
237
+
238
+ - **Snapshot.** A source directory is immutable: the same id with identical text is a no-op, different text fails with `snapshot_exists`, because ledger rows pin the old hash; use a new id. `--url` fetches with plain fetch (30 s timeout, no browser) and records extractor `tool:source-snapshot@1`. `--html` and `--text` import a capture the browser lane saved and record extractor `model` (lower trust), or the named extractor with `--text --extractor NAME@VERSION`. `--command` runs an argv without a shell, snapshots its stdout, and writes `command.json` with the argv, working directory, exit status, and output hashes.
239
+ - **Register.** `fact add` locates one quantity deterministically (`--find`: the first numeral within 80 characters after a unique anchor, in the same sentence; `--table`/`--row`/`--col` by header labels; `--page`/`--line`) and prints the row with 60 characters of source context on each side (`--json` for the full row). `fact quote` stores the whole containing sentence. The row records the source's kind, URL, host, and whether the brief's source floor points at it. A failed locate writes nothing.
240
+ - **Write by reference.** Placeholders: `{{F12:span_len}}` (value and unit), `{{F12:span_len:value}}`, `{{F12:span_len:unit}}`, `{{F12:span_len:q}}` (with the source qualifier), `{{Q7:completion}}`, `{{Q7:completion:lead=8}}`, `{{Q7:completion:from="no earlier":to="2027"}}`. The label must equal the row's label.
241
+ - **Fill.** `fact fill` is all or nothing: on `pointer_label_mismatch`, `unresolved_placeholder`, `quote_anchor_unresolved`, or `unit_duplicated` it exits `1` and writes nothing. On success it writes the output and `<out>.fill-map.json`. `--view` prints the rendered text without writing; `--inline` builds the inline edition (local stylesheets and scripts inlined, image paths made absolute) from the same source; `--format md` and `--format tex` fill Markdown and LaTeX sources; `--replace-failed` replaces a sentence that failed twice with an attributed block quote of its source.
242
+ - **Verify.** `fact verify` writes its record (default `fidelity-defects.json`; `--out` for another edition) with the verified file's `artifact_sha256`, reads `claims.json` and the locked `## Fidelity` block of `brief.md`, and exits `1` on any blocker or major defect; minors alone exit `0`. A Markdown edition (`--format md`, or a `.md` path) is verified with its fill map; a LaTeX edition is verified through its Markdown source, so `--format tex` exits `2`.
243
+ - **Meaning.** With `meaning check: on`, `fact pairs` writes the pairs a separate reader session judges, and `fact meaning-check` turns the readers' verdicts into `fidelity-meaning-defects.json` and the calibration log (verdict schema in [fact-ledger.md](fact-ledger.md)). No script calls a model.
244
+ - **Export.** `fact export --csv --out "$SESSION_DIR/assets/<chart>.csv"` writes `fact_id,label,plot_value,unit` for every active value row, the only data a chart may plot, and prints `N of M sources snapshotted, by extractor: ...` for the closing briefing.
245
+
246
+ Derivations are computed in exact rationals from the operands' stored values and rendered at the operands' fewest significant figures; a refused operation exits `1` with `incompatible_operands` and writes no row.
247
+
248
+ | Operation | Operands | Checks |
249
+ | --- | --- | --- |
250
+ | `sum` | two or more ids, `--unit` | one unit; summing `%` needs `--disjoint` and `--rationale` |
251
+ | `diff` | two ids (minuend, subtrahend) | one unit; a difference of `%` is in `pp` |
252
+ | `growth` | `--from <id> --to <id>` | one declared entity, two different `valid_at` periods, a positive start; result in `%` |
253
+ | `ratio` | `--num <id> --den <id>`, `--unit` | a non-zero denominator |
254
+ | `share` | `--part <id> --whole <id>` | one unit; result in `%` |
255
+ | `convert` | one id, `--to <unit>` | a known conversion between the units |
256
+ | `expr` | a formula over ids, `+ - * /`, `0`, `1`, `100`, `--unit`, `--rationale` | none (recorded `unchecked`) |
257
+
258
+ Exit codes: `0` ok; `1` a semantic failure (`fact_not_located`, `fact_ambiguous`, a fill error, `incompatible_operands`, `snapshot_exists`, or a verify record with a blocker or major defect); `2` usage, an unreadable input, or an invalid ledger write.
@@ -49,7 +49,11 @@ Always required, never null: `fact_id`, `kind`, `raw` (string), `quote` (string)
49
49
  | `locator` | object | object | object or null | object or null | object or null |
50
50
  | `snapshot_sha256` | sha256 | sha256 | sha256 or null | sha256 or null | sha256 or null |
51
51
  | `rationale` | string or null | string or null | string or null | non-empty | string or null |
52
- | `exact` | from `value` | null | written by derive (rational or null) | rational or null | written by derive (rational or null) |
52
+ | `exact` | from `value` | null | reduced rational | rational or null | reduced rational |
53
+ | `context` | null | null | written by derive (object or null) | null | written by derive (object or null) |
54
+ | `operation` | null | null | sum/diff/growth/share/ratio/expr | null | `convert` |
55
+ | `precision` | null | null | `{ places, sigfigs }` | null | `{ places, sigfigs }` |
56
+ | `compatibility` | null | null | checked or unchecked (expr) | null | checked or unchecked (expr) |
53
57
  <!-- kind-rules:end -->
54
58
 
55
59
  - `exact` is `{ num: integer string, den: positive integer string }` or null. A measured row's
@@ -61,6 +65,20 @@ Always required, never null: `fact_id`, `kind`, `raw` (string), `quote` (string)
61
65
  col_label? }`.
62
66
  - All other fields are nullable with the types in `fact-ledger-schema.mjs`.
63
67
 
68
+ Exact rational integer strings are canonical: no leading zeros, negative zero, negative denominator,
69
+ or common factors. Unit aliases normalize at input, with numeric scaling for basis-point factors
70
+ in composites. Derived rows store canonical units, compared exactly, including symbols' case; a source
71
+ row's registered alias is canonicalized when the ledger is read (`unit-rules.md` dimension 31).
72
+
73
+ A ledger written before `context`, `precision`, and `compatibility` existed still loads: source rows
74
+ read those columns as null, and older derived or converted rows load but are never used as operands.
75
+
76
+ Derived and converted rows require a matching operation, its closed context, distinct existing operands,
77
+ formula, derived_from, precision, and compatibility. Source rows cannot carry derivation fields.
78
+ Precision records nonnegative decimal places and positive significant figures, independently of display
79
+ text. Recomputation verifies redundant audit fields and recursively verifies each shared dependency once.
80
+ New work may consume only active transitive dependencies; historical verification ignores current eligibility.
81
+
64
82
  ## Labels
65
83
 
66
84
  A label is unique across ALL rows, whatever their status. Retracting or superseding a row does
@@ -0,0 +1,95 @@
1
+ # Fact ledger
2
+
3
+ `$SESSION_DIR/fact-ledger.json` holds every number and every direct quotation a research run may put in front of a reader. Rows come only from a source snapshot (through a deterministic locator), from a script-computed derivation, or from an explicit assumption with a rationale. A report source never carries a typed value: it carries a placeholder, and `fact fill` renders the row. The validity rules (ids, per-kind columns, the status machine) are in [fact-ledger-grammar.md](fact-ledger-grammar.md); this file is the working guide.
4
+
5
+ Every command is `node "$SKILL_DIR/scripts/report-tools.mjs" <command> --session-dir "$SESSION_DIR"`; the flags and exit codes are in [deliverable-phase.md](deliverable-phase.md), sections 10 and 11.
6
+
7
+ ## Files
8
+
9
+ | Path | Written by | Holds |
10
+ | --- | --- | --- |
11
+ | `sources/S<n>/text.txt` | `source snapshot` | the extracted text the locators read (one line per block; pages joined by a form feed); `sha256` covers this file |
12
+ | `sources/S<n>/source.html` | `source snapshot` (page sources) | the captured page, read by the table locator |
13
+ | `sources/S<n>/meta.json` | `source snapshot` | `source_id`, `url`, `observed_at`, `sha256`, `extractor`, `revision`, `kind`, `provenance` |
14
+ | `sources/S<n>/sentences.jsonl`, `acronyms.json` | `source snapshot` | the sentence index (`S<n>:s<k>`) and the acronym table the fidelity gate reads |
15
+ | `sources/S<n>/command.json` | `source snapshot --command` | argv, working directory, exit status, stdout and stderr hashes |
16
+ | `fact-ledger.json` | `fact add`, `fact quote`, `fact derive`, `fact retract` | `{ schemaVersion: 1, rows, events }` |
17
+ | `claims.json` | `claims index` | the claim table and the `verified-claims` membership |
18
+ | `fidelity-defects.json` | `fact verify` | the verify record: the fidelity envelope (`record_kind: "fidelity"`, `artifact_sha256`, `report`, `written_at`, `defects`) plus `command`, `status`, `exitCode`, `format`, `counts`, `gatesRun` |
19
+ | `fidelity-meaning-defects.json` | `fact meaning-check` | the meaning record: the fidelity envelope (`record_kind: "meaning"`) plus the run fields under Meaning verdicts |
20
+
21
+ A source directory is immutable. Snapshotting the same id again with the same text is a no-op; different text fails with `snapshot_exists`, because rows pin the snapshot hash.
22
+
23
+ ## Row fields
24
+
25
+ | Field | Meaning |
26
+ | --- | --- |
27
+ | `fact_id` | `F<n>` for a value, `Q<n>` for a quotation; assigned by the writer |
28
+ | `kind` | `measured` (located in a snapshot), `quote`, `derived`, `converted`, or `assumed` |
29
+ | `label` | the name a placeholder must repeat exactly (`[a-z0-9_-]`, up to 32 characters) |
30
+ | `value`, `unit`, `exact` | the decimal value as written, its normalized unit, and the exact rational the arithmetic uses |
31
+ | `raw`, `quote`, `value_span` | the source's own characters for the value, the whole containing sentence or cell, and where `raw` sits inside `quote` |
32
+ | `qualifier` | a bounding word the source puts on the value: `upper_inclusive` (up to, at most), `lower_inclusive` (at least), `approximate` (about), `near` (nearly), `lower_exclusive` (over, more than), `upper_exclusive` (under, less than) |
33
+ | `source_id`, `snapshot_sha256`, `sentence_id` | the snapshot the row was read from, its hash at that moment, and the sentence id |
34
+ | `locator` | where `quote` sits in `text.txt` (`artifact`, `line`, `page`, `char_start`, `char_end`, and `table`, `row_label`, `col_label` for a table cell), plus the source's `source_kind`, `url`, `host`, and `pointed` (whether the brief's source floor names it) |
35
+ | `extracted_by` | the snapshot's extractor: `tool:source-snapshot@1` (fetched or executed by the script), `model` (a capture the browser lane imported; lower trust), or `extractor:<name>@<version>`; `fact-derive` for a derivation |
36
+ | `observed_at`, `valid_at` | when the row was registered, and the period the value describes (`--valid-at`) |
37
+ | `entity`, `population`, `accounting_basis`, `observation_id` | declarations the derivations check (`--entity`, `--population`, `--accounting-basis`, `--observation O<n>`) |
38
+ | `operation`, `operands`, `derived_from`, `formula`, `factor`, `render` | for a derived or converted row: what was computed from which rows, and the rendered text |
39
+ | `rationale` | why an assumed value was chosen (required for `assumed`), or why an `expr` derivation is sound |
40
+ | `status` | `active`, `retracted`, or `superseded`; rows are never deleted, and each change appends an event |
41
+
42
+ ## Locators
43
+
44
+ | Flags | Resolves |
45
+ | --- | --- |
46
+ | `--find "<anchor>"` | one normalized anchor occurrence, then the first numeral starting within 80 characters after it, in the same sentence; `--unit-expected` checks the unit |
47
+ | `--table <id or caption> --row <label> --col <label>` | the cell by header labels, never by index; duplicate labels fail |
48
+ | `--page N --line N` | exactly one numeral on that line of an extracted document |
49
+ | `--match "<words>"` (`fact quote`) | the unique sentence containing the words; the whole sentence is stored, so a later partial quote cannot drop its hedge |
50
+
51
+ A locator that finds nothing returns `fact_not_located`, more than one candidate `fact_ambiguous`, and nothing is written. Read the printed row and its 60 characters of context on each side before using it; a wrong row is retracted, never edited.
52
+
53
+ ## Meaning verdicts
54
+
55
+ `ingestVerdicts(pairs, verdictFiles, options)` (`scripts/meaning-check.mjs`) folds the meaning readers' verdict files over the pairs of `extractPairs` and returns four lists:
56
+
57
+ | Field | Holds |
58
+ | --- | --- |
59
+ | `defects` | one `meaning_drift_confirmed` (both tiers named the same kind, never `other`) or `meaning_drift_warn` (one tier) per flagged pair, in pair order; the fragment comes from the verdict whose kind is reported |
60
+ | `dropped` | every verdict that was not accepted, with its file, pair, tier, and reason (unknown pair, invalid tier or verdict, unknown or empty drift kind, a fragment missing from the sentence or the source) |
61
+ | `unverified` | every pair no reader judged (`no_verdict`) and every `force_strong` pair no strong reader judged (`no_strong_verdict`), in pair order; a dropped verdict does not count as read |
62
+ | `calibrationLines` | one JSON line per flagged pair, appended to `fidelity-calibration.jsonl` when `calibrationPath` is given |
63
+
64
+ Coverage is fail-closed. A consumer treats a non-empty `unverified` list as blocking: those pairs were never read, so an empty `defects` list does not mean the report is clean. An unreadable verdict path or invalid JSON raises a `TypeError` naming the file, which the CLI maps to exit code 2.
65
+
66
+ `fact meaning-check` writes these lists into `fidelity-meaning-defects.json` through the one fidelity record writer (`writeFidelityRecord` in `scripts/fidelity-records.mjs`). The record is the envelope every fidelity record carries, `record_kind` (`meaning`), `artifact_sha256` and `report` (copied from `pairs.json`, so the record names the edition the readers judged), `written_at`, and `defects` (the folded defects plus one `meaning_unverified` per unread pair), followed by the run fields `command`, `status`, `exitCode`, `run_id`, `pairs_sha256`, `counts`, `dropped`, `unverified`, `calibration_path`, `calibration_written`, `calibration_replaced`, and `promotion_eligibility`. Any other field makes the record malformed. A pairs file without `artifact_sha256` and `report` exits 2 before anything is written. The delivery barrier refuses the record when no delivered edition's source bytes hash to its `artifact_sha256`, so a rewrite needs a fresh `fact pairs` and reader run.
67
+
68
+ ## Operations
69
+
70
+ - `fact add --source S<n> <locator> --label <label>` registers a measured row; `fact add --assumed <value> --unit <unit> --rationale <why> --label <label>` registers an assumption.
71
+ - `fact quote --source S<n> --match "<words>" --label <label>` registers a quotation.
72
+ - `fact derive --op <sum|diff|growth|ratio|share|convert|expr> ...` computes a new row from active rows and prints its id; the placeholder uses that id. Operands go by position for sum, diff, and convert (`--to <unit>`), as `--from/--to`, `--part/--whole`, or `--num/--den` for growth, share, and ratio, and as one formula argument for expr. Every op except convert declares its result unit with `--unit <unit>` (`--unit null` for dimensionless); a `%` sum also needs `--disjoint --reference <denominator> --rationale <why>`. The unit rules are in `unit-rules.md`.
73
+ - `fact retract <id> --reason <why>` retires a row; `--replacement <id>` marks it superseded by another active row. Rows that depend on a retired row fail at fill time.
74
+ - `fact export --csv --out <file>` writes `fact_id,label,plot_value,unit` for every active value row: the only data a chart may plot.
75
+ - `fact fill <src> --out <file>` renders every placeholder from the ledger; `fact verify <file>` checks the filled edition against the ledger, the snapshots, and the claim index; `outcome set <format> delivered` records intent permissively, while the required `outcome verify` and `outcome finish` enforce the delivery barrier.
76
+ - `fact pairs <file>` writes `pairs.json` for the meaning readers; `fact meaning-check --pairs pairs.json --verdicts <file> ...` ingests their verdicts.
77
+
78
+ ## Meaning command
79
+
80
+ `pairs.json` is `{ schemaVersion, skipped, report, format, artifact_sha256, pairs_sha256, counts, verdict_schema, pairs }`. `verdict_schema` is the machine-read verdict contract (the allowed `model_tier`, `verdict`, and `drift_kind` values and the required fields), built from the registries in `meaning-check.mjs`; hand it to the reader with the pairs. Each pair has `pair_id` (`P<n>` in document order), `kind` (`quote`, `claim`, or `translation`), `sentence_key`, `sentence`, `source_lang`, `report_lang`, `force_strong`, and either `fact_ids` with `quote` or `claim_id` with `statement`.
81
+
82
+ A reader writes one verdict file per tier: `{ "model_tier": "cheap" | "strong", "verdicts": [...] }` (a bare array works when each verdict names its own `model_tier`). One verdict per pair:
83
+
84
+ | Field | Value |
85
+ | --- | --- |
86
+ | `pair_id` | a `pair_id` from `pairs.json`; any other id is dropped (`unknown_pair`) |
87
+ | `verdict` | `ok` or `drift` |
88
+ | `drift_kind` | for `drift`, exactly one of `subject_changed`, `scope_widened`, `hedge_dropped`, `certainty_added`, `causality_added`, `direction_flipped`, `population_changed`, `evidence_grade_raised`, `other` |
89
+ | `fragment` | for `drift`, a non-empty verbatim substring of the pair's `sentence` (whitespace-normalized) |
90
+ | `source_fragment` | for `drift`, a non-empty verbatim substring of the pair's `quote` or `statement` |
91
+ | `explanation` | one sentence, for the briefing; never read by the scripts |
92
+
93
+ A verdict that breaks any rule is dropped and listed in the record's `dropped` with its reason; it never becomes a defect. Both tiers naming the same `drift_kind` other than `other` on one pair is `meaning_drift_confirmed` (major); one tier, or two tiers that disagree, is `meaning_drift_warn` (minor). `--dispute P<n> --reason` keeps the defect and marks its calibration line `disputed`; the command still exits `1`, and the way forward is the repair loop described in deliverable-phase.md ("After a dispute"). A `{"skipped": true}` pairs file is accepted only when the locked brief says `meaning check: off`; with the check on (`meaning_check_required`) or no locked block (`fidelity_interview_incomplete`) it exits `1`. Every entry of `unverified` becomes one `meaning_unverified` defect (major), so a pair no reader judged fails the command (exit 1) through the same registry policy as any other defect. A malformed `pairs.json` or an unreadable verdict file exits 2.
94
+
95
+ `fidelity-calibration.jsonl` starts with a header line `{ "header": true, "promotion": ... }` holding the promotion criterion, then per run one run line `{ "run": true, run_id, pairs_sha256, flagged }` (written even when nothing was flagged, so `min_runs` counts real runs) and one line per flagged pair: `kind`, `pair_id`, `tiers`, `drift_kind`, `confirmed`, `disputed`, `reason`, `run_id`, `pairs_sha256`. The run id (`--run-id`, or a hash of the pairs and verdict files only, never the dispute) keys the run: rerunning the same inputs, with or without `--dispute`, replaces that run's lines in place, and a dispute recorded earlier carries forward to the rewritten lines. A corrupt log line exits `2` naming `<path>:<line>`. The record's `promotion_eligibility` evaluates the criterion over the log, counting report-level false blocks over the last `false_block_window` runs.
@@ -0,0 +1,19 @@
1
+ # Fidelity Claim Kinds
2
+
3
+ A report sentence states a plain fact or quote by default. When it claims more than that (that we ran or checked something, that a number is derived, that it is an assumption), the writer marks the claim kind in the placeholder, fill renders the mark as the `data-claim-kind` attribute on the element it writes, and the fidelity gate checks that every marked fact cites a ledger row the kind accepts. The gate never infers a claim kind from wording: an unmarked sentence is checked as a plain fact or quote (so an unmarked `assumed` row is a `claim_kind_mismatch`: an unmarked assumption reads as a fact and needs its `{{A:F12:label}}` mark), and only the unmarked-warning rule raises a warning (severity minor, never blocking) when a sentence reads like the kind and cites a fact not marked with it.
4
+
5
+ A mark binds per cited fact. Each `data-fact` element takes the kind of its nearest mark (its own attribute, else the closest marked ancestor), and only that kind is checked against its row; a parent mark applies to the facts it binds, never to the sentence as a whole. A fact under marks of two different kinds is a `claim_kind_mismatch`, and a mark that binds no fact of its own (all of them are shadowed by another kind, or it cites none) is its kind's defect: shadowing never satisfies a parent.
6
+
7
+ A `verified` claim is satisfied only when the cited row itself has a locator artifact inside `sources/<source_id>/` whose snapshot `meta.json` has `kind: "command"` (recorded command or test output); a different command row elsewhere in the sentence never satisfies it. Korean phrases are shown as `\u` escapes, exactly as `claim-kinds.mjs` stores them. The defect codes are in [report-gates.md](report-gates.md).
8
+
9
+ <!-- claim-kinds:begin (rendered from CLAIM_KINDS; do not edit by hand) -->
10
+ | Kind | Placeholder | Attribute fill emits | Accepted row kinds | Required source kind | Defect when unsatisfied | Unmarked warning | Notes |
11
+ |---|---|---|---|---|---|---|---|
12
+ | `fact` | `{{F12:label}}` | none (or `data-claim-kind="fact"`) | `measured`, `converted`, `derived` | any | `claim_kind_mismatch` | none | Accepts exactly what an unmarked numeral accepts, so marking fact is never stricter than not marking. Neither accepts an `assumed` row: an unmarked assumption reads as a fact, so it needs its `{{A:F12:label}}` mark (`claim_kind_mismatch` otherwise). |
13
+ | `quote` | `{{Q7:label}}` | none (or `data-claim-kind="quote"`) | `quote` | any | `claim_kind_mismatch` | none | - |
14
+ | `verified` | `{{V:F12:label}}` | `data-claim-kind="verified"` | `measured`, `quote` | `command` | `unsupported_verification` | `verification_claim_unmarked` on a first-person run: we/I (have) ran, run, executed, verified by (not ran out of); by running after we/our in the sentence; \uC6B0\uB9AC or \uC800\uD76C as the subject word (+ \uAC00, \uB294, or \uD300\uC774), then \uC2E4\uD589\uD588, \uC2E4\uD589\uD558\uC5EC, \uC2E4\uD589\uD574, \uB3CC\uB838, or \uB3CC\uB824 in the clause with no third-party subject between; \uC9C1\uC811 with no third-party subject, then \uC2E4\uD589 or \uB3CC\uB9AC in the clause | The cited row itself must have a locator artifact inside `sources/<source_id>/` whose `meta.json` kind is `command`; another command row in the sentence never counts. Whether it is the right command for the claim is the meaning verifier's call (minor), not this gate's. |
15
+ | `derived` | `{{D:F12:label}}` | `data-claim-kind="derived"` | `derived`, `converted` | any | `claim_kind_mismatch` | none | - |
16
+ | `assumed` | `{{A:F12:label}}` | `data-claim-kind="assumed"` | `assumed` | any | `claim_kind_mismatch` | none | Numeric assumptions only (an assumed ledger row carries a decimal value); a qualitative assumption stays prose. |
17
+ <!-- claim-kinds:end -->
18
+
19
+ Regenerate the table with `node scripts/claim-kinds.mjs --render-doc`.