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,186 @@
1
+ import { expect, test } from "bun:test"
2
+ import { readFileSync, unlinkSync, writeFileSync } from "node:fs"
3
+ import { join } from "node:path"
4
+
5
+ import { sha256Of } from "./cli-support.mjs"
6
+ import { MEANING_RECORD, writeFidelityRecord } from "./fidelity-records.mjs"
7
+ import { delivered, expectRefused, fullyRecorded, LOCKED_BRIEF, meaningFile, type Recorded } from "./tests/delivery-barrier-session.ts"
8
+
9
+ /** A delivered, fidelity-verified html edition in a session whose locked brief sets `meaning check`. */
10
+ async function meaningSession(meaning: "on" | "off", codes: string[] | null) {
11
+ const session = await fullyRecorded(LOCKED_BRIEF.replace("meaning check: on", `meaning check: ${meaning}`))
12
+ if (codes === null) unlinkSync(session.recordPath.meaning)
13
+ else session.write("meaning", { defects: codes.map((code) => ({ code, severity: "major", message: code })) })
14
+ return session
15
+ }
16
+
17
+ for (const code of ["meaning_drift_confirmed", "meaning_unverified"]) {
18
+ test(`#given a meaning record with ${code} #when verified and finished #then delivery refuses`, async () => {
19
+ const session = await meaningSession("on", [code])
20
+ await expectRefused(session, { code: "fidelity_unresolved", codes: [code], path: session.recordPath.meaning })
21
+ })
22
+ }
23
+
24
+ test("#given the brief says meaning check on and the meaning step was skipped #when verified and finished #then delivery refuses: meaning gate not run", async () => {
25
+ const session = await meaningSession("on", null)
26
+ await expectRefused(session, { message: expect.stringContaining("meaning gate not run") })
27
+ })
28
+
29
+ test("#given a malformed ## Fidelity block that tries to turn meaning check off #when no meaning record exists #then delivery refuses: meaning gate not run", async () => {
30
+ const malformed = LOCKED_BRIEF.replace("meaning check: on (answered_by: default)", "meaning check: on (answered_by: default)\nmeaning check: off (answered_by: user)")
31
+ const session = await fullyRecorded(malformed)
32
+ unlinkSync(session.recordPath.meaning)
33
+ await expectRefused(session, { message: expect.stringContaining("meaning gate not run") })
34
+ })
35
+
36
+ test("#given the brief says meaning check off and no meaning record #when verified and finished #then delivery passes", async () => {
37
+ const session = await meaningSession("off", null)
38
+ expect((await session.outcomes()).map((result) => result.exitCode)).toEqual([0, 0])
39
+ })
40
+
41
+ test("#given a clean meaning record (warn only) under meaning check on #when verified #then delivery passes", async () => {
42
+ const { invoke } = await meaningSession("on", ["meaning_drift_warn"])
43
+ expect((await invoke(["verify"])).exitCode).toBe(0)
44
+ })
45
+
46
+ test("#given meaning check on, no fidelity evidence, and an explicit fidelity not_run #when verified #then the logged exemption covers the meaning step too", async () => {
47
+ const { dir, invoke } = await delivered()
48
+ writeFileSync(join(dir, "brief.md"), LOCKED_BRIEF)
49
+ await invoke(["gate", "fidelity", "not_run", "--reason", "No factual content in this edition"])
50
+ expect((await invoke(["verify"])).exitCode).toBe(0)
51
+ })
52
+
53
+ test("#given an edition meaning record but no fidelity record #when the fidelity gate is skipped with a reason #then delivery still refuses", async () => {
54
+ const { dir, path, invoke } = await delivered()
55
+ writeFileSync(join(dir, "brief.md"), LOCKED_BRIEF)
56
+ writeFidelityRecord(meaningFile(dir, "html"), "meaning", { artifact_sha256: sha256Of(readFileSync(path)), report: path, defects: [] })
57
+ await invoke(["gate", "fidelity", "not_run", "--reason", "This harness has no verifier"])
58
+ const result = await invoke(["verify"])
59
+ expect(result.exitCode).toBe(1)
60
+ expect(JSON.stringify(result.json.problems)).toContain("fidelity gate not run")
61
+ })
62
+
63
+ /** Deliver a second edition with byte-different text and its own clean fidelity record. */
64
+ async function secondEdition(session: Recorded, format: string, text: string) {
65
+ const path = join(session.dir, `second.${format}`)
66
+ const record = join(session.dir, "second-fidelity.json")
67
+ writeFileSync(path, text)
68
+ writeFidelityRecord(record, "fidelity", { artifact_sha256: sha256Of(text), report: path, defects: [] })
69
+ await session.invoke(["set", format, "delivered", "--path", path, "--verified-by", record])
70
+ const meaning = meaningFile(session.dir, format)
71
+ const writeMeaning = (fields: Record<string, unknown> = {}) => writeFidelityRecord(meaning, "meaning", { artifact_sha256: sha256Of(text), report: path, defects: [], ...fields })
72
+ const problems = async () => (await session.invoke(["verify"])).json.problems
73
+ return { path, meaning, writeMeaning, problems }
74
+ }
75
+
76
+ for (const [format, text] of [["md", "Report in Markdown."], ["post", "Report in a post."]]) {
77
+ test(`#given html and ${format} delivered, each with its own meaning record #when verified and finished #then delivery passes`, async () => {
78
+ const session = await fullyRecorded()
79
+ const second = await secondEdition(session, format, text)
80
+ second.writeMeaning()
81
+ expect((await session.outcomes()).map((result) => result.exitCode)).toEqual([0, 0])
82
+ })
83
+
84
+ test(`#given html and ${format} delivered but only html has a meaning record #when verified #then only ${format} refuses: meaning gate not run`, async () => {
85
+ const session = await fullyRecorded()
86
+ const second = await secondEdition(session, format, text)
87
+ expect(await second.problems()).toEqual([expect.objectContaining({ code: "edition_unverified", format, path: second.meaning, message: expect.stringContaining(`meaning gate not run; run fact pairs and fact meaning-check --edition ${format}`) })])
88
+ })
89
+
90
+ test(`#given the ${format} meaning record is for other bytes #when verified #then only ${format} refuses: a different edition`, async () => {
91
+ const session = await fullyRecorded()
92
+ const second = await secondEdition(session, format, text)
93
+ second.writeMeaning({ artifact_sha256: sha256Of("an earlier draft") })
94
+ expect(await second.problems()).toEqual([expect.objectContaining({ code: "edition_unverified", format, path: second.meaning, message: expect.stringContaining("is for a different edition") })])
95
+ })
96
+
97
+ test(`#given the ${format} meaning record carries a confirmed drift #when verified #then only that record blocks`, async () => {
98
+ const session = await fullyRecorded()
99
+ const second = await secondEdition(session, format, text)
100
+ second.writeMeaning({ defects: [{ code: "meaning_drift_confirmed", severity: "major", message: "drift" }] })
101
+ expect(await second.problems()).toEqual([expect.objectContaining({ code: "fidelity_unresolved", format, codes: ["meaning_drift_confirmed"], path: second.meaning })])
102
+ })
103
+
104
+ test(`#given one legacy session meaning record checked on html and ${format} delivered #when verified #then ${format} refuses: a different edition`, async () => {
105
+ const session = await fullyRecorded()
106
+ unlinkSync(session.recordPath.meaning)
107
+ writeFidelityRecord(join(session.dir, MEANING_RECORD), "meaning", { artifact_sha256: sha256Of(readFileSync(session.path)), report: session.path, defects: [] })
108
+ const second = await secondEdition(session, format, text)
109
+ expect(await second.problems()).toEqual([expect.objectContaining({ code: "edition_unverified", format, path: join(session.dir, MEANING_RECORD), message: expect.stringContaining("is for a different edition") })])
110
+ })
111
+ }
112
+
113
+ test("#given a single edition and only the legacy session meaning record for its bytes #when verified and finished #then delivery passes", async () => {
114
+ const session = await fullyRecorded()
115
+ unlinkSync(session.recordPath.meaning)
116
+ writeFidelityRecord(join(session.dir, MEANING_RECORD), "meaning", { artifact_sha256: sha256Of(readFileSync(session.path)), report: session.path, defects: [] })
117
+ expect((await session.outcomes()).map((result) => result.exitCode)).toEqual([0, 0])
118
+ })
119
+
120
+ test("#given a pdf rendered from the html source #when only the html edition has a meaning record #then the pdf shares it and delivery passes", async () => {
121
+ const session = await fullyRecorded()
122
+ const pdf = join(session.dir, "report.pdf")
123
+ writeFileSync(pdf, "%PDF-1.7 rendered")
124
+ await session.invoke(["set", "pdf", "delivered", "--path", pdf, "--rendered-from", session.path, "--verified-by", session.record])
125
+ expect((await session.outcomes()).map((result) => result.exitCode)).toEqual([0, 0])
126
+ })
127
+
128
+ test("#given html and a byte-identical md copy at another path #when only the html edition has a meaning record #then the md binds to it by bytes and delivery passes", async () => {
129
+ const session = await fullyRecorded()
130
+ const second = await secondEdition(session, "md", readFileSync(session.path, "utf8"))
131
+ expect(await second.problems()).toEqual([])
132
+ expect((await session.outcomes()).map((result) => result.exitCode)).toEqual([0, 0])
133
+ })
134
+
135
+ // Reader runs on the same bytes are ordered by their records' written_at, never by file name.
136
+ const EARLIER = new Date("2026-01-01T10:00:00.000Z")
137
+ const LATER = new Date("2026-01-01T11:00:00.000Z")
138
+ const DRIFT = [{ code: "meaning_drift_confirmed", severity: "major", message: "drift" }]
139
+
140
+ /** Write a meaning record for the delivered html bytes at an explicit time. */
141
+ const meaningAt = (session: Recorded, path: string, at: Date, defects: Record<string, unknown>[] = []) =>
142
+ writeFidelityRecord(path, "meaning", { artifact_sha256: sha256Of(readFileSync(session.path)), report: session.path, defects }, at)
143
+
144
+ /** Deliver a pdf rendered from the html source; it has no meaning record of its own. */
145
+ const deliverPdf = async (session: Recorded) => {
146
+ const pdf = join(session.dir, "report.pdf")
147
+ writeFileSync(pdf, "%PDF-1.7 rendered")
148
+ await session.invoke(["set", "pdf", "delivered", "--path", pdf, "--rendered-from", session.path, "--verified-by", session.record])
149
+ }
150
+
151
+ test("#given a clean own record written after a legacy record confirming drift on the same bytes #when verified and finished #then the newer clean run passes", async () => {
152
+ const session = await fullyRecorded()
153
+ meaningAt(session, join(session.dir, MEANING_RECORD), EARLIER, DRIFT)
154
+ meaningAt(session, session.recordPath.meaning, LATER)
155
+ expect((await session.outcomes()).map((result) => result.exitCode)).toEqual([0, 0])
156
+ })
157
+
158
+ test("#given a legacy record confirming drift written after a clean own record on the same bytes #when verified and finished #then the newer drift refuses", async () => {
159
+ const session = await fullyRecorded()
160
+ meaningAt(session, session.recordPath.meaning, EARLIER)
161
+ meaningAt(session, join(session.dir, MEANING_RECORD), LATER, DRIFT)
162
+ await expectRefused(session, { code: "fidelity_unresolved", codes: ["meaning_drift_confirmed"], path: join(session.dir, MEANING_RECORD) })
163
+ })
164
+
165
+ test("#given a clean own record and a legacy drift record written at the same time on the same bytes #when verified and finished #then the more severe verdict refuses", async () => {
166
+ const session = await fullyRecorded()
167
+ meaningAt(session, session.recordPath.meaning, LATER)
168
+ meaningAt(session, join(session.dir, MEANING_RECORD), LATER, DRIFT)
169
+ await expectRefused(session, { code: "fidelity_unresolved", codes: ["meaning_drift_confirmed"], path: join(session.dir, MEANING_RECORD) })
170
+ })
171
+
172
+ test("#given two edition records for the same bytes, the alphabetically-first clean and older #when a pdf of those bytes is verified and finished #then the newer drift refuses", async () => {
173
+ const session = await fullyRecorded()
174
+ await deliverPdf(session)
175
+ meaningAt(session, meaningFile(session.dir, "html"), EARLIER)
176
+ meaningAt(session, meaningFile(session.dir, "post"), LATER, DRIFT)
177
+ await expectRefused(session, { code: "fidelity_unresolved", codes: ["meaning_drift_confirmed"], path: meaningFile(session.dir, "post") })
178
+ })
179
+
180
+ test("#given two edition records for the same bytes, the alphabetically-first confirming drift and older #when verified and finished #then the newer clean run passes", async () => {
181
+ const session = await fullyRecorded()
182
+ await deliverPdf(session)
183
+ meaningAt(session, meaningFile(session.dir, "html"), EARLIER, DRIFT)
184
+ meaningAt(session, meaningFile(session.dir, "post"), LATER)
185
+ expect((await session.outcomes()).map((result) => result.exitCode)).toEqual([0, 0])
186
+ })
@@ -1,28 +1,12 @@
1
1
  import { describe, expect, onTestFinished, test } from "bun:test"
2
- import { mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs"
2
+ import { mkdtempSync, readFileSync, rmSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs"
3
3
  import { tmpdir } from "node:os"
4
- import { join } from "node:path"
4
+ import { dirname, join } from "node:path"
5
5
 
6
6
  import { sha256Of } from "./cli-support.mjs"
7
7
  import { DEFECT_CODES } from "./contracts.mjs"
8
- import { FIDELITY_RECORDS, MEANING_RECORD, RECORD_ENVELOPE, writeFidelityRecord } from "./fidelity-records.mjs"
9
- import { blocksDelivery } from "./gates-fidelity.mjs"
10
- import { run } from "./report-tools.mjs"
11
-
12
- async function delivered(format = "html", codes: string[] | null = null) {
13
- const root = mkdtempSync(join(tmpdir(), "delivery-barrier-"))
14
- onTestFinished(() => rmSync(root, { recursive: true, force: true }))
15
- const dir = join(root, "20260101-120000")
16
- mkdirSync(dir)
17
- const path = join(dir, `report.${format}`)
18
- const record = join(dir, "fidelity-defects.json")
19
- writeFileSync(path, "<p>Report</p>")
20
- if (codes !== null) writeFidelityRecord(record, "fidelity", { artifact_sha256: sha256Of(readFileSync(path)), report: path, defects: codes.map((code) => ({ code, severity: "minor", message: code })) })
21
- const invoke = (args: string[]) => run(["outcome", ...args, "--session-dir", dir])
22
- await invoke(["init", "--promised", format])
23
- expect((await invoke(["set", format, "delivered", "--path", path, ...(codes === null ? [] : ["--verified-by", record])])).exitCode).toBe(0)
24
- return { dir, path, record, invoke }
25
- }
8
+ import { fidelityProblems, FIDELITY_RECORDS, RECORD_ENVELOPE, writeFidelityRecord } from "./fidelity-records.mjs"
9
+ import { BLOCKER, delivered, expectRefused, fullyRecorded } from "./tests/delivery-barrier-session.ts"
26
10
 
27
11
  // Enumerate the source registry, not a second list of today's fidelity codes.
28
12
  for (const [code, entry] of Object.entries(DEFECT_CODES)) {
@@ -113,32 +97,6 @@ test("#given an explicit skip and an existing blocker #when verified #then the s
113
97
  expect((await invoke(["verify"])).exitCode).toBe(1)
114
98
  })
115
99
 
116
- const LOCKED_BRIEF = readFileSync(join(import.meta.dir, "tests", "fixtures", "fidelity-brief-locked.md"), "utf8")
117
- const BLOCKER = Object.keys(DEFECT_CODES).find((code) => blocksDelivery({ code })) as string
118
-
119
- /**
120
- * A delivered html edition whose locked brief requires every table record, each present, fresh for
121
- * the delivered bytes, and clean. recordPath is where the session keeps each row's record; a table
122
- * row with no entry here fails the coverage test below, so a new row brings its own cases.
123
- */
124
- async function fullyRecorded(brief = LOCKED_BRIEF) {
125
- const session = await delivered("html", [])
126
- writeFileSync(join(session.dir, "brief.md"), brief)
127
- const recordPath: Record<string, string> = { fidelity: session.record, meaning: join(session.dir, MEANING_RECORD) }
128
- const write = (kind: string, fields: Record<string, unknown> = {}) =>
129
- writeFidelityRecord(recordPath[kind], kind, { artifact_sha256: sha256Of(readFileSync(session.path)), report: session.path, defects: [], ...fields })
130
- write("meaning")
131
- const outcomes = async () => Promise.all(["verify", "finish"].map((command) => session.invoke([command])))
132
- return { ...session, recordPath, write, outcomes }
133
- }
134
-
135
- async function expectRefused(session: Awaited<ReturnType<typeof fullyRecorded>>, problem: Record<string, unknown>) {
136
- for (const result of await session.outcomes()) {
137
- expect(result.exitCode).toBe(1)
138
- expect(result.json.problems).toContainEqual(expect.objectContaining(problem))
139
- }
140
- }
141
-
142
100
  test("#given the record table #when every row is read #then each row has every rule column and an arranged session path", async () => {
143
101
  const { recordPath } = await fullyRecorded()
144
102
  expect(Object.keys(recordPath).sort()).toEqual(FIDELITY_RECORDS.map((entry) => entry.kind).sort())
@@ -148,7 +106,7 @@ test("#given the record table #when every row is read #then each row has every r
148
106
  }
149
107
  })
150
108
 
151
- // The table is the barrier's spec: every row gets the same five cases.
109
+ // The table is the barrier's spec: every row gets the same six cases.
152
110
  for (const entry of FIDELITY_RECORDS) {
153
111
  describe(`record row ${entry.kind}`, () => {
154
112
  test("#given the record is missing while required #when verified and finished #then delivery refuses", async () => {
@@ -157,6 +115,13 @@ for (const entry of FIDELITY_RECORDS) {
157
115
  await expectRefused(session, { code: "edition_unverified", path: session.recordPath[entry.kind] })
158
116
  })
159
117
 
118
+ test("#given the record is missing and the row names none #when verified and finished #then delivery refuses: the step was not run", async () => {
119
+ const session = await fullyRecorded()
120
+ unlinkSync(session.recordPath[entry.kind])
121
+ await session.invoke(["set", "html", "delivered", "--path", session.path])
122
+ await expectRefused(session, { code: "edition_unverified", message: expect.stringContaining(`${entry.step} not run`) })
123
+ })
124
+
160
125
  test("#given the record is for other bytes #when verified and finished #then delivery refuses: a different edition", async () => {
161
126
  const session = await fullyRecorded()
162
127
  session.write(entry.kind, { artifact_sha256: sha256Of("an earlier draft") })
@@ -191,6 +156,12 @@ for (const entry of FIDELITY_RECORDS) {
191
156
  })
192
157
  }
193
158
 
159
+ test("#given a row whose --verified-by record is gone #when verified and finished #then delivery refuses: the record is missing", async () => {
160
+ const session = await fullyRecorded()
161
+ unlinkSync(session.record)
162
+ await expectRefused(session, { code: "edition_unverified", format: "html", path: session.record, message: `html: fidelity record ${session.record} is missing` })
163
+ })
164
+
194
165
  test("#given a record kind outside the table or a malformed envelope #when written #then the writer refuses", () => {
195
166
  const dir = mkdtempSync(join(tmpdir(), "delivery-barrier-writer-"))
196
167
  onTestFinished(() => rmSync(dir, { recursive: true, force: true }))
@@ -200,46 +171,28 @@ test("#given a record kind outside the table or a malformed envelope #when writt
200
171
  expect(() => writeFidelityRecord(join(dir, "c.json"), "meaning", { ...fields, gatesRun: [] })).toThrow("unknown field")
201
172
  })
202
173
 
203
- /** A delivered, fidelity-verified html edition in a session whose locked brief sets `meaning check`. */
204
- async function meaningSession(meaning: "on" | "off", codes: string[] | null) {
205
- const session = await fullyRecorded(LOCKED_BRIEF.replace("meaning check: on", `meaning check: ${meaning}`))
206
- if (codes === null) unlinkSync(session.recordPath.meaning)
207
- else session.write("meaning", { defects: codes.map((code) => ({ code, severity: "major", message: code })) })
208
- return session
209
- }
174
+ test("#given an unattached session fidelity blocker and a clean attached record #when verified and finished #then the session blocker refuses", async () => {
175
+ const session = await fullyRecorded()
176
+ const other = join(session.dir, "other.json")
177
+ writeFidelityRecord(other, "fidelity", { artifact_sha256: sha256Of(readFileSync(session.path)), report: session.path, defects: [] })
178
+ await session.invoke(["set", "html", "delivered", "--path", session.path, "--verified-by", other])
179
+ session.write("fidelity", { defects: [{ code: BLOCKER }] })
180
+ await expectRefused(session, { code: "fidelity_unresolved", codes: [BLOCKER], path: session.record })
181
+ })
210
182
 
211
- for (const code of ["meaning_drift_confirmed", "meaning_unverified"]) {
212
- test(`#given a meaning record with ${code} #when verified and finished #then delivery refuses`, async () => {
213
- const session = await meaningSession("on", [code])
214
- await expectRefused(session, { code: "fidelity_unresolved", codes: [code], path: join(session.dir, MEANING_RECORD) })
183
+ for (const [name, alias] of [
184
+ ["a dot segment", (dir: string) => `${dir}/./fidelity-defects.json`],
185
+ ["a directory link", (dir: string) => {
186
+ const link = join(dirname(dir), "alias")
187
+ symlinkSync(dir, link, "junction")
188
+ return join(link, "fidelity-defects.json")
189
+ }],
190
+ ] as const) {
191
+ test(`#given two target paths reaching one blocking record through ${name} #when the barrier runs #then it reports the blocker once`, async () => {
192
+ const session = await fullyRecorded()
193
+ session.write("fidelity", { defects: [{ code: BLOCKER }] })
194
+ const manifest = JSON.parse(readFileSync(join(session.dir, "outcome.json"), "utf8"))
195
+ manifest.deliverables.push({ ...manifest.deliverables[0], format: "md", verifiedBy: alias(session.dir) })
196
+ expect(fidelityProblems(session.dir, manifest).filter((problem) => problem.code === "fidelity_unresolved")).toHaveLength(1)
215
197
  })
216
198
  }
217
-
218
- test("#given the brief says meaning check on and the meaning step was skipped #when verified and finished #then delivery refuses: meaning gate not run", async () => {
219
- const session = await meaningSession("on", null)
220
- await expectRefused(session, { message: expect.stringContaining("meaning gate not run") })
221
- })
222
-
223
- test("#given a malformed ## Fidelity block that tries to turn meaning check off #when no meaning record exists #then delivery refuses: meaning gate not run", async () => {
224
- const malformed = LOCKED_BRIEF.replace("meaning check: on (answered_by: default)", "meaning check: on (answered_by: default)\nmeaning check: off (answered_by: user)")
225
- const session = await fullyRecorded(malformed)
226
- unlinkSync(session.recordPath.meaning)
227
- await expectRefused(session, { message: expect.stringContaining("meaning gate not run") })
228
- })
229
-
230
- test("#given the brief says meaning check off and no meaning record #when verified and finished #then delivery passes", async () => {
231
- const session = await meaningSession("off", null)
232
- expect((await session.outcomes()).map((result) => result.exitCode)).toEqual([0, 0])
233
- })
234
-
235
- test("#given a clean meaning record (warn only) under meaning check on #when verified #then delivery passes", async () => {
236
- const { invoke } = await meaningSession("on", ["meaning_drift_warn"])
237
- expect((await invoke(["verify"])).exitCode).toBe(0)
238
- })
239
-
240
- test("#given meaning check on, no fidelity evidence, and an explicit fidelity not_run #when verified #then the logged exemption covers the meaning step too", async () => {
241
- const { dir, invoke } = await delivered()
242
- writeFileSync(join(dir, "brief.md"), LOCKED_BRIEF)
243
- await invoke(["gate", "fidelity", "not_run", "--reason", "No factual content in this edition"])
244
- expect((await invoke(["verify"])).exitCode).toBe(0)
245
- })
@@ -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` */