oh-my-opencode 5.1.48 → 5.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/refusal-rule.d.ts +7 -0
- package/dist/cli/index.js +1194 -752
- package/dist/cli/service-auth/accounts-gate.d.ts +11 -0
- package/dist/cli/service-auth/commands.d.ts +2 -4
- package/dist/cli/service-auth/index.d.ts +1 -1
- package/dist/cli/service-auth/keystore.d.ts +26 -2
- package/dist/cli/service-auth/protocol.d.ts +7 -1
- package/dist/cli/service-auth/session.d.ts +6 -0
- package/dist/cli/service-auth/signed-out-marker.d.ts +11 -0
- package/dist/cli-node/index.js +1194 -752
- package/dist/config/schema/hooks.d.ts +1 -0
- package/dist/create-hooks.d.ts +1 -0
- package/dist/features/boulder-state/storage.d.ts +1 -1
- package/dist/features/boulder-state/types.d.ts +1 -1
- package/dist/hooks/atlas/idle-continuation.d.ts +2 -1
- package/dist/hooks/atlas/types.d.ts +9 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/momus-review-persistence/hook.d.ts +19 -0
- package/dist/hooks/momus-review-persistence/index.d.ts +1 -0
- package/dist/hooks/momus-review-persistence/review-resolution.d.ts +20 -0
- package/dist/hooks/prometheus-md-only/constants.d.ts +2 -0
- package/dist/hooks/prometheus-md-only/fixtures/denial-log.d.ts +1 -0
- package/dist/hooks/prometheus-md-only/fixtures/guarded-hashline.d.ts +1 -0
- package/dist/hooks/prometheus-md-only/fixtures/invoke-before.d.ts +7 -0
- package/dist/hooks/prometheus-md-only/fixtures/opencode-patch-parser.d.ts +2 -0
- package/dist/hooks/prometheus-md-only/hook.d.ts +5 -1
- package/dist/hooks/prometheus-md-only/path-policy.d.ts +2 -9
- package/dist/hooks/prometheus-md-only/vendor/opencode-patch-parser.d.ts +22 -0
- package/dist/index.js +2322 -1045
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
- package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +2 -1
- package/dist/shared/posthog-activity-state.d.ts +1 -0
- package/dist/shared/posthog.d.ts +1 -2
- package/dist/shared/resolve-file-write-path.d.ts +6 -0
- package/dist/skills/ast-grep/README.md +1 -1
- package/dist/skills/ast-grep/install.ps1 +5 -3
- package/dist/skills/ast-grep/install.sh +4 -5
- package/dist/skills/ast-grep/references/install.md +2 -2
- package/dist/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
- package/dist/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/dist/skills/coding-agent-sessions/SKILL.md +11 -11
- package/dist/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/dist/skills/coding-agent-sessions/references/codex.md +4 -4
- package/dist/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/dist/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/dist/skills/frontend/ATTRIBUTION.md +1 -1
- package/dist/skills/frontend/SKILL.md +34 -12
- package/dist/skills/frontend/references/design/README.md +2 -2
- package/dist/skills/frontend/references/design/_INDEX.md +2 -1
- package/dist/skills/frontend/references/design/component-catalogs.md +14 -10
- package/dist/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/dist/skills/frontend/references/design/grounding.md +29 -0
- package/dist/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/dist/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/dist/skills/ultimate-browsing/SKILL.md +5 -4
- package/dist/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/dist/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/dist/skills/ultimate-browsing/engine/summary.py +1 -1
- package/dist/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/dist/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/dist/skills/ulw-research/AGENTS.md +8 -4
- package/dist/skills/ulw-research/SKILL.md +7 -316
- package/dist/skills/ulw-research/references/deliverable-phase.md +23 -21
- package/dist/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/dist/skills/ulw-research/references/fact-ledger.md +42 -8
- package/dist/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/dist/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/dist/skills/ulw-research/references/report-gates.md +12 -5
- package/dist/skills/ulw-research/references/research-collection.md +195 -0
- package/dist/skills/ulw-research/references/research-delivery.md +123 -0
- package/dist/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/dist/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
- package/dist/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
- package/dist/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
- package/dist/skills/ulw-research/scripts/contracts.mjs +13 -4
- package/dist/skills/ulw-research/scripts/contracts.test.ts +6 -110
- package/dist/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
- package/dist/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
- package/dist/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
- package/dist/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/dist/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
- package/dist/skills/ulw-research/scripts/copy-returns-polish.test.ts +4 -3
- package/dist/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/dist/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/dist/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
- package/dist/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
- package/dist/skills/ulw-research/scripts/entities.mjs +13 -4
- package/dist/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
- package/dist/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
- package/dist/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
- package/dist/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
- package/dist/skills/ulw-research/scripts/fact-commands.mjs +35 -16
- package/dist/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
- package/dist/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/dist/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
- package/dist/skills/ulw-research/scripts/fact-fill.mjs +6 -4
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
- package/dist/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
- package/dist/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/dist/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
- package/dist/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
- package/dist/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
- package/dist/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/dist/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/dist/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/dist/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/dist/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
- package/dist/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/dist/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/dist/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/dist/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/dist/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/dist/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/dist/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
- package/dist/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/dist/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
- package/dist/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/dist/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/dist/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/dist/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/dist/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
- package/dist/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/dist/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
- package/dist/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/dist/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/dist/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
- package/dist/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/dist/skills/ulw-research/scripts/numerals.test.ts +3 -0
- package/dist/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/dist/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
- package/dist/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/dist/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
- package/dist/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/dist/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/dist/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
- package/dist/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
- package/dist/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/dist/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/dist/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
- package/dist/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
- package/dist/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
- package/dist/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
- package/dist/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
- package/dist/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/dist/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/dist/skills/ulw-research/scripts/report-tools.test.ts +5 -800
- package/dist/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
- package/dist/skills/ulw-research/scripts/source-commands.mjs +79 -26
- package/dist/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
- package/dist/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/dist/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/dist/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
- package/dist/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/dist/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/dist/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
- package/dist/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
- package/dist/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
- package/dist/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
- package/dist/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
- package/dist/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
- package/dist/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
- package/dist/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
- package/dist/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
- package/dist/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
- package/dist/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
- package/dist/skills/ulw-research/scripts/units.mjs +14 -3
- package/dist/skills/ulw-research/scripts/units.test.ts +10 -0
- package/dist/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
- package/dist/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
- package/dist/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
- package/dist/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
- package/dist/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
- package/dist/tools/delegate-task/executor-types.d.ts +1 -0
- package/dist/tools/delegate-task/sync-session-existence.d.ts +4 -0
- package/dist/tools/delegate-task/types.d.ts +1 -0
- package/dist/tools/skill/description-formatter.d.ts +2 -1
- package/dist/tools/skill/types.d.ts +6 -0
- package/dist/tui.js +131 -33
- package/package.json +18 -15
- package/packages/git-bash-mcp/package.json +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/README.md +1 -1
- 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/lcx/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/SKILL.md +7 -7
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/agents/openai.yaml +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 +2 -2
- package/packages/omo-codex/plugin/components/telemetry/README.md +2 -2
- package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +52 -15
- package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +52 -19
- 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/telemetry/src/codex-hook.ts +2 -9
- package/packages/omo-codex/plugin/components/telemetry/src/posthog.ts +4 -13
- package/packages/omo-codex/plugin/components/telemetry/src/product-identity.ts +0 -2
- package/packages/omo-codex/plugin/components/telemetry/test/codex-hook-diagnostics.test.ts +0 -3
- package/packages/omo-codex/plugin/components/telemetry/test/codex-hook.test.ts +3 -8
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +3 -0
- 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/ultrawork/src/directive-content.ts +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/directive.md +3 -0
- 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 +16 -16
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/shared/package.json +1 -1
- package/packages/omo-codex/plugin/skills/ast-grep/README.md +1 -1
- package/packages/omo-codex/plugin/skills/ast-grep/install.ps1 +5 -3
- package/packages/omo-codex/plugin/skills/ast-grep/install.sh +4 -5
- package/packages/omo-codex/plugin/skills/ast-grep/references/install.md +2 -2
- package/packages/omo-codex/plugin/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +11 -11
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/codex.md +4 -4
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +1 -1
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +34 -12
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +2 -1
- package/packages/omo-codex/plugin/skills/frontend/references/design/component-catalogs.md +14 -10
- package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/packages/omo-codex/plugin/skills/frontend/references/design/grounding.md +29 -0
- package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +7 -7
- package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +5 -4
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/summary.py +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +3 -0
- package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +8 -4
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +7 -316
- package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +23 -21
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +42 -8
- package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +12 -5
- package/packages/omo-codex/plugin/skills/ulw-research/references/research-collection.md +195 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/research-delivery.md +123 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +13 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/entities.mjs +13 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +35 -16
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +6 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +79 -26
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +14 -3
- package/packages/omo-codex/plugin/test/payload-equivalence.test.mjs +6 -4
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -3
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +338 -237
- package/packages/prompts-core/prompts/ultrawork/codex.md +3 -0
- package/packages/shared-skills/skill-source-filter.mjs +6 -2
- package/packages/shared-skills/skills/ast-grep/README.md +1 -1
- package/packages/shared-skills/skills/ast-grep/install.ps1 +5 -3
- package/packages/shared-skills/skills/ast-grep/install.sh +4 -5
- package/packages/shared-skills/skills/ast-grep/references/install.md +2 -2
- package/packages/shared-skills/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/shared-skills/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +11 -11
- package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/packages/shared-skills/skills/coding-agent-sessions/references/codex.md +4 -4
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +1 -1
- package/packages/shared-skills/skills/frontend/SKILL.md +34 -12
- package/packages/shared-skills/skills/frontend/references/design/README.md +2 -2
- package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +2 -1
- package/packages/shared-skills/skills/frontend/references/design/component-catalogs.md +14 -10
- package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/packages/shared-skills/skills/frontend/references/design/grounding.md +29 -0
- package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +5 -4
- package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/packages/shared-skills/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/packages/shared-skills/skills/ultimate-browsing/engine/summary.py +1 -1
- package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/packages/shared-skills/skills/ulw-research/AGENTS.md +8 -4
- package/packages/shared-skills/skills/ulw-research/SKILL.md +7 -316
- package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +23 -21
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +42 -8
- package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/packages/shared-skills/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/packages/shared-skills/skills/ulw-research/references/report-gates.md +12 -5
- package/packages/shared-skills/skills/ulw-research/references/research-collection.md +195 -0
- package/packages/shared-skills/skills/ulw-research/references/research-delivery.md +123 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
- package/packages/shared-skills/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
- package/packages/shared-skills/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +13 -4
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +6 -110
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
- package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-polish.test.ts +4 -3
- package/packages/shared-skills/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/packages/shared-skills/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
- package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
- package/packages/shared-skills/skills/ulw-research/scripts/entities.mjs +13 -4
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +35 -16
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
- package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +6 -4
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
- package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/packages/shared-skills/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
- package/packages/shared-skills/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +3 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/packages/shared-skills/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
- package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
- package/packages/shared-skills/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +5 -800
- package/packages/shared-skills/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +79 -26
- package/packages/shared-skills/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
- package/packages/shared-skills/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
- package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +14 -3
- package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +10 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/amp_redirect_stub.html +0 -7
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/search_interstitial.html +0 -19
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_available.json +0 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_snapshot.html +0 -1128
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_fetch_chain.py +0 -77
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +0 -86
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +0 -279
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate.py +0 -252
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate_validators.py +0 -78
|
@@ -9,6 +9,7 @@ import { finalReading, rewriteParticle } from "./particles-ko.mjs"
|
|
|
9
9
|
import { failureCount, markReplaced, sentenceKey } from "./fidelity-state.mjs"
|
|
10
10
|
import { findPlaceholders, lead, qualifiedText, renderBlockquote, renderQuote, renderValueHtml, subSpan, valueParts } from "./placeholder.mjs"
|
|
11
11
|
import { splitSentences } from "./sentence-split.mjs"
|
|
12
|
+
import { CLAIM_KINDS } from "./claim-kinds.mjs"
|
|
12
13
|
|
|
13
14
|
/** The output formats fill renders. */
|
|
14
15
|
export const FILL_FORMATS = Object.freeze(["html", "md"])
|
|
@@ -24,7 +25,7 @@ function resolveValue(row, parsed, format) {
|
|
|
24
25
|
const { valueText, unitText } = valueParts(row)
|
|
25
26
|
const text = { full: row.raw, value: valueText, unit: unitText, q: qualifiedText(row) }[parsed.mode]
|
|
26
27
|
if (text === null) return { code: "quote_anchor_unresolved", message: `${row.fact_id} has qualifier ${row.qualifier}, but its quote holds no qualifier beside "${row.raw}"` }
|
|
27
|
-
return { row, parsed, visible: text, rendered: format === "html" ? renderValueHtml(row, text) : text }
|
|
28
|
+
return { row, parsed, visible: text, rendered: format === "html" ? renderValueHtml(row, text, { mode: parsed.mode, claimKind: parsed.claimKind ?? null }) : text }
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
function resolveQuote(row, parsed, format) {
|
|
@@ -33,17 +34,18 @@ function resolveQuote(row, parsed, format) {
|
|
|
33
34
|
if (selection === null) {
|
|
34
35
|
return { code: "quote_anchor_unresolved", message: `from "${parsed.from}" and to "${parsed.to}" must each occur exactly once in ${row.fact_id}'s quote, in that order` }
|
|
35
36
|
}
|
|
36
|
-
const rendered = renderQuote(row, parsed.selector, selection.text, format)
|
|
37
|
+
const rendered = renderQuote(row, parsed.selector, selection.text, format, parsed.claimKind)
|
|
37
38
|
return { row, parsed, visible: `[${row.source_id}]`, rendered, selector: parsed.selector, quote_span: [selection.start, selection.end] }
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
function resolve(found, rows, format) {
|
|
41
42
|
const { parsed } = found
|
|
42
|
-
if (parsed.error) return { code: "unresolved_placeholder", message: parsed.error }
|
|
43
|
+
if (parsed.error) return { code: "unresolved_placeholder", message: `${parsed.error}; for literal opening braces, write {{ instead of {{ (including in code spans)` }
|
|
43
44
|
const row = rows.get(parsed.id)
|
|
44
45
|
if (row === undefined) return { code: "unresolved_placeholder", message: `${parsed.id} is not in the fact ledger` }
|
|
45
46
|
if (row.status !== "active") return { code: "unresolved_placeholder", message: `${parsed.id} is ${row.status}; cite its replacement` }
|
|
46
47
|
if (row.label !== parsed.label) return { code: "pointer_label_mismatch", message: `${parsed.id} has label "${row.label}", not "${parsed.label}"` }
|
|
48
|
+
if (parsed.claimKind && !CLAIM_KINDS[parsed.claimKind].rowKinds.includes(row.kind)) return { code: CLAIM_KINDS[parsed.claimKind].defect, message: `${parsed.claimKind} does not accept a ${row.kind} row (${parsed.id})` }
|
|
47
49
|
return parsed.kind === "Q" ? resolveQuote(row, parsed, format) : resolveValue(row, parsed, format)
|
|
48
50
|
}
|
|
49
51
|
|
|
@@ -146,7 +148,7 @@ function assemble(text, parts, context) {
|
|
|
146
148
|
flush(part.start)
|
|
147
149
|
const start = context.out.length
|
|
148
150
|
if (part.parsed) {
|
|
149
|
-
const extra = part.selector === undefined ? {} : { selector: part.selector, quote_span: part.quote_span }
|
|
151
|
+
const extra = { ...(part.selector === undefined ? {} : { selector: part.selector, quote_span: part.quote_span }), ...(part.parsed.claimKind ? { claimKind: part.parsed.claimKind } : {}), ...(part.parsed.mode === "value" ? { mode: "value" } : {}) }
|
|
150
152
|
context.fillMap.push({ token: part.token, fact_id: part.row.fact_id, start, end: start + part.rendered.length, rendered: part.rendered, ...extra })
|
|
151
153
|
} else if (part.rendered !== "") {
|
|
152
154
|
context.fillMap.push({ token: null, fact_id: part.source.fact_id, replaced: part.key, start, end: start + part.rendered.length, rendered: part.rendered })
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// per-kind choices (id prefix, measured exact) from it, and the per-kind table in
|
|
4
4
|
// references/fact-ledger-grammar.md is rendered from it (`node fact-ledger-kinds.mjs --render-grammar`).
|
|
5
5
|
// Also the value primitives the schema shares. Pure functions with no IO; node: builtins only.
|
|
6
|
-
|
|
7
6
|
import { isCliEntry } from "./entry-guard.mjs"
|
|
8
7
|
import { canonicalUnit, inputScale, RuleError } from "./unit-algebra.mjs"
|
|
9
8
|
import { metadataErrors } from "./unit-metadata.mjs"
|
|
@@ -15,7 +14,6 @@ export const DECIMAL = /^-?\d+(?:\.\d+)?$/
|
|
|
15
14
|
const INTEGER = /^-?\d+$/
|
|
16
15
|
const SHA256 = /^[0-9a-f]{64}$/
|
|
17
16
|
const SOURCE_ID = /^S\d+$/
|
|
18
|
-
|
|
19
17
|
export const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value)
|
|
20
18
|
export const isString = (value) => typeof value === "string"
|
|
21
19
|
export const isNonEmpty = (value) => typeof value === "string" && value.length > 0
|
|
@@ -48,6 +46,7 @@ export function locatorErrors(locator, at) {
|
|
|
48
46
|
const errors = []
|
|
49
47
|
if (!isObject(locator)) return errors
|
|
50
48
|
if (!isNonEmpty(locator.artifact)) errors.push(`${at}.artifact must be a non-empty string`)
|
|
49
|
+
if (locator.unit !== undefined && locator.unit !== "bytes") errors.push(`${at}.unit must be bytes`)
|
|
51
50
|
if (!(isCount(locator.char_start) && isCount(locator.char_end) && locator.char_start <= locator.char_end)) errors.push(`${at}.char_start/char_end must be integers with start <= end`)
|
|
52
51
|
for (const key of ["page", "line"]) {
|
|
53
52
|
if (locator[key] !== undefined && !(Number.isInteger(locator[key]) && locator[key] >= 1)) errors.push(`${at}.${key} must be an integer >= 1`)
|
|
@@ -87,6 +86,7 @@ export const RULES = Object.freeze({
|
|
|
87
86
|
rational_or_null: { cell: "rational or null", says: "null or { num: integer string, den: positive integer string }", test: orNull(isRational) },
|
|
88
87
|
by_derive: { cell: "reduced rational", says: "a reduced { num: integer string, den: positive integer string }", test: isRational },
|
|
89
88
|
context_by_derive: { cell: "written by derive (object or null)", says: "null or an object", test: orNull(isObject) },
|
|
89
|
+
source_context: { cell: "quote context or null (legacy)", says: "null or { prefix, suffix } up to 32 characters each", test: orNull((v) => isObject(v) && Object.keys(v).sort().join(",") === "prefix,suffix" && [v.prefix, v.suffix].every((s) => isString(s) && [...s].length <= 32)) },
|
|
90
90
|
derived_operation: { cell: "sum/diff/growth/share/ratio/expr", says: "a derived operation", test: (v) => Object.hasOwn(OPERATION_RULES, v) && OPERATION_RULES[v].kind === "derived" },
|
|
91
91
|
convert_operation: { cell: "`convert`", says: "convert", test: (v) => Object.hasOwn(OPERATION_RULES, v) && OPERATION_RULES[v].kind === "converted" },
|
|
92
92
|
precision: { cell: "`{ places, sigfigs }`", says: "nonnegative places and positive sigfigs", test: (v) => isObject(v) && Object.keys(v).sort().join(",") === "places,sigfigs" && Number.isSafeInteger(v.places) && v.places >= 0 && Number.isSafeInteger(v.sigfigs) && v.sigfigs >= 1 },
|
|
@@ -101,14 +101,13 @@ const unsourced = { source_id: "source_or_null", locator: "locator_or_null", sna
|
|
|
101
101
|
const row = (cells) => Object.freeze(Object.fromEntries(KIND_COLUMNS.map((column) => [column, cells[column] ?? (["precision", "compatibility"].includes(column) && cells.operation !== "null" ? column : "null")])))
|
|
102
102
|
|
|
103
103
|
export const KIND_RULES = Object.freeze({
|
|
104
|
-
measured: row({ fact_id: "f_id", value: "decimal", ...sourced, rationale: "string_or_null", exact: "from_value", context: "
|
|
105
|
-
quote: row({ fact_id: "q_id", value: "null", ...sourced, rationale: "string_or_null", exact: "null", context: "
|
|
104
|
+
measured: row({ fact_id: "f_id", value: "decimal", ...sourced, rationale: "string_or_null", exact: "from_value", context: "source_context", operation: "null" }),
|
|
105
|
+
quote: row({ fact_id: "q_id", value: "null", ...sourced, rationale: "string_or_null", exact: "null", context: "source_context", operation: "null" }),
|
|
106
106
|
derived: row({ fact_id: "f_id", value: "decimal", ...unsourced, rationale: "string_or_null", exact: "by_derive", context: "context_by_derive", operation: "derived_operation" }),
|
|
107
107
|
assumed: row({ fact_id: "f_id", value: "decimal", ...unsourced, rationale: "non_empty", exact: "rational_or_null", context: "null", operation: "null" }),
|
|
108
108
|
converted: row({ fact_id: "f_id", value: "decimal", ...unsourced, rationale: "string_or_null", exact: "by_derive", context: "context_by_derive", operation: "convert_operation" }),
|
|
109
109
|
})
|
|
110
110
|
|
|
111
|
-
/** The id prefix nextId assigns for a kind; an unknown kind gets F and fails validation later. */
|
|
112
111
|
export function idPrefix(kind) {
|
|
113
112
|
return RULES[KIND_RULES[kind]?.fact_id ?? "f_id"].prefix
|
|
114
113
|
}
|
|
@@ -169,7 +168,7 @@ const carriesDerivation = (row) => row.operation !== null || row.operands?.lengt
|
|
|
169
168
|
/** Dimension 32: a stored exact must equal the row's own value at the value's decimal places. */
|
|
170
169
|
function valueExactDisagreement(row) {
|
|
171
170
|
const exact = readExact(row.exact)
|
|
172
|
-
const value = readDecimal(row.value)
|
|
171
|
+
const value = isString(row.value) ? readDecimal(row.value).replace(/^-(0+(?:\.0+)?)$/, "$1") : row.value
|
|
173
172
|
if (!isRational(exact) || !isDecimal(value) || roundPlaces(toBig(exact), placesOf(value)) === value) return null
|
|
174
173
|
return `value ${value} disagrees with exact ${exact.num}/${exact.den} at ${placesOf(value)} decimal places`
|
|
175
174
|
}
|
|
@@ -212,7 +211,7 @@ export function kindRuleErrors(candidate, at) {
|
|
|
212
211
|
const value = candidate[column] === undefined && LATER_COLUMNS.includes(column) ? null : candidate[column]
|
|
213
212
|
const rule = RULES[legacy && column === "exact" ? "rational_or_null" : rules[column]]
|
|
214
213
|
// An exact with unknown keys loads as written (never stripped); readFlaws refuses it at use and at write.
|
|
215
|
-
if (column === "exact" && hasUnknownExactKeys(value) && !isCurrentDerived(candidate)) continue
|
|
214
|
+
if (column === "exact" && rules[column] !== "null" && hasUnknownExactKeys(value) && !isCurrentDerived(candidate)) continue
|
|
216
215
|
if (rule.test(value, candidate)) continue
|
|
217
216
|
errors.push(`${at}.${column} must be ${rule.says} for a ${candidate.kind} row`)
|
|
218
217
|
if (rule.detail) errors.push(...rule.detail(value, `${at}.${column}`))
|
|
@@ -62,6 +62,7 @@ const GENERATORS: Record<string, Gen> = {
|
|
|
62
62
|
rational_or_null: { valid: (r) => pick(r, [null, rational(r)]), invalid: (r) => pick(r, [{ num: "1", den: "0" }, { num: 1, den: "2" }, "1/2"]) },
|
|
63
63
|
by_derive: { valid: rational, invalid: (r) => pick(r, [null, { num: "1", den: "0" }, { num: "x", den: "2" }, "1/2"]) },
|
|
64
64
|
context_by_derive: { valid: (_r, row) => row.kind === "converted" ? { target: "km" } : null, invalid: (r) => pick(r, ["km", 3, ["km"], false]) },
|
|
65
|
+
source_context: { valid: (r) => pick(r, [null, { prefix: "source ", suffix: " text" }]), invalid: (r) => pick(r, [{ target: "km" }, { prefix: "x".repeat(33), suffix: "" }, { prefix: "", suffix: 3 }]) },
|
|
65
66
|
derived_operation: { valid: () => "expr", invalid: (r) => pick(r, [null, "convert", "mean"]) },
|
|
66
67
|
convert_operation: { valid: () => "convert", invalid: (r) => pick(r, [null, "sum", "mean"]) },
|
|
67
68
|
precision: { valid: () => ({ places: 2, sigfigs: 3 }), invalid: (r) => pick(r, [null, { places: -1, sigfigs: 3 }, { places: 2, sigfigs: 0 }]) },
|
|
@@ -122,7 +123,7 @@ describe("rows generated from KIND_RULES", () => {
|
|
|
122
123
|
expect(validateLedger(ledgerOf({ ...row, exact: { num: "-1", den: "2" } }))).toEqual([])
|
|
123
124
|
})
|
|
124
125
|
|
|
125
|
-
test("
|
|
126
|
+
test("source context rejects derivation fields, and assumed rows still require null context", () => {
|
|
126
127
|
for (const kind of ["measured", "quote", "assumed"]) {
|
|
127
128
|
const row = { ...validRow(kind, prng(3)), context: { target: "km" } }
|
|
128
129
|
expect({ kind, named: validateLedger(ledgerOf(row)).some((error: string) => error.startsWith("rows[0].context")) }).toEqual({ kind, named: true })
|
|
@@ -3,7 +3,8 @@ import { copyFileSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "
|
|
|
3
3
|
import { tmpdir } from "node:os"
|
|
4
4
|
import { join } from "node:path"
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { recompute } from "./fact-derive.mjs"
|
|
7
|
+
import { derive } from "./tests/derive-clock"
|
|
7
8
|
import { LEDGER_FILENAME, addRow, emptyLedger, readLedger, validateLedger, writeLedger } from "./fact-ledger.mjs"
|
|
8
9
|
|
|
9
10
|
// Generated by dev's own addRow (tests/fixtures/fidelity/gen-legacy-dev.mjs pins the dev commit):
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
// fact-locate.mjs - deterministic fact locators over a source snapshot.
|
|
1
|
+
// fact-locate.mjs - deterministic fact locators over a source snapshot's locator text.
|
|
2
2
|
//
|
|
3
3
|
// Each locator returns { value, unit, raw, quote, value_span, sentence_id, qualifier, locator } or
|
|
4
4
|
// { error: "fact_not_located" | "fact_ambiguous", candidates }; it never guesses. `quote` is the
|
|
5
|
-
// whole containing sentence, cell, or line of text.
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// whole containing sentence, cell, or line of the view text (source-view.mjs: the visible text
|
|
6
|
+
// nodes of a page, never markup), `value_span` is [start, end] inside `quote` with
|
|
7
|
+
// quote.slice(...value_span) === raw, and locator.char_start/char_end place `quote` in that text;
|
|
8
|
+
// raw-locator.mjs turns them into bytes. Every call takes `sourceId` (S<n>) so sentence ids match
|
|
9
|
+
// sentences.jsonl, which indexes the same text.
|
|
8
10
|
// Plain Node ESM, sibling imports only; no network, no OCR.
|
|
9
11
|
|
|
10
12
|
import { proseText } from "./html-lite.mjs"
|
|
@@ -101,7 +103,8 @@ export function locateFind(text, { find, unitExpected, sourceId }) {
|
|
|
101
103
|
return result.unit === expected ? result : { error: "unit_expected_mismatch", expected, found: result.unit, candidates: [result] }
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
/** A row's cells by visual column (a colspan cell repeats). */
|
|
107
|
+
export function cellsOf(row) {
|
|
105
108
|
const cells = []
|
|
106
109
|
for (const cell of row.children.filter((child) => child.tag === "td" || child.tag === "th")) {
|
|
107
110
|
const span = Math.max(1, Number.parseInt(cell.attrs.colspan ?? "1", 10) || 1)
|
|
@@ -110,7 +113,7 @@ function cellsOf(row) {
|
|
|
110
113
|
return cells
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
function rowsOf(table) {
|
|
116
|
+
export function rowsOf(table) {
|
|
114
117
|
const rows = []
|
|
115
118
|
const walk = (node) => {
|
|
116
119
|
for (const child of node.children) {
|
|
@@ -142,10 +145,13 @@ function tablesOf(root) {
|
|
|
142
145
|
const cellLabel = (cell) => label(proseText(cell))
|
|
143
146
|
const indexesOf = (labels, wanted) => labels.flatMap((value, index) => (value === wanted ? [index] : []))
|
|
144
147
|
|
|
145
|
-
/**
|
|
146
|
-
|
|
148
|
+
/**
|
|
149
|
+
* --table/--row/--col: the table by id, caption, or a header label; row and column by normalized label,
|
|
150
|
+
* never by index. `source` is page html or an extracted view ({ root, text, blocks }) of one.
|
|
151
|
+
*/
|
|
152
|
+
export function locateTable(source, { table, row, col, sourceId }) {
|
|
147
153
|
requireSource(sourceId)
|
|
148
|
-
const { root, text, blocks } = snapshotBlocks(
|
|
154
|
+
const { root, text, blocks } = typeof source === "string" ? snapshotBlocks(source) : source
|
|
149
155
|
const key = label(table)
|
|
150
156
|
const tables = tablesOf(root).filter((entry) => entry.table.attrs.id === table || entry.caption === key || (entry.header && cellsOf(entry.header).map(cellLabel).includes(key)))
|
|
151
157
|
if (tables.length !== 1) return tables.length === 0 ? notLocated() : ambiguous(tables.map((entry) => ({ table: entry.table.attrs.id ?? entry.caption })))
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { expect, test } from "bun:test"
|
|
2
|
+
import { recompute } from "./fact-derive.mjs"
|
|
3
|
+
import { derive } from "./tests/derive-clock"
|
|
4
|
+
import { addRow, emptyLedger, retractRow, validateLedger } from "./fact-ledger.mjs"
|
|
5
|
+
import { canonicalUnit } from "./unit-rules.mjs"
|
|
6
|
+
import { convertFactor } from "./units.mjs"
|
|
7
|
+
import { Row, Book, now, book, row, sum, refused } from "./tests/fact-unit-dimensions-support"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
test("dimension 21: expression root and literal precision remain explicit", () => {
|
|
12
|
+
const l = book(["1000", "3"], ["m", "m"])
|
|
13
|
+
expect(row(l, { op: "expr", formula: "F1 - F2", unit: "m" }).value).toBe("997")
|
|
14
|
+
expect(row(l, { op: "expr", formula: "(F1 - F2) * 1", unit: "m" }).value).toBe("1000")
|
|
15
|
+
expect(row(l, { op: "expr", formula: "(F1 - F2) * 1.00", unit: "m" }).value).toBe("1000")
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
test("dimension 22: cloned snapshots are deterministic and replay appends a new identity", () => {
|
|
19
|
+
const original = book()
|
|
20
|
+
const left = structuredClone(original)
|
|
21
|
+
const right = structuredClone(original)
|
|
22
|
+
expect(row(left, sum)).toEqual(row(right, sum))
|
|
23
|
+
expect(row(left, sum).fact_id).toBe("F4")
|
|
24
|
+
expect(original.rows).toHaveLength(2)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test("dimension 23: coherent trusted input rewrites are a documented arithmetic limitation", () => {
|
|
28
|
+
const l = book(["0", "3"])
|
|
29
|
+
const km = row(l, { op: "convert", operands: ["F1"], to: "km" })
|
|
30
|
+
const cm = row(l, { op: "convert", operands: ["F1"], to: "cm" })
|
|
31
|
+
const coherent = { ...cm, fact_id: km.fact_id, label: km.label }
|
|
32
|
+
expect(recompute(l, coherent).ok).toBe(true)
|
|
33
|
+
expect(recompute(l, { ...km, unit: "cm" }).ok).toBe(false)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test("dimension 24: sum permutation and ordered arithmetic keep their own semantics", () => {
|
|
37
|
+
const l = book(["2", "5"])
|
|
38
|
+
for (const operands of [["F1", "F2"], ["F2", "F1"]]) expect(row(l, { ...sum, operands }).exact).toEqual({ num: "7", den: "1" })
|
|
39
|
+
expect(row(l, { ...sum, op: "diff" }).exact).toEqual({ num: "-3", den: "1" })
|
|
40
|
+
expect(row(l, { ...sum, op: "diff", operands: ["F2", "F1"] }).exact).toEqual({ num: "3", den: "1" })
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test("dimension 25: numeric domain boundaries do not forbid negative ordinary arithmetic", () => {
|
|
44
|
+
for (const values of [["6", "5"], ["-1", "5"], ["0", "0"]]) expect(derive(book(values), { op: "share", part: "F1", whole: "F2", unit: "%" }).error).toBeDefined()
|
|
45
|
+
expect(row(book(["-2", "-5"]), sum).exact).toEqual({ num: "-7", den: "1" })
|
|
46
|
+
expect(derive(book(), { op: "expr", formula: "F1 / (F2 - F2)", unit: null }).error).toBeDefined()
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test("dimension 26: percent products and pct nodes preserve fraction semantics", () => {
|
|
50
|
+
const l = book(["20", "50"], ["%", "%"])
|
|
51
|
+
expect(row(l, { op: "expr", formula: "pct(F1 * F2)", unit: "%" }).exact).toEqual({ num: "10", den: "1" })
|
|
52
|
+
expect(row(l, { op: "diff", operands: ["F2", "F1"], unit: "%p" }).exact).toEqual({ num: "30", den: "1" })
|
|
53
|
+
expect(derive(l, { op: "expr", formula: "pct(F1)", unit: "%" }).error).toBeDefined()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test("dimension 27: conversion maps compose with exact independent rational arithmetic", () => {
|
|
57
|
+
const groups = [["mm", "cm", "m", "km"], ["g", "kg", "t"], ["ms", "s", "min", "h"], ["B", "KB", "MB", "GB", "TB"], ["C", "F"], ["%", "\u2030"], ["KRW", "KRW-e3", "KRW-e4", "KRW-e8", "KRW-e12"]]
|
|
58
|
+
const apply = (factor: { num: bigint; den: bigint; offset?: { num: bigint; den: bigint } } | null, x: [bigint, bigint]): [bigint, bigint] => {
|
|
59
|
+
if (!factor) throw Error("missing conversion")
|
|
60
|
+
const o = factor.offset ?? { num: 0n, den: 1n }
|
|
61
|
+
return [x[0] * factor.num * o.den + o.num * x[1] * factor.den, x[1] * factor.den * o.den]
|
|
62
|
+
}
|
|
63
|
+
for (const group of groups) for (const a of group) for (const b of group) for (const c of group) {
|
|
64
|
+
for (const value of [-3n, 0n, 7n]) {
|
|
65
|
+
const direct = apply(convertFactor(a, c), [value, 1n])
|
|
66
|
+
const chained = apply(convertFactor(b, c), apply(convertFactor(a, b), [value, 1n]))
|
|
67
|
+
expect(direct[0] * chained[1]).toBe(chained[0] * direct[1])
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
expect(derive(book(), { op: "convert", operands: ["F1"], to: "furlong" }).error).toBeDefined()
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test("dimension 28: grammar rejects injection and preserves precedence", () => {
|
|
74
|
+
for (const formula of ["F1;globalThis.x=1", "-F1", "F1 ** 2", "pct F1", "(F1", "F1 F2"]) expect(derive(book(), { op: "expr", formula, unit: "m" }).error).toBeDefined()
|
|
75
|
+
expect(row(book(), { op: "expr", formula: "F1 + F2 * 2", unit: "m" }).exact).toEqual({ num: "7", den: "1" })
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test("dimension 29: signed rounding ties and canonical display are exact", () => {
|
|
79
|
+
for (const [values, expected] of [[["-1.25", "-2.5"], "-3.8"], [["1.25", "2.5"], "3.8"], [["999.5", "0"], "1000"]] as const) {
|
|
80
|
+
expect(row(book([...values]), sum).value).toBe(expected)
|
|
81
|
+
}
|
|
82
|
+
const l = book(["20", "15"], ["%", "%"])
|
|
83
|
+
const delta = row(l, { op: "diff", operands: ["F1", "F2"], unit: "%p" })
|
|
84
|
+
expect(recompute(l, { ...delta, render: "5 pp" }).ok).toBe(false)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
test("dimension 30: seeded factor trees have an independent arithmetic and unit oracle", () => {
|
|
88
|
+
let seed = 0x11009
|
|
89
|
+
const next = () => { seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0; return seed }
|
|
90
|
+
const atoms = ["m", "s", "person"]
|
|
91
|
+
for (let i = 0; i < 100; i++) {
|
|
92
|
+
const counts = [0, 0, 0]
|
|
93
|
+
const factors = Array.from({ length: 5 }, () => {
|
|
94
|
+
const index = next() % atoms.length
|
|
95
|
+
const exponent = (next() % 7) - 3
|
|
96
|
+
counts[index] += exponent
|
|
97
|
+
return `(${atoms[index]}^${exponent})`
|
|
98
|
+
})
|
|
99
|
+
const side = (positive: boolean) => atoms.map((a, index) => ({ a, n: counts[index] })).filter(({ n }) => positive ? n > 0 : n < 0).sort((a, b) => a.a.localeCompare(b.a)).map(({ a, n }) => Math.abs(n) === 1 ? a : `${a}^${Math.abs(n)}`)
|
|
100
|
+
const top = side(true).join("*") || "1"
|
|
101
|
+
const bottom = side(false)
|
|
102
|
+
const expectedUnit = bottom.length ? `${top}/${bottom.length === 1 ? bottom[0] : `(${bottom.join("*")})`}` : top === "1" ? null : top
|
|
103
|
+
expect(canonicalUnit(factors.join("*"))).toBe(expectedUnit)
|
|
104
|
+
const a = BigInt(1 + next() % 100)
|
|
105
|
+
const b = BigInt(1 + next() % 100)
|
|
106
|
+
const l = book([String(a), String(b)], [factors.join("*"), "person"])
|
|
107
|
+
const result = row(l, { op: "expr", formula: "F1 * F2 / F2", unit: expectedUnit })
|
|
108
|
+
expect(result.exact).toEqual({ num: String(a), den: "1" })
|
|
109
|
+
expect(result.unit).toBe(expectedUnit)
|
|
110
|
+
expect(recompute(l, result).ok).toBe(true)
|
|
111
|
+
expect(recompute(l, { ...result, exact: { num: String(a + 1n), den: "1" } }).ok).toBe(false)
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test("dimension 30: independent rational oracle crosses every operation with metadata and tampering", () => {
|
|
116
|
+
let state = 3917
|
|
117
|
+
const next = () => { state = (Math.imul(state, 1103515245) + 12345) >>> 0; return state }
|
|
118
|
+
const exact = (n: bigint, d = 1n) => {
|
|
119
|
+
let a = n < 0n ? -n : n
|
|
120
|
+
let b = d
|
|
121
|
+
while (b) [a, b] = [b, a % b]
|
|
122
|
+
return { num: String(n / a), den: String(d / a) }
|
|
123
|
+
}
|
|
124
|
+
for (let i = 0; i < 50; i++) {
|
|
125
|
+
const a = BigInt(1 + next() % 50)
|
|
126
|
+
const b = a + BigInt(1 + next() % 50)
|
|
127
|
+
const meta = { population: { country: "x", cohort: i }, accounting_basis: "cash", snapshot_window: "2026", entity: "acme" }
|
|
128
|
+
const specs: Array<[Row, { num: string; den: string }]> = [
|
|
129
|
+
[{ ...sum }, exact(a + b)],
|
|
130
|
+
[{ ...sum, op: "diff" }, exact(a - b)],
|
|
131
|
+
[{ op: "ratio", num: "F1", den: "F2", unit: null }, exact(a, b)],
|
|
132
|
+
[{ op: "share", part: "F1", whole: "F2", unit: "%" }, exact(100n * a, b)],
|
|
133
|
+
[{ op: "growth", from: "F1", to: "F2", unit: "%" }, exact(100n * (b - a), a)],
|
|
134
|
+
[{ op: "convert", operands: ["F1"], to: "cm" }, exact(100n * a)],
|
|
135
|
+
[{ op: "expr", formula: "F1 * 2 / F2", unit: null }, exact(2n * a, b)],
|
|
136
|
+
]
|
|
137
|
+
for (const [spec, expected] of specs) {
|
|
138
|
+
const l = book([String(a), String(b)], ["metre", "m"], [{ ...meta, valid_at: "2025" }, { ...meta, valid_at: "2026" }])
|
|
139
|
+
const result = row(l, spec)
|
|
140
|
+
expect(result.exact).toEqual(expected)
|
|
141
|
+
expect(recompute(l, result).ok).toBe(true)
|
|
142
|
+
expect(recompute(l, { ...result, unit: "person" }).ok).toBe(false)
|
|
143
|
+
expect(recompute(l, { ...result, exact: { num: String(BigInt(expected.num) + 1n), den: expected.den } }).ok).toBe(false)
|
|
144
|
+
}
|
|
145
|
+
const l = book([String(100n * a), String(b)], ["bp/person", "person"])
|
|
146
|
+
const result = row(l, { op: "expr", formula: "F1 * F2", unit: "%p" })
|
|
147
|
+
expect(result.exact).toEqual(exact(a * b))
|
|
148
|
+
}
|
|
149
|
+
})
|
|
@@ -1,36 +1,12 @@
|
|
|
1
1
|
import { expect, test } from "bun:test"
|
|
2
|
-
|
|
3
|
-
import { derive
|
|
2
|
+
import { recompute } from "./fact-derive.mjs"
|
|
3
|
+
import { derive } from "./tests/derive-clock"
|
|
4
4
|
import { addRow, emptyLedger, retractRow, validateLedger } from "./fact-ledger.mjs"
|
|
5
5
|
import { canonicalUnit } from "./unit-rules.mjs"
|
|
6
6
|
import { convertFactor } from "./units.mjs"
|
|
7
|
+
import { Row, Book, now, book, row, sum, refused } from "./tests/fact-unit-dimensions-support"
|
|
8
|
+
|
|
7
9
|
|
|
8
|
-
type Row = Record<string, unknown>
|
|
9
|
-
type Book = { schemaVersion: number; rows: Row[]; events: unknown[] }
|
|
10
|
-
const now = "2026-10-23T08:00:00Z"
|
|
11
|
-
function book(values: string[] = ["1", "3"], units: Array<string | null> = ["m", "m"], extras: Row[] = []): Book {
|
|
12
|
-
const ledger = emptyLedger() as Book
|
|
13
|
-
values.forEach((value, i) => addRow(ledger, {
|
|
14
|
-
kind: "measured", value, unit: units[i], raw: value, quote: value, source_id: "S1",
|
|
15
|
-
locator: { artifact: "sources/S1/text.txt", char_start: 0, char_end: 1 },
|
|
16
|
-
snapshot_sha256: "a".repeat(64), extracted_by: "test", observed_at: now, label: `f${i + 1}`,
|
|
17
|
-
...extras[i],
|
|
18
|
-
}))
|
|
19
|
-
return ledger
|
|
20
|
-
}
|
|
21
|
-
function row(ledger: Book, spec: Row): Row {
|
|
22
|
-
const result = derive(ledger, { ...spec, now })
|
|
23
|
-
if (!("row" in result)) throw Error(JSON.stringify(result))
|
|
24
|
-
return result.row as Row
|
|
25
|
-
}
|
|
26
|
-
const sum = { op: "sum", operands: ["F1", "F2"], unit: "m" }
|
|
27
|
-
function refused(ledger: Book, spec: unknown) {
|
|
28
|
-
const before = structuredClone(ledger)
|
|
29
|
-
const result = derive(ledger, spec)
|
|
30
|
-
expect(result).toMatchObject({ error: "incompatible_operands" })
|
|
31
|
-
expect(ledger).toEqual(before)
|
|
32
|
-
return result
|
|
33
|
-
}
|
|
34
10
|
|
|
35
11
|
test("dimension 01: kind operation consistency cannot disable recursive trust", () => {
|
|
36
12
|
for (const patch of [{ operation: null }, { kind: "measured" }, { operation: "convert" }]) {
|
|
@@ -267,143 +243,3 @@ test("dimension 20: supplied expression operand set must match formula leaves",
|
|
|
267
243
|
for (const operands of [["F2"], ["F1", "F2"], ["F1", "F1"], [], null]) refused(book(), { op: "expr", formula: "F1", unit: "m", operands })
|
|
268
244
|
expect(row(book(), { op: "expr", formula: "F1 + F2", operands: ["F2", "F1"], unit: "m" }).value).toBe("4")
|
|
269
245
|
})
|
|
270
|
-
|
|
271
|
-
test("dimension 21: expression root and literal precision remain explicit", () => {
|
|
272
|
-
const l = book(["1000", "3"], ["m", "m"])
|
|
273
|
-
expect(row(l, { op: "expr", formula: "F1 - F2", unit: "m" }).value).toBe("997")
|
|
274
|
-
expect(row(l, { op: "expr", formula: "(F1 - F2) * 1", unit: "m" }).value).toBe("1000")
|
|
275
|
-
expect(row(l, { op: "expr", formula: "(F1 - F2) * 1.00", unit: "m" }).value).toBe("1000")
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
test("dimension 22: cloned snapshots are deterministic and replay appends a new identity", () => {
|
|
279
|
-
const original = book()
|
|
280
|
-
const left = structuredClone(original)
|
|
281
|
-
const right = structuredClone(original)
|
|
282
|
-
expect(row(left, sum)).toEqual(row(right, sum))
|
|
283
|
-
expect(row(left, sum).fact_id).toBe("F4")
|
|
284
|
-
expect(original.rows).toHaveLength(2)
|
|
285
|
-
})
|
|
286
|
-
|
|
287
|
-
test("dimension 23: coherent trusted input rewrites are a documented arithmetic limitation", () => {
|
|
288
|
-
const l = book(["0", "3"])
|
|
289
|
-
const km = row(l, { op: "convert", operands: ["F1"], to: "km" })
|
|
290
|
-
const cm = row(l, { op: "convert", operands: ["F1"], to: "cm" })
|
|
291
|
-
const coherent = { ...cm, fact_id: km.fact_id, label: km.label }
|
|
292
|
-
expect(recompute(l, coherent).ok).toBe(true)
|
|
293
|
-
expect(recompute(l, { ...km, unit: "cm" }).ok).toBe(false)
|
|
294
|
-
})
|
|
295
|
-
|
|
296
|
-
test("dimension 24: sum permutation and ordered arithmetic keep their own semantics", () => {
|
|
297
|
-
const l = book(["2", "5"])
|
|
298
|
-
for (const operands of [["F1", "F2"], ["F2", "F1"]]) expect(row(l, { ...sum, operands }).exact).toEqual({ num: "7", den: "1" })
|
|
299
|
-
expect(row(l, { ...sum, op: "diff" }).exact).toEqual({ num: "-3", den: "1" })
|
|
300
|
-
expect(row(l, { ...sum, op: "diff", operands: ["F2", "F1"] }).exact).toEqual({ num: "3", den: "1" })
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
test("dimension 25: numeric domain boundaries do not forbid negative ordinary arithmetic", () => {
|
|
304
|
-
for (const values of [["6", "5"], ["-1", "5"], ["0", "0"]]) expect(derive(book(values), { op: "share", part: "F1", whole: "F2", unit: "%" }).error).toBeDefined()
|
|
305
|
-
expect(row(book(["-2", "-5"]), sum).exact).toEqual({ num: "-7", den: "1" })
|
|
306
|
-
expect(derive(book(), { op: "expr", formula: "F1 / (F2 - F2)", unit: null }).error).toBeDefined()
|
|
307
|
-
})
|
|
308
|
-
|
|
309
|
-
test("dimension 26: percent products and pct nodes preserve fraction semantics", () => {
|
|
310
|
-
const l = book(["20", "50"], ["%", "%"])
|
|
311
|
-
expect(row(l, { op: "expr", formula: "pct(F1 * F2)", unit: "%" }).exact).toEqual({ num: "10", den: "1" })
|
|
312
|
-
expect(row(l, { op: "diff", operands: ["F2", "F1"], unit: "%p" }).exact).toEqual({ num: "30", den: "1" })
|
|
313
|
-
expect(derive(l, { op: "expr", formula: "pct(F1)", unit: "%" }).error).toBeDefined()
|
|
314
|
-
})
|
|
315
|
-
|
|
316
|
-
test("dimension 27: conversion maps compose with exact independent rational arithmetic", () => {
|
|
317
|
-
const groups = [["mm", "cm", "m", "km"], ["g", "kg", "t"], ["ms", "s", "min", "h"], ["B", "KB", "MB", "GB", "TB"], ["C", "F"], ["%", "\u2030"], ["KRW", "KRW-e3", "KRW-e4", "KRW-e8", "KRW-e12"]]
|
|
318
|
-
const apply = (factor: { num: bigint; den: bigint; offset?: { num: bigint; den: bigint } } | null, x: [bigint, bigint]): [bigint, bigint] => {
|
|
319
|
-
if (!factor) throw Error("missing conversion")
|
|
320
|
-
const o = factor.offset ?? { num: 0n, den: 1n }
|
|
321
|
-
return [x[0] * factor.num * o.den + o.num * x[1] * factor.den, x[1] * factor.den * o.den]
|
|
322
|
-
}
|
|
323
|
-
for (const group of groups) for (const a of group) for (const b of group) for (const c of group) {
|
|
324
|
-
for (const value of [-3n, 0n, 7n]) {
|
|
325
|
-
const direct = apply(convertFactor(a, c), [value, 1n])
|
|
326
|
-
const chained = apply(convertFactor(b, c), apply(convertFactor(a, b), [value, 1n]))
|
|
327
|
-
expect(direct[0] * chained[1]).toBe(chained[0] * direct[1])
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
expect(derive(book(), { op: "convert", operands: ["F1"], to: "furlong" }).error).toBeDefined()
|
|
331
|
-
})
|
|
332
|
-
|
|
333
|
-
test("dimension 28: grammar rejects injection and preserves precedence", () => {
|
|
334
|
-
for (const formula of ["F1;globalThis.x=1", "-F1", "F1 ** 2", "pct F1", "(F1", "F1 F2"]) expect(derive(book(), { op: "expr", formula, unit: "m" }).error).toBeDefined()
|
|
335
|
-
expect(row(book(), { op: "expr", formula: "F1 + F2 * 2", unit: "m" }).exact).toEqual({ num: "7", den: "1" })
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
test("dimension 29: signed rounding ties and canonical display are exact", () => {
|
|
339
|
-
for (const [values, expected] of [[["-1.25", "-2.5"], "-3.8"], [["1.25", "2.5"], "3.8"], [["999.5", "0"], "1000"]] as const) {
|
|
340
|
-
expect(row(book([...values]), sum).value).toBe(expected)
|
|
341
|
-
}
|
|
342
|
-
const l = book(["20", "15"], ["%", "%"])
|
|
343
|
-
const delta = row(l, { op: "diff", operands: ["F1", "F2"], unit: "%p" })
|
|
344
|
-
expect(recompute(l, { ...delta, render: "5 pp" }).ok).toBe(false)
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
test("dimension 30: seeded factor trees have an independent arithmetic and unit oracle", () => {
|
|
348
|
-
let seed = 0x11009
|
|
349
|
-
const next = () => { seed = (Math.imul(seed, 1664525) + 1013904223) >>> 0; return seed }
|
|
350
|
-
const atoms = ["m", "s", "person"]
|
|
351
|
-
for (let i = 0; i < 100; i++) {
|
|
352
|
-
const counts = [0, 0, 0]
|
|
353
|
-
const factors = Array.from({ length: 5 }, () => {
|
|
354
|
-
const index = next() % atoms.length
|
|
355
|
-
const exponent = (next() % 7) - 3
|
|
356
|
-
counts[index] += exponent
|
|
357
|
-
return `(${atoms[index]}^${exponent})`
|
|
358
|
-
})
|
|
359
|
-
const side = (positive: boolean) => atoms.map((a, index) => ({ a, n: counts[index] })).filter(({ n }) => positive ? n > 0 : n < 0).sort((a, b) => a.a.localeCompare(b.a)).map(({ a, n }) => Math.abs(n) === 1 ? a : `${a}^${Math.abs(n)}`)
|
|
360
|
-
const top = side(true).join("*") || "1"
|
|
361
|
-
const bottom = side(false)
|
|
362
|
-
const expectedUnit = bottom.length ? `${top}/${bottom.length === 1 ? bottom[0] : `(${bottom.join("*")})`}` : top === "1" ? null : top
|
|
363
|
-
expect(canonicalUnit(factors.join("*"))).toBe(expectedUnit)
|
|
364
|
-
const a = BigInt(1 + next() % 100)
|
|
365
|
-
const b = BigInt(1 + next() % 100)
|
|
366
|
-
const l = book([String(a), String(b)], [factors.join("*"), "person"])
|
|
367
|
-
const result = row(l, { op: "expr", formula: "F1 * F2 / F2", unit: expectedUnit })
|
|
368
|
-
expect(result.exact).toEqual({ num: String(a), den: "1" })
|
|
369
|
-
expect(result.unit).toBe(expectedUnit)
|
|
370
|
-
expect(recompute(l, result).ok).toBe(true)
|
|
371
|
-
expect(recompute(l, { ...result, exact: { num: String(a + 1n), den: "1" } }).ok).toBe(false)
|
|
372
|
-
}
|
|
373
|
-
})
|
|
374
|
-
|
|
375
|
-
test("dimension 30: independent rational oracle crosses every operation with metadata and tampering", () => {
|
|
376
|
-
let state = 3917
|
|
377
|
-
const next = () => { state = (Math.imul(state, 1103515245) + 12345) >>> 0; return state }
|
|
378
|
-
const exact = (n: bigint, d = 1n) => {
|
|
379
|
-
let a = n < 0n ? -n : n
|
|
380
|
-
let b = d
|
|
381
|
-
while (b) [a, b] = [b, a % b]
|
|
382
|
-
return { num: String(n / a), den: String(d / a) }
|
|
383
|
-
}
|
|
384
|
-
for (let i = 0; i < 50; i++) {
|
|
385
|
-
const a = BigInt(1 + next() % 50)
|
|
386
|
-
const b = a + BigInt(1 + next() % 50)
|
|
387
|
-
const meta = { population: { country: "x", cohort: i }, accounting_basis: "cash", snapshot_window: "2026", entity: "acme" }
|
|
388
|
-
const specs: Array<[Row, { num: string; den: string }]> = [
|
|
389
|
-
[{ ...sum }, exact(a + b)],
|
|
390
|
-
[{ ...sum, op: "diff" }, exact(a - b)],
|
|
391
|
-
[{ op: "ratio", num: "F1", den: "F2", unit: null }, exact(a, b)],
|
|
392
|
-
[{ op: "share", part: "F1", whole: "F2", unit: "%" }, exact(100n * a, b)],
|
|
393
|
-
[{ op: "growth", from: "F1", to: "F2", unit: "%" }, exact(100n * (b - a), a)],
|
|
394
|
-
[{ op: "convert", operands: ["F1"], to: "cm" }, exact(100n * a)],
|
|
395
|
-
[{ op: "expr", formula: "F1 * 2 / F2", unit: null }, exact(2n * a, b)],
|
|
396
|
-
]
|
|
397
|
-
for (const [spec, expected] of specs) {
|
|
398
|
-
const l = book([String(a), String(b)], ["metre", "m"], [{ ...meta, valid_at: "2025" }, { ...meta, valid_at: "2026" }])
|
|
399
|
-
const result = row(l, spec)
|
|
400
|
-
expect(result.exact).toEqual(expected)
|
|
401
|
-
expect(recompute(l, result).ok).toBe(true)
|
|
402
|
-
expect(recompute(l, { ...result, unit: "person" }).ok).toBe(false)
|
|
403
|
-
expect(recompute(l, { ...result, exact: { num: String(BigInt(expected.num) + 1n), den: expected.den } }).ok).toBe(false)
|
|
404
|
-
}
|
|
405
|
-
const l = book([String(100n * a), String(b)], ["bp/person", "person"])
|
|
406
|
-
const result = row(l, { op: "expr", formula: "F1 * F2", unit: "%p" })
|
|
407
|
-
expect(result.exact).toEqual(exact(a * b))
|
|
408
|
-
}
|
|
409
|
-
})
|