oh-my-opencode 5.1.44 → 5.1.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +865 -413
- package/dist/cli/service-auth/keystore.d.ts +4 -2
- package/dist/cli-node/index.js +865 -413
- package/dist/features/builtin-commands/templates/hyperplan.d.ts +1 -1
- package/dist/features/builtin-commands/templates/refactor-sections/team-mode-addendum.d.ts +1 -1
- package/dist/features/builtin-commands/templates/remove-ai-slops.d.ts +1 -1
- package/dist/hooks/sisyphus-junior-notepad/constants.d.ts +1 -1
- package/dist/index.js +5270 -2308
- package/dist/oh-my-opencode.schema.json +2 -2
- package/dist/plugin/command-execute-before.d.ts +3 -0
- package/dist/plugin/skill-context.d.ts +2 -0
- package/dist/plugin/skill-security-notice.d.ts +3 -0
- package/dist/plugin-config/omo-config-chain.d.ts +1 -0
- package/dist/skills/ast-grep/AGENTS.md +1 -1
- package/dist/skills/browser/SKILL.md +21 -22
- package/dist/skills/browser/references/commands.md +2 -9
- package/dist/skills/browser/references/install.md +1 -2
- package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
- package/dist/skills/browser/scripts/browser-doctor.mjs +2 -2
- package/dist/skills/browser/scripts/browser-engine-guard.mjs +63 -487
- package/dist/skills/browser/scripts/browser-install.mjs +1 -1
- package/dist/skills/browser/scripts/omowright.mjs +1 -10
- package/dist/skills/frontend/ATTRIBUTION.md +2 -2
- package/dist/skills/frontend/SKILL.md +79 -118
- package/dist/skills/frontend/references/design/README.md +75 -224
- package/dist/skills/frontend/references/design/stylegallery.md +32 -27
- package/dist/skills/frontend/references/perfection/README.md +2 -2
- package/dist/skills/frontend/scripts/perfection/check-accent-stripes.ts +8 -24
- package/dist/skills/frontend/scripts/perfection/check-draft-guard.test.ts +101 -0
- package/dist/skills/frontend/scripts/perfection/check-draft-guard.ts +40 -0
- package/dist/skills/frontend/scripts/perfection/collect-files.test.ts +50 -0
- package/dist/skills/frontend/scripts/perfection/collect-files.ts +53 -0
- package/dist/skills/frontend/scripts/perfection/draft-guard/findings.ts +16 -0
- package/dist/skills/frontend/scripts/perfection/draft-guard/lint.ts +38 -0
- package/dist/skills/frontend/scripts/perfection/draft-guard/rules.ts +69 -0
- package/dist/skills/frontend/scripts/perfection/draft-guard/text-nodes.ts +121 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/attr-lookalikes.tsx +10 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/bound-values.tsx +12 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/const-generics.tsx +3 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/generics.tsx +7 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/measured-stats.html +6 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/plain-copy.html +9 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/state-copy.html +11 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/attribute-edges.html +7 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/components.tsx +16 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/decorative-badge.vue +7 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/emoji-icons.tsx +9 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/jsx-edges.tsx +1 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.html +6 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.tsx +8 -0
- package/dist/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/vanity-stats.html +5 -0
- package/dist/skills/programming/SKILL.md +10 -7
- package/dist/skills/programming/references/typescript/README.md +10 -7
- package/dist/skills/programming/references/typescript/bootstrap.md +9 -23
- package/dist/skills/programming/references/typescript/oxc-lint.md +130 -0
- package/dist/skills/programming/references/typescript/tsconfig-strict.md +6 -62
- package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.ts +12 -12
- package/dist/skills/programming/scripts/typescript/new-project.test.ts +162 -0
- package/dist/skills/programming/scripts/typescript/new-project.ts +20 -81
- package/dist/skills/programming/scripts/typescript/oxc/.oxfmtrc.json +9 -0
- package/dist/skills/programming/scripts/typescript/oxc/.oxlintrc.json +46 -0
- package/dist/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/LICENSE +21 -0
- package/dist/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/index.js +10 -0
- package/dist/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/no-em-dash-copy.js +28 -0
- package/dist/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/require-suppression-reason.js +62 -0
- package/dist/skills/programming/scripts/typescript/oxc/tsconfig.json +29 -0
- package/dist/skills/refactor/SKILL.md +13 -13
- package/dist/skills/ultimate-browsing/engine/AGENTS.md +1 -1
- package/dist/skills/ulw-execute/SKILL.md +1 -1
- package/dist/skills/ulw-research/AGENTS.md +21 -3
- package/dist/skills/ulw-research/SKILL.md +37 -12
- package/dist/skills/ulw-research/references/deliverable-phase.md +77 -16
- package/dist/skills/ulw-research/references/fact-ledger-grammar.md +134 -0
- 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 +68 -3
- 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/claims-index.mjs +125 -0
- package/dist/skills/ulw-research/scripts/claims-index.test.ts +149 -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 +89 -3
- package/dist/skills/ulw-research/scripts/contracts.test.ts +100 -12
- 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 +197 -0
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.mjs +250 -0
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +156 -0
- package/dist/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +160 -0
- package/dist/skills/ulw-research/scripts/fact-ledger-schema.mjs +221 -0
- package/dist/skills/ulw-research/scripts/fact-ledger-schema.test.ts +43 -0
- package/dist/skills/ulw-research/scripts/fact-ledger.mjs +123 -0
- package/dist/skills/ulw-research/scripts/fact-ledger.test.ts +254 -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 +150 -0
- package/dist/skills/ulw-research/scripts/fidelity-brief.test.ts +179 -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 +64 -0
- package/dist/skills/ulw-research/scripts/fidelity-state.test.ts +46 -0
- 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-context.test.ts +64 -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 +29 -9
- package/dist/skills/ulw-research/scripts/gates-structure.mjs +1 -1
- package/dist/skills/ulw-research/scripts/gates-text.mjs +1 -27
- 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/lexicon.mjs +47 -0
- package/dist/skills/ulw-research/scripts/lexicon.test.ts +57 -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/numerals.mjs +226 -0
- package/dist/skills/ulw-research/scripts/numerals.test.ts +120 -0
- package/dist/skills/ulw-research/scripts/outcome.mjs +8 -8
- package/dist/skills/ulw-research/scripts/outcome.test.ts +29 -0
- package/dist/skills/ulw-research/scripts/particles-ko.mjs +108 -0
- package/dist/skills/ulw-research/scripts/particles-ko.test.ts +86 -0
- 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/rational.mjs +139 -0
- package/dist/skills/ulw-research/scripts/rational.test.ts +46 -0
- package/dist/skills/ulw-research/scripts/report-tools-commands.mjs +21 -10
- 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/sentence-split.mjs +77 -0
- package/dist/skills/ulw-research/scripts/sentence-split.test.ts +109 -0
- 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/claim-graph.md +13 -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/tests/fixtures/fidelity-brief-locked.md +17 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/fidelity-brief-pending.md +17 -0
- package/dist/skills/ulw-research/scripts/text-normalize.mjs +82 -0
- package/dist/skills/ulw-research/scripts/text-normalize.test.ts +73 -0
- package/dist/skills/ulw-research/scripts/text-similarity.mjs +28 -0
- package/dist/skills/ulw-research/scripts/text-similarity.test.ts +38 -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 +105 -0
- package/dist/skills/ulw-research/scripts/units.test.ts +83 -0
- package/dist/skills/visual-qa/SKILL.md +70 -63
- package/dist/skills/visual-qa/scripts/skill-doc-fields.test.ts +34 -0
- package/dist/testing/module-mock-lifecycle.d.ts +1 -1
- package/dist/tools/delegate-task/constants.d.ts +1 -1
- package/dist/tools/session-manager/constants.d.ts +1 -1
- package/dist/tools/session-manager/file-storage.d.ts +9 -2
- package/dist/tools/session-manager/sdk-storage.d.ts +19 -1
- package/dist/tools/session-manager/session-formatter.d.ts +3 -3
- package/dist/tools/session-manager/storage.d.ts +8 -2
- package/dist/tools/skill/mcp-capability-formatter.d.ts +1 -1
- package/dist/tools/skill/native-skills.d.ts +2 -2
- package/dist/tools/skill/skill-mcp-authority.d.ts +3 -0
- package/dist/tui.js +537 -104
- package/docs/reference/web-terminal-visual-qa.md +8 -0
- package/package.json +24 -16
- package/packages/lsp-core/src/lsp/client-wrapper.ts +4 -4
- package/packages/lsp-core/src/mcp-protocol-pin.test.ts +29 -0
- package/packages/lsp-core/src/mcp.ts +5 -2
- package/packages/lsp-core/src/post-edit/not-installed.test.ts +2 -2
- package/packages/lsp-daemon/dist/cli.js +7 -7
- package/packages/lsp-daemon/dist/client.js +7 -7
- package/packages/lsp-daemon/dist/index.js +7 -7
- package/packages/lsp-daemon/package.json +3 -3
- package/packages/lsp-tools-mcp/dist/cli.js +7 -7
- package/packages/lsp-tools-mcp/dist/mcp.js +7 -7
- package/packages/lsp-tools-mcp/dist/tools.js +4 -4
- package/packages/lsp-tools-mcp/package.json +3 -3
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +77 -26
- 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/bootstrap/src/hook.ts +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/biome.json +1 -21
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +4 -4
- 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/biome.json +1 -21
- 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 +4 -4
- package/packages/omo-codex/plugin/components/lsp/biome.json +1 -21
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +4 -4
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +4 -4
- package/packages/omo-codex/plugin/components/lsp/scripts/build-runtime.mjs +0 -1
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook-not-installed.test.ts +1 -1
- package/packages/omo-codex/plugin/components/rules/biome.json +1 -21
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +4 -4
- 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/biome.json +1 -21
- package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +90 -1
- package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +91 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +4 -4
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/biome.json +1 -21
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/biome.json +1 -21
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/directive.md +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 +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/biome.json +1 -21
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +7 -7
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +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 +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/commit-mutations.mjs +2 -0
- 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 +14 -9
- package/packages/omo-codex/plugin/skills/ast-grep/AGENTS.md +1 -1
- package/packages/omo-codex/plugin/skills/browser/SKILL.md +21 -22
- package/packages/omo-codex/plugin/skills/browser/references/commands.md +2 -9
- package/packages/omo-codex/plugin/skills/browser/references/install.md +1 -2
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/omo-codex/plugin/skills/browser/scripts/browser-doctor.mjs +2 -2
- package/packages/omo-codex/plugin/skills/browser/scripts/browser-engine-guard.mjs +63 -487
- package/packages/omo-codex/plugin/skills/browser/scripts/browser-install.mjs +1 -1
- package/packages/omo-codex/plugin/skills/browser/scripts/omowright.mjs +1 -10
- package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +79 -118
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +75 -224
- package/packages/omo-codex/plugin/skills/frontend/references/design/stylegallery.md +32 -27
- package/packages/omo-codex/plugin/skills/frontend/references/perfection/README.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/check-accent-stripes.ts +8 -24
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/check-draft-guard.ts +40 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/collect-files.ts +53 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/findings.ts +16 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/lint.ts +38 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/rules.ts +69 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/draft-guard/text-nodes.ts +121 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/attr-lookalikes.tsx +10 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/bound-values.tsx +12 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/const-generics.tsx +3 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/generics.tsx +7 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/measured-stats.html +6 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/plain-copy.html +9 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/state-copy.html +11 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/attribute-edges.html +7 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/components.tsx +16 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/decorative-badge.vue +7 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/emoji-icons.tsx +9 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/jsx-edges.tsx +1 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.html +6 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.tsx +8 -0
- package/packages/omo-codex/plugin/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/vanity-stats.html +5 -0
- package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/programming/SKILL.md +10 -7
- package/packages/omo-codex/plugin/skills/programming/references/typescript/README.md +10 -7
- package/packages/omo-codex/plugin/skills/programming/references/typescript/bootstrap.md +9 -23
- package/packages/omo-codex/plugin/skills/programming/references/typescript/oxc-lint.md +130 -0
- package/packages/omo-codex/plugin/skills/programming/references/typescript/tsconfig-strict.md +6 -62
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +12 -12
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/new-project.ts +20 -81
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/.oxfmtrc.json +9 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/.oxlintrc.json +46 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/LICENSE +21 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/index.js +10 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/no-em-dash-copy.js +28 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/require-suppression-reason.js +62 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/oxc/tsconfig.json +29 -0
- package/packages/omo-codex/plugin/skills/refactor/SKILL.md +14 -14
- package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +21 -3
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +37 -12
- package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +77 -16
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +134 -0
- 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 +68 -3
- 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/claims-index.mjs +125 -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 +89 -3
- 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 +250 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-schema.mjs +221 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger.mjs +123 -0
- 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 +150 -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 +64 -0
- 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 +29 -9
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-structure.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-text.mjs +1 -27
- 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/lexicon.mjs +47 -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/numerals.mjs +226 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +8 -8
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/particles-ko.mjs +108 -0
- 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/rational.mjs +139 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +21 -10
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +15 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/sentence-split.mjs +77 -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/text-normalize.mjs +82 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/text-similarity.mjs +28 -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 +105 -0
- package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +71 -64
- package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +4 -1
- 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 +12 -8
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +169 -30
- package/packages/shared-skills/skills/ast-grep/AGENTS.md +1 -1
- package/packages/shared-skills/skills/browser/SKILL.md +21 -22
- package/packages/shared-skills/skills/browser/references/commands.md +2 -9
- package/packages/shared-skills/skills/browser/references/install.md +1 -2
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/shared-skills/skills/browser/scripts/browser-doctor.mjs +2 -2
- package/packages/shared-skills/skills/browser/scripts/browser-engine-guard.mjs +63 -487
- package/packages/shared-skills/skills/browser/scripts/browser-install.mjs +1 -1
- package/packages/shared-skills/skills/browser/scripts/omowright.mjs +1 -10
- package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +2 -2
- package/packages/shared-skills/skills/frontend/SKILL.md +79 -118
- package/packages/shared-skills/skills/frontend/references/design/README.md +75 -224
- package/packages/shared-skills/skills/frontend/references/design/stylegallery.md +32 -27
- package/packages/shared-skills/skills/frontend/references/perfection/README.md +2 -2
- package/packages/shared-skills/skills/frontend/scripts/perfection/check-accent-stripes.ts +8 -24
- package/packages/shared-skills/skills/frontend/scripts/perfection/check-draft-guard.test.ts +101 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/check-draft-guard.ts +40 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/collect-files.test.ts +50 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/collect-files.ts +53 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/findings.ts +16 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/lint.ts +38 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/rules.ts +69 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/draft-guard/text-nodes.ts +121 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/attr-lookalikes.tsx +10 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/bound-values.tsx +12 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/const-generics.tsx +3 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/generics.tsx +7 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/measured-stats.html +6 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/plain-copy.html +9 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/negative/state-copy.html +11 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/attribute-edges.html +7 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/components.tsx +16 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/decorative-badge.vue +7 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/emoji-icons.tsx +9 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/jsx-edges.tsx +1 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.html +6 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/promo-copy.tsx +8 -0
- package/packages/shared-skills/skills/frontend/scripts/perfection/fixtures/draft-guard/positive/vanity-stats.html +5 -0
- package/packages/shared-skills/skills/programming/SKILL.md +10 -7
- package/packages/shared-skills/skills/programming/references/typescript/README.md +10 -7
- package/packages/shared-skills/skills/programming/references/typescript/bootstrap.md +9 -23
- package/packages/shared-skills/skills/programming/references/typescript/oxc-lint.md +130 -0
- package/packages/shared-skills/skills/programming/references/typescript/tsconfig-strict.md +6 -62
- package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.ts +12 -12
- package/packages/shared-skills/skills/programming/scripts/typescript/new-project.test.ts +162 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/new-project.ts +20 -81
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/.oxfmtrc.json +9 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/.oxlintrc.json +46 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/LICENSE +21 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/index.js +10 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/no-em-dash-copy.js +28 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/lint/oxlint-plugin/require-suppression-reason.js +62 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/oxc/tsconfig.json +29 -0
- package/packages/shared-skills/skills/refactor/SKILL.md +13 -13
- package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +1 -1
- package/packages/shared-skills/skills/ulw-execute/SKILL.md +1 -1
- package/packages/shared-skills/skills/ulw-research/AGENTS.md +21 -3
- package/packages/shared-skills/skills/ulw-research/SKILL.md +37 -12
- package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +77 -16
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +134 -0
- 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 +68 -3
- 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/claims-index.mjs +125 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claims-index.test.ts +149 -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 +89 -3
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +100 -12
- 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 +197 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +250 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +156 -0
- 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 +221 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-schema.test.ts +43 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.mjs +123 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger.test.ts +254 -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 +150 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-brief.test.ts +179 -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 +64 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-state.test.ts +46 -0
- 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-context.test.ts +64 -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 +29 -9
- package/packages/shared-skills/skills/ulw-research/scripts/gates-structure.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/gates-text.mjs +1 -27
- 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/lexicon.mjs +47 -0
- package/packages/shared-skills/skills/ulw-research/scripts/lexicon.test.ts +57 -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/numerals.mjs +226 -0
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +120 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +8 -8
- package/packages/shared-skills/skills/ulw-research/scripts/outcome.test.ts +29 -0
- package/packages/shared-skills/skills/ulw-research/scripts/particles-ko.mjs +108 -0
- package/packages/shared-skills/skills/ulw-research/scripts/particles-ko.test.ts +86 -0
- 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/rational.mjs +139 -0
- package/packages/shared-skills/skills/ulw-research/scripts/rational.test.ts +46 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +21 -10
- 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/sentence-split.mjs +77 -0
- package/packages/shared-skills/skills/ulw-research/scripts/sentence-split.test.ts +109 -0
- 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/claim-graph.md +13 -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/tests/fixtures/fidelity-brief-locked.md +17 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/fidelity-brief-pending.md +17 -0
- package/packages/shared-skills/skills/ulw-research/scripts/text-normalize.mjs +82 -0
- package/packages/shared-skills/skills/ulw-research/scripts/text-normalize.test.ts +73 -0
- package/packages/shared-skills/skills/ulw-research/scripts/text-similarity.mjs +28 -0
- package/packages/shared-skills/skills/ulw-research/scripts/text-similarity.test.ts +38 -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 +105 -0
- package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +83 -0
- package/packages/shared-skills/skills/visual-qa/SKILL.md +70 -63
- package/packages/shared-skills/skills/visual-qa/scripts/skill-doc-fields.test.ts +34 -0
- package/script/qa/web-terminal-visual-qa.mjs +6 -2
- package/script/qa/xterm-live-terminal.mjs +26 -3
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// fact-ledger-kinds.mjs - the per-kind column rules of the fact ledger as one table, KIND_RULES.
|
|
2
|
+
// validateLedger checks every per-kind column through kindRuleErrors, the writers read their
|
|
3
|
+
// per-kind choices (id prefix, measured exact) from it, and the per-kind table in
|
|
4
|
+
// references/fact-ledger-grammar.md is rendered from it (`node fact-ledger-kinds.mjs --render-grammar`).
|
|
5
|
+
// Also the value primitives the schema shares. Pure functions with no IO; node: builtins only.
|
|
6
|
+
|
|
7
|
+
import { isCliEntry } from "./entry-guard.mjs"
|
|
8
|
+
import { canonicalUnit, inputScale, RuleError } from "./unit-algebra.mjs"
|
|
9
|
+
import { metadataErrors } from "./unit-metadata.mjs"
|
|
10
|
+
import { mul } from "./rational.mjs"
|
|
11
|
+
import { placesOf, roundPlaces, toBig } from "./unit-precision.mjs"
|
|
12
|
+
import { OPERATION_RULES } from "./unit-rules.mjs"
|
|
13
|
+
|
|
14
|
+
export const DECIMAL = /^-?\d+(?:\.\d+)?$/
|
|
15
|
+
const INTEGER = /^-?\d+$/
|
|
16
|
+
const SHA256 = /^[0-9a-f]{64}$/
|
|
17
|
+
const SOURCE_ID = /^S\d+$/
|
|
18
|
+
|
|
19
|
+
export const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value)
|
|
20
|
+
export const isString = (value) => typeof value === "string"
|
|
21
|
+
export const isNonEmpty = (value) => typeof value === "string" && value.length > 0
|
|
22
|
+
export const isCount = (value) => Number.isInteger(value) && value >= 0
|
|
23
|
+
const isDecimal = (value) => isString(value) && DECIMAL.test(value)
|
|
24
|
+
export function isRational(exact) {
|
|
25
|
+
if (!isObject(exact) || Object.keys(exact).sort().join(",") !== "den,num" || !isString(exact.num) || !isString(exact.den) || !INTEGER.test(exact.num) || !/^[1-9]\d*$/.test(exact.den)) return false
|
|
26
|
+
let a = BigInt(exact.num)
|
|
27
|
+
let b = BigInt(exact.den)
|
|
28
|
+
if (String(a) !== exact.num) return false
|
|
29
|
+
if (a < 0n) a = -a
|
|
30
|
+
while (b !== 0n) [a, b] = [b, a % b]
|
|
31
|
+
return a === 1n
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The exact rational of a decimal string, reduced. */
|
|
35
|
+
export function exactFromDecimal(value) {
|
|
36
|
+
const [whole, fraction = ""] = value.replace(/^-/, "").split(".")
|
|
37
|
+
let num = BigInt(whole + fraction)
|
|
38
|
+
let den = 10n ** BigInt(fraction.length)
|
|
39
|
+
let a = num
|
|
40
|
+
let b = den
|
|
41
|
+
while (b !== 0n) [a, b] = [b, a % b]
|
|
42
|
+
if (a > 1n) [num, den] = [num / a, den / a]
|
|
43
|
+
return { num: `${value.startsWith("-") && num !== 0n ? "-" : ""}${num}`, den: `${den}` }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** @returns {string[]} what is wrong with a locator object, each message prefixed with `at`. */
|
|
47
|
+
export function locatorErrors(locator, at) {
|
|
48
|
+
const errors = []
|
|
49
|
+
if (!isObject(locator)) return errors
|
|
50
|
+
if (!isNonEmpty(locator.artifact)) errors.push(`${at}.artifact must be a non-empty string`)
|
|
51
|
+
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
|
+
for (const key of ["page", "line"]) {
|
|
53
|
+
if (locator[key] !== undefined && !(Number.isInteger(locator[key]) && locator[key] >= 1)) errors.push(`${at}.${key} must be an integer >= 1`)
|
|
54
|
+
}
|
|
55
|
+
for (const key of ["table", "row_label", "col_label"]) {
|
|
56
|
+
if (locator[key] !== undefined && !isString(locator[key])) errors.push(`${at}.${key} must be a string`)
|
|
57
|
+
}
|
|
58
|
+
return errors
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const isLocator = (value) => isObject(value) && locatorErrors(value, "").length === 0
|
|
62
|
+
const orNull = (test) => (value, row) => value === null || test(value, row)
|
|
63
|
+
const locatorDetail = (value, at) => locatorErrors(value, at)
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Every rule a KIND_RULES cell may name: `cell` is its text in the grammar table, `says` ends the
|
|
67
|
+
* error message, `test(value, row)` decides it, and `prefix` (id rules only) is what nextId uses.
|
|
68
|
+
*/
|
|
69
|
+
export const RULES = Object.freeze({
|
|
70
|
+
f_id: { cell: "`F<n>`", says: "F<n>", prefix: "F", test: (v) => isString(v) && /^F\d+$/.test(v) },
|
|
71
|
+
q_id: { cell: "`Q<n>`", says: "Q<n>", prefix: "Q", test: (v) => isString(v) && /^Q\d+$/.test(v) },
|
|
72
|
+
decimal: { cell: "decimal", says: "a decimal string", test: isDecimal },
|
|
73
|
+
null: { cell: "null", says: "null", test: (v) => v === null },
|
|
74
|
+
source: { cell: "`S<n>`", says: "S<n>", test: (v) => isString(v) && SOURCE_ID.test(v) },
|
|
75
|
+
source_or_null: { cell: "`S<n>` or null", says: "S<n> or null", test: orNull((v) => isString(v) && SOURCE_ID.test(v)) },
|
|
76
|
+
locator: { cell: "object", says: "a locator object", test: isLocator, detail: locatorDetail },
|
|
77
|
+
locator_or_null: { cell: "object or null", says: "a locator object or null", test: orNull(isLocator), detail: locatorDetail },
|
|
78
|
+
sha256: { cell: "sha256", says: "a sha256 hex digest", test: (v) => isString(v) && SHA256.test(v) },
|
|
79
|
+
sha256_or_null: { cell: "sha256 or null", says: "a sha256 hex digest or null", test: orNull((v) => isString(v) && SHA256.test(v)) },
|
|
80
|
+
string_or_null: { cell: "string or null", says: "null or a string", test: orNull(isString) },
|
|
81
|
+
non_empty: { cell: "non-empty", says: "a non-empty string (required for this kind)", test: isNonEmpty },
|
|
82
|
+
from_value: {
|
|
83
|
+
cell: "from `value`",
|
|
84
|
+
says: "the reduced exact rational of value",
|
|
85
|
+
test: (v, row) => isRational(v) && (!isDecimal(row.value) || (v.num === exactFromDecimal(row.value).num && v.den === exactFromDecimal(row.value).den)),
|
|
86
|
+
},
|
|
87
|
+
rational_or_null: { cell: "rational or null", says: "null or { num: integer string, den: positive integer string }", test: orNull(isRational) },
|
|
88
|
+
by_derive: { cell: "reduced rational", says: "a reduced { num: integer string, den: positive integer string }", test: isRational },
|
|
89
|
+
context_by_derive: { cell: "written by derive (object or null)", says: "null or an object", test: orNull(isObject) },
|
|
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
|
+
convert_operation: { cell: "`convert`", says: "convert", test: (v) => Object.hasOwn(OPERATION_RULES, v) && OPERATION_RULES[v].kind === "converted" },
|
|
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 },
|
|
93
|
+
compatibility: { cell: "checked or unchecked (expr)", says: "the operation's compatibility", test: (v, r) => Object.hasOwn(OPERATION_RULES, r.operation) && v === OPERATION_RULES[r.operation].compatibility },
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
/** The grammar's per-kind columns, in table order; `exact` follows `value` because it reads it. */
|
|
97
|
+
export const KIND_COLUMNS = Object.freeze(["fact_id", "value", "source_id", "locator", "snapshot_sha256", "rationale", "exact", "context", "operation", "precision", "compatibility"])
|
|
98
|
+
|
|
99
|
+
const sourced = { source_id: "source", locator: "locator", snapshot_sha256: "sha256" }
|
|
100
|
+
const unsourced = { source_id: "source_or_null", locator: "locator_or_null", snapshot_sha256: "sha256_or_null" }
|
|
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
|
+
|
|
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: "null", operation: "null" }),
|
|
105
|
+
quote: row({ fact_id: "q_id", value: "null", ...sourced, rationale: "string_or_null", exact: "null", context: "null", operation: "null" }),
|
|
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
|
+
assumed: row({ fact_id: "f_id", value: "decimal", ...unsourced, rationale: "non_empty", exact: "rational_or_null", context: "null", operation: "null" }),
|
|
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
|
+
})
|
|
110
|
+
|
|
111
|
+
/** The id prefix nextId assigns for a kind; an unknown kind gets F and fails validation later. */
|
|
112
|
+
export function idPrefix(kind) {
|
|
113
|
+
return RULES[KIND_RULES[kind]?.fact_id ?? "f_id"].prefix
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Whether addRow computes the row's exact from its decimal value. */
|
|
117
|
+
export function exactComesFromValue(kind) {
|
|
118
|
+
return KIND_RULES[kind]?.exact === "from_value"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Columns added after schema version 1 shipped; a ledger written before them still loads (dimension 31). */
|
|
122
|
+
export const LATER_COLUMNS = Object.freeze(["context", "precision", "compatibility"])
|
|
123
|
+
const DERIVED_KINDS = Object.freeze(["derived", "converted"])
|
|
124
|
+
const PRE_REGISTRY_COLUMNS = KIND_COLUMNS.slice(0, KIND_COLUMNS.indexOf("exact") + 1)
|
|
125
|
+
|
|
126
|
+
/** A derived or converted row written before the later columns existed: loadable, never an operand. */
|
|
127
|
+
export const isLegacyDerived = (row) => isObject(row) && DERIVED_KINDS.includes(row.kind) && LATER_COLUMNS.every((column) => !Object.hasOwn(row, column))
|
|
128
|
+
/** A derived or converted row carrying this version's columns: the only rows checked strictly at read time. */
|
|
129
|
+
export const isCurrentDerived = (row) => isObject(row) && DERIVED_KINDS.includes(row.kind) && !isLegacyDerived(row)
|
|
130
|
+
|
|
131
|
+
/** An exact carrying keys beyond num and den: never normalized, never silently stripped, flagged on use. */
|
|
132
|
+
export const hasUnknownExactKeys = (exact) => isObject(exact) && isString(exact.num) && isString(exact.den) && Object.keys(exact).sort().join(",") !== "den,num"
|
|
133
|
+
/** Any integer pair dev's writer accepted, reduced: sign on num, no leading zeros, gcd 1; anything else as given. */
|
|
134
|
+
function readExact(exact) {
|
|
135
|
+
if (!isObject(exact) || hasUnknownExactKeys(exact) || !isString(exact.num) || !isString(exact.den) || !INTEGER.test(exact.num) || !/^\d+$/.test(exact.den) || /^0+$/.test(exact.den)) return exact
|
|
136
|
+
const { num, den } = mul({ num: BigInt(exact.num), den: BigInt(exact.den) }, 1n)
|
|
137
|
+
return { num: String(num), den: String(den) }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const readDecimal = (value) => (isDecimal(value) ? value.replace(/^(-?)0+(?=\d)/, "$1") : value)
|
|
141
|
+
|
|
142
|
+
/** A source unit read through the alias table; unknown, scaled, or unparseable text stays as given. */
|
|
143
|
+
export function sourceUnit(unit) {
|
|
144
|
+
try {
|
|
145
|
+
const canonical = canonicalUnit(unit)
|
|
146
|
+
return isString(canonical) && !inputScale(unit) ? canonical : unit
|
|
147
|
+
} catch (error) {
|
|
148
|
+
if (error instanceof RuleError) return unit
|
|
149
|
+
throw error
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* A row as every reader sees it (dimension 31 wins at read time): rows not written by derive in this
|
|
155
|
+
* version get reduced exacts and decimals; source rows also get canonical units and null later columns.
|
|
156
|
+
* Current derived rows stay as stored so tampering is still detected. Idempotent.
|
|
157
|
+
*/
|
|
158
|
+
export function readRow(row) {
|
|
159
|
+
if (!isObject(row) || !Object.hasOwn(KIND_RULES, row.kind) || isCurrentDerived(row)) return row
|
|
160
|
+
const read = { ...row, value: readDecimal(row.value), exact: exactComesFromValue(row.kind) ? row.exact : readExact(row.exact) }
|
|
161
|
+
if (DERIVED_KINDS.includes(row.kind)) return read
|
|
162
|
+
read.unit = sourceUnit(row.unit)
|
|
163
|
+
for (const column of LATER_COLUMNS) if (!Object.hasOwn(read, column)) read[column] = null
|
|
164
|
+
return read
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const carriesDerivation = (row) => row.operation !== null || row.operands?.length || row.derived_from?.length || row.formula !== null || row.factor !== null
|
|
168
|
+
|
|
169
|
+
/** Dimension 32: a stored exact must equal the row's own value at the value's decimal places. */
|
|
170
|
+
function valueExactDisagreement(row) {
|
|
171
|
+
const exact = readExact(row.exact)
|
|
172
|
+
const value = readDecimal(row.value)
|
|
173
|
+
if (!isRational(exact) || !isDecimal(value) || roundPlaces(toBig(exact), placesOf(value)) === value) return null
|
|
174
|
+
return `value ${value} disagrees with exact ${exact.num}/${exact.den} at ${placesOf(value)} decimal places`
|
|
175
|
+
}
|
|
176
|
+
function unitFlaw(unit) {
|
|
177
|
+
try {
|
|
178
|
+
return isString(unit) && (canonicalUnit(unit) !== unit || inputScale(unit)) ? `unit ${unit} has no canonical stored text` : null
|
|
179
|
+
} catch (error) {
|
|
180
|
+
if (error instanceof RuleError) return `unit ${unit}: ${error.message}`
|
|
181
|
+
throw error
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* What a loadable source row (read through readRow) still breaks under the strict dimensions: dev's
|
|
187
|
+
* writer accepted it, so it loads, but derive and recompute refuse it as an operand with this shape.
|
|
188
|
+
* addRow refuses a new row that has any. Empty for derived and converted rows.
|
|
189
|
+
*/
|
|
190
|
+
export function readFlaws(row) {
|
|
191
|
+
if (!isObject(row) || DERIVED_KINDS.includes(row.kind)) return []
|
|
192
|
+
const flaws = []
|
|
193
|
+
if (carriesDerivation(row)) flaws.push({ shape: "dimension 1", detail: "a source row carries an operation or derivation fields", expected: "null operation and empty derivation fields on measured, quote, and assumed rows" })
|
|
194
|
+
const unit = unitFlaw(row.unit)
|
|
195
|
+
if (unit) flaws.push({ shape: "dimension 6", detail: unit, expected: "a registered or canonical unit without scale" })
|
|
196
|
+
for (const detail of metadataErrors(row)) flaws.push({ shape: "dimension 18", detail, expected: "present, type-valid metadata; explicit null when unknown" })
|
|
197
|
+
const unknownKeys = hasUnknownExactKeys(row.exact)
|
|
198
|
+
const disagreement = unknownKeys ? null : valueExactDisagreement(row)
|
|
199
|
+
if (unknownKeys) flaws.push({ shape: "dimension 7", detail: `exact has unknown keys: ${Object.keys(row.exact).filter((key) => key !== "num" && key !== "den").join(", ")}`, expected: "an exact with exactly num and den (the stored row is kept as written)" })
|
|
200
|
+
if (disagreement) flaws.push({ shape: "dimension 32", detail: disagreement, expected: "an exact equal to value at the value's decimal places" })
|
|
201
|
+
return flaws
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** @returns {string[]} every per-kind column error of one row; empty for an unknown kind. */
|
|
205
|
+
export function kindRuleErrors(candidate, at) {
|
|
206
|
+
const rules = KIND_RULES[candidate.kind]
|
|
207
|
+
if (rules === undefined) return []
|
|
208
|
+
const errors = []
|
|
209
|
+
const legacy = isLegacyDerived(candidate)
|
|
210
|
+
const columns = legacy ? PRE_REGISTRY_COLUMNS : isCurrentDerived(candidate) ? KIND_COLUMNS : KIND_COLUMNS.filter((column) => column !== "operation")
|
|
211
|
+
for (const column of columns) {
|
|
212
|
+
const value = candidate[column] === undefined && LATER_COLUMNS.includes(column) ? null : candidate[column]
|
|
213
|
+
const rule = RULES[legacy && column === "exact" ? "rational_or_null" : rules[column]]
|
|
214
|
+
// 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
|
|
216
|
+
if (rule.test(value, candidate)) continue
|
|
217
|
+
errors.push(`${at}.${column} must be ${rule.says} for a ${candidate.kind} row`)
|
|
218
|
+
if (rule.detail) errors.push(...rule.detail(value, `${at}.${column}`))
|
|
219
|
+
}
|
|
220
|
+
return errors
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** At-rest rules of current derived rows; source rows answer to readFlaws at use and at write instead. */
|
|
224
|
+
export function storedErrors(row) {
|
|
225
|
+
if (!isCurrentDerived(row)) return []
|
|
226
|
+
const errors = metadataErrors(row)
|
|
227
|
+
try {
|
|
228
|
+
if (canonicalUnit(row.unit) !== row.unit || inputScale(row.unit)) errors.push("unit must be canonical stored text")
|
|
229
|
+
} catch (error) { errors.push(error.message) }
|
|
230
|
+
return errors
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
export const KIND_TABLE_BEGIN = "<!-- kind-rules:begin (rendered from KIND_RULES; do not edit by hand) -->"
|
|
234
|
+
export const KIND_TABLE_END = "<!-- kind-rules:end -->"
|
|
235
|
+
|
|
236
|
+
/** The grammar's per-kind Markdown table, one line per column, ending in a newline. */
|
|
237
|
+
export function renderKindTable() {
|
|
238
|
+
const kinds = Object.keys(KIND_RULES)
|
|
239
|
+
const lines = [`| field | ${kinds.join(" | ")} |`, `|${"---|".repeat(kinds.length + 1)}`]
|
|
240
|
+
for (const column of KIND_COLUMNS) lines.push(`| \`${column}\` | ${kinds.map((kind) => RULES[KIND_RULES[kind][column]].cell).join(" | ")} |`)
|
|
241
|
+
return `${lines.join("\n")}\n`
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (isCliEntry(import.meta.url)) {
|
|
245
|
+
if (process.argv[2] === "--render-grammar" && process.argv.length === 3) process.stdout.write(renderKindTable())
|
|
246
|
+
else {
|
|
247
|
+
process.stderr.write("usage: node fact-ledger-kinds.mjs --render-grammar\n")
|
|
248
|
+
process.exitCode = 2
|
|
249
|
+
}
|
|
250
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { spawnSync } from "node:child_process"
|
|
3
|
+
import { readFileSync } from "node:fs"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
|
|
6
|
+
import { KIND_COLUMNS, KIND_RULES as RULES_TABLE, KIND_TABLE_BEGIN, KIND_TABLE_END, exactFromDecimal, renderKindTable } from "./fact-ledger-kinds.mjs"
|
|
7
|
+
import { FACT_KINDS, LEDGER_SCHEMA_VERSION, ROW_DEFAULTS, validateLedger } from "./fact-ledger-schema.mjs"
|
|
8
|
+
|
|
9
|
+
// Property tests generated from KIND_RULES: for every kind, rows built from each column's rule
|
|
10
|
+
// validate, and breaking exactly one column's rule is rejected with an error naming that column.
|
|
11
|
+
|
|
12
|
+
/** mulberry32: a fixed seed gives the same rows on every run. */
|
|
13
|
+
function prng(seed: number) {
|
|
14
|
+
let state = seed >>> 0
|
|
15
|
+
return () => {
|
|
16
|
+
state = (state + 0x6d2b79f5) >>> 0
|
|
17
|
+
let t = state
|
|
18
|
+
t = Math.imul(t ^ (t >>> 15), t | 1)
|
|
19
|
+
t ^= t + Math.imul(t ^ (t >>> 7), t | 61)
|
|
20
|
+
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const KIND_RULES = RULES_TABLE as Record<string, Record<string, string>>
|
|
25
|
+
|
|
26
|
+
type Random = () => number
|
|
27
|
+
const int = (random: Random, max: number) => Math.floor(random() * max)
|
|
28
|
+
const pick = <T>(random: Random, list: T[]): T => list[int(random, list.length)]
|
|
29
|
+
|
|
30
|
+
function decimal(random: Random) {
|
|
31
|
+
const whole = `${pick(random, ["-", ""])}${int(random, 5000)}`
|
|
32
|
+
return random() < 0.5 ? whole : `${whole}.${String(int(random, 1000)).padStart(pick(random, [1, 2, 3]), "0").slice(0, 3)}`
|
|
33
|
+
}
|
|
34
|
+
const rational = (random: Random) => exactFromDecimal(decimal(random))
|
|
35
|
+
const locator = (random: Random) => ({ artifact: `sources/S${1 + int(random, 9)}/text.txt`, char_start: 3, char_end: 3 + int(random, 80) })
|
|
36
|
+
const sha = (random: Random) => Array.from({ length: 64 }, () => "0123456789abcdef"[int(random, 16)]).join("")
|
|
37
|
+
|
|
38
|
+
type Row = Record<string, unknown> & { value: string }
|
|
39
|
+
type Gen = { valid: (random: Random, row: Row) => unknown; invalid: (random: Random, row: Row) => unknown }
|
|
40
|
+
|
|
41
|
+
/** One valid and one invalid generator per rule name; an unknown rule name fails the test below. */
|
|
42
|
+
const GENERATORS: Record<string, Gen> = {
|
|
43
|
+
f_id: { valid: (r) => `F${1 + int(r, 99)}`, invalid: (r) => `Q${1 + int(r, 99)}` },
|
|
44
|
+
q_id: { valid: (r) => `Q${1 + int(r, 99)}`, invalid: (r) => `F${1 + int(r, 99)}` },
|
|
45
|
+
decimal: { valid: decimal, invalid: (r) => pick(r, ["1e3", "", null, 12, "1,000"]) },
|
|
46
|
+
null: { valid: () => null, invalid: (r) => pick(r, ["0", { num: "1", den: "1" }, "S1", "why"]) },
|
|
47
|
+
source: { valid: (r) => `S${1 + int(r, 9)}`, invalid: (r) => pick(r, [null, "F1", "S"]) },
|
|
48
|
+
source_or_null: { valid: (r) => pick(r, [null, `S${1 + int(r, 9)}`]), invalid: (r) => pick(r, ["F1", "S", 3]) },
|
|
49
|
+
locator: { valid: locator, invalid: (r) => pick(r, [null, { artifact: "", char_start: 0, char_end: 1 }, { artifact: "a", char_start: 5, char_end: 1 }]) },
|
|
50
|
+
locator_or_null: { valid: (r) => pick(r, [null, locator(r)]), invalid: (r) => pick(r, ["here", { artifact: "", char_start: 0, char_end: 1 }]) },
|
|
51
|
+
sha256: { valid: sha, invalid: (r) => pick(r, [null, "abc", "A".repeat(64)]) },
|
|
52
|
+
sha256_or_null: { valid: (r) => pick(r, [null, sha(r)]), invalid: (r) => pick(r, ["abc", 7]) },
|
|
53
|
+
string_or_null: { valid: (r) => pick(r, [null, "", "a reason"]), invalid: (r) => pick(r, [5, false, {}]) },
|
|
54
|
+
non_empty: { valid: () => "assumed from the filing year", invalid: (r) => pick(r, ["", null]) },
|
|
55
|
+
from_value: {
|
|
56
|
+
valid: (_r, row) => exactFromDecimal(row.value),
|
|
57
|
+
invalid: (r, row) => {
|
|
58
|
+
const exact = exactFromDecimal(row.value)
|
|
59
|
+
return pick(r, [null, { num: `${BigInt(exact.num) + 7n}`, den: exact.den }, { num: `${BigInt(exact.num) * 2n}`, den: `${BigInt(exact.den) * 2n}` }])
|
|
60
|
+
},
|
|
61
|
+
},
|
|
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
|
+
by_derive: { valid: rational, invalid: (r) => pick(r, [null, { num: "1", den: "0" }, { num: "x", den: "2" }, "1/2"]) },
|
|
64
|
+
context_by_derive: { valid: (_r, row) => row.kind === "converted" ? { target: "km" } : null, invalid: (r) => pick(r, ["km", 3, ["km"], false]) },
|
|
65
|
+
derived_operation: { valid: () => "expr", invalid: (r) => pick(r, [null, "convert", "mean"]) },
|
|
66
|
+
convert_operation: { valid: () => "convert", invalid: (r) => pick(r, [null, "sum", "mean"]) },
|
|
67
|
+
precision: { valid: () => ({ places: 2, sigfigs: 3 }), invalid: (r) => pick(r, [null, { places: -1, sigfigs: 3 }, { places: 2, sigfigs: 0 }]) },
|
|
68
|
+
compatibility: { valid: (_r, row) => row.operation === "expr" ? "unchecked" : "checked", invalid: () => "invalid" },
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function baseRow(kind: string): Row {
|
|
72
|
+
const row: Record<string, unknown> = { ...structuredClone(ROW_DEFAULTS), kind, raw: "raw", quote: "quoted text", extracted_by: "fact add", observed_at: "2026-10-23T08:00:00Z", label: "x" }
|
|
73
|
+
if (kind === "derived" || kind === "converted") Object.assign(row, { operands: ["F1000"], derived_from: ["F1000"], formula: "F1000", render: "raw" })
|
|
74
|
+
return row as Row
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** A row whose every KIND_COLUMNS field follows its kind's rule, in column order (exact after value). */
|
|
78
|
+
function validRow(kind: string, random: Random) {
|
|
79
|
+
const row = baseRow(kind)
|
|
80
|
+
for (const column of KIND_COLUMNS) row[column] = GENERATORS[KIND_RULES[kind][column]].valid(random, row)
|
|
81
|
+
return row
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const ledgerOf = (row: object) => ({ schemaVersion: LEDGER_SCHEMA_VERSION, rows: [row, { ...validRow("measured", prng(42)), fact_id: "F1000", label: "dependency" }], events: [] })
|
|
85
|
+
const SEEDS = Array.from({ length: 60 }, (_, index) => index + 1)
|
|
86
|
+
|
|
87
|
+
describe("KIND_RULES covers the grammar", () => {
|
|
88
|
+
test("every kind has a rule for every column, and every rule has a generator", () => {
|
|
89
|
+
expect(Object.keys(KIND_RULES)).toEqual([...FACT_KINDS])
|
|
90
|
+
for (const kind of FACT_KINDS) {
|
|
91
|
+
expect(Object.keys(KIND_RULES[kind])).toEqual([...KIND_COLUMNS])
|
|
92
|
+
for (const column of KIND_COLUMNS) expect(Object.hasOwn(GENERATORS, KIND_RULES[kind][column])).toBe(true)
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
describe("rows generated from KIND_RULES", () => {
|
|
98
|
+
for (const kind of FACT_KINDS) {
|
|
99
|
+
test(`${kind}: seeded valid rows validate`, () => {
|
|
100
|
+
for (const seed of SEEDS) {
|
|
101
|
+
const row = validRow(kind, prng(seed))
|
|
102
|
+
expect({ seed, errors: validateLedger(ledgerOf(row)) }).toEqual({ seed, errors: [] })
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
for (const column of KIND_COLUMNS) {
|
|
107
|
+
test(`${kind}: breaking only the ${column} rule (${KIND_RULES[kind][column]}) is rejected`, () => {
|
|
108
|
+
for (const seed of SEEDS) {
|
|
109
|
+
const random = prng(seed * 7919)
|
|
110
|
+
const row = validRow(kind, random)
|
|
111
|
+
row[column] = GENERATORS[KIND_RULES[kind][column]].invalid(random, row)
|
|
112
|
+
const errors = validateLedger(ledgerOf(row))
|
|
113
|
+
expect({ seed, value: row[column], named: errors.some((error: string) => error.startsWith(`rows[0].${column}`)) }).toEqual({ seed, value: row[column], named: true })
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
test("review MEDIUM: a measured row with value -0.50 and exact 7/1 is rejected", () => {
|
|
120
|
+
const row = { ...validRow("measured", prng(1)), value: "-0.50", exact: { num: "7", den: "1" } }
|
|
121
|
+
expect(validateLedger(ledgerOf(row)).some((error: string) => error.startsWith("rows[0].exact"))).toBe(true)
|
|
122
|
+
expect(validateLedger(ledgerOf({ ...row, exact: { num: "-1", den: "2" } }))).toEqual([])
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test("review r6 LOW 3: context is a derived or converted column; a measured, quote, or assumed row carrying one is rejected", () => {
|
|
126
|
+
for (const kind of ["measured", "quote", "assumed"]) {
|
|
127
|
+
const row = { ...validRow(kind, prng(3)), context: { target: "km" } }
|
|
128
|
+
expect({ kind, named: validateLedger(ledgerOf(row)).some((error: string) => error.startsWith("rows[0].context")) }).toEqual({ kind, named: true })
|
|
129
|
+
}
|
|
130
|
+
expect(validateLedger(ledgerOf(validRow("converted", prng(4))))).toEqual([])
|
|
131
|
+
expect(validateLedger(ledgerOf({ ...validRow("derived", prng(4)), context: { target: "km" } })).length).toBeGreaterThan(0)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
test("review LOW 1: a quote row with a non-null exact is rejected", () => {
|
|
135
|
+
const row = { ...validRow("quote", prng(2)), exact: { num: "1", den: "1" } }
|
|
136
|
+
expect(validateLedger(ledgerOf(row)).some((error: string) => error.startsWith("rows[0].exact"))).toBe(true)
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
describe("the grammar's per-kind table is rendered from KIND_RULES", () => {
|
|
141
|
+
const grammar = readFileSync(join(import.meta.dir, "..", "references", "fact-ledger-grammar.md"), "utf8")
|
|
142
|
+
|
|
143
|
+
test("the committed table section equals renderKindTable() byte for byte", () => {
|
|
144
|
+
const begin = grammar.indexOf(KIND_TABLE_BEGIN)
|
|
145
|
+
const end = grammar.indexOf(KIND_TABLE_END)
|
|
146
|
+
expect(begin).toBeGreaterThanOrEqual(0)
|
|
147
|
+
expect(end).toBeGreaterThan(begin)
|
|
148
|
+
expect(grammar.slice(begin + KIND_TABLE_BEGIN.length + 1, end)).toBe(renderKindTable())
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
test("node fact-ledger-kinds.mjs --render-grammar prints the same table", () => {
|
|
152
|
+
const result = spawnSync("node", [join(import.meta.dir, "fact-ledger-kinds.mjs"), "--render-grammar"], { encoding: "utf8" })
|
|
153
|
+
expect(result.status).toBe(0)
|
|
154
|
+
expect(result.stdout).toBe(renderKindTable())
|
|
155
|
+
})
|
|
156
|
+
})
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { describe, expect, onTestFinished, test } from "bun:test"
|
|
2
|
+
import { copyFileSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
|
3
|
+
import { tmpdir } from "node:os"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
|
|
6
|
+
import { derive, recompute } from "./fact-derive.mjs"
|
|
7
|
+
import { LEDGER_FILENAME, addRow, emptyLedger, readLedger, validateLedger, writeLedger } from "./fact-ledger.mjs"
|
|
8
|
+
|
|
9
|
+
// Generated by dev's own addRow (tests/fixtures/fidelity/gen-legacy-dev.mjs pins the dev commit):
|
|
10
|
+
// no context/precision/compatibility columns, alias units, unreduced and zero-padded exacts.
|
|
11
|
+
const FIXTURE = join(import.meta.dir, "tests", "fixtures", "fidelity", "ledger-legacy-dev.json")
|
|
12
|
+
const SOURCE_IDS = ["F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "Q1", "F9", "F10", "F11", "F12", "F13", "F16", "F17", "F18"]
|
|
13
|
+
|
|
14
|
+
type Row = Record<string, unknown>
|
|
15
|
+
type Ledger = { schemaVersion: number; rows: Row[]; events: unknown[] }
|
|
16
|
+
|
|
17
|
+
function tempDir(): string {
|
|
18
|
+
const dir = mkdtempSync(join(tmpdir(), "ulw-legacy-"))
|
|
19
|
+
onTestFinished(() => rmSync(dir, { recursive: true, force: true }))
|
|
20
|
+
return dir
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const fixture = (): Ledger => JSON.parse(readFileSync(FIXTURE, "utf8"))
|
|
24
|
+
const rowOf = (ledger: Ledger, id: string): Row => ledger.rows.find((row) => row.fact_id === id) as Row
|
|
25
|
+
|
|
26
|
+
function loaded(): { dir: string; ledger: Ledger } {
|
|
27
|
+
const dir = tempDir()
|
|
28
|
+
copyFileSync(FIXTURE, join(dir, LEDGER_FILENAME))
|
|
29
|
+
return { dir, ledger: readLedger(dir) as Ledger }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("ledgers written by dev's writer (dimension 31 wins at read time)", () => {
|
|
33
|
+
test("#given dev's generated ledger #when validated #then it has no errors", () => {
|
|
34
|
+
expect(validateLedger(fixture())).toEqual([])
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test("#given dev's ledger #when read #then exacts, decimals, and source units are normalized", () => {
|
|
38
|
+
const { ledger } = loaded()
|
|
39
|
+
expect(["F1", "F3", "F4", "F5", "F6", "F8", "F11"].map((id) => rowOf(ledger, id).unit)).toEqual(["km", "%", "%p", "person", "KRW-e3", "%", "km"])
|
|
40
|
+
const exacts = Object.fromEntries(["F10", "F11", "F12", "F13", "F14", "F15", "F18"].map((id) => [id, rowOf(ledger, id).exact]))
|
|
41
|
+
expect(exacts).toEqual({ F10: { num: "1", den: "1" }, F11: { num: "5", den: "2" }, F12: { num: "7", den: "1" }, F13: { num: "-5", den: "2" }, F14: { num: "41", den: "2" }, F15: { num: "12000", den: "1" }, F18: { num: "3", den: "1", note: "from the brief" } })
|
|
42
|
+
expect(rowOf(ledger, "F7").value).toBe("7")
|
|
43
|
+
for (const id of SOURCE_IDS) {
|
|
44
|
+
for (const column of ["context", "precision", "compatibility"]) expect(rowOf(ledger, id)[column]).toBeNull()
|
|
45
|
+
}
|
|
46
|
+
for (const id of ["F14", "F15"]) expect(Object.hasOwn(rowOf(ledger, id), "precision")).toBe(false)
|
|
47
|
+
expect(validateLedger(ledger)).toEqual([])
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test("#given dev's ledger #when measured rows are summed #then the new row is canonical and recomputes", () => {
|
|
51
|
+
for (const ledger of [fixture(), loaded().ledger]) {
|
|
52
|
+
const result = derive(ledger, { op: "sum", operands: ["F1", "F2"], unit: "km" })
|
|
53
|
+
expect(result.error).toBeUndefined()
|
|
54
|
+
expect(result.row).toMatchObject({ kind: "derived", unit: "km", value: "21", exact: { num: "41", den: "2" }, compatibility: "checked" })
|
|
55
|
+
const padded = derive(ledger, { op: "sum", operands: ["F1", "F7"], unit: "km" })
|
|
56
|
+
expect(padded.row).toMatchObject({ value: "19", exact: { num: "19", den: "1" } })
|
|
57
|
+
expect(validateLedger(ledger)).toEqual([])
|
|
58
|
+
expect(recompute(ledger, result.row).ok).toBe(true)
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test("#given a legacy derived or converted row #when recomputed #then it reports a legacy derived row", () => {
|
|
63
|
+
for (const ledger of [fixture(), loaded().ledger]) {
|
|
64
|
+
for (const id of ["F14", "F15"]) {
|
|
65
|
+
const result = recompute(ledger, rowOf(ledger, id))
|
|
66
|
+
expect(result.ok).toBe(false)
|
|
67
|
+
expect(result.shape).toBe("legacy derived row")
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test("#given a legacy derived or converted row #when used as an operand #then derive refuses it", () => {
|
|
73
|
+
const ledger = fixture()
|
|
74
|
+
const before = ledger.rows.length
|
|
75
|
+
for (const id of ["F14", "F15"]) {
|
|
76
|
+
const result = derive(ledger, { op: "sum", operands: [id, "F2"], unit: "km" })
|
|
77
|
+
expect(result.error).toBe("incompatible_operands")
|
|
78
|
+
expect(result.shape).toBe("legacy derived row")
|
|
79
|
+
}
|
|
80
|
+
expect(ledger.rows.length).toBe(before)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
test("#given a loadable row the strict dimensions reject #when used as an operand #then it is flagged by dimension", () => {
|
|
84
|
+
const ledger = loaded().ledger
|
|
85
|
+
const before = ledger.rows.length
|
|
86
|
+
for (const [id, shape] of [["F16", "dimension 18"], ["F17", "dimension 1"]]) {
|
|
87
|
+
const result = derive(ledger, { op: "sum", operands: [id, "F2"], unit: "km" })
|
|
88
|
+
expect(result.error).toBe("incompatible_operands")
|
|
89
|
+
expect(result.shape).toBe(shape)
|
|
90
|
+
}
|
|
91
|
+
expect(ledger.rows.length).toBe(before)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test("#given a new source row with a flaw dev accepted #when added #then this version's writer refuses it", () => {
|
|
95
|
+
const ledger = fixture()
|
|
96
|
+
const blank = { ...rowOf(ledger, "F16"), fact_id: undefined, label: "blank-again" }
|
|
97
|
+
expect(() => addRow(ledger, blank)).toThrow("dimension 18")
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test("#given dev's ledger #when read and written twice #then the second pass changes nothing", () => {
|
|
101
|
+
const { dir, ledger } = loaded()
|
|
102
|
+
writeLedger(dir, ledger)
|
|
103
|
+
const once = readFileSync(join(dir, LEDGER_FILENAME), "utf8")
|
|
104
|
+
const written = JSON.parse(once) as Ledger
|
|
105
|
+
expect(rowOf(written, "F11").exact).toEqual({ num: "5", den: "2" })
|
|
106
|
+
expect(Object.hasOwn(rowOf(written, "F14"), "precision")).toBe(false)
|
|
107
|
+
writeLedger(dir, readLedger(dir))
|
|
108
|
+
expect(readFileSync(join(dir, LEDGER_FILENAME), "utf8")).toBe(once)
|
|
109
|
+
expect(readLedger(dir)).toEqual(written)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test("#given a current derived row with its later columns stripped #when validated #then it reads as legacy and never recomputes", () => {
|
|
113
|
+
const ledger = fixture()
|
|
114
|
+
const made = derive(ledger, { op: "sum", operands: ["F1", "F2"], unit: "km" }).row as Row
|
|
115
|
+
const stripped = rowOf(ledger, made.fact_id as string)
|
|
116
|
+
for (const column of ["context", "precision", "compatibility"]) delete stripped[column]
|
|
117
|
+
stripped.unit = "kilometers"
|
|
118
|
+
expect(validateLedger(ledger)).toEqual([])
|
|
119
|
+
expect(recompute(ledger, stripped).shape).toBe("legacy derived row")
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
test("#given a ledger written by this version #when read and written #then it is unchanged", () => {
|
|
123
|
+
const dir = tempDir()
|
|
124
|
+
const ledger = emptyLedger() as Ledger
|
|
125
|
+
const source = { kind: "measured", raw: "", quote: "", source_id: "S1", locator: { artifact: "sources/S1/text.txt", char_start: 0, char_end: 4 }, snapshot_sha256: "b".repeat(64), extracted_by: "fact add --find", observed_at: "2026-10-23T08:00:00Z" }
|
|
126
|
+
addRow(ledger, { ...source, value: "3", unit: "kilometers", label: "a" })
|
|
127
|
+
addRow(ledger, { ...source, value: "4", unit: "km", label: "b" })
|
|
128
|
+
expect(derive(ledger, { op: "sum", operands: ["F1", "F2"], unit: "km" }).error).toBeUndefined()
|
|
129
|
+
writeLedger(dir, ledger)
|
|
130
|
+
const first = readFileSync(join(dir, LEDGER_FILENAME), "utf8")
|
|
131
|
+
writeLedger(dir, readLedger(dir))
|
|
132
|
+
expect(readFileSync(join(dir, LEDGER_FILENAME), "utf8")).toBe(first)
|
|
133
|
+
expect(readLedger(dir)).toEqual(JSON.parse(first))
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
test("#given a legacy exact with an unknown key #when read and written #then the key survives and the row is refused as an operand", () => {
|
|
137
|
+
const { dir, ledger } = loaded()
|
|
138
|
+
writeLedger(dir, ledger)
|
|
139
|
+
const written = JSON.parse(readFileSync(join(dir, LEDGER_FILENAME), "utf8")) as Ledger
|
|
140
|
+
expect(rowOf(written, "F18").exact).toEqual({ num: "3", den: "1", note: "from the brief" })
|
|
141
|
+
const result = derive(ledger, { op: "sum", operands: ["F18", "F2"], unit: "km" })
|
|
142
|
+
expect(result.error).toBe("incompatible_operands")
|
|
143
|
+
expect(result.shape).toBe("dimension 7")
|
|
144
|
+
expect(recompute(ledger, rowOf(ledger, "F18")).ok).toBe(false)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
test("#given an assumed row whose value and exact disagree #when read #then it loads flagged and a new row is refused (dimension 32)", () => {
|
|
148
|
+
const dir = tempDir()
|
|
149
|
+
const seed = fixture()
|
|
150
|
+
const mismatched = { ...rowOf(seed, "F11"), fact_id: "F19", label: "mismatched-exact", unit: "km", value: "1.5", exact: { num: "3", den: "1" } }
|
|
151
|
+
seed.rows.push(mismatched)
|
|
152
|
+
writeFileSync(join(dir, LEDGER_FILENAME), JSON.stringify(seed))
|
|
153
|
+
const ledger = readLedger(dir) as Ledger
|
|
154
|
+
expect(rowOf(ledger, "F19").value).toBe("1.5")
|
|
155
|
+
const result = derive(ledger, { op: "sum", operands: ["F19", "F2"], unit: "km" })
|
|
156
|
+
expect(result.error).toBe("incompatible_operands")
|
|
157
|
+
expect(result.shape).toBe("dimension 32")
|
|
158
|
+
expect(() => addRow(ledger, { ...mismatched, fact_id: undefined, label: "mismatch-new" })).toThrow("dimension 32")
|
|
159
|
+
})
|
|
160
|
+
})
|