oh-my-opencode 5.1.48 → 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 (495) 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 -316
  64. package/dist/skills/ulw-research/references/deliverable-phase.md +23 -21
  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 +12 -5
  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 +13 -4
  77. package/dist/skills/ulw-research/scripts/contracts.test.ts +6 -110
  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-polish.test.ts +4 -3
  84. package/dist/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  85. package/dist/skills/ulw-research/scripts/css-lite.mjs +4 -2
  86. package/dist/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
  87. package/dist/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
  88. package/dist/skills/ulw-research/scripts/entities.mjs +13 -4
  89. package/dist/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
  90. package/dist/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
  91. package/dist/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
  92. package/dist/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
  93. package/dist/skills/ulw-research/scripts/fact-commands.mjs +35 -16
  94. package/dist/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
  95. package/dist/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  96. package/dist/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
  97. package/dist/skills/ulw-research/scripts/fact-fill.mjs +6 -4
  98. package/dist/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  99. package/dist/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
  100. package/dist/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
  101. package/dist/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  102. package/dist/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
  103. package/dist/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
  104. package/dist/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
  105. package/dist/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  106. package/dist/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  107. package/dist/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  108. package/dist/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  109. package/dist/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
  110. package/dist/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  111. package/dist/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  112. package/dist/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  113. package/dist/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  114. package/dist/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  115. package/dist/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  116. package/dist/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
  117. package/dist/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  118. package/dist/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
  119. package/dist/skills/ulw-research/scripts/gates-static.mjs +10 -1
  120. package/dist/skills/ulw-research/scripts/html-lite.mjs +6 -2
  121. package/dist/skills/ulw-research/scripts/html-markup.mjs +2 -2
  122. package/dist/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  123. package/dist/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
  124. package/dist/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  125. package/dist/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
  126. package/dist/skills/ulw-research/scripts/md-lite.mjs +1 -1
  127. package/dist/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  128. package/dist/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
  129. package/dist/skills/ulw-research/scripts/numerals.mjs +18 -7
  130. package/dist/skills/ulw-research/scripts/numerals.test.ts +3 -0
  131. package/dist/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  132. package/dist/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
  133. package/dist/skills/ulw-research/scripts/outcome.mjs +11 -22
  134. package/dist/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
  135. package/dist/skills/ulw-research/scripts/placeholder.mjs +18 -6
  136. package/dist/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  137. package/dist/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
  138. package/dist/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
  139. package/dist/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  140. package/dist/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  141. package/dist/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
  142. package/dist/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
  143. package/dist/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
  144. package/dist/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
  145. package/dist/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
  146. package/dist/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  147. package/dist/skills/ulw-research/scripts/report-tools.mjs +7 -1
  148. package/dist/skills/ulw-research/scripts/report-tools.test.ts +5 -800
  149. package/dist/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
  150. package/dist/skills/ulw-research/scripts/source-commands.mjs +79 -26
  151. package/dist/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
  152. package/dist/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  153. package/dist/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  154. package/dist/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
  155. package/dist/skills/ulw-research/scripts/source-view.mjs +89 -0
  156. package/dist/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  157. package/dist/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
  158. package/dist/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
  159. package/dist/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
  160. package/dist/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
  161. package/dist/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
  162. package/dist/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
  163. package/dist/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
  164. package/dist/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
  165. package/dist/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
  166. package/dist/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
  167. package/dist/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
  168. package/dist/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
  169. package/dist/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
  170. package/dist/skills/ulw-research/scripts/units.mjs +14 -3
  171. package/dist/skills/ulw-research/scripts/units.test.ts +10 -0
  172. package/dist/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
  173. package/dist/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
  174. package/dist/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
  175. package/dist/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
  176. package/dist/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
  177. package/dist/tools/delegate-task/executor-types.d.ts +1 -0
  178. package/dist/tools/delegate-task/sync-session-existence.d.ts +4 -0
  179. package/dist/tools/delegate-task/types.d.ts +1 -0
  180. package/dist/tools/skill/description-formatter.d.ts +2 -1
  181. package/dist/tools/skill/types.d.ts +6 -0
  182. package/dist/tui.js +131 -33
  183. package/package.json +18 -15
  184. package/packages/git-bash-mcp/package.json +1 -1
  185. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  186. package/packages/omo-codex/plugin/README.md +1 -1
  187. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  188. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  189. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  190. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  191. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  192. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  193. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  194. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  195. package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
  196. package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  197. package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/SKILL.md +7 -7
  198. package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/agents/openai.yaml +1 -1
  199. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  200. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  201. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  202. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  203. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  204. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  205. package/packages/omo-codex/plugin/components/teammode/package.json +2 -2
  206. package/packages/omo-codex/plugin/components/telemetry/README.md +2 -2
  207. package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +52 -15
  208. package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +52 -19
  209. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  210. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  211. package/packages/omo-codex/plugin/components/telemetry/src/codex-hook.ts +2 -9
  212. package/packages/omo-codex/plugin/components/telemetry/src/posthog.ts +4 -13
  213. package/packages/omo-codex/plugin/components/telemetry/src/product-identity.ts +0 -2
  214. package/packages/omo-codex/plugin/components/telemetry/test/codex-hook-diagnostics.test.ts +0 -3
  215. package/packages/omo-codex/plugin/components/telemetry/test/codex-hook.test.ts +3 -8
  216. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +3 -0
  217. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  218. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  219. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  220. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  221. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  222. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +3 -0
  223. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  224. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  225. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  226. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  227. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  228. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  229. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  230. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  231. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  232. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  233. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  234. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  235. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  236. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  237. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  238. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  239. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  240. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  241. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  242. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  243. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  244. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  245. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  246. package/packages/omo-codex/plugin/package-lock.json +16 -16
  247. package/packages/omo-codex/plugin/package.json +1 -1
  248. package/packages/omo-codex/plugin/shared/package.json +1 -1
  249. package/packages/omo-codex/plugin/skills/ast-grep/README.md +1 -1
  250. package/packages/omo-codex/plugin/skills/ast-grep/install.ps1 +5 -3
  251. package/packages/omo-codex/plugin/skills/ast-grep/install.sh +4 -5
  252. package/packages/omo-codex/plugin/skills/ast-grep/references/install.md +2 -2
  253. package/packages/omo-codex/plugin/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
  254. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  255. package/packages/omo-codex/plugin/skills/coding-agent-sessions/AGENTS.md +5 -4
  256. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +11 -11
  257. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +2 -2
  258. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/codex.md +4 -4
  259. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
  260. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
  261. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +1 -1
  262. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +34 -12
  263. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +2 -2
  264. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +2 -1
  265. package/packages/omo-codex/plugin/skills/frontend/references/design/component-catalogs.md +14 -10
  266. package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +19 -6
  267. package/packages/omo-codex/plugin/skills/frontend/references/design/grounding.md +29 -0
  268. package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
  269. package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
  270. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
  271. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  272. package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +7 -7
  273. package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
  274. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +5 -4
  275. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -2
  276. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/__main__.py +9 -6
  277. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/summary.py +1 -1
  278. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  279. package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
  280. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +3 -0
  281. package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +8 -4
  282. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +7 -316
  283. package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +23 -21
  284. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
  285. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +42 -8
  286. package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
  287. package/packages/omo-codex/plugin/skills/ulw-research/references/markdown-blocks.md +12 -0
  288. package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +12 -5
  289. package/packages/omo-codex/plugin/skills/ulw-research/references/research-collection.md +195 -0
  290. package/packages/omo-codex/plugin/skills/ulw-research/references/research-delivery.md +123 -0
  291. package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
  292. package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +13 -4
  293. package/packages/omo-codex/plugin/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
  294. package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  295. package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-lite.mjs +4 -2
  296. package/packages/omo-codex/plugin/skills/ulw-research/scripts/entities.mjs +13 -4
  297. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
  298. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
  299. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
  300. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
  301. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +35 -16
  302. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  303. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +6 -4
  304. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  305. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  306. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  307. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  308. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  309. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  310. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
  311. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  312. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  313. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  314. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  315. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  316. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  317. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  318. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +10 -1
  319. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +6 -2
  320. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +2 -2
  321. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  322. package/packages/omo-codex/plugin/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  323. package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +1 -1
  324. package/packages/omo-codex/plugin/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  325. package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +18 -7
  326. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  327. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +11 -22
  328. package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +18 -6
  329. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  330. package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  331. package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  332. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  333. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +7 -1
  334. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +79 -26
  335. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  336. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  337. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-view.mjs +89 -0
  338. package/packages/omo-codex/plugin/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  339. package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +14 -3
  340. package/packages/omo-codex/plugin/test/payload-equivalence.test.mjs +6 -4
  341. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -3
  342. package/packages/omo-codex/scripts/install-dist/install-local.mjs +338 -237
  343. package/packages/prompts-core/prompts/ultrawork/codex.md +3 -0
  344. package/packages/shared-skills/skill-source-filter.mjs +6 -2
  345. package/packages/shared-skills/skills/ast-grep/README.md +1 -1
  346. package/packages/shared-skills/skills/ast-grep/install.ps1 +5 -3
  347. package/packages/shared-skills/skills/ast-grep/install.sh +4 -5
  348. package/packages/shared-skills/skills/ast-grep/references/install.md +2 -2
  349. package/packages/shared-skills/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
  350. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  351. package/packages/shared-skills/skills/coding-agent-sessions/AGENTS.md +5 -4
  352. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +11 -11
  353. package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +2 -2
  354. package/packages/shared-skills/skills/coding-agent-sessions/references/codex.md +4 -4
  355. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
  356. package/packages/shared-skills/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
  357. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +1 -1
  358. package/packages/shared-skills/skills/frontend/SKILL.md +34 -12
  359. package/packages/shared-skills/skills/frontend/references/design/README.md +2 -2
  360. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +2 -1
  361. package/packages/shared-skills/skills/frontend/references/design/component-catalogs.md +14 -10
  362. package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +19 -6
  363. package/packages/shared-skills/skills/frontend/references/design/grounding.md +29 -0
  364. package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
  365. package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
  366. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +5 -4
  367. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -2
  368. package/packages/shared-skills/skills/ultimate-browsing/engine/__main__.py +9 -6
  369. package/packages/shared-skills/skills/ultimate-browsing/engine/summary.py +1 -1
  370. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  371. package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
  372. package/packages/shared-skills/skills/ulw-research/AGENTS.md +8 -4
  373. package/packages/shared-skills/skills/ulw-research/SKILL.md +7 -316
  374. package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +23 -21
  375. package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
  376. package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +42 -8
  377. package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
  378. package/packages/shared-skills/skills/ulw-research/references/markdown-blocks.md +12 -0
  379. package/packages/shared-skills/skills/ulw-research/references/report-gates.md +12 -5
  380. package/packages/shared-skills/skills/ulw-research/references/research-collection.md +195 -0
  381. package/packages/shared-skills/skills/ulw-research/references/research-delivery.md +123 -0
  382. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
  383. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
  384. package/packages/shared-skills/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
  385. package/packages/shared-skills/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
  386. package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +13 -4
  387. package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +6 -110
  388. package/packages/shared-skills/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
  389. package/packages/shared-skills/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
  390. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
  391. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
  392. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
  393. package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-polish.test.ts +4 -3
  394. package/packages/shared-skills/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  395. package/packages/shared-skills/skills/ulw-research/scripts/css-lite.mjs +4 -2
  396. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
  397. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
  398. package/packages/shared-skills/skills/ulw-research/scripts/entities.mjs +13 -4
  399. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
  400. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
  401. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
  402. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
  403. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +35 -16
  404. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
  405. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  406. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
  407. package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +6 -4
  408. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  409. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
  410. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
  411. package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  412. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
  413. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
  414. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
  415. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  416. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  417. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  418. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  419. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
  420. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  421. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  422. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  423. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  424. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  425. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  426. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
  427. package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  428. package/packages/shared-skills/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
  429. package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +10 -1
  430. package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +6 -2
  431. package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +2 -2
  432. package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  433. package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
  434. package/packages/shared-skills/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  435. package/packages/shared-skills/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
  436. package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +1 -1
  437. package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  438. package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
  439. package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +18 -7
  440. package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +3 -0
  441. package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  442. package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
  443. package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +11 -22
  444. package/packages/shared-skills/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
  445. package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +18 -6
  446. package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  447. package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
  448. package/packages/shared-skills/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
  449. package/packages/shared-skills/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  450. package/packages/shared-skills/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  451. package/packages/shared-skills/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
  452. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
  453. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
  454. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
  455. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
  456. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  457. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +7 -1
  458. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +5 -800
  459. package/packages/shared-skills/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
  460. package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +79 -26
  461. package/packages/shared-skills/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
  462. package/packages/shared-skills/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  463. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  464. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
  465. package/packages/shared-skills/skills/ulw-research/scripts/source-view.mjs +89 -0
  466. package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  467. package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
  468. package/packages/shared-skills/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
  469. package/packages/shared-skills/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
  470. package/packages/shared-skills/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
  471. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
  472. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
  473. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
  474. package/packages/shared-skills/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
  475. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
  476. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
  477. package/packages/shared-skills/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
  478. package/packages/shared-skills/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
  479. package/packages/shared-skills/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
  480. package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +14 -3
  481. package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +10 -0
  482. package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
  483. package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
  484. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
  485. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
  486. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
  487. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/amp_redirect_stub.html +0 -7
  488. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/search_interstitial.html +0 -19
  489. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_available.json +0 -1
  490. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_snapshot.html +0 -1128
  491. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_fetch_chain.py +0 -77
  492. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +0 -86
  493. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +0 -279
  494. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate.py +0 -252
  495. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate_validators.py +0 -78
@@ -7,12 +7,16 @@
7
7
  // No model, no network.
8
8
 
9
9
  import { existsSync, readFileSync } from "node:fs"
10
- import { join, resolve, sep } from "node:path"
10
+ import { resolve, sep } from "node:path"
11
11
 
12
- import { sha256Of } from "./cli-support.mjs"
13
12
  import { sameValue, unitKey } from "./fidelity-binding.mjs"
13
+ import { readCsv } from "./fidelity-csv.mjs"
14
14
  import { claimChecks } from "./fidelity-claims.mjs"
15
+ import { sourceChecks } from "./fidelity-sources.mjs"
16
+ import { convertedHalfFits, followsOriginal, isDerivedRow, originalProblem, renderLocator, staleRender, textAfter } from "./fidelity-originals.mjs"
17
+ import { snapshotCause, unreadableSnapshots } from "./fidelity-snapshot-readability.mjs"
15
18
  import { STRUCTURAL, clip, traceChecks } from "./fidelity-trace.mjs"
19
+ import { fillEntriesByNode, valueUnitProblem } from "./fidelity-value-unit.mjs"
16
20
  import { parseHtml, proseText } from "./html-lite.mjs"
17
21
  import { parseNumeral } from "./numerals.mjs"
18
22
  import { div, fromDecimal, sub } from "./rational.mjs"
@@ -45,11 +49,14 @@ function quoteProblem(row, shown, selector) {
45
49
  return bare(shown) === bare(want) ? null : ["quote_not_verbatim", `"${clip(shown)}" is not the ${selector} selection of ${row.fact_id}: "${clip(want)}"`]
46
50
  }
47
51
 
48
- function factProblem(row, shown, attrs = {}) {
52
+ function factProblem(row, shown, attrs = {}, rows = new Map()) {
53
+ const original = originalProblem(row, shown, rows, attrs["data-fact-mode"] === "value")
54
+ if (original) return original
49
55
  if (attrs["data-unit"] !== undefined && unitKey(attrs["data-unit"]) !== unitKey(row.unit)) return ["unit_mismatch", `data-unit "${attrs["data-unit"]}" but ${row.fact_id} is in "${row.unit}"`]
50
56
  if (attrs.value !== undefined && !sameValue(attrs.value, row.value)) return ["value_mismatch", `value="${attrs.value}" but ${row.fact_id} is ${row.value}`]
57
+ if (convertedHalfFits(row, shown)) return null
51
58
  const text = normalizeText(shown)
52
- const forms = [row.raw, row.render, row.value, row.unit && `${row.value} ${row.unit}`].filter(Boolean).map(normalizeText)
59
+ const forms = [...(["converted", "derived"].includes(row.kind) ? [] : [row.raw, row.render]), row.value, row.unit && `${row.value} ${row.unit}`].filter(Boolean).map(normalizeText)
53
60
  if (forms.some((form) => text === form || (text.endsWith(` ${form}`) && !/\d/.test(text.slice(0, -form.length))))) return null
54
61
  const parsed = parseNumeral(text)
55
62
  if (!parsed) return unitKey(text) === unitKey(row.unit) ? null : ["value_mismatch", `"${clip(shown)}" shows no value of ${row.fact_id} (${row.value})`]
@@ -73,16 +80,6 @@ export function exportRows(ledger) {
73
80
  return (ledger?.rows ?? []).filter((row) => row.status === "active" && row.kind !== "quote" && row.value !== null).map((row) => ({ fact_id: row.fact_id, label: row.label, plot_value: row.value, unit: row.unit ?? "" }))
74
81
  }
75
82
 
76
- function readCsv(path) {
77
- if (!existsSync(path)) return null
78
- const lines = readFileSync(path, "utf8").split(/\r?\n/).filter((line) => line.trim() !== "")
79
- const cells = (line) => line.split(",").map((cell) => cell.trim().replace(/^"(.*)"$/, "$1"))
80
- const head = cells(lines[0] ?? "")
81
- const [id, value, unit] = ["fact_id", "plot_value", "unit"].map((name) => head.indexOf(name))
82
- if (id < 0 || value < 0 || unit < 0 || lines.length < 2) return null
83
- return lines.slice(1).map((line) => cells(line)).map((c) => ({ fact_id: c[id], plot_value: c[value], unit: c[unit] ?? "" }))
84
- }
85
-
86
83
  const walk = (node) => node.children.filter((child) => child.tag !== "#text").flatMap((child) => [child, ...walk(child)])
87
84
 
88
85
  /** SVG text nodes of a chart: data labels, or ticks with their declared grid. */
@@ -140,46 +137,57 @@ function chartProblem(ctx, fig, labels, ledger) {
140
137
  * @param {object} ctx buildContext(...) from gates-static.mjs
141
138
  * @param {{ rows: object[] }} ledger
142
139
  * @param {{ fact_id: string, rendered: string, selector?: string }[] | null} fillMap Markdown fill map, or null
143
- * @param {string | null} sourcesDir directory of <S-id>/{text.txt, meta.json} snapshots
140
+ * @param {string | null} sourcesDir directory of <S-id>/ snapshots (source-view.mjs reads them)
144
141
  * @param {{ recompute?: (ledger: object, row: object) => { ok: boolean }, asOf?: string | null }} options
145
142
  */
146
143
  export function valueChecks(ctx, ledger, fillMap, sourcesDir, options = {}) {
147
144
  const out = []
148
- const flag = (code, node, message) => out.push(ctx.defect(code, node, message))
145
+ const flag = (code, node, message, extra = {}) => out.push({ ...ctx.defect(code, node, message), ...extra })
149
146
  const rows = new Map((ledger?.rows ?? []).map((row) => [row.fact_id, row]))
150
147
  const cited = new Map()
148
+ const fills = (fillMap ?? []).map((entry) => entry.rendered)
151
149
  const check = (id, node, problemOf) => {
152
150
  const row = rows.get(id)
153
151
  if (!row) return flag("unresolved_placeholder", node, `${id} names no ledger row`)
154
152
  if (!cited.has(id)) cited.set(id, node)
155
153
  const problem = problemOf(row)
156
- if (problem) flag(problem[0], node, problem[1])
154
+ if (problem) out.push({ ...ctx.defect(problem[0], node, problem[1]), ...(problem[2] ? { part: problem[2] } : {}) })
157
155
  }
158
156
  for (const node of ctx.all.filter((cur) => cur.attrs["data-fact"] !== undefined)) {
159
157
  const selector = node.attrs["data-quote-sel"] ?? "full"
160
- check(node.attrs["data-fact"], node, (row) => (row.kind === "quote" ? quoteProblem(row, withoutFooter(node), selector) : factProblem(row, proseText(node), node.attrs)))
158
+ const glued = (row) => isDerivedRow(row) && node.attrs["data-fact-mode"] !== "value" && followsOriginal(row, rows, fills)(textAfter(node)) ? ["value_mismatch", `another original follows ${row.fact_id}: "${clip(textAfter(node))}"`, "original"] : null
159
+ check(node.attrs["data-fact"], node, (row) => (row.kind === "quote" ? quoteProblem(row, withoutFooter(node), selector) : (factProblem(row, proseText(node), node.attrs, rows) ?? glued(row))))
161
160
  }
162
161
  const docText = bare(proseText(ctx.body))
163
162
  for (const entry of fillMap ?? []) {
164
163
  check(entry.fact_id, ctx.body, (row) => {
165
- const problem = row.kind === "quote" ? quoteProblem(row, entry.rendered, entry.selector ?? "full") : factProblem(row, entry.rendered)
166
- if (problem || docText.includes(bare(entry.rendered))) return problem
167
- return [row.kind === "quote" ? "quote_not_verbatim" : "value_mismatch", `filled text "${clip(entry.rendered)}" of ${row.fact_id} is no longer in the report`]
164
+ const problem = row.kind === "quote" ? quoteProblem(row, entry.rendered, entry.selector ?? "full") : factProblem(row, entry.rendered, { "data-fact-mode": entry.mode }, rows)
165
+ const stale = isDerivedRow(row) ? staleRender(row, entry.rendered, proseText(ctx.body), followsOriginal(row, rows, fills)) : docText.includes(bare(entry.rendered)) ? null : "gone"
166
+ if (problem || stale === null) return problem
167
+ return [row.kind === "quote" ? "quote_not_verbatim" : "value_mismatch", `filled text "${clip(entry.rendered)}" of ${row.fact_id} is no longer in the report${stale === "original" ? " (its original was edited)" : ""}`, ...(stale === "original" ? ["original"] : [])]
168
168
  })
169
169
  }
170
170
  rowChecks(ctx, ledger, rows, cited, options, flag)
171
+ // options.fillEntries: the edition's fill-map entries whose element is in the checked HTML (value mode, fill's unit_bound).
172
+ const facts = ctx.all.filter((cur) => cur.attrs["data-fact"] !== undefined)
173
+ const entries = fillEntriesByNode(facts, options.fillEntries)
174
+ for (const node of facts) {
175
+ const row = rows.get(node.attrs["data-fact"])
176
+ const problem = row && row.kind !== "quote" && !factProblem(row, proseText(node), node.attrs, rows) && valueUnitProblem(node, row, entries.get(node))
177
+ if (problem) flag(problem[0], node, problem[1])
178
+ }
171
179
  for (const tr of ctx.all.filter((node) => node.tag === "tr" && node.attrs["data-row-kind"] !== "comparison")) {
172
180
  const groups = new Set(ctx.util.descendants(tr).map((node) => rows.get(node.attrs["data-fact"])?.observation_id).filter(Boolean))
173
181
  if (groups.size > 1) flag("join_integrity_fail", tr, `one table row joins observations ${[...groups].join(", ")}`)
174
182
  }
175
- const meta = sourceChecks(ctx, rows, cited, sourcesDir, flag)
183
+ const meta = sourceChecks(ctx, rows, cited, sourcesDir, flag, unreadableSnapshots(ctx, rows, cited, sourcesDir, out))
176
184
  claimChecks(ctx, rows, (row) => provenance(sourcesDir, meta, row), flag)
177
185
  const charts = chartFigures(ctx)
178
186
  for (const { node, scope } of ctx.elements) {
179
187
  const kind = ctx.kindOf(scope)
180
188
  if ((node.tag === "q" || node.tag === "blockquote") && node.attrs["data-fact"] === undefined && kind !== "sources" && kind !== "toc") flag("quote_unbound", node, `<${node.tag}> with no Q row: "${clip(proseText(node))}"`)
181
189
  }
182
- traceChecks(ctx, charts, fillMap, flag, fillSiteFits(rows))
190
+ traceChecks(ctx, charts, fillMap, flag, fillSiteFits(rows), renderLocator(rows, fillMap))
183
191
  for (const [fig, labels] of charts) {
184
192
  const problem = chartProblem(ctx, fig, labels, ledger)
185
193
  if (problem) flag("chart_value_untraceable", fig, problem)
@@ -190,16 +198,23 @@ export function valueChecks(ctx, ledger, fillMap, sourcesDir, options = {}) {
190
198
  function rowChecks(ctx, ledger, rows, cited, options, flag) {
191
199
  // An as-of of "latest ..." or "... per snapshot" names no fixed revision, so quote_stale_version is skipped.
192
200
  const asOf = options.asOf && !/^latest\b|per snapshot/i.test(options.asOf) ? options.asOf : null
201
+ // A sentence-final period ("release v1.2.") is punctuation, not part of the revision token.
202
+ const target = asOf?.replace(/\.(?=\s|$)/gu, "")
193
203
  for (const [id, node] of cited) {
194
204
  const row = rows.get(id)
195
205
  if (row.fuzzy === true) flag("quote_fuzzy_match", node, `${id} was registered from a similarity match on an OCR-noisy snapshot`)
196
206
  const revision = typeof row.revision === "string" ? row.revision : (row.revision?.id ?? row.revision?.tag ?? null)
197
- if (asOf && revision && !asOf.includes(revision)) flag("quote_stale_version", node, `${id} is from revision ${revision}; the brief targets "${asOf}"`)
207
+ const revisionPattern = revision && new RegExp(`(?<![\\w.-])${revision.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w.-])`, "u")
208
+ if (asOf && revisionPattern && !revisionPattern.test(target)) flag("quote_stale_version", node, `${id} is from revision ${revision}; the brief targets "${asOf}"`)
198
209
  if ((row.kind === "derived" || row.kind === "converted") && options.recompute?.(ledger, row)?.ok === false) flag("value_mismatch", node, `${id} does not recompute from its operands`)
199
210
  const window = row.snapshot_window
200
211
  if (window && typeof window === "object" && window.start && window.end) {
201
212
  const [start, end] = [Date.parse(window.start), Date.parse(window.end)]
202
- const outside = (row.operands ?? []).find((op) => !(Date.parse(rows.get(op)?.observed_at) >= start && Date.parse(rows.get(op)?.observed_at) < end))
213
+ const [absent, present] = [(row.operands ?? []).filter((op) => !rows.has(op)), (row.operands ?? []).filter((op) => rows.has(op))]
214
+ for (const op of absent) flag("operand_missing", node, `operand ${op} of ${id} is not in the fact ledger`)
215
+ const missing = present.filter((op) => !rows.get(op).observed_at)
216
+ for (const op of missing) flag("operand_timestamp_missing", node, `operand ${op} of ${id} has no observed_at timestamp`)
217
+ const outside = present.find((op) => !missing.includes(op) && !(Date.parse(rows.get(op).observed_at) >= start && Date.parse(rows.get(op).observed_at) < end))
203
218
  if (outside) flag("snapshot_mismatch", node, `operand ${outside} of ${id} was observed outside ${window.start} to ${window.end}`)
204
219
  }
205
220
  }
@@ -213,17 +228,3 @@ function provenance(sourcesDir, meta, row) {
213
228
  const path = resolve(dir, artifact)
214
229
  return path.startsWith(`${dir}${sep}`) && existsSync(path) ? meta.get(row.source_id)?.kind : undefined
215
230
  }
216
-
217
- function sourceChecks(ctx, rows, cited, sourcesDir, flag) {
218
- const ids = new Set([...cited.keys()].map((id) => rows.get(id)?.source_id).filter(Boolean))
219
- for (const match of proseText(ctx.body).matchAll(/\[S(\d+)\]/g)) ids.add(`S${match[1]}`)
220
- const meta = new Map()
221
- for (const id of sourcesDir ? ids : []) {
222
- const [metaPath, textPath] = [join(sourcesDir, id, "meta.json"), join(sourcesDir, id, "text.txt")]
223
- if (!existsSync(metaPath) || !existsSync(textPath)) continue
224
- meta.set(id, JSON.parse(readFileSync(metaPath, "utf8")))
225
- const actual = sha256Of(readFileSync(textPath))
226
- if (meta.get(id).sha256 !== actual) flag("snapshot_hash_mismatch", ctx.body, `${id}/text.txt changed after its snapshot was pinned`)
227
- }
228
- return meta
229
- }
@@ -1,36 +1,15 @@
1
+ /// <reference types="bun-types" />
1
2
  import { describe, expect, test } from "bun:test"
2
- import { cpSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"
3
+ import { cpSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"
3
4
  import { tmpdir } from "node:os"
4
5
  import { join } from "node:path"
5
6
 
6
- import { exportRows, valueChecks } from "./fidelity-values.mjs"
7
- import { buildContext } from "./gates-static.mjs"
8
- import { parseHtml } from "./html-lite.mjs"
7
+ import { exportRows } from "./fidelity-values.mjs"
9
8
  import { similarity } from "./text-similarity.mjs"
9
+ import { LEDGER, SOURCES, ledgerWith, run, type Defect, type Row } from "./tests/fidelity-values-support"
10
10
 
11
- type Defect = { code: string; severity: string; selector: string; line: number; message: string; hint: string }
12
- type Row = Record<string, unknown> & { fact_id: string }
13
- type Ledger = { schemaVersion: number; rows: Row[]; events: unknown[] }
14
- type Options = { ledger?: Ledger; fillMap?: unknown[] | null; sourcesDir?: string | null; baseDir?: string; recompute?: (ledger: Ledger, row: Row) => { ok: boolean }; asOf?: string }
15
-
16
- const FIX = join(import.meta.dir, "tests", "fixtures", "fidelity")
17
- const SOURCES = join(FIX, "sources")
18
- const LEDGER: Ledger = JSON.parse(readFileSync(join(FIX, "ledger-verify.json"), "utf8"))
19
11
  const FULL = "we estimate completion no earlier than 2027"
20
12
 
21
- function ledgerWith(id: string, patch: Record<string, unknown>): Ledger {
22
- const copy = structuredClone(LEDGER)
23
- copy.rows = copy.rows.map((row) => (row.fact_id === id ? { ...row, ...patch } : row))
24
- return copy
25
- }
26
-
27
- function run(body: string, options: Options = {}): Defect[] {
28
- const html = /<h2/.test(body) ? body : `<h2>Findings</h2>\n${body}`
29
- const ctx = buildContext(parseHtml(html), { baseDir: options.baseDir ?? FIX })
30
- const sourcesDir = options.sourcesDir === undefined ? SOURCES : options.sourcesDir
31
- return valueChecks(ctx, options.ledger ?? LEDGER, options.fillMap ?? null, sourcesDir, options) as Defect[]
32
- }
33
-
34
13
  const codes = (defects: Defect[]) => defects.map((defect) => defect.code)
35
14
  const fact = (id: string, text: string, unit = "") => `<data class="fact" data-fact="${id}"${unit ? ` data-unit="${unit}"` : ""}>${text}</data>`
36
15
  const quote = (sel: string, text: string) => `<p>They wrote <q data-fact="Q1" data-quote-sel="${sel}">${text}</q> [S4].</p>`
@@ -53,8 +32,8 @@ describe("untraceable numbers", () => {
53
32
  expect(codes(run("<h2>Appendix</h2><p>Revenue grew 12% [S4].</p>"))).toEqual(["untraceable_number"])
54
33
  })
55
34
 
56
- test("#given the same sentence under Sources #when checked #then it is out of scope", () => {
57
- expect(run("<h2>Sources</h2><p>Revenue grew 12% [S4].</p>")).toEqual([])
35
+ test("#given the same sentence under Sources #when checked #then prose still needs a ledger row", () => {
36
+ expect(codes(run("<h2>Sources</h2><p>Revenue grew 12% [S4].</p>"))).toEqual(["untraceable_number"])
58
37
  })
59
38
 
60
39
  test("#given a date in prose #when checked #then dates are in scope", () => {
@@ -218,14 +197,14 @@ describe("verification claims are structural (data-claim-kind)", () => {
218
197
  `<p>The bridge, which ran across the river, spans ${fact("F1", "up to 320 m", "m")} [S4].</p>`,
219
198
  `<p>The span is ${fact("F1", "up to 320 m", "m")}, as confirmed with the annual inspection report [S4].</p>`,
220
199
  `<p>Clinical tests confirmed the span of ${fact("F1", "up to 320 m", "m")} [S4].</p>`,
221
- `<p>확인된 길이는 ${fact("F1", "최대 320 m", "m")} [S4].</p>`,
200
+ `<p>\uD655\uC778\uB41C \uAE38\uC774\uB294 ${fact("F1", "\uCD5C\uB300 320 m", "m")} [S4].</p>`,
222
201
  ]
223
202
  for (const body of quiet) {
224
203
  test(`#given findings prose ${JSON.stringify(body.replace(/<[^>]+>/g, ""))} #when checked #then no warning and no major`, () => expect(run(body)).toEqual([]))
225
204
  }
226
205
 
227
206
  test("#given an UNMARKED Korean run-and-check sentence #when checked #then verification_claim_unmarked minor only", () => {
228
- expect(severities(run(`<p>우리가 실행해 확인한 길이는 ${fact("F1", "최대 320 m", "m")} [S4].</p>`))).toEqual(["verification_claim_unmarked:minor"])
207
+ expect(severities(run(`<p>\uC6B0\uB9AC\uAC00 \uC2E4\uD589\uD574 \uD655\uC778\uD55C \uAE38\uC774\uB294 ${fact("F1", "\uCD5C\uB300 320 m", "m")} [S4].</p>`))).toEqual(["verification_claim_unmarked:minor"])
229
208
  })
230
209
 
231
210
  test("#given the same run-and-check sentence MARKED verified on a command row #when checked #then no warning", () => {
@@ -20,11 +20,13 @@ import { parseHtml } from "./html-lite.mjs"
20
20
 
21
21
  /** Every code this gate can raise. */
22
22
  export const FIDELITY_GATE_CODES = Object.freeze([
23
+ "unit_unbound",
23
24
  "fidelity_interview_incomplete", "unresolved_placeholder", "value_mismatch", "unit_mismatch", "quote_not_verbatim",
24
25
  "quote_unbound", "claim_status_invalid", "snapshot_hash_mismatch", "chart_value_untraceable", "untraceable_number",
25
26
  "unsupported_verification", "conflicting_readings", "hedge_dropped", "direction_flipped", "causality_added",
26
- "qualifier_dropped", "unbound_sentence", "snapshot_mismatch", "join_integrity_fail", "quote_stale_version",
27
+ "qualifier_dropped", "unbound_sentence", "snapshot_mismatch", "operand_timestamp_missing", "operand_missing", "join_integrity_fail", "quote_stale_version",
27
28
  "quote_fuzzy_match", "name_outside_quote", "scope_widened_warn", "claim_kind_mismatch", "verification_claim_unmarked",
29
+ "snapshot_not_raw", "relocated", "quote_converted_only",
28
30
  ])
29
31
  const RANK = { blocker: 0, major: 1, minor: 2 }
30
32
 
@@ -40,8 +42,8 @@ export function deliveryBlockReason(defect) {
40
42
  }
41
43
 
42
44
  function withIntegrity(defect) {
43
- const { code, severity, selector, line, message, hint } = defect
44
- return { code, severity, integrity: DEFECT_CODES[code].integrity, selector, line, message, hint }
45
+ const { code, severity, selector, line, message, hint, part, fact_id } = defect
46
+ return { code, severity, integrity: DEFECT_CODES[code].integrity, selector, line, message, hint, ...(part ? { part } : {}), ...(fact_id ? { fact_id } : {}) }
45
47
  }
46
48
 
47
49
  function compareDefects(a, b) {
@@ -93,17 +95,17 @@ function incomplete(parsed) {
93
95
 
94
96
  /**
95
97
  * @param {string} html the filled report
96
- * @param {{ ledger: { rows: object[] }, sourcesDir?: string | null, claims?: object | null, fillMap?: object[] | null,
98
+ * @param {{ ledger: { rows: object[] }, sourcesDir?: string | null, claims?: object | null, fillMap?: object[] | null, fillEntries?: object[] | null,
97
99
  * brief: string | object, baseDir?: string, recompute?: (ledger: object, row: object) => { ok: boolean } }} options
98
100
  * `brief` is the brief.md text or parseFidelityBlock output; `baseDir` resolves data-chart-source paths.
99
101
  * @returns {{ defects: object[], gatesRun: string[] }}
100
102
  */
101
- export function checkFidelity(html, { ledger, sourcesDir = null, claims = null, fillMap = null, brief, baseDir, recompute } = {}) {
103
+ export function checkFidelity(html, { ledger, sourcesDir = null, claims = null, fillMap = null, fillEntries = null, brief, baseDir, recompute } = {}) {
102
104
  const parsed = brief && typeof brief === "object" ? brief : parseFidelityBlock(String(brief ?? ""))
103
105
  if (!isLocked(parsed)) return { defects: [incomplete(parsed)], gatesRun: ["fidelity_interview_incomplete"] }
104
106
  const ctx = buildContext(parseHtml(html), { baseDir })
105
107
  const defects = [
106
- ...valueChecks(ctx, ledger, fillMap, sourcesDir, { recompute, asOf: parsed.fields["as of"] }),
108
+ ...valueChecks(ctx, ledger, fillMap, sourcesDir, { recompute, asOf: parsed.fields["as of"], fillEntries }),
107
109
  ...bindingChecks(ctx, claims, ledger),
108
110
  ]
109
111
  const pairs = fidelityPairs(ctx, ledger, sourcesDir)
@@ -0,0 +1,17 @@
1
+ import { describe, expect, test } from "bun:test"
2
+
3
+ import { buildContext, checkStatic } from "./gates-static.mjs"
4
+ import { parseHtml } from "./html-lite.mjs"
5
+
6
+ describe("HTML exact Closing headings", () => {
7
+ test.each([
8
+ ["Closing", "closing", false],
9
+ ["Closing the gap: GPU prices", null, true],
10
+ ] as const)("#given %s as an HTML heading #when checked #then closing scope matches the whole heading", (heading: string, kind: string | null, missing: boolean) => {
11
+ const root = parseHtml(`<h2>${heading}</h2><p>Prices fell 8%.</p>`)
12
+ expect(buildContext(root, {}).sections.map((section: { kind: string | null }) => section.kind)).toEqual([kind])
13
+ const { defects } = checkStatic(root, {})
14
+ expect(defects.some((defect) => "code" in defect && defect.code === "missing_closing_section")).toBe(missing)
15
+ expect(defects.some((defect) => "code" in defect && defect.code === "unsourced_number")).toBe(missing)
16
+ })
17
+ })
@@ -14,6 +14,7 @@ import { figureGates } from "./gates-figures.mjs"
14
14
  import { structureGates } from "./gates-structure.mjs"
15
15
  import { textGates } from "./gates-text.mjs"
16
16
  import { PROSE_TAGS, proseText, selectorPath } from "./html-lite.mjs"
17
+ import { sourcesClaims } from "./sources-citation.mjs"
17
18
 
18
19
  const CITATION_MARKER = /\[S\d+\]|\[Source\s*\d+\]|\[\uCD9C\uCC98[^\]]*\]|\[\^\d+\]/i
19
20
  const ANCHOR_HREF = /^#(?:s|src|source|ref|fn)/i
@@ -65,6 +66,11 @@ function hasCitation(node) {
65
66
  return CITATION_MARKER.test(proseText(node)) || descendants(node).some(isCitationAnchor)
66
67
  }
67
68
 
69
+ function proseScope(scope, owner) {
70
+ if (scope.section?.kind !== "sources" || !owner || sourcesClaims(owner).length === 0) return scope
71
+ return { ...scope, section: { ...scope.section, kind: null } }
72
+ }
73
+
68
74
  function toRegExp(pattern) {
69
75
  const re = pattern instanceof RegExp ? pattern : new RegExp(String(pattern), "i")
70
76
  return new RegExp(re.source, re.flags.replace(/[gy]/g, ""))
@@ -181,7 +187,10 @@ export function checkStatic(doc, options = {}) {
181
187
  continue
182
188
  }
183
189
  gatesRun.push(...gate.codes)
184
- defects.push(...gate.run(ctx))
190
+ const context = gate.codes.includes("unsourced_number")
191
+ ? { ...ctx, elements: ctx.elements.map(({ node, scope }) => ({ node, scope: proseScope(scope, node) })) }
192
+ : ctx
193
+ defects.push(...gate.run(context))
185
194
  }
186
195
  return { defects, gatesRun, gatesSkipped }
187
196
  }
@@ -23,7 +23,7 @@ const PROSE_SKIP = new Set(["code", "pre", "kbd", "samp", "script", "style", "sv
23
23
  const SAFE_ID = /^[A-Za-z_][\w-]*$/
24
24
 
25
25
  export const PROSE_TAGS = new Set(["p", "li", ...HEADINGS, "td", "th", "figcaption", "blockquote", "dd", "dt"])
26
- const PROSE_BREAKS = new Set([...P_CLOSERS, ...PROSE_TAGS, "br", "tr"])
26
+ export const PROSE_BREAKS = new Set([...P_CLOSERS, ...PROSE_TAGS, "br", "tr"])
27
27
 
28
28
  function makeNode(tag, attrs, parent, line) {
29
29
  return { tag, attrs, children: [], parent, line, text: "", raw: null }
@@ -130,6 +130,8 @@ export function parseHtml(source) {
130
130
  const flushText = (to) => {
131
131
  if (to > textStart) {
132
132
  const node = makeNode("#text", {}, top(), lineAt(starts, textStart))
133
+ node.source_start = textStart
134
+ node.source_end = to
133
135
  node.text = decodeEntities(src.slice(textStart, to))
134
136
  top().children.push(node)
135
137
  }
@@ -157,7 +159,7 @@ export function parseHtml(source) {
157
159
  const tag = readTag(src, lt + 2)
158
160
  if (tag.name !== "body" && tag.name !== "html") {
159
161
  for (let k = stack.length - 1; k > 0; k--) {
160
- if (stack[k].tag === tag.name) { stack.length = k; break }
162
+ if (stack[k].tag === tag.name) { stack[k].source_end = lt; stack.length = k; break }
161
163
  }
162
164
  }
163
165
  at = textStart = tag.end
@@ -169,6 +171,8 @@ export function parseHtml(source) {
169
171
  if (P_CLOSERS.has(tag.name)) closeWithin("p", P_SCOPE)
170
172
  if (tag.name === "li") closeWithin("li", LI_SCOPE)
171
173
  const node = makeNode(tag.name, tag.attrs, top(), lineAt(starts, lt))
174
+ node.source_start = tag.end
175
+ node.source_end = src.length
172
176
  top().children.push(node)
173
177
  if (tag.name === "body") sawBody = true
174
178
  if (tag.attrs.id) root.ids.set(tag.attrs.id, (root.ids.get(tag.attrs.id) ?? 0) + 1)
@@ -61,10 +61,10 @@ function scriptEnd(src, from) {
61
61
  function closeOf(src, from, name) {
62
62
  if (name === "script") return scriptEnd(src, from)
63
63
  if (RAW.has(name)) {
64
- const close = new RegExp(`</${name}(?=[\\s/>])[^>]*>`, "gi")
64
+ const close = new RegExp(`</${name}(?=[\\s/>])`, "gi")
65
65
  close.lastIndex = from
66
66
  const match = close.exec(src)
67
- return match ? match.index + match[0].length : src.length
67
+ return match ? readTag(src, match.index + 2).end : src.length
68
68
  }
69
69
  let depth = 0
70
70
  let stop = src.length
@@ -0,0 +1,140 @@
1
+ // html-text-map.mjs - the reader-visible text of a page, with a source map back into the markup.
2
+ //
3
+ // extractHtml(html) returns { root, text, blocks, starts, ends }: `text` is one line per block
4
+ // (prose blocks and table cells; whitespace collapsed, entities decoded) and text unit k came from
5
+ // html.slice(starts[k], ends[k]). Only text nodes are read: attribute values, CSS, scripts, and
6
+ // anything a reader never sees (head, script, style, svg, noscript, template, code-like elements,
7
+ // aria-hidden="true" and their descendants, and what the page's CSS hides: css-hiding.mjs)
8
+ // contribute nothing, so a locator over `text` can never land in markup. A synthetic separator
9
+ // (collapsed break, line feed) maps to an empty range at the previous character's end.
10
+ // Plain Node ESM, sibling imports only.
11
+
12
+ import { cssVisibility } from "./css-hiding.mjs"
13
+ import { decodeOne } from "./entities.mjs"
14
+ import { parseHtml, PROSE_BREAKS, PROSE_TAGS } from "./html-lite.mjs"
15
+
16
+ const SKIP = new Set(["head", "script", "style", "svg", "code", "pre", "kbd", "samp", "template", "noscript"])
17
+ const INLINE = new Set([
18
+ "a", "abbr", "b", "bdi", "bdo", "cite", "data", "del", "dfn", "em", "font", "i", "ins", "label", "mark", "q", "s",
19
+ "small", "span", "strong", "sub", "sup", "time", "u", "var",
20
+ ])
21
+ const SPACE = /\s/
22
+
23
+ /** null when a reader never sees the element or anything inside it, else whether its own text shows (css: cssVisibility). */
24
+ function shownState(node, css, parentShown) {
25
+ if (SKIP.has(node.tag) || String(node.attrs?.["aria-hidden"] ?? "").trim().toLowerCase() === "true") return null
26
+ return css(node, parentShown)
27
+ }
28
+
29
+ function newLine() {
30
+ return { node: null, text: "", starts: [], ends: [], space: null }
31
+ }
32
+
33
+ function put(line, unit, start, end) {
34
+ if (SPACE.test(unit)) {
35
+ if (line.text !== "" && line.space === null) line.space = [start, end]
36
+ return
37
+ }
38
+ if (line.space !== null) {
39
+ line.text += " "
40
+ line.starts.push(line.space[0])
41
+ line.ends.push(line.space[1])
42
+ line.space = null
43
+ }
44
+ line.text += unit
45
+ line.starts.push(start)
46
+ line.ends.push(end)
47
+ }
48
+
49
+ /** A collapsible break with no source character of its own. */
50
+ function softBreak(line) {
51
+ if (line.text !== "" && line.space === null) {
52
+ const at = line.ends[line.ends.length - 1]
53
+ line.space = [at, at]
54
+ }
55
+ }
56
+
57
+ /** Append a text node's source range, decoding entities exactly as decodeEntities does. */
58
+ function putText(line, src, from, to) {
59
+ for (let at = from; at < to; ) {
60
+ if (src[at] === "&") {
61
+ const semi = src.indexOf(";", at + 1)
62
+ const decoded = semi !== -1 && semi < to && semi - at <= 12 ? decodeOne(src.slice(at + 1, semi)) : null
63
+ if (decoded !== null) {
64
+ for (let k = 0; k < decoded.length; k++) put(line, decoded[k], at, semi + 1)
65
+ at = semi + 1
66
+ continue
67
+ }
68
+ }
69
+ put(line, src[at], at, at + 1)
70
+ at++
71
+ }
72
+ }
73
+
74
+ function putProse(line, src, node, css, shown) {
75
+ for (const child of node.children) {
76
+ if (child.tag === "#text") {
77
+ if (shown) putText(line, src, child.source_start, child.source_end)
78
+ continue
79
+ }
80
+ const state = shownState(child, css, shown)
81
+ if (state === null) continue
82
+ const breaks = PROSE_BREAKS.has(child.tag)
83
+ if (breaks) softBreak(line)
84
+ putProse(line, src, child, css, state)
85
+ if (breaks) softBreak(line)
86
+ }
87
+ }
88
+
89
+ /** See the file comment. blocks: [{ node, start, end }] with text.slice(start, end) one block line. */
90
+ export function extractHtml(html) {
91
+ const src = String(html)
92
+ const root = parseHtml(src)
93
+ const css = cssVisibility(root)
94
+ const lines = []
95
+ let line = newLine()
96
+ const flush = (node) => {
97
+ if (line.text !== "") lines.push({ ...line, node })
98
+ line = newLine()
99
+ }
100
+ const walk = (node, shown) => {
101
+ for (const child of node.children) {
102
+ if (child.tag === "#text") {
103
+ if (shown) putText(line, src, child.source_start, child.source_end)
104
+ continue
105
+ }
106
+ const state = shownState(child, css, shown)
107
+ if (state === null) continue
108
+ if (child.tag === "br") flush(node)
109
+ else if (INLINE.has(child.tag)) walk(child, state)
110
+ else if (PROSE_TAGS.has(child.tag)) {
111
+ flush(node)
112
+ putProse(line, src, child, css, state)
113
+ flush(child)
114
+ } else {
115
+ flush(node)
116
+ walk(child, state)
117
+ flush(child)
118
+ }
119
+ }
120
+ }
121
+ walk(root, true)
122
+ flush(root)
123
+ const blocks = []
124
+ const starts = []
125
+ const ends = []
126
+ let text = ""
127
+ for (const entry of lines) {
128
+ if (text !== "") {
129
+ const at = ends[ends.length - 1]
130
+ text += "\n"
131
+ starts.push(at)
132
+ ends.push(at)
133
+ }
134
+ blocks.push({ node: entry.node, start: text.length, end: text.length + entry.text.length })
135
+ text += entry.text
136
+ starts.push(...entry.starts)
137
+ ends.push(...entry.ends)
138
+ }
139
+ return { root, text, blocks, starts, ends }
140
+ }