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
@@ -1,238 +1,12 @@
1
1
  import { describe, expect, test } from "bun:test"
2
-
3
- import { derive, display, recompute } from "./fact-derive.mjs"
2
+ import { display, recompute } from "./fact-derive.mjs"
3
+ import { derive } from "./tests/derive-clock"
4
4
  import { parseExpr } from "./fact-expr.mjs"
5
5
  import { addRow, emptyLedger, OPERATIONS } from "./fact-ledger.mjs"
6
6
  import { canonicalUnit, expectedUnit, isAffine, OPERATION_RULES, renderPrecision } from "./unit-rules.mjs"
7
+ import { Row, Ledger, Spec, HASH, measured, ledger, PCT_SUM, VALID, PRECISION, RELABELS, Mutation, generic, repeatFirst, operands, MALFORMED, REPEATED, rowOf, relabel, OPS } from "./tests/fact-unit-rules-support"
7
8
 
8
- // Property checks of references/unit-rules.md: every case table below is keyed by the registry's own operation list,
9
- // so a new operation without valid, relabel, and malformed cases fails here.
10
-
11
- type Row = Record<string, unknown>
12
- type Ledger = { schemaVersion: number; rows: Row[]; events: unknown[] }
13
- type Spec = Record<string, unknown>
14
-
15
- const HASH = "a".repeat(64)
16
-
17
- function measured(factId: string, value: string, unit: string | null, extra: Row = {}): Row {
18
- return {
19
- fact_id: factId,
20
- kind: "measured",
21
- value,
22
- unit,
23
- raw: `${value} ${unit ?? ""}`.trim(),
24
- quote: `The figure is ${value}.`,
25
- source_id: "S1",
26
- locator: { artifact: "sources/S1/text.txt", char_start: 0, char_end: 20 },
27
- snapshot_sha256: HASH,
28
- extracted_by: "fact add --find",
29
- observed_at: "2026-10-23T08:00:00Z",
30
- label: factId.toLowerCase(),
31
- ...extra,
32
- }
33
- }
34
-
35
- function ledger(): Ledger {
36
- const built = emptyLedger() as Ledger
37
- const rows = [
38
- measured("F1", "1", "m"),
39
- measured("F2", "3", "m"),
40
- measured("F3", "20", "%"),
41
- measured("F4", "15", "%"),
42
- measured("F5", "5", "%p"),
43
- measured("F6", "100", "case", { valid_at: "2025", entity: "acme" }),
44
- measured("F7", "112", "case", { valid_at: "2026", entity: "acme" }),
45
- measured("F8", "40", "person"),
46
- measured("F9", "200", "person"),
47
- measured("F10", "100", "USD"),
48
- measured("F11", "20", "USD/person"),
49
- measured("F12", "0", "m"),
50
- measured("F13", "25", "C", { valid_at: "2025", entity: "x" }),
51
- measured("F14", "30", "C", { valid_at: "2026", entity: "x" }),
52
- measured("F15", "0", "person"),
53
- measured("F16", "2.5", "kilometre"),
54
- measured("F17", "120", "case", { valid_at: "2026", entity: "other" }),
55
- measured("F18", "0", "case", { valid_at: "2025", entity: "acme" }),
56
- measured("F19", "-5", "person"),
57
- measured("F20", "2", "%p"),
58
- measured("F21", "105", "case", { valid_at: "2025", entity: "acme" }),
59
- measured("F22", "200", "person"),
60
- ]
61
- for (const row of rows) addRow(built, row)
62
- return built
63
- }
64
-
65
- const PCT_SUM = { op: "sum", operands: ["F3", "F4"], unit: "%", disjoint: true, reference: "all respondents", rationale: "disjoint answer shares" }
66
-
67
- const VALID: Record<string, Array<{ spec: Spec; unit: string | null; precision?: "places" | "sigfigs" }>> = {
68
- sum: [
69
- { spec: { op: "sum", operands: ["F1", "F2"], unit: "m" }, unit: "m" },
70
- { spec: { op: "sum", operands: ["F1", "F2"], unit: "metre" }, unit: "m" },
71
- { spec: PCT_SUM, unit: "%" },
72
- ],
73
- diff: [
74
- { spec: { op: "diff", operands: ["F2", "F1"], unit: "m" }, unit: "m" },
75
- { spec: { op: "diff", operands: ["F3", "F4"], unit: "pp" }, unit: "%p" },
76
- { spec: { op: "diff", operands: ["F3", "F5"], unit: "%" }, unit: "%" },
77
- { spec: { op: "diff", operands: ["F5", "F20"], unit: "%p" }, unit: "%p" },
78
- ],
79
- growth: [{ spec: { op: "growth", from: "F6", to: "F7", unit: "%" }, unit: "%" }],
80
- share: [
81
- { spec: { op: "share", part: "F8", whole: "F9", unit: "%" }, unit: "%" },
82
- { spec: { op: "share", part: "F22", whole: "F9", unit: "%" }, unit: "%" },
83
- ],
84
- ratio: [
85
- { spec: { op: "ratio", num: "F10", den: "F11", unit: "person" }, unit: "person" },
86
- { spec: { op: "ratio", num: "F1", den: "F2", unit: null }, unit: null },
87
- { spec: { op: "ratio", num: "F10", den: "F9", unit: "USD/person" }, unit: "USD/person" },
88
- ],
89
- convert: [
90
- { spec: { op: "convert", operands: ["F12"], to: "km" }, unit: "km" },
91
- { spec: { op: "convert", operands: ["F16"], to: "m" }, unit: "m" },
92
- { spec: { op: "convert", operands: ["F2"], to: "kilometre" }, unit: "km" },
93
- ],
94
- expr: [
95
- { spec: { op: "expr", formula: "F3 - F4", unit: "%p" }, unit: "%p", precision: "places" },
96
- { spec: { op: "expr", formula: "F2 - F1", unit: "m" }, unit: "m", precision: "places" },
97
- { spec: { op: "expr", formula: "F5", unit: "%p" }, unit: "%p", precision: "places" },
98
- { spec: { op: "expr", formula: "F1 * F1 / F2", unit: "m" }, unit: "m" },
99
- { spec: { op: "expr", formula: "F10 / F11", unit: "person" }, unit: "person" },
100
- { spec: { op: "expr", formula: "pct(F8 / F9)", unit: "%" }, unit: "%" },
101
- { spec: { op: "expr", formula: "F3 * F9", unit: "person" }, unit: "person" },
102
- ],
103
- }
104
-
105
- /** Invariant 5 per operation: sum and diff render by decimal places, the rest by significant figures (expr cases override). */
106
- const PRECISION: Record<string, "places" | "sigfigs"> = { sum: "places", diff: "places", growth: "sigfigs", share: "sigfigs", ratio: "sigfigs", convert: "sigfigs", expr: "sigfigs" }
107
-
108
- const RELABELS = [null, "%", "%p", "pp", "bp", "m", "metre", "km", "kilometre", "cm", "C", "case", "person", "USD", "USD/person", "person/USD", "KRW-e8"]
109
-
110
- type Mutation = { name: string; base: Spec; mutate: (row: Row, book: Ledger) => Row; reason: string }
111
- const generic = (base: Spec, arity: string[], arityReason = "takes ", missingReason = "no ledger row F999"): Mutation[] => [
112
- { name: "operands null", base, mutate: (row) => ({ ...row, operands: null }), reason: "operands must be a non-empty array" },
113
- { name: "operands empty", base, mutate: (row) => ({ ...row, operands: [] }), reason: "operands must be a non-empty array" },
114
- { name: "wrong arity", base, mutate: (row) => ({ ...row, operands: arity }), reason: arityReason },
115
- { name: "missing operand id", base, mutate: (row) => ({ ...row, operands: (row.operands as string[]).map((id, index) => (index === 0 ? "F999" : id)) }), reason: missingReason },
116
- { name: "invalid rational", base, mutate: (row) => ({ ...row, exact: { num: "x", den: "1" } }), reason: "exact must be { num, den }" },
117
- { name: "no operation", base, mutate: (row) => ({ ...row, operation: "mean" }), reason: 'operation "mean" is not one of' },
118
- { name: "repeated operand", base, mutate: (row) => ({ ...row, operands: repeatFirst(row.operands as string[]) }), reason: "is repeated" },
119
- { name: "self reference", base, mutate: (row) => ({ ...row, operands: (row.operands as string[]).map((id, index) => (index === 0 ? row.fact_id : id)) }), reason: "cites itself" },
120
- { name: "kind operation", base, mutate: (row) => ({ ...row, kind: "assumed" }), reason: "kind assumed" },
121
- { name: "audit fields", base, mutate: (row) => ({ ...row, factor: "999" }), reason: "factor" },
122
- { name: "precision field", base, mutate: (row) => ({ ...row, precision: { places: -1, sigfigs: 0 } }), reason: "precision" },
123
- { name: "compatibility field", base, mutate: (row) => ({ ...row, compatibility: "invalid" }), reason: "compatibility" },
124
- { name: "metadata fields", base, mutate: (row) => ({ ...row, population: { tampered: true } }), reason: "population" },
125
- { name: "noncanonical operand", base, mutate: (row, book) => {
126
- const operand = book.rows.find((r) => r.fact_id === (row.operands as string[])[0])
127
- if (!operand) throw Error("fixture operand missing")
128
- operand.unit = `${operand.unit}*bp/%p`
129
- return row
130
- }, reason: "canonical" },
131
- ]
132
- /** The first operand id in a second slot: [F1, F2] -> [F1, F1]; a one-operand row gets [F1, F1]. */
133
- const repeatFirst = (ids: string[]) => (ids.length === 1 ? [ids[0], ids[0]] : [...ids.slice(0, -1), ids[0]])
134
- const operands = (ids: string[]) => (row: Row) => ({ ...row, operands: ids })
135
-
136
- const MALFORMED: Record<string, Mutation[]> = {
137
- sum: [
138
- ...generic(VALID.sum[0].spec, ["F1"]),
139
- { name: "no context", base: VALID.sum[0].spec, mutate: (row) => ({ ...row, context: null }), reason: "sum rows carry a context object" },
140
- { name: "% without disjoint", base: PCT_SUM, mutate: (row) => ({ ...row, context: { disjoint: false, reference: "all respondents" } }), reason: "needs disjoint shares" },
141
- { name: "% without reference", base: PCT_SUM, mutate: (row) => ({ ...row, context: { disjoint: true, reference: null } }), reason: "one shared reference" },
142
- { name: "% without rationale", base: PCT_SUM, mutate: (row) => ({ ...row, rationale: null }), reason: "needs a rationale" },
143
- { name: "unequal units", base: VALID.sum[0].spec, mutate: operands(["F1", "F8"]), reason: "unit differs" },
144
- { name: "affine units", base: VALID.sum[0].spec, mutate: operands(["F13", "F14"]), reason: "sum refuses affine" },
145
- { name: "closed context", base: VALID.sum[0].spec, mutate: (row) => ({ ...row, context: { disjoint: "true", reference: 7, extra: true } }), reason: "sum context" },
146
- { name: "share total", base: PCT_SUM, mutate: (row, book) => {
147
- for (const operand of book.rows.filter((r) => ["F3", "F4"].includes(String(r.fact_id)))) Object.assign(operand, { value: "60", exact: { num: "60", den: "1" } })
148
- return row
149
- }, reason: "total <= 100" },
150
- ],
151
- diff: [
152
- ...generic(VALID.diff[0].spec, ["F2", "F1", "F8"]),
153
- { name: "%p - % pair", base: VALID.diff[2].spec, mutate: operands(["F5", "F3"]), reason: '"%p" - "%" is not a typed pair' },
154
- { name: "unit - other unit pair", base: VALID.diff[0].spec, mutate: operands(["F1", "F8"]), reason: '"m" - "person" is not a typed pair' },
155
- { name: "affine units", base: VALID.diff[0].spec, mutate: operands(["F14", "F13"]), reason: "affine (temperature)" },
156
- { name: "stray context", base: VALID.diff[0].spec, mutate: (row) => ({ ...row, context: "x" }), reason: "diff rows carry no context" },
157
- ],
158
- growth: [
159
- ...generic(VALID.growth[0].spec, ["F6"]),
160
- { name: "affine units", base: VALID.growth[0].spec, mutate: operands(["F13", "F14"]), reason: "growth refuses affine" },
161
- { name: "unequal units", base: VALID.growth[0].spec, mutate: operands(["F6", "F8"]), reason: "unit differs" },
162
- { name: "entity mismatch", base: VALID.growth[0].spec, mutate: operands(["F6", "F17"]), reason: "growth needs one declared entity" },
163
- { name: "one period", base: VALID.growth[0].spec, mutate: operands(["F6", "F21"]), reason: "two different valid_at periods" },
164
- { name: "from <= 0", base: VALID.growth[0].spec, mutate: operands(["F18", "F7"]), reason: "positive starting value in F18" },
165
- { name: "stray context", base: VALID.growth[0].spec, mutate: (row) => ({ ...row, context: { target: "km" } }), reason: "growth rows carry no context" },
166
- ],
167
- share: [
168
- ...generic(VALID.share[0].spec, ["F8"]),
169
- { name: "affine units", base: VALID.share[0].spec, mutate: operands(["F13", "F14"]), reason: "share refuses affine" },
170
- { name: "unequal units", base: VALID.share[0].spec, mutate: operands(["F8", "F1"]), reason: "unit differs" },
171
- { name: "whole <= 0", base: VALID.share[0].spec, mutate: operands(["F8", "F15"]), reason: "positive whole in F15" },
172
- { name: "part < 0", base: VALID.share[0].spec, mutate: operands(["F19", "F9"]), reason: "F19 is outside [0, F9]" },
173
- { name: "part above whole", base: VALID.share[0].spec, mutate: operands(["F9", "F8"]), reason: "F9 is outside [0, F8]" },
174
- { name: "stray context", base: VALID.share[0].spec, mutate: (row) => ({ ...row, context: "x" }), reason: "share rows carry no context" },
175
- ],
176
- ratio: [
177
- ...generic(VALID.ratio[0].spec, ["F10"]),
178
- { name: "affine units", base: VALID.ratio[0].spec, mutate: operands(["F13", "F14"]), reason: "ratio refuses affine" },
179
- { name: "divides by zero", base: VALID.ratio[0].spec, mutate: operands(["F10", "F15"]), reason: "denominator F15 is zero" },
180
- { name: "stray context", base: VALID.ratio[0].spec, mutate: (row) => ({ ...row, context: { target: "km" } }), reason: "ratio rows carry no context" },
181
- ],
182
- convert: [
183
- ...generic(VALID.convert[0].spec, ["F12", "F1"]),
184
- { name: "no context", base: VALID.convert[0].spec, mutate: (row) => ({ ...row, context: null }), reason: "convert rows carry a context object" },
185
- { name: "empty target", base: VALID.convert[0].spec, mutate: (row) => ({ ...row, context: { target: "" } }), reason: "carries its target in context.target" },
186
- { name: "alias target", base: VALID.convert[0].spec, mutate: (row) => ({ ...relabel(row, "kilometre")[0], context: { target: "kilometre" } }), reason: 'context.target "kilometre" is not canonical' },
187
- { name: "currency target", base: VALID.convert[0].spec, mutate: (row) => ({ ...row, context: { target: "USD" } }), reason: "no table conversion to USD (a currency)" },
188
- { name: "no factor to target", base: VALID.convert[0].spec, mutate: (row) => ({ ...row, context: { target: "person" } }), reason: 'F12 in "m" has no table conversion to person' },
189
- { name: "closed context", base: VALID.convert[0].spec, mutate: (row) => ({ ...row, context: { target: "km", extra: true } }), reason: "exactly target" },
190
- ],
191
- expr: [
192
- ...generic(VALID.expr[0].spec, ["F3", "F4", "F5"], "do not match operands", "do not match operands F999, F4"),
193
- { name: "formula id mismatch", base: VALID.expr[0].spec, mutate: (row) => ({ ...row, formula: "F3 - F99" }), reason: "do not match operands" },
194
- { name: "parse error", base: VALID.expr[0].spec, mutate: (row) => ({ ...row, formula: "F3 -" }), reason: "the expression ends early" },
195
- { name: "divides by zero", base: VALID.expr[0].spec, mutate: (row) => ({ ...row, formula: "F3 / (F4 - F4)" }), reason: "the expression divides by zero" },
196
- { name: "uninferable unit", base: VALID.expr[0].spec, mutate: (row) => ({ ...row, formula: "F3 + F4" }), reason: "adding % values" },
197
- { name: "affine sum", base: VALID.expr[0].spec, mutate: (row) => ({ ...row, formula: "F13 + F14", operands: ["F13", "F14"] }), reason: "adding affine (temperature)" },
198
- { name: "stray context", base: VALID.expr[0].spec, mutate: (row) => ({ ...row, context: { target: "km" } }), reason: "expr rows carry no context" },
199
- { name: "undated rate", base: VALID.expr[0].spec, mutate: (row, book) => {
200
- const rate = book.rows.find((r) => r.fact_id === "F3")
201
- if (!rate) throw Error("fixture rate missing")
202
- rate.unit = "USD/EUR"
203
- return { ...row, formula: "F3 * F4" }
204
- }, reason: "dated valid_at" },
205
- ],
206
- }
207
-
208
- /** Review r6 HIGH: one id twice is never disjoint shares; expr repeats F1 inside its formula and stores the id set. */
209
- const REPEATED: Record<string, Spec> = {
210
- sum: { ...PCT_SUM, operands: ["F3", "F3"] },
211
- diff: { op: "diff", operands: ["F3", "F3"], unit: "%p" },
212
- growth: { op: "growth", from: "F6", to: "F6", unit: "%" },
213
- share: { op: "share", part: "F9", whole: "F9", unit: "%" },
214
- ratio: { op: "ratio", num: "F10", den: "F10", unit: null },
215
- convert: { op: "convert", operands: ["F12", "F12"], to: "km" },
216
- expr: { op: "expr", formula: "F1 * F1 / F2", unit: "m" },
217
- }
218
-
219
- function rowOf(result: { row?: Row; error?: string; detail?: string }): Row {
220
- if (!result.row) throw new Error(`expected a row, got ${result.error}: ${result.detail}`)
221
- return result.row
222
- }
223
-
224
- /** The row relabeled to `unit`; when the operation persists a context, a second variant rewrites that context too. */
225
- function relabel(row: Row, unit: string | null): Row[] {
226
- const shown = display(row.value, unit)
227
- const render = row.operation === "convert" ? `${shown} ${String(row.render).slice(String(row.render).indexOf("("))}` : shown
228
- const plain = { ...row, unit, raw: render, render }
229
- const context = row.context as Row
230
- if (row.operation === "convert") return [plain, { ...plain, context: { ...context, target: unit } }]
231
- if (row.operation === "sum") return [plain, { ...plain, context: { ...context, reference: `relabeled ${unit}` } }]
232
- return [plain]
233
- }
234
9
 
235
- const OPS = Object.keys(OPERATION_RULES)
236
10
 
237
11
  test("the case tables cover exactly the registry's operations, which are the ledger schema's operations", () => {
238
12
  expect([...OPS].sort()).toEqual([...OPERATIONS].sort())
@@ -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)