lazycodex-ai 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 (437) hide show
  1. package/dist/cli/index.js +1194 -752
  2. package/dist/cli/service-auth/accounts-gate.d.ts +11 -0
  3. package/dist/cli/service-auth/commands.d.ts +2 -4
  4. package/dist/cli/service-auth/index.d.ts +1 -1
  5. package/dist/cli/service-auth/keystore.d.ts +26 -2
  6. package/dist/cli/service-auth/protocol.d.ts +7 -1
  7. package/dist/cli/service-auth/session.d.ts +6 -0
  8. package/dist/cli/service-auth/signed-out-marker.d.ts +11 -0
  9. package/dist/cli-node/index.js +1194 -752
  10. package/package.json +3 -1
  11. package/packages/lsp-core/package.json +45 -0
  12. package/packages/lsp-core/src/index.ts +30 -0
  13. package/packages/lsp-core/src/lsp/bun-command-output.ts +82 -0
  14. package/packages/lsp-core/src/lsp/bun-project-checks.test.ts +170 -0
  15. package/packages/lsp-core/src/lsp/bun-project-checks.ts +223 -0
  16. package/packages/lsp-core/src/lsp/cleanup-errors.test.ts +18 -0
  17. package/packages/lsp-core/src/lsp/cleanup-errors.ts +14 -0
  18. package/packages/lsp-core/src/lsp/client-diagnostics-concurrency.integration.test.ts +52 -0
  19. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +471 -0
  20. package/packages/lsp-core/src/lsp/client-diagnostics-pull-timeout.test.ts +64 -0
  21. package/packages/lsp-core/src/lsp/client-wrapper-outside-cwd.test.ts +78 -0
  22. package/packages/lsp-core/src/lsp/client-wrapper.test.ts +213 -0
  23. package/packages/lsp-core/src/lsp/client-wrapper.ts +290 -0
  24. package/packages/lsp-core/src/lsp/client.ts +398 -0
  25. package/packages/lsp-core/src/lsp/config-loader.ts +254 -0
  26. package/packages/lsp-core/src/lsp/connection.ts +95 -0
  27. package/packages/lsp-core/src/lsp/constants.ts +17 -0
  28. package/packages/lsp-core/src/lsp/controlled-clock-test-support.ts +90 -0
  29. package/packages/lsp-core/src/lsp/directory-diagnostics.test.ts +221 -0
  30. package/packages/lsp-core/src/lsp/directory-diagnostics.ts +187 -0
  31. package/packages/lsp-core/src/lsp/effective-extension.ts +10 -0
  32. package/packages/lsp-core/src/lsp/errors.ts +89 -0
  33. package/packages/lsp-core/src/lsp/fake-bun-test-support.ts +102 -0
  34. package/packages/lsp-core/src/lsp/fixtures/diagnostics-freshness-contract-probe.ts +283 -0
  35. package/packages/lsp-core/src/lsp/fixtures/disposed-stdin-epipe-probe.ts +28 -0
  36. package/packages/lsp-core/src/lsp/fixtures/workspace-edit-contract-probe.ts +196 -0
  37. package/packages/lsp-core/src/lsp/fixtures/workspace-edit-server.mjs +234 -0
  38. package/packages/lsp-core/src/lsp/format-document.test.ts +143 -0
  39. package/packages/lsp-core/src/lsp/format-document.ts +90 -0
  40. package/packages/lsp-core/src/lsp/formatters.ts +144 -0
  41. package/packages/lsp-core/src/lsp/infer-extension.ts +66 -0
  42. package/packages/lsp-core/src/lsp/json-rpc-connection-cancellation.test.ts +97 -0
  43. package/packages/lsp-core/src/lsp/json-rpc-connection.ts +369 -0
  44. package/packages/lsp-core/src/lsp/language-mappings.ts +173 -0
  45. package/packages/lsp-core/src/lsp/manager-lifecycle.test.ts +262 -0
  46. package/packages/lsp-core/src/lsp/manager-max-clients.test.ts +151 -0
  47. package/packages/lsp-core/src/lsp/manager.ts +495 -0
  48. package/packages/lsp-core/src/lsp/outside-context-workspace.ts +33 -0
  49. package/packages/lsp-core/src/lsp/process-pipe-errors.test.ts +21 -0
  50. package/packages/lsp-core/src/lsp/process-signal-cleanup.ts +21 -0
  51. package/packages/lsp-core/src/lsp/process.ts +221 -0
  52. package/packages/lsp-core/src/lsp/server-definitions.ts +205 -0
  53. package/packages/lsp-core/src/lsp/server-install-state.ts +67 -0
  54. package/packages/lsp-core/src/lsp/server-installation.test.ts +376 -0
  55. package/packages/lsp-core/src/lsp/server-installation.ts +218 -0
  56. package/packages/lsp-core/src/lsp/server-resolution-local-binary.test.ts +170 -0
  57. package/packages/lsp-core/src/lsp/server-resolution.ts +117 -0
  58. package/packages/lsp-core/src/lsp/startup-failure.ts +1 -0
  59. package/packages/lsp-core/src/lsp/timer-provider.ts +13 -0
  60. package/packages/lsp-core/src/lsp/transport-protocol.ts +52 -0
  61. package/packages/lsp-core/src/lsp/transport-request-timeout.test.ts +73 -0
  62. package/packages/lsp-core/src/lsp/transport.ts +339 -0
  63. package/packages/lsp-core/src/lsp/types.ts +134 -0
  64. package/packages/lsp-core/src/lsp/utils.test.ts +72 -0
  65. package/packages/lsp-core/src/lsp/utils.ts +40 -0
  66. package/packages/lsp-core/src/lsp/workspace-apply-edit-failure.ts +19 -0
  67. package/packages/lsp-core/src/lsp/workspace-apply-edit-lease.integration.test.ts +218 -0
  68. package/packages/lsp-core/src/lsp/workspace-apply-edit-sync.integration.test.ts +113 -0
  69. package/packages/lsp-core/src/lsp/workspace-apply-edit-test-support.ts +194 -0
  70. package/packages/lsp-core/src/lsp/workspace-apply-edit.integration.test.ts +163 -0
  71. package/packages/lsp-core/src/lsp/workspace-document-state.test.ts +150 -0
  72. package/packages/lsp-core/src/lsp/workspace-document-state.ts +392 -0
  73. package/packages/lsp-core/src/lsp/workspace-edit-adversarial.test.ts +132 -0
  74. package/packages/lsp-core/src/lsp/workspace-edit-commit.test.ts +140 -0
  75. package/packages/lsp-core/src/lsp/workspace-edit-commit.ts +220 -0
  76. package/packages/lsp-core/src/lsp/workspace-edit-contract-evidence.test.ts +56 -0
  77. package/packages/lsp-core/src/lsp/workspace-edit-contract-evidence.ts +30 -0
  78. package/packages/lsp-core/src/lsp/workspace-edit-fingerprint.ts +44 -0
  79. package/packages/lsp-core/src/lsp/workspace-edit-options.test.ts +147 -0
  80. package/packages/lsp-core/src/lsp/workspace-edit-parse-helpers.ts +59 -0
  81. package/packages/lsp-core/src/lsp/workspace-edit-parser.ts +130 -0
  82. package/packages/lsp-core/src/lsp/workspace-edit-path.ts +98 -0
  83. package/packages/lsp-core/src/lsp/workspace-edit-plan-types.ts +60 -0
  84. package/packages/lsp-core/src/lsp/workspace-edit-plan.ts +73 -0
  85. package/packages/lsp-core/src/lsp/workspace-edit-prevalidation.test.ts +174 -0
  86. package/packages/lsp-core/src/lsp/workspace-edit-resource-parser.ts +89 -0
  87. package/packages/lsp-core/src/lsp/workspace-edit-simulation.ts +183 -0
  88. package/packages/lsp-core/src/lsp/workspace-edit-snapshot.ts +53 -0
  89. package/packages/lsp-core/src/lsp/workspace-edit-text.ts +125 -0
  90. package/packages/lsp-core/src/lsp/workspace-edit-types.ts +121 -0
  91. package/packages/lsp-core/src/lsp/workspace-edit.characterization.test.ts +95 -0
  92. package/packages/lsp-core/src/lsp/workspace-edit.ts +55 -0
  93. package/packages/lsp-core/src/lsp/workspace-markers.ts +18 -0
  94. package/packages/lsp-core/src/lsp/workspace-mutation-controller.ts +182 -0
  95. package/packages/lsp-core/src/mcp-protocol-pin.test.ts +97 -0
  96. package/packages/lsp-core/src/mcp.ts +122 -0
  97. package/packages/lsp-core/src/missing-dependency-result.test.ts +135 -0
  98. package/packages/lsp-core/src/missing-dependency-result.ts +79 -0
  99. package/packages/lsp-core/src/post-edit/file-location.ts +55 -0
  100. package/packages/lsp-core/src/post-edit/index.ts +3 -0
  101. package/packages/lsp-core/src/post-edit/not-installed-guidance.ts +36 -0
  102. package/packages/lsp-core/src/post-edit/not-installed.test.ts +224 -0
  103. package/packages/lsp-core/src/post-edit/orchestration.test.ts +157 -0
  104. package/packages/lsp-core/src/post-edit/orchestration.ts +205 -0
  105. package/packages/lsp-core/src/request-context.test.ts +200 -0
  106. package/packages/lsp-core/src/request-context.ts +235 -0
  107. package/packages/lsp-core/src/tool-surface.test.ts +173 -0
  108. package/packages/lsp-core/src/tools/bun-directory-diagnostics.test.ts +113 -0
  109. package/packages/lsp-core/src/tools/bun-directory-diagnostics.ts +75 -0
  110. package/packages/lsp-core/src/tools/definitions.ts +156 -0
  111. package/packages/lsp-core/src/tools/diagnostics.ts +133 -0
  112. package/packages/lsp-core/src/tools/format.test.ts +56 -0
  113. package/packages/lsp-core/src/tools/format.ts +56 -0
  114. package/packages/lsp-core/src/tools/index.ts +11 -0
  115. package/packages/lsp-core/src/tools/install-decision.ts +39 -0
  116. package/packages/lsp-core/src/tools/navigation.ts +88 -0
  117. package/packages/lsp-core/src/tools/parameters.test.ts +51 -0
  118. package/packages/lsp-core/src/tools/parameters.ts +49 -0
  119. package/packages/lsp-core/src/tools/rename.ts +69 -0
  120. package/packages/lsp-core/src/tools/result.ts +5 -0
  121. package/packages/lsp-core/src/tools/runtime.ts +21 -0
  122. package/packages/lsp-core/src/tools/schema.ts +9 -0
  123. package/packages/lsp-core/src/tools/status.ts +28 -0
  124. package/packages/lsp-core/src/tools/symbols.ts +99 -0
  125. package/packages/lsp-core/src/tools/types.ts +116 -0
  126. package/packages/lsp-core/src/tools.ts +1 -0
  127. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  128. package/packages/omo-codex/plugin/README.md +1 -1
  129. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  130. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  131. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  132. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  133. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  134. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  135. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  136. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  137. package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
  138. package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  139. package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/SKILL.md +7 -7
  140. package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/agents/openai.yaml +1 -1
  141. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  142. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  143. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  144. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  145. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  146. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  147. package/packages/omo-codex/plugin/components/teammode/package.json +2 -2
  148. package/packages/omo-codex/plugin/components/telemetry/README.md +2 -2
  149. package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +52 -15
  150. package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +52 -19
  151. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  152. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  153. package/packages/omo-codex/plugin/components/telemetry/src/codex-hook.ts +2 -9
  154. package/packages/omo-codex/plugin/components/telemetry/src/posthog.ts +4 -13
  155. package/packages/omo-codex/plugin/components/telemetry/src/product-identity.ts +0 -2
  156. package/packages/omo-codex/plugin/components/telemetry/test/codex-hook-diagnostics.test.ts +0 -3
  157. package/packages/omo-codex/plugin/components/telemetry/test/codex-hook.test.ts +3 -8
  158. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +3 -0
  159. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  160. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  161. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  162. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  163. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  164. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +3 -0
  165. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  166. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  167. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  168. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  169. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  170. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  171. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  172. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  173. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  174. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  175. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  176. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  177. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  178. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  179. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  180. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  181. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  182. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  183. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  184. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  185. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  186. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  187. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  188. package/packages/omo-codex/plugin/package-lock.json +16 -16
  189. package/packages/omo-codex/plugin/package.json +1 -1
  190. package/packages/omo-codex/plugin/shared/package.json +1 -1
  191. package/packages/omo-codex/plugin/skills/ast-grep/README.md +1 -1
  192. package/packages/omo-codex/plugin/skills/ast-grep/install.ps1 +5 -3
  193. package/packages/omo-codex/plugin/skills/ast-grep/install.sh +4 -5
  194. package/packages/omo-codex/plugin/skills/ast-grep/references/install.md +2 -2
  195. package/packages/omo-codex/plugin/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
  196. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  197. package/packages/omo-codex/plugin/skills/coding-agent-sessions/AGENTS.md +5 -4
  198. package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +11 -11
  199. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +2 -2
  200. package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/codex.md +4 -4
  201. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
  202. package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
  203. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +1 -1
  204. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +34 -12
  205. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +2 -2
  206. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +2 -1
  207. package/packages/omo-codex/plugin/skills/frontend/references/design/component-catalogs.md +14 -10
  208. package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +19 -6
  209. package/packages/omo-codex/plugin/skills/frontend/references/design/grounding.md +29 -0
  210. package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
  211. package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
  212. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
  213. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  214. package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +7 -7
  215. package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
  216. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +5 -4
  217. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -2
  218. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/__main__.py +9 -6
  219. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/summary.py +1 -1
  220. package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  221. package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
  222. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +3 -0
  223. package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +8 -4
  224. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +7 -316
  225. package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +23 -21
  226. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
  227. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +42 -8
  228. package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
  229. package/packages/omo-codex/plugin/skills/ulw-research/references/markdown-blocks.md +12 -0
  230. package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +12 -5
  231. package/packages/omo-codex/plugin/skills/ulw-research/references/research-collection.md +195 -0
  232. package/packages/omo-codex/plugin/skills/ulw-research/references/research-delivery.md +123 -0
  233. package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
  234. package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +13 -4
  235. package/packages/omo-codex/plugin/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
  236. package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  237. package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-lite.mjs +4 -2
  238. package/packages/omo-codex/plugin/skills/ulw-research/scripts/entities.mjs +13 -4
  239. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
  240. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
  241. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
  242. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
  243. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +35 -16
  244. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  245. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +6 -4
  246. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  247. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  248. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  249. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  250. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  251. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  252. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
  253. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  254. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  255. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  256. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  257. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  258. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  259. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  260. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +10 -1
  261. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +6 -2
  262. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +2 -2
  263. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  264. package/packages/omo-codex/plugin/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  265. package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +1 -1
  266. package/packages/omo-codex/plugin/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  267. package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +18 -7
  268. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  269. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +11 -22
  270. package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +18 -6
  271. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  272. package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  273. package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  274. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  275. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +7 -1
  276. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +79 -26
  277. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  278. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  279. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-view.mjs +89 -0
  280. package/packages/omo-codex/plugin/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  281. package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +14 -3
  282. package/packages/omo-codex/plugin/test/payload-equivalence.test.mjs +6 -4
  283. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -3
  284. package/packages/omo-codex/scripts/install-dist/install-local.mjs +338 -237
  285. package/packages/prompts-core/prompts/ultrawork/codex.md +3 -0
  286. package/packages/shared-skills/skill-source-filter.mjs +6 -2
  287. package/packages/shared-skills/skills/ast-grep/README.md +1 -1
  288. package/packages/shared-skills/skills/ast-grep/install.ps1 +5 -3
  289. package/packages/shared-skills/skills/ast-grep/install.sh +4 -5
  290. package/packages/shared-skills/skills/ast-grep/references/install.md +2 -2
  291. package/packages/shared-skills/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
  292. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  293. package/packages/shared-skills/skills/coding-agent-sessions/AGENTS.md +5 -4
  294. package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +11 -11
  295. package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +2 -2
  296. package/packages/shared-skills/skills/coding-agent-sessions/references/codex.md +4 -4
  297. package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
  298. package/packages/shared-skills/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
  299. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +1 -1
  300. package/packages/shared-skills/skills/frontend/SKILL.md +34 -12
  301. package/packages/shared-skills/skills/frontend/references/design/README.md +2 -2
  302. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +2 -1
  303. package/packages/shared-skills/skills/frontend/references/design/component-catalogs.md +14 -10
  304. package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +19 -6
  305. package/packages/shared-skills/skills/frontend/references/design/grounding.md +29 -0
  306. package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
  307. package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
  308. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +5 -4
  309. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -2
  310. package/packages/shared-skills/skills/ultimate-browsing/engine/__main__.py +9 -6
  311. package/packages/shared-skills/skills/ultimate-browsing/engine/summary.py +1 -1
  312. package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +4 -4
  313. package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
  314. package/packages/shared-skills/skills/ulw-research/AGENTS.md +8 -4
  315. package/packages/shared-skills/skills/ulw-research/SKILL.md +7 -316
  316. package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +23 -21
  317. package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
  318. package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +42 -8
  319. package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
  320. package/packages/shared-skills/skills/ulw-research/references/markdown-blocks.md +12 -0
  321. package/packages/shared-skills/skills/ulw-research/references/report-gates.md +12 -5
  322. package/packages/shared-skills/skills/ulw-research/references/research-collection.md +195 -0
  323. package/packages/shared-skills/skills/ulw-research/references/research-delivery.md +123 -0
  324. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
  325. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
  326. package/packages/shared-skills/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
  327. package/packages/shared-skills/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
  328. package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +13 -4
  329. package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +6 -110
  330. package/packages/shared-skills/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
  331. package/packages/shared-skills/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
  332. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
  333. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
  334. package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
  335. package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-polish.test.ts +4 -3
  336. package/packages/shared-skills/skills/ulw-research/scripts/css-hiding.mjs +158 -0
  337. package/packages/shared-skills/skills/ulw-research/scripts/css-lite.mjs +4 -2
  338. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
  339. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
  340. package/packages/shared-skills/skills/ulw-research/scripts/entities.mjs +13 -4
  341. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
  342. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
  343. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
  344. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
  345. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +35 -16
  346. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
  347. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +7 -2
  348. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
  349. package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +6 -4
  350. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
  351. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
  352. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
  353. package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +15 -9
  354. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
  355. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
  356. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
  357. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
  358. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
  359. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
  360. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
  361. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
  362. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
  363. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
  364. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
  365. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
  366. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
  367. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
  368. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
  369. package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
  370. package/packages/shared-skills/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
  371. package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +10 -1
  372. package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +6 -2
  373. package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +2 -2
  374. package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.mjs +140 -0
  375. package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
  376. package/packages/shared-skills/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
  377. package/packages/shared-skills/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
  378. package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +1 -1
  379. package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.mjs +33 -0
  380. package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
  381. package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +18 -7
  382. package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +3 -0
  383. package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.mjs +66 -0
  384. package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
  385. package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +11 -22
  386. package/packages/shared-skills/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
  387. package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +18 -6
  388. package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +3 -2
  389. package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
  390. package/packages/shared-skills/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
  391. package/packages/shared-skills/skills/ulw-research/scripts/raw-decode.mjs +110 -0
  392. package/packages/shared-skills/skills/ulw-research/scripts/raw-locator.mjs +47 -0
  393. package/packages/shared-skills/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
  394. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
  395. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
  396. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
  397. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
  398. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
  399. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +7 -1
  400. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +5 -800
  401. package/packages/shared-skills/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
  402. package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +79 -26
  403. package/packages/shared-skills/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
  404. package/packages/shared-skills/skills/ulw-research/scripts/source-refresh.mjs +82 -0
  405. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
  406. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
  407. package/packages/shared-skills/skills/ulw-research/scripts/source-view.mjs +89 -0
  408. package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.mjs +203 -0
  409. package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
  410. package/packages/shared-skills/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
  411. package/packages/shared-skills/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
  412. package/packages/shared-skills/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
  413. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
  414. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
  415. package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
  416. package/packages/shared-skills/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
  417. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
  418. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
  419. package/packages/shared-skills/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
  420. package/packages/shared-skills/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
  421. package/packages/shared-skills/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
  422. package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +14 -3
  423. package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +10 -0
  424. package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
  425. package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
  426. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
  427. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
  428. package/packages/shared-skills/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
  429. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/amp_redirect_stub.html +0 -7
  430. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/search_interstitial.html +0 -19
  431. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_available.json +0 -1
  432. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_snapshot.html +0 -1128
  433. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_fetch_chain.py +0 -77
  434. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +0 -86
  435. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +0 -279
  436. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate.py +0 -252
  437. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate_validators.py +0 -78
@@ -0,0 +1,47 @@
1
+ // Byte locators over a source view (source-view.mjs) and exact, context-bound re-anchoring.
2
+ // Matching always happens on the view's text (text nodes of a page, never markup); only the
3
+ // stored offsets are bytes of the view's artifact. value_span remains quote-relative text.
4
+
5
+ const chars = (text) => [...text]
6
+
7
+ /** Context is copied before normalization, around the value (or the whole quote for Q rows). */
8
+ export function quoteContext(text, start, end) {
9
+ return { prefix: chars(text.slice(0, start)).slice(-32).join(""), suffix: chars(text.slice(end)).slice(0, 32).join("") }
10
+ }
11
+
12
+ /** Convert a text-space locator from fact-locate.mjs into artifact byte offsets. */
13
+ export function rawLocated(view, found) {
14
+ if (found.error) return found
15
+ const { char_start: start, char_end: end } = found.locator
16
+ const from = start + (found.value_span?.[0] ?? 0)
17
+ const to = found.value_span ? start + found.value_span[1] : end
18
+ const [byteStart, byteEnd] = view.byteSpan(start, end)
19
+ return {
20
+ ...found,
21
+ context: quoteContext(view.text, from, to),
22
+ locator: { ...found.locator, artifact: view.artifact, unit: "bytes", char_start: byteStart, char_end: byteEnd },
23
+ }
24
+ }
25
+
26
+ function contextMatches(text, row, start, end) {
27
+ if (!row.context) return false
28
+ const from = row.value_span ? start + row.value_span[0] : start
29
+ const to = row.value_span ? start + row.value_span[1] : end
30
+ const { prefix, suffix } = row.context
31
+ return from >= prefix.length && text.slice(from - prefix.length, from) === prefix && text.slice(to, to + suffix.length) === suffix
32
+ }
33
+
34
+ /**
35
+ * The row's quote in the view: unchanged when an exact occurrence sits at the pinned byte range,
36
+ * else exactly one occurrence whose context matches (relocated), else null. Never rewrites the row.
37
+ */
38
+ export function reanchor(view, row) {
39
+ const quote = String(row.quote ?? "")
40
+ if (quote === "" || !view.byteSpan) return null
41
+ const { char_start: start, char_end: end } = row.locator
42
+ const hits = []
43
+ for (let at = view.text.indexOf(quote); at !== -1; at = view.text.indexOf(quote, at + 1)) hits.push({ at, span: view.byteSpan(at, at + quote.length) })
44
+ if (hits.some(({ span }) => span[0] === start && span[1] === end)) return { start, end, relocated: false }
45
+ const matching = hits.filter(({ at }) => contextMatches(view.text, row, at, at + quote.length))
46
+ return matching.length === 1 ? { start: matching[0].span[0], end: matching[0].span[1], relocated: true } : null
47
+ }
@@ -0,0 +1,139 @@
1
+ import { describe, expect, onTestFinished, test } from "bun:test"
2
+ import { mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs"
3
+ import { tmpdir } from "node:os"
4
+ import { join } from "node:path"
5
+
6
+ import { sha256Of } from "./cli-support.mjs"
7
+ import { DEFECT_CODES, INTEGRITY_CODES } from "./contracts.mjs"
8
+ import { readLedger, writeLedger } from "./fact-ledger.mjs"
9
+ import { run } from "./report-tools.mjs"
10
+ import { writeSnapshot } from "./source-snapshot.mjs"
11
+
12
+ const BRIEF = readFileSync(join(import.meta.dir, "tests/fixtures/fidelity-brief-locked.md"), "utf8")
13
+
14
+ function session(text: string, kind = "document") {
15
+ const dir = mkdtempSync(join(tmpdir(), "raw-snapshot-"))
16
+ onTestFinished(() => rmSync(dir, { recursive: true, force: true }))
17
+ writeFileSync(join(dir, "brief.md"), BRIEF)
18
+ writeSnapshot(join(dir, "sources"), "S1", { path: "source.txt", text, kind, extractor: "tool:source-snapshot@1" })
19
+ return dir
20
+ }
21
+
22
+ const add = (dir: string) => run(["fact", "add", "--session-dir", dir, "--source", "S1", "--find", "Span", "--label", "span", "--json"])
23
+
24
+ // fact verify needs the edition's fill map under the current brief, so the edition is filled, not hand-written.
25
+ async function verify(dir: string) {
26
+ const row = readLedger(dir).rows[0]
27
+ const [source, report] = [join(dir, "report.src.html"), join(dir, "report.html")]
28
+ writeFileSync(source, `<h2>Findings</h2><p>{{${row.fact_id}:${row.label}}} [S1].</p>`)
29
+ await run(["fact", "fill", source, "--out", report, "--session-dir", dir])
30
+ return run(["fact", "verify", report, "--session-dir", dir])
31
+ }
32
+
33
+ function refetch(dir: string, text: string) {
34
+ writeFileSync(join(dir, "sources/S1/raw.bin"), text)
35
+ const metaPath = join(dir, "sources/S1/meta.json")
36
+ const meta = JSON.parse(readFileSync(metaPath, "utf8"))
37
+ meta.sha256 = sha256Of(text)
38
+ writeFileSync(metaPath, JSON.stringify(meta))
39
+ const ledger = readLedger(dir)
40
+ ledger.rows[0].snapshot_sha256 = meta.sha256
41
+ writeLedger(dir, ledger)
42
+ }
43
+
44
+ describe("raw snapshot contract", () => {
45
+ test("text-only source cannot back a measured row", async () => {
46
+ const dir = session("Span is 320 m.")
47
+ const raw = join(dir, "sources/S1/raw.bin")
48
+ try { unlinkSync(raw) } catch (error) { if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error }
49
+ await expect(add(dir)).rejects.toThrow("raw.bin is missing")
50
+ })
51
+
52
+ test("raw bytes determine hash and UTF-8 locator offsets before normalization", async () => {
53
+ const text = "\u00e9.\nSpan is 320 m."
54
+ const dir = session(text)
55
+ writeFileSync(join(dir, "sources/S1/text.txt"), "Span is 999 m.")
56
+ const result = await add(dir)
57
+ expect(result.exitCode).toBe(0)
58
+ const row = result.json.data.row
59
+ expect(row.value).toBe("320")
60
+ expect(row.snapshot_sha256).toBe(sha256Of(Buffer.from(text)))
61
+ expect(row.locator).toMatchObject({ unit: "bytes", artifact: "raw.bin", char_start: 4, char_end: Buffer.byteLength(text) })
62
+ expect(row.context).toEqual({ prefix: "\u00e9.\nSpan is ", suffix: "." })
63
+ })
64
+
65
+ test("raw drift blocks verification even when quote re-anchors", async () => {
66
+ const dir = session("Span is 320 m.")
67
+ await add(dir)
68
+ writeFileSync(join(dir, "sources/S1/raw.bin"), " Span is 320 m.")
69
+ const result = await verify(dir)
70
+ expect(result.exitCode).toBe(1)
71
+ expect(result.json.defects.some((d: { code: string }) => d.code === "snapshot_hash_mismatch")).toBe(true)
72
+ })
73
+
74
+ test("explicitly repinned refetch re-anchors with old and new byte offsets", async () => {
75
+ const dir = session("Span is 320 m.")
76
+ await add(dir)
77
+ refetch(dir, " Span is 320 m.")
78
+ const result = await verify(dir)
79
+ expect(result.exitCode).toBe(0)
80
+ expect(result.json.defects).toContainEqual(expect.objectContaining({ code: "relocated", severity: "minor", message: expect.stringContaining("0:14 -> 3:17") }))
81
+ })
82
+
83
+ test("context disambiguates two exact quotes", async () => {
84
+ const dir = session("a".repeat(40) + "\nSpan is 320 m.\n" + "b".repeat(40))
85
+ await add(dir)
86
+ refetch(dir, "lead\n" + "c".repeat(40) + "\nSpan is 320 m.\n" + "d".repeat(40) + "\n" + "a".repeat(40) + "\nSpan is 320 m.\n" + "b".repeat(40))
87
+ const result = await verify(dir)
88
+ expect(result.exitCode).toBe(0)
89
+ expect(result.json.defects.some((d: { code: string }) => d.code === "relocated")).toBe(true)
90
+ })
91
+
92
+ test("identical contexts refuse ambiguous relocation with existing failure code", async () => {
93
+ const text = "a".repeat(40) + "\nSpan is 320 m.\n" + "b".repeat(40)
94
+ const dir = session(text)
95
+ await add(dir)
96
+ refetch(dir, `lead\n${text}\n${text}`)
97
+ const result = await verify(dir)
98
+ expect(result.exitCode).toBe(1)
99
+ expect(result.json.defects.some((d: { code: string }) => d.code === "quote_not_verbatim")).toBe(true)
100
+ expect(result.json.defects.some((d: { code: string }) => d.code === "relocated")).toBe(false)
101
+ })
102
+
103
+ test("snapshot_not_raw is an integrity blocker", () => {
104
+ expect(DEFECT_CODES.snapshot_not_raw).toMatchObject({ severity: "blocker", integrity: true })
105
+ expect(INTEGRITY_CODES.has("snapshot_not_raw")).toBe(true)
106
+ })
107
+
108
+ test("HTML snapshots hash unmodified markup rather than extracted prose", () => {
109
+ const dir = session("unused")
110
+ const html = "<p>Span&nbsp;is <b>320</b> m.</p>"
111
+ const saved = writeSnapshot(join(dir, "other"), "S2", { html, path: "source.html", extractor: "model" })
112
+ expect(readFileSync(join(saved.dir, "raw.bin"), "utf8")).toBe(html)
113
+ expect(saved.sha256).toBe(sha256Of(html))
114
+ })
115
+
116
+ test("raw table offsets select the labeled cell, not attribute numerals or another value", async () => {
117
+ const dir = session("unused")
118
+ const html = '<!--\u00e9--><table id="sizes"><tr><th>Name</th><th>Length</th></tr><tr><td>Deck</td><td><b title="9">320</b> m</td></tr><tr><td>Other</td><td>320 m</td></tr></table>'
119
+ writeSnapshot(join(dir, "sources"), "S2", { html, path: "source.html", extractor: "model" })
120
+ const result = await run(["fact", "add", "--session-dir", dir, "--source", "S2", "--table", "sizes", "--row", "Deck", "--col", "Length", "--label", "deck", "--json"])
121
+ expect(result.exitCode).toBe(0)
122
+ const row = result.json.data.row
123
+ expect(row.value).toBe("320")
124
+ expect(row.unit).toBe("m")
125
+ expect(row.quote).toBe("320 m")
126
+ expect(Buffer.from(html).subarray(row.locator.char_start, row.locator.char_end).toString()).toBe("320</b> m")
127
+ expect(row.quote.slice(...row.value_span)).toBe(row.raw)
128
+ expect(row.context.prefix).not.toContain("title")
129
+ })
130
+
131
+ test("converted-only sources refuse measured facts but allow warned quotations", async () => {
132
+ const dir = session("Span is 320 m.", "converted-only")
133
+ expect((await add(dir)).json.error).toBe("snapshot_not_raw")
134
+ const result = await run(["fact", "quote", "--session-dir", dir, "--source", "S1", "--match", "Span is 320 m.", "--json"])
135
+ expect(result.exitCode).toBe(0)
136
+ expect(result.json.defects).toContainEqual(expect.objectContaining({ code: "quote_converted_only" }))
137
+ expect(result.json.data.row.context).toEqual({ prefix: "", suffix: "" })
138
+ })
139
+ })
@@ -0,0 +1,233 @@
1
+ import { describe, expect, onTestFinished, test } from "bun:test"
2
+ import { createHash } from "node:crypto"
3
+ import { copyFileSync, existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
4
+ import { tmpdir } from "node:os"
5
+ import { delimiter, join } from "node:path"
6
+ import { PROMOTION } from "./fact-commands-meaning.mjs"
7
+ import { writeFidelityRecord } from "./fidelity-records.mjs"
8
+ import { PAIR_KINDS } from "./fact-pairs.mjs"
9
+ import { VERDICT_SCHEMA } from "./meaning-check.mjs"
10
+ import { POLISH_MARKUP_REASON } from "./polish-apply.mjs"
11
+ import { COMMANDS, run } from "./report-tools.mjs"
12
+ import { SCRIPT, FIXTURES, sessionDir, nodeWithoutBun, defectsFile, FIDELITY, sha256, CliRow, CliJson, nodeJson, fidelitySession, verify, codesOf, Pair, pairedSession, verdicts, calibrationRows, runLines, PolishJson, polishSession, polishApply, setField, fillTemplate, textSource, ledgerRow } from "./tests/report-tools-support"
13
+
14
+
15
+
16
+ describe("fact, source, and claims under node", () => {
17
+ test("#given a fresh session #when snapshot, add, quote, derive, fill, and verify run #then the filled report verifies clean", () => {
18
+ // given
19
+ const { dir, s, snap, add, quote, derive, mm } = fidelitySession()
20
+ // when
21
+ const filled = nodeJson(["fact", "fill", join(dir, "report.src.html"), "--out", join(dir, "report.html"), ...s])
22
+ const checked = verify(dir, "report.html")
23
+ // then
24
+ expect(snap.code).toBe(0)
25
+ expect(add.code).toBe(0)
26
+ expect(add.json.data.row.value).toBe("320")
27
+ expect(add.json.data.context.match).toBe("320 m")
28
+ expect(quote.code).toBe(0)
29
+ expect(quote.json.data.row.fact_id).toBe("Q1")
30
+ expect(derive.code).toBe(0)
31
+ expect(mm).toBe("F2")
32
+ expect(filled.code).toBe(0)
33
+ expect(readFileSync(join(dir, "report.html"), "utf8")).toContain("320,000 mm (320 m)")
34
+ expect(checked.code).toBe(0)
35
+ expect(checked.json.defects).toEqual([])
36
+ const record = JSON.parse(readFileSync(join(dir, "fidelity-defects.json"), "utf8"))
37
+ expect(record.artifact_sha256).toBe(sha256(join(dir, "report.html")))
38
+ })
39
+
40
+ test("#given a filled report #when a value is hand-edited or a number typed #then verify exits 1 with the code", () => {
41
+ const { dir, s } = fidelitySession()
42
+ nodeJson(["fact", "fill", join(dir, "report.src.html"), "--out", join(dir, "report.html"), ...s])
43
+ const html = readFileSync(join(dir, "report.html"), "utf8")
44
+ writeFileSync(join(dir, "unit.html"), html.replace("320 m", "320 mm"))
45
+ writeFileSync(join(dir, "typed.html"), html.replace("<h2>How", "<p>Revenue grew 12% [S4].</p>\n<h2>How"))
46
+ const unit = verify(dir, "unit.html")
47
+ const typed = verify(dir, "typed.html")
48
+ expect(unit.code).toBe(1)
49
+ expect(codesOf(unit.json)).toContain("unit_mismatch")
50
+ expect(typed.code).toBe(1)
51
+ expect(codesOf(typed.json)).toContain("untraceable_number")
52
+ })
53
+
54
+ test("#given an anchor that is not in the source #when fact add runs #then it exits 1 with fact_not_located and writes no row", () => {
55
+ const { dir, s } = fidelitySession()
56
+ const proc = nodeJson(["fact", "add", "--source", "S4", "--find", "nope", "--label", "x", ...s])
57
+ expect(proc.code).toBe(1)
58
+ expect(proc.stdout).toContain("fact_not_located")
59
+ expect(JSON.parse(readFileSync(join(dir, "fact-ledger.json"), "utf8")).rows.map((r: { fact_id: string }) => r.fact_id)).toEqual(["F1", "Q1", "F2"])
60
+ })
61
+
62
+ test("#given a snapshotted source #when snapshotted again with the same or different content #then same is a no-op and different exits 1", () => {
63
+ const { dir, s } = fidelitySession()
64
+ writeFileSync(join(dir, "other.txt"), "Span length: 999 m\n")
65
+ expect(nodeJson(["source", "snapshot", "--id", "S4", "--html", join(FIDELITY, "sources", "S4.html"), ...s]).code).toBe(0)
66
+ expect(nodeJson(["source", "snapshot", "--id", "S4", "--text", join(dir, "other.txt"), ...s]).code).toBe(1)
67
+ })
68
+
69
+ test("#given an assumed value with and without a rationale #when added #then the row is kind assumed, and no rationale exits 2", () => {
70
+ const { s } = fidelitySession()
71
+ const added = nodeJson(["fact", "add", "--assumed", "12", "--unit", "%", "--rationale", "planning share chosen by the requester", "--label", "share_assumed", "--json", ...s])
72
+ expect(added.code).toBe(0)
73
+ expect(added.json.data.row.kind).toBe("assumed")
74
+ expect(nodeJson(["fact", "add", "--assumed", "12", "--label", "x", ...s]).code).toBe(2)
75
+ })
76
+
77
+ test("#given the fixture claim graph #when claims index runs #then claims.json lists its claims", () => {
78
+ const dir = sessionDir()
79
+ const proc = nodeJson(["claims", "index", join(FIDELITY, "claim-graph.md"), "--out", join(dir, "claims.json")])
80
+ expect(proc.code).toBe(0)
81
+ expect(JSON.parse(readFileSync(join(dir, "claims.json"), "utf8")).claims.length).toBeGreaterThan(0)
82
+ })
83
+
84
+ test("#given a ledger #when exported as csv #then rows carry fact_id and plot_value and stderr has the coverage line", () => {
85
+ const { s } = fidelitySession()
86
+ const proc = nodeJson(["fact", "export", "--csv", ...s])
87
+ expect(proc.code).toBe(0)
88
+ expect(proc.stdout.split("\n")[0]).toContain("fact_id")
89
+ expect(proc.stdout).toContain("F2")
90
+ expect(proc.stderr).toContain("1 of 1 sources snapshotted")
91
+ })
92
+ })
93
+
94
+ describe("meaning verifier under node", () => {
95
+ test("#given the filled fixture #when fact pairs runs #then pairs.json holds one quote pair per cited ledger quote", () => {
96
+ const { dir, pairs } = pairedSession()
97
+ const written = JSON.parse(readFileSync(join(dir, "pairs.json"), "utf8"))
98
+ expect(pairs.code).toBe(0)
99
+ expect(written.skipped).toBe(false)
100
+ expect(written.pairs.map((p: Pair) => [p.pair_id, p.kind, p.fact_ids])).toEqual([
101
+ ["P1", "quote", ["F1"]],
102
+ ["P2", "quote", ["Q1"]],
103
+ ])
104
+ expect(written.pairs_sha256).toMatch(/^[0-9a-f]{64}$/)
105
+ expect(Object.keys(written.counts).filter((key) => key !== "total" && key !== "force_strong")).toEqual([...PAIR_KINDS])
106
+ expect(written.verdict_schema).toEqual(JSON.parse(JSON.stringify(VERDICT_SCHEMA)))
107
+ })
108
+
109
+ test("#given cheap and strong verdicts agreeing on hedge_dropped #when meaning-check runs #then it exits 1 with one confirmed defect and one calibration line", () => {
110
+ const { dir, s } = pairedSession()
111
+ const drift = { pair_id: "P2", verdict: "drift", drift_kind: "hedge_dropped", fragment: "no earlier than 2027", source_fragment: "we estimate", explanation: "The estimate reads as a fact." }
112
+ const cheap = verdicts(dir, "cheap.json", "cheap", [drift, { pair_id: "P1", verdict: "ok" }])
113
+ const strong = verdicts(dir, "strong.json", "strong", [drift])
114
+ const checked = nodeJson(["fact", "meaning-check", "--pairs", join(dir, "pairs.json"), "--verdicts", cheap, "--verdicts", strong, ...s])
115
+ const record = JSON.parse(readFileSync(join(dir, "fidelity-meaning-defects.json"), "utf8"))
116
+ expect(checked.code).toBe(1)
117
+ expect(codesOf(checked.json)).toEqual(["meaning_drift_confirmed"])
118
+ expect(codesOf(record)).toEqual(["meaning_drift_confirmed"])
119
+ expect(calibrationRows(dir)).toHaveLength(1)
120
+ expect(calibrationRows(dir)[0]).toMatchObject({ pair_id: "P2", drift_kind: "hedge_dropped", confirmed: true, disputed: false })
121
+ const header = JSON.parse(readFileSync(join(dir, "fidelity-calibration.jsonl"), "utf8").split("\n")[0])
122
+ expect(header.promotion).toEqual({ ...PROMOTION })
123
+ const again = nodeJson(["fact", "meaning-check", "--pairs", join(dir, "pairs.json"), "--verdicts", cheap, "--verdicts", strong, ...s])
124
+ expect(again.code).toBe(1)
125
+ expect(calibrationRows(dir)).toHaveLength(1)
126
+ })
127
+
128
+ test("#given a dispute on the confirmed pair #when meaning-check runs #then the defect stays and the calibration line is disputed", () => {
129
+ const { dir, s } = pairedSession()
130
+ const drift = { pair_id: "P2", verdict: "drift", drift_kind: "hedge_dropped", fragment: "no earlier than 2027", source_fragment: "we estimate" }
131
+ const files = ["--verdicts", verdicts(dir, "cheap.json", "cheap", [drift, { pair_id: "P1", verdict: "ok" }]), "--verdicts", verdicts(dir, "strong.json", "strong", [drift])]
132
+ const checked = nodeJson(["fact", "meaning-check", "--pairs", join(dir, "pairs.json"), ...files, "--dispute", "P2", "--reason", "the operator states it", ...s])
133
+ expect(checked.code).toBe(1)
134
+ expect(codesOf(checked.json)).toEqual(["meaning_drift_confirmed"])
135
+ expect(calibrationRows(dir)[0]).toMatchObject({ pair_id: "P2", disputed: true, reason: "the operator states it" })
136
+ })
137
+
138
+ test("#given --edition md #when meaning-check runs #then it writes that edition's record, and an unknown edition refuses", () => {
139
+ const { dir, s } = pairedSession()
140
+ const clean = [{ pair_id: "P1", verdict: "ok" }, { pair_id: "P2", verdict: "ok" }]
141
+ const files = ["--verdicts", verdicts(dir, "cheap.json", "cheap", clean), "--verdicts", verdicts(dir, "strong.json", "strong", clean)]
142
+ const checked = nodeJson(["fact", "meaning-check", "--pairs", join(dir, "pairs.json"), ...files, "--edition", "md", ...s])
143
+ const unknown = nodeJson(["fact", "meaning-check", "--pairs", join(dir, "pairs.json"), ...files, "--edition", "epub", ...s])
144
+ expect(checked.code).toBe(0)
145
+ expect(JSON.parse(readFileSync(join(dir, "fidelity-meaning-defects.md.json"), "utf8"))).toMatchObject({ record_kind: "meaning", artifact_sha256: sha256(join(dir, "report.html")) })
146
+ expect(existsSync(join(dir, "fidelity-meaning-defects.json"))).toBe(false)
147
+ expect(unknown.code).toBe(2)
148
+ })
149
+
150
+ test("#given a dropped verdict and an unread pair #when meaning-check runs #then both pairs are meaning_unverified and it exits 1", () => {
151
+ const { dir, s } = pairedSession()
152
+ const cheap = verdicts(dir, "cheap.json", "cheap", [{ pair_id: "P2", verdict: "drift", drift_kind: "hedge_dropped", fragment: "certainly by 2027", source_fragment: "we estimate" }])
153
+ const checked = nodeJson(["fact", "meaning-check", "--pairs", join(dir, "pairs.json"), "--verdicts", cheap, ...s])
154
+ const summary = JSON.parse(checked.stderr.trim().split("\n").at(-1) ?? "{}")
155
+ expect(checked.code).toBe(1)
156
+ expect(checked.json.status).toBe("fail")
157
+ expect(codesOf(checked.json)).toEqual(["meaning_unverified", "meaning_unverified"])
158
+ expect(checked.json.unverified.map((gap: { pair_id: string }) => gap.pair_id)).toEqual(["P1", "P2"])
159
+ expect(summary).toMatchObject({ command: "fact meaning-check", defects: 2, dropped: 1, unverified: 2 })
160
+ })
161
+
162
+ test("#given an unreadable verdict file or a malformed pairs file #when meaning-check runs #then it exits 2", () => {
163
+ const { dir, s } = pairedSession()
164
+ const broken = join(dir, "broken.json")
165
+ writeFileSync(broken, "{ not json")
166
+ expect(nodeJson(["fact", "meaning-check", "--pairs", join(dir, "pairs.json"), "--verdicts", broken, ...s]).code).toBe(2)
167
+ const ok = verdicts(dir, "cheap.json", "cheap", [])
168
+ for (const body of ["{}", "null", '{ "pairs": "P1" }']) {
169
+ writeFileSync(join(dir, "bad-pairs.json"), body)
170
+ expect(nodeJson(["fact", "meaning-check", "--pairs", join(dir, "bad-pairs.json"), "--verdicts", ok, ...s]).code, body).toBe(2)
171
+ }
172
+ })
173
+
174
+ test("#given a confirmed run then a dispute on the same inputs #when meaning-check reruns #then the log still holds one run with the dispute", () => {
175
+ const { dir, s } = pairedSession()
176
+ const drift = { pair_id: "P2", verdict: "drift", drift_kind: "hedge_dropped", fragment: "no earlier than 2027", source_fragment: "we estimate" }
177
+ const files = ["--verdicts", verdicts(dir, "cheap.json", "cheap", [drift, { pair_id: "P1", verdict: "ok" }]), "--verdicts", verdicts(dir, "strong.json", "strong", [drift])]
178
+ const first = nodeJson(["fact", "meaning-check", "--pairs", join(dir, "pairs.json"), ...files, ...s])
179
+ const disputed = nodeJson(["fact", "meaning-check", "--pairs", join(dir, "pairs.json"), ...files, "--dispute", "P2", "--reason", "the operator states it", ...s])
180
+ expect(disputed.code).toBe(1)
181
+ expect(disputed.json.run_id).toBe(first.json.run_id)
182
+ expect(disputed.json.promotion_eligibility).toMatchObject({ runs: 1, confirmed: 1, disputed_confirmed: 1, disputed_to_confirmed: 1 })
183
+ expect(calibrationRows(dir)).toEqual([expect.objectContaining({ pair_id: "P2", disputed: true, reason: "the operator states it" })])
184
+ const plain = nodeJson(["fact", "meaning-check", "--pairs", join(dir, "pairs.json"), ...files, ...s])
185
+ expect(plain.json.promotion_eligibility).toMatchObject({ runs: 1, disputed_confirmed: 1 })
186
+ })
187
+
188
+ test("#given a run with no flagged pair #when meaning-check runs #then the run is still logged and counted", () => {
189
+ const { dir, s } = pairedSession()
190
+ const ok = (pair_id: string) => ({ pair_id, verdict: "ok" })
191
+ const files = ["--verdicts", verdicts(dir, "cheap.json", "cheap", [ok("P1"), ok("P2")]), "--verdicts", verdicts(dir, "strong.json", "strong", [ok("P1"), ok("P2")])]
192
+ const checked = nodeJson(["fact", "meaning-check", "--pairs", join(dir, "pairs.json"), ...files, ...s])
193
+ expect(checked.code).toBe(0)
194
+ expect(calibrationRows(dir)).toEqual([])
195
+ expect(runLines(dir)).toEqual([expect.objectContaining({ run_id: checked.json.run_id, flagged: 0 })])
196
+ expect(checked.json.promotion_eligibility.runs).toBe(1)
197
+ })
198
+
199
+ test("#given a corrupt calibration log #when meaning-check runs #then it exits 2 naming the file and line", () => {
200
+ const { dir, s } = pairedSession()
201
+ writeFileSync(join(dir, "fidelity-calibration.jsonl"), "{ not json\n")
202
+ const checked = nodeJson(["fact", "meaning-check", "--pairs", join(dir, "pairs.json"), "--verdicts", verdicts(dir, "cheap.json", "cheap", []), ...s])
203
+ expect(checked.code).toBe(2)
204
+ expect(checked.stderr).toContain(`${join(dir, "fidelity-calibration.jsonl")}:1`)
205
+ })
206
+
207
+ test("#given a skipped pairs file #when meaning-check runs #then it is accepted only when the locked brief says off", () => {
208
+ const { dir, s } = fidelitySession()
209
+ const skip = join(dir, "skip.json")
210
+ writeFileSync(skip, JSON.stringify({ schemaVersion: 1, skipped: true, pairs: [] }))
211
+ const check = () => nodeJson(["fact", "meaning-check", "--pairs", skip, "--verdicts", verdicts(dir, "cheap.json", "cheap", []), ...s])
212
+ const on = check()
213
+ expect(on.code).toBe(1)
214
+ expect(on.json.error).toBe("meaning_check_required")
215
+ writeFileSync(join(dir, "brief.md"), "# Brief\n")
216
+ expect(check().json.error).toBe("fidelity_interview_incomplete")
217
+ expect(check().code).toBe(1)
218
+ writeFileSync(join(dir, "brief.md"), readFileSync(join(FIXTURES, "fidelity-brief-locked.md"), "utf8").replace("meaning check: on", "meaning check: off"))
219
+ const off = check()
220
+ expect(off.code).toBe(0)
221
+ expect(off.json.status).toBe("skipped")
222
+ })
223
+
224
+ test("#given a brief with meaning check off #when fact pairs runs #then it exits 0 and writes skipped", () => {
225
+ const { dir, s } = fidelitySession()
226
+ const brief = readFileSync(join(dir, "brief.md"), "utf8").replace("meaning check: on", "meaning check: off")
227
+ writeFileSync(join(dir, "brief.md"), brief)
228
+ nodeJson(["fact", "fill", join(dir, "report.src.html"), "--out", join(dir, "report.html"), ...s])
229
+ const pairs = nodeJson(["fact", "pairs", join(dir, "report.html"), "--out", join(dir, "pairs.json"), ...s])
230
+ expect(pairs.code).toBe(0)
231
+ expect(JSON.parse(readFileSync(join(dir, "pairs.json"), "utf8"))).toMatchObject({ skipped: true })
232
+ })
233
+ })
@@ -0,0 +1,141 @@
1
+ import { describe, expect, onTestFinished, test } from "bun:test"
2
+ import { createHash } from "node:crypto"
3
+ import { copyFileSync, existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
4
+ import { tmpdir } from "node:os"
5
+ import { delimiter, join } from "node:path"
6
+ import { PROMOTION } from "./fact-commands-meaning.mjs"
7
+ import { writeFidelityRecord } from "./fidelity-records.mjs"
8
+ import { PAIR_KINDS } from "./fact-pairs.mjs"
9
+ import { VERDICT_SCHEMA } from "./meaning-check.mjs"
10
+ import { POLISH_MARKUP_REASON } from "./polish-apply.mjs"
11
+ import { COMMANDS, run } from "./report-tools.mjs"
12
+ import { SCRIPT, FIXTURES, sessionDir, nodeWithoutBun, defectsFile, FIDELITY, sha256, CliRow, CliJson, nodeJson, fidelitySession, verify, codesOf, Pair, pairedSession, verdicts, calibrationRows, runLines, PolishJson, polishSession, polishApply, setField, fillTemplate, textSource, ledgerRow } from "./tests/report-tools-support"
13
+
14
+
15
+
16
+ describe("polish wiring under node", () => {
17
+ test("#given the fixture edits #when fact polish-apply runs #then it exits 0, applies the wording edit, and rejects the numeral edit", () => {
18
+ const { dir } = polishSession()
19
+ const applied = polishApply(dir, join(FIDELITY, "polish-edits.json"))
20
+ const polished = readFileSync(join(dir, "report.polished.html"), "utf8")
21
+ expect(applied.code).toBe(0)
22
+ expect(applied.json.data).toMatchObject({ applied: 1, rejected: 1, unlocated: 0 })
23
+ expect(applied.json.defects.map((d) => [d.code, d.index])).toEqual([["polish_edit_rejected", 1]])
24
+ expect(polished).toContain("Collected from a single cited public page")
25
+ expect(polished).toContain("retrieved 2026-10-01")
26
+ expect(polished).not.toContain("2026-10-02")
27
+ })
28
+
29
+ test("#given an edit that adds an attribute #when polish-apply and polish-diff run #then apply refuses it as markup changed and diff exits 1", () => {
30
+ const { dir } = polishSession()
31
+ const html = readFileSync(join(dir, "report.html"), "utf8")
32
+ const edits = join(dir, "attribute.json")
33
+ writeFileSync(edits, JSON.stringify([{ before: "<h2>Findings</h2>", after: '<h2 id="findings">Findings</h2>', reason: "anchor" }]))
34
+ const applied = polishApply(dir, edits)
35
+ expect(applied.code).toBe(0)
36
+ expect(applied.json.data).toMatchObject({ applied: 0, rejected: 1, unlocated: 0 })
37
+ expect(applied.json.defects.map((d) => [d.code, d.index])).toEqual([["polish_edit_rejected", 0]])
38
+ expect(applied.json.defects[0]?.reason).toContain("markup changed")
39
+ expect(readFileSync(join(dir, "report.polished.html"), "utf8")).toBe(html)
40
+ writeFileSync(join(dir, "attribute.html"), html.replace("<h2>Findings</h2>", '<h2 id="findings">Findings</h2>'))
41
+ const diffed = nodeJson(["fact", "polish-diff", "--before", join(dir, "report.html"), "--after", join(dir, "attribute.html"), "--ledger", join(dir, "fact-ledger.json"), "--session-dir", dir])
42
+ expect(diffed.code).toBe(1)
43
+ expect(codesOf(diffed.json)).toContain("polish_diff_fail")
44
+ expect(JSON.stringify(diffed.json)).toContain("markup changed")
45
+ })
46
+
47
+ test("#given an edit whose before holds & #when fact polish-apply runs #then it prints polish-apply's own markup reason verbatim for that edit", () => {
48
+ const { dir } = polishSession()
49
+ const before = "Acme & Bolt"
50
+ const edits = join(dir, "ampersand.json")
51
+ writeFileSync(edits, JSON.stringify([{ before, after: "Acme and Bolt", reason: "ampersand" }]))
52
+ const applied = polishApply(dir, edits)
53
+ const line = ` rejected edit 0 ("${before}"): ${POLISH_MARKUP_REASON}`
54
+ expect(applied.code).toBe(0)
55
+ expect(applied.json.data).toMatchObject({ applied: 0, rejected: 1, unlocated: 0 })
56
+ expect(applied.json.defects.map((d) => [d.code, d.index, d.reason])).toEqual([["polish_edit_rejected", 0, POLISH_MARKUP_REASON]])
57
+ expect(applied.stderr.split("\n")).toContain(line)
58
+ })
59
+
60
+ test("#given a polished copy edited by hand #when fact polish-diff runs #then it exits 1 with polish_diff_fail", () => {
61
+ const { dir } = polishSession()
62
+ const html = readFileSync(join(dir, "report.html"), "utf8")
63
+ writeFileSync(join(dir, "hand.html"), html.replace("320 m", "321 m"))
64
+ const diffed = nodeJson(["fact", "polish-diff", "--before", join(dir, "report.html"), "--after", join(dir, "hand.html"), "--ledger", join(dir, "fact-ledger.json"), "--session-dir", dir])
65
+ expect(diffed.code).toBe(1)
66
+ expect(codesOf(diffed.json)).toContain("polish_diff_fail")
67
+ })
68
+
69
+ test("#given an accepted subject and object swap #when fact pairs runs with --changed-since #then only that sentence's pair is emitted, with the new text", () => {
70
+ const { dir, s } = polishSession()
71
+ const edits = join(dir, "swap.json")
72
+ writeFileSync(edits, JSON.stringify([{ before: "Acme acquired Bolt", after: "Bolt acquired Acme", reason: "word order" }]))
73
+ const applied = polishApply(dir, edits)
74
+ const map = join(dir, "report.html.fill-map.json")
75
+ const changed = nodeJson(["fact", "pairs", join(dir, "report.polished.html"), "--changed-since", map, "--out", join(dir, "pairs-changed.json"), ...s])
76
+ const written = JSON.parse(readFileSync(join(dir, "pairs-changed.json"), "utf8"))
77
+ expect(applied.json.data).toMatchObject({ applied: 1, rejected: 0 })
78
+ expect(changed.code).toBe(0)
79
+ expect(written.pairs.map((p: Pair) => [p.kind, p.fact_ids])).toEqual([["quote", ["F1"]]])
80
+ expect(written.pairs[0].sentence).toContain("Bolt acquired Acme")
81
+ })
82
+ })
83
+
84
+ describe("markdown editions under node", () => {
85
+ const MD = ["# Bridge", "", "## Findings", "", "The span length is {{F1:span_len}} [S4].", "", "The operator wrote {{Q1:completion}} [S4].", "", "## How this report was made", "", "Collected from one cited public page [S4].", "", "## Sources", "", "1. [S4] Bridge page, retrieved 2026-10-01.", ""].join("\n")
86
+
87
+ function mdSession() {
88
+ const session = fidelitySession()
89
+ writeFileSync(join(session.dir, "report.src.md"), MD)
90
+ const filled = nodeJson(["fact", "fill", join(session.dir, "report.src.md"), "--format", "md", "--out", join(session.dir, "report.md"), ...session.s])
91
+ return { ...session, filled }
92
+ }
93
+
94
+ test("#given a markdown source #when filled and verified as md #then verify passes and records the md bytes", () => {
95
+ const { dir, filled } = mdSession()
96
+ const checked = verify(dir, "report.md", ["--format", "md"])
97
+ expect(filled.code).toBe(0)
98
+ expect(readFileSync(join(dir, "report.md"), "utf8")).toContain("The span length is 320 m [S4].")
99
+ expect(checked.code).toBe(0)
100
+ expect(checked.json.defects).toEqual([])
101
+ expect(JSON.parse(readFileSync(join(dir, "fidelity-defects.json"), "utf8")).artifact_sha256).toBe(sha256(join(dir, "report.md")))
102
+ })
103
+
104
+ test("#given a filled markdown edition #when a value is hand-edited or a number typed #then md verify exits 1 with the code", () => {
105
+ const { dir } = mdSession()
106
+ const md = readFileSync(join(dir, "report.md"), "utf8")
107
+ writeFileSync(join(dir, "typed.md"), md.replace("## How", "Revenue grew 12% [S4].\n\n## How"))
108
+ copyFileSync(join(dir, "report.md.fill-map.json"), join(dir, "typed.md.fill-map.json"))
109
+ const typed = verify(dir, "typed.md", ["--format", "md"])
110
+ expect(typed.code).toBe(1)
111
+ expect(codesOf(typed.json)).toContain("untraceable_number")
112
+ })
113
+
114
+ test("#given a filled markdown edition with an unterminated fence #when verified or paired #then both refuse with exit 2", () => {
115
+ const { dir, s } = mdSession()
116
+ writeFileSync(join(dir, "fence.md"), `${readFileSync(join(dir, "report.md"), "utf8")}\n\`\`\`\nunclosed code\n`)
117
+ copyFileSync(join(dir, "report.md.fill-map.json"), join(dir, "fence.md.fill-map.json"))
118
+ const checked = verify(dir, "fence.md", ["--format", "md"])
119
+ expect(checked.code).toBe(2)
120
+ expect(checked.stderr).toContain("unterminated_fence")
121
+ expect(nodeJson(["fact", "pairs", join(dir, "fence.md"), "--out", join(dir, "pairs.json"), ...s]).code).toBe(2)
122
+ })
123
+
124
+ test("#given a filled markdown edition #when fact pairs runs on it #then it pairs the same quotes as the html edition", () => {
125
+ const { dir, s } = mdSession()
126
+ const pairs = nodeJson(["fact", "pairs", join(dir, "report.md"), "--out", join(dir, "pairs.json"), ...s])
127
+ expect(pairs.code).toBe(0)
128
+ expect(JSON.parse(readFileSync(join(dir, "pairs.json"), "utf8")).pairs.map((p: Pair) => p.fact_ids)).toEqual([["F1"], ["Q1"]])
129
+ })
130
+ })
131
+
132
+ describe("repair decide with several defect files", () => {
133
+ test("#given two --defects files #when decided #then the attempt signature carries both codes", async () => {
134
+ const dir = sessionDir()
135
+ const state = join(dir, "repair-state.json")
136
+ await run(["repair", "decide", "--state", state, "--defects", defectsFile(dir, "a.json", ["heading_generic"]), "--defects", defectsFile(dir, "b.json", ["untraceable_number"])])
137
+ const signature = JSON.parse(readFileSync(state, "utf8")).attempts[0].signature
138
+ expect(signature).toContain("heading_generic")
139
+ expect(signature).toContain("untraceable_number")
140
+ })
141
+ })