lazycodex-ai 5.1.44 → 5.1.46

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 (412) hide show
  1. package/README.de.md +0 -5
  2. package/README.es.md +0 -5
  3. package/README.fr.md +0 -5
  4. package/README.it.md +0 -5
  5. package/README.ja.md +0 -5
  6. package/README.ko.md +0 -5
  7. package/README.md +4 -5
  8. package/README.pt-br.md +0 -5
  9. package/README.ru.md +0 -6
  10. package/README.zh-cn.md +0 -5
  11. package/dist/cli/index.js +865 -413
  12. package/dist/cli/service-auth/keystore.d.ts +4 -2
  13. package/dist/cli-node/index.js +865 -413
  14. package/docs/reference/web-terminal-visual-qa.md +8 -0
  15. package/package.json +1 -1
  16. package/packages/lsp-daemon/dist/cli.js +7 -7
  17. package/packages/lsp-daemon/dist/client.js +7 -7
  18. package/packages/lsp-daemon/dist/index.js +7 -7
  19. package/packages/lsp-daemon/package.json +3 -3
  20. package/packages/lsp-tools-mcp/dist/cli.js +7 -7
  21. package/packages/lsp-tools-mcp/dist/mcp.js +7 -7
  22. package/packages/lsp-tools-mcp/dist/tools.js +4 -4
  23. package/packages/lsp-tools-mcp/package.json +3 -3
  24. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  25. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +77 -26
  26. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  27. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  28. package/packages/omo-codex/plugin/components/bootstrap/src/hook.ts +1 -1
  29. package/packages/omo-codex/plugin/components/comment-checker/biome.json +1 -21
  30. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  31. package/packages/omo-codex/plugin/components/comment-checker/package.json +4 -4
  32. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  33. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  34. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/biome.json +1 -21
  35. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  36. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +4 -4
  37. package/packages/omo-codex/plugin/components/lsp/biome.json +1 -21
  38. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  39. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +4 -4
  40. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  41. package/packages/omo-codex/plugin/components/lsp/package.json +4 -4
  42. package/packages/omo-codex/plugin/components/lsp/scripts/build-runtime.mjs +0 -1
  43. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-not-installed.test.ts +1 -1
  44. package/packages/omo-codex/plugin/components/rules/biome.json +1 -21
  45. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  46. package/packages/omo-codex/plugin/components/rules/package.json +4 -4
  47. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  48. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  49. package/packages/omo-codex/plugin/components/telemetry/biome.json +1 -21
  50. package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +90 -1
  51. package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +91 -1
  52. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  53. package/packages/omo-codex/plugin/components/telemetry/package.json +4 -4
  54. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +2 -2
  55. package/packages/omo-codex/plugin/components/ultrawork/biome.json +1 -21
  56. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  57. package/packages/omo-codex/plugin/components/ultrawork/package.json +4 -4
  58. package/packages/omo-codex/plugin/components/ulw-execute-continuation/biome.json +1 -21
  59. package/packages/omo-codex/plugin/components/ulw-execute-continuation/directive.md +1 -1
  60. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  61. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +4 -4
  62. package/packages/omo-codex/plugin/components/ulw-loop/biome.json +1 -21
  63. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +1 -1
  64. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +7 -7
  65. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +5 -5
  66. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +1 -1
  67. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  68. package/packages/omo-codex/plugin/components/ulw-loop/package.json +4 -4
  69. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +1 -1
  70. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +5 -5
  71. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +1 -1
  72. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-mutations.mjs +2 -0
  73. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  74. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  75. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  76. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  77. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  78. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  79. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  80. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  81. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  82. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  83. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  84. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  85. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  86. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  87. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  88. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  89. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  90. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  91. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  92. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  93. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  94. package/packages/omo-codex/plugin/package-lock.json +12 -12
  95. package/packages/omo-codex/plugin/package.json +1 -1
  96. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +14 -9
  97. package/packages/omo-codex/plugin/skills/ast-grep/AGENTS.md +1 -1
  98. package/packages/omo-codex/plugin/skills/browser/SKILL.md +21 -22
  99. package/packages/omo-codex/plugin/skills/browser/references/commands.md +2 -9
  100. package/packages/omo-codex/plugin/skills/browser/references/install.md +1 -2
  101. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  102. package/packages/omo-codex/plugin/skills/browser/scripts/browser-doctor.mjs +2 -2
  103. package/packages/omo-codex/plugin/skills/browser/scripts/browser-engine-guard.mjs +63 -487
  104. package/packages/omo-codex/plugin/skills/browser/scripts/browser-install.mjs +1 -1
  105. package/packages/omo-codex/plugin/skills/browser/scripts/omowright.mjs +1 -10
  106. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +2 -2
  107. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +79 -118
  108. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +75 -224
  109. package/packages/omo-codex/plugin/skills/frontend/references/design/stylegallery.md +32 -27
  110. package/packages/omo-codex/plugin/skills/frontend/references/perfection/README.md +2 -2
  111. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/check-accent-stripes.ts +8 -24
  112. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/check-draft-guard.ts +40 -0
  113. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/collect-files.ts +53 -0
  114. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/findings.ts +16 -0
  115. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/lint.ts +38 -0
  116. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/rules.ts +69 -0
  117. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/text-nodes.ts +121 -0
  118. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/attr-lookalikes.tsx +10 -0
  119. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/bound-values.tsx +12 -0
  120. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/const-generics.tsx +3 -0
  121. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/generics.tsx +7 -0
  122. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/measured-stats.html +6 -0
  123. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/plain-copy.html +9 -0
  124. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/state-copy.html +11 -0
  125. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/attribute-edges.html +7 -0
  126. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/components.tsx +16 -0
  127. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/decorative-badge.vue +7 -0
  128. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/emoji-icons.tsx +9 -0
  129. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/jsx-edges.tsx +1 -0
  130. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.html +6 -0
  131. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.tsx +8 -0
  132. package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/vanity-stats.html +5 -0
  133. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +1 -1
  134. package/packages/omo-codex/plugin/skills/programming/SKILL.md +10 -7
  135. package/packages/omo-codex/plugin/skills/programming/references/typescript/README.md +10 -7
  136. package/packages/omo-codex/plugin/skills/programming/references/typescript/bootstrap.md +9 -23
  137. package/packages/omo-codex/plugin/skills/programming/references/typescript/oxc-lint.md +130 -0
  138. package/packages/omo-codex/plugin/skills/programming/references/typescript/tsconfig-strict.md +6 -62
  139. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +12 -12
  140. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/new-project.ts +20 -81
  141. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/.oxfmtrc.json +9 -0
  142. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/.oxlintrc.json +46 -0
  143. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/LICENSE +21 -0
  144. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/index.js +10 -0
  145. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/no-em-dash-copy.js +28 -0
  146. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/require-suppression-reason.js +62 -0
  147. package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/tsconfig.json +29 -0
  148. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +14 -14
  149. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +1 -1
  150. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +2 -2
  151. package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +1 -1
  152. package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +2 -2
  153. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
  154. package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +21 -3
  155. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +37 -12
  156. package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +77 -16
  157. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +134 -0
  158. package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +95 -0
  159. package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
  160. package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +68 -3
  161. package/packages/omo-codex/plugin/skills/ulw-research/references/report-visualization.md +2 -0
  162. package/packages/omo-codex/plugin/skills/ulw-research/references/unit-rules.md +157 -0
  163. package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
  164. package/packages/omo-codex/plugin/skills/ulw-research/scripts/claims-index.mjs +125 -0
  165. package/packages/omo-codex/plugin/skills/ulw-research/scripts/cli-support.mjs +19 -5
  166. package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +89 -3
  167. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
  168. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
  169. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +239 -0
  170. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +66 -0
  171. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-expr.mjs +108 -0
  172. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +194 -0
  173. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +250 -0
  174. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-schema.mjs +221 -0
  175. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger.mjs +123 -0
  176. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +231 -0
  177. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
  178. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
  179. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
  180. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-brief.mjs +150 -0
  181. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
  182. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
  183. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
  184. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-state.mjs +64 -0
  185. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
  186. package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
  187. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
  188. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +29 -9
  189. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-structure.mjs +1 -1
  190. package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-text.mjs +1 -27
  191. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +2 -2
  192. package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +182 -0
  193. package/packages/omo-codex/plugin/skills/ulw-research/scripts/lexicon.mjs +47 -0
  194. package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +193 -0
  195. package/packages/omo-codex/plugin/skills/ulw-research/scripts/meaning-check.mjs +196 -0
  196. package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +226 -0
  197. package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +8 -8
  198. package/packages/omo-codex/plugin/skills/ulw-research/scripts/particles-ko.mjs +108 -0
  199. package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +184 -0
  200. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +115 -0
  201. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-diff.mjs +31 -0
  202. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-project.mjs +125 -0
  203. package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-rules.mjs +170 -0
  204. package/packages/omo-codex/plugin/skills/ulw-research/scripts/rational.mjs +139 -0
  205. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +21 -10
  206. package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +15 -0
  207. package/packages/omo-codex/plugin/skills/ulw-research/scripts/sentence-split.mjs +77 -0
  208. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +140 -0
  209. package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
  210. package/packages/omo-codex/plugin/skills/ulw-research/scripts/text-normalize.mjs +82 -0
  211. package/packages/omo-codex/plugin/skills/ulw-research/scripts/text-similarity.mjs +28 -0
  212. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
  213. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-errors.mjs +23 -0
  214. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
  215. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
  216. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-precision.mjs +44 -0
  217. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
  218. package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules.mjs +173 -0
  219. package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +105 -0
  220. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +71 -64
  221. package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +4 -1
  222. package/packages/omo-codex/plugin/test/sync-skills-codex-compatibility.test.mjs +19 -0
  223. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +12 -8
  224. package/packages/omo-codex/scripts/install-dist/install-local.mjs +169 -30
  225. package/packages/shared-skills/skills/ast-grep/AGENTS.md +1 -1
  226. package/packages/shared-skills/skills/browser/SKILL.md +21 -22
  227. package/packages/shared-skills/skills/browser/references/commands.md +2 -9
  228. package/packages/shared-skills/skills/browser/references/install.md +1 -2
  229. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  230. package/packages/shared-skills/skills/browser/scripts/browser-doctor.mjs +2 -2
  231. package/packages/shared-skills/skills/browser/scripts/browser-engine-guard.mjs +63 -487
  232. package/packages/shared-skills/skills/browser/scripts/browser-install.mjs +1 -1
  233. package/packages/shared-skills/skills/browser/scripts/omowright.mjs +1 -10
  234. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +2 -2
  235. package/packages/shared-skills/skills/frontend/SKILL.md +79 -118
  236. package/packages/shared-skills/skills/frontend/references/design/README.md +75 -224
  237. package/packages/shared-skills/skills/frontend/references/design/stylegallery.md +32 -27
  238. package/packages/shared-skills/skills/frontend/references/perfection/README.md +2 -2
  239. package/packages/shared-skills/skills/frontend/scripts/perfection/check-accent-stripes.ts +8 -24
  240. package/packages/shared-skills/skills/frontend/scripts/perfection/check-draft-guard.test.ts +101 -0
  241. package/packages/shared-skills/skills/frontend/scripts/perfection/check-draft-guard.ts +40 -0
  242. package/packages/shared-skills/skills/frontend/scripts/perfection/collect-files.test.ts +50 -0
  243. package/packages/shared-skills/skills/frontend/scripts/perfection/collect-files.ts +53 -0
  244. package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/findings.ts +16 -0
  245. package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/lint.ts +38 -0
  246. package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/rules.ts +69 -0
  247. package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/text-nodes.ts +121 -0
  248. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/attr-lookalikes.tsx +10 -0
  249. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/bound-values.tsx +12 -0
  250. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/const-generics.tsx +3 -0
  251. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/generics.tsx +7 -0
  252. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/measured-stats.html +6 -0
  253. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/plain-copy.html +9 -0
  254. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/state-copy.html +11 -0
  255. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/attribute-edges.html +7 -0
  256. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/components.tsx +16 -0
  257. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/decorative-badge.vue +7 -0
  258. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/emoji-icons.tsx +9 -0
  259. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/jsx-edges.tsx +1 -0
  260. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.html +6 -0
  261. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.tsx +8 -0
  262. package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/vanity-stats.html +5 -0
  263. package/packages/shared-skills/skills/programming/SKILL.md +10 -7
  264. package/packages/shared-skills/skills/programming/references/typescript/README.md +10 -7
  265. package/packages/shared-skills/skills/programming/references/typescript/bootstrap.md +9 -23
  266. package/packages/shared-skills/skills/programming/references/typescript/oxc-lint.md +130 -0
  267. package/packages/shared-skills/skills/programming/references/typescript/tsconfig-strict.md +6 -62
  268. package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.ts +12 -12
  269. package/packages/shared-skills/skills/programming/scripts/typescript/new-project.test.ts +162 -0
  270. package/packages/shared-skills/skills/programming/scripts/typescript/new-project.ts +20 -81
  271. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/.oxfmtrc.json +9 -0
  272. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/.oxlintrc.json +46 -0
  273. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/LICENSE +21 -0
  274. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/index.js +10 -0
  275. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/no-em-dash-copy.js +28 -0
  276. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/require-suppression-reason.js +62 -0
  277. package/packages/shared-skills/skills/programming/scripts/typescript/oxc/tsconfig.json +29 -0
  278. package/packages/shared-skills/skills/refactor/SKILL.md +13 -13
  279. package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +1 -1
  280. package/packages/shared-skills/skills/ulw-execute/SKILL.md +1 -1
  281. package/packages/shared-skills/skills/ulw-research/AGENTS.md +21 -3
  282. package/packages/shared-skills/skills/ulw-research/SKILL.md +37 -12
  283. package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +77 -16
  284. package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +134 -0
  285. package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +95 -0
  286. package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
  287. package/packages/shared-skills/skills/ulw-research/references/report-gates.md +68 -3
  288. package/packages/shared-skills/skills/ulw-research/references/report-visualization.md +2 -0
  289. package/packages/shared-skills/skills/ulw-research/references/unit-rules.md +157 -0
  290. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
  291. package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +193 -0
  292. package/packages/shared-skills/skills/ulw-research/scripts/claims-index.mjs +125 -0
  293. package/packages/shared-skills/skills/ulw-research/scripts/claims-index.test.ts +149 -0
  294. package/packages/shared-skills/skills/ulw-research/scripts/cli-support.mjs +19 -5
  295. package/packages/shared-skills/skills/ulw-research/scripts/cli-support.test.ts +13 -2
  296. package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +89 -3
  297. package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +100 -12
  298. package/packages/shared-skills/skills/ulw-research/scripts/copy-returns.test.ts +183 -0
  299. package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +245 -0
  300. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
  301. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
  302. package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +239 -0
  303. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +66 -0
  304. package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +375 -0
  305. package/packages/shared-skills/skills/ulw-research/scripts/fact-expr.mjs +108 -0
  306. package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +194 -0
  307. package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.test.ts +221 -0
  308. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-grammar.test.ts +197 -0
  309. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +250 -0
  310. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +156 -0
  311. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +160 -0
  312. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-schema.mjs +221 -0
  313. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-schema.test.ts +43 -0
  314. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.mjs +123 -0
  315. package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.test.ts +254 -0
  316. package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +231 -0
  317. package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.test.ts +118 -0
  318. package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
  319. package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.test.ts +167 -0
  320. package/packages/shared-skills/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
  321. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +409 -0
  322. package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +388 -0
  323. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
  324. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.test.ts +81 -0
  325. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-brief.mjs +150 -0
  326. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-brief.test.ts +179 -0
  327. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
  328. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
  329. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.test.ts +117 -0
  330. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
  331. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-state.mjs +64 -0
  332. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-state.test.ts +46 -0
  333. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
  334. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.test.ts +58 -0
  335. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
  336. package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +270 -0
  337. package/packages/shared-skills/skills/ulw-research/scripts/gates-context.test.ts +64 -0
  338. package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
  339. package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.test.ts +73 -0
  340. package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +29 -9
  341. package/packages/shared-skills/skills/ulw-research/scripts/gates-structure.mjs +1 -1
  342. package/packages/shared-skills/skills/ulw-research/scripts/gates-text.mjs +1 -27
  343. package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +2 -2
  344. package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +182 -0
  345. package/packages/shared-skills/skills/ulw-research/scripts/lexicon.mjs +47 -0
  346. package/packages/shared-skills/skills/ulw-research/scripts/lexicon.test.ts +57 -0
  347. package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +193 -0
  348. package/packages/shared-skills/skills/ulw-research/scripts/md-lite.test.ts +271 -0
  349. package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.mjs +196 -0
  350. package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.test.ts +228 -0
  351. package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +226 -0
  352. package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +120 -0
  353. package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +8 -8
  354. package/packages/shared-skills/skills/ulw-research/scripts/outcome.test.ts +29 -0
  355. package/packages/shared-skills/skills/ulw-research/scripts/particles-ko.mjs +108 -0
  356. package/packages/shared-skills/skills/ulw-research/scripts/particles-ko.test.ts +86 -0
  357. package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +184 -0
  358. package/packages/shared-skills/skills/ulw-research/scripts/placeholder.test.ts +77 -0
  359. package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +115 -0
  360. package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.test.ts +363 -0
  361. package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.mjs +31 -0
  362. package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.test.ts +168 -0
  363. package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +187 -0
  364. package/packages/shared-skills/skills/ulw-research/scripts/polish-project.mjs +125 -0
  365. package/packages/shared-skills/skills/ulw-research/scripts/polish-rules.mjs +170 -0
  366. package/packages/shared-skills/skills/ulw-research/scripts/rational.mjs +139 -0
  367. package/packages/shared-skills/skills/ulw-research/scripts/rational.test.ts +46 -0
  368. package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +21 -10
  369. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +15 -0
  370. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +458 -2
  371. package/packages/shared-skills/skills/ulw-research/scripts/sentence-split.mjs +77 -0
  372. package/packages/shared-skills/skills/ulw-research/scripts/sentence-split.test.ts +109 -0
  373. package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +140 -0
  374. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
  375. package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +113 -0
  376. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/assets/share.csv +3 -0
  377. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/claim-graph.md +13 -0
  378. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/gen-legacy-dev.mjs +89 -0
  379. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-basic.json +223 -0
  380. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-legacy-dev.json +859 -0
  381. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-verify.json +420 -0
  382. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-bad.html +19 -0
  383. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-good.html +39 -0
  384. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/acronyms.json +3 -0
  385. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/meta.json +9 -0
  386. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/sentences.jsonl +7 -0
  387. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/text.txt +7 -0
  388. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4.html +19 -0
  389. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/acronyms.json +1 -0
  390. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/meta.json +9 -0
  391. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/sentences.jsonl +1 -0
  392. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/text.txt +1 -0
  393. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S9.txt +1 -0
  394. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity-brief-locked.md +17 -0
  395. package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity-brief-pending.md +17 -0
  396. package/packages/shared-skills/skills/ulw-research/scripts/text-normalize.mjs +82 -0
  397. package/packages/shared-skills/skills/ulw-research/scripts/text-normalize.test.ts +73 -0
  398. package/packages/shared-skills/skills/ulw-research/scripts/text-similarity.mjs +28 -0
  399. package/packages/shared-skills/skills/ulw-research/scripts/text-similarity.test.ts +38 -0
  400. package/packages/shared-skills/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
  401. package/packages/shared-skills/skills/ulw-research/scripts/unit-errors.mjs +23 -0
  402. package/packages/shared-skills/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
  403. package/packages/shared-skills/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
  404. package/packages/shared-skills/skills/ulw-research/scripts/unit-precision.mjs +44 -0
  405. package/packages/shared-skills/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
  406. package/packages/shared-skills/skills/ulw-research/scripts/unit-rules.mjs +173 -0
  407. package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +105 -0
  408. package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +83 -0
  409. package/packages/shared-skills/skills/visual-qa/SKILL.md +70 -63
  410. package/packages/shared-skills/skills/visual-qa/scripts/skill-doc-fields.test.ts +34 -0
  411. package/script/qa/web-terminal-visual-qa.mjs +6 -2
  412. package/script/qa/xterm-live-terminal.mjs +26 -3
@@ -0,0 +1,187 @@
1
+ // fact-pairs.mjs - (source, report sentence) pairs for the meaning verifier.
2
+ //
3
+ // extractPairs walks the prose sentences of a filled HTML report and emits, in document order:
4
+ // a quote pair per distinct ledger quote cited by a findings sentence (facts sharing one quote
5
+ // share one pair), a claim pair per sentence bound only by data-claim (the statement comes from
6
+ // the claim index), and a translation pair per data-translates span. force_strong routes a pair
7
+ // to the strong reader when the lexicon raised name_outside_quote or scope_widened_warn, when the
8
+ // hedge-position heuristic fired, or when the source hedges and the sentence's last six tokens do
9
+ // not. No model, no IO: the readers run as separate lanes. Plain Node ESM; no Hangul here.
10
+
11
+ import { isExemptKind, proseSentences } from "./fidelity-binding.mjs"
12
+ import { isKoreanTerm, lexicalPattern, lexiconChecks } from "./fidelity-lexicon.mjs"
13
+ import { sentenceKey } from "./fidelity-state.mjs"
14
+ import { buildContext } from "./gates-static.mjs"
15
+ import { parseHtml, proseText } from "./html-lite.mjs"
16
+ import { HEDGES } from "./lexicon.mjs"
17
+ import { normalizeText } from "./text-normalize.mjs"
18
+
19
+ /** Every pair kind the producers below emit; consumers count and schema pairs by this list. */
20
+ export const PAIR_KINDS = Object.freeze(["quote", "claim", "translation"])
21
+ const TAIL = 6
22
+
23
+ const lang = (text) => (isKoreanTerm(text) ? "ko" : "en")
24
+ /** Sentence text with its fact spans cut out, so a Korean value does not make an English sentence Korean. */
25
+ const outsideFacts = (sentence) => sentence.facts.reduceRight((text, fact) => text.slice(0, fact.start - sentence.start) + text.slice(fact.end - sentence.start), sentence.text)
26
+
27
+ /** Latin hedges match whole words ("exact"); Korean hedges match as substrings, one syllable as a token prefix ("stem"). */
28
+ const HEDGE_RES = HEDGES.map((term) => lexicalPattern(term, isKoreanTerm(term) ? "stem" : "exact"))
29
+
30
+ /** Hedge terms that are also month names; capitalized and followed by a date number, they are months. */
31
+ const MONTH_WORDS = new Set(["may"])
32
+
33
+ /** A capitalized Latin match after the first word is a name or a month ("in May"), not a hedge. */
34
+ const isHedgeAt = (text, match) => {
35
+ if (!/^\p{Lu}/u.test(match[0]) || isKoreanTerm(match[0])) return true
36
+ if (match.index !== 0) return false
37
+ // At the start of a text a capital is only sentence case, so the word still hedges - unless it is a
38
+ // month name followed by a date number ("May 2024 sales rose.").
39
+ return !(MONTH_WORDS.has(match[0].toLowerCase()) && /^\s+\p{Nd}/u.test(text.slice(match.index + match[0].length)))
40
+ }
41
+
42
+ export const hasHedge = (text) => {
43
+ const normal = normalizeText(text)
44
+ return HEDGE_RES.some((re) => [...normal.matchAll(re)].some((match) => isHedgeAt(normal, match)))
45
+ }
46
+
47
+ /** The source hedges and none of the sentence's last six tokens carries a hedge. */
48
+ function tailDropsHedge(source, sentence) {
49
+ if (!hasHedge(source)) return false
50
+ return !hasHedge(normalizeText(sentence).split(/\s+/u).filter(Boolean).slice(-TAIL).join(" "))
51
+ }
52
+
53
+ function slugOf(scope) {
54
+ const heading = scope.section?.heading
55
+ if (!heading) return ""
56
+ return (heading.attrs.id ?? proseText(heading))
57
+ .toLowerCase()
58
+ .replace(/[^\p{L}\p{N}]+/gu, "-")
59
+ .replace(/^-+|-+$/gu, "")
60
+ }
61
+
62
+ function claimMap(claims) {
63
+ const list = Array.isArray(claims) ? claims : (claims?.claims ?? [])
64
+ return new Map(list.map((claim) => [claim.claim_id, claim]))
65
+ }
66
+
67
+ const idsOf = (attr) => String(attr ?? "").split(/[\s,]+/u).filter(Boolean)
68
+
69
+ /** Claim ids that bind a sentence: on the block or an ancestor, or on an inner element whose text the sentence holds. */
70
+ function claimIdsFor(ctx, block, sentence) {
71
+ const { closest, descendants } = ctx.util
72
+ const up = block.owner.attrs["data-claim"] !== undefined ? block.owner : closest(block.owner, (node) => node.attrs?.["data-claim"] !== undefined)
73
+ const ids = up ? idsOf(up.attrs["data-claim"]) : []
74
+ const text = normalizeText(sentence.text)
75
+ for (const node of descendants(block.owner)) {
76
+ if (node.attrs?.["data-claim"] === undefined) continue
77
+ const inner = normalizeText(proseText(node))
78
+ if (inner !== "" && text.includes(inner)) ids.push(...idsOf(node.attrs["data-claim"]))
79
+ }
80
+ return [...new Set(ids)]
81
+ }
82
+
83
+ function exempt(ctx, node) {
84
+ return isExemptKind(node.attrs["data-kind"]) || Boolean(ctx.util.closest(node, (cur) => isExemptKind(cur.attrs?.["data-kind"])))
85
+ }
86
+
87
+ /** Lexicon routing for every fact span of one quote group in one sentence. */
88
+ function lexiconRouted(group, sentence) {
89
+ const probes = group.facts.map((fact, index) => ({
90
+ pair_id: String(index),
91
+ quote: group.quote,
92
+ sentence: sentence.text,
93
+ span: [fact.start - sentence.start, fact.end - sentence.start],
94
+ qualifier: group.row.kind === "quote" ? null : (group.rows.get(fact.id)?.qualifier ?? null),
95
+ }))
96
+ return lexiconChecks(probes).forceStrong.length > 0
97
+ }
98
+
99
+ function quotePairs(sentence, rows, filled, slug, reportLang) {
100
+ const groups = new Map()
101
+ for (const fact of sentence.facts) {
102
+ const row = rows.get(fact.id)
103
+ if (!row?.quote || (filled && !filled.has(fact.id))) continue
104
+ const group = groups.get(row.quote) ?? { quote: row.quote, row, rows, facts: [], ids: [] }
105
+ group.facts.push(fact)
106
+ if (!group.ids.includes(fact.id)) group.ids.push(fact.id)
107
+ groups.set(row.quote, group)
108
+ }
109
+ const allIds = [...new Set(sentence.facts.map((fact) => fact.id))]
110
+ return [...groups.values()].map((group) => ({
111
+ kind: "quote",
112
+ sentence_key: sentenceKey(slug, allIds),
113
+ fact_ids: [...group.ids].sort(),
114
+ quote: group.quote,
115
+ sentence: sentence.text,
116
+ source_lang: lang(group.quote),
117
+ report_lang: reportLang ?? lang(outsideFacts(sentence)),
118
+ force_strong: lexiconRouted(group, sentence) || tailDropsHedge(group.quote, sentence.text),
119
+ }))
120
+ }
121
+
122
+ function claimPairs(ctx, block, sentence, byClaim, slug, reportLang) {
123
+ if (sentence.facts.length > 0 || exempt(ctx, block.owner)) return []
124
+ return claimIdsFor(ctx, block, sentence)
125
+ .map((id) => byClaim.get(id))
126
+ .filter(Boolean)
127
+ .map((claim) => ({
128
+ kind: "claim",
129
+ sentence_key: sentenceKey(slug, [claim.claim_id]),
130
+ claim_id: claim.claim_id,
131
+ statement: claim.statement,
132
+ sentence: sentence.text,
133
+ source_lang: lang(claim.statement),
134
+ report_lang: reportLang ?? lang(sentence.text),
135
+ force_strong: lexiconChecks([{ pair_id: "c", quote: claim.statement, sentence: sentence.text }]).forceStrong.length > 0 || tailDropsHedge(claim.statement, sentence.text),
136
+ }))
137
+ }
138
+
139
+ function translationPair(node, rows, filled, slug, reportLang) {
140
+ const id = node.attrs["data-translates"]
141
+ const row = rows.get(id)
142
+ const text = proseText(node)
143
+ if (!row?.quote || text === "" || (filled && !filled.has(id))) return null
144
+ return {
145
+ kind: "translation",
146
+ sentence_key: sentenceKey(slug, [id]),
147
+ fact_ids: [id],
148
+ quote: row.quote,
149
+ sentence: text,
150
+ source_lang: lang(row.quote),
151
+ report_lang: reportLang ?? lang(text),
152
+ force_strong: tailDropsHedge(row.quote, text),
153
+ }
154
+ }
155
+
156
+ /**
157
+ * @param {string} html the filled report
158
+ * @param {{ rows: object[] }} ledger
159
+ * @param {{ claims: object[] } | object[] | null} claims indexClaims(...) output or its claims array
160
+ * @param {{ fact_id: string }[]} [fillMap] fact fill's map; when given, only facts it filled are paired
161
+ * @returns {object[]} pairs with pair_id P1, P2, ... in document order
162
+ */
163
+ export function extractPairs(html, ledger, claims, fillMap) {
164
+ const ctx = buildContext(parseHtml(html), {})
165
+ const rows = new Map((ledger?.rows ?? []).map((row) => [row.fact_id, row]))
166
+ const filled = Array.isArray(fillMap) ? new Set(fillMap.map((entry) => entry.fact_id)) : null
167
+ const byClaim = claimMap(claims)
168
+ const htmlLang = ctx.all.find((node) => node.tag === "html")?.attrs.lang
169
+ const reportLang = htmlLang ? htmlLang.toLowerCase().split("-")[0] : null
170
+ const out = []
171
+ const seenSpans = new Set()
172
+ for (const block of proseSentences(ctx)) {
173
+ const findings = Boolean(block.scope.section) && block.kind === null
174
+ if (!findings) continue
175
+ const slug = slugOf(block.scope)
176
+ for (const sentence of block.sentences) {
177
+ out.push(...quotePairs(sentence, rows, filled, slug, reportLang), ...claimPairs(ctx, block, sentence, byClaim, slug, reportLang))
178
+ }
179
+ for (const node of ctx.util.descendants(block.owner)) {
180
+ if (node.attrs?.["data-translates"] === undefined || seenSpans.has(node)) continue
181
+ seenSpans.add(node)
182
+ const pair = translationPair(node, rows, filled, slug, reportLang)
183
+ if (pair) out.push(pair)
184
+ }
185
+ }
186
+ return out.map((pair, index) => ({ pair_id: `P${index + 1}`, ...pair }))
187
+ }
@@ -0,0 +1,89 @@
1
+ // Iterative postorder verifier: one snapshot and one arithmetic evaluation per reachable node.
2
+ import { compute } from "./fact-derive.mjs"
3
+ import { parseExpr } from "./fact-expr.mjs"
4
+ import { isLegacyDerived, kindRuleErrors, readFlaws, readRow } from "./fact-ledger-kinds.mjs"
5
+ import { rowErrors } from "./fact-ledger-schema.mjs"
6
+ import { refuse } from "./unit-algebra.mjs"
7
+ import { refusal } from "./unit-errors.mjs"
8
+ import { object, sameDeclared } from "./unit-metadata.mjs"
9
+ import { expectedUnit, malformed, ruleOf, unitMismatch } from "./unit-rules.mjs"
10
+
11
+ const derived = (row) => row.kind === "derived" || row.kind === "converted"
12
+ const fail = (detail, shape, expected) => ({ ok: false, detail, shape, expected })
13
+ function refuseLegacy(row) {
14
+ if (isLegacyDerived(row)) refuse(`${row.fact_id} was written before precision, compatibility, and context existed; regenerate it with derive`, undefined, "legacy derived row", "a derived row regenerated by derive")
15
+ }
16
+
17
+ function verifyRow(row, rows) {
18
+ const rule = ruleOf(row.operation)
19
+ const ast = rule.expression ? parseExpr(row.formula) : null
20
+ const context = { ...row.context, rationale: row.rationale }
21
+ const unit = expectedUnit(row.operation, rows, { ast, context })
22
+ const expected = { unit, ...compute(row.operation, rows, unit, ast, context, row.formula), derived_from: rows.map((r) => r.fact_id) }
23
+ if (row.unit !== unit) return fail(`${row.fact_id}: ${unitMismatch(row.operation, rows, row.unit, unit)}`, "result_unit", expected)
24
+ for (const field of Object.keys(expected)) {
25
+ if (!sameDeclared(row[field], expected[field])) return fail(`${row.fact_id}: stored ${field} differs from the recomputed result`, `${field}_mismatch`, expected)
26
+ }
27
+ return { ok: true, expected }
28
+ }
29
+
30
+ export function verifyGraph(ledger, roots, requireActive = false, override = null) {
31
+ try {
32
+ if (!object(ledger) || !Array.isArray(ledger.rows)) refuse("ledger rows must be an array")
33
+ const byId = new Map()
34
+ for (const r of ledger.rows) {
35
+ if (!object(r) || typeof r.fact_id !== "string" || byId.has(r.fact_id)) refuse("ledger rows need distinct fact ids", undefined, "identity", "distinct fact ids")
36
+ byId.set(r.fact_id, r)
37
+ }
38
+ if (override) byId.set(override.fact_id, override)
39
+ const states = new Map()
40
+ const snapshots = new Map()
41
+ const results = new Map()
42
+ const stack = roots.map((id) => ({ id, finish: false }))
43
+ while (stack.length) {
44
+ const { id, finish } = stack.pop()
45
+ if (states.get(id) === "done") continue
46
+ if (!finish) {
47
+ if (states.get(id) === "open") refuse(`${id}: operands form a cycle`, undefined, "cycle", "an acyclic dependency graph")
48
+ const original = byId.get(id)
49
+ if (!original) refuse(`no ledger row ${id}`, undefined, "missing_operand", "an existing fact id")
50
+ const row = { ...readRow(original) }
51
+ snapshots.set(id, row)
52
+ if (requireActive && row.status !== "active") refuse(`${id} is ${row.status}`, undefined, "inactive_dependency", "active dependencies at every depth")
53
+ const [flaw] = readFlaws(row)
54
+ if (flaw) refuse(`${id}: ${flaw.detail}`, undefined, flaw.shape, flaw.expected)
55
+ if (derived(row)) {
56
+ refuseLegacy(row)
57
+ const shape = malformed(row)
58
+ if (shape) refuse(`${id}: ${shape}`, undefined, "malformed_row", "the operation's registry row shape")
59
+ }
60
+ const errors = derived(row) ? kindRuleErrors(row, id) : rowErrors(row)
61
+ if (errors.length) refuse(errors.join("; "), undefined, "row_schema", "a canonical row matching its kind and operation")
62
+ if (row.exact === null || row.value === null) refuse(`${id} has no exact value to derive from`)
63
+ states.set(id, "open")
64
+ stack.push({ id, finish: true })
65
+ if (derived(row)) for (const dependency of row.operands) stack.push({ id: dependency, finish: false })
66
+ } else {
67
+ const row = snapshots.get(id)
68
+ if (derived(row)) {
69
+ const result = verifyRow(row, row.operands.map((ref) => snapshots.get(ref)))
70
+ if (!result.ok) return result
71
+ const errors = rowErrors(row)
72
+ if (errors.length) refuse(errors.join("; "), undefined, "row_schema", "a complete canonical row")
73
+ results.set(id, result)
74
+ }
75
+ states.set(id, "done")
76
+ }
77
+ }
78
+ return results.get(roots[0]) ?? { ok: true }
79
+ } catch (error) { return { ok: false, ...refusal(error) } }
80
+ }
81
+
82
+ export function recompute(ledger, row) {
83
+ try {
84
+ refuseLegacy(row)
85
+ const shape = malformed(row)
86
+ if (shape) refuse(shape, undefined, "malformed_row", "a valid derived or converted row")
87
+ return verifyGraph(ledger, [row.fact_id], false, row)
88
+ } catch (error) { return { ok: false, ...refusal(error) } }
89
+ }
@@ -0,0 +1,130 @@
1
+ // fidelity-binding.mjs - sentence binding and claim-status checks of the fidelity gate.
2
+ //
3
+ // proseSentences walks every leaf prose element (p, li) outside figures, tables and page chrome,
4
+ // keeps the offsets of each data-fact element inside its text, and splits it into sentences, so
5
+ // every check sees which ledger rows a sentence cites. bindingChecks raises unbound_sentence (a
6
+ // findings sentence with no fact, placeholder, citation, or data-claim), claim_status_invalid (a
7
+ // data-claim id that is unknown, refuted or unresolved outside a Gaps or Contradictions section,
8
+ // or high-risk and missing from verified-claims), and conflicting_readings (two active rows with
9
+ // one value and unit but different periods or qualifiers cited in one sentence). Plain Node ESM.
10
+
11
+ import { proseText } from "./html-lite.mjs"
12
+ import { fromDecimal, eq } from "./rational.mjs"
13
+ import { splitSentences } from "./sentence-split.mjs"
14
+ import { normalizeUnit } from "./units.mjs"
15
+
16
+ const SKIP = new Set(["code", "pre", "kbd", "samp", "script", "style", "svg", "template", "noscript"])
17
+ const CONTAINERS = new Set(["figure", "table", "nav", "header", "footer"])
18
+ /** data-kind values whose sentences need no binding (opinion and recommendation blocks). */
19
+ export const EXEMPT_KINDS = Object.freeze(["opinion", "recommendation"])
20
+ export const isExemptKind = (kind) => EXEMPT_KINDS.includes(kind)
21
+ const DECIMAL = /^-?\d+(?:\.\d+)?$/
22
+ /** Gaps and Contradictions headings (English, and the Korean gap/limit/contradiction/conflict words). */
23
+ const GAPS_HEADING = /\b(?:gaps?|contradictions?|unresolved)\b|\uACF5\uBC31|\uD55C\uACC4|\uBAA8\uC21C|\uC0C1\uCDA9/i
24
+
25
+ /** Canonical unit token, or the raw unit without spaces, lowercased; "" for none. */
26
+ export function unitKey(unit) {
27
+ if (unit === null || unit === undefined) return ""
28
+ return normalizeUnit(String(unit)) ?? String(unit).replace(/\s+/g, "").toLowerCase()
29
+ }
30
+
31
+ /** Exact decimal equality; false when either side is not a plain decimal. */
32
+ export function sameValue(a, b) {
33
+ const [x, y] = [String(a ?? "").replace(/,/g, ""), String(b ?? "").replace(/,/g, "")]
34
+ return DECIMAL.test(x) && DECIMAL.test(y) && eq(fromDecimal(x), fromDecimal(y))
35
+ }
36
+
37
+ function serialize(node, out) {
38
+ for (const child of node.children) {
39
+ if (child.tag === "#text") out.text += child.text
40
+ else if (child.tag === "br") out.text += " "
41
+ else if (SKIP.has(child.tag)) continue
42
+ else if (child.attrs["data-fact"] !== undefined) {
43
+ const text = proseText(child)
44
+ out.facts.push({ id: child.attrs["data-fact"], start: out.text.length, end: out.text.length + text.length, text, node: child })
45
+ out.text += text
46
+ } else serialize(child, out)
47
+ }
48
+ return out
49
+ }
50
+
51
+ /**
52
+ * Leaf prose elements with their sentences and the facts each sentence cites.
53
+ * @returns {{ owner: object, scope: object, kind: string|null, sentences: { text: string, start: number, end: number, facts: object[] }[] }[]}
54
+ */
55
+ export function proseSentences(ctx) {
56
+ const { closest, descendants } = ctx.util
57
+ const blocks = []
58
+ for (const { node, scope } of ctx.elements) {
59
+ if (node.tag !== "p" && node.tag !== "li") continue
60
+ if (node.tag === "li" && descendants(node).some((child) => child.tag === "p" || child.tag === "li")) continue
61
+ if (closest(node, (cur) => CONTAINERS.has(cur.tag))) continue
62
+ const { text, facts } = serialize(node, { text: "", facts: [] })
63
+ const sentences = splitSentences(text).map((s) => ({ ...s, facts: facts.filter((f) => f.start < s.end && f.end > s.start) }))
64
+ blocks.push({ owner: node, scope, kind: ctx.kindOf(scope), sentences })
65
+ }
66
+ return blocks
67
+ }
68
+
69
+ function claimList(claims) {
70
+ const list = Array.isArray(claims) ? claims : (claims?.claims ?? [])
71
+ return new Map(list.map((claim) => [claim.claim_id, claim]))
72
+ }
73
+
74
+ const selfOrUp = (ctx, node, test) => test(node) || Boolean(ctx.util.closest(node, test))
75
+
76
+ function claimDefects(ctx, claims) {
77
+ const byId = claimList(claims)
78
+ const out = []
79
+ for (const { node, scope } of ctx.elements) {
80
+ const attr = node.attrs["data-claim"]
81
+ if (attr === undefined) continue
82
+ const heading = scope.section?.heading
83
+ const inGaps = Boolean(heading) && GAPS_HEADING.test(proseText(heading))
84
+ for (const id of attr.split(/[\s,]+/).filter(Boolean)) {
85
+ const claim = byId.get(id)
86
+ let why = null
87
+ if (!claim) why = `data-claim ${id} is not in the claim index`
88
+ else if (/refuted|unresolved/i.test(claim.status) && !inGaps) why = `claim ${id} is ${claim.status} but asserted outside Gaps or Contradictions`
89
+ else if (!inGaps && /high/i.test(claim.risk) && !claim.in_verified) why = `high-risk claim ${id} is not in verified-claims`
90
+ if (why) out.push(ctx.defect("claim_status_invalid", node, why))
91
+ }
92
+ }
93
+ return out
94
+ }
95
+
96
+ function conflict(rows, facts) {
97
+ const cited = [...new Set(facts.map((f) => f.id))].map((id) => rows.get(id)).filter((row) => row && row.status === "active" && row.kind !== "quote")
98
+ for (let i = 0; i < cited.length; i++) {
99
+ for (let k = i + 1; k < cited.length; k++) {
100
+ const [a, b] = [cited[i], cited[k]]
101
+ if (!sameValue(a.value, b.value) || unitKey(a.unit) !== unitKey(b.unit)) continue
102
+ if (a.valid_at !== b.valid_at || a.qualifier !== b.qualifier) return [a, b]
103
+ }
104
+ }
105
+ return null
106
+ }
107
+
108
+ /**
109
+ * @param {object} ctx buildContext(...) from gates-static.mjs
110
+ * @param {{ claims: object[] } | object[] | null} claims indexClaims(...) output or its claims array
111
+ * @param {{ rows: object[] } | null} ledger
112
+ */
113
+ export function bindingChecks(ctx, claims, ledger) {
114
+ const rows = new Map((ledger?.rows ?? []).map((row) => [row.fact_id, row]))
115
+ const out = []
116
+ for (const block of proseSentences(ctx)) {
117
+ const findings = Boolean(block.scope.section) && block.kind === null
118
+ const exempt = selfOrUp(ctx, block.owner, (cur) => isExemptKind(cur.attrs["data-kind"]) || cur.attrs["data-claim"] !== undefined)
119
+ for (const sentence of block.sentences) {
120
+ const bound = sentence.facts.length > 0 || ctx.util.citationMarker.test(sentence.text) || sentence.text.includes("{{")
121
+ if (findings && !exempt && !bound) out.push(ctx.defect("unbound_sentence", block.owner, `no fact, citation, or data-claim binds: "${sentence.text.slice(0, 80)}"`))
122
+ const pair = conflict(rows, sentence.facts)
123
+ if (pair) {
124
+ const [a, b] = pair
125
+ out.push(ctx.defect("conflicting_readings", block.owner, `${a.fact_id} (${a.valid_at ?? "no period"}) and ${b.fact_id} (${b.valid_at ?? "no period"}) share ${a.value} ${a.unit ?? ""} in one sentence`))
126
+ }
127
+ }
128
+ }
129
+ return [...out, ...claimDefects(ctx, claims)]
130
+ }
@@ -0,0 +1,150 @@
1
+ // fidelity-brief.mjs - parse the `## Fidelity` block of brief.md and decide whether it is locked.
2
+ //
3
+ // Grammar (fable-interview.md section 3, "Parser contract"): the block starts at a line
4
+ // `## Fidelity` and ends at the next `## ` heading. It holds the seven field lines below, in any
5
+ // order, each `<field>: <value> (answered_by: user|default|request|pending)`, plus an optional
6
+ // `asked: <field>, <field>` history line. Values carry no parentheses, and `answered_by: pending`
7
+ // is legal only with the value `pending`. Trailing whitespace on a line is ignored; a second
8
+ // `## Fidelity` block is a parse error. This module parses; it never interprets a value, which
9
+ // is each consumer's job. Plain Node ESM, node: builtins only.
10
+
11
+ import { readFileSync } from "node:fs"
12
+
13
+ export const FIDELITY_FIELDS = Object.freeze([
14
+ "as of",
15
+ "meaning check",
16
+ "source floor",
17
+ "derived numbers",
18
+ "languages",
19
+ "identifier classes",
20
+ "fallback",
21
+ ])
22
+ export const FIDELITY_ANSWERED_BY = Object.freeze(["user", "default", "request", "pending"])
23
+ const LOCKED_ANSWERED_BY = new Set(["user", "default", "request"])
24
+
25
+ export const FIDELITY_INCOMPLETE = "fidelity_interview_incomplete"
26
+ const INCOMPLETE_HINT =
27
+ "A fidelity field is pending or missing in brief.md; answer the open question or record its default, then rerun."
28
+
29
+ const BLOCK_HEADING = /^## Fidelity\s*$/
30
+ const NEXT_HEADING = /^## /
31
+ const FIELD_LINE = /^([a-z][a-z ]*[a-z]): ([^()]+) \(answered_by: (user|default|request|pending)\)$/
32
+ const ASKED_LINE = /^asked:(.*)$/
33
+
34
+ function blockLines(markdown) {
35
+ const lines = String(markdown)
36
+ .split(/\r?\n/)
37
+ .map((line) => line.trimEnd())
38
+ const headings = lines.flatMap((line, index) => (BLOCK_HEADING.test(line) ? [index] : []))
39
+ if (headings.length === 0) return null
40
+ const [start, ...duplicates] = headings
41
+ const body = []
42
+ for (let index = start + 1; index < lines.length; index += 1) {
43
+ if (NEXT_HEADING.test(lines[index])) break
44
+ body.push({ text: lines[index], line: index + 1 })
45
+ }
46
+ return { body, duplicates: duplicates.map((index) => index + 1) }
47
+ }
48
+
49
+ function parseAsked(rest, line, text, errors) {
50
+ const names = rest
51
+ .split(",")
52
+ .map((name) => name.trim())
53
+ .filter(Boolean)
54
+ const unknown = names.filter((name) => !FIDELITY_FIELDS.includes(name))
55
+ if (unknown.length > 0) {
56
+ errors.push(`line ${line}: malformed asked line, unknown field ${unknown.join(", ")}: ${text}`)
57
+ return []
58
+ }
59
+ return names
60
+ }
61
+
62
+ function parseFieldLine({ text, line }, parsed, errors) {
63
+ const match = FIELD_LINE.exec(text)
64
+ if (!match) return errors.push(`line ${line}: malformed fidelity line: ${text}`)
65
+ const [, field, rawValue, answeredBy] = match
66
+ const value = rawValue.trim()
67
+ if (!FIDELITY_FIELDS.includes(field)) return errors.push(`line ${line}: malformed fidelity line, unknown field "${field}": ${text}`)
68
+ if (Object.hasOwn(parsed.fields, field)) return errors.push(`line ${line}: malformed fidelity line, duplicate field "${field}": ${text}`)
69
+ if ((answeredBy === "pending") !== (value === "pending")) {
70
+ return errors.push(`line ${line}: malformed fidelity line, value pending and answered_by: pending go together: ${text}`)
71
+ }
72
+ parsed.fields[field] = value
73
+ parsed.answered_by[field] = answeredBy
74
+ }
75
+
76
+ /**
77
+ * @param {string} briefMd the whole brief.md text
78
+ * @returns {{ present: boolean, fields: Record<string, string>, answered_by: Record<string, string>,
79
+ * asked: string[], missing: string[], pending: string[], errors: string[] }}
80
+ */
81
+ export function parseFidelityBlock(briefMd) {
82
+ const parsed = { present: false, fields: {}, answered_by: {}, asked: [], missing: [], pending: [], errors: [] }
83
+ const block = blockLines(briefMd)
84
+ if (block !== null) {
85
+ const { body, duplicates } = block
86
+ parsed.present = true
87
+ for (const line of duplicates) parsed.errors.push(`line ${line}: duplicate ## Fidelity block, only one is allowed`)
88
+ let sawAsked = false
89
+ for (const entry of body) {
90
+ if (entry.text.trim() === "") continue
91
+ const asked = ASKED_LINE.exec(entry.text)
92
+ if (!asked) {
93
+ parseFieldLine(entry, parsed, parsed.errors)
94
+ continue
95
+ }
96
+ if (sawAsked) parsed.errors.push(`line ${entry.line}: malformed asked line, duplicate: ${entry.text}`)
97
+ else parsed.asked = parseAsked(asked[1], entry.line, entry.text, parsed.errors)
98
+ sawAsked = true
99
+ }
100
+ }
101
+ parsed.missing = FIDELITY_FIELDS.filter((field) => !Object.hasOwn(parsed.fields, field))
102
+ parsed.pending = FIDELITY_FIELDS.filter((field) => parsed.answered_by[field] === "pending")
103
+ return parsed
104
+ }
105
+
106
+ /** Locked = no parse error and all seven fields present with answered_by user, default, or request. */
107
+ export function isLocked(parsed) {
108
+ if (!parsed || parsed.errors.length > 0) return false
109
+ return FIDELITY_FIELDS.every((field) => Object.hasOwn(parsed.fields, field) && LOCKED_ANSWERED_BY.has(parsed.answered_by[field]))
110
+ }
111
+
112
+ /** The brief's `meaning check` field turns the meaning verifier off only when it reads "off". */
113
+ export function meaningCheckEnabled(parsed) {
114
+ return !/^off\b/iu.test(parsed?.fields?.["meaning check"] ?? "")
115
+ }
116
+
117
+ function incompleteDefect(briefPath, parsed) {
118
+ const reasons = []
119
+ if (!parsed.present) reasons.push("no ## Fidelity block")
120
+ else if (parsed.missing.length > 0) reasons.push(`missing: ${parsed.missing.join(", ")}`)
121
+ if (parsed.pending.length > 0) reasons.push(`pending: ${parsed.pending.join(", ")}`)
122
+ return {
123
+ code: FIDELITY_INCOMPLETE,
124
+ severity: "blocker",
125
+ integrity: true,
126
+ selector: null,
127
+ line: null,
128
+ message: `${briefPath}: fidelity interview incomplete (${reasons.join("; ")})`,
129
+ hint: INCOMPLETE_HINT,
130
+ }
131
+ }
132
+
133
+ /**
134
+ * The lock check the reader-facing CLI stages run before writing anything.
135
+ * exitCode 0 = locked; 1 = pending, missing, or no brief (one `fidelity_interview_incomplete`
136
+ * defect); 2 = malformed block (usage error, `errors` name the lines, no defect).
137
+ * @param {string} briefPath
138
+ */
139
+ export function requireLockedBrief(briefPath) {
140
+ let markdown = ""
141
+ try {
142
+ markdown = readFileSync(briefPath, "utf8")
143
+ } catch (error) {
144
+ if (error?.code !== "ENOENT") throw error
145
+ }
146
+ const parsed = parseFidelityBlock(markdown)
147
+ if (parsed.errors.length > 0) return { locked: false, exitCode: 2, defects: [], errors: parsed.errors, parsed }
148
+ if (isLocked(parsed)) return { locked: true, exitCode: 0, defects: [], errors: [], parsed }
149
+ return { locked: false, exitCode: 1, defects: [incompleteDefect(briefPath, parsed)], errors: [], parsed }
150
+ }
@@ -0,0 +1,89 @@
1
+ // fidelity-claims.mjs - the claim-kind checks of the deterministic fidelity gate.
2
+ //
3
+ // A mark (the data-claim-kind attribute) binds per cited fact: a data-fact element takes the kind
4
+ // of its nearest mark (its own, else the closest marked ancestor), and only that kind is checked
5
+ // against its ledger row (claim-kinds.mjs CLAIM_KINDS); a row that fails is the kind's defect
6
+ // (unsupported_verification for `verified`). A fact under two marks of different kinds is a
7
+ // claim_kind_mismatch, and a mark that owns no fact of its own kind (every fact under it cites
8
+ // nothing or is shadowed by another kind) is its kind's defect: shadowing never satisfies a parent.
9
+ // An unmarked fact citing a row neither fact nor quote accepts (an assumed row) is a
10
+ // claim_kind_mismatch naming the mark it needs. A sentence that matches a kind's unmarked patterns warns (minor, verification_claim_unmarked)
11
+ // unless every fact it cites is marked that kind; a sentence citing no fact warns unless its block
12
+ // is marked. No model, no network.
13
+
14
+ import { CLAIM_ATTRIBUTE, CLAIM_KINDS, claimSatisfied, unmarkedKinds, unmarkedRequires } from "./claim-kinds.mjs"
15
+ import { proseSentences } from "./fidelity-binding.mjs"
16
+ import { clip } from "./fidelity-trace.mjs"
17
+ import { proseText } from "./html-lite.mjs"
18
+
19
+ const marked = (node) => node.attrs?.[CLAIM_ATTRIBUTE] !== undefined
20
+ const kindOf = (node) => node.attrs[CLAIM_ATTRIBUTE].trim()
21
+ const isFact = (node) => node.attrs["data-fact"] !== undefined
22
+
23
+ /** The marks over a node, nearest first: its own mark, then each marked ancestor. */
24
+ function marksOver(node) {
25
+ const marks = []
26
+ for (let cur = node; cur; cur = cur.parent) if (marked(cur)) marks.push(cur)
27
+ return marks
28
+ }
29
+
30
+ const nearestKind = (node) => {
31
+ const [mark] = marksOver(node)
32
+ return mark ? kindOf(mark) : null
33
+ }
34
+
35
+ /** Whether `mark` binds `fact`: every mark from the fact up to `mark` names the same kind. */
36
+ function binds(mark, fact) {
37
+ const marks = marksOver(fact)
38
+ const at = marks.indexOf(mark)
39
+ return at >= 0 && marks.slice(0, at + 1).every((cur) => kindOf(cur) === kindOf(mark))
40
+ }
41
+
42
+ /**
43
+ * @param {object} ctx buildContext(...) from gates-static.mjs
44
+ * @param {Map<string, object>} rows ledger rows by fact_id
45
+ * @param {(row: object) => string | undefined} provenanceOf meta.json kind of the snapshot holding the row's own locator artifact
46
+ * @param {(code: string, node: object, message: string) => void} flag
47
+ */
48
+ export function claimChecks(ctx, rows, provenanceOf, flag) {
49
+ for (const node of ctx.all.filter(marked)) {
50
+ const name = kindOf(node)
51
+ const entry = CLAIM_KINDS[name]
52
+ if (!entry) {
53
+ flag("claim_kind_mismatch", node, `${CLAIM_ATTRIBUTE}="${name}" is not a claim kind (${Object.keys(CLAIM_KINDS).join(", ")})`)
54
+ continue
55
+ }
56
+ const cited = [node, ...ctx.util.descendants(node)].filter(isFact)
57
+ if (cited.some((fact) => binds(node, fact))) continue
58
+ const why = cited.length === 0 ? "cites no fact" : "cites no fact of its own (a nested mark of another kind never satisfies it)"
59
+ flag(entry.defect, node, `"${clip(proseText(node))}" is marked ${name} but ${why}`)
60
+ }
61
+ for (const fact of ctx.all.filter(isFact)) {
62
+ const [nearest, ...outer] = marksOver(fact)
63
+ const id = fact.attrs["data-fact"]
64
+ if (!nearest) {
65
+ const need = unmarkedRequires(rows.get(id))
66
+ const mark = need && `{{${CLAIM_KINDS[need].prefix}:${id}}} (${CLAIM_ATTRIBUTE}="${need}")`
67
+ if (need) flag("claim_kind_mismatch", fact, `${id} is a ${rows.get(id).kind} row cited with no mark; an unmarked numeral reads as a fact, so mark it ${mark}`)
68
+ continue
69
+ }
70
+ const name = kindOf(nearest)
71
+ const conflict = outer.find((mark) => kindOf(mark) !== name)
72
+ if (conflict) flag("claim_kind_mismatch", fact, `${id} is marked ${name} inside a ${kindOf(conflict)} mark; a fact carries one claim kind`)
73
+ const entry = CLAIM_KINDS[name]
74
+ const row = rows.get(id)
75
+ if (!entry || !row || claimSatisfied(name, row, provenanceOf(row))) continue
76
+ const source = entry.sourceKinds ? ` and not one whose own locator artifact sits in a ${entry.sourceKinds.join(" or ")} snapshot` : ""
77
+ flag(entry.defect, fact, `${row.fact_id} is marked ${name}, but it is a ${row.kind} row${source}`)
78
+ }
79
+ for (const block of proseSentences(ctx)) {
80
+ if (block.kind === "sources" || block.kind === "toc") continue
81
+ for (const sentence of block.sentences) {
82
+ for (const name of unmarkedKinds(sentence.text)) {
83
+ const covered = sentence.facts.length > 0 ? sentence.facts.every((fact) => nearestKind(fact.node) === name) : nearestKind(block.owner) === name
84
+ if (covered) continue
85
+ flag(CLAIM_KINDS[name].unmarked.code, block.owner, `"${clip(sentence.text)}" reads as a ${name} claim but cites a fact with no ${CLAIM_ATTRIBUTE}="${name}" mark`)
86
+ }
87
+ }
88
+ }
89
+ }