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,176 @@
|
|
|
1
|
+
// source-snapshot.mjs - the per-source snapshot that fact locators read.
|
|
2
|
+
//
|
|
3
|
+
// snapshotFromHtml turns a page into text.txt form: one line per block (prose blocks via
|
|
4
|
+
// proseText, one table cell per line; head, scripts, styles and code are dropped). writeSnapshot
|
|
5
|
+
// writes <sourcesDir>/<S-id>/ with text.txt (pages joined by a form feed), source.html when html
|
|
6
|
+
// was given, meta.json, sentences.jsonl (sid S<n>:s<k>) and acronyms.json. The sha256 covers
|
|
7
|
+
// text.txt only. Nothing here touches the network: the caller supplies the html or text.
|
|
8
|
+
// Plain Node ESM, node: builtins and sibling imports only.
|
|
9
|
+
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
|
|
11
|
+
import { join } from "node:path"
|
|
12
|
+
|
|
13
|
+
import { sha256Of } from "./cli-support.mjs"
|
|
14
|
+
import { parseHtml, PROSE_TAGS, proseText } from "./html-lite.mjs"
|
|
15
|
+
import { splitSentences } from "./sentence-split.mjs"
|
|
16
|
+
|
|
17
|
+
const SKIP = new Set(["head", "script", "style", "svg", "code", "pre", "kbd", "samp", "template", "noscript"])
|
|
18
|
+
const INLINE = new Set([
|
|
19
|
+
"a", "abbr", "b", "bdi", "bdo", "cite", "data", "del", "dfn", "em", "font", "i", "ins", "label", "mark", "q", "s",
|
|
20
|
+
"small", "span", "strong", "sub", "sup", "time", "u", "var",
|
|
21
|
+
])
|
|
22
|
+
const SOURCE_ID = /^S[1-9]\d*$/
|
|
23
|
+
const EXTRACTOR = /^(?:tool:source-snapshot@1|model|extractor:[\w.@+-]+)$/
|
|
24
|
+
const KINDS = new Set(["page", "command", "document"])
|
|
25
|
+
const PROVENANCE = new Set(["primary", "secondary", "unknown"])
|
|
26
|
+
const ACRONYM_AFTER = /\(([A-Z]{2,6})\)/g
|
|
27
|
+
const ACRONYM_BEFORE = /(?<![\p{L}\p{N}])([A-Z]{2,6})\s?\(([A-Z][^()\n]{2,79})\)/gu
|
|
28
|
+
const NAME_WORD = /^[A-Z][\p{L}\p{N}-]*$/u
|
|
29
|
+
const CONNECTOR = new Set(["of", "and", "for", "the", "in", "on", "to", "&"])
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Block lines of a page: { root, text, blocks: [{ node, start, end }] } where text joins the block
|
|
33
|
+
* texts with "\n" and text.slice(start, end) is one block. fact-locate reads cells through `node`.
|
|
34
|
+
*/
|
|
35
|
+
export function snapshotBlocks(html) {
|
|
36
|
+
const root = parseHtml(html)
|
|
37
|
+
const lines = []
|
|
38
|
+
let buffer = ""
|
|
39
|
+
const flush = (node) => {
|
|
40
|
+
const line = buffer.replace(/\s+/g, " ").trim()
|
|
41
|
+
buffer = ""
|
|
42
|
+
if (line) lines.push({ node, text: line })
|
|
43
|
+
}
|
|
44
|
+
const walk = (node) => {
|
|
45
|
+
for (const child of node.children) {
|
|
46
|
+
if (child.tag === "#text") buffer += child.text
|
|
47
|
+
else if (SKIP.has(child.tag)) continue
|
|
48
|
+
else if (child.tag === "br") flush(node)
|
|
49
|
+
else if (INLINE.has(child.tag)) walk(child)
|
|
50
|
+
else if (PROSE_TAGS.has(child.tag)) {
|
|
51
|
+
flush(node)
|
|
52
|
+
const line = proseText(child)
|
|
53
|
+
if (line) lines.push({ node: child, text: line })
|
|
54
|
+
} else {
|
|
55
|
+
flush(node)
|
|
56
|
+
walk(child)
|
|
57
|
+
flush(child)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
walk(root)
|
|
62
|
+
flush(root)
|
|
63
|
+
const blocks = []
|
|
64
|
+
let at = 0
|
|
65
|
+
for (const line of lines) {
|
|
66
|
+
blocks.push({ node: line.node, start: at, end: at + line.text.length })
|
|
67
|
+
at += line.text.length + 1
|
|
68
|
+
}
|
|
69
|
+
return { root, text: lines.map((line) => line.text).join("\n"), blocks }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** text.txt form of an html page. */
|
|
73
|
+
export function snapshotFromHtml(html) {
|
|
74
|
+
return snapshotBlocks(html).text
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Sentence index rows { sid: "S<n>:s<k>", start, end, text }; a form feed (page break) always ends a sentence. */
|
|
78
|
+
export function indexSentences(text, sourceId) {
|
|
79
|
+
return splitSentences(text.replace(/\f/g, "\n")).map((row) => ({
|
|
80
|
+
sid: `${sourceId}:${row.sid}`,
|
|
81
|
+
start: row.start,
|
|
82
|
+
end: row.end,
|
|
83
|
+
text: text.slice(row.start, row.end),
|
|
84
|
+
}))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const sentenceIdAt = (sentences, offset) => sentences.find((row) => row.start <= offset && offset < row.end)?.sid ?? null
|
|
88
|
+
|
|
89
|
+
/** "High Bandwidth Memory (HBM)": the capitalized words before the parenthesis, at most one per letter. */
|
|
90
|
+
function nameBefore(text, at, letters) {
|
|
91
|
+
const prefix = text.slice(Math.max(0, at - 160), at)
|
|
92
|
+
if (!/\S[ \t]?$/.test(prefix)) return null
|
|
93
|
+
const words = [...prefix.matchAll(/\S+/g)]
|
|
94
|
+
const picked = []
|
|
95
|
+
let content = 0
|
|
96
|
+
for (let k = words.length - 1; k >= 0 && content < letters; k--) {
|
|
97
|
+
const word = words[k][0]
|
|
98
|
+
if (NAME_WORD.test(word)) content++
|
|
99
|
+
else if (!CONNECTOR.has(word) || picked.length === 0) break
|
|
100
|
+
picked.unshift(words[k])
|
|
101
|
+
}
|
|
102
|
+
while (picked.length > 0 && CONNECTOR.has(picked[0][0])) picked.shift()
|
|
103
|
+
if (picked.length === 0) return null
|
|
104
|
+
const start = at - prefix.length + picked[0].index
|
|
105
|
+
const end = at - prefix.length + picked[picked.length - 1].index + picked[picked.length - 1][0].length
|
|
106
|
+
return { start, expansion: text.slice(start, end) }
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Every `Full Name (ACR)` and `ACR (Full Name)` in the text, ordered by position. */
|
|
110
|
+
export function findAcronyms(text, sentences) {
|
|
111
|
+
const entries = []
|
|
112
|
+
for (const match of text.matchAll(ACRONYM_AFTER)) {
|
|
113
|
+
const name = nameBefore(text, match.index, match[1].length)
|
|
114
|
+
if (!name || name.expansion === match[1]) continue
|
|
115
|
+
entries.push({ acronym: match[1], expansion: name.expansion, span: [name.start, match.index + match[0].length] })
|
|
116
|
+
}
|
|
117
|
+
for (const match of text.matchAll(ACRONYM_BEFORE)) {
|
|
118
|
+
const expansion = match[2].trim()
|
|
119
|
+
if (!/\s/.test(expansion) || expansion === expansion.toUpperCase()) continue
|
|
120
|
+
entries.push({ acronym: match[1], expansion, span: [match.index, match.index + match[0].length] })
|
|
121
|
+
}
|
|
122
|
+
return entries
|
|
123
|
+
.sort((a, b) => a.span[0] - b.span[0])
|
|
124
|
+
.map((entry) => ({ acronym: entry.acronym, expansion: entry.expansion, sentence_id: sentenceIdAt(sentences, entry.span[0]), span: entry.span }))
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function snapshotText(input) {
|
|
128
|
+
if (Array.isArray(input.pages)) return input.pages.join("\f")
|
|
129
|
+
if (typeof input.text === "string") return input.text
|
|
130
|
+
if (typeof input.html === "string") return snapshotFromHtml(input.html)
|
|
131
|
+
throw new TypeError("writeSnapshot needs text, pages, or html")
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function checkInput(sourceId, input) {
|
|
135
|
+
if (!SOURCE_ID.test(String(sourceId))) throw new TypeError(`source id must be S<n>, got ${sourceId}`)
|
|
136
|
+
if (!EXTRACTOR.test(String(input.extractor))) throw new TypeError(`extractor must be tool:source-snapshot@1, model, or extractor:<name>, got ${input.extractor}`)
|
|
137
|
+
if (typeof (input.url ?? input.path) !== "string") throw new TypeError("writeSnapshot needs a url or a path")
|
|
138
|
+
if (input.kind !== undefined && !KINDS.has(input.kind)) throw new TypeError(`kind must be page, command, or document, got ${input.kind}`)
|
|
139
|
+
if (input.provenance !== undefined && !PROVENANCE.has(input.provenance)) throw new TypeError(`provenance must be primary, secondary, or unknown, got ${input.provenance}`)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Write <sourcesDir>/<sourceId>/ for one source; returns { dir, sha256 } with sha256 over text.txt.
|
|
144
|
+
* input: { url | path, text | pages | html, extractor, observed_at?, revision?, kind?, provenance? }.
|
|
145
|
+
* Re-writing the same text is a no-op change; a different text over an existing snapshot throws
|
|
146
|
+
* snapshot_exists, because ledger rows pin the old hash.
|
|
147
|
+
*/
|
|
148
|
+
export function writeSnapshot(sourcesDir, sourceId, input) {
|
|
149
|
+
checkInput(sourceId, input)
|
|
150
|
+
const text = snapshotText(input)
|
|
151
|
+
const sha256 = sha256Of(Buffer.from(text, "utf8"))
|
|
152
|
+
const dir = join(sourcesDir, sourceId)
|
|
153
|
+
const textPath = join(dir, "text.txt")
|
|
154
|
+
if (existsSync(textPath) && sha256Of(readFileSync(textPath)) !== sha256) {
|
|
155
|
+
throw new Error(`snapshot_exists: ${sourceId} already holds a different text.txt`)
|
|
156
|
+
}
|
|
157
|
+
mkdirSync(dir, { recursive: true })
|
|
158
|
+
writeFileSync(textPath, text)
|
|
159
|
+
if (typeof input.html === "string") writeFileSync(join(dir, "source.html"), input.html)
|
|
160
|
+
const meta = {
|
|
161
|
+
source_id: sourceId,
|
|
162
|
+
url: input.url ?? input.path,
|
|
163
|
+
observed_at: input.observed_at ?? new Date().toISOString(),
|
|
164
|
+
sha256,
|
|
165
|
+
extractor: input.extractor,
|
|
166
|
+
revision: input.revision ?? null,
|
|
167
|
+
kind: input.kind ?? (input.url !== undefined || typeof input.html === "string" ? "page" : "document"),
|
|
168
|
+
provenance: input.provenance ?? "unknown",
|
|
169
|
+
}
|
|
170
|
+
writeFileSync(join(dir, "meta.json"), `${JSON.stringify(meta, null, 2)}\n`)
|
|
171
|
+
const sentences = indexSentences(text, sourceId)
|
|
172
|
+
writeFileSync(join(dir, "sentences.jsonl"), sentences.map((row) => `${JSON.stringify(row)}\n`).join(""))
|
|
173
|
+
const acronyms = { schemaVersion: 1, source_id: sourceId, snapshot_sha256: sha256, entries: findAcronyms(text, sentences) }
|
|
174
|
+
writeFileSync(join(dir, "acronyms.json"), `${JSON.stringify(acronyms, null, 2)}\n`)
|
|
175
|
+
return { dir, sha256 }
|
|
176
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, expect, onTestFinished, test } from "bun:test"
|
|
2
|
+
import { createHash } from "node:crypto"
|
|
3
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs"
|
|
4
|
+
import { tmpdir } from "node:os"
|
|
5
|
+
import { join } from "node:path"
|
|
6
|
+
|
|
7
|
+
import { snapshotFromHtml, writeSnapshot } from "./source-snapshot.mjs"
|
|
8
|
+
|
|
9
|
+
const FIXTURES = join(import.meta.dir, "tests", "fixtures", "fidelity", "sources")
|
|
10
|
+
const S4_HTML = readFileSync(join(FIXTURES, "S4.html"), "utf8")
|
|
11
|
+
const S9_TEXT = readFileSync(join(FIXTURES, "S9.txt"), "utf8")
|
|
12
|
+
|
|
13
|
+
function tempDir(): string {
|
|
14
|
+
const dir = mkdtempSync(join(tmpdir(), "ulw-snapshot-"))
|
|
15
|
+
onTestFinished(() => rmSync(dir, { recursive: true, force: true }))
|
|
16
|
+
return dir
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type Sentence = { sid: string; start: number; end: number; text: string }
|
|
20
|
+
|
|
21
|
+
function readSentences(dir: string): Sentence[] {
|
|
22
|
+
return readFileSync(join(dir, "sentences.jsonl"), "utf8")
|
|
23
|
+
.split("\n")
|
|
24
|
+
.filter((line) => line !== "")
|
|
25
|
+
.map((line) => JSON.parse(line))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("snapshotFromHtml", () => {
|
|
29
|
+
test("#given a page with prose, a table, a script and a comment #when snapshotted #then blocks are lines and each table cell is its own line", () => {
|
|
30
|
+
const lines = snapshotFromHtml(S4_HTML).split("\n")
|
|
31
|
+
expect(lines).toContain("Span length: 320 m")
|
|
32
|
+
expect(lines).toContain("After the delay, we estimate completion no earlier than 2027.")
|
|
33
|
+
const header = lines.indexOf("Metric")
|
|
34
|
+
expect(lines.slice(header, header + 4)).toEqual(["Metric", "Length", "Span", "320 m"])
|
|
35
|
+
const text = lines.join("\n")
|
|
36
|
+
expect(text).not.toContain("not prose")
|
|
37
|
+
expect(text).not.toContain("internal draft comment")
|
|
38
|
+
expect(text).not.toContain("p { margin")
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe("writeSnapshot", () => {
|
|
43
|
+
test("#given html #when written #then text.txt, source.html, meta.json, sentences.jsonl and acronyms.json exist and sha256 covers text.txt", () => {
|
|
44
|
+
const sources = tempDir()
|
|
45
|
+
const result = writeSnapshot(sources, "S4", { url: "https://example.test/bridge", html: S4_HTML, extractor: "tool:source-snapshot@1", observed_at: "2026-10-23T00:00:00Z" })
|
|
46
|
+
expect(result.dir).toBe(join(sources, "S4"))
|
|
47
|
+
for (const name of ["text.txt", "source.html", "meta.json", "sentences.jsonl", "acronyms.json"]) expect(existsSync(join(result.dir, name))).toBe(true)
|
|
48
|
+
const text = readFileSync(join(result.dir, "text.txt"))
|
|
49
|
+
expect(result.sha256).toBe(createHash("sha256").update(text).digest("hex"))
|
|
50
|
+
expect(readFileSync(join(result.dir, "source.html"), "utf8")).toBe(S4_HTML)
|
|
51
|
+
const meta = JSON.parse(readFileSync(join(result.dir, "meta.json"), "utf8"))
|
|
52
|
+
expect(meta).toEqual({
|
|
53
|
+
source_id: "S4",
|
|
54
|
+
url: "https://example.test/bridge",
|
|
55
|
+
observed_at: "2026-10-23T00:00:00Z",
|
|
56
|
+
sha256: result.sha256,
|
|
57
|
+
extractor: "tool:source-snapshot@1",
|
|
58
|
+
revision: null,
|
|
59
|
+
kind: "page",
|
|
60
|
+
provenance: "unknown",
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test("#given a written snapshot #when sentences.jsonl is read #then every row slices text.txt and carries an S<n>:s<k> id", () => {
|
|
65
|
+
const sources = tempDir()
|
|
66
|
+
const { dir } = writeSnapshot(sources, "S4", { url: "https://example.test/bridge", html: S4_HTML, extractor: "tool:source-snapshot@1" })
|
|
67
|
+
const text = readFileSync(join(dir, "text.txt"), "utf8")
|
|
68
|
+
const sentences = readSentences(dir)
|
|
69
|
+
expect(sentences.length).toBeGreaterThanOrEqual(4)
|
|
70
|
+
sentences.forEach((row, index) => {
|
|
71
|
+
expect(row.sid).toBe(`S4:s${index + 1}`)
|
|
72
|
+
expect(text.slice(row.start, row.end)).toBe(row.text)
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test("#given both acronym forms #when written #then acronyms.json lists each expansion with its sentence and span", () => {
|
|
77
|
+
const sources = tempDir()
|
|
78
|
+
const text = "The deck uses High Bandwidth Memory (HBM) sensors.\nA second note cites TSV (Through Silicon Via) bonding."
|
|
79
|
+
const { dir, sha256 } = writeSnapshot(sources, "S2", { path: "notes/memo.txt", text, extractor: "model" })
|
|
80
|
+
const acronyms = JSON.parse(readFileSync(join(dir, "acronyms.json"), "utf8"))
|
|
81
|
+
expect(acronyms.source_id).toBe("S2")
|
|
82
|
+
expect(acronyms.snapshot_sha256).toBe(sha256)
|
|
83
|
+
const entries = acronyms.entries.map((entry: { acronym: string; expansion: string; sentence_id: string; span: number[] }) => [entry.acronym, entry.expansion, entry.sentence_id, text.slice(entry.span[0], entry.span[1])])
|
|
84
|
+
expect(entries).toEqual([
|
|
85
|
+
["HBM", "High Bandwidth Memory", "S2:s1", "High Bandwidth Memory (HBM)"],
|
|
86
|
+
["TSV", "Through Silicon Via", "S2:s2", "TSV (Through Silicon Via)"],
|
|
87
|
+
])
|
|
88
|
+
expect(JSON.parse(readFileSync(join(dir, "meta.json"), "utf8")).kind).toBe("document")
|
|
89
|
+
expect(existsSync(join(dir, "source.html"))).toBe(false)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test("#given pages #when written #then pages are joined by a form feed and no sentence crosses a page", () => {
|
|
93
|
+
const sources = tempDir()
|
|
94
|
+
const { dir } = writeSnapshot(sources, "S7", { path: "report.pdf", pages: ["First page ends here", "second page starts here."], extractor: "extractor:pdftotext" })
|
|
95
|
+
expect(readFileSync(join(dir, "text.txt"), "utf8")).toBe("First page ends here\fsecond page starts here.")
|
|
96
|
+
expect(readSentences(dir).map((row) => row.text)).toEqual(["First page ends here", "second page starts here."])
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
test("#given Korean text #when written #then the sentence index covers it", () => {
|
|
100
|
+
const sources = tempDir()
|
|
101
|
+
const { dir } = writeSnapshot(sources, "S9", { path: "S9.txt", text: S9_TEXT, extractor: "tool:source-snapshot@1" })
|
|
102
|
+
expect(readSentences(dir).map((row) => row.sid)).toEqual(["S9:s1"])
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test("#given an invalid extractor, source id, or a different text over an existing snapshot #when written #then it throws", () => {
|
|
106
|
+
const sources = tempDir()
|
|
107
|
+
expect(() => writeSnapshot(sources, "S1", { path: "a.txt", text: "x", extractor: "guess" })).toThrow()
|
|
108
|
+
expect(() => writeSnapshot(sources, "source1", { path: "a.txt", text: "x", extractor: "model" })).toThrow()
|
|
109
|
+
writeSnapshot(sources, "S1", { path: "a.txt", text: "first", extractor: "model" })
|
|
110
|
+
expect(() => writeSnapshot(sources, "S1", { path: "a.txt", text: "first", extractor: "model" })).not.toThrow()
|
|
111
|
+
expect(() => writeSnapshot(sources, "S1", { path: "a.txt", text: "second", extractor: "model" })).toThrow(/snapshot_exists/)
|
|
112
|
+
})
|
|
113
|
+
})
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// gen-legacy-dev.mjs - regenerates ledger-legacy-dev.json with dev's own pre-registry writer, so the
|
|
2
|
+
// dimension 31 tests load every value shape dev could write, not hand-picked rows.
|
|
3
|
+
// Pinned dev commit: 99220c4e95f9ef3a667834486f2caa03873deeab (origin/dev when the fixture was generated).
|
|
4
|
+
// Usage: node gen-legacy-dev.mjs [dev-sha]. Needs git and the commit in the local clone; node: builtins only.
|
|
5
|
+
|
|
6
|
+
import { execFileSync } from "node:child_process"
|
|
7
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"
|
|
8
|
+
import { tmpdir } from "node:os"
|
|
9
|
+
import { dirname, join } from "node:path"
|
|
10
|
+
import { fileURLToPath, pathToFileURL } from "node:url"
|
|
11
|
+
|
|
12
|
+
const DEV_SHA = process.argv[2] ?? "99220c4e95f9ef3a667834486f2caa03873deeab"
|
|
13
|
+
const SCRIPTS = "packages/shared-skills/skills/ulw-research/scripts"
|
|
14
|
+
// fact-ledger.mjs and every module it imports at the pinned commit.
|
|
15
|
+
const WRITER_FILES = ["fact-ledger.mjs", "fact-ledger-kinds.mjs", "fact-ledger-schema.mjs", "cli-support.mjs", "entry-guard.mjs"]
|
|
16
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
17
|
+
const OUT = join(HERE, "ledger-legacy-dev.json")
|
|
18
|
+
|
|
19
|
+
const at = { artifact: "sources/S1/text.txt", line: 2, char_start: 0, char_end: 30 }
|
|
20
|
+
const sha = "a".repeat(64)
|
|
21
|
+
const base = { extracted_by: "fact add --find", observed_at: "2026-10-20T08:00:00Z" }
|
|
22
|
+
|
|
23
|
+
function measured(label, value, unit) {
|
|
24
|
+
const raw = `${value} ${unit}`
|
|
25
|
+
return { ...base, kind: "measured", label, value, unit, raw, quote: `The figure is ${raw}.`, value_span: [14, 14 + value.length], source_id: "S1", locator: at, snapshot_sha256: sha }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function assumed(label, value, unit, exact) {
|
|
29
|
+
return { ...base, kind: "assumed", label, value, unit, exact, raw: "", quote: "", extracted_by: "fact add --assumed", rationale: "planning assumption from the brief" }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function legacy(kind, label, fields) {
|
|
33
|
+
return { ...base, kind, label, raw: "", quote: "", extracted_by: "fact derive", ...fields }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Every kind and every value shape dev's addRow accepts: alias units (English and Korean count and
|
|
37
|
+
// currency words, written as escapes so this file stays ASCII), leading zeros, negative values,
|
|
38
|
+
// assumed rows with unreduced or zero-padded exacts, and a legacy derived and converted row. The
|
|
39
|
+
// last three are shapes dev accepted that the strict dimensions do not (empty metadata, an operation
|
|
40
|
+
// on a source row, an extra key in exact): they must load and be refused only as operands.
|
|
41
|
+
const SEEDS = [
|
|
42
|
+
measured("north-span", "12", "kilometers"),
|
|
43
|
+
measured("south-span", "8.5", "kilometers"),
|
|
44
|
+
measured("share-online", "41.2", "per cent"),
|
|
45
|
+
measured("share-gap", "1.5", "pp"),
|
|
46
|
+
measured("staff-count", "320", "\uBA85"),
|
|
47
|
+
measured("unit-price", "750", "\uCC9C\uC6D0"),
|
|
48
|
+
measured("padded-span", "007", "km"),
|
|
49
|
+
measured("net-change", "-2.5", "per cent"),
|
|
50
|
+
{ ...base, kind: "quote", label: "quote-claim", raw: "grew fast", quote: "The market grew fast.", value_span: [11, 20], source_id: "S1", locator: at, snapshot_sha256: sha },
|
|
51
|
+
assumed("growth-assumption", "3", "%", null),
|
|
52
|
+
assumed("whole-ratio", "1", "%", { num: "2", den: "2" }),
|
|
53
|
+
assumed("quarter-ratio", "2.5", "kilometers", { num: "10", den: "4" }),
|
|
54
|
+
assumed("padded-exact", "7", "km", { num: "007", den: "01" }),
|
|
55
|
+
assumed("negative-exact", "-2.5", "per cent", { num: "-10", den: "04" }),
|
|
56
|
+
legacy("derived", "total-span", { value: "20.5", unit: "kilometers", exact: { num: "82", den: "4" }, operation: "sum", operands: ["F1", "F2"], derived_from: ["F1", "F2"], formula: "F1 + F2" }),
|
|
57
|
+
legacy("converted", "span-metres", { value: "12000", unit: "m", exact: { num: "24000", den: "2" }, operation: "convert", operands: ["F1"], derived_from: ["F1"], formula: "F1 * 1000", factor: "1000" }),
|
|
58
|
+
{ ...measured("blank-entity", "5", "km"), entity: "" },
|
|
59
|
+
{ ...measured("tagged-sum", "6", "km"), operation: "sum" },
|
|
60
|
+
assumed("keyed-exact", "3", "km", { num: "3", den: "1", note: "from the brief" }),
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
/** JSON with every non-ASCII character as a \u escape, tab-indented like the other fixtures. */
|
|
64
|
+
function asciiJson(value) {
|
|
65
|
+
return `${JSON.stringify(value, null, "\t").replace(/[\u0080-\uffff]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`)}\n`
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function main() {
|
|
69
|
+
const root = execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd: HERE, encoding: "utf8" }).trim()
|
|
70
|
+
const work = mkdtempSync(join(tmpdir(), "gen-legacy-dev-"))
|
|
71
|
+
try {
|
|
72
|
+
mkdirSync(join(work, "scripts"))
|
|
73
|
+
for (const file of WRITER_FILES) {
|
|
74
|
+
const source = execFileSync("git", ["show", `${DEV_SHA}:${SCRIPTS}/${file}`], { cwd: root, encoding: "utf8" })
|
|
75
|
+
writeFileSync(join(work, "scripts", file), source)
|
|
76
|
+
}
|
|
77
|
+
const dev = await import(pathToFileURL(join(work, "scripts", "fact-ledger.mjs")).href)
|
|
78
|
+
const ledger = dev.emptyLedger()
|
|
79
|
+
for (const seed of SEEDS) dev.addRow(ledger, seed)
|
|
80
|
+
const errors = dev.validateLedger(ledger)
|
|
81
|
+
if (errors.length > 0) throw new Error(`dev's validateLedger rejected the generated ledger: ${errors.join("; ")}`)
|
|
82
|
+
writeFileSync(OUT, asciiJson(ledger))
|
|
83
|
+
process.stdout.write(`wrote ${OUT} (${ledger.rows.length} rows, dev ${DEV_SHA})\n`)
|
|
84
|
+
} finally {
|
|
85
|
+
rmSync(work, { recursive: true, force: true })
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await main()
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"rows": [
|
|
4
|
+
{
|
|
5
|
+
"fact_id": "F12",
|
|
6
|
+
"kind": "measured",
|
|
7
|
+
"raw": "320 m",
|
|
8
|
+
"quote": "The bridge has a main span of up to 320 m.",
|
|
9
|
+
"extracted_by": "fixture",
|
|
10
|
+
"observed_at": "2026-10-23T00:00:00Z",
|
|
11
|
+
"label": "span_len",
|
|
12
|
+
"value": "320",
|
|
13
|
+
"unit": "m",
|
|
14
|
+
"value_span": null,
|
|
15
|
+
"source_id": "S4",
|
|
16
|
+
"locator": {
|
|
17
|
+
"artifact": "sources/S4/text.txt",
|
|
18
|
+
"char_start": 0,
|
|
19
|
+
"char_end": 42
|
|
20
|
+
},
|
|
21
|
+
"locate_hint": null,
|
|
22
|
+
"snapshot_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
23
|
+
"valid_at": null,
|
|
24
|
+
"qualifier": "upper_inclusive",
|
|
25
|
+
"entity": null,
|
|
26
|
+
"sentence_id": null,
|
|
27
|
+
"observation_id": null,
|
|
28
|
+
"fuzzy": false,
|
|
29
|
+
"outside_floor": false,
|
|
30
|
+
"population": null,
|
|
31
|
+
"missing_rule": null,
|
|
32
|
+
"accounting_basis": null,
|
|
33
|
+
"snapshot_window": null,
|
|
34
|
+
"revision": null,
|
|
35
|
+
"exact": {
|
|
36
|
+
"num": "320",
|
|
37
|
+
"den": "1"
|
|
38
|
+
},
|
|
39
|
+
"render": null,
|
|
40
|
+
"operation": null,
|
|
41
|
+
"operands": [],
|
|
42
|
+
"derived_from": [],
|
|
43
|
+
"formula": null,
|
|
44
|
+
"factor": null,
|
|
45
|
+
"rationale": null,
|
|
46
|
+
"status": "active"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"fact_id": "F13",
|
|
50
|
+
"kind": "measured",
|
|
51
|
+
"raw": "15.2%",
|
|
52
|
+
"quote": "The rate reached 15.2% in May.",
|
|
53
|
+
"extracted_by": "fixture",
|
|
54
|
+
"observed_at": "2026-10-23T00:00:00Z",
|
|
55
|
+
"label": "rate",
|
|
56
|
+
"value": "15.2",
|
|
57
|
+
"unit": "%",
|
|
58
|
+
"value_span": null,
|
|
59
|
+
"source_id": "S4",
|
|
60
|
+
"locator": {
|
|
61
|
+
"artifact": "sources/S4/text.txt",
|
|
62
|
+
"char_start": 43,
|
|
63
|
+
"char_end": 73
|
|
64
|
+
},
|
|
65
|
+
"locate_hint": null,
|
|
66
|
+
"snapshot_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
67
|
+
"valid_at": null,
|
|
68
|
+
"qualifier": null,
|
|
69
|
+
"entity": null,
|
|
70
|
+
"sentence_id": null,
|
|
71
|
+
"observation_id": null,
|
|
72
|
+
"fuzzy": false,
|
|
73
|
+
"outside_floor": false,
|
|
74
|
+
"population": null,
|
|
75
|
+
"missing_rule": null,
|
|
76
|
+
"accounting_basis": null,
|
|
77
|
+
"snapshot_window": null,
|
|
78
|
+
"revision": null,
|
|
79
|
+
"exact": {
|
|
80
|
+
"num": "76",
|
|
81
|
+
"den": "5"
|
|
82
|
+
},
|
|
83
|
+
"render": null,
|
|
84
|
+
"operation": null,
|
|
85
|
+
"operands": [],
|
|
86
|
+
"derived_from": [],
|
|
87
|
+
"formula": null,
|
|
88
|
+
"factor": null,
|
|
89
|
+
"rationale": null,
|
|
90
|
+
"status": "active"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"fact_id": "Q7",
|
|
94
|
+
"kind": "quote",
|
|
95
|
+
"raw": "we estimate completion no earlier than 2027",
|
|
96
|
+
"quote": "we estimate completion no earlier than 2027",
|
|
97
|
+
"extracted_by": "fixture",
|
|
98
|
+
"observed_at": "2026-10-23T00:00:00Z",
|
|
99
|
+
"label": "completion",
|
|
100
|
+
"value": null,
|
|
101
|
+
"unit": null,
|
|
102
|
+
"value_span": null,
|
|
103
|
+
"source_id": "S4",
|
|
104
|
+
"locator": {
|
|
105
|
+
"artifact": "sources/S4/text.txt",
|
|
106
|
+
"char_start": 74,
|
|
107
|
+
"char_end": 117
|
|
108
|
+
},
|
|
109
|
+
"locate_hint": null,
|
|
110
|
+
"snapshot_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
111
|
+
"valid_at": null,
|
|
112
|
+
"qualifier": null,
|
|
113
|
+
"entity": null,
|
|
114
|
+
"sentence_id": null,
|
|
115
|
+
"observation_id": null,
|
|
116
|
+
"fuzzy": false,
|
|
117
|
+
"outside_floor": false,
|
|
118
|
+
"population": null,
|
|
119
|
+
"missing_rule": null,
|
|
120
|
+
"accounting_basis": null,
|
|
121
|
+
"snapshot_window": null,
|
|
122
|
+
"revision": null,
|
|
123
|
+
"exact": null,
|
|
124
|
+
"render": null,
|
|
125
|
+
"operation": null,
|
|
126
|
+
"operands": [],
|
|
127
|
+
"derived_from": [],
|
|
128
|
+
"formula": null,
|
|
129
|
+
"factor": null,
|
|
130
|
+
"rationale": null,
|
|
131
|
+
"status": "active"
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"fact_id": "F21",
|
|
135
|
+
"kind": "measured",
|
|
136
|
+
"raw": "4\uc870 2,000\uc5b5 \uc6d0",
|
|
137
|
+
"quote": "3\ubd84\uae30 HBM \ub9e4\ucd9c\uc740 4\uc870 2,000\uc5b5 \uc6d0\uc744 \uae30\ub85d\ud588\ub2e4.",
|
|
138
|
+
"extracted_by": "fixture",
|
|
139
|
+
"observed_at": "2026-10-23T00:00:00Z",
|
|
140
|
+
"label": "hbm_rev",
|
|
141
|
+
"value": "4200000000000",
|
|
142
|
+
"unit": "KRW",
|
|
143
|
+
"value_span": null,
|
|
144
|
+
"source_id": "S5",
|
|
145
|
+
"locator": {
|
|
146
|
+
"artifact": "sources/S5/text.txt",
|
|
147
|
+
"char_start": 0,
|
|
148
|
+
"char_end": 30
|
|
149
|
+
},
|
|
150
|
+
"locate_hint": null,
|
|
151
|
+
"snapshot_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
152
|
+
"valid_at": "2026 Q3",
|
|
153
|
+
"qualifier": null,
|
|
154
|
+
"entity": "HBM3E",
|
|
155
|
+
"sentence_id": null,
|
|
156
|
+
"observation_id": null,
|
|
157
|
+
"fuzzy": false,
|
|
158
|
+
"outside_floor": false,
|
|
159
|
+
"population": null,
|
|
160
|
+
"missing_rule": null,
|
|
161
|
+
"accounting_basis": null,
|
|
162
|
+
"snapshot_window": null,
|
|
163
|
+
"revision": null,
|
|
164
|
+
"exact": {
|
|
165
|
+
"num": "4200000000000",
|
|
166
|
+
"den": "1"
|
|
167
|
+
},
|
|
168
|
+
"render": null,
|
|
169
|
+
"operation": null,
|
|
170
|
+
"operands": [],
|
|
171
|
+
"derived_from": [],
|
|
172
|
+
"formula": null,
|
|
173
|
+
"factor": null,
|
|
174
|
+
"rationale": null,
|
|
175
|
+
"status": "active"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"fact_id": "F40",
|
|
179
|
+
"kind": "converted",
|
|
180
|
+
"raw": "320000 mm",
|
|
181
|
+
"quote": "The bridge has a main span of up to 320 m.",
|
|
182
|
+
"extracted_by": "fixture",
|
|
183
|
+
"observed_at": "2026-10-23T00:00:00Z",
|
|
184
|
+
"label": "span_mm",
|
|
185
|
+
"value": "320000",
|
|
186
|
+
"unit": "mm",
|
|
187
|
+
"value_span": null,
|
|
188
|
+
"source_id": null,
|
|
189
|
+
"locator": null,
|
|
190
|
+
"locate_hint": null,
|
|
191
|
+
"snapshot_sha256": null,
|
|
192
|
+
"valid_at": null,
|
|
193
|
+
"qualifier": null,
|
|
194
|
+
"entity": null,
|
|
195
|
+
"sentence_id": null,
|
|
196
|
+
"observation_id": null,
|
|
197
|
+
"fuzzy": false,
|
|
198
|
+
"outside_floor": false,
|
|
199
|
+
"population": null,
|
|
200
|
+
"missing_rule": null,
|
|
201
|
+
"accounting_basis": null,
|
|
202
|
+
"snapshot_window": null,
|
|
203
|
+
"revision": null,
|
|
204
|
+
"exact": {
|
|
205
|
+
"num": "320000",
|
|
206
|
+
"den": "1"
|
|
207
|
+
},
|
|
208
|
+
"render": null,
|
|
209
|
+
"operation": "convert",
|
|
210
|
+
"operands": [
|
|
211
|
+
"F12"
|
|
212
|
+
],
|
|
213
|
+
"derived_from": [
|
|
214
|
+
"F12"
|
|
215
|
+
],
|
|
216
|
+
"formula": null,
|
|
217
|
+
"factor": "1000",
|
|
218
|
+
"rationale": null,
|
|
219
|
+
"status": "active"
|
|
220
|
+
}
|
|
221
|
+
],
|
|
222
|
+
"events": []
|
|
223
|
+
}
|