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,179 @@
1
+ import { describe, expect, onTestFinished, test } from "bun:test"
2
+ import { copyFileSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"
3
+ import { tmpdir } from "node:os"
4
+ import { join } from "node:path"
5
+
6
+ import { derive } from "./fact-derive.mjs"
7
+ import { valueChecks } from "./fidelity-values.mjs"
8
+ import { buildContext } from "./gates-static.mjs"
9
+ import { parseHtml } from "./html-lite.mjs"
10
+ import { run } from "./report-tools.mjs"
11
+ import { FIX, LEDGER, type Row, type VerifyResult, codes, filled, prose } from "./tests/value-mode-support"
12
+
13
+ describe("value-mode unit binding through fact fill and verify", () => {
14
+ for (const format of ["html", "md"]) {
15
+ test(`#given ${format} value mode followed by meters #when edited to mm #then verify fails unit_mismatch`, async () => {
16
+ const body = format === "html" ? prose("{{F1:span_len:value}}", " meters") : "## Findings\n\nUp to {{F1:span_len:value}} meters [S4]."
17
+ const { verify, edit } = await filled(body, format)
18
+ expect((await verify()).exitCode).toBe(0)
19
+ edit(" meters", " mm")
20
+ const edited = await verify()
21
+ expect(codes(edited)).toContain("unit_mismatch")
22
+ expect(edited.exitCode).toBe(1)
23
+ })
24
+ }
25
+
26
+ for (const [pointer, tail] of [
27
+ ["{{F1:span_len:value}}", " m in total length"],
28
+ ["{{F20:gap_pp:value}}", " \uD37C\uC13C\uD2B8 \uD3EC\uC778\uD2B8\uB85C"],
29
+ ["{{F21:rev_eok:value}}", " \uC5B5 \uC6D0\uC73C\uB85C"],
30
+ ]) {
31
+ test(`#given ordinary prose ${JSON.stringify(tail)} after ${pointer} #when verified #then it binds the row's own unit, never a squashed join`, async () => {
32
+ const result = await (await filled(prose(pointer, tail))).verify()
33
+ expect(codes(result)).toEqual([])
34
+ expect(result.exitCode).toBe(0)
35
+ })
36
+ }
37
+
38
+ for (const [pointer, tail, from, to] of [
39
+ ["{{F1:span_len:value}}", "m", "</data>m", "</data>mm"],
40
+ ["{{F3:rate_yoy:value}}", "%", "</data>%", "</data>%p"],
41
+ ["{{F1:span_len:value}}", " m\uC758", " m\uC758", " mm\uC758"],
42
+ ["{{F21:rev_eok:value}}", "\uC5B5 \uC6D0\uC73C\uB85C", "\uC5B5 \uC6D0\uC73C\uB85C", "\uC5B5 \uB2EC\uB7EC\uB85C"],
43
+ ["{{F22:staff:value}}", "\uBA85\uC774\uB2E4", "\uBA85\uC774\uB2E4", "\uAC74\uC774\uB2E4"],
44
+ ["${{F23:price:value}}", "", "$<data", "\u20AC<data"],
45
+ ]) {
46
+ test(`#given the documented form ${pointer}${tail} #when its unit is hand-edited #then unit_mismatch blocks`, async () => {
47
+ const { verify, edit } = await filled(prose(pointer, tail))
48
+ const clean = await verify()
49
+ expect(codes(clean)).toEqual([])
50
+ edit(from, to)
51
+ const edited = await verify()
52
+ expect(codes(edited)).toContain("unit_mismatch")
53
+ expect(edited.exitCode).toBe(1)
54
+ })
55
+ }
56
+
57
+ // A longer unit that starts with the row's unit (% -> %p): read whole, spaced or glued, EN and KO (peosenteu pointeu).
58
+ for (const to of ["% \uD3EC\uC778\uD2B8", " \uD37C\uC13C\uD2B8 \uD3EC\uC778\uD2B8", " percent points", " % p", " %p", "%\uD3EC\uC778\uD2B8", "\uD37C\uC13C\uD2B8\uD3EC\uC778\uD2B8"]) {
59
+ test(`#given {{F3:rate_yoy:value}}% #when hand-edited to ${JSON.stringify(to)} #then the longer unit is read and unit_mismatch blocks`, async () => {
60
+ const { verify, edit } = await filled(prose("{{F3:rate_yoy:value}}", "%"))
61
+ expect(codes(await verify())).toEqual([])
62
+ edit("</data>%", `</data>${to}`)
63
+ const edited = await verify()
64
+ expect(codes(edited)).toContain("unit_mismatch")
65
+ expect(edited.exitCode).toBe(1)
66
+ })
67
+ }
68
+
69
+ test("#given no following unit #when verified #then unit_unbound is a minor warning", async () => {
70
+ const result = await (await filled(prose("{{F1:span_len:value}}", " long"))).verify()
71
+ expect(result.exitCode).toBe(0)
72
+ expect(result.json.defects).toContainEqual(expect.objectContaining({ code: "unit_unbound", severity: "minor" }))
73
+ expect((result.json as { gatesRun?: string[] }).gatesRun).toContain("unit_unbound")
74
+ })
75
+
76
+ test("#given the documented split form :value then :unit #when verified #then the unit element binds", async () => {
77
+ expect(codes(await (await filled(prose("{{F1:span_len:value}} {{F1:span_len:unit}}", ""))).verify())).toEqual([])
78
+ })
79
+
80
+ test("#given a converted row in value mode followed by its unit #when verified #then display(value, unit) still verifies", async () => {
81
+ expect(codes(await (await filled(prose("{{F8:span_mm:value}}", " mm"))).verify())).not.toContain("unit_mismatch")
82
+ })
83
+
84
+ test("#given a converted row in value mode rendered with its original in brackets #when the unit beside it is hand-edited #then unit_mismatch blocks", async () => {
85
+ const ledger = structuredClone(LEDGER)
86
+ const { row } = derive(ledger, { op: "convert", operands: ["F1"], to: "mm", now: "2026-10-23T08:00:00Z", label: "span_conv" }) as { row: Row }
87
+ const { verify, edit, text } = await filled(prose(`{{${row.fact_id}:span_conv:value}}`, " mm"), "html", ledger)
88
+ expect(text).toContain(">320,000 mm (320 m)</data> mm")
89
+ expect(codes(await verify())).toEqual([])
90
+ edit("</data> mm", "</data> km")
91
+ const edited = await verify()
92
+ expect(codes(edited)).toContain("unit_mismatch")
93
+ expect(edited.exitCode).toBe(1)
94
+ })
95
+
96
+ test("#given the sign already inside a value-mode element (valueParts falling back to the raw text) #when checked #then it is bound", () => {
97
+ const element = (shown: string) => `<h2>Findings</h2><p><data class="fact" value="12" data-fact="F23" data-unit="USD" data-fact-mode="value" data-lineage="MEASURED">${shown}</data> [S4].</p>`
98
+ const found = (shown: string) => (valueChecks(buildContext(parseHtml(element(shown)), {}), LEDGER, null, null) as { code: string }[]).map((d) => d.code)
99
+ expect(found("$12")).not.toContain("unit_unbound")
100
+ expect(found("$12")).not.toContain("unit_mismatch")
101
+ })
102
+
103
+ test("#given a dimensionless value #when no unit follows #then no unit warning", async () => {
104
+ expect(codes(await (await filled("<p>{{F2:units_sold:value}} [S4].</p>")).verify())).not.toContain("unit_unbound")
105
+ })
106
+ })
107
+
108
+ describe("value-mode units in tables bind by visual column", () => {
109
+ const table = (head: string, rows: string) => `<h2>Findings</h2><table><thead>${head}</thead><tbody>${rows}</tbody></table>`
110
+ for (const [name, html, code] of [
111
+ ["a matching header", table("<tr><th>Span (meters)</th></tr>", "<tr><td>{{F1:span_len:value}}</td></tr>"), null],
112
+ ["a wrong header", table("<tr><th>Span (mm)</th></tr>", "<tr><td>{{F1:span_len:value}}</td></tr>"), "unit_mismatch"],
113
+ ["a conflicting suffix", table("<tr><th>Span (m)</th></tr>", "<tr><td>{{F1:span_len:value}} mm</td></tr>"), "unit_mismatch"],
114
+ ["a header disagreeing with a suffix", table("<tr><th>Span (mm)</th></tr>", "<tr><td>{{F1:span_len:value}} m</td></tr>"), "unit_mismatch"],
115
+ ["a unit only in another column", table("<tr><th>Span</th><th>Offset (m)</th></tr>", "<tr><td>{{F1:span_len:value}}</td><td>unknown</td></tr>"), "unit_unbound"],
116
+ ["a colspan earlier in the row", table("<tr><th>Bridge</th><th>Deck (km)</th><th>Span (m)</th></tr>", '<tr><td colspan="2">Bridge A</td><td>{{F1:span_len:value}}</td></tr>'), null],
117
+ ["a rowspan from above", table("<tr><th>Bridge</th><th>Span (m)</th><th>Deck (km)</th></tr>", '<tr><td rowspan="2">A</td><td>x</td><td>y</td></tr><tr><td>{{F1:span_len:value}}</td><td>{{F24:deck_len:value}}</td></tr>'), "unit_unbound"],
118
+ ["a headerless key/value table", "<h2>Findings</h2><table><tr><td>Deck</td><td>{{F24:deck_len}}</td></tr><tr><td>Span</td><td>{{F1:span_len:value}}</td></tr></table>", "unit_unbound"],
119
+ ["a key/value table with row headers and a matching suffix", "<h2>Findings</h2><table><tr><th>Deck</th><td>{{F24:deck_len}}</td></tr><tr><th>Span</th><td>{{F1:span_len:value}} m</td></tr></table>", null],
120
+ ["a body row header under a matching column header", table("<tr><th>Part</th><th>Length (m)</th></tr>", '<tr><th scope="row">Span</th><td>{{F1:span_len:value}}</td></tr>'), null],
121
+ ["a body row header under a wrong column header", table("<tr><th>Part</th><th>Length (mm)</th></tr>", '<tr><th scope="row">Span</th><td>{{F1:span_len:value}}</td></tr>'), "unit_mismatch"],
122
+ ] as const) {
123
+ test(`#given ${name} #when verified #then ${code ?? "it binds cleanly"}`, async () => {
124
+ const result = await (await filled(html)).verify()
125
+ if (code === null) expect(codes(result)).toEqual([])
126
+ else expect(codes(result)).toContain(code)
127
+ expect(result.exitCode).toBe(code === "unit_mismatch" ? 1 : 0)
128
+ if (code !== "unit_mismatch") expect(codes(result)).not.toContain("unit_mismatch")
129
+ })
130
+ }
131
+ })
132
+
133
+ // Rows as `fact add --find` writes them: the scale word stays in the value's render ("3,234\uC5B5", "12 million").
134
+ const PAGE = "<html><body><p>\uB9E4\uCD9C\uC740 3,234\uC5B5 \uC6D0\uC774\uB2E4.</p><p>Revenue reached 12 million dollars.</p><p>The main span is 320 m long.</p></body></html>"
135
+
136
+ async function located(pointers: string) {
137
+ const dir = mkdtempSync(join(tmpdir(), "value-mode-find-"))
138
+ onTestFinished(() => rmSync(dir, { recursive: true, force: true }))
139
+ copyFileSync(join(FIX, "fidelity-brief-locked.md"), join(dir, "brief.md"))
140
+ writeFileSync(join(dir, "page.html"), PAGE)
141
+ expect((await run(["source", "snapshot", "--id", "S1", "--html", join(dir, "page.html"), "--session-dir", dir])).exitCode).toBe(0)
142
+ const rows: Row[] = []
143
+ for (const [find, label] of [["\uB9E4\uCD9C\uC740", "won"], ["Revenue reached", "rev"], ["main span is", "sp"]]) {
144
+ const added = (await run(["fact", "add", "--source", "S1", "--find", find, "--label", label, "--session-dir", dir, "--json"])) as { exitCode: number; json: { data: { row: Row } } }
145
+ expect(added.exitCode).toBe(0)
146
+ rows.push(added.json.data.row)
147
+ }
148
+ writeFileSync(join(dir, "report.src.html"), `<h2>Findings</h2><p>The source states ${pointers} [S1].</p>`)
149
+ expect((await run(["fact", "fill", join(dir, "report.src.html"), "--out", join(dir, "report.html"), "--session-dir", dir])).exitCode).toBe(0)
150
+ const verify = async () => (await run(["fact", "verify", join(dir, "report.html"), "--session-dir", dir])) as VerifyResult
151
+ const edit = (from: string, to: string) => writeFileSync(join(dir, "report.html"), readFileSync(join(dir, "report.html"), "utf8").replace(from, to))
152
+ return { rows, verify, edit }
153
+ }
154
+
155
+ describe("value-mode facts whose render carries a scale word", () => {
156
+ test("#given rows from fact add --find #when located #then the scale is folded into an unscaled unit", async () => {
157
+ const { rows } = await located("{{F1:won}}")
158
+ expect(rows.map((row) => [row.value, row.unit])).toEqual([["323400000000", "KRW"], ["12000000", "USD"], ["320", "m"]])
159
+ })
160
+
161
+ for (const pointers of ["{{F1:won:value}} \uC6D0", "{{F1:won:value}}{{F1:won:unit}}", "{{F2:rev:value}} dollars", "{{F2:rev:value}} {{F2:rev:unit}}"]) {
162
+ test(`#given ${JSON.stringify(pointers)} right after fill #when verified #then the scale word is part of the value, not a unit`, async () => {
163
+ const result = await (await located(pointers)).verify()
164
+ expect(codes(result)).toEqual([])
165
+ expect(result.exitCode).toBe(0)
166
+ })
167
+ }
168
+
169
+ for (const [pointers, from, to] of [["{{F3:sp:value}}m", "</data>m", "</data>mm"], ["{{F1:won:value}} \uC6D0", "</data> \uC6D0", "</data> \uB2EC\uB7EC"]]) {
170
+ test(`#given ${JSON.stringify(pointers)} #when the unit outside the element is hand-edited #then unit_mismatch still blocks`, async () => {
171
+ const { verify, edit } = await located(pointers)
172
+ expect(codes(await verify())).toEqual([])
173
+ edit(from, to)
174
+ const edited = await verify()
175
+ expect(codes(edited)).toContain("unit_mismatch")
176
+ expect(edited.exitCode).toBe(1)
177
+ })
178
+ }
179
+ })
@@ -0,0 +1,157 @@
1
+ // Node CLI cases of the #11290 fold: the pre-polish fill map stays authoritative for
2
+ // `fact pairs --changed-since` (in place and out of place), a fill map that does not record the
3
+ // polished bytes is refused, `fact derive` takes its time from --now only, and a missing fill map
4
+ // is named by brief_changed.
5
+ import { describe, expect, test } from "bun:test"
6
+ import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs"
7
+ import { join } from "node:path"
8
+
9
+ import { NOW, codesOf, fidelitySession, nodeJson, polishSession, sha256, verdicts, verify } from "./tests/report-tools-support"
10
+
11
+ type Written = { pairs: { pair_id: string; sentence: string }[]; changed_since?: unknown }
12
+
13
+ const WORDING = [{ before: "The operator wrote", after: "The operator stated", reason: "wording" }]
14
+
15
+ function polish(dir: string, s: string[], out: string) {
16
+ const edits = join(dir, "wording.json")
17
+ writeFileSync(edits, JSON.stringify(WORDING))
18
+ return nodeJson(["fact", "polish-apply", join(dir, "report.html"), "--edits", edits, "--out", out, ...s])
19
+ }
20
+
21
+ /** A hand edit of F1's rendered value after polish (the converted row's "(320 m)" stays). */
22
+ function editValue(path: string) {
23
+ const html = readFileSync(path, "utf8")
24
+ expect(html.split(">320 m</data>").length).toBe(2)
25
+ writeFileSync(path, html.replace(">320 m</data>", ">321 m</data>"))
26
+ }
27
+
28
+ function pairs(dir: string, s: string[], report: string, map: string) {
29
+ const out = join(dir, "pairs-changed.json")
30
+ rmSync(out, { force: true })
31
+ const run = nodeJson(["fact", "pairs", report, "--changed-since", map, "--out", out, ...s])
32
+ const written = (run.code === 0 ? JSON.parse(readFileSync(out, "utf8")) : { pairs: [] }) as Written
33
+ return { run, written, sentences: written.pairs.map((pair) => pair.sentence) }
34
+ }
35
+
36
+ describe("the pre-polish fill map stays authoritative for --changed-since", () => {
37
+ test("#given a report polished in place #when a value is edited and fact pairs runs --changed-since on the fill map #then the reworded and the edited sentences are re-checked and meaning-check runs", () => {
38
+ // given
39
+ const { dir, s } = polishSession()
40
+ const report = join(dir, "report.html")
41
+ const fillMap = `${report}.fill-map.json`
42
+ const before = readFileSync(fillMap, "utf8")
43
+ const applied = polish(dir, s, report)
44
+ // when
45
+ const reworded = pairs(dir, s, report, fillMap)
46
+ editValue(report)
47
+ const edited = pairs(dir, s, report, fillMap)
48
+ const ok = edited.written.pairs.map((pair) => ({ pair_id: pair.pair_id, verdict: "ok" }))
49
+ const readers = ["cheap", "strong"].flatMap((tier) => ["--verdicts", verdicts(dir, `${tier}.json`, tier, ok)])
50
+ const checked = nodeJson(["fact", "meaning-check", "--pairs", join(dir, "pairs-changed.json"), ...readers, ...s])
51
+ // then
52
+ expect([applied.code, applied.json.data.applied, applied.json.data.rejected]).toEqual([0, 1, 0])
53
+ expect(readFileSync(fillMap, "utf8")).toBe(before)
54
+ expect(reworded.run.code).toBe(0)
55
+ expect(reworded.sentences.some((text) => text.includes("The operator stated"))).toBe(true)
56
+ expect(edited.run.code).toBe(0)
57
+ expect(edited.sentences.some((text) => text.includes("321 m"))).toBe(true)
58
+ expect(edited.sentences.some((text) => text.includes("The operator stated"))).toBe(true)
59
+ expect(edited.written.changed_since).toEqual({ fill_map: fillMap, narrowed: false })
60
+ expect(checked.code).toBe(0)
61
+ const record = JSON.parse(readFileSync(join(dir, "fidelity-meaning-defects.json"), "utf8"))
62
+ expect(record).toMatchObject({ record_kind: "meaning", status: "pass", report, artifact_sha256: sha256(report) })
63
+ })
64
+
65
+ test("#given a report polished to a new file #when fact pairs runs --changed-since on either map #then only the reworded sentence, and after a value edit also the edited one", () => {
66
+ // given
67
+ const { dir, s } = polishSession()
68
+ const report = join(dir, "report.html")
69
+ const fillMap = `${report}.fill-map.json`
70
+ const out = join(dir, "report.polished.html")
71
+ const polishedMap = `${out}.polished.fill-map.json`
72
+ const before = readFileSync(fillMap, "utf8")
73
+ const applied = polish(dir, s, out)
74
+ // when
75
+ const viaPolished = pairs(dir, s, out, polishedMap)
76
+ const viaFill = pairs(dir, s, out, fillMap)
77
+ editValue(out)
78
+ const edited = pairs(dir, s, out, polishedMap)
79
+ // then
80
+ expect(applied.code).toBe(0)
81
+ expect(readFileSync(fillMap, "utf8")).toBe(before)
82
+ expect(existsSync(`${out}.fill-map.json`)).toBe(false)
83
+ expect(JSON.parse(readFileSync(polishedMap, "utf8"))).toMatchObject({
84
+ artifact: out,
85
+ brief_sha256: JSON.parse(before).brief_sha256,
86
+ polishedFrom: { map: fillMap, sha256: sha256(fillMap) },
87
+ })
88
+ for (const run of [viaPolished, viaFill]) {
89
+ expect(run.run.code).toBe(0)
90
+ expect(run.sentences).toEqual([expect.stringContaining("The operator stated")])
91
+ expect(run.written.changed_since).toEqual({ fill_map: fillMap, narrowed: true })
92
+ }
93
+ expect(edited.sentences).toEqual([expect.stringContaining("321 m"), expect.stringContaining("The operator stated")])
94
+ })
95
+
96
+ test("#given a fill map that does not record the report's bytes #when fact polish-apply runs #then it exits 2 and writes nothing", () => {
97
+ // given
98
+ const { dir, s } = polishSession()
99
+ const fillMap = join(dir, "report.html.fill-map.json")
100
+ const out = join(dir, "report.polished.html")
101
+ writeFileSync(fillMap, JSON.stringify({ ...JSON.parse(readFileSync(fillMap, "utf8")), artifact_sha256: "0".repeat(64) }))
102
+ // when
103
+ const tampered = polish(dir, s, out)
104
+ // then
105
+ expect(tampered.code).toBe(2)
106
+ expect(tampered.stderr).toContain(fillMap)
107
+ expect([out, `${out}.polished.fill-map.json`, `${out}.fill-map.json`].map(existsSync)).toEqual([false, false, false])
108
+ })
109
+
110
+ test("#given the pre-polish fill map rewritten after polish #when fact pairs follows the polished map #then it exits 2", () => {
111
+ // given
112
+ const { dir, s } = polishSession()
113
+ const fillMap = join(dir, "report.html.fill-map.json")
114
+ const out = join(dir, "report.polished.html")
115
+ polish(dir, s, out)
116
+ writeFileSync(fillMap, JSON.stringify({ ...JSON.parse(readFileSync(fillMap, "utf8")), entries: [] }))
117
+ // when
118
+ const followed = pairs(dir, s, out, `${out}.polished.fill-map.json`)
119
+ // then
120
+ expect(followed.run.code).toBe(2)
121
+ expect(followed.run.stderr).toContain(fillMap)
122
+ })
123
+ })
124
+
125
+ describe("fact derive takes its time from the caller", () => {
126
+ test("#given no --now #when fact derive runs #then it exits 2 with missing_now and appends nothing; with --now the row carries that time", () => {
127
+ // given
128
+ const { dir, s } = fidelitySession()
129
+ const rows = () => JSON.parse(readFileSync(join(dir, "fact-ledger.json"), "utf8")).rows.length
130
+ const count = rows()
131
+ // when
132
+ const missing = nodeJson(["fact", "derive", "--op", "convert", "F1", "--to", "km", "--label", "span_km", "--json", ...s])
133
+ const after = rows()
134
+ const stamped = nodeJson(["fact", "derive", "--op", "convert", "F1", "--to", "km", "--label", "span_km", "--now", NOW, "--json", ...s])
135
+ // then
136
+ expect(missing.code).toBe(2)
137
+ expect(missing.stderr).toContain("missing_now")
138
+ expect(after).toBe(count)
139
+ expect(stamped.code).toBe(0)
140
+ expect(stamped.json.data.row).toMatchObject({ observed_at: "2026-10-23T08:00:00.000Z" })
141
+ })
142
+ })
143
+
144
+ describe("fact verify names a missing fill map", () => {
145
+ test("#given an HTML edition with no fill map #when fact verify runs #then brief_changed says no fill map at its path", () => {
146
+ // given
147
+ const { dir, s } = fidelitySession()
148
+ nodeJson(["fact", "fill", join(dir, "report.src.html"), "--out", join(dir, "report.html"), ...s])
149
+ const map = join(dir, "report.html.fill-map.json")
150
+ rmSync(map)
151
+ // when
152
+ const checked = verify(dir, "report.html")
153
+ // then
154
+ expect([checked.code, codesOf(checked.json)]).toEqual([1, ["brief_changed"]])
155
+ expect((checked.json.defects[0] as { message?: string }).message).toContain(`no fill map at ${map}`)
156
+ })
157
+ })
@@ -0,0 +1,72 @@
1
+ import { expect, onTestFinished, test } from "bun:test"
2
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"
3
+ import { tmpdir } from "node:os"
4
+ import { join } from "node:path"
5
+
6
+ import { derive } from "./fact-derive.mjs"
7
+ import { fill } from "./fact-fill.mjs"
8
+ import { addRow, emptyLedger } from "./fact-ledger.mjs"
9
+ import { kindRuleErrors, readFlaws } from "./fact-ledger-kinds.mjs"
10
+ import { valueChecks } from "./fidelity-values.mjs"
11
+ import { buildContext } from "./gates-static.mjs"
12
+ import { parseHtml } from "./html-lite.mjs"
13
+ import { parsePlaceholder, valueParts } from "./placeholder.mjs"
14
+ import { checkMetadata } from "./unit-metadata.mjs"
15
+
16
+ const NOW = "2026-10-23T08:00:00Z"
17
+ const FIX = join(import.meta.dir, "tests", "fixtures", "fidelity")
18
+ const fixture = () => JSON.parse(readFileSync(join(FIX, "ledger-verify.json"), "utf8"))
19
+ const book = () => {
20
+ const ledger = emptyLedger() as { schemaVersion: number; rows: Record<string, unknown>[]; events: unknown[] }
21
+ for (const value of ["3", "1"]) addRow(ledger, { kind: "measured", value, unit: "m", raw: value + " m", quote: value + " m", source_id: "S1", locator: { artifact: "text.txt", char_start: 0, char_end: 3 }, snapshot_sha256: "a".repeat(64), extracted_by: "test", observed_at: NOW, label: "length-" + value })
22
+ return ledger
23
+ }
24
+
25
+ test("quoted CSV labels preserve fact_id plot_value and unit columns", () => {
26
+ const dir = mkdtempSync(join(tmpdir(), "value-csv-"))
27
+ onTestFinished(() => rmSync(dir, { recursive: true, force: true }))
28
+ writeFileSync(join(dir, "chart.csv"), 'fact_id,label,plot_value,unit\nF5,"a, b",12,%\n')
29
+ const ctx = buildContext(parseHtml('<figure data-chart-source="chart.csv"><svg><text>12%</text></svg></figure>'), { baseDir: dir })
30
+ expect(valueChecks(ctx, fixture(), null, null)).toEqual([])
31
+ })
32
+
33
+ test("quote exact with unknown keys still violates the quote null rule", () => {
34
+ const quote = fixture().rows.find((r: { fact_id: string }) => r.fact_id === "Q1")
35
+ expect(kindRuleErrors({ ...quote, exact: { num: "3", den: "1", note: "x" } }, "Q1")).toContain("Q1.exact must be null for a quote row")
36
+ })
37
+
38
+ test("negative decimal zero agrees with exact zero in dimension 32", () => {
39
+ const row = book().rows[0]
40
+ expect(readFlaws({ ...row, value: "-0.0", exact: { num: "0", den: "1" } })).toEqual([])
41
+ })
42
+
43
+ test("derive without caller time refuses without appending", () => {
44
+ const ledger = book()
45
+ expect(derive(ledger, { op: "diff", operands: ["F1", "F2"], unit: "m" })).toMatchObject({ error: "missing_now", shape: "missing_now" })
46
+ expect(ledger.rows).toHaveLength(2)
47
+ })
48
+
49
+ test("addRow validation errors are not incompatible operands", () => {
50
+ const ledger = book()
51
+ expect(() => derive(ledger, { op: "diff", operands: ["F1", "F2"], unit: "m", now: NOW, label: "invalid label" })).toThrow("invalid fact ledger")
52
+ expect(ledger.rows).toHaveLength(2)
53
+ })
54
+
55
+ test("prefix currency value uses the original source text", () => {
56
+ expect(valueParts({ raw: "$4.2m", unit: "USD", value: "4200000" }).valueText).toBe("$4.2m")
57
+ })
58
+
59
+ test("zero padded placeholder id is rejected during parsing", () => {
60
+ expect(parsePlaceholder("{{F012:x}}")).toMatchObject({ error: expect.stringContaining("id") })
61
+ })
62
+
63
+ test("literal opening braces fail with an actionable escape", () => {
64
+ const result = fill("Example: \u0060{{\u0060", { rows: [] }, { format: "md" })
65
+ expect(result.errors).toEqual([expect.objectContaining({ code: "unresolved_placeholder", message: expect.stringContaining("&#123;") })])
66
+ })
67
+
68
+ test("null metadata matches null but never a declared population", () => {
69
+ const a = book().rows[0]
70
+ expect(() => checkMetadata([a, { ...a }], ["population"])).not.toThrow()
71
+ expect(() => checkMetadata([a, { ...a, population: "all" }], ["population"])).toThrow("population differs")
72
+ })
@@ -0,0 +1,80 @@
1
+ import { expect, onTestFinished, test } from "bun:test"
2
+ import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"
3
+ import { tmpdir } from "node:os"
4
+ import { join } from "node:path"
5
+
6
+ import { sha256Of } from "./cli-support.mjs"
7
+ import { runFactPolish } from "./fact-commands-polish.mjs"
8
+ import { runFactVerify } from "./fact-commands-verify.mjs"
9
+ import { valueChecks } from "./fidelity-values.mjs"
10
+ import { buildContext } from "./gates-static.mjs"
11
+ import { parseHtml } from "./html-lite.mjs"
12
+ import { briefHashOf } from "./fidelity-policy.mjs"
13
+ import { parseFidelityBlock } from "./fidelity-brief.mjs"
14
+
15
+ const FIX = join(import.meta.dir, "tests", "fixtures")
16
+ const ledger = () => JSON.parse(readFileSync(join(FIX, "fidelity", "ledger-verify.json"), "utf8"))
17
+ // Source-backed rows are checked against their pinned snapshots; a missing sources dir blocks them.
18
+ const check = (html: string, book: ReturnType<typeof ledger>, options = {}) => valueChecks(buildContext(parseHtml(html), {}), book, null, join(FIX, "fidelity", "sources"), options)
19
+ const codes = (defects: { code: string }[]) => defects.map((d) => d.code)
20
+
21
+ test("revision v1 does not match the brief's v12", () => {
22
+ const book = ledger()
23
+ book.rows.find((r: { fact_id: string }) => r.fact_id === "Q1").revision = "v1"
24
+ const html = '<p><q data-fact="Q1">we estimate completion no earlier than 2027</q></p>'
25
+ expect(check(html, book, { asOf: "v12 release" }).map((d: { code: string }) => d.code)).toContain("quote_stale_version")
26
+ expect(check(html, book, { asOf: "v1 release" })).toEqual([])
27
+ })
28
+
29
+ test("a sentence-final period after the brief's revision still matches it", () => {
30
+ const book = ledger()
31
+ book.rows.find((r: { fact_id: string }) => r.fact_id === "Q1").revision = "v1.2"
32
+ const html = '<p><q data-fact="Q1">we estimate completion no earlier than 2027</q></p>'
33
+ expect(check(html, book, { asOf: "release v1.2." })).toEqual([])
34
+ expect(codes(check(html, book, { asOf: "release v1.2.3." }))).toContain("quote_stale_version")
35
+ expect(codes(check(html, book, { asOf: "release v1.20." }))).toContain("quote_stale_version")
36
+ })
37
+
38
+ test("an operand id absent from the ledger is operand_missing, not a missing timestamp", () => {
39
+ const book = ledger()
40
+ const row = book.rows.find((r: { fact_id: string }) => r.fact_id === "F8")
41
+ row.snapshot_window = { dataset_id: "d1", start: "2026-09-01T00:00:00Z", end: "2026-11-01T00:00:00Z" }
42
+ row.operands = ["F99"]
43
+ const found = codes(check('<p><data data-fact="F8">320,000 mm (320 m)</data></p>', book))
44
+ expect(found).toContain("operand_missing")
45
+ expect(found).not.toContain("operand_timestamp_missing")
46
+ expect(found).not.toContain("snapshot_mismatch")
47
+ })
48
+
49
+ test("missing operand observed_at is not a snapshot window mismatch", () => {
50
+ const book = ledger()
51
+ const row = book.rows.find((r: { fact_id: string }) => r.fact_id === "F8")
52
+ row.snapshot_window = { dataset_id: "d1", start: "2026-11-01T00:00:00Z", end: "2026-12-01T00:00:00Z" }
53
+ delete book.rows.find((r: { fact_id: string }) => r.fact_id === row.operands[0]).observed_at
54
+ const defects = check('<p><data data-fact="F8">320,000 mm (320 m)</data></p>', book)
55
+ expect(defects.map((d: { code: string }) => d.code)).toContain("operand_timestamp_missing")
56
+ expect(defects.map((d: { code: string }) => d.code)).not.toContain("snapshot_mismatch")
57
+ expect(defects.find((d: { code: string }) => d.code === "operand_timestamp_missing").message).toContain("observed_at")
58
+ })
59
+
60
+ test("polished edition retains original brief binding after brief changes", () => {
61
+ const dir = mkdtempSync(join(tmpdir(), "polish-binding-"))
62
+ onTestFinished(() => rmSync(dir, { recursive: true, force: true }))
63
+ const brief = readFileSync(join(FIX, "fidelity-brief-locked.md"), "utf8")
64
+ const report = join(dir, "report.html")
65
+ const output = join(dir, "polished.html")
66
+ const html = '<html><body><p>The overview remains clear.</p></body></html>'
67
+ writeFileSync(join(dir, "brief.md"), brief)
68
+ writeFileSync(join(dir, "fact-ledger.json"), JSON.stringify({ schemaVersion: 1, rows: [], events: [] }))
69
+ writeFileSync(report, html)
70
+ writeFileSync(report + ".fill-map.json", JSON.stringify({ format: "html", artifact: report, artifact_sha256: sha256Of(html), entries: [], brief_sha256: briefHashOf(parseFidelityBlock(brief)) }))
71
+ writeFileSync(join(dir, "edits.json"), "[]")
72
+ expect(runFactPolish("polish-apply", [report, "--session-dir", dir, "--edits", join(dir, "edits.json"), "--out", output]).exitCode).toBe(0)
73
+ const verified = () => codes((runFactVerify({ positionals: [output], flags: { "session-dir": dir } }) as { json: { defects: { code: string }[] } }).json.defects)
74
+ expect(verified()).not.toContain("brief_changed")
75
+ writeFileSync(join(dir, "brief.md"), brief.replace("derived numbers: allowed", "derived numbers: none"))
76
+ expect(verified()).toContain("brief_changed")
77
+ expect(existsSync(output + ".fill-map.json")).toBe(false)
78
+ const map = JSON.parse(readFileSync(output + ".polished.fill-map.json", "utf8"))
79
+ expect(map.brief_sha256).toBe(briefHashOf(parseFidelityBlock(brief)))
80
+ })
@@ -20,6 +20,7 @@ export interface ExecutorContext {
20
20
  title: string;
21
21
  }) => Promise<void>;
22
22
  syncPollTimeoutMs?: number;
23
+ taskCleanupDelayMs?: number;
23
24
  }
24
25
  export interface ParentContext {
25
26
  sessionID: string;
@@ -0,0 +1,4 @@
1
+ import type { OpencodeClient } from "./types";
2
+ export declare function isDefiniteSessionNotFound(error: unknown): boolean;
3
+ export declare function isSyncSessionDeleted(client: OpencodeClient, sessionID: string): Promise<boolean>;
4
+ export declare function formatDeletedSyncSessionError(sessionID: string): string;
@@ -129,6 +129,7 @@ export interface DelegateTaskToolOptions {
129
129
  modelFallbackControllerAccessor?: ModelFallbackControllerAccessor;
130
130
  onSyncSessionCreated?: (event: SyncSessionCreatedEvent) => Promise<void>;
131
131
  syncPollTimeoutMs?: number;
132
+ taskCleanupDelayMs?: number;
132
133
  /** OpenCode native skill accessor for skills registered via config.skills.paths. Same shape as SkillLoadOptions.nativeSkills. */
133
134
  nativeSkills?: {
134
135
  all(): {
@@ -1,7 +1,8 @@
1
- import type { SkillInfo } from "./types";
1
+ import type { CoreListedSkill, SkillInfo } from "./types";
2
2
  import type { CommandInfo } from "../slashcommand/types";
3
3
  interface CombinedDescriptionOptions {
4
4
  includeSkills?: boolean;
5
+ coreListedSkills?: readonly CoreListedSkill[];
5
6
  }
6
7
  export declare function deduplicatePathAliasedSkills(skills: SkillInfo[]): SkillInfo[];
7
8
  export declare function formatCombinedDescription(skills?: SkillInfo[], commands?: CommandInfo[], options?: CombinedDescriptionOptions): string;
@@ -16,6 +16,12 @@ export interface SkillInfo {
16
16
  metadata?: Record<string, string>;
17
17
  allowedTools?: string[];
18
18
  }
19
+ /** One entry of OpenCode core's own skill list (Skill.all()), as returned by SkillLoadOptions.nativeSkills.all(). */
20
+ export interface CoreListedSkill {
21
+ name: string;
22
+ description?: string;
23
+ location: string;
24
+ }
19
25
  export interface SkillLoadOptions {
20
26
  /** When true, only load from OpenCode paths (.opencode/skills/, ~/.config/opencode/skills/) */
21
27
  opencodeOnly?: boolean;