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
@@ -0,0 +1,158 @@
1
+ // css-hiding.mjs - whether CSS hides an element of a page from every reader, from its inline style and
2
+ // the page's own <style> rules (external stylesheets and scripts are not evaluated).
3
+ //
4
+ // Text counts as reader-visible when it is visible under at least one condition. So a rule can hide
5
+ // only when it certainly applies: it sits outside every at-rule except @layer (and @media all), its
6
+ // <style> element applies everywhere (no media other than all, CSS type, not in <noscript>), and its
7
+ // selector is one tag (case-insensitive), one .class or one #id (case-sensitive). A rule that only
8
+ // might apply (inside @media, @supports, @container, @scope, @starting-style, @document, @keyframes,
9
+ // nested in another style rule, or with a compound, combinator, attribute or pseudo-class selector)
10
+ // never hides, but it can make text visible when its subject could match and it outranks the hiding
11
+ // declaration in the cascade (origin and importance, inline style, layer order, specificity, order).
12
+ // Hiding properties: display:none and content-visibility:hidden remove the subtree;
13
+ // visibility:hidden|collapse is inherited and a descendant's visibility:visible shows it again.
14
+ // The hidden attribute (except hidden="until-found") and a closed <dialog> are the user-agent
15
+ // display:none, which any author display declaration overrides. opacity, clip, zero size, font-size,
16
+ // off-screen positions and colors do not hide: the text still renders or is read aloud.
17
+ // Plain Node ESM, sibling imports only.
18
+
19
+ import { scanRules } from "./css-lite.mjs"
20
+
21
+ const PROPS = new Set(["display", "visibility", "content-visibility"])
22
+ const INHERITED = new Set(["inherit", "unset", "revert", "revert-layer"])
23
+ const IMPORTANT = /!\s*important\s*$/i
24
+ const SIMPLE = /^(?:[.#][^\s.#:[\]>+~,()*"'\\]+|[a-z][a-z0-9-]*)$/iu
25
+ const PSEUDO_ELEMENT = /::|:(?:before|after|first-line|first-letter)\b/i
26
+ const NAME = "[^\\s.#:[\\]>+~,()*\"'\\\\]+"
27
+ const STYLE_IN_SVG = /<style\b[^>]*>([\s\S]*?)<\/style\s*>/gi
28
+
29
+ /** [tier (0 user agent, 1 author, 2 author !important), inline, layer, specificity, order]: higher wins. */
30
+ const outranks = (a, b) => {
31
+ for (let k = 0; k < a.length; k++) if (a[k] !== b[k]) return a[k] > b[k]
32
+ return false
33
+ }
34
+
35
+ function specificity(selector) {
36
+ const core = selector.replace(/\[[^\]]*\]/g, " [] ")
37
+ const ids = core.match(new RegExp(`#${NAME}`, "gu"))?.length ?? 0
38
+ const classes = (core.match(new RegExp(`\\.${NAME}`, "gu"))?.length ?? 0) + (core.match(/\[\]|:(?!:)[\w-]+/g)?.length ?? 0)
39
+ const tags = core.match(/(?:^|[\s>+~(])[a-z][\w-]*/gi)?.length ?? 0
40
+ return ids * 10000 + classes * 100 + tags
41
+ }
42
+
43
+ /** The subject compound of a selector: its tag, classes and ids (attribute and pseudo-class parts dropped). */
44
+ function subject(selector) {
45
+ const compound = selector.trim().match(/(?:[^\s>+~[\]()]|\[[^\]]*\]|\([^)]*\))+$/)?.[0] ?? ""
46
+ const core = compound.replace(/\([^)]*\)/g, "").replace(/\[[^\]]*\]/g, "").replace(/:+[\w-]+/g, "")
47
+ const tag = core.match(/^[a-z][a-z0-9-]*/i)?.[0].toLowerCase() ?? null
48
+ const classes = [...core.matchAll(new RegExp(`\\.(${NAME})`, "gu"))].map((match) => match[1])
49
+ const ids = [...core.matchAll(new RegExp(`#(${NAME})`, "gu"))].map((match) => match[1])
50
+ return { tag, classes, ids, key: ids[0] ? `#${ids[0]}` : classes[0] ? `.${classes[0]}` : tag ?? "*" }
51
+ }
52
+
53
+ const classList = (node) => String(node.attrs?.class ?? "").split(/[ \t\n\f\r]+/).filter(Boolean)
54
+
55
+ function matches(sub, node) {
56
+ if (sub.tag !== null && sub.tag !== node.tag) return false
57
+ const names = classList(node)
58
+ return sub.classes.every((name) => names.includes(name)) && sub.ids.every((id) => id === node.attrs?.id)
59
+ }
60
+
61
+ /** Whether every enclosing at-rule leaves the rule unconditional (@layer, @media all), its layer name, and keyframe steps. */
62
+ function context(atRule) {
63
+ const entries = atRule ? atRule.split(/\s(?=@)/) : []
64
+ const layers = entries.filter((entry) => /^@layer\b/i.test(entry)).map((entry) => entry.slice(6).trim())
65
+ const conditional = entries.some((entry) => !/^@layer\b/i.test(entry) && !/^@media(?:\s+all)?\s*$/i.test(entry))
66
+ return { conditional, layer: layers.length ? layers.join(".") : null, keyframe: entries.some((entry) => /^@(?:-[a-z]+-)?keyframes\b/i.test(entry)) }
67
+ }
68
+
69
+ const declaration = (value) => ({ value: value.replace(IMPORTANT, "").trim().toLowerCase(), important: IMPORTANT.test(value) })
70
+
71
+ /** Every <style> text that applies to the page, with whether it applies only under a condition. */
72
+ function sheets(root) {
73
+ const found = []
74
+ const visit = (node, conditional) => {
75
+ if (node.tag === "template") return
76
+ const inner = conditional || node.tag === "noscript"
77
+ if (node.tag === "style") {
78
+ const type = String(node.attrs?.type ?? "").trim().toLowerCase()
79
+ const media = String(node.attrs?.media ?? "").trim().toLowerCase()
80
+ if (type === "" || type === "text/css") found.push({ css: String(node.raw ?? ""), conditional: inner || (media !== "" && media !== "all") })
81
+ }
82
+ if (node.tag === "svg") for (const match of String(node.raw ?? "").matchAll(STYLE_IN_SVG)) found.push({ css: match[1], conditional: inner })
83
+ for (const child of node.children ?? []) visit(child, inner)
84
+ }
85
+ visit(root, false)
86
+ return found
87
+ }
88
+
89
+ /** Index every rule declaring display, visibility or content-visibility by its subject key. */
90
+ function indexRules(root) {
91
+ const index = new Map()
92
+ const layers = []
93
+ const layerRank = (name, important) => {
94
+ if (name === null) return important ? -1 : 1e6
95
+ if (!layers.includes(name)) layers.push(name)
96
+ return important ? 1e6 - layers.indexOf(name) : layers.indexOf(name)
97
+ }
98
+ let order = 0
99
+ for (const sheet of sheets(root)) {
100
+ const css = sheet.css.replace(/<!--|-->/g, " ")
101
+ for (const match of css.replace(/\/\*[\s\S]*?(?:\*\/|$)/g, "").matchAll(/@layer\s+([^{;]*)[{;]/gi)) {
102
+ for (const name of match[1].split(",").map((part) => part.trim())) if (name && !layers.includes(name)) layers.push(name)
103
+ }
104
+ for (const rule of scanRules(css)) {
105
+ const decls = rule.declarations.filter((entry) => PROPS.has(entry.prop))
106
+ if (!decls.length || rule.selector.startsWith("@")) continue
107
+ const { conditional, layer, keyframe } = context(rule.atRule)
108
+ if (keyframe) continue
109
+ for (const selector of rule.selector.split(/,(?![^()[\]]*[)\]])/).map((part) => part.trim())) {
110
+ if (!selector || PSEUDO_ELEMENT.test(selector)) continue
111
+ const sub = subject(selector)
112
+ const certain = !sheet.conditional && !conditional && !rule.nested && SIMPLE.test(selector)
113
+ const spec = specificity(selector)
114
+ for (const entry of decls) {
115
+ const { value, important } = declaration(entry.value)
116
+ const rank = [important ? 2 : 1, 0, layerRank(layer, important), spec, order++]
117
+ if (!index.has(sub.key)) index.set(sub.key, [])
118
+ index.get(sub.key).push({ sub, prop: entry.prop, value, rank, certain })
119
+ }
120
+ }
121
+ }
122
+ }
123
+ return index
124
+ }
125
+
126
+ /** The values a property can take on the element: the certain winner, plus every possible rule that outranks it. */
127
+ function possible(candidates, prop) {
128
+ let top = null
129
+ for (const entry of candidates) if (entry.prop === prop && entry.certain && (top === null || outranks(entry.rank, top.rank))) top = entry
130
+ const values = [top === null ? "unset" : top.value]
131
+ for (const entry of candidates) if (entry.prop === prop && !entry.certain && (top === null || outranks(entry.rank, top.rank))) values.push(entry.value)
132
+ return values
133
+ }
134
+
135
+ /**
136
+ * cssVisibility(root) -> (node, parentShown) => null when CSS removes the element and its subtree from every
137
+ * rendering, else whether its own text can be seen (visibility, inherited from parentShown).
138
+ */
139
+ export function cssVisibility(root) {
140
+ const index = indexRules(root)
141
+ return (node, parentShown) => {
142
+ const attrs = node.attrs ?? {}
143
+ const keys = ["*", node.tag, ...classList(node).map((name) => `.${name}`), attrs.id === undefined ? null : `#${attrs.id}`]
144
+ const candidates = []
145
+ for (const key of new Set(keys)) for (const entry of index.get(key) ?? []) if (matches(entry.sub, node)) candidates.push(entry)
146
+ const hiddenAttr = attrs.hidden !== undefined && String(attrs.hidden).trim().toLowerCase() !== "until-found"
147
+ if (hiddenAttr || (node.tag === "dialog" && attrs.open === undefined)) candidates.push({ prop: "display", value: "none", rank: [0, 0, 0, 0, -1], certain: true })
148
+ const inline = attrs.style ? (scanRules(`x{${attrs.style}}`)[0]?.declarations ?? []) : []
149
+ inline.forEach((entry, at) => {
150
+ if (!PROPS.has(entry.prop)) return
151
+ const { value, important } = declaration(entry.value)
152
+ candidates.push({ prop: entry.prop, value, rank: [important ? 2 : 1, 1, 0, 0, at], certain: true })
153
+ })
154
+ if (possible(candidates, "display").every((value) => value === "none")) return null
155
+ if (possible(candidates, "content-visibility").every((value) => value === "hidden")) return null
156
+ return possible(candidates, "visibility").some((value) => (INHERITED.has(value) ? parentShown : value !== "hidden" && value !== "collapse"))
157
+ }
158
+ }
@@ -57,8 +57,9 @@ function parseDeclarations(text) {
57
57
  }
58
58
 
59
59
  /**
60
- * @returns {{ selector: string, declarations: { prop: string, value: string }[], atRule?: string }[]}
61
- * atRule is the chain of enclosing grouping at-rule preludes joined by a space.
60
+ * @returns {{ selector: string, declarations: { prop: string, value: string }[], atRule?: string, nested?: true }[]}
61
+ * atRule is the chain of enclosing grouping at-rule preludes joined by a space; nested is true for a
62
+ * rule written inside another style rule (CSS nesting).
62
63
  */
63
64
  export function scanRules(css) {
64
65
  if (typeof css !== "string") return []
@@ -88,6 +89,7 @@ export function scanRules(css) {
88
89
  const atChain = stack.filter((entry) => entry.group).map((entry) => entry.prelude)
89
90
  const rule = { selector: frame.prelude, declarations: parseDeclarations(frame.body + buffer) }
90
91
  if (atChain.length) rule.atRule = atChain.join(" ")
92
+ if (stack.some((entry) => !entry.group)) rule.nested = true
91
93
  slots[frame.slot] = rule
92
94
  }
93
95
  buffer = ""
@@ -1,14 +1,23 @@
1
1
  // entities.mjs - HTML character reference decoding for html-lite.mjs.
2
2
  //
3
- // Supports a fixed set of named entities plus decimal and hex numeric references. Unknown or
4
- // malformed references stay verbatim. Single indexOf scan; plain Node ESM, no dependencies.
3
+ // Supports a fixed set of named entities (NAMED_ENTITIES) plus decimal and hex numeric references.
4
+ // Unknown or malformed references stay verbatim. Single indexOf scan; plain Node ESM, no dependencies.
5
5
 
6
+ // Beyond the common set, every named reference whose character a number reader treats specially is
7
+ // listed (signs, dashes, the space separators that group digits, and Markdown punctuation), so the
8
+ // conversion probe reads `&minus;5` and `1&thinsp;234` as a converter does.
6
9
  const NAMED_ENTITIES = {
7
10
  amp: "&", lt: "<", gt: ">", quot: '"', apos: "'", nbsp: "\u00a0", mdash: "\u2014", ndash: "\u2013",
8
11
  hellip: "\u2026", copy: "\u00a9", reg: "\u00ae", times: "\u00d7", middot: "\u00b7",
12
+ minus: "\u2212", hyphen: "\u2010", dash: "\u2010", NonBreakingSpace: "\u00a0", thinsp: "\u2009",
13
+ ThinSpace: "\u2009", numsp: "\u2007", puncsp: "\u2008", hairsp: "\u200a", VeryThinSpace: "\u200a",
14
+ ensp: "\u2002", emsp: "\u2003", emsp13: "\u2004", emsp14: "\u2005", MediumSpace: "\u205f",
15
+ comma: ",", period: ".", plus: "+", lowbar: "_", UnderBar: "_", ast: "*", midast: "*", grave: "`",
16
+ DiacriticalGrave: "`", lpar: "(", rpar: ")", lsqb: "[", lbrack: "[", rsqb: "]", rbrack: "]",
9
17
  }
10
18
 
11
- function decodeOne(body) {
19
+ /** One reference body (between `&` and `;`), or null when unsupported. */
20
+ export function decodeOne(body) {
12
21
  if (Object.hasOwn(NAMED_ENTITIES, body)) return NAMED_ENTITIES[body]
13
22
  if (body[0] !== "#") return null
14
23
  const hex = body[1] === "x" || body[1] === "X"
@@ -26,7 +35,7 @@ export function decodeEntities(text) {
26
35
  let at = source.indexOf("&")
27
36
  while (at !== -1) {
28
37
  const semi = source.indexOf(";", at + 1)
29
- const decoded = semi !== -1 && semi - at <= 12 ? decodeOne(source.slice(at + 1, semi)) : null
38
+ const decoded = semi !== -1 && semi - at <= 32 ? decodeOne(source.slice(at + 1, semi)) : null
30
39
  if (decoded !== null) {
31
40
  out += source.slice(last, at) + decoded
32
41
  last = semi + 1
@@ -13,10 +13,13 @@ import { dirname, extname, resolve } from "node:path"
13
13
 
14
14
  import { CliError, sha256Of, writeJsonAtomic } from "./cli-support.mjs"
15
15
  import { DEFECT_CODES } from "./contracts.mjs"
16
+ import { markdownAsHtml } from "./fact-commands-verify.mjs"
16
17
  import { FILL_FORMATS, fill } from "./fact-fill.mjs"
17
18
  import { readLedger, writeLedger } from "./fact-ledger.mjs"
18
19
  import { briefHashOf, fillPolicy, lockedBrief, policyErrors } from "./fidelity-policy.mjs"
19
20
  import { readState, writeState } from "./fidelity-state.mjs"
21
+ import { withBoundUnits } from "./fidelity-value-unit.mjs"
22
+ import { parseMarkdown } from "./md-lite.mjs"
20
23
  import { findPlaceholders } from "./placeholder.mjs"
21
24
  import { sessionDirOf } from "./source-commands.mjs"
22
25
 
@@ -28,6 +31,13 @@ export const FILL_INPUT_FORMAT_RULES = Object.freeze({
28
31
  export const FILL_INPUT_FORMATS = Object.freeze([...FILL_FORMATS, ...Object.keys(FILL_INPUT_FORMAT_RULES.aliases)])
29
32
  const fillFormatOf = (format) => FILL_INPUT_FORMAT_RULES.aliases[format] ?? format
30
33
 
34
+ /** Value-mode entries carry the unit fill saw bound to them (unit_bound), read off the edition verify will check. */
35
+ function boundUnits(out, entries, ledger, format) {
36
+ if (!entries.some((entry) => entry.mode === "value")) return entries
37
+ const html = fillFormatOf(format) === "html" ? out : parseMarkdown(out).problems.length === 0 ? markdownAsHtml(out, entries, ledger).html : null
38
+ return html === null ? entries : withBoundUnits(html, entries, new Map(ledger.rows.map((row) => [row.fact_id, row])))
39
+ }
40
+
31
41
  function readText(path) {
32
42
  try {
33
43
  return readFileSync(path, "utf8")
@@ -80,7 +90,7 @@ export function runFactFill(args) {
80
90
  const artifact = sha256Of(out)
81
91
  const map = `${resolve(flags.out)}.fill-map.json`
82
92
  const brief = briefHashOf(parsed)
83
- writeJsonAtomic(map, { schemaVersion: 1, format, artifact: resolve(flags.out), artifact_sha256: artifact, brief_sha256: brief, entries })
93
+ writeJsonAtomic(map, { schemaVersion: 1, format, artifact: resolve(flags.out), artifact_sha256: artifact, brief_sha256: brief, entries: boundUnits(out, entries, ledger, format) })
84
94
  const data = { out: resolve(flags.out), map, artifact_sha256: artifact, brief_sha256: brief, occurrences: entries.length, warnings }
85
95
  return { json: { command: "fact fill", status: "pass", exitCode: 0, defects: [], data }, summary: `fact fill: wrote ${flags.out} (${entries.length} pointer(s))`, exitCode: 0 }
86
96
  }
@@ -1,9 +1,10 @@
1
1
  // fact-commands-meaning.mjs - `fact pairs` and `fact meaning-check`, the script half of the
2
2
  // separate-session meaning verifier. fact pairs writes the (source, report sentence) pairs a
3
- // reader lane gets and nothing else (with --changed-since, only the pairs whose sentence polish
4
- // rewrote); it honours the brief's `meaning check` field and writes
3
+ // reader lane gets and nothing else (with --changed-since, only the pairs whose sentence changed
4
+ // since the pre-polish fill map's artifact, or all when those bytes are gone); it honours the brief's `meaning check` field and writes
5
5
  // `{ skipped: true }` when it is off. fact meaning-check ingests the readers' verdict files
6
- // (meaning-check.mjs), writes fidelity-meaning-defects.json (a fidelity record bound to the pairs'
6
+ // (meaning-check.mjs), writes the meaning record of one edition (fidelity-meaning-defects.<format>.json
7
+ // with --edition, else the legacy fidelity-meaning-defects.json; a fidelity record bound to the pairs'
7
8
  // artifact_sha256 and report, through writeFidelityRecord), and records the run in
8
9
  // fidelity-calibration.jsonl: a header with the promotion criterion on first write, then per run
9
10
  // one run line plus one line per flagged pair. The run id hashes the inputs only, so a rerun or a
@@ -15,13 +16,13 @@ import { existsSync, readFileSync } from "node:fs"
15
16
  import { dirname, join, resolve } from "node:path"
16
17
 
17
18
  import { CliError, parseArgs, readJsonFile, sha256Of, writeJsonAtomic, writeTextAtomic } from "./cli-support.mjs"
18
- import { countDefects } from "./contracts.mjs"
19
+ import { countDefects, DELIVERABLE_FORMATS } from "./contracts.mjs"
19
20
  import { ledgerFrom, markdownAsHtml, readEdition, sessionOf } from "./fact-commands-verify.mjs"
20
21
  import { pairsChangedSince } from "./fact-commands-polish.mjs"
21
22
  import { extractPairs, PAIR_KINDS } from "./fact-pairs.mjs"
22
23
  import { FIDELITY_INCOMPLETE } from "./fidelity-brief.mjs"
23
24
  import { briefPathOf, lockedBrief, meaningCheckEnabled } from "./fidelity-policy.mjs"
24
- import { MEANING_RECORD, writeFidelityRecord } from "./fidelity-records.mjs"
25
+ import { MEANING_RECORD, meaningRecordName, writeFidelityRecord } from "./fidelity-records.mjs"
25
26
  import { blocksDelivery } from "./gates-fidelity.mjs"
26
27
  import { ingestVerdicts, unverifiedDefects, VERDICT_SCHEMA } from "./meaning-check.mjs"
27
28
 
@@ -57,10 +58,12 @@ function runPairs(args) {
57
58
  const ledger = ledgerFrom(flags, session)
58
59
  const html = edition.format === "md" ? markdownAsHtml(edition.text, edition.entries, ledger).html : edition.text
59
60
  const all = extractPairs(html, ledger, claims)
60
- const pairs = flags["changed-since"] === undefined ? all : pairsChangedSince(all, flags["changed-since"], ledger, claims)
61
+ const since = flags["changed-since"] === undefined ? null : pairsChangedSince(all, flags["changed-since"], ledger, claims)
62
+ const pairs = since?.pairs ?? all
61
63
  const counts = { total: pairs.length, force_strong: pairs.filter((pair) => pair.force_strong).length }
62
64
  for (const kind of PAIR_KINDS) counts[kind] = pairs.filter((pair) => pair.kind === kind).length
63
- const record = { schemaVersion: 1, skipped: false, report: resolve(report), format: edition.format, artifact_sha256: sha256Of(edition.text), pairs_sha256: sha256Of(JSON.stringify(pairs)), counts, verdict_schema: VERDICT_SCHEMA, pairs }
65
+ const changed = since === null ? {} : { changed_since: { fill_map: since.fill_map, narrowed: since.narrowed } }
66
+ const record = { schemaVersion: 1, skipped: false, report: resolve(report), format: edition.format, artifact_sha256: sha256Of(edition.text), pairs_sha256: sha256Of(JSON.stringify(pairs)), counts, ...changed, verdict_schema: VERDICT_SCHEMA, pairs }
64
67
  writeJsonAtomic(out, record)
65
68
  const summary = `fact pairs: ${counts.total} pair(s), ${counts.force_strong} routed to the strong reader`
66
69
  return { json: { command: "fact pairs", status: "pass", exitCode: 0, data: { out, ...record, pairs: undefined } }, summary, exitCode: 0 }
@@ -124,8 +127,9 @@ function skippedRefusal(flags, parsed) {
124
127
 
125
128
  function runMeaningCheck(args) {
126
129
  const { flags } = args
127
- const usage = "usage: fact meaning-check --pairs pairs.json --verdicts cheap.json [--verdicts strong.json] [--dispute P<n> --reason TEXT] [--run-id ID] [--calibration F] [--brief brief.md] [--out F]"
130
+ const usage = "usage: fact meaning-check --pairs pairs.json --verdicts cheap.json [--verdicts strong.json] [--dispute P<n> --reason TEXT] [--run-id ID] [--calibration F] [--brief brief.md] [--edition FORMAT] [--out F]"
128
131
  if (flags.pairs === undefined || !Array.isArray(flags.verdicts)) throw new CliError(usage)
132
+ if (flags.edition !== undefined && !DELIVERABLE_FORMATS.includes(flags.edition)) throw new CliError(`--edition must be a deliverable format (${DELIVERABLE_FORMATS.join(", ")}), got ${flags.edition}`)
129
133
  const session = sessionOf(flags)
130
134
  const { parsed, refusal } = lockedBrief("fact meaning-check", flags)
131
135
  if (refusal) return { ...refusal, json: { ...refusal.json, error: FIDELITY_INCOMPLETE } }
@@ -146,7 +150,7 @@ function runMeaningCheck(args) {
146
150
  } catch (error) {
147
151
  throw new CliError(`invalid verdict input: ${error.message}`)
148
152
  }
149
- const out = resolve(flags.out ?? inSession(session, MEANING_RECORD, "--out"))
153
+ const out = resolve(flags.out ?? inSession(session, flags.edition === undefined ? MEANING_RECORD : meaningRecordName(flags.edition), "--out"))
150
154
  const calibration = resolve(flags.calibration ?? join(session ?? dirname(out), CALIBRATION_FILE))
151
155
  const runId = flags["run-id"] ?? sha256Of([pairsText, ...verdictTexts].join("\0")).slice(0, 16)
152
156
  const pairsSha = pairsDoc.pairs_sha256 ?? sha256Of(pairsText)
@@ -6,18 +6,24 @@
6
6
  // rejected (major) or unlocated (minor) edit leaves its region unchanged and is listed with its
7
7
  // index and reason so the orchestrator can re-send it; every rejection reason is printed verbatim, one
8
8
  // line per rejected edit (index and the first 60 characters of its before), in the summary and in
9
- // data.rejected_reasons. It exits 0 whenever it wrote the output.
9
+ // data.rejected_reasons. It exits 0 whenever it wrote the output; json.applied is false when
10
+ // any edit was rejected or unlocated, independently of data.applied's accepted-edit count.
11
+ // polish-apply never rewrites a fill map: it checks that the edition's map (fillMapOf) records the
12
+ // input's bytes, refusing otherwise, and writes `<out>.polished.fill-map.json` beside the output
13
+ // (artifact = the polished file, its hash, and polishedFrom = the fill map's path and sha256).
10
14
  // polish-diff is the whole-document backstop (polish-diff.mjs) between the pre-polish artifact
11
- // and any later copy; it exits 1 on a blocker. pairsChangedSince keeps only the pairs whose
12
- // sentence text (normalized) is not in the pre-polish artifact that a fill map names, so the
13
- // meaning readers re-check exactly what polish rewrote. No model, no network.
15
+ // and any later copy; it exits 1 on a blocker. pairsChangedSince always reads the pre-polish fill
16
+ // map (following a polished map's polishedFrom) and keeps only the pairs whose sentence text
17
+ // (normalized) is not in the artifact it names, so the meaning readers re-check exactly what
18
+ // changed; when that artifact no longer hashes to the map (polished in place, or edited) nothing
19
+ // can be ruled out and every pair is kept. No model, no network.
14
20
 
15
21
  import { existsSync, readFileSync } from "node:fs"
16
22
  import { join, resolve } from "node:path"
17
23
 
18
24
  import { CliError, parseArgs, readJsonFile, sha256Of, writeJsonAtomic, writeTextAtomic } from "./cli-support.mjs"
19
25
  import { countDefects, DEFECT_CODES } from "./contracts.mjs"
20
- import { editionFormatOf, ledgerFrom, markdownAsHtml, sessionOf } from "./fact-commands-verify.mjs"
26
+ import { editionFormatOf, fillMapOf, ledgerFrom, markdownAsHtml, sessionOf } from "./fact-commands-verify.mjs"
21
27
  import { extractPairs } from "./fact-pairs.mjs"
22
28
  import { lockedBrief } from "./fidelity-policy.mjs"
23
29
  import { applyEdits, POLISH_EDIT_FIELDS, validatePolishEdits } from "./polish-apply.mjs"
@@ -58,7 +64,7 @@ const rejectionLine = (entry, edit) => ` rejected edit ${entry.index} ("${befor
58
64
  function runPolishApply(args) {
59
65
  const [report] = args.positionals
60
66
  const { flags } = args
61
- if (!report || flags.edits === undefined || flags.out === undefined) throw new CliError("usage: fact polish-apply <report.html> --edits polish-edits.json --out <report.polished.html> [--ledger F] [--brief brief.md] [--session-dir D]")
67
+ if (!report || flags.edits === undefined || flags.out === undefined) throw new CliError("usage: fact polish-apply <report.html> --edits polish-edits.json --out <report.polished.html> [--fill-map F] [--ledger F] [--brief brief.md] [--session-dir D]")
62
68
  if (editionFormatOf(report, flags.format) === "md") throw new CliError("fact polish-apply takes the filled HTML edition; polish a Markdown edition through its HTML source")
63
69
  const { refusal } = lockedBrief("fact polish-apply", flags)
64
70
  if (refusal) return refusal
@@ -71,13 +77,18 @@ function runPolishApply(args) {
71
77
  throw new CliError(`${flags.edits} must be an array of {${Object.keys(POLISH_EDIT_FIELDS).join(", ")}} objects: ${error.message}`)
72
78
  }
73
79
  const result = applyEdits(html, edits, ledgerFrom(flags))
80
+ const mapPath = fillMapOf(report, html, flags["fill-map"])
81
+ const map = mapPath === null ? null : readJsonFile(mapPath)
82
+ if (map !== null && map.artifact_sha256 !== sha256Of(html)) throw new CliError(`${mapPath} does not record the bytes of ${report} (artifact_sha256 differs); refusing to carry its brief binding to the polished edition`)
83
+ const polishedFrom = map?.polishedFrom ?? (map && { map: resolve(mapPath), sha256: sha256Of(readText(mapPath)) })
74
84
  writeTextAtomic(flags.out, result.out)
85
+ if (map) writeJsonAtomic(`${resolve(flags.out)}.polished.fill-map.json`, { ...map, artifact: resolve(flags.out), artifact_sha256: sha256Of(result.out), polishedFrom })
75
86
  const defects = [...result.rejected, ...result.unlocated].sort((a, b) => a.index - b.index).map(withSeverity)
76
87
  const counts = { applied: result.applied.length, rejected: result.rejected.length, unlocated: result.unlocated.length }
77
88
  const data = { out: resolve(flags.out), ...counts, applied_indexes: result.applied, input_sha256: sha256Of(html), edits_sha256: sha256Of(editsText), out_sha256: sha256Of(result.out) }
78
89
  const rejected_reasons = result.rejected.map((entry) => rejectionLine(entry, edits[entry.index]))
79
90
  const summary = [`fact polish-apply: ${counts.applied} applied, ${counts.rejected} rejected, ${counts.unlocated} unlocated of ${edits.length} edit(s)`, ...rejected_reasons].join("\n")
80
- return { json: { command: "fact polish-apply", status: "pass", exitCode: 0, defects, data: { ...data, rejected_reasons } }, summary, exitCode: 0 }
91
+ return { json: { command: "fact polish-apply", status: "pass", exitCode: 0, applied: counts.rejected === 0 && counts.unlocated === 0, defects, data: { ...data, rejected_reasons } }, summary, exitCode: 0 }
81
92
  }
82
93
 
83
94
  function runPolishDiff(args) {
@@ -100,29 +111,40 @@ function runPolishDiff(args) {
100
111
 
101
112
  const pairKey = (pair) => [pair.kind, pair.sentence_key, pair.claim_id ?? "", normalizeText(pair.sentence)].join("\u0000")
102
113
 
114
+ /** The pre-polish fill map behind --changed-since: a polished map leads to it through polishedFrom, whose sha256 it must still match. */
115
+ function prePolishMap(mapPath) {
116
+ const given = readJsonFile(mapPath)
117
+ if (given.polishedFrom === undefined) return { path: resolve(mapPath), map: given }
118
+ const { map: path, sha256 } = given.polishedFrom
119
+ if (typeof path !== "string" || sha256Of(readText(path)) !== sha256) throw new CliError(`${path} changed after polish wrote ${mapPath}; --changed-since needs the pre-polish fill map it names`)
120
+ return { path, map: readJsonFile(path) }
121
+ }
122
+
103
123
  /**
104
- * The pairs whose sentence text changed since the pre-polish artifact a fill map names: a pair is
105
- * kept unless the same (kind, sentence key, normalized sentence) occurs in that artifact. The map
106
- * must carry the artifact's `artifact_sha256` and the bytes must still match it, so the filter is
107
- * fail-closed on a hand-made or stale map.
124
+ * The pairs whose sentence text changed since the pre-polish artifact the fill map names: a pair is
125
+ * kept unless the same (kind, sentence key, normalized sentence) occurs in that artifact. Only bytes
126
+ * that still hash to the map's `artifact_sha256` narrow the set; otherwise (the report was polished
127
+ * in place, or edited) every pair is kept, so nothing changed escapes the meaning re-check.
108
128
  * @param {object[]} pairs extractPairs output on the polished edition
109
- * @param {string} mapPath the pre-polish fill map (fact fill's `<out>.fill-map.json`)
129
+ * @param {string} mapPath fact fill's `<out>.fill-map.json`, or polish-apply's `<out>.polished.fill-map.json`
130
+ * @returns {{ pairs: object[], fill_map: string, narrowed: boolean }}
110
131
  */
111
132
  export function pairsChangedSince(pairs, mapPath, ledger, claims) {
112
- const map = readJsonFile(mapPath)
113
- if (typeof map.artifact !== "string" || !existsSync(map.artifact)) throw new CliError(`${mapPath} names no readable pre-polish artifact`)
114
- const text = readText(map.artifact)
115
- if (typeof map.artifact_sha256 !== "string") throw new CliError(`${mapPath} fill map has no artifact_sha256; --changed-since needs the pre-polish hash`)
116
- if (sha256Of(text) !== map.artifact_sha256) throw new CliError(`${map.artifact} changed after fill; --changed-since needs the pre-polish bytes`)
133
+ const { path, map } = prePolishMap(mapPath)
134
+ if (typeof map.artifact !== "string") throw new CliError(`${path} names no pre-polish artifact`)
135
+ if (typeof map.artifact_sha256 !== "string") throw new CliError(`${path} fill map has no artifact_sha256; --changed-since needs the pre-polish hash`)
136
+ const text = existsSync(map.artifact) ? readText(map.artifact) : null
137
+ if (text === null || sha256Of(text) !== map.artifact_sha256) return { pairs, fill_map: path, narrowed: false }
117
138
  const html = map.format === "md" ? markdownAsHtml(text, map.entries, ledger).html : text
118
139
  const before = new Map()
119
140
  for (const pair of extractPairs(html, ledger, claims)) before.set(pairKey(pair), (before.get(pairKey(pair)) ?? 0) + 1)
120
- return pairs.filter((pair) => {
141
+ const kept = pairs.filter((pair) => {
121
142
  const left = before.get(pairKey(pair)) ?? 0
122
143
  if (left === 0) return true
123
144
  before.set(pairKey(pair), left - 1)
124
145
  return false
125
146
  })
147
+ return { pairs: kept, fill_map: path, narrowed: true }
126
148
  }
127
149
 
128
150
  /** @param {string[]} argv the arguments after `fact polish-apply` or `fact polish-diff` */
@@ -8,10 +8,12 @@
8
8
  // escaped HTML projection (same line numbers) and checked with its fill map, which is the only
9
9
  // record of where its values sit; LaTeX is verified through its Markdown source.
10
10
  //
11
- // The locked brief is re-judged here, not only at fill: the edition's fill map (--fill-map, else
12
- // `<report>.fill-map.json` when present) must carry the current brief's brief_sha256
13
- // (brief_changed), and policyErrors re-runs over every data-fact the edition carries, so an
14
- // edition that violates the current derived-numbers or source-floor rule fails here too.
11
+ // The locked brief is re-judged here, not only at fill: every edition (HTML or Markdown) needs its
12
+ // fill map (fillMapOf: --fill-map, else whichever of `<report>.fill-map.json` and the
13
+ // `<report>.polished.fill-map.json` polish-apply writes records the edition's bytes), and that map
14
+ // must carry the current brief's brief_sha256; no map, or another hash, is brief_changed. policyErrors
15
+ // re-runs over every data-fact the edition carries, so an edition that violates the current
16
+ // derived-numbers or source-floor rule fails here too.
15
17
  //
16
18
  // The delivery barrier that reads these records (and every other fidelity record) is
17
19
  // fidelity-records.mjs.
@@ -66,8 +68,8 @@ function entryHtml(text, entry, rows) {
66
68
  if (entry.translates) return `<span data-translates="${escapeHtml(entry.translates)}">${projectMarkdownInline(body.replace(/^<!--[\s\S]*?-->\s*/u, ""))}</span>`
67
69
  if (!row) return `<data data-fact="${escapeHtml(entry.fact_id)}">${escapeHtml(body)}</data>`
68
70
  const quoted = row.kind === "quote" ? parseRenderedQuote(body) : null
69
- if (quoted) return renderQuote(row, entry.selector ?? "full", quoted.text, "html")
70
- return row.kind === "quote" ? null : renderValueHtml(row, body)
71
+ if (quoted) return renderQuote(row, entry.selector ?? "full", quoted.text, "html", entry.claimKind)
72
+ return row.kind === "quote" ? null : renderValueHtml(row, body, { mode: entry.mode, claimKind: entry.claimKind ?? null })
71
73
  }
72
74
 
73
75
  /** [from, to) of the filled text as HTML, each intact fill-map entry rebuilt as its fill element. */
@@ -149,6 +151,17 @@ const MARKDOWN_EXTENSION = /\.(md|markdown)$/iu
149
151
  /** The edition format: the explicit flag, else md for a .md or .markdown path (any case), else html. */
150
152
  export const editionFormatOf = (path, flag) => flag ?? (MARKDOWN_EXTENSION.test(path) ? "md" : "html")
151
153
 
154
+ /**
155
+ * An edition's fill map path: --fill-map, else whichever of fact fill's `<report>.fill-map.json` and
156
+ * polish-apply's `<report>.polished.fill-map.json` records these bytes as its artifact_sha256, else the
157
+ * first of them that exists; null when neither exists.
158
+ */
159
+ export function fillMapOf(report, text, flag) {
160
+ if (flag !== undefined) return flag
161
+ const found = [`${report}.fill-map.json`, `${report}.polished.fill-map.json`].filter((path) => existsSync(path))
162
+ return found.find((path) => readJsonFile(path).artifact_sha256 === sha256Of(text)) ?? found[0] ?? null
163
+ }
164
+
152
165
  /** The report's format (from --format or its extension) and, for Markdown, its fill map entries. */
153
166
  export function readEdition(report, flags) {
154
167
  const format = editionFormatOf(report, flags.format)
@@ -174,19 +187,23 @@ export function runFactVerify(args) {
174
187
  const briefPath = flags.brief ?? join(sessionDir, "brief.md")
175
188
  const ledger = readLedger(sessionDir)
176
189
  const projected = edition.format === "md" ? markdownAsHtml(edition.text, edition.entries, ledger) : null
190
+ // The entries whose element is in the checked HTML: an HTML edition's own map, or the projection's intact entries.
191
+ const ownMap = flags["fill-map"] ?? `${report}.fill-map.json`
192
+ const fillEntries = projected ? edition.entries.filter((entry) => !projected.stale.includes(entry)) : existsSync(ownMap) ? readJsonFile(ownMap).entries : null
177
193
  const result = checkFidelity(projected?.html ?? edition.text, {
178
194
  ledger,
179
195
  sourcesDir: resolve(flags.sources ?? join(sessionDir, "sources")),
180
196
  claims: flags.claims !== undefined || existsSync(claimsPath) ? readJsonFile(claimsPath) : null,
181
197
  fillMap: projected?.stale ?? edition.entries,
198
+ fillEntries,
182
199
  brief: existsSync(briefPath) ? readText(briefPath) : "",
183
200
  baseDir: dirname(resolve(report)),
184
201
  recompute,
185
202
  })
186
203
  const checked = projected?.html ?? edition.text
187
204
  const pointers = [...checked.matchAll(/\bdata-fact="([^"]+)"/gu)].map((match) => ({ token: match[0], start: match.index, parsed: { id: match[1] } }))
188
- const mapPath = flags["fill-map"] ?? `${report}.fill-map.json`
189
- const bound = existsSync(mapPath) ? briefBindingErrors(readJsonFile(mapPath), mapPath, parsed) : []
205
+ const mapPath = fillMapOf(report, edition.text, flags["fill-map"])
206
+ const bound = briefBindingErrors(mapPath === null ? null : readJsonFile(mapPath), mapPath ?? `${report}.fill-map.json`, parsed)
190
207
  const defects = [...bound, ...policyErrors(checked, pointers, ledger, sessionDir, policy).errors, ...result.defects]
191
208
  const counts = countDefects(defects)
192
209
  const status = defects.some(blocksDelivery) ? "fail" : "pass"
@@ -10,11 +10,14 @@ import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs"
10
10
  import { join, resolve } from "node:path"
11
11
 
12
12
  import { CliError, parseArgs, sha256Of } from "./cli-support.mjs"
13
+ import { DEFECT_CODES } from "./contracts.mjs"
14
+ import { alteredIn } from "./conversion-probe.mjs"
13
15
  import { derive } from "./fact-derive.mjs"
14
16
  import { addRow, readLedger, retractRow, supersedeRow, writeLedger } from "./fact-ledger.mjs"
15
17
  import { locateFind, locateMatch, locatePageLine, locateTable } from "./fact-locate.mjs"
16
18
  import { QUALIFIER_FORMS } from "./fidelity-lexicon.mjs"
17
19
  import { exportRows } from "./fidelity-values.mjs"
20
+ import { rawLocated } from "./raw-locator.mjs"
18
21
  import { pointedFlag, sessionDirOf, snapshotOf } from "./source-commands.mjs"
19
22
 
20
23
  const CONTEXT = 60
@@ -30,26 +33,30 @@ function readText(path) {
30
33
  }
31
34
  }
32
35
 
33
- function nowIso(flags) {
34
- const date = flags.now === undefined ? new Date() : new Date(flags.now)
36
+ /** A caller-supplied --now value as ISO; parsing only, never the clock. */
37
+ function isoOf(text) {
38
+ const date = new Date(text)
35
39
  if (Number.isNaN(date.getTime())) throw new CliError("--now requires an ISO timestamp")
36
40
  return date.toISOString()
37
41
  }
38
42
 
43
+ const nowIso = (flags) => (flags.now === undefined ? new Date().toISOString() : isoOf(flags.now))
44
+
39
45
  function failure(command, error, detail) {
40
46
  const json = { command, status: "fail", exitCode: 1, error, ...detail }
41
47
  return { json, summary: `${command}: ${error}${detail.message ? ` (${detail.message})` : ""}`, exitCode: 1 }
42
48
  }
43
49
 
44
- function locate(flags, snapshot, id, quote) {
50
+ /** Every locator reads the view text (visible text nodes for a page), never raw markup. */
51
+ function locate(flags, view, id, quote) {
45
52
  const sourceId = id
46
- if (quote) return locateMatch(snapshot.text, { match: flags.match, sourceId })
47
- if (flags.find !== undefined) return locateFind(snapshot.text, { find: flags.find, unitExpected: flags["unit-expected"], sourceId })
53
+ if (quote) return locateMatch(view.text, { match: flags.match, sourceId })
54
+ if (flags.find !== undefined) return locateFind(view.text, { find: flags.find, unitExpected: flags["unit-expected"], sourceId })
48
55
  if (flags.table !== undefined) {
49
- if (snapshot.html === null) throw new CliError(`source ${id} has no source.html; --table needs a page snapshot`)
50
- return locateTable(snapshot.html, { table: flags.table, row: flags.row, col: flags.col, sourceId })
56
+ if (view.root === null) throw new CliError(`source ${id} is not an HTML page snapshot; --table needs one`)
57
+ return locateTable(view, { table: flags.table, row: flags.row, col: flags.col, sourceId })
51
58
  }
52
- if (flags.page !== undefined) return locatePageLine(snapshot.text, { page: Number(flags.page), line: Number(flags.line), sourceId })
59
+ if (flags.page !== undefined) return locatePageLine(view.text, { page: Number(flags.page), line: Number(flags.line), sourceId })
53
60
  throw new CliError("usage: fact add --source S<n> (--find TEXT | --table T --row R --col C | --page P --line L) --label LABEL")
54
61
  }
55
62
 
@@ -76,11 +83,17 @@ function runAdd(args, quote) {
76
83
  const id = args.required("source")
77
84
  if (quote) args.required("match")
78
85
  const sessionDir = sessionDirOf(flags)
79
- const snapshot = snapshotOf(sessionDir, id)
80
- const found = locate(flags, snapshot, id, quote)
86
+ const { meta, view, sha256, drift } = snapshotOf(sessionDir, id)
87
+ const converted = view.mode === "converted"
88
+ if (drift.length > 0) return failure(command, "snapshot_hash_mismatch", { message: `${id}: ${drift.join("; ")}` })
89
+ if (view.mode === "legacy") return failure(command, "snapshot_not_raw", { message: `${id} is a legacy text.txt snapshot without raw.bin; snapshot the source again under a new id` })
90
+ if (converted && !quote) return failure(command, "snapshot_not_raw", { message: `${id} is converted-only (no raw bytes); measured rows need source snapshot --raw <original> --text <extraction>` })
91
+ const located = locate(flags, view, id, quote)
92
+ const found = rawLocated(view, located)
81
93
  if (found.error === "unit_expected_mismatch") return failure(command, "unit_mismatch", { message: `expected ${found.expected}, found ${found.found}`, candidates: found.candidates })
82
94
  if (found.error) return failure(command, found.error, { message: `no single match in ${id}`, candidates: found.candidates })
83
- const { meta } = snapshot
95
+ const altered = alteredIn(join(sessionDir, "sources", id), [found.raw, found.quote])
96
+ if (altered.length > 0) return failure(command, "conversion_altered_number", { message: `${id}'s conversion altered ${[...new Set(altered)].join(", ")} (sources/${id}/conversion.json); the converted text cannot back this number, even where the source also shows it unaltered: write a verified text of the source and run \`source snapshot --id ${id} --text <verified.txt> --replace\`, then add the fact again`, tokens: altered, hint: DEFECT_CODES.conversion_altered_number.hint })
84
97
  const host = URL.canParse(meta.url) ? new URL(meta.url).hostname : null
85
98
  const labelSource = flags.find ?? flags.match ?? (flags.table !== undefined ? `${flags.row}_${flags.col}` : `p${flags.page}_l${flags.line}`)
86
99
  const ledger = readLedger(sessionDir)
@@ -89,9 +102,10 @@ function runAdd(args, quote) {
89
102
  ...(quote ? {} : { value: found.value, unit: found.unit, value_span: found.value_span, qualifier: QUALIFIER_OF.get(found.qualifier) ?? null }),
90
103
  raw: found.raw,
91
104
  quote: found.quote,
105
+ context: found.context,
92
106
  source_id: id,
93
107
  locator: { ...found.locator, source_kind: meta.kind, url: meta.url, host, pointed: pointedFlag(sessionDir, meta.url) },
94
- snapshot_sha256: meta.sha256,
108
+ snapshot_sha256: sha256,
95
109
  sentence_id: found.sentence_id ?? null,
96
110
  extracted_by: meta.extractor,
97
111
  observed_at: nowIso(flags),
@@ -104,11 +118,14 @@ function runAdd(args, quote) {
104
118
  fuzzy: found.fuzzy === true,
105
119
  })
106
120
  writeLedger(sessionDir, ledger)
107
- const context = contextOf(snapshot.text, row)
108
- const json = { command, status: "pass", exitCode: 0, defects: [], data: { row, context } }
121
+ const context = contextOf(view.text, located)
122
+ const { severity, integrity } = DEFECT_CODES.quote_converted_only
123
+ const defects = converted ? [{ code: "quote_converted_only", severity, integrity, message: `${id} is converted-only; the quote is not bound to the original fetched bytes` }] : []
124
+ const json = { command, status: "pass", exitCode: 0, defects, data: { row, context } }
109
125
  if (flags.json) return { json, exitCode: 0 }
110
126
  const shown = quote ? row.quote : `${row.raw} (value ${row.value}${row.unit ? `, unit ${row.unit}` : ""})`
111
- return { text: `${row.fact_id} ${row.label}: ${shown} [${id} ${row.sentence_id ?? "-"}]\n ...${context.before}[${context.match}]${context.after}...`, exitCode: 0 }
127
+ const warnings = defects.map((defect) => `\n warning ${defect.code}: ${defect.message}`).join("")
128
+ return { text: `${row.fact_id} ${row.label}: ${shown} [${id} ${row.sentence_id ?? "-"}]\n ...${context.before}[${context.match}]${context.after}...${warnings}`, exitCode: 0 }
112
129
  }
113
130
 
114
131
  function runRetract(args) {
@@ -157,11 +174,13 @@ function runExport(args) {
157
174
  function runDerive(args) {
158
175
  const { flags } = args
159
176
  const op = args.required("op")
177
+ // Time is the caller's: without --now the CLI refuses with the library's missing_now code (exit 2) and never reads the clock.
178
+ if (flags.now === undefined) throw new CliError("missing_now: fact derive needs --now <ISO timestamp> (the row's observed_at); no row written")
160
179
  const sessionDir = sessionDirOf(flags)
161
180
  const ledger = readLedger(sessionDir)
162
181
  // The registry refuses any field outside the operation's inputFields, so only the given flags go in; --unit null declares a dimensionless result.
163
182
  const picked = ["to", "from", "num", "den", "part", "whole", "reference", "rationale", "label"].filter((key) => flags[key] !== undefined)
164
- const spec = { op, now: nowIso(flags), ...Object.fromEntries(picked.map((key) => [key, flags[key]])) }
183
+ const spec = { op, now: isoOf(flags.now), ...Object.fromEntries(picked.map((key) => [key, flags[key]])) }
165
184
  if (op === "expr") spec.formula = args.positionals[0]
166
185
  else if (args.positionals.length > 0) spec.operands = args.positionals
167
186
  if (flags.disjoint === true) spec.disjoint = true