oh-my-opencode 5.1.47 → 5.1.51

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 (497) hide show
  1. package/dist/agents/refusal-rule.d.ts +7 -0
  2. package/dist/cli/index.js +1194 -752
  3. package/dist/cli/service-auth/accounts-gate.d.ts +11 -0
  4. package/dist/cli/service-auth/commands.d.ts +2 -4
  5. package/dist/cli/service-auth/index.d.ts +1 -1
  6. package/dist/cli/service-auth/keystore.d.ts +26 -2
  7. package/dist/cli/service-auth/protocol.d.ts +7 -1
  8. package/dist/cli/service-auth/session.d.ts +6 -0
  9. package/dist/cli/service-auth/signed-out-marker.d.ts +11 -0
  10. package/dist/cli-node/index.js +1194 -752
  11. package/dist/config/schema/hooks.d.ts +1 -0
  12. package/dist/create-hooks.d.ts +1 -0
  13. package/dist/features/boulder-state/storage.d.ts +1 -1
  14. package/dist/features/boulder-state/types.d.ts +1 -1
  15. package/dist/hooks/atlas/idle-continuation.d.ts +2 -1
  16. package/dist/hooks/atlas/types.d.ts +9 -1
  17. package/dist/hooks/index.d.ts +1 -0
  18. package/dist/hooks/momus-review-persistence/hook.d.ts +19 -0
  19. package/dist/hooks/momus-review-persistence/index.d.ts +1 -0
  20. package/dist/hooks/momus-review-persistence/review-resolution.d.ts +20 -0
  21. package/dist/hooks/prometheus-md-only/constants.d.ts +2 -0
  22. package/dist/hooks/prometheus-md-only/fixtures/denial-log.d.ts +1 -0
  23. package/dist/hooks/prometheus-md-only/fixtures/guarded-hashline.d.ts +1 -0
  24. package/dist/hooks/prometheus-md-only/fixtures/invoke-before.d.ts +7 -0
  25. package/dist/hooks/prometheus-md-only/fixtures/opencode-patch-parser.d.ts +2 -0
  26. package/dist/hooks/prometheus-md-only/hook.d.ts +5 -1
  27. package/dist/hooks/prometheus-md-only/path-policy.d.ts +2 -9
  28. package/dist/hooks/prometheus-md-only/vendor/opencode-patch-parser.d.ts +22 -0
  29. package/dist/index.js +2322 -1045
  30. package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
  31. package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +2 -1
  32. package/dist/shared/posthog-activity-state.d.ts +1 -0
  33. package/dist/shared/posthog.d.ts +1 -2
  34. package/dist/shared/resolve-file-write-path.d.ts +6 -0
  35. package/dist/skills/ast-grep/README.md +1 -1
  36. package/dist/skills/ast-grep/install.ps1 +5 -3
  37. package/dist/skills/ast-grep/install.sh +4 -5
  38. package/dist/skills/ast-grep/references/install.md +2 -2
  39. package/dist/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
  40. package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
  41. package/dist/skills/coding-agent-sessions/AGENTS.md +5 -4
  42. package/dist/skills/coding-agent-sessions/SKILL.md +11 -11
  43. package/dist/skills/coding-agent-sessions/references/all-platforms.md +2 -2
  44. package/dist/skills/coding-agent-sessions/references/codex.md +4 -4
  45. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
  46. package/dist/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
  47. package/dist/skills/frontend/ATTRIBUTION.md +1 -1
  48. package/dist/skills/frontend/SKILL.md +34 -12
  49. package/dist/skills/frontend/references/design/README.md +2 -2
  50. package/dist/skills/frontend/references/design/_INDEX.md +2 -1
  51. package/dist/skills/frontend/references/design/component-catalogs.md +14 -10
  52. package/dist/skills/frontend/references/design/design-system-architecture.md +19 -6
  53. package/dist/skills/frontend/references/design/grounding.md +29 -0
  54. package/dist/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
  55. package/dist/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
  56. package/dist/skills/ultimate-browsing/SKILL.md +5 -4
  57. package/dist/skills/ultimate-browsing/engine/AGENTS.md +2 -2
  58. package/dist/skills/ultimate-browsing/engine/__main__.py +9 -6
  59. package/dist/skills/ultimate-browsing/engine/summary.py +1 -1
  60. package/dist/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  61. package/dist/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
  62. package/dist/skills/ulw-research/AGENTS.md +8 -4
  63. package/dist/skills/ulw-research/SKILL.md +7 -259
  64. package/dist/skills/ulw-research/references/deliverable-phase.md +68 -24
  65. package/dist/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
  66. package/dist/skills/ulw-research/references/fact-ledger.md +42 -8
  67. package/dist/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
  68. package/dist/skills/ulw-research/references/markdown-blocks.md +12 -0
  69. package/dist/skills/ulw-research/references/report-gates.md +14 -6
  70. package/dist/skills/ulw-research/references/research-collection.md +195 -0
  71. package/dist/skills/ulw-research/references/research-delivery.md +123 -0
  72. package/dist/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
  73. package/dist/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
  74. package/dist/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
  75. package/dist/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
  76. package/dist/skills/ulw-research/scripts/contracts.mjs +16 -15
  77. package/dist/skills/ulw-research/scripts/contracts.test.ts +6 -109
  78. package/dist/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
  79. package/dist/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
  80. package/dist/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
  81. package/dist/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
  82. package/dist/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
  83. package/dist/skills/ulw-research/scripts/copy-returns-fidelity-policy.test.ts +156 -0
  84. package/dist/skills/ulw-research/scripts/copy-returns-polish.test.ts +10 -9
  85. package/dist/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  86. package/dist/skills/ulw-research/scripts/css-lite.mjs +4 -2
  87. package/dist/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
  88. package/dist/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
  89. package/dist/skills/ulw-research/scripts/entities.mjs +13 -4
  90. package/dist/skills/ulw-research/scripts/fact-commands-fill.mjs +96 -0
  91. package/dist/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
  92. package/dist/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
  93. package/dist/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
  94. package/dist/skills/ulw-research/scripts/fact-commands.mjs +40 -62
  95. package/dist/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
  96. package/dist/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  97. package/dist/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
  98. package/dist/skills/ulw-research/scripts/fact-fill.mjs +9 -6
  99. package/dist/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  100. package/dist/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
  101. package/dist/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
  102. package/dist/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  103. package/dist/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
  104. package/dist/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
  105. package/dist/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
  106. package/dist/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  107. package/dist/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  108. package/dist/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  109. package/dist/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  110. package/dist/skills/ulw-research/scripts/fidelity-policy.mjs +183 -0
  111. package/dist/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  112. package/dist/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  113. package/dist/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  114. package/dist/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  115. package/dist/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  116. package/dist/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  117. package/dist/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
  118. package/dist/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  119. package/dist/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
  120. package/dist/skills/ulw-research/scripts/gates-static.mjs +10 -1
  121. package/dist/skills/ulw-research/scripts/html-lite.mjs +6 -2
  122. package/dist/skills/ulw-research/scripts/html-markup.mjs +2 -2
  123. package/dist/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  124. package/dist/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
  125. package/dist/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  126. package/dist/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
  127. package/dist/skills/ulw-research/scripts/md-lite.mjs +1 -1
  128. package/dist/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  129. package/dist/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
  130. package/dist/skills/ulw-research/scripts/numerals.mjs +18 -7
  131. package/dist/skills/ulw-research/scripts/numerals.test.ts +3 -0
  132. package/dist/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  133. package/dist/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
  134. package/dist/skills/ulw-research/scripts/outcome.mjs +11 -22
  135. package/dist/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
  136. package/dist/skills/ulw-research/scripts/placeholder.mjs +18 -6
  137. package/dist/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  138. package/dist/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
  139. package/dist/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
  140. package/dist/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  141. package/dist/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  142. package/dist/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
  143. package/dist/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
  144. package/dist/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
  145. package/dist/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
  146. package/dist/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
  147. package/dist/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  148. package/dist/skills/ulw-research/scripts/report-tools.mjs +7 -1
  149. package/dist/skills/ulw-research/scripts/report-tools.test.ts +6 -560
  150. package/dist/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
  151. package/dist/skills/ulw-research/scripts/source-commands.mjs +87 -29
  152. package/dist/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
  153. package/dist/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  154. package/dist/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  155. package/dist/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
  156. package/dist/skills/ulw-research/scripts/source-view.mjs +89 -0
  157. package/dist/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  158. package/dist/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
  159. package/dist/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
  160. package/dist/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
  161. package/dist/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
  162. package/dist/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
  163. package/dist/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
  164. package/dist/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
  165. package/dist/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
  166. package/dist/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
  167. package/dist/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
  168. package/dist/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
  169. package/dist/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
  170. package/dist/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
  171. package/dist/skills/ulw-research/scripts/units.mjs +14 -3
  172. package/dist/skills/ulw-research/scripts/units.test.ts +10 -0
  173. package/dist/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
  174. package/dist/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
  175. package/dist/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
  176. package/dist/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
  177. package/dist/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
  178. package/dist/tools/delegate-task/executor-types.d.ts +1 -0
  179. package/dist/tools/delegate-task/sync-session-existence.d.ts +4 -0
  180. package/dist/tools/delegate-task/types.d.ts +1 -0
  181. package/dist/tools/skill/description-formatter.d.ts +2 -1
  182. package/dist/tools/skill/types.d.ts +6 -0
  183. package/dist/tui.js +131 -33
  184. package/package.json +18 -15
  185. package/packages/git-bash-mcp/package.json +1 -1
  186. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  187. package/packages/omo-codex/plugin/README.md +1 -1
  188. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  189. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  190. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  191. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  192. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  193. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  194. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  195. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  196. package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
  197. package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  198. package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/SKILL.md +7 -7
  199. package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/agents/openai.yaml +1 -1
  200. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  201. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  202. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  203. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  204. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  205. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  206. package/packages/omo-codex/plugin/components/teammode/package.json +2 -2
  207. package/packages/omo-codex/plugin/components/telemetry/README.md +2 -2
  208. package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +52 -15
  209. package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +52 -19
  210. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  211. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  212. package/packages/omo-codex/plugin/components/telemetry/src/codex-hook.ts +2 -9
  213. package/packages/omo-codex/plugin/components/telemetry/src/posthog.ts +4 -13
  214. package/packages/omo-codex/plugin/components/telemetry/src/product-identity.ts +0 -2
  215. package/packages/omo-codex/plugin/components/telemetry/test/codex-hook-diagnostics.test.ts +0 -3
  216. package/packages/omo-codex/plugin/components/telemetry/test/codex-hook.test.ts +3 -8
  217. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +3 -0
  218. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  219. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  220. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  221. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  222. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  223. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +3 -0
  224. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  225. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  226. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  227. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  228. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  229. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  230. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  231. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  232. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  233. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  234. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  235. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  236. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  237. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  238. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  239. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  240. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  241. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  242. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  243. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  244. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  245. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  246. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  247. package/packages/omo-codex/plugin/package-lock.json +16 -16
  248. package/packages/omo-codex/plugin/package.json +1 -1
  249. package/packages/omo-codex/plugin/shared/package.json +1 -1
  250. package/packages/omo-codex/plugin/skills/ast-grep/README.md +1 -1
  251. package/packages/omo-codex/plugin/skills/ast-grep/install.ps1 +5 -3
  252. package/packages/omo-codex/plugin/skills/ast-grep/install.sh +4 -5
  253. package/packages/omo-codex/plugin/skills/ast-grep/references/install.md +2 -2
  254. package/packages/omo-codex/plugin/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
  255. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  256. package/packages/omo-codex/plugin/skills/coding-agent-sessions/AGENTS.md +5 -4
  257. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +11 -11
  258. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +2 -2
  259. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/codex.md +4 -4
  260. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
  261. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
  262. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +1 -1
  263. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +34 -12
  264. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +2 -2
  265. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +2 -1
  266. package/packages/omo-codex/plugin/skills/frontend/references/design/component-catalogs.md +14 -10
  267. package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +19 -6
  268. package/packages/omo-codex/plugin/skills/frontend/references/design/grounding.md +29 -0
  269. package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
  270. package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
  271. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
  272. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  273. package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +7 -7
  274. package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
  275. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +5 -4
  276. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -2
  277. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/__main__.py +9 -6
  278. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/summary.py +1 -1
  279. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  280. package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
  281. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +3 -0
  282. package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +8 -4
  283. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +7 -259
  284. package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +68 -24
  285. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
  286. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +42 -8
  287. package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
  288. package/packages/omo-codex/plugin/skills/ulw-research/references/markdown-blocks.md +12 -0
  289. package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +14 -6
  290. package/packages/omo-codex/plugin/skills/ulw-research/references/research-collection.md +195 -0
  291. package/packages/omo-codex/plugin/skills/ulw-research/references/research-delivery.md +123 -0
  292. package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
  293. package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +16 -15
  294. package/packages/omo-codex/plugin/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
  295. package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  296. package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-lite.mjs +4 -2
  297. package/packages/omo-codex/plugin/skills/ulw-research/scripts/entities.mjs +13 -4
  298. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-fill.mjs +96 -0
  299. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
  300. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
  301. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
  302. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +40 -62
  303. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  304. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +9 -6
  305. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  306. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  307. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  308. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  309. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  310. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  311. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-policy.mjs +183 -0
  312. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  313. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  314. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  315. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  316. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  317. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  318. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  319. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +10 -1
  320. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +6 -2
  321. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +2 -2
  322. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  323. package/packages/omo-codex/plugin/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  324. package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +1 -1
  325. package/packages/omo-codex/plugin/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  326. package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +18 -7
  327. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  328. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +11 -22
  329. package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +18 -6
  330. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  331. package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  332. package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  333. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  334. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +7 -1
  335. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +87 -29
  336. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  337. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  338. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-view.mjs +89 -0
  339. package/packages/omo-codex/plugin/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  340. package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +14 -3
  341. package/packages/omo-codex/plugin/test/payload-equivalence.test.mjs +6 -4
  342. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -3
  343. package/packages/omo-codex/scripts/install-dist/install-local.mjs +338 -237
  344. package/packages/prompts-core/prompts/ultrawork/codex.md +3 -0
  345. package/packages/shared-skills/skill-source-filter.mjs +6 -2
  346. package/packages/shared-skills/skills/ast-grep/README.md +1 -1
  347. package/packages/shared-skills/skills/ast-grep/install.ps1 +5 -3
  348. package/packages/shared-skills/skills/ast-grep/install.sh +4 -5
  349. package/packages/shared-skills/skills/ast-grep/references/install.md +2 -2
  350. package/packages/shared-skills/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
  351. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  352. package/packages/shared-skills/skills/coding-agent-sessions/AGENTS.md +5 -4
  353. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +11 -11
  354. package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +2 -2
  355. package/packages/shared-skills/skills/coding-agent-sessions/references/codex.md +4 -4
  356. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
  357. package/packages/shared-skills/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
  358. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +1 -1
  359. package/packages/shared-skills/skills/frontend/SKILL.md +34 -12
  360. package/packages/shared-skills/skills/frontend/references/design/README.md +2 -2
  361. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +2 -1
  362. package/packages/shared-skills/skills/frontend/references/design/component-catalogs.md +14 -10
  363. package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +19 -6
  364. package/packages/shared-skills/skills/frontend/references/design/grounding.md +29 -0
  365. package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
  366. package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
  367. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +5 -4
  368. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -2
  369. package/packages/shared-skills/skills/ultimate-browsing/engine/__main__.py +9 -6
  370. package/packages/shared-skills/skills/ultimate-browsing/engine/summary.py +1 -1
  371. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  372. package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
  373. package/packages/shared-skills/skills/ulw-research/AGENTS.md +8 -4
  374. package/packages/shared-skills/skills/ulw-research/SKILL.md +7 -259
  375. package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +68 -24
  376. package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
  377. package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +42 -8
  378. package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
  379. package/packages/shared-skills/skills/ulw-research/references/markdown-blocks.md +12 -0
  380. package/packages/shared-skills/skills/ulw-research/references/report-gates.md +14 -6
  381. package/packages/shared-skills/skills/ulw-research/references/research-collection.md +195 -0
  382. package/packages/shared-skills/skills/ulw-research/references/research-delivery.md +123 -0
  383. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
  384. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
  385. package/packages/shared-skills/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
  386. package/packages/shared-skills/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
  387. package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +16 -15
  388. package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +6 -109
  389. package/packages/shared-skills/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
  390. package/packages/shared-skills/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
  391. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
  392. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
  393. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
  394. package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-fidelity-policy.test.ts +156 -0
  395. package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-polish.test.ts +10 -9
  396. package/packages/shared-skills/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  397. package/packages/shared-skills/skills/ulw-research/scripts/css-lite.mjs +4 -2
  398. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
  399. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
  400. package/packages/shared-skills/skills/ulw-research/scripts/entities.mjs +13 -4
  401. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-fill.mjs +96 -0
  402. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
  403. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
  404. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
  405. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +40 -62
  406. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
  407. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  408. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
  409. package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +9 -6
  410. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  411. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
  412. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
  413. package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  414. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
  415. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
  416. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
  417. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  418. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  419. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  420. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  421. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-policy.mjs +183 -0
  422. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  423. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  424. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  425. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  426. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  427. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  428. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
  429. package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  430. package/packages/shared-skills/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
  431. package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +10 -1
  432. package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +6 -2
  433. package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +2 -2
  434. package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  435. package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
  436. package/packages/shared-skills/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  437. package/packages/shared-skills/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
  438. package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +1 -1
  439. package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  440. package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
  441. package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +18 -7
  442. package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +3 -0
  443. package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  444. package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
  445. package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +11 -22
  446. package/packages/shared-skills/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
  447. package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +18 -6
  448. package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  449. package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
  450. package/packages/shared-skills/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
  451. package/packages/shared-skills/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  452. package/packages/shared-skills/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  453. package/packages/shared-skills/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
  454. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
  455. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
  456. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
  457. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
  458. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  459. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +7 -1
  460. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +6 -560
  461. package/packages/shared-skills/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
  462. package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +87 -29
  463. package/packages/shared-skills/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
  464. package/packages/shared-skills/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  465. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  466. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
  467. package/packages/shared-skills/skills/ulw-research/scripts/source-view.mjs +89 -0
  468. package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  469. package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
  470. package/packages/shared-skills/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
  471. package/packages/shared-skills/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
  472. package/packages/shared-skills/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
  473. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
  474. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
  475. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
  476. package/packages/shared-skills/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
  477. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
  478. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
  479. package/packages/shared-skills/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
  480. package/packages/shared-skills/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
  481. package/packages/shared-skills/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
  482. package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +14 -3
  483. package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +10 -0
  484. package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
  485. package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
  486. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
  487. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
  488. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
  489. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/amp_redirect_stub.html +0 -7
  490. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/search_interstitial.html +0 -19
  491. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_available.json +0 -1
  492. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_snapshot.html +0 -1128
  493. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_fetch_chain.py +0 -77
  494. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +0 -86
  495. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +0 -279
  496. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate.py +0 -252
  497. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate_validators.py +0 -78
@@ -9,8 +9,10 @@ import { finalReading, rewriteParticle } from "./particles-ko.mjs"
9
9
  import { failureCount, markReplaced, sentenceKey } from "./fidelity-state.mjs"
10
10
  import { findPlaceholders, lead, qualifiedText, renderBlockquote, renderQuote, renderValueHtml, subSpan, valueParts } from "./placeholder.mjs"
11
11
  import { splitSentences } from "./sentence-split.mjs"
12
+ import { CLAIM_KINDS } from "./claim-kinds.mjs"
12
13
 
13
- const FORMATS = Object.freeze(["html", "md"])
14
+ /** The output formats fill renders. */
15
+ export const FILL_FORMATS = Object.freeze(["html", "md"])
14
16
  const TRANSLATION = Object.freeze({
15
17
  html: /^\s*<span data-translates="(Q\d+)">[\s\S]*?<\/span>/u,
16
18
  md: /^\s*<!-- translates: (Q\d+) -->[^\n]*/u,
@@ -23,7 +25,7 @@ function resolveValue(row, parsed, format) {
23
25
  const { valueText, unitText } = valueParts(row)
24
26
  const text = { full: row.raw, value: valueText, unit: unitText, q: qualifiedText(row) }[parsed.mode]
25
27
  if (text === null) return { code: "quote_anchor_unresolved", message: `${row.fact_id} has qualifier ${row.qualifier}, but its quote holds no qualifier beside "${row.raw}"` }
26
- return { row, parsed, visible: text, rendered: format === "html" ? renderValueHtml(row, text) : text }
28
+ return { row, parsed, visible: text, rendered: format === "html" ? renderValueHtml(row, text, { mode: parsed.mode, claimKind: parsed.claimKind ?? null }) : text }
27
29
  }
28
30
 
29
31
  function resolveQuote(row, parsed, format) {
@@ -32,17 +34,18 @@ function resolveQuote(row, parsed, format) {
32
34
  if (selection === null) {
33
35
  return { code: "quote_anchor_unresolved", message: `from "${parsed.from}" and to "${parsed.to}" must each occur exactly once in ${row.fact_id}'s quote, in that order` }
34
36
  }
35
- const rendered = renderQuote(row, parsed.selector, selection.text, format)
37
+ const rendered = renderQuote(row, parsed.selector, selection.text, format, parsed.claimKind)
36
38
  return { row, parsed, visible: `[${row.source_id}]`, rendered, selector: parsed.selector, quote_span: [selection.start, selection.end] }
37
39
  }
38
40
 
39
41
  function resolve(found, rows, format) {
40
42
  const { parsed } = found
41
- if (parsed.error) return { code: "unresolved_placeholder", message: parsed.error }
43
+ if (parsed.error) return { code: "unresolved_placeholder", message: `${parsed.error}; for literal opening braces, write &#123;&#123; instead of {{ (including in code spans)` }
42
44
  const row = rows.get(parsed.id)
43
45
  if (row === undefined) return { code: "unresolved_placeholder", message: `${parsed.id} is not in the fact ledger` }
44
46
  if (row.status !== "active") return { code: "unresolved_placeholder", message: `${parsed.id} is ${row.status}; cite its replacement` }
45
47
  if (row.label !== parsed.label) return { code: "pointer_label_mismatch", message: `${parsed.id} has label "${row.label}", not "${parsed.label}"` }
48
+ if (parsed.claimKind && !CLAIM_KINDS[parsed.claimKind].rowKinds.includes(row.kind)) return { code: CLAIM_KINDS[parsed.claimKind].defect, message: `${parsed.claimKind} does not accept a ${row.kind} row (${parsed.id})` }
46
49
  return parsed.kind === "Q" ? resolveQuote(row, parsed, format) : resolveValue(row, parsed, format)
47
50
  }
48
51
 
@@ -145,7 +148,7 @@ function assemble(text, parts, context) {
145
148
  flush(part.start)
146
149
  const start = context.out.length
147
150
  if (part.parsed) {
148
- const extra = part.selector === undefined ? {} : { selector: part.selector, quote_span: part.quote_span }
151
+ const extra = { ...(part.selector === undefined ? {} : { selector: part.selector, quote_span: part.quote_span }), ...(part.parsed.claimKind ? { claimKind: part.parsed.claimKind } : {}), ...(part.parsed.mode === "value" ? { mode: "value" } : {}) }
149
152
  context.fillMap.push({ token: part.token, fact_id: part.row.fact_id, start, end: start + part.rendered.length, rendered: part.rendered, ...extra })
150
153
  } else if (part.rendered !== "") {
151
154
  context.fillMap.push({ token: null, fact_id: part.source.fact_id, replaced: part.key, start, end: start + part.rendered.length, rendered: part.rendered })
@@ -166,7 +169,7 @@ function assemble(text, parts, context) {
166
169
  */
167
170
  export function fill(sourceText, ledger, options = {}) {
168
171
  const format = options.format ?? "html"
169
- if (!FORMATS.includes(format)) throw new TypeError(`format must be one of ${FORMATS.join(", ")}`)
172
+ if (!FILL_FORMATS.includes(format)) throw new TypeError(`format must be one of ${FILL_FORMATS.join(", ")}`)
170
173
  const rows = new Map(ledger.rows.map((row) => [row.fact_id, row]))
171
174
  const found = findPlaceholders(sourceText)
172
175
  const errors = []
@@ -3,7 +3,6 @@
3
3
  // per-kind choices (id prefix, measured exact) from it, and the per-kind table in
4
4
  // references/fact-ledger-grammar.md is rendered from it (`node fact-ledger-kinds.mjs --render-grammar`).
5
5
  // Also the value primitives the schema shares. Pure functions with no IO; node: builtins only.
6
-
7
6
  import { isCliEntry } from "./entry-guard.mjs"
8
7
  import { canonicalUnit, inputScale, RuleError } from "./unit-algebra.mjs"
9
8
  import { metadataErrors } from "./unit-metadata.mjs"
@@ -15,7 +14,6 @@ export const DECIMAL = /^-?\d+(?:\.\d+)?$/
15
14
  const INTEGER = /^-?\d+$/
16
15
  const SHA256 = /^[0-9a-f]{64}$/
17
16
  const SOURCE_ID = /^S\d+$/
18
-
19
17
  export const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value)
20
18
  export const isString = (value) => typeof value === "string"
21
19
  export const isNonEmpty = (value) => typeof value === "string" && value.length > 0
@@ -48,6 +46,7 @@ export function locatorErrors(locator, at) {
48
46
  const errors = []
49
47
  if (!isObject(locator)) return errors
50
48
  if (!isNonEmpty(locator.artifact)) errors.push(`${at}.artifact must be a non-empty string`)
49
+ if (locator.unit !== undefined && locator.unit !== "bytes") errors.push(`${at}.unit must be bytes`)
51
50
  if (!(isCount(locator.char_start) && isCount(locator.char_end) && locator.char_start <= locator.char_end)) errors.push(`${at}.char_start/char_end must be integers with start <= end`)
52
51
  for (const key of ["page", "line"]) {
53
52
  if (locator[key] !== undefined && !(Number.isInteger(locator[key]) && locator[key] >= 1)) errors.push(`${at}.${key} must be an integer >= 1`)
@@ -87,6 +86,7 @@ export const RULES = Object.freeze({
87
86
  rational_or_null: { cell: "rational or null", says: "null or { num: integer string, den: positive integer string }", test: orNull(isRational) },
88
87
  by_derive: { cell: "reduced rational", says: "a reduced { num: integer string, den: positive integer string }", test: isRational },
89
88
  context_by_derive: { cell: "written by derive (object or null)", says: "null or an object", test: orNull(isObject) },
89
+ source_context: { cell: "quote context or null (legacy)", says: "null or { prefix, suffix } up to 32 characters each", test: orNull((v) => isObject(v) && Object.keys(v).sort().join(",") === "prefix,suffix" && [v.prefix, v.suffix].every((s) => isString(s) && [...s].length <= 32)) },
90
90
  derived_operation: { cell: "sum/diff/growth/share/ratio/expr", says: "a derived operation", test: (v) => Object.hasOwn(OPERATION_RULES, v) && OPERATION_RULES[v].kind === "derived" },
91
91
  convert_operation: { cell: "`convert`", says: "convert", test: (v) => Object.hasOwn(OPERATION_RULES, v) && OPERATION_RULES[v].kind === "converted" },
92
92
  precision: { cell: "`{ places, sigfigs }`", says: "nonnegative places and positive sigfigs", test: (v) => isObject(v) && Object.keys(v).sort().join(",") === "places,sigfigs" && Number.isSafeInteger(v.places) && v.places >= 0 && Number.isSafeInteger(v.sigfigs) && v.sigfigs >= 1 },
@@ -101,14 +101,13 @@ const unsourced = { source_id: "source_or_null", locator: "locator_or_null", sna
101
101
  const row = (cells) => Object.freeze(Object.fromEntries(KIND_COLUMNS.map((column) => [column, cells[column] ?? (["precision", "compatibility"].includes(column) && cells.operation !== "null" ? column : "null")])))
102
102
 
103
103
  export const KIND_RULES = Object.freeze({
104
- measured: row({ fact_id: "f_id", value: "decimal", ...sourced, rationale: "string_or_null", exact: "from_value", context: "null", operation: "null" }),
105
- quote: row({ fact_id: "q_id", value: "null", ...sourced, rationale: "string_or_null", exact: "null", context: "null", operation: "null" }),
104
+ measured: row({ fact_id: "f_id", value: "decimal", ...sourced, rationale: "string_or_null", exact: "from_value", context: "source_context", operation: "null" }),
105
+ quote: row({ fact_id: "q_id", value: "null", ...sourced, rationale: "string_or_null", exact: "null", context: "source_context", operation: "null" }),
106
106
  derived: row({ fact_id: "f_id", value: "decimal", ...unsourced, rationale: "string_or_null", exact: "by_derive", context: "context_by_derive", operation: "derived_operation" }),
107
107
  assumed: row({ fact_id: "f_id", value: "decimal", ...unsourced, rationale: "non_empty", exact: "rational_or_null", context: "null", operation: "null" }),
108
108
  converted: row({ fact_id: "f_id", value: "decimal", ...unsourced, rationale: "string_or_null", exact: "by_derive", context: "context_by_derive", operation: "convert_operation" }),
109
109
  })
110
110
 
111
- /** The id prefix nextId assigns for a kind; an unknown kind gets F and fails validation later. */
112
111
  export function idPrefix(kind) {
113
112
  return RULES[KIND_RULES[kind]?.fact_id ?? "f_id"].prefix
114
113
  }
@@ -169,7 +168,7 @@ const carriesDerivation = (row) => row.operation !== null || row.operands?.lengt
169
168
  /** Dimension 32: a stored exact must equal the row's own value at the value's decimal places. */
170
169
  function valueExactDisagreement(row) {
171
170
  const exact = readExact(row.exact)
172
- const value = readDecimal(row.value)
171
+ const value = isString(row.value) ? readDecimal(row.value).replace(/^-(0+(?:\.0+)?)$/, "$1") : row.value
173
172
  if (!isRational(exact) || !isDecimal(value) || roundPlaces(toBig(exact), placesOf(value)) === value) return null
174
173
  return `value ${value} disagrees with exact ${exact.num}/${exact.den} at ${placesOf(value)} decimal places`
175
174
  }
@@ -212,7 +211,7 @@ export function kindRuleErrors(candidate, at) {
212
211
  const value = candidate[column] === undefined && LATER_COLUMNS.includes(column) ? null : candidate[column]
213
212
  const rule = RULES[legacy && column === "exact" ? "rational_or_null" : rules[column]]
214
213
  // An exact with unknown keys loads as written (never stripped); readFlaws refuses it at use and at write.
215
- if (column === "exact" && hasUnknownExactKeys(value) && !isCurrentDerived(candidate)) continue
214
+ if (column === "exact" && rules[column] !== "null" && hasUnknownExactKeys(value) && !isCurrentDerived(candidate)) continue
216
215
  if (rule.test(value, candidate)) continue
217
216
  errors.push(`${at}.${column} must be ${rule.says} for a ${candidate.kind} row`)
218
217
  if (rule.detail) errors.push(...rule.detail(value, `${at}.${column}`))
@@ -1,10 +1,12 @@
1
- // fact-locate.mjs - deterministic fact locators over a source snapshot.
1
+ // fact-locate.mjs - deterministic fact locators over a source snapshot's locator text.
2
2
  //
3
3
  // Each locator returns { value, unit, raw, quote, value_span, sentence_id, qualifier, locator } or
4
4
  // { error: "fact_not_located" | "fact_ambiguous", candidates }; it never guesses. `quote` is the
5
- // whole containing sentence, cell, or line of text.txt, `value_span` is [start, end] inside
6
- // `quote` with quote.slice(...value_span) === raw, and locator.char_start/char_end place `quote`
7
- // in text.txt. Every call takes `sourceId` (S<n>) so sentence ids match sentences.jsonl.
5
+ // whole containing sentence, cell, or line of the view text (source-view.mjs: the visible text
6
+ // nodes of a page, never markup), `value_span` is [start, end] inside `quote` with
7
+ // quote.slice(...value_span) === raw, and locator.char_start/char_end place `quote` in that text;
8
+ // raw-locator.mjs turns them into bytes. Every call takes `sourceId` (S<n>) so sentence ids match
9
+ // sentences.jsonl, which indexes the same text.
8
10
  // Plain Node ESM, sibling imports only; no network, no OCR.
9
11
 
10
12
  import { proseText } from "./html-lite.mjs"
@@ -101,7 +103,8 @@ export function locateFind(text, { find, unitExpected, sourceId }) {
101
103
  return result.unit === expected ? result : { error: "unit_expected_mismatch", expected, found: result.unit, candidates: [result] }
102
104
  }
103
105
 
104
- function cellsOf(row) {
106
+ /** A row's cells by visual column (a colspan cell repeats). */
107
+ export function cellsOf(row) {
105
108
  const cells = []
106
109
  for (const cell of row.children.filter((child) => child.tag === "td" || child.tag === "th")) {
107
110
  const span = Math.max(1, Number.parseInt(cell.attrs.colspan ?? "1", 10) || 1)
@@ -110,7 +113,7 @@ function cellsOf(row) {
110
113
  return cells
111
114
  }
112
115
 
113
- function rowsOf(table) {
116
+ export function rowsOf(table) {
114
117
  const rows = []
115
118
  const walk = (node) => {
116
119
  for (const child of node.children) {
@@ -142,10 +145,13 @@ function tablesOf(root) {
142
145
  const cellLabel = (cell) => label(proseText(cell))
143
146
  const indexesOf = (labels, wanted) => labels.flatMap((value, index) => (value === wanted ? [index] : []))
144
147
 
145
- /** --table/--row/--col: the table by id, caption, or a header label; row and column by normalized label, never by index. */
146
- export function locateTable(sourceHtml, { table, row, col, sourceId }) {
148
+ /**
149
+ * --table/--row/--col: the table by id, caption, or a header label; row and column by normalized label,
150
+ * never by index. `source` is page html or an extracted view ({ root, text, blocks }) of one.
151
+ */
152
+ export function locateTable(source, { table, row, col, sourceId }) {
147
153
  requireSource(sourceId)
148
- const { root, text, blocks } = snapshotBlocks(sourceHtml)
154
+ const { root, text, blocks } = typeof source === "string" ? snapshotBlocks(source) : source
149
155
  const key = label(table)
150
156
  const tables = tablesOf(root).filter((entry) => entry.table.attrs.id === table || entry.caption === key || (entry.header && cellsOf(entry.header).map(cellLabel).includes(key)))
151
157
  if (tables.length !== 1) return tables.length === 0 ? notLocated() : ambiguous(tables.map((entry) => ({ table: entry.table.attrs.id ?? entry.caption })))
@@ -14,6 +14,7 @@
14
14
  import { CLAIM_ATTRIBUTE, CLAIM_KINDS, claimSatisfied, unmarkedKinds, unmarkedRequires } from "./claim-kinds.mjs"
15
15
  import { proseSentences } from "./fidelity-binding.mjs"
16
16
  import { clip } from "./fidelity-trace.mjs"
17
+ import { isClaimSegment } from "./sources-citation.mjs"
17
18
  import { proseText } from "./html-lite.mjs"
18
19
 
19
20
  const marked = (node) => node.attrs?.[CLAIM_ATTRIBUTE] !== undefined
@@ -63,7 +64,7 @@ export function claimChecks(ctx, rows, provenanceOf, flag) {
63
64
  const id = fact.attrs["data-fact"]
64
65
  if (!nearest) {
65
66
  const need = unmarkedRequires(rows.get(id))
66
- const mark = need && `{{${CLAIM_KINDS[need].prefix}:${id}}} (${CLAIM_ATTRIBUTE}="${need}")`
67
+ const mark = need && `{{${CLAIM_KINDS[need].prefix}:${id}:${rows.get(id).label}}} (${CLAIM_ATTRIBUTE}="${need}")`
67
68
  if (need) flag("claim_kind_mismatch", fact, `${id} is a ${rows.get(id).kind} row cited with no mark; an unmarked numeral reads as a fact, so mark it ${mark}`)
68
69
  continue
69
70
  }
@@ -77,8 +78,8 @@ export function claimChecks(ctx, rows, provenanceOf, flag) {
77
78
  flag(entry.defect, fact, `${row.fact_id} is marked ${name}, but it is a ${row.kind} row${source}`)
78
79
  }
79
80
  for (const block of proseSentences(ctx)) {
80
- if (block.kind === "sources" || block.kind === "toc") continue
81
- for (const sentence of block.sentences) {
81
+ if (block.kind === "toc") continue
82
+ for (const sentence of block.kind === "sources" ? block.sentences.filter((s) => isClaimSegment(s.text)) : block.sentences) {
82
83
  for (const name of unmarkedKinds(sentence.text)) {
83
84
  const covered = sentence.facts.length > 0 ? sentence.facts.every((fact) => nearestKind(fact.node) === name) : nearestKind(block.owner) === name
84
85
  if (covered) continue
@@ -0,0 +1,32 @@
1
+ // Read fact export CSV records, including quoted commas, escaped quotes and newlines.
2
+ import { existsSync, readFileSync } from "node:fs"
3
+
4
+ export function readCsv(path) {
5
+ if (!existsSync(path)) return null
6
+ const text = readFileSync(path, "utf8")
7
+ const records = []
8
+ let cells = []
9
+ let cell = ""
10
+ let quoted = false
11
+ for (let i = 0; i <= text.length; i++) {
12
+ const char = text[i]
13
+ if (char === '"') {
14
+ if (quoted && text[i + 1] === '"') {
15
+ cell += '"'
16
+ i++
17
+ } else quoted = !quoted
18
+ } else if (!quoted && (char === "," || char === "\n" || char === undefined)) {
19
+ cells.push(cell.trim())
20
+ cell = ""
21
+ if (char !== ",") {
22
+ if (cells.some((value) => value !== "")) records.push(cells)
23
+ cells = []
24
+ }
25
+ } else if (char !== undefined) cell += char
26
+ }
27
+ if (quoted) return null
28
+ const [head = [], ...rows] = records
29
+ const [id, value, unit] = ["fact_id", "plot_value", "unit"].map((name) => head.indexOf(name))
30
+ if (id < 0 || value < 0 || unit < 0 || rows.length === 0) return null
31
+ return rows.map((c) => ({ fact_id: c[id], plot_value: c[value], unit: c[unit] ?? "" }))
32
+ }
@@ -4,7 +4,7 @@
4
4
  // causality_added, qualifier_dropped (six-token window around the fact span; a :q rendering puts
5
5
  // the qualifier inside the span), scope_widened_warn, and the acronym-aware name_outside_quote.
6
6
  // The hedge-position heuristic never yields a code; it only sets force_strong for the meaning
7
- // verifier, as do name_outside_quote and scope_widened_warn. Latin terms match whole words (terms
7
+ // verifier; name_outside_quote and scope_widened_warn also emit minor defects. Latin terms match whole words (terms
8
8
  // of four or more letters also match their inflections); Korean terms match as substrings, single
9
9
  // syllables as tokens. No model, no IO. Plain Node ESM; Korean strings are \u escapes.
10
10
 
@@ -0,0 +1,161 @@
1
+ // Check displayed conversion originals against their operands, not the mutable render field.
2
+ import { sameValue, unitKey } from "./fidelity-binding.mjs"
3
+ import { proseText } from "./html-lite.mjs"
4
+ import { parseNumeral } from "./numerals.mjs"
5
+ import { normalizeText } from "./text-normalize.mjs"
6
+ import { display } from "./unit-precision.mjs"
7
+ import { dimensionOf } from "./units.mjs"
8
+
9
+ const DERIVED = new Set(["converted", "derived"])
10
+ /**
11
+ * The one bracket grammar of the skill. After NFKC (fullwidth parentheses and square brackets fold
12
+ * to ASCII), an original is a () [] 【】 「」 or 〔〕 group. Inside a derived or converted element every
13
+ * group is an original, never prose; a dash separator is not an original.
14
+ */
15
+ const PAIRS = [["(", ")"], ["[", "]"], ["\u3010", "\u3011"], ["\u300C", "\u300D"], ["\u3014", "\u3015"]]
16
+ const [OPEN, CLOSE] = [PAIRS.map((pair) => pair[0]).join(""), PAIRS.map((pair) => pair[1]).join("")]
17
+ const inClass = (chars) => chars.replace(/[\\\]\[^-]/g, "\\$&")
18
+ const literal = (chars) => chars.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
19
+ const ANY = inClass(OPEN + CLOSE)
20
+ const GROUP = new RegExp(PAIRS.map(([open, close]) => `${literal(open)}([^${ANY}]*)${literal(close)}`).join("|"), "gu")
21
+ const BRACKET = new RegExp(`[${ANY}]`, "u")
22
+ const folded = (text) => String(text).normalize("NFKC")
23
+ const innerOf = (match) => match.slice(1).find((part) => part !== undefined)
24
+ const mismatch = (message) => ["value_mismatch", message, "original"]
25
+
26
+ const halfFits = (row, half) => {
27
+ const expected = normalizeText(folded(display(String(row.value), row.unit ?? null)))
28
+ const text = normalizeText(half)
29
+ return text === expected || (text.endsWith(` ${expected}`) && !/\d/.test(text.slice(0, -expected.length)))
30
+ }
31
+
32
+ /** The converted half (the text outside the originals) equals display(row.value, row.unit), rebuilt from the row. */
33
+ export function convertedHalfFits(row, shown) {
34
+ if (!DERIVED.has(row.kind) || row.value === null || row.value === undefined) return false
35
+ return halfFits(row, folded(shown).replace(GROUP, ""))
36
+ }
37
+
38
+ /** A second number outside the value verify reads and its originals ("320,000 mm - 999 m"). */
39
+ function strayNumber(row, half) {
40
+ if (row.value === null || row.value === undefined || halfFits(row, half)) return null
41
+ const read = parseNumeral(half)
42
+ if (!read || !sameValue(read.value, row.value)) return null
43
+ const rest = half.slice(0, read.span[0]) + half.slice(read.span[1])
44
+ return /\d/.test(row.unit ? rest.split(row.unit).join("") : rest) ? mismatch(`"${half.trim()}" carries a number besides the value and its originals`) : null
45
+ }
46
+
47
+ /** `valueMode`: the element shows the bare value ({{F:x:value}}), so no original is due. */
48
+ export function originalProblem(row, shown, rows, valueMode = false) {
49
+ if (!DERIVED.has(row.kind)) return null
50
+ const text = folded(shown)
51
+ const groups = [...text.matchAll(GROUP)]
52
+ const half = text.replace(GROUP, "")
53
+ if (BRACKET.test(half)) return mismatch("original brackets are malformed or nested")
54
+ const operands = (row.derived_from ?? []).map((id) => rows.get(id))
55
+ for (const [index, match] of groups.entries()) {
56
+ const [inner, operand] = [innerOf(match), operands[index]]
57
+ const parsed = parseNumeral(inner)
58
+ if (!operand) return mismatch(`original "${inner}" has no operand of ${row.fact_id}`)
59
+ if (!parsed || !sameValue(parsed.value, operand.value)) return mismatch(`original "${inner}" differs from operand ${operand.fact_id}`)
60
+ if (unitKey(parsed.unitRaw ?? "") !== unitKey(operand.unit ?? "")) return ["unit_mismatch", `original "${inner}" is not in ${operand.unit}`, "original"]
61
+ const expected = normalizeText(folded(display(operand.value, operand.unit ?? null)))
62
+ if (normalizeText(inner) !== expected) return mismatch(`original "${inner}" differs from "${expected}"`)
63
+ }
64
+ if (groups.length > 0 && !/\d/.test(text.slice(0, groups[0].index))) return mismatch(`original "${innerOf(groups[0])}" precedes the value`)
65
+ if (row.kind === "converted" && !valueMode && /\d/.test(half) && groups.length < operands.length) return mismatch(`the original of ${row.derived_from[groups.length]} is missing after the value`)
66
+ return strayNumber(row, half)
67
+ }
68
+
69
+ /** Stands for a filled element or another fill-map value in the text after a fill site. */
70
+ const FILLED = "\uFFFC"
71
+ /** After a fill site the grammar also reads the brackets an element never carries as originals. */
72
+ const FOLLOW = [...PAIRS, ["\u300E", "\u300F"], ["\u300A", "\u300B"], ["\u3008", "\u3009"], ["\u27E8", "\u27E9"]]
73
+ const dimension = (unit) => dimensionOf(String(unit).replace(/-e\d+$/u, "")) ?? unitKey(unit)
74
+
75
+ /** What the text after a fill site opens: a bracket group's content, or the clause after a separator (`open`); else null. */
76
+ function openedGroup(text) {
77
+ const rest = folded(text).trimStart()
78
+ const pair = FOLLOW.find(([open]) => rest.startsWith(open))
79
+ const close = pair ? rest.indexOf(pair[1], 1) : -1
80
+ if (pair) return { group: rest.slice(1, close === -1 ? undefined : close).trim(), open: false }
81
+ return /^[-\u2013\u2014/]/u.test(rest) ? { group: rest.slice(1).split(/[,;:!?)\]]|\.(?!\d)/u)[0].trim(), open: true } : null
82
+ }
83
+
84
+ /**
85
+ * Whether the text right after a fill site of `row` opens another original: a bracket group or a
86
+ * separator clause that is exactly a numeral with a unit of the row's or an operand's dimension (a
87
+ * separator clause may end in a glued particle, as Korean writes "- 999 m\uC774\uB2E4").
88
+ * A filled element, a fill-map value (`fills`), a placeholder, a unitless number (a year, a
89
+ * footnote) and prose with digits are not originals; untraceable_number still reads a stray number.
90
+ */
91
+ export const followsOriginal = (row, rows, fills = []) => (text) => {
92
+ if (!DERIVED.has(row?.kind)) return false
93
+ const after = [...fills].filter(Boolean).sort((a, b) => b.length - a.length).reduce((cur, fill) => cur.split(fill).join(FILLED), String(text))
94
+ const { group, open } = openedGroup(after) ?? { group: "" }
95
+ if (!group || group.includes(FILLED) || group.includes("{{")) return false
96
+ const units = [row.unit, ...(row.derived_from ?? []).map((id) => rows.get(id)?.unit)].filter(Boolean)
97
+ return [group, ...(open ? [group.replace(/[\uAC00-\uD7A3]+$/u, "").trim()] : [])].some((body) => {
98
+ const read = parseNumeral(body, 0)
99
+ return Boolean(read?.unitRaw) && read.span[0] === 0 && read.span[1] === body.length && units.some((unit) => dimension(unit) === dimension(read.unitRaw))
100
+ })
101
+ }
102
+
103
+ /** The prose after `node` in its parent, each filled element (data-fact) read as one FILLED mark. */
104
+ export const textAfter = (node) => {
105
+ const siblings = node.parent?.children.slice(node.parent.children.indexOf(node) + 1) ?? []
106
+ return proseText({ children: siblings.map((child) => (child.attrs?.["data-fact"] === undefined ? child : { tag: "#text", text: FILLED })) })
107
+ }
108
+
109
+ const SPACE = "[\\s\\u3000]"
110
+ const DIGIT = "[0-9\\uFF10-\\uFF19]"
111
+ /** Each character with the fullwidth form NFKC folds onto it. */
112
+ const wide = (chars) => [...chars].map((ch) => ch + (/[!-~]/.test(ch) ? String.fromCharCode(ch.charCodeAt(0) + 0xfee0) : "")).join("")
113
+ const [WIDE_OPEN, WIDE_CLOSE] = [inClass(wide(OPEN)), inClass(wide(CLOSE))]
114
+
115
+ /** A pattern reading `rendered` modulo the bracket grammar: NFKC fullwidth forms, bracket style, spacing around brackets. */
116
+ function renderPattern(rendered) {
117
+ const text = folded(rendered).trim()
118
+ let body = ""
119
+ for (let at = 0; at < text.length; at += 1) {
120
+ const ch = text[at]
121
+ const beside = (OPEN + CLOSE).includes(text.slice(at).trimStart()[0]) || OPEN.includes(text[at - 1])
122
+ if (/\s/.test(ch)) body += beside ? "" : `${SPACE}+`
123
+ else if (OPEN.includes(ch)) body += `${SPACE}*([${WIDE_OPEN}])${SPACE}*`
124
+ else if (CLOSE.includes(ch)) body += `${SPACE}*([${WIDE_CLOSE}])`
125
+ else body += `[${inClass(wide(ch))}]`
126
+ }
127
+ // A digit may not run into a render's leading or trailing digit ("1,320 m" holds no "320 m"); a bracket ends the render.
128
+ return new RegExp(`${/^\d/.test(text) ? `(?<!${DIGIT})` : ""}${body}${/\d$/.test(text) ? `(?!${DIGIT})` : ""}`, "gu")
129
+ }
130
+
131
+ const pairIndex = (ch, side) => [...side].findIndex((open) => open === folded(ch))
132
+ /** [start, end) of `rendered` in `text` from `from` under the bracket grammar, not followed by another original (`follows`); or null. */
133
+ export function findRender(text, rendered, from = 0, follows = () => false) {
134
+ const pattern = renderPattern(rendered)
135
+ pattern.lastIndex = from
136
+ for (let match = pattern.exec(text); match; match = pattern.exec(text)) {
137
+ const marks = match.slice(1)
138
+ const paired = marks.every((mark, i) => i % 2 === 1 || pairIndex(mark, OPEN) === pairIndex(marks[i + 1] ?? "", CLOSE))
139
+ const end = match.index + match[0].length
140
+ if (paired && !follows(text.slice(end))) return [match.index, end]
141
+ pattern.lastIndex = match.index + 1
142
+ }
143
+ return null
144
+ }
145
+
146
+ /** Where a stale fill-map entry's text sits: derived and converted renders are read under the bracket grammar. */
147
+ export const renderLocator = (rows, fillMap) => (entry, text, from) => {
148
+ const row = rows.get(entry.fact_id)
149
+ if (DERIVED.has(row?.kind)) return findRender(text, entry.rendered, from, followsOriginal(row, rows, (fillMap ?? []).map((other) => other.rendered)))
150
+ const hit = text.indexOf(entry.rendered, from)
151
+ return hit === -1 ? null : [hit, hit + entry.rendered.length]
152
+ }
153
+
154
+ /** Null when a stale derived render is still in the report; "original" when only its converted half is; "gone" otherwise. */
155
+ export function staleRender(row, rendered, text, follows) {
156
+ if (findRender(text, rendered, 0, follows) !== null) return null
157
+ const half = folded(rendered).replace(GROUP, "").trim()
158
+ return half !== "" && /\d/.test(half) && findRender(text, half) !== null ? "original" : "gone"
159
+ }
160
+
161
+ export const isDerivedRow = (row) => DERIVED.has(row?.kind)
@@ -0,0 +1,183 @@
1
+ // fidelity-policy.mjs - what the locked `## Fidelity` block of brief.md means to the CLI stages.
2
+ //
3
+ // lockedBrief is the gate the six reader-facing stages (fact fill, verify, pairs, meaning-check,
4
+ // polish-apply, polish-diff) run before reading or writing anything: an unlocked brief returns
5
+ // one fidelity_interview_incomplete defect (exit 1), a malformed block is a usage error (exit 2).
6
+ // fillPolicy reads the fields fact fill consumes (a value off its table is a usage error, exit 2),
7
+ // and policyErrors refuses a placeholder whose row the brief forbids: derived_not_allowed
8
+ // (`derived numbers: none` against a derived, converted, or assumed row) and source_outside_floor
9
+ // (the source floor is evaluated live against each source's meta.json at every fill and verify;
10
+ // under any floor but `standard` a row resting on no source is refused too; a row's outside_floor
11
+ // is only the cached result). briefHashOf binds the locked block into the fill map, and
12
+ // briefBindingErrors refuses an edition whose map was filled under another brief (brief_changed).
13
+ // Collection stages never call this module. node: builtins and sibling imports only.
14
+
15
+ import { existsSync, readFileSync } from "node:fs"
16
+ import { join, resolve } from "node:path"
17
+
18
+ import { CliError, sha256Of } from "./cli-support.mjs"
19
+ import { DEFECT_CODES } from "./contracts.mjs"
20
+ import { KIND_RULES } from "./fact-ledger-kinds.mjs"
21
+ import { FIDELITY_FIELDS, meaningCheckEnabled, requireLockedBrief } from "./fidelity-brief.mjs"
22
+
23
+ export { meaningCheckEnabled }
24
+
25
+ /**
26
+ * Ledger kinds a brief with `derived numbers: none` refuses. A policy list, not the ledger's
27
+ * derivation set: it also names assumed. Every name must be a KIND_RULES kind (checked at load).
28
+ */
29
+ const DERIVED_KINDS = Object.freeze(["derived", "converted", "assumed"])
30
+ for (const kind of DERIVED_KINDS) {
31
+ if (!Object.hasOwn(KIND_RULES, kind)) throw new Error(`fidelity-policy: ${kind} is not a KIND_RULES kind`)
32
+ }
33
+
34
+ /** Fields whose only legal values are a closed table: value (any case) -> its reading. */
35
+ const VALUE_TABLES = Object.freeze({
36
+ "derived numbers": Object.freeze({ allowed: false, none: true }),
37
+ fallback: Object.freeze({ "block-quote": "quote", omit: "omit" }),
38
+ })
39
+
40
+ /** The reading of a table value; any other text is a usage error naming the field and the value. */
41
+ function tableValue(field, value) {
42
+ const table = VALUE_TABLES[field]
43
+ const key = value.trim().toLowerCase()
44
+ if (Object.hasOwn(table, key)) return table[key]
45
+ throw new CliError(`${field} "${value}" is not ${Object.keys(table).join(", ")}`)
46
+ }
47
+
48
+ /** How each field value the fill stages consume is read; the single owner of those readings. */
49
+ export const FIDELITY_VALUE_RULES = Object.freeze({
50
+ tables: VALUE_TABLES,
51
+ derivedKinds: DERIVED_KINDS,
52
+ derivedRefused: (kind) => DERIVED_KINDS.includes(kind),
53
+ derivedNone: (value) => tableValue("derived numbers", value),
54
+ fallback: (value) => tableValue("fallback", value),
55
+ translations: (value) => /with translation beside/iu.test(value),
56
+ meaningCheck: (value) => meaningCheckEnabled({ fields: { "meaning check": value } }),
57
+ })
58
+
59
+ /** The `source floor` forms: `pattern` reads one, `listed` forms carry a comma list. */
60
+ export const SOURCE_FLOOR_RULES = Object.freeze([
61
+ Object.freeze({ kind: "standard", form: "standard", pattern: /^standard$/iu, listed: false }),
62
+ Object.freeze({ kind: "primary", form: "primary only", pattern: /^primary only$/iu, listed: false }),
63
+ Object.freeze({ kind: "pointed", form: "pointed only: <list>", pattern: /^pointed only:\s*(.+)$/iu, listed: true }),
64
+ Object.freeze({ kind: "classes", form: "classes: <list>", pattern: /^classes:\s*(.+)$/iu, listed: true }),
65
+ ])
66
+ const lineOf = (text, at) => text.slice(0, at).split("\n").length
67
+ const listOf = (text) => text.split(",").map((entry) => entry.trim()).filter(Boolean)
68
+
69
+ /** brief.md from --brief, else the session's; a usage error when neither is known. */
70
+ export function briefPathOf(flags) {
71
+ if (flags.brief !== undefined && flags.brief !== true) return resolve(flags.brief)
72
+ const dir = flags["session-dir"] ?? process.env.SESSION_DIR
73
+ if (!dir || dir === true) throw new CliError("--brief is required without --session-dir (or SESSION_DIR)")
74
+ return join(resolve(dir), "brief.md")
75
+ }
76
+
77
+ /**
78
+ * @returns {{ parsed: object, refusal: null } | { parsed: null, refusal: { json: object, summary: string, exitCode: 1 } }}
79
+ */
80
+ export function lockedBrief(command, flags) {
81
+ const path = briefPathOf(flags)
82
+ const lock = requireLockedBrief(path)
83
+ if (lock.exitCode === 2) throw new CliError(`${path}: ${lock.errors.join("; ")}`)
84
+ if (lock.locked) return { parsed: lock.parsed, refusal: null }
85
+ const json = { command, status: "fail", exitCode: 1, defects: lock.defects }
86
+ return { parsed: null, refusal: { json, summary: `${command}: ${lock.defects[0].message}`, exitCode: 1 } }
87
+ }
88
+
89
+ /**
90
+ * The source floor as a rule, or null for `standard`; a usage error for any other text.
91
+ * @returns {null | { kind: "primary" } | { kind: "pointed" | "classes", list: string[] }}
92
+ */
93
+ export function parseSourceFloor(value) {
94
+ const text = value.trim()
95
+ for (const rule of SOURCE_FLOOR_RULES) {
96
+ const match = rule.pattern.exec(text)
97
+ if (!match) continue
98
+ if (rule.kind === "standard") return null
99
+ return rule.listed ? { kind: rule.kind, list: listOf(match[1]) } : { kind: rule.kind }
100
+ }
101
+ throw new CliError(`source floor "${value}" is not ${SOURCE_FLOOR_RULES.map((rule) => rule.form).join(", ")}`)
102
+ }
103
+
104
+ function hostOf(url) {
105
+ return typeof url === "string" && URL.canParse(url) ? new URL(url).hostname.toLowerCase() : null
106
+ }
107
+
108
+ /** Whether one source (id and meta.json) satisfies a parsed floor rule. */
109
+ export function sourceInsideFloor(rule, sourceId, meta) {
110
+ if (rule.kind === "primary") return meta.provenance === "primary"
111
+ if (rule.kind === "pointed") return rule.list.includes(sourceId) || rule.list.includes(meta.url)
112
+ const host = hostOf(meta.url)
113
+ return host !== null && rule.list.some((entry) => host === entry.toLowerCase() || host.endsWith(`.${entry.toLowerCase()}`))
114
+ }
115
+
116
+ /** The fields fact fill consumes: the fallback form and whether a translation binds to its quote. */
117
+ export function fillPolicy(parsed) {
118
+ const field = (name) => parsed.fields[name]
119
+ return {
120
+ fallback: FIDELITY_VALUE_RULES.fallback(field("fallback")),
121
+ translations: FIDELITY_VALUE_RULES.translations(field("languages")),
122
+ derivedNone: FIDELITY_VALUE_RULES.derivedNone(field("derived numbers")),
123
+ floor: parseSourceFloor(field("source floor")),
124
+ }
125
+ }
126
+
127
+ /** Source ids behind a row: its own, or for a derived row its operands' sources; null marks a leaf with no source. */
128
+ function sourcesOf(row, rows, seen = new Set()) {
129
+ if (seen.has(row.fact_id)) return []
130
+ seen.add(row.fact_id)
131
+ if (row.source_id) return [row.source_id]
132
+ const inputs = [...(row.operands ?? []), ...(row.derived_from ?? [])]
133
+ if (inputs.length === 0) return [null]
134
+ return inputs.flatMap((id) => (rows.has(id) ? sourcesOf(rows.get(id), rows, seen) : [null]))
135
+ }
136
+
137
+ /** sha256 of the locked `## Fidelity` block normalized: the seven fields in FIDELITY_FIELDS order, `field: value` per line. */
138
+ export const briefHashOf = (parsed) => sha256Of(FIDELITY_FIELDS.map((field) => `${field}: ${parsed.fields[field]}`).join("\n"))
139
+
140
+ /** brief_changed when there is no fill map (map null) or it was not written under the current brief (or predates the binding). */
141
+ export function briefBindingErrors(map, mapPath, parsed) {
142
+ if (map?.brief_sha256 === briefHashOf(parsed)) return []
143
+ const why = map === null ? `no fill map at ${mapPath}` : `${mapPath} ${typeof map.brief_sha256 === "string" ? "was filled under a different ## Fidelity block" : "records no brief_sha256"}`
144
+ return [{ code: "brief_changed", token: null, line: null, message: `${why}; re-run fact fill under the current brief`, severity: DEFECT_CODES.brief_changed.severity, integrity: DEFECT_CODES.brief_changed.integrity, hint: DEFECT_CODES.brief_changed.hint }]
145
+ }
146
+
147
+ function metaOf(sessionDir, sourceId, cache) {
148
+ if (!cache.has(sourceId)) {
149
+ const file = join(sessionDir, "sources", sourceId, "meta.json")
150
+ cache.set(sourceId, existsSync(file) ? JSON.parse(readFileSync(file, "utf8")) : { url: null, provenance: "unknown" })
151
+ }
152
+ return cache.get(sourceId)
153
+ }
154
+
155
+ const defect = (code, pointer, text, message) => ({ code, token: pointer.token, line: lineOf(text, pointer.start), message, severity: DEFECT_CODES[code].severity, hint: DEFECT_CODES[code].hint })
156
+
157
+ /**
158
+ * Refusals for the placeholders of one fill source (or the data-fact elements of one edition at
159
+ * verify). Sets each referenced row's outside_floor to this evaluation and reports whether any
160
+ * cached value changed.
161
+ * @param {{ token: string, start: number, parsed: { id?: string } }[]} found findPlaceholders output
162
+ * @returns {{ errors: object[], cacheChanged: boolean }}
163
+ */
164
+ export function policyErrors(text, found, ledger, sessionDir, policy) {
165
+ const rows = new Map(ledger.rows.map((row) => [row.fact_id, row]))
166
+ const metas = new Map()
167
+ const errors = []
168
+ let cacheChanged = false
169
+ for (const pointer of found) {
170
+ const row = rows.get(pointer.parsed?.id)
171
+ if (row === undefined || row.status !== "active") continue
172
+ if (policy.derivedNone && FIDELITY_VALUE_RULES.derivedRefused(row.kind)) {
173
+ errors.push(defect("derived_not_allowed", pointer, text, `${row.fact_id} is ${row.kind}; the brief records derived numbers: none`))
174
+ }
175
+ const sources = sourcesOf(row, rows)
176
+ const outside = policy.floor === null ? [] : [...new Set(sources.filter((id) => id === null || !sourceInsideFloor(policy.floor, id, metaOf(sessionDir, id, metas))))]
177
+ if (row.outside_floor !== outside.length > 0) cacheChanged = true
178
+ row.outside_floor = outside.length > 0
179
+ const named = outside.map((id) => id ?? "no source").join(", ")
180
+ if (outside.length > 0) errors.push(defect("source_outside_floor", pointer, text, `${row.fact_id} rests on ${named}, outside the source floor`))
181
+ }
182
+ return { errors, cacheChanged }
183
+ }