oh-my-opencode 5.1.47 → 5.1.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (497) hide show
  1. package/dist/agents/refusal-rule.d.ts +7 -0
  2. package/dist/cli/index.js +1194 -752
  3. package/dist/cli/service-auth/accounts-gate.d.ts +11 -0
  4. package/dist/cli/service-auth/commands.d.ts +2 -4
  5. package/dist/cli/service-auth/index.d.ts +1 -1
  6. package/dist/cli/service-auth/keystore.d.ts +26 -2
  7. package/dist/cli/service-auth/protocol.d.ts +7 -1
  8. package/dist/cli/service-auth/session.d.ts +6 -0
  9. package/dist/cli/service-auth/signed-out-marker.d.ts +11 -0
  10. package/dist/cli-node/index.js +1194 -752
  11. package/dist/config/schema/hooks.d.ts +1 -0
  12. package/dist/create-hooks.d.ts +1 -0
  13. package/dist/features/boulder-state/storage.d.ts +1 -1
  14. package/dist/features/boulder-state/types.d.ts +1 -1
  15. package/dist/hooks/atlas/idle-continuation.d.ts +2 -1
  16. package/dist/hooks/atlas/types.d.ts +9 -1
  17. package/dist/hooks/index.d.ts +1 -0
  18. package/dist/hooks/momus-review-persistence/hook.d.ts +19 -0
  19. package/dist/hooks/momus-review-persistence/index.d.ts +1 -0
  20. package/dist/hooks/momus-review-persistence/review-resolution.d.ts +20 -0
  21. package/dist/hooks/prometheus-md-only/constants.d.ts +2 -0
  22. package/dist/hooks/prometheus-md-only/fixtures/denial-log.d.ts +1 -0
  23. package/dist/hooks/prometheus-md-only/fixtures/guarded-hashline.d.ts +1 -0
  24. package/dist/hooks/prometheus-md-only/fixtures/invoke-before.d.ts +7 -0
  25. package/dist/hooks/prometheus-md-only/fixtures/opencode-patch-parser.d.ts +2 -0
  26. package/dist/hooks/prometheus-md-only/hook.d.ts +5 -1
  27. package/dist/hooks/prometheus-md-only/path-policy.d.ts +2 -9
  28. package/dist/hooks/prometheus-md-only/vendor/opencode-patch-parser.d.ts +22 -0
  29. package/dist/index.js +2322 -1045
  30. package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
  31. package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +2 -1
  32. package/dist/shared/posthog-activity-state.d.ts +1 -0
  33. package/dist/shared/posthog.d.ts +1 -2
  34. package/dist/shared/resolve-file-write-path.d.ts +6 -0
  35. package/dist/skills/ast-grep/README.md +1 -1
  36. package/dist/skills/ast-grep/install.ps1 +5 -3
  37. package/dist/skills/ast-grep/install.sh +4 -5
  38. package/dist/skills/ast-grep/references/install.md +2 -2
  39. package/dist/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
  40. package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
  41. package/dist/skills/coding-agent-sessions/AGENTS.md +5 -4
  42. package/dist/skills/coding-agent-sessions/SKILL.md +11 -11
  43. package/dist/skills/coding-agent-sessions/references/all-platforms.md +2 -2
  44. package/dist/skills/coding-agent-sessions/references/codex.md +4 -4
  45. package/dist/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
  46. package/dist/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
  47. package/dist/skills/frontend/ATTRIBUTION.md +1 -1
  48. package/dist/skills/frontend/SKILL.md +34 -12
  49. package/dist/skills/frontend/references/design/README.md +2 -2
  50. package/dist/skills/frontend/references/design/_INDEX.md +2 -1
  51. package/dist/skills/frontend/references/design/component-catalogs.md +14 -10
  52. package/dist/skills/frontend/references/design/design-system-architecture.md +19 -6
  53. package/dist/skills/frontend/references/design/grounding.md +29 -0
  54. package/dist/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
  55. package/dist/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
  56. package/dist/skills/ultimate-browsing/SKILL.md +5 -4
  57. package/dist/skills/ultimate-browsing/engine/AGENTS.md +2 -2
  58. package/dist/skills/ultimate-browsing/engine/__main__.py +9 -6
  59. package/dist/skills/ultimate-browsing/engine/summary.py +1 -1
  60. package/dist/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  61. package/dist/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
  62. package/dist/skills/ulw-research/AGENTS.md +8 -4
  63. package/dist/skills/ulw-research/SKILL.md +7 -259
  64. package/dist/skills/ulw-research/references/deliverable-phase.md +68 -24
  65. package/dist/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
  66. package/dist/skills/ulw-research/references/fact-ledger.md +42 -8
  67. package/dist/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
  68. package/dist/skills/ulw-research/references/markdown-blocks.md +12 -0
  69. package/dist/skills/ulw-research/references/report-gates.md +14 -6
  70. package/dist/skills/ulw-research/references/research-collection.md +195 -0
  71. package/dist/skills/ulw-research/references/research-delivery.md +123 -0
  72. package/dist/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
  73. package/dist/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
  74. package/dist/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
  75. package/dist/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
  76. package/dist/skills/ulw-research/scripts/contracts.mjs +16 -15
  77. package/dist/skills/ulw-research/scripts/contracts.test.ts +6 -109
  78. package/dist/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
  79. package/dist/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
  80. package/dist/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
  81. package/dist/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
  82. package/dist/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
  83. package/dist/skills/ulw-research/scripts/copy-returns-fidelity-policy.test.ts +156 -0
  84. package/dist/skills/ulw-research/scripts/copy-returns-polish.test.ts +10 -9
  85. package/dist/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  86. package/dist/skills/ulw-research/scripts/css-lite.mjs +4 -2
  87. package/dist/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
  88. package/dist/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
  89. package/dist/skills/ulw-research/scripts/entities.mjs +13 -4
  90. package/dist/skills/ulw-research/scripts/fact-commands-fill.mjs +96 -0
  91. package/dist/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
  92. package/dist/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
  93. package/dist/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
  94. package/dist/skills/ulw-research/scripts/fact-commands.mjs +40 -62
  95. package/dist/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
  96. package/dist/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  97. package/dist/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
  98. package/dist/skills/ulw-research/scripts/fact-fill.mjs +9 -6
  99. package/dist/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  100. package/dist/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
  101. package/dist/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
  102. package/dist/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  103. package/dist/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
  104. package/dist/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
  105. package/dist/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
  106. package/dist/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  107. package/dist/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  108. package/dist/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  109. package/dist/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  110. package/dist/skills/ulw-research/scripts/fidelity-policy.mjs +183 -0
  111. package/dist/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  112. package/dist/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  113. package/dist/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  114. package/dist/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  115. package/dist/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  116. package/dist/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  117. package/dist/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
  118. package/dist/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  119. package/dist/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
  120. package/dist/skills/ulw-research/scripts/gates-static.mjs +10 -1
  121. package/dist/skills/ulw-research/scripts/html-lite.mjs +6 -2
  122. package/dist/skills/ulw-research/scripts/html-markup.mjs +2 -2
  123. package/dist/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  124. package/dist/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
  125. package/dist/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  126. package/dist/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
  127. package/dist/skills/ulw-research/scripts/md-lite.mjs +1 -1
  128. package/dist/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  129. package/dist/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
  130. package/dist/skills/ulw-research/scripts/numerals.mjs +18 -7
  131. package/dist/skills/ulw-research/scripts/numerals.test.ts +3 -0
  132. package/dist/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  133. package/dist/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
  134. package/dist/skills/ulw-research/scripts/outcome.mjs +11 -22
  135. package/dist/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
  136. package/dist/skills/ulw-research/scripts/placeholder.mjs +18 -6
  137. package/dist/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  138. package/dist/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
  139. package/dist/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
  140. package/dist/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  141. package/dist/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  142. package/dist/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
  143. package/dist/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
  144. package/dist/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
  145. package/dist/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
  146. package/dist/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
  147. package/dist/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  148. package/dist/skills/ulw-research/scripts/report-tools.mjs +7 -1
  149. package/dist/skills/ulw-research/scripts/report-tools.test.ts +6 -560
  150. package/dist/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
  151. package/dist/skills/ulw-research/scripts/source-commands.mjs +87 -29
  152. package/dist/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
  153. package/dist/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  154. package/dist/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  155. package/dist/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
  156. package/dist/skills/ulw-research/scripts/source-view.mjs +89 -0
  157. package/dist/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  158. package/dist/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
  159. package/dist/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
  160. package/dist/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
  161. package/dist/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
  162. package/dist/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
  163. package/dist/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
  164. package/dist/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
  165. package/dist/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
  166. package/dist/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
  167. package/dist/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
  168. package/dist/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
  169. package/dist/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
  170. package/dist/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
  171. package/dist/skills/ulw-research/scripts/units.mjs +14 -3
  172. package/dist/skills/ulw-research/scripts/units.test.ts +10 -0
  173. package/dist/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
  174. package/dist/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
  175. package/dist/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
  176. package/dist/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
  177. package/dist/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
  178. package/dist/tools/delegate-task/executor-types.d.ts +1 -0
  179. package/dist/tools/delegate-task/sync-session-existence.d.ts +4 -0
  180. package/dist/tools/delegate-task/types.d.ts +1 -0
  181. package/dist/tools/skill/description-formatter.d.ts +2 -1
  182. package/dist/tools/skill/types.d.ts +6 -0
  183. package/dist/tui.js +131 -33
  184. package/package.json +18 -15
  185. package/packages/git-bash-mcp/package.json +1 -1
  186. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  187. package/packages/omo-codex/plugin/README.md +1 -1
  188. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  189. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  190. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  191. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  192. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  193. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  194. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  195. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  196. package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
  197. package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  198. package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/SKILL.md +7 -7
  199. package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/agents/openai.yaml +1 -1
  200. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  201. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  202. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  203. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  204. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  205. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  206. package/packages/omo-codex/plugin/components/teammode/package.json +2 -2
  207. package/packages/omo-codex/plugin/components/telemetry/README.md +2 -2
  208. package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +52 -15
  209. package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +52 -19
  210. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  211. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  212. package/packages/omo-codex/plugin/components/telemetry/src/codex-hook.ts +2 -9
  213. package/packages/omo-codex/plugin/components/telemetry/src/posthog.ts +4 -13
  214. package/packages/omo-codex/plugin/components/telemetry/src/product-identity.ts +0 -2
  215. package/packages/omo-codex/plugin/components/telemetry/test/codex-hook-diagnostics.test.ts +0 -3
  216. package/packages/omo-codex/plugin/components/telemetry/test/codex-hook.test.ts +3 -8
  217. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +3 -0
  218. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  219. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  220. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  221. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  222. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  223. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +3 -0
  224. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  225. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  226. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  227. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  228. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  229. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  230. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  231. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  232. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  233. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  234. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  235. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  236. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  237. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  238. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  239. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  240. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  241. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  242. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  243. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  244. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  245. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  246. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  247. package/packages/omo-codex/plugin/package-lock.json +16 -16
  248. package/packages/omo-codex/plugin/package.json +1 -1
  249. package/packages/omo-codex/plugin/shared/package.json +1 -1
  250. package/packages/omo-codex/plugin/skills/ast-grep/README.md +1 -1
  251. package/packages/omo-codex/plugin/skills/ast-grep/install.ps1 +5 -3
  252. package/packages/omo-codex/plugin/skills/ast-grep/install.sh +4 -5
  253. package/packages/omo-codex/plugin/skills/ast-grep/references/install.md +2 -2
  254. package/packages/omo-codex/plugin/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
  255. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  256. package/packages/omo-codex/plugin/skills/coding-agent-sessions/AGENTS.md +5 -4
  257. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +11 -11
  258. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +2 -2
  259. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/codex.md +4 -4
  260. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
  261. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
  262. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +1 -1
  263. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +34 -12
  264. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +2 -2
  265. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +2 -1
  266. package/packages/omo-codex/plugin/skills/frontend/references/design/component-catalogs.md +14 -10
  267. package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +19 -6
  268. package/packages/omo-codex/plugin/skills/frontend/references/design/grounding.md +29 -0
  269. package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
  270. package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
  271. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
  272. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  273. package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +7 -7
  274. package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
  275. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +5 -4
  276. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -2
  277. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/__main__.py +9 -6
  278. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/summary.py +1 -1
  279. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  280. package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
  281. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +3 -0
  282. package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +8 -4
  283. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +7 -259
  284. package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +68 -24
  285. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
  286. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +42 -8
  287. package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
  288. package/packages/omo-codex/plugin/skills/ulw-research/references/markdown-blocks.md +12 -0
  289. package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +14 -6
  290. package/packages/omo-codex/plugin/skills/ulw-research/references/research-collection.md +195 -0
  291. package/packages/omo-codex/plugin/skills/ulw-research/references/research-delivery.md +123 -0
  292. package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
  293. package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +16 -15
  294. package/packages/omo-codex/plugin/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
  295. package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  296. package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-lite.mjs +4 -2
  297. package/packages/omo-codex/plugin/skills/ulw-research/scripts/entities.mjs +13 -4
  298. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-fill.mjs +96 -0
  299. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
  300. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
  301. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
  302. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +40 -62
  303. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  304. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +9 -6
  305. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  306. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  307. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  308. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  309. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  310. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  311. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-policy.mjs +183 -0
  312. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  313. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  314. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  315. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  316. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  317. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  318. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  319. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +10 -1
  320. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +6 -2
  321. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +2 -2
  322. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  323. package/packages/omo-codex/plugin/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  324. package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +1 -1
  325. package/packages/omo-codex/plugin/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  326. package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +18 -7
  327. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  328. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +11 -22
  329. package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +18 -6
  330. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  331. package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  332. package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  333. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  334. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +7 -1
  335. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +87 -29
  336. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  337. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  338. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-view.mjs +89 -0
  339. package/packages/omo-codex/plugin/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  340. package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +14 -3
  341. package/packages/omo-codex/plugin/test/payload-equivalence.test.mjs +6 -4
  342. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -3
  343. package/packages/omo-codex/scripts/install-dist/install-local.mjs +338 -237
  344. package/packages/prompts-core/prompts/ultrawork/codex.md +3 -0
  345. package/packages/shared-skills/skill-source-filter.mjs +6 -2
  346. package/packages/shared-skills/skills/ast-grep/README.md +1 -1
  347. package/packages/shared-skills/skills/ast-grep/install.ps1 +5 -3
  348. package/packages/shared-skills/skills/ast-grep/install.sh +4 -5
  349. package/packages/shared-skills/skills/ast-grep/references/install.md +2 -2
  350. package/packages/shared-skills/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
  351. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  352. package/packages/shared-skills/skills/coding-agent-sessions/AGENTS.md +5 -4
  353. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +11 -11
  354. package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +2 -2
  355. package/packages/shared-skills/skills/coding-agent-sessions/references/codex.md +4 -4
  356. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
  357. package/packages/shared-skills/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
  358. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +1 -1
  359. package/packages/shared-skills/skills/frontend/SKILL.md +34 -12
  360. package/packages/shared-skills/skills/frontend/references/design/README.md +2 -2
  361. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +2 -1
  362. package/packages/shared-skills/skills/frontend/references/design/component-catalogs.md +14 -10
  363. package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +19 -6
  364. package/packages/shared-skills/skills/frontend/references/design/grounding.md +29 -0
  365. package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
  366. package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
  367. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +5 -4
  368. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -2
  369. package/packages/shared-skills/skills/ultimate-browsing/engine/__main__.py +9 -6
  370. package/packages/shared-skills/skills/ultimate-browsing/engine/summary.py +1 -1
  371. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  372. package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
  373. package/packages/shared-skills/skills/ulw-research/AGENTS.md +8 -4
  374. package/packages/shared-skills/skills/ulw-research/SKILL.md +7 -259
  375. package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +68 -24
  376. package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
  377. package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +42 -8
  378. package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
  379. package/packages/shared-skills/skills/ulw-research/references/markdown-blocks.md +12 -0
  380. package/packages/shared-skills/skills/ulw-research/references/report-gates.md +14 -6
  381. package/packages/shared-skills/skills/ulw-research/references/research-collection.md +195 -0
  382. package/packages/shared-skills/skills/ulw-research/references/research-delivery.md +123 -0
  383. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
  384. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
  385. package/packages/shared-skills/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
  386. package/packages/shared-skills/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
  387. package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +16 -15
  388. package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +6 -109
  389. package/packages/shared-skills/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
  390. package/packages/shared-skills/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
  391. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
  392. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
  393. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
  394. package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-fidelity-policy.test.ts +156 -0
  395. package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-polish.test.ts +10 -9
  396. package/packages/shared-skills/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  397. package/packages/shared-skills/skills/ulw-research/scripts/css-lite.mjs +4 -2
  398. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
  399. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
  400. package/packages/shared-skills/skills/ulw-research/scripts/entities.mjs +13 -4
  401. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-fill.mjs +96 -0
  402. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
  403. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
  404. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
  405. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +40 -62
  406. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
  407. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  408. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
  409. package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +9 -6
  410. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  411. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
  412. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
  413. package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  414. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
  415. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
  416. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
  417. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  418. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  419. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  420. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  421. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-policy.mjs +183 -0
  422. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  423. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  424. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  425. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  426. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  427. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  428. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
  429. package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  430. package/packages/shared-skills/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
  431. package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +10 -1
  432. package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +6 -2
  433. package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +2 -2
  434. package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  435. package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
  436. package/packages/shared-skills/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  437. package/packages/shared-skills/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
  438. package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +1 -1
  439. package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  440. package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
  441. package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +18 -7
  442. package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +3 -0
  443. package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  444. package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
  445. package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +11 -22
  446. package/packages/shared-skills/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
  447. package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +18 -6
  448. package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  449. package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
  450. package/packages/shared-skills/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
  451. package/packages/shared-skills/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  452. package/packages/shared-skills/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  453. package/packages/shared-skills/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
  454. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
  455. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
  456. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
  457. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
  458. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  459. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +7 -1
  460. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +6 -560
  461. package/packages/shared-skills/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
  462. package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +87 -29
  463. package/packages/shared-skills/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
  464. package/packages/shared-skills/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  465. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  466. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
  467. package/packages/shared-skills/skills/ulw-research/scripts/source-view.mjs +89 -0
  468. package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  469. package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
  470. package/packages/shared-skills/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
  471. package/packages/shared-skills/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
  472. package/packages/shared-skills/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
  473. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
  474. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
  475. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
  476. package/packages/shared-skills/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
  477. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
  478. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
  479. package/packages/shared-skills/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
  480. package/packages/shared-skills/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
  481. package/packages/shared-skills/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
  482. package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +14 -3
  483. package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +10 -0
  484. package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
  485. package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
  486. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
  487. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
  488. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
  489. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/amp_redirect_stub.html +0 -7
  490. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/search_interstitial.html +0 -19
  491. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_available.json +0 -1
  492. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_snapshot.html +0 -1128
  493. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_fetch_chain.py +0 -77
  494. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +0 -86
  495. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +0 -279
  496. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate.py +0 -252
  497. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate_validators.py +0 -78
@@ -78,7 +78,7 @@ describe("the polish edit contract is polish-apply's", () => {
78
78
  return "refused"
79
79
  }
80
80
  })()
81
- const cli = outcome(() => runFactPolish("polish-apply", [reportPath, "--edits", editsPath, "--ledger", ledgerPath, "--out", join(dir, "out.html")]))
81
+ const cli = outcome(() => runFactPolish("polish-apply", [reportPath, "--edits", editsPath, "--ledger", ledgerPath, "--brief", BRIEF, "--out", join(dir, "out.html")]))
82
82
  expect(cli === "ok" ? "ok" : "refused").toBe(direct)
83
83
  if (cli !== "ok") expect(cli).toContain(editsPath)
84
84
  })
@@ -87,7 +87,7 @@ describe("the polish edit contract is polish-apply's", () => {
87
87
 
88
88
  describe("the ledger loader is one shared helper", () => {
89
89
  const pairs = (extra: string[]) => () => runFactMeaning("pairs", [reportPath, "--brief", BRIEF, "--out", join(dir, "pairs.json"), ...extra])
90
- const diff = (extra: string[]) => () => runFactPolish("polish-diff", ["--before", reportPath, "--after", reportPath, ...extra])
90
+ const diff = (extra: string[]) => () => runFactPolish("polish-diff", ["--before", reportPath, "--after", reportPath, "--brief", BRIEF, ...extra])
91
91
 
92
92
  test("#given the command modules #when read #then neither defines its own ledger loader or session resolver", () => {
93
93
  expect(polishCommand.ledgerFrom).toBe(ledgerFrom)
@@ -122,7 +122,7 @@ describe("the edition format comes from verify's grammar", () => {
122
122
  expect(source("fact-commands-polish.mjs")).not.toMatch(/md\|markdown/u)
123
123
  for (const name of ["r.md", "R.MD", "r.markdown", "R.Markdown", "r.html", "r.HTML"]) {
124
124
  const path = file(name, readFileSync(reportPath, "utf8"))
125
- const result = outcome(() => runFactPolish("polish-apply", [path, "--edits", editsPath, "--ledger", ledgerPath, "--out", join(dir, "out.html")]))
125
+ const result = outcome(() => runFactPolish("polish-apply", [path, "--edits", editsPath, "--ledger", ledgerPath, "--brief", BRIEF, "--out", join(dir, "out.html")]))
126
126
  expect(result.includes("takes the filled HTML edition"), name).toBe(editionFormatOf(path, undefined) === "md")
127
127
  }
128
128
  })
@@ -130,7 +130,7 @@ describe("the edition format comes from verify's grammar", () => {
130
130
 
131
131
  describe("polish-diff counts with contracts and exits on blockers only", () => {
132
132
  type DiffRun = { exitCode: number; json: { counts: Record<string, number>; defects: { severity: string }[] } }
133
- const run = (before: string, after: string) => runFactPolish("polish-diff", ["--before", file("b.html", doc(before)), "--after", file("a.html", doc(after)), "--ledger", ledgerPath]) as unknown as DiffRun
133
+ const run = (before: string, after: string) => runFactPolish("polish-diff", ["--before", file("b.html", doc(before)), "--after", file("a.html", doc(after)), "--ledger", ledgerPath, "--brief", BRIEF]) as unknown as DiffRun
134
134
 
135
135
  test("#given every registry code #when polishDiffFails classifies it #then only registry blockers fail", () => {
136
136
  for (const [code, entry] of Object.entries(DEFECT_CODES) as [string, { severity: string }][]) expect(polishDiffFails([{ code, severity: "minor" }]), code).toBe(entry.severity === "blocker")
@@ -161,7 +161,7 @@ describe("fact polish-apply prints every rejection reason verbatim", () => {
161
161
  test("#given an edit holding & #when fact polish-apply runs #then the printed line carries polish-apply's own markup reason", () => {
162
162
  const before = "Revenue & profit rose across the year"
163
163
  const editsPath = file("edits-markup-ampersand.json", JSON.stringify([{ before, after: "Revenue and profit rose across the year", reason: "wording" }]))
164
- const run = runFactPolish("polish-apply", [reportPath, "--edits", editsPath, "--ledger", ledgerPath, "--out", join(dir, "ampersand.html")]) as unknown as ApplyRun
164
+ const run = runFactPolish("polish-apply", [reportPath, "--edits", editsPath, "--ledger", ledgerPath, "--brief", BRIEF, "--out", join(dir, "ampersand.html")]) as unknown as ApplyRun
165
165
 
166
166
  expect(run.exitCode).toBe(0)
167
167
  expect(run.json.data.rejected).toBe(1)
@@ -173,7 +173,7 @@ describe("fact polish-apply prints every rejection reason verbatim", () => {
173
173
  })
174
174
 
175
175
  describe("--changed-since stays fail-closed", () => {
176
- const { pairsChangedSince } = polishCommand as unknown as { pairsChangedSince: (pairs: unknown[], mapPath: string, ledger: unknown, claims: unknown) => unknown[] }
176
+ const { pairsChangedSince } = polishCommand as unknown as { pairsChangedSince: (pairs: unknown[], mapPath: string, ledger: unknown, claims: unknown) => unknown }
177
177
  const artifact = file("pre-polish.html", doc("<p>The span is 320 m [S1].</p>"))
178
178
 
179
179
  test("#given a fill map without artifact_sha256 #when --changed-since reads it #then it refuses", () => {
@@ -181,9 +181,10 @@ describe("--changed-since stays fail-closed", () => {
181
181
  expect(outcome(() => pairsChangedSince([], mapPath, LEDGER, null))).toContain("fill map has no artifact_sha256")
182
182
  })
183
183
 
184
- test("#given a fill map whose artifact changed since fill #when read #then it still refuses", () => {
184
+ test("#given a fill map whose artifact changed since fill #when read #then nothing is filtered out: every pair is re-checked", () => {
185
185
  const mapPath = file("fill-map-stale.json", JSON.stringify({ artifact, artifact_sha256: "0".repeat(64), format: "html", entries: [] }))
186
- expect(outcome(() => pairsChangedSince([], mapPath, LEDGER, null))).toContain("changed after fill")
186
+ const pairs = [{ kind: "quote", sentence_key: "s1", claim_id: null, sentence: "The span is 320 m [S1]." }]
187
+ expect(pairsChangedSince(pairs, mapPath, LEDGER, null)).toEqual({ pairs, fill_map: mapPath, narrowed: false })
187
188
  })
188
189
  })
189
190
 
@@ -203,7 +204,7 @@ describe("polish-apply writes atomically and honours --format", () => {
203
204
 
204
205
  test("#given --format md on an .html report #when polish-apply runs #then it refuses the Markdown edition", () => {
205
206
  const editsPath = file("edits-format.json", "[]")
206
- const result = outcome(() => runFactPolish("polish-apply", [reportPath, "--edits", editsPath, "--ledger", ledgerPath, "--out", join(dir, "fmt.html"), "--format", "md"]))
207
+ const result = outcome(() => runFactPolish("polish-apply", [reportPath, "--edits", editsPath, "--ledger", ledgerPath, "--brief", BRIEF, "--out", join(dir, "fmt.html"), "--format", "md"]))
207
208
  expect(result).toContain("takes the filled HTML edition")
208
209
  })
209
210
  })
@@ -0,0 +1,158 @@
1
+ // css-hiding.mjs - whether CSS hides an element of a page from every reader, from its inline style and
2
+ // the page's own <style> rules (external stylesheets and scripts are not evaluated).
3
+ //
4
+ // Text counts as reader-visible when it is visible under at least one condition. So a rule can hide
5
+ // only when it certainly applies: it sits outside every at-rule except @layer (and @media all), its
6
+ // <style> element applies everywhere (no media other than all, CSS type, not in <noscript>), and its
7
+ // selector is one tag (case-insensitive), one .class or one #id (case-sensitive). A rule that only
8
+ // might apply (inside @media, @supports, @container, @scope, @starting-style, @document, @keyframes,
9
+ // nested in another style rule, or with a compound, combinator, attribute or pseudo-class selector)
10
+ // never hides, but it can make text visible when its subject could match and it outranks the hiding
11
+ // declaration in the cascade (origin and importance, inline style, layer order, specificity, order).
12
+ // Hiding properties: display:none and content-visibility:hidden remove the subtree;
13
+ // visibility:hidden|collapse is inherited and a descendant's visibility:visible shows it again.
14
+ // The hidden attribute (except hidden="until-found") and a closed <dialog> are the user-agent
15
+ // display:none, which any author display declaration overrides. opacity, clip, zero size, font-size,
16
+ // off-screen positions and colors do not hide: the text still renders or is read aloud.
17
+ // Plain Node ESM, sibling imports only.
18
+
19
+ import { scanRules } from "./css-lite.mjs"
20
+
21
+ const PROPS = new Set(["display", "visibility", "content-visibility"])
22
+ const INHERITED = new Set(["inherit", "unset", "revert", "revert-layer"])
23
+ const IMPORTANT = /!\s*important\s*$/i
24
+ const SIMPLE = /^(?:[.#][^\s.#:[\]>+~,()*"'\\]+|[a-z][a-z0-9-]*)$/iu
25
+ const PSEUDO_ELEMENT = /::|:(?:before|after|first-line|first-letter)\b/i
26
+ const NAME = "[^\\s.#:[\\]>+~,()*\"'\\\\]+"
27
+ const STYLE_IN_SVG = /<style\b[^>]*>([\s\S]*?)<\/style\s*>/gi
28
+
29
+ /** [tier (0 user agent, 1 author, 2 author !important), inline, layer, specificity, order]: higher wins. */
30
+ const outranks = (a, b) => {
31
+ for (let k = 0; k < a.length; k++) if (a[k] !== b[k]) return a[k] > b[k]
32
+ return false
33
+ }
34
+
35
+ function specificity(selector) {
36
+ const core = selector.replace(/\[[^\]]*\]/g, " [] ")
37
+ const ids = core.match(new RegExp(`#${NAME}`, "gu"))?.length ?? 0
38
+ const classes = (core.match(new RegExp(`\\.${NAME}`, "gu"))?.length ?? 0) + (core.match(/\[\]|:(?!:)[\w-]+/g)?.length ?? 0)
39
+ const tags = core.match(/(?:^|[\s>+~(])[a-z][\w-]*/gi)?.length ?? 0
40
+ return ids * 10000 + classes * 100 + tags
41
+ }
42
+
43
+ /** The subject compound of a selector: its tag, classes and ids (attribute and pseudo-class parts dropped). */
44
+ function subject(selector) {
45
+ const compound = selector.trim().match(/(?:[^\s>+~[\]()]|\[[^\]]*\]|\([^)]*\))+$/)?.[0] ?? ""
46
+ const core = compound.replace(/\([^)]*\)/g, "").replace(/\[[^\]]*\]/g, "").replace(/:+[\w-]+/g, "")
47
+ const tag = core.match(/^[a-z][a-z0-9-]*/i)?.[0].toLowerCase() ?? null
48
+ const classes = [...core.matchAll(new RegExp(`\\.(${NAME})`, "gu"))].map((match) => match[1])
49
+ const ids = [...core.matchAll(new RegExp(`#(${NAME})`, "gu"))].map((match) => match[1])
50
+ return { tag, classes, ids, key: ids[0] ? `#${ids[0]}` : classes[0] ? `.${classes[0]}` : tag ?? "*" }
51
+ }
52
+
53
+ const classList = (node) => String(node.attrs?.class ?? "").split(/[ \t\n\f\r]+/).filter(Boolean)
54
+
55
+ function matches(sub, node) {
56
+ if (sub.tag !== null && sub.tag !== node.tag) return false
57
+ const names = classList(node)
58
+ return sub.classes.every((name) => names.includes(name)) && sub.ids.every((id) => id === node.attrs?.id)
59
+ }
60
+
61
+ /** Whether every enclosing at-rule leaves the rule unconditional (@layer, @media all), its layer name, and keyframe steps. */
62
+ function context(atRule) {
63
+ const entries = atRule ? atRule.split(/\s(?=@)/) : []
64
+ const layers = entries.filter((entry) => /^@layer\b/i.test(entry)).map((entry) => entry.slice(6).trim())
65
+ const conditional = entries.some((entry) => !/^@layer\b/i.test(entry) && !/^@media(?:\s+all)?\s*$/i.test(entry))
66
+ return { conditional, layer: layers.length ? layers.join(".") : null, keyframe: entries.some((entry) => /^@(?:-[a-z]+-)?keyframes\b/i.test(entry)) }
67
+ }
68
+
69
+ const declaration = (value) => ({ value: value.replace(IMPORTANT, "").trim().toLowerCase(), important: IMPORTANT.test(value) })
70
+
71
+ /** Every <style> text that applies to the page, with whether it applies only under a condition. */
72
+ function sheets(root) {
73
+ const found = []
74
+ const visit = (node, conditional) => {
75
+ if (node.tag === "template") return
76
+ const inner = conditional || node.tag === "noscript"
77
+ if (node.tag === "style") {
78
+ const type = String(node.attrs?.type ?? "").trim().toLowerCase()
79
+ const media = String(node.attrs?.media ?? "").trim().toLowerCase()
80
+ if (type === "" || type === "text/css") found.push({ css: String(node.raw ?? ""), conditional: inner || (media !== "" && media !== "all") })
81
+ }
82
+ if (node.tag === "svg") for (const match of String(node.raw ?? "").matchAll(STYLE_IN_SVG)) found.push({ css: match[1], conditional: inner })
83
+ for (const child of node.children ?? []) visit(child, inner)
84
+ }
85
+ visit(root, false)
86
+ return found
87
+ }
88
+
89
+ /** Index every rule declaring display, visibility or content-visibility by its subject key. */
90
+ function indexRules(root) {
91
+ const index = new Map()
92
+ const layers = []
93
+ const layerRank = (name, important) => {
94
+ if (name === null) return important ? -1 : 1e6
95
+ if (!layers.includes(name)) layers.push(name)
96
+ return important ? 1e6 - layers.indexOf(name) : layers.indexOf(name)
97
+ }
98
+ let order = 0
99
+ for (const sheet of sheets(root)) {
100
+ const css = sheet.css.replace(/<!--|-->/g, " ")
101
+ for (const match of css.replace(/\/\*[\s\S]*?(?:\*\/|$)/g, "").matchAll(/@layer\s+([^{;]*)[{;]/gi)) {
102
+ for (const name of match[1].split(",").map((part) => part.trim())) if (name && !layers.includes(name)) layers.push(name)
103
+ }
104
+ for (const rule of scanRules(css)) {
105
+ const decls = rule.declarations.filter((entry) => PROPS.has(entry.prop))
106
+ if (!decls.length || rule.selector.startsWith("@")) continue
107
+ const { conditional, layer, keyframe } = context(rule.atRule)
108
+ if (keyframe) continue
109
+ for (const selector of rule.selector.split(/,(?![^()[\]]*[)\]])/).map((part) => part.trim())) {
110
+ if (!selector || PSEUDO_ELEMENT.test(selector)) continue
111
+ const sub = subject(selector)
112
+ const certain = !sheet.conditional && !conditional && !rule.nested && SIMPLE.test(selector)
113
+ const spec = specificity(selector)
114
+ for (const entry of decls) {
115
+ const { value, important } = declaration(entry.value)
116
+ const rank = [important ? 2 : 1, 0, layerRank(layer, important), spec, order++]
117
+ if (!index.has(sub.key)) index.set(sub.key, [])
118
+ index.get(sub.key).push({ sub, prop: entry.prop, value, rank, certain })
119
+ }
120
+ }
121
+ }
122
+ }
123
+ return index
124
+ }
125
+
126
+ /** The values a property can take on the element: the certain winner, plus every possible rule that outranks it. */
127
+ function possible(candidates, prop) {
128
+ let top = null
129
+ for (const entry of candidates) if (entry.prop === prop && entry.certain && (top === null || outranks(entry.rank, top.rank))) top = entry
130
+ const values = [top === null ? "unset" : top.value]
131
+ for (const entry of candidates) if (entry.prop === prop && !entry.certain && (top === null || outranks(entry.rank, top.rank))) values.push(entry.value)
132
+ return values
133
+ }
134
+
135
+ /**
136
+ * cssVisibility(root) -> (node, parentShown) => null when CSS removes the element and its subtree from every
137
+ * rendering, else whether its own text can be seen (visibility, inherited from parentShown).
138
+ */
139
+ export function cssVisibility(root) {
140
+ const index = indexRules(root)
141
+ return (node, parentShown) => {
142
+ const attrs = node.attrs ?? {}
143
+ const keys = ["*", node.tag, ...classList(node).map((name) => `.${name}`), attrs.id === undefined ? null : `#${attrs.id}`]
144
+ const candidates = []
145
+ for (const key of new Set(keys)) for (const entry of index.get(key) ?? []) if (matches(entry.sub, node)) candidates.push(entry)
146
+ const hiddenAttr = attrs.hidden !== undefined && String(attrs.hidden).trim().toLowerCase() !== "until-found"
147
+ if (hiddenAttr || (node.tag === "dialog" && attrs.open === undefined)) candidates.push({ prop: "display", value: "none", rank: [0, 0, 0, 0, -1], certain: true })
148
+ const inline = attrs.style ? (scanRules(`x{${attrs.style}}`)[0]?.declarations ?? []) : []
149
+ inline.forEach((entry, at) => {
150
+ if (!PROPS.has(entry.prop)) return
151
+ const { value, important } = declaration(entry.value)
152
+ candidates.push({ prop: entry.prop, value, rank: [important ? 2 : 1, 1, 0, 0, at], certain: true })
153
+ })
154
+ if (possible(candidates, "display").every((value) => value === "none")) return null
155
+ if (possible(candidates, "content-visibility").every((value) => value === "hidden")) return null
156
+ return possible(candidates, "visibility").some((value) => (INHERITED.has(value) ? parentShown : value !== "hidden" && value !== "collapse"))
157
+ }
158
+ }
@@ -57,8 +57,9 @@ function parseDeclarations(text) {
57
57
  }
58
58
 
59
59
  /**
60
- * @returns {{ selector: string, declarations: { prop: string, value: string }[], atRule?: string }[]}
61
- * atRule is the chain of enclosing grouping at-rule preludes joined by a space.
60
+ * @returns {{ selector: string, declarations: { prop: string, value: string }[], atRule?: string, nested?: true }[]}
61
+ * atRule is the chain of enclosing grouping at-rule preludes joined by a space; nested is true for a
62
+ * rule written inside another style rule (CSS nesting).
62
63
  */
63
64
  export function scanRules(css) {
64
65
  if (typeof css !== "string") return []
@@ -88,6 +89,7 @@ export function scanRules(css) {
88
89
  const atChain = stack.filter((entry) => entry.group).map((entry) => entry.prelude)
89
90
  const rule = { selector: frame.prelude, declarations: parseDeclarations(frame.body + buffer) }
90
91
  if (atChain.length) rule.atRule = atChain.join(" ")
92
+ if (stack.some((entry) => !entry.group)) rule.nested = true
91
93
  slots[frame.slot] = rule
92
94
  }
93
95
  buffer = ""
@@ -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
- })