oh-my-opencode 5.1.45 → 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.
- package/.agents/command/publish.md +9 -9
- package/.agents/skills/publish/SKILL.md +9 -9
- package/.opencode/command/publish.md +9 -9
- package/README.de.md +0 -5
- package/README.es.md +0 -5
- package/README.fr.md +0 -5
- package/README.it.md +0 -5
- package/README.ja.md +0 -5
- package/README.ko.md +0 -5
- package/README.md +4 -5
- package/README.pt-br.md +0 -5
- package/README.ru.md +0 -6
- package/README.zh-cn.md +0 -5
- package/dist/cli/index.js +76 -156
- package/dist/cli-node/index.js +76 -156
- package/dist/config-migration/index.d.ts +2 -2
- package/dist/config-migration/migration-plans.d.ts +0 -10
- package/dist/index.js +1166 -1203
- package/dist/skills/ast-grep/AGENTS.md +1 -1
- package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
- package/dist/skills/frontend/ATTRIBUTION.md +2 -2
- package/dist/skills/ultimate-browsing/engine/AGENTS.md +1 -1
- package/dist/skills/ulw-research/AGENTS.md +14 -2
- package/dist/skills/ulw-research/SKILL.md +37 -12
- package/dist/skills/ulw-research/references/deliverable-phase.md +73 -13
- package/dist/skills/ulw-research/references/fact-ledger-grammar.md +19 -1
- package/dist/skills/ulw-research/references/fact-ledger.md +95 -0
- package/dist/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
- package/dist/skills/ulw-research/references/report-gates.md +21 -4
- package/dist/skills/ulw-research/references/report-visualization.md +2 -0
- package/dist/skills/ulw-research/references/unit-rules.md +157 -0
- package/dist/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
- package/dist/skills/ulw-research/scripts/claim-kinds.test.ts +193 -0
- package/dist/skills/ulw-research/scripts/cli-support.mjs +19 -5
- package/dist/skills/ulw-research/scripts/cli-support.test.ts +13 -2
- package/dist/skills/ulw-research/scripts/contracts.mjs +27 -6
- package/dist/skills/ulw-research/scripts/contracts.test.ts +3 -0
- package/dist/skills/ulw-research/scripts/copy-returns.test.ts +183 -0
- package/dist/skills/ulw-research/scripts/delivery-barrier.test.ts +245 -0
- package/dist/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
- package/dist/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
- package/dist/skills/ulw-research/scripts/fact-commands.mjs +239 -0
- package/dist/skills/ulw-research/scripts/fact-derive.mjs +66 -0
- package/dist/skills/ulw-research/scripts/fact-derive.test.ts +375 -0
- package/dist/skills/ulw-research/scripts/fact-expr.mjs +108 -0
- package/dist/skills/ulw-research/scripts/fact-fill.mjs +194 -0
- package/dist/skills/ulw-research/scripts/fact-fill.test.ts +221 -0
- package/dist/skills/ulw-research/scripts/fact-ledger-grammar.test.ts +9 -2
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.mjs +127 -13
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +18 -3
- package/dist/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +160 -0
- package/dist/skills/ulw-research/scripts/fact-ledger-schema.mjs +33 -16
- package/dist/skills/ulw-research/scripts/fact-ledger.mjs +29 -9
- package/dist/skills/ulw-research/scripts/fact-ledger.test.ts +2 -0
- package/dist/skills/ulw-research/scripts/fact-locate.mjs +231 -0
- package/dist/skills/ulw-research/scripts/fact-locate.test.ts +118 -0
- package/dist/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
- package/dist/skills/ulw-research/scripts/fact-pairs.test.ts +167 -0
- package/dist/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
- package/dist/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +409 -0
- package/dist/skills/ulw-research/scripts/fact-unit-rules.test.ts +388 -0
- package/dist/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
- package/dist/skills/ulw-research/scripts/fidelity-binding.test.ts +81 -0
- package/dist/skills/ulw-research/scripts/fidelity-brief.mjs +5 -0
- package/dist/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
- package/dist/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
- package/dist/skills/ulw-research/scripts/fidelity-lexicon.test.ts +117 -0
- package/dist/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
- package/dist/skills/ulw-research/scripts/fidelity-state.mjs +2 -1
- package/dist/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
- package/dist/skills/ulw-research/scripts/fidelity-trace.test.ts +58 -0
- package/dist/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
- package/dist/skills/ulw-research/scripts/fidelity-values.test.ts +270 -0
- package/dist/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
- package/dist/skills/ulw-research/scripts/gates-fidelity.test.ts +73 -0
- package/dist/skills/ulw-research/scripts/gates-static.mjs +28 -8
- package/dist/skills/ulw-research/scripts/html-lite.mjs +2 -2
- package/dist/skills/ulw-research/scripts/html-markup.mjs +182 -0
- package/dist/skills/ulw-research/scripts/md-lite.mjs +193 -0
- package/dist/skills/ulw-research/scripts/md-lite.test.ts +271 -0
- package/dist/skills/ulw-research/scripts/meaning-check.mjs +196 -0
- package/dist/skills/ulw-research/scripts/meaning-check.test.ts +228 -0
- package/dist/skills/ulw-research/scripts/outcome.mjs +6 -5
- package/dist/skills/ulw-research/scripts/placeholder.mjs +184 -0
- package/dist/skills/ulw-research/scripts/placeholder.test.ts +77 -0
- package/dist/skills/ulw-research/scripts/polish-apply.mjs +115 -0
- package/dist/skills/ulw-research/scripts/polish-apply.test.ts +363 -0
- package/dist/skills/ulw-research/scripts/polish-diff.mjs +31 -0
- package/dist/skills/ulw-research/scripts/polish-diff.test.ts +168 -0
- package/dist/skills/ulw-research/scripts/polish-markup.test.ts +187 -0
- package/dist/skills/ulw-research/scripts/polish-project.mjs +125 -0
- package/dist/skills/ulw-research/scripts/polish-rules.mjs +170 -0
- package/dist/skills/ulw-research/scripts/report-tools-commands.mjs +19 -8
- package/dist/skills/ulw-research/scripts/report-tools.mjs +15 -0
- package/dist/skills/ulw-research/scripts/report-tools.test.ts +458 -2
- package/dist/skills/ulw-research/scripts/source-commands.mjs +140 -0
- package/dist/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
- package/dist/skills/ulw-research/scripts/source-snapshot.test.ts +113 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/assets/share.csv +3 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/gen-legacy-dev.mjs +89 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-basic.json +223 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-legacy-dev.json +859 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-verify.json +420 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/report-bad.html +19 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/report-good.html +39 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/acronyms.json +3 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/meta.json +9 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/sentences.jsonl +7 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/text.txt +7 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4.html +19 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/acronyms.json +1 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/meta.json +9 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/sentences.jsonl +1 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/text.txt +1 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S9.txt +1 -0
- package/dist/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
- package/dist/skills/ulw-research/scripts/unit-errors.mjs +23 -0
- package/dist/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
- package/dist/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
- package/dist/skills/ulw-research/scripts/unit-precision.mjs +44 -0
- package/dist/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
- package/dist/skills/ulw-research/scripts/unit-rules.mjs +173 -0
- package/dist/skills/ulw-research/scripts/units.mjs +4 -4
- package/dist/testing/module-mock-lifecycle.d.ts +1 -1
- package/dist/tui.js +32 -34
- package/package.json +13 -13
- package/packages/lsp-core/src/mcp-protocol-pin.test.ts +29 -0
- package/packages/lsp-core/src/mcp.ts +5 -2
- package/packages/lsp-daemon/dist/cli.js +3 -3
- package/packages/lsp-daemon/dist/client.js +3 -3
- package/packages/lsp-daemon/dist/index.js +3 -3
- package/packages/lsp-tools-mcp/dist/cli.js +3 -3
- package/packages/lsp-tools-mcp/dist/mcp.js +3 -3
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +22 -12
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +12 -12
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +8 -3
- package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +0 -2
- package/packages/omo-codex/plugin/skills/ast-grep/AGENTS.md +1 -1
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +2 -2
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +14 -2
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +36 -11
- package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +73 -13
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +19 -1
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +95 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +21 -4
- package/packages/omo-codex/plugin/skills/ulw-research/references/report-visualization.md +2 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/unit-rules.md +157 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/cli-support.mjs +19 -5
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +27 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +239 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +66 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-expr.mjs +108 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +194 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +127 -13
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-schema.mjs +33 -16
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger.mjs +29 -9
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +231 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-brief.mjs +5 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-state.mjs +2 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +28 -8
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +2 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +182 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +193 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/meaning-check.mjs +196 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +6 -5
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +184 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +115 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-diff.mjs +31 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-project.mjs +125 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-rules.mjs +170 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +19 -8
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +15 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +140 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-errors.mjs +23 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-precision.mjs +44 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/unit-rules.mjs +173 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +4 -4
- package/packages/omo-codex/plugin/test/sync-skills-codex-compatibility.test.mjs +19 -0
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +7 -3
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +26 -16
- package/packages/shared-skills/skills/ast-grep/AGENTS.md +1 -1
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +2 -2
- package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +1 -1
- package/packages/shared-skills/skills/ulw-research/AGENTS.md +14 -2
- package/packages/shared-skills/skills/ulw-research/SKILL.md +37 -12
- package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +73 -13
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +19 -1
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +95 -0
- package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +19 -0
- package/packages/shared-skills/skills/ulw-research/references/report-gates.md +21 -4
- package/packages/shared-skills/skills/ulw-research/references/report-visualization.md +2 -0
- package/packages/shared-skills/skills/ulw-research/references/unit-rules.md +157 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +116 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +193 -0
- package/packages/shared-skills/skills/ulw-research/scripts/cli-support.mjs +19 -5
- package/packages/shared-skills/skills/ulw-research/scripts/cli-support.test.ts +13 -2
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +27 -6
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +3 -0
- package/packages/shared-skills/skills/ulw-research/scripts/copy-returns.test.ts +183 -0
- package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +245 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +191 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +164 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +239 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +66 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +375 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-expr.mjs +108 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +194 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.test.ts +221 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-grammar.test.ts +9 -2
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +127 -13
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +18 -3
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +160 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-schema.mjs +33 -16
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.mjs +29 -9
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.test.ts +2 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +231 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.test.ts +118 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.mjs +187 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-pairs.test.ts +167 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-recompute.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +409 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +388 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.mjs +130 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-binding.test.ts +81 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-brief.mjs +5 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +145 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.test.ts +117 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +163 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-state.mjs +2 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +85 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.test.ts +58 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +229 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +270 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +113 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.test.ts +73 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +28 -8
- package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +2 -2
- package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +182 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +193 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite.test.ts +271 -0
- package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.mjs +196 -0
- package/packages/shared-skills/skills/ulw-research/scripts/meaning-check.test.ts +228 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +6 -5
- package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +184 -0
- package/packages/shared-skills/skills/ulw-research/scripts/placeholder.test.ts +77 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +115 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.test.ts +363 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.mjs +31 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-diff.test.ts +168 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +187 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-project.mjs +125 -0
- package/packages/shared-skills/skills/ulw-research/scripts/polish-rules.mjs +170 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +19 -8
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +15 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +458 -2
- package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +140 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +176 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +113 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/assets/share.csv +3 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/gen-legacy-dev.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-basic.json +223 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-legacy-dev.json +859 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/ledger-verify.json +420 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-bad.html +19 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/report-good.html +39 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/acronyms.json +3 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/meta.json +9 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/sentences.jsonl +7 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4/text.txt +7 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S4.html +19 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/acronyms.json +1 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/meta.json +9 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/sentences.jsonl +1 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S5/text.txt +1 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity/sources/S9.txt +1 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-algebra.mjs +101 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-errors.mjs +23 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-metadata.mjs +31 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-pairs.mjs +72 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-precision.mjs +44 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-rules-shapes.mjs +64 -0
- package/packages/shared-skills/skills/ulw-research/scripts/unit-rules.mjs +173 -0
- package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +4 -4
- package/dist/config-migration/dori-section-rename.d.ts +0 -3
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { spawnSync } from "node:child_process"
|
|
3
|
+
import { readFileSync } from "node:fs"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
|
|
6
|
+
import { CLAIM_ATTRIBUTE, CLAIM_KINDS, CLAIM_TABLE_BEGIN, CLAIM_TABLE_END, renderClaimKindTable, unmarkedKinds } from "./claim-kinds.mjs"
|
|
7
|
+
import { DEFECT_CODES } from "./contracts.mjs"
|
|
8
|
+
import { valueChecks } from "./fidelity-values.mjs"
|
|
9
|
+
import { buildContext } from "./gates-static.mjs"
|
|
10
|
+
import { parseHtml } from "./html-lite.mjs"
|
|
11
|
+
|
|
12
|
+
// Protects the claim-kind contract the writer (fill) and the gate share: a fact marked with a
|
|
13
|
+
// claim kind must cite a row that kind accepts, a mark binds only the facts it owns, and the doc
|
|
14
|
+
// table writers read is rendered from CLAIM_KINDS. Expectations come from ORACLE below, written by
|
|
15
|
+
// hand from the design, never from the table under test.
|
|
16
|
+
|
|
17
|
+
type Defect = { code: string; severity: string }
|
|
18
|
+
type Row = Record<string, unknown> & { fact_id: string; kind: string; source_id: string | null; raw?: string; quote?: string }
|
|
19
|
+
|
|
20
|
+
const FIX = join(import.meta.dir, "tests", "fixtures", "fidelity")
|
|
21
|
+
const BASE: { rows: Row[] } = JSON.parse(readFileSync(join(FIX, "ledger-verify.json"), "utf8"))
|
|
22
|
+
const byId = (id: string) => BASE.rows.find((row) => row.fact_id === id) as Row
|
|
23
|
+
const [F1, F7, F8, Q1] = ["F1", "F7", "F8", "Q1"].map(byId) as [Row, Row, Row, Row]
|
|
24
|
+
const plain = { render: null, operation: null, operands: [], derived_from: [], formula: null, factor: null }
|
|
25
|
+
const F9: Row = { ...F8, ...plain, fact_id: "F9", kind: "assumed", label: "growth_rate", raw: "15%", value: "15", unit: "%", exact: { num: "15", den: "1" }, rationale: "planning assumption" }
|
|
26
|
+
const F10: Row = { ...F8, ...plain, fact_id: "F10", kind: "derived", label: "share_sum", raw: "39%", value: "39", unit: "%", exact: { num: "39", den: "1" }, operation: "sum", operands: ["F5", "F6"] }
|
|
27
|
+
const F11: Row = { ...F7, fact_id: "F11", locator: null }
|
|
28
|
+
const F12: Row = { ...F7, fact_id: "F12", locator: { artifact: "missing.txt", char_start: 0, char_end: 9 } }
|
|
29
|
+
const F13: Row = { ...F7, fact_id: "F13", locator: { artifact: "../S4/text.txt", char_start: 0, char_end: 9 } }
|
|
30
|
+
const LEDGER = { ...BASE, rows: [...BASE.rows, F9, F10, F11, F12, F13] }
|
|
31
|
+
const SUBJECTS = [F1, F7, F8, F9, F10, F11, F12, F13, Q1]
|
|
32
|
+
|
|
33
|
+
/** Hand-written oracle: the ledger row kinds each claim kind accepts, and whether the cited row must be command-backed. */
|
|
34
|
+
const ORACLE: Record<string, { rows: string[]; command: boolean }> = {
|
|
35
|
+
fact: { rows: ["measured", "converted", "derived"], command: false },
|
|
36
|
+
quote: { rows: ["quote"], command: false },
|
|
37
|
+
verified: { rows: ["measured", "quote"], command: true },
|
|
38
|
+
derived: { rows: ["derived", "converted"], command: false },
|
|
39
|
+
assumed: { rows: ["assumed"], command: false },
|
|
40
|
+
}
|
|
41
|
+
/** Rows an unmarked element refuses (an unmarked numeral reads as a fact, so an assumption needs its A mark). */
|
|
42
|
+
const UNMARKED_REFUSED = new Set(["F9"])
|
|
43
|
+
/** Rows whose own locator artifact sits in a snapshot whose meta.json kind is command (S5/text.txt). */
|
|
44
|
+
const COMMAND_BACKED = new Set(["F7"])
|
|
45
|
+
const MAJOR_FOR: Record<string, string> = { fact: "claim_kind_mismatch", quote: "claim_kind_mismatch", verified: "unsupported_verification", derived: "claim_kind_mismatch", assumed: "claim_kind_mismatch" }
|
|
46
|
+
const expected = (kind: string, row: Row) => {
|
|
47
|
+
const rule = ORACLE[kind] as { rows: string[]; command: boolean }
|
|
48
|
+
return rule.rows.includes(row.kind) && (!rule.command || COMMAND_BACKED.has(row.fact_id)) ? [] : [MAJOR_FOR[kind] as string]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const CLAIM_CODES = new Set(["claim_kind_mismatch", "unsupported_verification", "verification_claim_unmarked"])
|
|
52
|
+
const run = (body: string) => valueChecks(buildContext(parseHtml(`<h2>Findings</h2>\n${body}`), { baseDir: FIX }), LEDGER, null, join(FIX, "sources"), {}) as Defect[]
|
|
53
|
+
const claims = (body: string) => run(body).filter((d) => CLAIM_CODES.has(d.code))
|
|
54
|
+
const codes = (body: string) => claims(body).map((d) => d.code).sort()
|
|
55
|
+
|
|
56
|
+
const value = (row: Row, kind?: string) => {
|
|
57
|
+
const mark = kind ? ` ${CLAIM_ATTRIBUTE}="${kind}"` : ""
|
|
58
|
+
return row.kind === "quote" ? `<q data-fact="${row.fact_id}"${mark} data-quote-sel="full">${row.quote}</q>` : `<data class="fact"${mark} data-fact="${row.fact_id}">${row.raw}</data>`
|
|
59
|
+
}
|
|
60
|
+
const prose = (inner: string, lead = "The figure is") => `<p>${lead} ${inner} [S4].</p>`
|
|
61
|
+
const cell = (inner: string) => `<table><tr><th>Item</th><td>${inner}</td></tr></table>`
|
|
62
|
+
|
|
63
|
+
describe("every claim kind against the hand-written oracle", () => {
|
|
64
|
+
for (const kind of Object.keys(ORACLE)) {
|
|
65
|
+
for (const row of SUBJECTS) {
|
|
66
|
+
const want = expected(kind, row)
|
|
67
|
+
const label = want.length === 0 ? "no claim defect" : `${want[0]} major`
|
|
68
|
+
test(`#given ${kind} marked on ${row.fact_id} (${row.kind}) in prose and in a table cell #then ${label}`, () => {
|
|
69
|
+
for (const body of [prose(value(row, kind)), cell(value(row, kind))]) {
|
|
70
|
+
expect(codes(body)).toEqual(want)
|
|
71
|
+
for (const defect of claims(body)) expect(defect.severity).toBe("major")
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
test(`#given ${kind} marked on an enclosing element over ${row.fact_id} #then the parent's mark binds it: ${label}`, () => {
|
|
75
|
+
expect(codes(`<p><span ${CLAIM_ATTRIBUTE}="${kind}">The figure is ${value(row)}</span> [S4].</p>`)).toEqual(want)
|
|
76
|
+
})
|
|
77
|
+
test(`#given ${kind} marked on ${row.fact_id} beside an unmarked command row #then the neighbour never satisfies it: ${label}`, () => {
|
|
78
|
+
expect(codes(prose(`${value(row, kind)}, and the suite reported ${value(F7)}`))).toEqual(want)
|
|
79
|
+
})
|
|
80
|
+
for (const inner of Object.keys(ORACLE).filter((other) => other !== kind)) {
|
|
81
|
+
test(`#given ${inner} nested inside ${kind} over ${row.fact_id} #then mismatch, the parent owns nothing, and the inner kind is still checked`, () => {
|
|
82
|
+
const body = `<p><span ${CLAIM_ATTRIBUTE}="${kind}">The figure is ${value(row, inner)}</span> [S4].</p>`
|
|
83
|
+
expect(codes(body)).toEqual(["claim_kind_mismatch", MAJOR_FOR[kind] as string, ...expected(inner, row)].sort())
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (const row of SUBJECTS) {
|
|
90
|
+
const refused = UNMARKED_REFUSED.has(row.fact_id) ? ["claim_kind_mismatch:major"] : []
|
|
91
|
+
const refusedLabel = refused.length ? "claim_kind_mismatch major" : "no claim code"
|
|
92
|
+
test(`#given ${row.fact_id} unmarked in a first-person run sentence #then the minor verification_claim_unmarked, plus ${refusedLabel}`, () => {
|
|
93
|
+
const found = claims(prose(value(row), "We ran the check and it reported"))
|
|
94
|
+
expect(found.map((d) => `${d.code}:${d.severity}`).sort()).toEqual([...refused, "verification_claim_unmarked:minor"].sort())
|
|
95
|
+
})
|
|
96
|
+
test(`#given ${row.fact_id} unmarked in a plain sentence in prose and in a table cell #then ${refusedLabel}`, () => {
|
|
97
|
+
for (const body of [prose(value(row)), cell(value(row))]) expect(claims(body).map((d) => `${d.code}:${d.severity}`)).toEqual(refused)
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
test("#given an unknown claim kind #then claim_kind_mismatch", () => {
|
|
102
|
+
expect(codes(prose(value(F1, "rumoured")))).toEqual(["claim_kind_mismatch"])
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test("#given every defect and warning code the table names #then contracts defines it, and warnings are never major", () => {
|
|
106
|
+
for (const entry of Object.values(CLAIM_KINDS) as { defect: string; unmarked: { code: string } | null }[]) {
|
|
107
|
+
expect(DEFECT_CODES[entry.defect]).toBeDefined()
|
|
108
|
+
if (entry.unmarked) expect(DEFECT_CODES[entry.unmarked.code].severity).toBe("minor")
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
describe("design-check regressions", () => {
|
|
114
|
+
test("#given verified wrapping a fact-marked page row (F1 shadowing) #then the parent is unsupported and the nesting is a mismatch", () => {
|
|
115
|
+
expect(codes(`<p><span ${CLAIM_ATTRIBUTE}="verified">The inspection put the span at ${value(F1, "fact")}</span> [S4].</p>`)).toEqual(["claim_kind_mismatch", "unsupported_verification"])
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
test("#given one verified value and an unmarked value in the same run sentence (F2) #then the unmarked one still warns", () => {
|
|
119
|
+
expect(codes(prose(`${value(F7, "verified")}, and the span is ${value(F1)}`, "We ran the suite and it reported"))).toEqual(["verification_claim_unmarked"])
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test("#given a truthful converted and derived value marked fact (F3) #then no stricter than unmarked", () => {
|
|
123
|
+
for (const row of [F8, F9, F10]) expect(codes(prose(value(row, "fact")))).toEqual(codes(prose(value(row))))
|
|
124
|
+
expect(codes(prose(value(F8, "fact")))).toEqual([])
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
test("#given the assumed row F9 unmarked or marked fact #then both refuse it, and the unmarked message names the A mark", () => {
|
|
128
|
+
expect(codes(prose(value(F9)))).toEqual(["claim_kind_mismatch"])
|
|
129
|
+
expect(codes(prose(value(F9, "fact")))).toEqual(["claim_kind_mismatch"])
|
|
130
|
+
expect(JSON.stringify(claims(prose(value(F9))))).toContain("{{A:F9}}")
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
test("#given the assumed row F9 marked {{A:F9}} (data-claim-kind=assumed) #then no claim defect", () => {
|
|
134
|
+
expect(codes(prose(value(F9, "assumed")))).toEqual([])
|
|
135
|
+
expect(codes(cell(value(F9, "assumed")))).toEqual([])
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
test("#given verified citing a command-sourced row with no, a missing, or an escaping locator artifact (F4) #then unsupported_verification", () => {
|
|
139
|
+
for (const row of [F11, F12, F13]) expect(codes(prose(value(row, "verified")))).toEqual(["unsupported_verification"])
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
describe("unmarked phrases", () => {
|
|
144
|
+
const fires = [
|
|
145
|
+
"We verified the span by running the measurement script.",
|
|
146
|
+
"We ran the suite twice.",
|
|
147
|
+
"We executed the export.",
|
|
148
|
+
"We verified by hand-counting the rows.",
|
|
149
|
+
"We have run the tests.",
|
|
150
|
+
"I ran the suite.",
|
|
151
|
+
"Our numbers were obtained by running the script.",
|
|
152
|
+
"우리가 실행해 확인한 길이",
|
|
153
|
+
"저희가 스크립트를 실행하여 검증한 값",
|
|
154
|
+
"저희가 테스트를 돌렸다",
|
|
155
|
+
"우리 팀이 벤치마크를 돌려 얻은 값",
|
|
156
|
+
"그래서 우리는 스크립트를 실행했다",
|
|
157
|
+
"직접 실행한 결과",
|
|
158
|
+
]
|
|
159
|
+
const quiet = [
|
|
160
|
+
"The tests showed a 12 % drop.",
|
|
161
|
+
"The bridge, which ran across the river, spans 320 m.",
|
|
162
|
+
"The span, as confirmed with the annual inspection report, is 320 m.",
|
|
163
|
+
"Clinical tests confirmed the diagnosis.",
|
|
164
|
+
"The authors obtained estimates by running simulations.",
|
|
165
|
+
"We ran out of time.",
|
|
166
|
+
"확인된 길이는 최대 320 m",
|
|
167
|
+
"개발자가 직접 실행한 결과를 보고했다",
|
|
168
|
+
"실행해 확인한 길이",
|
|
169
|
+
"우리나라에서 실행된 정책은 320개다",
|
|
170
|
+
"우리 정부가 실행한 예산",
|
|
171
|
+
"우리가 의뢰한 업체가 테스트를 돌렸다",
|
|
172
|
+
"우리는 결과를 확인했다",
|
|
173
|
+
]
|
|
174
|
+
for (const text of fires) test(`#given "${text}" #then verified`, () => expect(unmarkedKinds(text)).toEqual(["verified"]))
|
|
175
|
+
for (const text of quiet) test(`#given "${text}" #then nothing`, () => expect(unmarkedKinds(text)).toEqual([]))
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
describe("references/fidelity-claim-kinds.md is rendered from CLAIM_KINDS", () => {
|
|
179
|
+
const doc = readFileSync(join(import.meta.dir, "..", "references", "fidelity-claim-kinds.md"), "utf8")
|
|
180
|
+
|
|
181
|
+
test("the committed table section equals renderClaimKindTable() byte for byte", () => {
|
|
182
|
+
const [begin, end] = [doc.indexOf(CLAIM_TABLE_BEGIN), doc.indexOf(CLAIM_TABLE_END)]
|
|
183
|
+
expect(begin).toBeGreaterThanOrEqual(0)
|
|
184
|
+
expect(end).toBeGreaterThan(begin)
|
|
185
|
+
expect(doc.slice(begin + CLAIM_TABLE_BEGIN.length + 1, end)).toBe(renderClaimKindTable())
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
test("node claim-kinds.mjs --render-doc prints the same table", () => {
|
|
189
|
+
const result = spawnSync("node", [join(import.meta.dir, "claim-kinds.mjs"), "--render-doc"], { encoding: "utf8" })
|
|
190
|
+
expect(result.status).toBe(0)
|
|
191
|
+
expect(result.stdout).toBe(renderClaimKindTable())
|
|
192
|
+
})
|
|
193
|
+
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// cli-support.mjs - argument parsing, typed usage errors, exit-code mapping, and JSON file IO
|
|
2
2
|
// shared by every report-tools subcommand. node: builtins only.
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { createHash } from "node:crypto"
|
|
5
|
+
import { readFileSync, renameSync, rmSync, writeFileSync } from "node:fs"
|
|
5
6
|
import { basename, dirname, join } from "node:path"
|
|
6
7
|
|
|
7
8
|
/** A usage or input error: always exit code 2. */
|
|
@@ -87,11 +88,24 @@ export function readJsonFile(path) {
|
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
/**
|
|
91
|
-
export
|
|
91
|
+
/** The sha256 hex digest of a string or bytes: the one hash every record and receipt carries. */
|
|
92
|
+
export const sha256Of = (content) => createHash("sha256").update(content).digest("hex")
|
|
93
|
+
|
|
94
|
+
/** Write text through a sibling temp file + rename; a failed write removes the temp file. */
|
|
95
|
+
export function writeTextAtomic(path, text) {
|
|
92
96
|
const temp = join(dirname(path), `.${basename(path)}.${process.pid}.${Date.now()}.tmp`)
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
try {
|
|
98
|
+
writeFileSync(temp, text)
|
|
99
|
+
renameSync(temp, path)
|
|
100
|
+
} catch (error) {
|
|
101
|
+
rmSync(temp, { force: true })
|
|
102
|
+
throw error
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Write JSON through writeTextAtomic, so a crash never leaves a half-written file. */
|
|
107
|
+
export function writeJsonAtomic(path, value) {
|
|
108
|
+
writeTextAtomic(path, `${JSON.stringify(value, null, 2)}\n`)
|
|
95
109
|
}
|
|
96
110
|
|
|
97
111
|
/** Print a result: JSON on stdout, one summary line on stderr. */
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { describe, expect, onTestFinished, test } from "bun:test"
|
|
2
|
-
import { mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
|
2
|
+
import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
|
3
3
|
import { tmpdir } from "node:os"
|
|
4
4
|
import { join } from "node:path"
|
|
5
5
|
import { pathToFileURL } from "node:url"
|
|
6
6
|
|
|
7
|
-
import { CliError, exitCodeFor, parseArgs, readJsonFile, writeJsonAtomic } from "./cli-support.mjs"
|
|
7
|
+
import { CliError, exitCodeFor, parseArgs, readJsonFile, writeJsonAtomic, writeTextAtomic } from "./cli-support.mjs"
|
|
8
8
|
import { isCliEntry } from "./entry-guard.mjs"
|
|
9
9
|
|
|
10
10
|
function tempDir() {
|
|
@@ -65,6 +65,17 @@ describe("JSON files", () => {
|
|
|
65
65
|
expect(leftovers).toEqual([])
|
|
66
66
|
})
|
|
67
67
|
|
|
68
|
+
test("#given a target the rename cannot replace #when written atomically #then the write throws and no temp file is left", () => {
|
|
69
|
+
// given: a non-empty directory where the file should go
|
|
70
|
+
const dir = tempDir()
|
|
71
|
+
const target = join(dir, "calibration.jsonl")
|
|
72
|
+
mkdirSync(target)
|
|
73
|
+
writeFileSync(join(target, "keep"), "x")
|
|
74
|
+
// when / then
|
|
75
|
+
expect(() => writeTextAtomic(target, "line\n")).toThrow()
|
|
76
|
+
expect(readdirSync(dir)).toEqual(["calibration.jsonl"])
|
|
77
|
+
})
|
|
78
|
+
|
|
68
79
|
test("#given a missing or malformed file #when read #then a CliError names the path", () => {
|
|
69
80
|
const dir = tempDir()
|
|
70
81
|
expect(() => readJsonFile(join(dir, "missing.json"))).toThrow(CliError)
|
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
|
|
7
7
|
export const SCHEMA_VERSION = 1
|
|
8
8
|
|
|
9
|
+
/** The severity vocabulary in rank order; every DEFECT_CODES entry uses one of these. */
|
|
10
|
+
export const SEVERITIES = Object.freeze(["blocker", "major", "minor"])
|
|
11
|
+
|
|
12
|
+
/** Defect counts per severity, then total. */
|
|
13
|
+
export function countDefects(defects) {
|
|
14
|
+
const counts = Object.fromEntries(SEVERITIES.map((severity) => [severity, 0]))
|
|
15
|
+
for (const defect of defects) counts[defect.severity] += 1
|
|
16
|
+
return { ...counts, total: defects.length }
|
|
17
|
+
}
|
|
18
|
+
|
|
9
19
|
function deepFreeze(value) {
|
|
10
20
|
if (value === null || typeof value !== "object") return value
|
|
11
21
|
for (const key of Object.keys(value)) deepFreeze(value[key])
|
|
@@ -48,7 +58,7 @@ export const DEFECT_CODES = deepFreeze({
|
|
|
48
58
|
pointer_label_mismatch: { severity: "blocker", integrity: true, hint: "The placeholder's label is not the row's label; check the id, then the label." },
|
|
49
59
|
quote_anchor_unresolved: { severity: "blocker", integrity: true, hint: "A from or to anchor is missing or occurs more than once in the quote; pick unique anchor words." },
|
|
50
60
|
unit_duplicated: { severity: "blocker", integrity: true, hint: "The unit after the placeholder repeats the row's unit; use :value or remove the unit." },
|
|
51
|
-
polish_diff_fail: { severity: "blocker", integrity: true, hint: "The polish pass changed numbers, tickers, or
|
|
61
|
+
polish_diff_fail: { severity: "blocker", integrity: true, hint: "The polish pass changed numbers, tickers, table rows, claim ids, markup, or hidden content; revert to the pre-polish artifact and apply the language fixes only." },
|
|
52
62
|
fidelity_interview_incomplete: { severity: "blocker", integrity: true, hint: "A fidelity field is pending or missing in brief.md; answer the open question or record its default, then rerun." },
|
|
53
63
|
derived_not_allowed: { severity: "blocker", integrity: true, hint: "The brief allows no derived or assumed numbers; cite a source row directly or change the brief with the requester." },
|
|
54
64
|
source_outside_floor: { severity: "blocker", integrity: true, hint: "The row's source is outside the source floor the brief records; use a source the brief allows or remove the fact." },
|
|
@@ -57,7 +67,8 @@ export const DEFECT_CODES = deepFreeze({
|
|
|
57
67
|
quote_unbound: { severity: "blocker", integrity: true, hint: "A direct quotation has no ledger quote row; register it with fact quote and insert it through its placeholder." },
|
|
58
68
|
edition_unverified: { severity: "blocker", integrity: true, hint: "The delivered file has no matching fact verify record; run fact verify on that edition and record it with --verified-by." },
|
|
59
69
|
untraceable_number: { severity: "major", integrity: false, hint: "Replace the typed number with a {{F<n>}} placeholder backed by a ledger row, or register it as assumed with a rationale." },
|
|
60
|
-
unsupported_verification: { severity: "major", integrity: false, hint: "
|
|
70
|
+
unsupported_verification: { severity: "major", integrity: false, hint: "A sentence marked verified (data-claim-kind=\"verified\") cites a row that is not recorded command output; cite the recorded output row or drop the verified mark." },
|
|
71
|
+
claim_kind_mismatch: { severity: "major", integrity: false, hint: "An element marked with a claim kind (data-claim-kind) cites a row that kind does not accept, or the kind is unknown; cite a row of that kind or fix the mark (references/fidelity-claim-kinds.md)." },
|
|
61
72
|
conflicting_readings: { severity: "major", integrity: false, hint: "Two ledger rows with different periods or qualifiers are cited as one fact; split the sentence or retire one row." },
|
|
62
73
|
hedge_dropped: { severity: "major", integrity: false, hint: "Restore the qualifier the source uses (estimated, about, not yet)." },
|
|
63
74
|
direction_flipped: { severity: "major", integrity: false, hint: "The source states the opposite direction; rewrite from the quote." },
|
|
@@ -65,7 +76,8 @@ export const DEFECT_CODES = deepFreeze({
|
|
|
65
76
|
qualifier_dropped: { severity: "major", integrity: false, hint: "The source bounds this value (up to, at least, about); restore the qualifier or use :q." },
|
|
66
77
|
unbound_sentence: { severity: "major", integrity: false, hint: "Bind the sentence to a fact, a quote, a citation, or a claim-graph node with data-claim." },
|
|
67
78
|
meaning_drift_confirmed: { severity: "major", integrity: false, hint: "Two independent readers found the same drift; rewrite from the quote or dispute with a reason." },
|
|
68
|
-
|
|
79
|
+
meaning_unverified: { severity: "major", integrity: false, hint: "No meaning reader judged this pair, or no strong reader judged a force_strong pair; run the missing reader on pairs.json and rerun fact meaning-check." },
|
|
80
|
+
polish_edit_rejected: { severity: "major", integrity: false, hint: "The edit changed a number, a hedge, a qualifier, a link, or a claim id, or contained <, > or &; re-propose the language fix only, narrowed to the words around any entity or tag." },
|
|
69
81
|
polish_hedging_decreased: { severity: "major", integrity: false, hint: "The polish pass removed qualifiers; restore them." },
|
|
70
82
|
polish_text_churn: { severity: "major", integrity: false, hint: "More than half the text changed during polish; the pass composed instead of proofreading." },
|
|
71
83
|
snapshot_mismatch: { severity: "major", integrity: false, hint: "The operands come from different collection windows; use rows from one window or state the windows separately." },
|
|
@@ -73,6 +85,7 @@ export const DEFECT_CODES = deepFreeze({
|
|
|
73
85
|
quote_stale_version: { severity: "major", integrity: false, hint: "The quoted source revision differs from the revision the report targets; re-snapshot the target revision or date the quote." },
|
|
74
86
|
quote_fuzzy_match: { severity: "minor", integrity: false, hint: "The source text is OCR-noisy; confirm the snapshot and prefer the raw document." },
|
|
75
87
|
name_outside_quote: { severity: "minor", integrity: false, hint: "A name in the sentence is not in the cited quote; add the source or remove the name." },
|
|
88
|
+
verification_claim_unmarked: { severity: "minor", integrity: false, hint: "The sentence reads as a check we ran but carries no verified mark; mark it {{V:F<n>:label}} on a recorded command-output row, or reword it." },
|
|
76
89
|
scope_widened_warn: { severity: "minor", integrity: false, hint: "The sentence uses a broader scope word than the quote; confirm the scope or narrow it." },
|
|
77
90
|
particle_unverified: { severity: "minor", integrity: false, hint: "The particle after this value could not be checked; read it aloud and fix it by hand if wrong." },
|
|
78
91
|
fallback_without_entity: { severity: "minor", integrity: false, hint: "The replacement quote has no entity for attribution; set one on the row." },
|
|
@@ -87,10 +100,10 @@ export const FIDELITY_CODES = Object.freeze([
|
|
|
87
100
|
"unresolved_placeholder", "value_mismatch", "unit_mismatch", "quote_not_verbatim", "claim_status_invalid",
|
|
88
101
|
"incompatible_operands", "pointer_label_mismatch", "quote_anchor_unresolved", "unit_duplicated", "polish_diff_fail",
|
|
89
102
|
"fidelity_interview_incomplete", "derived_not_allowed", "source_outside_floor", "snapshot_hash_mismatch",
|
|
90
|
-
"chart_value_untraceable", "quote_unbound", "edition_unverified", "untraceable_number", "unsupported_verification",
|
|
103
|
+
"chart_value_untraceable", "quote_unbound", "edition_unverified", "untraceable_number", "unsupported_verification", "claim_kind_mismatch",
|
|
91
104
|
"conflicting_readings", "hedge_dropped", "direction_flipped", "causality_added", "qualifier_dropped", "unbound_sentence",
|
|
92
|
-
"meaning_drift_confirmed", "polish_edit_rejected", "polish_hedging_decreased", "polish_text_churn", "snapshot_mismatch",
|
|
93
|
-
"join_integrity_fail", "quote_stale_version", "quote_fuzzy_match", "name_outside_quote", "scope_widened_warn",
|
|
105
|
+
"meaning_drift_confirmed", "meaning_unverified", "polish_edit_rejected", "polish_hedging_decreased", "polish_text_churn", "snapshot_mismatch",
|
|
106
|
+
"join_integrity_fail", "quote_stale_version", "quote_fuzzy_match", "name_outside_quote", "verification_claim_unmarked", "scope_widened_warn",
|
|
94
107
|
"particle_unverified", "fallback_without_entity", "meaning_drift_warn", "polish_edit_unlocated", "polish_link_changed",
|
|
95
108
|
"polish_name_changed",
|
|
96
109
|
])
|
|
@@ -184,6 +197,14 @@ export function validateOutcomeManifest(manifest) {
|
|
|
184
197
|
if (isObject(manifest.gates)) {
|
|
185
198
|
for (const gate of GATE_NAMES) check(errors, GATE_STATUSES.includes(manifest.gates[gate]), `gates.${gate} must be one of ${GATE_STATUSES.join(", ")}`)
|
|
186
199
|
} else errors.push("gates must be an object")
|
|
200
|
+
if (manifest.gateHistory !== undefined) {
|
|
201
|
+
if (!Array.isArray(manifest.gateHistory)) errors.push("gateHistory must be an array")
|
|
202
|
+
else for (const entry of manifest.gateHistory) {
|
|
203
|
+
check(errors, isObject(entry) && GATE_NAMES.includes(entry.gate) && GATE_STATUSES.includes(entry.status)
|
|
204
|
+
&& typeof entry.at === "string" && ISO_DATE.test(entry.at)
|
|
205
|
+
&& (entry.reason === undefined || typeof entry.reason === "string"), "gateHistory entries must have a known gate, status, timestamp, and optional reason")
|
|
206
|
+
}
|
|
207
|
+
}
|
|
187
208
|
if (Array.isArray(manifest.residualDefects)) manifest.residualDefects.forEach((defect, index) => validateDefect(defect, `residualDefects[${index}]`, errors))
|
|
188
209
|
else errors.push("residualDefects must be an array")
|
|
189
210
|
check(errors, manifest.repair === null || isObject(manifest.repair), "repair must be null or an object")
|
|
@@ -75,6 +75,7 @@ const FIDELITY_BLOCKERS = [
|
|
|
75
75
|
const FIDELITY_MAJORS = [
|
|
76
76
|
"untraceable_number",
|
|
77
77
|
"unsupported_verification",
|
|
78
|
+
"claim_kind_mismatch",
|
|
78
79
|
"conflicting_readings",
|
|
79
80
|
"hedge_dropped",
|
|
80
81
|
"direction_flipped",
|
|
@@ -82,6 +83,7 @@ const FIDELITY_MAJORS = [
|
|
|
82
83
|
"qualifier_dropped",
|
|
83
84
|
"unbound_sentence",
|
|
84
85
|
"meaning_drift_confirmed",
|
|
86
|
+
"meaning_unverified",
|
|
85
87
|
"polish_edit_rejected",
|
|
86
88
|
"polish_hedging_decreased",
|
|
87
89
|
"polish_text_churn",
|
|
@@ -92,6 +94,7 @@ const FIDELITY_MAJORS = [
|
|
|
92
94
|
const FIDELITY_MINORS = [
|
|
93
95
|
"quote_fuzzy_match",
|
|
94
96
|
"name_outside_quote",
|
|
97
|
+
"verification_claim_unmarked",
|
|
95
98
|
"scope_widened_warn",
|
|
96
99
|
"particle_unverified",
|
|
97
100
|
"fallback_without_entity",
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// Architecture contract (retention bar): each shared table, list, regex, or classifier has one owner
|
|
2
|
+
// export, so a reintroduced local copy in a consumer fails one of these tests (#11156 sweep).
|
|
3
|
+
import { describe, expect, test } from "bun:test"
|
|
4
|
+
import { readdirSync, readFileSync } from "node:fs"
|
|
5
|
+
import { join } from "node:path"
|
|
6
|
+
|
|
7
|
+
import { sha256Of } from "./cli-support.mjs"
|
|
8
|
+
import { countDefects, DEFECT_CODES, SEVERITIES } from "./contracts.mjs"
|
|
9
|
+
import { meaningStatus, PROMOTION, promotionEligibility } from "./fact-commands-meaning.mjs"
|
|
10
|
+
import { editionFormatOf, MD_HTML_BLOCK_RULES, markdownAsHtml, projectMarkdownInline } from "./fact-commands-verify.mjs"
|
|
11
|
+
import { EXEMPT_KINDS, isExemptKind } from "./fidelity-binding.mjs"
|
|
12
|
+
import { blocksDelivery } from "./gates-fidelity.mjs"
|
|
13
|
+
import { MD_BLOCK_PATTERNS, parseMarkdown } from "./md-lite.mjs"
|
|
14
|
+
import { DRIFT_KINDS, isDriftKind, MODEL_TIERS, VERDICT_SCHEMA, VERDICTS } from "./meaning-check.mjs"
|
|
15
|
+
import { parseRenderedQuote, renderQuote } from "./placeholder.mjs"
|
|
16
|
+
|
|
17
|
+
const source = (name: string) => readFileSync(join(import.meta.dir, name), "utf8")
|
|
18
|
+
|
|
19
|
+
describe("severity vocabulary comes from contracts", () => {
|
|
20
|
+
test("#given DEFECT_CODES #when its severities are collected #then they are exactly SEVERITIES", () => {
|
|
21
|
+
const used = new Set(Object.values(DEFECT_CODES).map((entry) => entry.severity))
|
|
22
|
+
expect([...used].sort()).toEqual([...SEVERITIES].sort())
|
|
23
|
+
expect(Object.isFrozen(SEVERITIES)).toBe(true)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test("#given defects of every severity #when counted #then the buckets are SEVERITIES plus total", () => {
|
|
27
|
+
const defects = SEVERITIES.map((severity) => ({ severity }))
|
|
28
|
+
expect(Object.keys(countDefects(defects))).toEqual([...SEVERITIES, "total"])
|
|
29
|
+
expect(countDefects(defects)).toEqual({ ...Object.fromEntries(SEVERITIES.map((severity) => [severity, 1])), total: SEVERITIES.length })
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test("#given every registry code and an unknown code #when the meaning status is computed #then it follows blocksDelivery", () => {
|
|
33
|
+
for (const code of [...Object.keys(DEFECT_CODES), "not_a_registered_code"]) {
|
|
34
|
+
expect(meaningStatus([{ code, severity: "minor" }]), code).toBe(blocksDelivery({ code }) ? "fail" : "pass")
|
|
35
|
+
}
|
|
36
|
+
expect(meaningStatus([])).toBe("pass")
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test("#given the command modules #when read #then none restates the severity buckets, the pair kinds, or a window literal", () => {
|
|
40
|
+
for (const name of ["fact-commands-meaning.mjs", "fact-commands-verify.mjs"]) {
|
|
41
|
+
const text = source(name)
|
|
42
|
+
expect(text, name).not.toMatch(/blocker:\s*0/u)
|
|
43
|
+
expect(text, name).not.toMatch(/\["quote",\s*"claim",\s*"translation"\]/u)
|
|
44
|
+
expect(text, name).not.toMatch(/slice\(-\d+\)/u)
|
|
45
|
+
expect(text, name).not.toMatch(/counts\.blocker \+ counts\.major/u)
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe("one sha256 helper", () => {
|
|
51
|
+
test("#given every runtime module #when read #then only cli-support builds a hash, so records and receipts share sha256Of", () => {
|
|
52
|
+
const modules = readdirSync(import.meta.dir).filter((name) => name.endsWith(".mjs") && name !== "cli-support.mjs")
|
|
53
|
+
expect(modules.length).toBeGreaterThan(0)
|
|
54
|
+
for (const name of modules) expect(source(name), name).not.toMatch(/createHash\(/u)
|
|
55
|
+
expect(sha256Of("abc")).toBe("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad")
|
|
56
|
+
expect(sha256Of(Buffer.from("abc"))).toBe(sha256Of("abc"))
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
describe("calibration policy is the exported PROMOTION", () => {
|
|
61
|
+
const confirmedRun = (run: string, disputed: boolean) => ({ run_id: run, confirmed: true, disputed })
|
|
62
|
+
|
|
63
|
+
test("#given false blocks only before the window #when eligibility is computed #then they are not counted", () => {
|
|
64
|
+
const window = PROMOTION.false_block_window
|
|
65
|
+
expect(Number.isInteger(window) && window > 0).toBe(true)
|
|
66
|
+
const old = [0, 1, 2].map((n) => confirmedRun(`old${n}`, true))
|
|
67
|
+
const recent = Array.from({ length: window }, (_, n) => confirmedRun(`new${n}`, false))
|
|
68
|
+
expect(promotionEligibility([...old, ...recent]).report_false_blocks_in_window).toBe(0)
|
|
69
|
+
expect(promotionEligibility([...recent, ...old]).report_false_blocks_in_window).toBe(3)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test("#given false blocks at the policy maximum #when eligibility is computed #then the threshold read from PROMOTION decides", () => {
|
|
73
|
+
const limit = PROMOTION.max_report_false_blocks_in_window
|
|
74
|
+
const clean = Array.from({ length: PROMOTION.min_runs * 20 }, (_, n) => confirmedRun(`c${n}`, false))
|
|
75
|
+
const blocks = (count: number) => Array.from({ length: count }, (_, n) => confirmedRun(`b${n}`, true))
|
|
76
|
+
expect(promotionEligibility([...clean, ...blocks(limit)]).eligible).toBe(true)
|
|
77
|
+
expect(promotionEligibility([...clean, ...blocks(limit + 1)]).eligible).toBe(false)
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
describe("verdict schema comes from the meaning-check registries", () => {
|
|
82
|
+
test("#given VERDICT_SCHEMA #when read #then its vocabularies are the owner exports", () => {
|
|
83
|
+
expect(VERDICT_SCHEMA.verdict).toBe(VERDICTS)
|
|
84
|
+
expect(VERDICT_SCHEMA.model_tier).toBe(MODEL_TIERS)
|
|
85
|
+
expect(VERDICT_SCHEMA.drift_kind).toBe(DRIFT_KINDS)
|
|
86
|
+
expect(Object.isFrozen(VERDICTS)).toBe(true)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test("#given every drift kind and a stranger #when checked #then isDriftKind is membership in DRIFT_KINDS", () => {
|
|
90
|
+
for (const kind of DRIFT_KINDS) expect(isDriftKind(kind)).toBe(true)
|
|
91
|
+
expect(isDriftKind("not_a_kind")).toBe(false)
|
|
92
|
+
expect(source("meaning-check.mjs")).not.toMatch(/new Set\(DRIFT_KINDS\)|new Set\(\["drift"/u)
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
describe("inherited lexical tables come from their owners", () => {
|
|
97
|
+
test("#given fact-pairs #when read #then it holds no Hangul range, exempt-kind set, or hand-built hedge regex", () => {
|
|
98
|
+
const text = source("fact-pairs.mjs")
|
|
99
|
+
expect(text).not.toMatch(/\\uAC00-\\uD7A3/u)
|
|
100
|
+
expect(text).not.toMatch(/\["opinion",\s*"recommendation"\]/u)
|
|
101
|
+
expect(text).not.toMatch(/new RegExp\(/u)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test("#given EXEMPT_KINDS #when checked #then isExemptKind is membership in it", () => {
|
|
105
|
+
for (const kind of EXEMPT_KINDS) expect(isExemptKind(kind)).toBe(true)
|
|
106
|
+
expect(isExemptKind("finding")).toBe(false)
|
|
107
|
+
expect(isExemptKind(undefined)).toBe(false)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test("#given md-lite #when its block grammar is read #then MD_BLOCK_PATTERNS names every block regex", () => {
|
|
111
|
+
expect(Object.keys(MD_BLOCK_PATTERNS).sort()).toEqual(["BLOCKQUOTE", "CLOSING_HASHES", "FENCE", "HEADING", "LIST_ITEM", "TABLE_SEPARATOR", "THEMATIC_BREAK"])
|
|
112
|
+
for (const pattern of Object.values(MD_BLOCK_PATTERNS)) expect(pattern).toBeInstanceOf(RegExp)
|
|
113
|
+
expect(Object.isFrozen(MD_BLOCK_PATTERNS)).toBe(true)
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
describe("markdown projection rules", () => {
|
|
118
|
+
const EVERY_KIND = ["# Title", "", "Para text.", "", "- item", "", "> quoted", "", "| a | b |", "| - | - |", "| 1 | 2 |", "", "```", "code", "```", ""].join("\n")
|
|
119
|
+
|
|
120
|
+
test("#given every block kind md-lite emits #when projected #then each kind has a projection rule", () => {
|
|
121
|
+
const kinds = [...new Set(parseMarkdown(EVERY_KIND).blocks.map((block: { kind: string }) => block.kind))].sort()
|
|
122
|
+
expect(kinds).toEqual(["blockquote", "code", "heading", "list_item", "paragraph", "table_row"])
|
|
123
|
+
for (const kind of kinds) expect(Object.hasOwn(MD_HTML_BLOCK_RULES, kind), kind).toBe(true)
|
|
124
|
+
expect(Object.keys(MD_HTML_BLOCK_RULES).sort()).toEqual(kinds)
|
|
125
|
+
const { html } = markdownAsHtml(EVERY_KIND, [], { rows: [] })
|
|
126
|
+
expect(html).toContain("<h1>Title</h1>")
|
|
127
|
+
expect(html).toContain("<ul><li>item</li></ul>")
|
|
128
|
+
expect(html).toMatch(/<table><tr><td>a<\/td><td>b<\/td><\/tr>\s*<tr><td>1<\/td><td>2<\/td><\/tr><\/table>/u)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
test("#given an unterminated fence #when projected #then it is refused naming the line", () => {
|
|
132
|
+
expect(() => markdownAsHtml("Intro.\n\n```\nno close\n", [], { rows: [] })).toThrow(/unterminated_fence at line 3/u)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test("#given inline links and code #when projected #then they stay elements and the rest is escaped", () => {
|
|
136
|
+
expect(projectMarkdownInline("a <b> [x](https://e.x) `c`")).toBe('a <b> <a href="https://e.x">x</a> <code>c</code>')
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test("#given a fill map with quotes in translates and fact_id #when projected #then the attributes are escaped", () => {
|
|
140
|
+
const md = "Before X after.\n\n> Quoted block.\n"
|
|
141
|
+
const span = { fact_id: 'F"1', start: 7, end: 8, rendered: "X", translates: 'T"1' }
|
|
142
|
+
const block = { fact_id: 'Q"2', start: md.indexOf(">"), end: md.length - 1, rendered: "", replaced: "a" }
|
|
143
|
+
const { html } = markdownAsHtml(md, [span, block], { rows: [] })
|
|
144
|
+
expect(html).toContain('data-translates="T"1"')
|
|
145
|
+
expect(html).toContain('data-fact="Q"2"')
|
|
146
|
+
expect(markdownAsHtml("Before X after.\n", [{ ...span, translates: undefined }], { rows: [] }).html).toContain('data-fact="F"1"')
|
|
147
|
+
expect(html).not.toMatch(/="[^"]*"1"/u)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test("#given paths of each case #when the format is inferred #then md and markdown are Markdown and a flag wins", () => {
|
|
151
|
+
expect(["r.md", "R.MD", "r.markdown", "r.html", "r.txt"].map((path) => editionFormatOf(path, undefined))).toEqual(["md", "md", "md", "html", "html"])
|
|
152
|
+
expect(editionFormatOf("r.md", "html")).toBe("html")
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
describe("rendered quotes round-trip through the placeholder owner", () => {
|
|
157
|
+
const row = { fact_id: "Q1", kind: "quote", source_id: "S4", quote: "We estimate completion no earlier than 2027." }
|
|
158
|
+
const selections = [
|
|
159
|
+
["full", row.quote],
|
|
160
|
+
["lead=2", "We estimate"],
|
|
161
|
+
["from=completion,to=2027", "completion no earlier than 2027"],
|
|
162
|
+
]
|
|
163
|
+
|
|
164
|
+
for (const [selector, text] of selections) {
|
|
165
|
+
test(`#given a ${selector} quote rendered for md #when projected #then the quote element and marker survive`, () => {
|
|
166
|
+
const rendered = renderQuote(row, selector, text, "md")
|
|
167
|
+
expect(parseRenderedQuote(rendered)).toEqual({ text, marker: "[S4]" })
|
|
168
|
+
const md = `The operator wrote ${rendered}.\n`
|
|
169
|
+
const entry = { fact_id: "Q1", start: md.indexOf(rendered), end: md.indexOf(rendered) + rendered.length, rendered, selector }
|
|
170
|
+
const projected = markdownAsHtml(md, [entry], { rows: [row] })
|
|
171
|
+
expect(projected.html).toContain(renderQuote(row, selector, text, "html"))
|
|
172
|
+
expect(projected.stale).toEqual([])
|
|
173
|
+
const edited = md.replace(text, `${text} indeed`)
|
|
174
|
+
const stale = markdownAsHtml(edited, [{ ...entry, end: entry.end + " indeed".length }], { rows: [row] })
|
|
175
|
+
expect(stale.html).not.toContain("<q ")
|
|
176
|
+
expect(stale.stale).toHaveLength(1)
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
test("#given bytes that are not the md quote layout #when parsed #then null", () => {
|
|
181
|
+
expect(parseRenderedQuote("plain text [S4]")).toBeNull()
|
|
182
|
+
})
|
|
183
|
+
})
|