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,228 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test"
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
|
3
|
+
import { tmpdir } from "node:os"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
|
|
6
|
+
import { DRIFT_KINDS, ingestVerdicts } from "./meaning-check.mjs"
|
|
7
|
+
|
|
8
|
+
type Verdict = Record<string, unknown>
|
|
9
|
+
type Result = {
|
|
10
|
+
defects: { code: string; severity: string; integrity: boolean; pair_id: string; drift_kind: string; fragment: string; source_fragment: string }[]
|
|
11
|
+
dropped: { pair_id: string | null; reason: string; model_tier: string | null; file: string }[]
|
|
12
|
+
unverified: { pair_id: string; reason: string }[]
|
|
13
|
+
calibrationLines: string[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const PAIRS = [
|
|
17
|
+
{ pair_id: "P1", kind: "quote", sentence_key: "findings#Q1", fact_ids: ["Q1"], quote: "The pilot covered North America.", sentence: "The rollout was worldwide.", force_strong: true },
|
|
18
|
+
{ pair_id: "P2", kind: "claim", claim_id: "C2", statement: "Revenue may grow in 2026.", sentence: "Revenue will grow in 2026.", force_strong: true },
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
const base = (tier: string, extra: Verdict = {}): Verdict => ({
|
|
22
|
+
pair_id: "P1",
|
|
23
|
+
verdict: "drift",
|
|
24
|
+
drift_kind: "scope_widened",
|
|
25
|
+
fragment: "worldwide",
|
|
26
|
+
source_fragment: "North America",
|
|
27
|
+
explanation: "The assertion broadens the reported region.",
|
|
28
|
+
model_tier: tier,
|
|
29
|
+
...extra,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
let dir = ""
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
dir = mkdtempSync(join(tmpdir(), "meaning-check-"))
|
|
35
|
+
})
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
rmSync(dir, { recursive: true, force: true })
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
function file(name: string, verdicts: Verdict[]): string {
|
|
41
|
+
const path = join(dir, name)
|
|
42
|
+
writeFileSync(path, JSON.stringify(verdicts))
|
|
43
|
+
return path
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function run(cheap: Verdict[], strong: Verdict[] = [], options: object = {}): Result {
|
|
47
|
+
return ingestVerdicts(PAIRS, [file("cheap.json", cheap), file("strong.json", strong)], options) as Result
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe("ingestVerdicts: tier agreement", () => {
|
|
51
|
+
test("#given cheap and strong agree on scope_widened #when ingested #then meaning_drift_confirmed (major, non-integrity)", () => {
|
|
52
|
+
const result = run([base("cheap")], [base("strong")])
|
|
53
|
+
expect(result.defects).toHaveLength(1)
|
|
54
|
+
expect(result.defects[0]).toMatchObject({ code: "meaning_drift_confirmed", severity: "major", integrity: false, pair_id: "P1", drift_kind: "scope_widened" })
|
|
55
|
+
expect(result.dropped).toEqual([])
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test("#given only the cheap tier flags #when ingested #then meaning_drift_warn (minor)", () => {
|
|
59
|
+
const result = run([base("cheap")])
|
|
60
|
+
expect(result.defects.map((d) => [d.code, d.severity])).toEqual([["meaning_drift_warn", "minor"]])
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
test("#given both tiers agree on other #when ingested #then warn only, never confirmed", () => {
|
|
64
|
+
const result = run([base("cheap", { drift_kind: "other" })], [base("strong", { drift_kind: "other" })])
|
|
65
|
+
expect(result.defects.map((d) => d.code)).toEqual(["meaning_drift_warn"])
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
test("#given the tiers name different drift kinds #when ingested #then warn only", () => {
|
|
69
|
+
const result = run([base("cheap")], [base("strong", { drift_kind: "subject_changed" })])
|
|
70
|
+
expect(result.defects.map((d) => d.code)).toEqual(["meaning_drift_warn"])
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test("#given an ok verdict #when ingested #then no defect and no calibration line", () => {
|
|
74
|
+
const result = run([{ pair_id: "P1", verdict: "ok", model_tier: "cheap" }])
|
|
75
|
+
expect(result.defects).toEqual([])
|
|
76
|
+
expect(result.calibrationLines).toEqual([])
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test("#given a claim pair #when both tiers agree on evidence_grade_raised #then confirmed against the statement", () => {
|
|
80
|
+
const verdict = { pair_id: "P2", verdict: "drift", drift_kind: "evidence_grade_raised", fragment: "will grow", source_fragment: "may grow", explanation: "certainty raised" }
|
|
81
|
+
const result = run([{ ...verdict, model_tier: "cheap" }], [{ ...verdict, model_tier: "strong" }])
|
|
82
|
+
expect(result.defects.map((d) => [d.code, d.pair_id])).toEqual([["meaning_drift_confirmed", "P2"]])
|
|
83
|
+
})
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
describe("ingestVerdicts: grounding drops", () => {
|
|
87
|
+
test("#given a fragment that is not in the sentence #when ingested #then dropped and counted, no defect", () => {
|
|
88
|
+
const result = run([base("cheap", { fragment: "Europe" })])
|
|
89
|
+
expect(result.defects).toEqual([])
|
|
90
|
+
expect(result.dropped).toHaveLength(1)
|
|
91
|
+
expect(result.dropped[0]).toMatchObject({ pair_id: "P1", reason: "fragment_not_found", model_tier: "cheap" })
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test("#given a source fragment that is not in the quote #when ingested #then dropped", () => {
|
|
95
|
+
const result = run([base("cheap", { source_fragment: "Asia" })])
|
|
96
|
+
expect(result.defects).toEqual([])
|
|
97
|
+
expect(result.dropped.map((d) => d.reason)).toEqual(["source_fragment_not_found"])
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test("#given empty or blank fragments #when ingested #then dropped as empty", () => {
|
|
101
|
+
const result = run([base("cheap", { fragment: "" })], [base("strong", { source_fragment: " " })])
|
|
102
|
+
expect(result.defects).toEqual([])
|
|
103
|
+
expect(result.dropped.map((d) => d.reason)).toEqual(["empty_fragment", "empty_fragment"])
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
test("#given an unknown drift_kind #when ingested #then dropped", () => {
|
|
107
|
+
const result = run([base("cheap", { drift_kind: "tone_shifted" })])
|
|
108
|
+
expect(result.defects).toEqual([])
|
|
109
|
+
expect(result.dropped.map((d) => d.reason)).toEqual(["unknown_drift_kind"])
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test("#given an unknown pair id or tier #when ingested #then dropped", () => {
|
|
113
|
+
const result = run([base("cheap", { pair_id: "P99" }), base("medium")])
|
|
114
|
+
expect(result.dropped.map((d) => d.reason)).toEqual(["unknown_pair", "invalid_model_tier"])
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
test("#given extra whitespace in a fragment #when ingested #then it still grounds", () => {
|
|
118
|
+
const result = run([base("cheap", { fragment: "rollout \n was", source_fragment: " North America " })])
|
|
119
|
+
expect(result.dropped).toEqual([])
|
|
120
|
+
expect(result.defects.map((d) => d.code)).toEqual(["meaning_drift_warn"])
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test("#given the closed drift_kind set #when read #then it holds exactly the nine kinds", () => {
|
|
124
|
+
expect([...DRIFT_KINDS]).toEqual([
|
|
125
|
+
"subject_changed", "scope_widened", "hedge_dropped", "certainty_added", "causality_added",
|
|
126
|
+
"direction_flipped", "population_changed", "evidence_grade_raised", "other",
|
|
127
|
+
])
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe("ingestVerdicts: calibration log", () => {
|
|
132
|
+
test("#given a dispute on a confirmed pair #when ingested #then the defect stays and the line records disputed:true", () => {
|
|
133
|
+
const result = run([base("cheap")], [base("strong")], { dispute: { pair_id: "P1", reason: "Scope is justified by a second observation." } })
|
|
134
|
+
expect(result.defects.map((d) => d.code)).toEqual(["meaning_drift_confirmed"])
|
|
135
|
+
expect(result.calibrationLines.map((line) => JSON.parse(line))).toEqual([
|
|
136
|
+
{ kind: "quote", pair_id: "P1", tiers: ["cheap", "strong"], drift_kind: "scope_widened", confirmed: true, disputed: true, reason: "Scope is justified by a second observation." },
|
|
137
|
+
])
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
test("#given two flagged pairs and one ok verdict #when ingested with a calibration path #then exactly one line per flagged pair is appended", () => {
|
|
141
|
+
const calibrationPath = join(dir, "fidelity-calibration.jsonl")
|
|
142
|
+
const claimDrift = { pair_id: "P2", verdict: "drift", drift_kind: "certainty_added", fragment: "will", source_fragment: "may", explanation: "x", model_tier: "cheap" }
|
|
143
|
+
const cheap = [base("cheap"), claimDrift, { pair_id: "P1", verdict: "ok", model_tier: "cheap" }]
|
|
144
|
+
const first = run(cheap, [], { calibrationPath })
|
|
145
|
+
expect(first.calibrationLines).toHaveLength(2)
|
|
146
|
+
const lines = () => readFileSync(calibrationPath, "utf8").split("\n").filter(Boolean)
|
|
147
|
+
expect(lines()).toHaveLength(2)
|
|
148
|
+
expect(JSON.parse(lines()[1])).toEqual({ kind: "claim", pair_id: "P2", tiers: ["cheap"], drift_kind: "certainty_added", confirmed: false, disputed: false, reason: null })
|
|
149
|
+
run(cheap, [], { calibrationPath })
|
|
150
|
+
expect(lines()).toHaveLength(4)
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
describe("ingestVerdicts: coverage", () => {
|
|
155
|
+
test("#given a pair no reader judged #when ingested #then it is reported as unverified, never as clean", () => {
|
|
156
|
+
const result = run([base("cheap")], [base("strong")])
|
|
157
|
+
expect(result.unverified).toEqual([{ pair_id: "P2", reason: "no_verdict" }])
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
test("#given a force_strong pair with only a cheap verdict #when ingested #then unverified says no_strong_verdict", () => {
|
|
161
|
+
const result = run([base("cheap"), { pair_id: "P2", verdict: "ok", model_tier: "cheap" }])
|
|
162
|
+
expect(result.unverified).toEqual([
|
|
163
|
+
{ pair_id: "P1", reason: "no_strong_verdict" },
|
|
164
|
+
{ pair_id: "P2", reason: "no_strong_verdict" },
|
|
165
|
+
])
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
test("#given every pair read by both tiers #when ingested #then unverified is empty", () => {
|
|
169
|
+
const ok = (pair_id: string, tier: string): Verdict => ({ pair_id, verdict: "ok", model_tier: tier })
|
|
170
|
+
const result = run([ok("P1", "cheap"), ok("P2", "cheap")], [ok("P1", "strong"), ok("P2", "strong")])
|
|
171
|
+
expect(result.unverified).toEqual([])
|
|
172
|
+
expect(result.defects).toEqual([])
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
test("#given only dropped verdicts on a pair #when ingested #then the pair counts as unread", () => {
|
|
176
|
+
const result = run([base("cheap", { fragment: "Europe" })], [base("strong", { fragment: "Europe" })])
|
|
177
|
+
expect(result.unverified.map((entry) => entry.pair_id)).toEqual(["P1", "P2"])
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
describe("ingestVerdicts: the reported fragment", () => {
|
|
182
|
+
test("#given an earlier other verdict #when the tiers agree on a later kind #then the fragment comes from the agreed verdict", () => {
|
|
183
|
+
const other = base("cheap", { drift_kind: "other", fragment: "The rollout", source_fragment: "The pilot" })
|
|
184
|
+
const result = run([other, base("cheap")], [base("strong")])
|
|
185
|
+
expect(result.defects).toHaveLength(1)
|
|
186
|
+
expect(result.defects[0]).toMatchObject({ code: "meaning_drift_confirmed", drift_kind: "scope_widened", fragment: "worldwide", source_fragment: "North America" })
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
describe("ingestVerdicts: determinism", () => {
|
|
191
|
+
test("#given the same verdicts twice #when ingested #then both results are byte-for-byte equal", () => {
|
|
192
|
+
const cheap = [base("cheap"), { pair_id: "P2", verdict: "drift", drift_kind: "certainty_added", fragment: "will grow", source_fragment: "may grow", model_tier: "cheap" }, base("medium")]
|
|
193
|
+
const strong = [base("strong")]
|
|
194
|
+
const first = JSON.stringify(run(cheap, strong))
|
|
195
|
+
const second = JSON.stringify(run(cheap, strong))
|
|
196
|
+
expect(second).toBe(first)
|
|
197
|
+
})
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
describe("ingestVerdicts: unreadable verdict files", () => {
|
|
201
|
+
test("#given a missing verdict file #when ingested #then a short typed error names the file", () => {
|
|
202
|
+
const missing = join(dir, "absent.json")
|
|
203
|
+
expect(() => ingestVerdicts(PAIRS, [missing])).toThrow(TypeError)
|
|
204
|
+
expect(() => ingestVerdicts(PAIRS, [missing])).toThrow(/absent\.json: cannot read the verdict file/u)
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
test("#given invalid JSON #when ingested #then a short typed error names the file", () => {
|
|
208
|
+
const path = join(dir, "broken.json")
|
|
209
|
+
writeFileSync(path, "{ not json")
|
|
210
|
+
expect(() => ingestVerdicts(PAIRS, [path])).toThrow(/broken\.json: the verdict file is not valid JSON/u)
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
describe("ingestVerdicts: malformed pairs input", () => {
|
|
215
|
+
test("#given a missing or malformed pairs input #when ingested #then it refuses instead of reporting a clean result", () => {
|
|
216
|
+
for (const bad of [undefined, null, {}, { pairs: "P1" }, "pairs.json", 7]) {
|
|
217
|
+
expect(() => ingestVerdicts(bad as never, []), JSON.stringify(bad)).toThrow(TypeError)
|
|
218
|
+
}
|
|
219
|
+
expect(ingestVerdicts({ pairs: [] }, []).unverified).toEqual([])
|
|
220
|
+
})
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
describe("ingestVerdicts: the no-model contract", () => {
|
|
224
|
+
test("#given the module source #when read #then it never fetches or imports a network or model client", () => {
|
|
225
|
+
const text = readFileSync(join(import.meta.dir, "meaning-check.mjs"), "utf8")
|
|
226
|
+
expect(text).not.toMatch(/\bfetch\s*\(|node:https?|node:net|from\s+"(?!\.\/|node:)/u)
|
|
227
|
+
})
|
|
228
|
+
})
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
// numerals.mjs - shared numeral patterns and the numeral parser.
|
|
2
|
+
//
|
|
3
|
+
// NUM, UNIT, NOT_CLAIMS and CLAIMS are the claim-detection patterns the unsourced-number gate uses
|
|
4
|
+
// (gates-text.mjs imports them from here). parseNumeral reads one numeral into an exact decimal
|
|
5
|
+
// string: thousands separators, Korean and Latin scale words, percent and percentage points, the
|
|
6
|
+
// English number words one to twelve, an adjacent currency symbol or unit, and an adjacent bounding
|
|
7
|
+
// qualifier. No numeral is ever passed through Number(). Plain Node ESM, sibling imports only.
|
|
8
|
+
|
|
9
|
+
import { add, fromDecimal, mul, toDecimal } from "./rational.mjs"
|
|
10
|
+
import { normalizeUnit } from "./units.mjs"
|
|
11
|
+
|
|
12
|
+
const MONTH = "(?:jan|feb|mar|apr|may|jun|jul|aug|sep|sept|oct|nov|dec)[a-z]*\\.?"
|
|
13
|
+
/** Stripped before claim detection: dates, versions, figure refs, times, bare years. */
|
|
14
|
+
export const NOT_CLAIMS = [
|
|
15
|
+
/\b\d{4}[-./]\d{1,2}(?:[-./]\d{1,2})?\b/g,
|
|
16
|
+
/\b\d{2}\.\d{2}\.\d{2}\b/g,
|
|
17
|
+
/\d{2,4}\s*\uB144\s*\d{1,2}\s*\uC6D4(?:\s*\d{1,2}\s*\uC77C)?/g,
|
|
18
|
+
/\d{1,2}\s*\uC6D4(?:\s*\d{1,2}\s*\uC77C)?/g,
|
|
19
|
+
/(?:19|20)\d{2}\s*\uB144/g,
|
|
20
|
+
new RegExp(`\\b${MONTH}\\s+\\d{1,2}(?:st|nd|rd|th)?(?:,?\\s+\\d{4})?`, "gi"),
|
|
21
|
+
new RegExp(`\\b\\d{1,2}(?:st|nd|rd|th)?\\s+${MONTH}(?:\\s+\\d{4})?`, "gi"),
|
|
22
|
+
/\bv\d+(?:\.\d+)*\b|\b\d+(?:\.\d+){2,}\b/gi,
|
|
23
|
+
/\b(?:version|ver\.?|release)\s*\d+(?:\.\d+)*/gi,
|
|
24
|
+
/\b(?:fig(?:ure)?\.?|table|chart|section|step|phase|wave|part)\s*\d+(?:\.\d+)*/gi,
|
|
25
|
+
/(?:\uADF8\uB9BC|\uB3C4\uD45C|\uD45C|\uBC84\uC804)\s*\d+(?:\.\d+)*/g,
|
|
26
|
+
/\b\d{1,2}:\d{2}\b/g,
|
|
27
|
+
/\b(?:19|20)\d{2}s?\b/g,
|
|
28
|
+
]
|
|
29
|
+
export const NUM = "(?<![\\p{L}\\d.,])\\d+(?:[.,]\\d+)*"
|
|
30
|
+
export const UNIT =
|
|
31
|
+
"(?:%|\\u2030|\\u00D7|\\uD37C\\uC13C\\uD2B8|\\uB2EC\\uB7EC|\\uC720\\uB85C|\\uC5D4|\\uC6D0|\\uB9CC|\\uC5B5|\\uC870|\\uCC9C|\\uCD08|\\uBD84|\\uC2DC\\uAC04|\\uC77C|\\uC8FC|\\uAC1C\\uC6D4|\\uB144|\\uBA85|\\uAC74|\\uAC1C|\\uD68C|\\uBC88|\\uBC30" +
|
|
32
|
+
"|(?:percent|pp|bps?|x|k|m|b|bn|million|billion|thousand|trillion|usd|krw|eur|ms|s|secs?|seconds?|mins?|minutes?|h|hrs?|hours?|days?|weeks?|months?|years?|kb|mb|gb|tb|users?|people|employees|engineers|tokens?|requests?|stars?|downloads?|customers?)(?![\\p{L}\\d]))"
|
|
33
|
+
export const CLAIMS = [
|
|
34
|
+
new RegExp(`[$\\u20AC\\u00A3\\u00A5\\u20A9]\\s?${NUM}`, "u"),
|
|
35
|
+
new RegExp(`${NUM}\\s?${UNIT}`, "iu"),
|
|
36
|
+
/(?<![\d.,])\d{1,3}(?:,\d{3})+(?![\d,])/,
|
|
37
|
+
/(?<![\d.])\d+\.\d+(?![\d.])/,
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
const WORDS = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve"]
|
|
41
|
+
const WORD_RE = new RegExp(`\\b(?:${WORDS.join("|")})\\b`, "gi")
|
|
42
|
+
/** Korean scale words: cheon (1e3) inside a group, then man, eok, jo as group scales. */
|
|
43
|
+
const CHEON = "\uCC9C"
|
|
44
|
+
const BIG = new Map([["\uB9CC", 10000n], ["\uC5B5", 100000000n], ["\uC870", 1000000000000n]])
|
|
45
|
+
/** A scale word that starts a longer word is not a scale: jogak (pieces), mankeum (as much as). */
|
|
46
|
+
const KO_GROUP = /^(\s?)(?:(\uCC9C)?([\uB9CC\uC5B5\uC870])|(\uCC9C))(?![\uAC01\uD07C])/
|
|
47
|
+
const KO_NEXT = new RegExp(`^\\s?(${NUM})(\uCC9C)?([\uB9CC\uC5B5\uC870])?`, "u")
|
|
48
|
+
const LATIN_SCALE = /^(?:(k|K|M|bn|B)|\s(thousand|million|billion|trillion|bn))(?![\p{L}\d])/u
|
|
49
|
+
const LATIN_FACTOR = { k: 1000n, K: 1000n, M: 1000000n, bn: 1000000000n, B: 1000000000n, thousand: 1000n, million: 1000000n, billion: 1000000000n, trillion: 1000000000000n }
|
|
50
|
+
const CURRENCY = "$\u20AC\u00A3\u00A5\u20A9"
|
|
51
|
+
const SYMBOL_UNIT = /^\s?(%p|%|\u2030|\u00D7|\u00B0C|\u00B0F|\u2103|\u2109)/
|
|
52
|
+
const LATIN_UNIT = /^\s?([A-Za-z]+)(?![\p{L}\d])/u
|
|
53
|
+
const HANGUL_UNIT = /^\s?([\uAC00-\uD7A3]+)/
|
|
54
|
+
/** "3 bun-ui 1" is the fraction one third, not 3 minutes. */
|
|
55
|
+
const FRACTION = /^\s?\uBD84\uC758\s?\d/
|
|
56
|
+
const UNIT_AFTER = new RegExp(`^\\s?${UNIT}`, "iu")
|
|
57
|
+
const NUM_AT = new RegExp(`^${NUM}`, "u")
|
|
58
|
+
/** Range separators: hyphen, en dash, tilde, "to". */
|
|
59
|
+
const RANGE_SEP = /^(?:\s?[-\u2013~]\s?|\sto\s)/
|
|
60
|
+
const SIGN = "-\u2212"
|
|
61
|
+
/** Bounding qualifiers: before the value (Korean and English) and right after it (Korean). */
|
|
62
|
+
const QUALIFIERS_BEFORE = ["up to", "at least", "at most", "more than", "less than", "about", "approximately", "nearly", "over", "under", "\uCD5C\uB300", "\uCD5C\uC18C", "\uC57D"]
|
|
63
|
+
const QUALIFIER_AFTER = /^\s?(\uC774\uC0C1|\uC774\uD558|\uBBF8\uB9CC|\uCD08\uACFC|\uB0B4\uC678|\uAE4C\uC9C0)/
|
|
64
|
+
|
|
65
|
+
/** "1,237" -> "1237"; a malformed group ("1,23") keeps only the leading plain number. */
|
|
66
|
+
function cleanDigits(raw) {
|
|
67
|
+
if (/^\d{1,3}(?:,\d{3})+(?:\.\d+)?$/.test(raw)) return { digits: raw.replace(/,/g, ""), length: raw.length }
|
|
68
|
+
const plain = /^\d+(?:\.\d+)?/.exec(raw)[0]
|
|
69
|
+
return { digits: plain, length: plain.length }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** The bare-year and four-digit-date patterns: a numeral they cover can still be a value. */
|
|
73
|
+
const YEAR_SHAPED = new Set([NOT_CLAIMS[0], NOT_CLAIMS[12]])
|
|
74
|
+
|
|
75
|
+
function excludedRanges(text) {
|
|
76
|
+
const ranges = []
|
|
77
|
+
for (const re of NOT_CLAIMS) {
|
|
78
|
+
for (const match of text.matchAll(re)) ranges.push([match.index, match.index + match[0].length, YEAR_SHAPED.has(re)])
|
|
79
|
+
}
|
|
80
|
+
return ranges
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** A year-shaped numeral is a value when it has a decimal part or a currency, scale word or unit touches it. */
|
|
84
|
+
function anchored(text, at, end, digits) {
|
|
85
|
+
if (digits.includes(".") || (at > 0 && CURRENCY.includes(text[at - 1]))) return true
|
|
86
|
+
return UNIT_AFTER.test(text.slice(end)) || unitAfter(text, end) !== null
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* First digit run or number word at or after `from` that no NOT_CLAIMS pattern covers. A year-shaped
|
|
91
|
+
* match exactly over the numeral (not "2020s", not "2020-03") is lifted when the numeral is anchored.
|
|
92
|
+
*/
|
|
93
|
+
function findStart(text, from, ranges) {
|
|
94
|
+
const covering = (at) => ranges.filter(([s, e]) => at >= s && at < e)
|
|
95
|
+
let digit = null
|
|
96
|
+
for (const match of text.slice(from).matchAll(new RegExp(NUM, "gu"))) {
|
|
97
|
+
const at = from + match.index
|
|
98
|
+
const candidate = { at, ...cleanDigits(match[0]) }
|
|
99
|
+
const end = at + candidate.length
|
|
100
|
+
const cover = covering(at)
|
|
101
|
+
const lifted = cover.every(([s, e, yearShaped]) => yearShaped && s === at && e <= end)
|
|
102
|
+
if (cover.length === 0 || (lifted && anchored(text, at, end, candidate.digits))) {
|
|
103
|
+
digit = candidate
|
|
104
|
+
break
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const free = (at) => covering(at).length === 0
|
|
108
|
+
for (const match of text.slice(from).matchAll(WORD_RE)) {
|
|
109
|
+
const at = from + match.index
|
|
110
|
+
if (digit && digit.at < at) break
|
|
111
|
+
if (free(at)) return { at, digits: String(WORDS.indexOf(match[0].toLowerCase()) + 1), length: match[0].length }
|
|
112
|
+
}
|
|
113
|
+
return digit
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Korean groups such as "4jo 2,000eok": each later group needs a smaller scale. */
|
|
117
|
+
function koreanScale(text, at, digits) {
|
|
118
|
+
const first = KO_GROUP.exec(text.slice(at))
|
|
119
|
+
if (!first) return null
|
|
120
|
+
const big = first[3] ? BIG.get(first[3]) : 1n
|
|
121
|
+
const firstFactor = (first[2] || first[4] ? 1000n : 1n) * big
|
|
122
|
+
let value = mul(fromDecimal(digits), firstFactor)
|
|
123
|
+
let end = at + first[0].length
|
|
124
|
+
let last = first[3] ? big : 0n
|
|
125
|
+
let groups = 1
|
|
126
|
+
while (last > 1n) {
|
|
127
|
+
const next = KO_NEXT.exec(text.slice(end))
|
|
128
|
+
if (!next) break
|
|
129
|
+
const factor = (next[2] ? 1000n : 1n) * (next[3] ? BIG.get(next[3]) : 1n)
|
|
130
|
+
if (factor <= 1n || factor >= last) break
|
|
131
|
+
value = add(value, mul(fromDecimal(cleanDigits(next[1]).digits), factor))
|
|
132
|
+
end += next[0].length
|
|
133
|
+
last = next[3] ? BIG.get(next[3]) : 0n
|
|
134
|
+
groups++
|
|
135
|
+
}
|
|
136
|
+
return { value, end, factor: groups === 1 ? firstFactor : null }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Scale words after a numeral: { value (rational) | null, end, factor | null (one plain factor) }. */
|
|
140
|
+
function scaled(text, end, digits) {
|
|
141
|
+
const korean = koreanScale(text, end, digits)
|
|
142
|
+
if (korean) return korean
|
|
143
|
+
const latin = LATIN_SCALE.exec(text.slice(end))
|
|
144
|
+
if (!latin) return { value: null, end, factor: null }
|
|
145
|
+
const factor = LATIN_FACTOR[latin[1] ?? latin[2].toLowerCase()]
|
|
146
|
+
return { value: mul(fromDecimal(digits), factor), end: end + latin[0].length, factor }
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** The second endpoint of "5-10", "5 to 10", "5~10" or "5\u201310", or null. */
|
|
150
|
+
function rangeEnd(text, at, ranges) {
|
|
151
|
+
const sep = RANGE_SEP.exec(text.slice(at))
|
|
152
|
+
if (!sep) return null
|
|
153
|
+
const pos = at + sep[0].length
|
|
154
|
+
const num = NUM_AT.exec(text.slice(pos))
|
|
155
|
+
if (!num || ranges.some(([s, e]) => pos >= s && pos < e)) return null
|
|
156
|
+
const { digits, length } = cleanDigits(num[0])
|
|
157
|
+
const scale = scaled(text, pos + length, digits)
|
|
158
|
+
return { value: scale.value ? toDecimal(scale.value) : digits, end: scale.end, factor: scale.factor }
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function unitAfter(text, at) {
|
|
162
|
+
const rest = text.slice(at)
|
|
163
|
+
if (FRACTION.test(rest)) return null
|
|
164
|
+
const symbol = SYMBOL_UNIT.exec(rest)
|
|
165
|
+
if (symbol) return { unitRaw: symbol[1], end: at + symbol[0].length }
|
|
166
|
+
const latin = LATIN_UNIT.exec(rest)
|
|
167
|
+
if (latin) return normalizeUnit(latin[1]) ? { unitRaw: latin[1], end: at + latin[0].length } : null
|
|
168
|
+
const hangul = HANGUL_UNIT.exec(rest)
|
|
169
|
+
if (!hangul) return null
|
|
170
|
+
const lead = hangul[0].length - hangul[1].length
|
|
171
|
+
for (let size = hangul[1].length; size > 0; size--) {
|
|
172
|
+
const candidate = hangul[1].slice(0, size)
|
|
173
|
+
if (!BIG.has(candidate) && candidate !== CHEON && normalizeUnit(candidate)) return { unitRaw: candidate, end: at + lead + size }
|
|
174
|
+
}
|
|
175
|
+
return null
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function qualifierBefore(text, at) {
|
|
179
|
+
const tokens = text.slice(0, at).trim().split(/\s+/).slice(-2).map((token) => token.replace(/^[^\p{L}]+/u, ""))
|
|
180
|
+
for (const phrase of QUALIFIERS_BEFORE) {
|
|
181
|
+
const words = phrase.split(" ")
|
|
182
|
+
for (let skip = 0; skip + words.length <= tokens.length; skip++) {
|
|
183
|
+
const window = tokens.slice(tokens.length - skip - words.length, tokens.length - skip)
|
|
184
|
+
if (window.join(" ").toLowerCase() === phrase) return window.join(" ")
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return null
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* The first numeral at or after `from`: { value, raw, span: [start, end], unitRaw, qualifierRaw },
|
|
192
|
+
* or null. `value` is an exact decimal string with scale words folded in ("3eok won" -> "300000000",
|
|
193
|
+
* unit "won") and a leading minus sign ("-" or U+2212) kept as "-"; `span` covers the sign, an
|
|
194
|
+
* adjacent currency symbol, the numeral, scale words and the unit. A range ("5-10 m", "5 to 10 m",
|
|
195
|
+
* "5~10%") adds `valueEnd` and `range: true`; the unit, and a scale word written only on the second
|
|
196
|
+
* endpoint ("3~5eok"), apply to both endpoints.
|
|
197
|
+
*/
|
|
198
|
+
export function parseNumeral(text, from = 0) {
|
|
199
|
+
const ranges = excludedRanges(text)
|
|
200
|
+
const found = findStart(text, from, ranges)
|
|
201
|
+
if (!found) return null
|
|
202
|
+
let start = found.at
|
|
203
|
+
const first = scaled(text, found.at + found.length, found.digits)
|
|
204
|
+
let value = first.value
|
|
205
|
+
let end = first.end
|
|
206
|
+
const second = rangeEnd(text, end, ranges)
|
|
207
|
+
if (second) {
|
|
208
|
+
end = second.end
|
|
209
|
+
if (!value && second.factor) value = mul(fromDecimal(found.digits), second.factor)
|
|
210
|
+
}
|
|
211
|
+
let unitRaw = null
|
|
212
|
+
if (start > 0 && CURRENCY.includes(text[start - 1])) [unitRaw, start] = [text[start - 1], start - 1]
|
|
213
|
+
const negative = start > 0 && SIGN.includes(text[start - 1]) && !/[\p{L}\p{N}]/u.test(text[start - 2] ?? "")
|
|
214
|
+
if (negative) start--
|
|
215
|
+
const unit = unitRaw ? null : unitAfter(text, end)
|
|
216
|
+
if (unit) [unitRaw, end] = [unit.unitRaw, unit.end]
|
|
217
|
+
const after = QUALIFIER_AFTER.exec(text.slice(end))
|
|
218
|
+
return {
|
|
219
|
+
value: (negative ? "-" : "") + (value ? toDecimal(value) : found.digits),
|
|
220
|
+
...(second ? { valueEnd: second.value, range: true } : {}),
|
|
221
|
+
raw: text.slice(start, end),
|
|
222
|
+
span: [start, end],
|
|
223
|
+
unitRaw,
|
|
224
|
+
qualifierRaw: qualifierBefore(text, start) ?? (after ? after[1] : null),
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
|
|
3
|
+
import { CLAIMS, NOT_CLAIMS, NUM, parseNumeral, UNIT } from "./numerals.mjs"
|
|
4
|
+
import { normalizeUnit } from "./units.mjs"
|
|
5
|
+
|
|
6
|
+
describe("shared claim patterns", () => {
|
|
7
|
+
test("exports the gate patterns", () => {
|
|
8
|
+
expect(typeof NUM).toBe("string")
|
|
9
|
+
expect(typeof UNIT).toBe("string")
|
|
10
|
+
expect(NOT_CLAIMS.length).toBe(13)
|
|
11
|
+
expect(CLAIMS.length).toBe(4)
|
|
12
|
+
})
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
describe("parseNumeral", () => {
|
|
16
|
+
test("a length with its unit", () => {
|
|
17
|
+
expect(parseNumeral("320 m")).toEqual({ value: "320", raw: "320 m", span: [0, 5], unitRaw: "m", qualifierRaw: null })
|
|
18
|
+
expect(parseNumeral("320 mm")?.unitRaw).toBe("mm")
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test("thousands separators", () => {
|
|
22
|
+
const parsed = parseNumeral("we shipped 1,237 units")
|
|
23
|
+
expect(parsed?.value).toBe("1237")
|
|
24
|
+
expect(parsed?.span).toEqual([11, 16])
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test("Korean scale words fold into the value and leave the currency unit", () => {
|
|
28
|
+
const parsed = parseNumeral("3억 원")
|
|
29
|
+
expect(parsed?.value).toBe("300000000")
|
|
30
|
+
expect(parsed?.raw).toBe("3억 원")
|
|
31
|
+
expect(normalizeUnit(parsed?.unitRaw as string)).toBe(normalizeUnit("원"))
|
|
32
|
+
expect(parseNumeral("4조 2,000억 원")?.value).toBe("4200000000000")
|
|
33
|
+
expect(parseNumeral("3천만 원")?.value).toBe("30000000")
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test("Latin suffixes and scale words", () => {
|
|
37
|
+
expect(parseNumeral("1.2k")?.value).toBe("1200")
|
|
38
|
+
expect(parseNumeral("$3M")).toMatchObject({ value: "3000000", raw: "$3M", unitRaw: "$" })
|
|
39
|
+
expect(parseNumeral("2.5bn")?.value).toBe("2500000000")
|
|
40
|
+
expect(parseNumeral("$4B")?.value).toBe("4000000000")
|
|
41
|
+
expect(parseNumeral("3 million users")?.value).toBe("3000000")
|
|
42
|
+
expect(parseNumeral("5 km")).toMatchObject({ value: "5", unitRaw: "km" })
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test("percent and percentage points stay distinct", () => {
|
|
46
|
+
expect(parseNumeral("15.2%")).toMatchObject({ value: "15.2", unitRaw: "%" })
|
|
47
|
+
expect(parseNumeral("2%p")).toMatchObject({ value: "2", unitRaw: "%p" })
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test("long decimals keep every digit", () => {
|
|
51
|
+
expect(parseNumeral("10000000000000000.1")?.value).toBe("10000000000000000.1")
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test("English number words one to twelve only", () => {
|
|
55
|
+
expect(parseNumeral("three engineers")?.value).toBe("3")
|
|
56
|
+
expect(parseNumeral("Twelve")?.value).toBe("12")
|
|
57
|
+
expect(parseNumeral("thirteen")).toBeNull()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test("qualifiers before and after the value", () => {
|
|
61
|
+
expect(parseNumeral("최대 4조 2,000억 원")).toMatchObject({ value: "4200000000000", qualifierRaw: "최대", span: [3, 14] })
|
|
62
|
+
expect(parseNumeral("up to 320 m")?.qualifierRaw).toBe("up to")
|
|
63
|
+
expect(parseNumeral("320 m 이상")?.qualifierRaw).toBe("이상")
|
|
64
|
+
expect(parseNumeral("the 320 m mast")?.qualifierRaw).toBeNull()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test("dates, versions and figure references are not numerals", () => {
|
|
68
|
+
expect(parseNumeral("v2.3.1")).toBeNull()
|
|
69
|
+
expect(parseNumeral("see figure 3")).toBeNull()
|
|
70
|
+
expect(parseNumeral("2026-10-23")).toBeNull()
|
|
71
|
+
expect(parseNumeral("v2.3.1 shipped 40 fixes")?.value).toBe("40")
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
test("year-shaped values next to a currency, scale word or unit, or with a decimal part, are numerals", () => {
|
|
75
|
+
expect(parseNumeral("2000억 원")).toMatchObject({ value: "200000000000", unitRaw: "원" })
|
|
76
|
+
expect(parseNumeral("1900만 원")).toMatchObject({ value: "19000000", unitRaw: "원" })
|
|
77
|
+
expect(parseNumeral("2050명")).toMatchObject({ value: "2050", unitRaw: "명" })
|
|
78
|
+
expect(parseNumeral("$2000")).toMatchObject({ value: "2000", raw: "$2000", unitRaw: "$" })
|
|
79
|
+
expect(parseNumeral("1999 users")?.value).toBe("1999")
|
|
80
|
+
expect(parseNumeral("1234.56 m")).toMatchObject({ value: "1234.56", unitRaw: "m" })
|
|
81
|
+
expect(parseNumeral("$1500.25")).toMatchObject({ value: "1500.25", unitRaw: "$" })
|
|
82
|
+
expect(parseNumeral("2020.5억")?.value).toBe("202050000000")
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test("bare years and dates stay excluded", () => {
|
|
86
|
+
expect(parseNumeral("in 2020")).toBeNull()
|
|
87
|
+
expect(parseNumeral("2020-03-01")).toBeNull()
|
|
88
|
+
expect(parseNumeral("the 2020s")).toBeNull()
|
|
89
|
+
expect(parseNumeral("in 2020, 5 users")?.value).toBe("5")
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test("a leading minus sign is part of the value and the span", () => {
|
|
93
|
+
expect(parseNumeral("-5%")).toMatchObject({ value: "-5", raw: "-5%", span: [0, 3], unitRaw: "%" })
|
|
94
|
+
expect(parseNumeral("fell \u22123.2%")).toMatchObject({ value: "-3.2", span: [5, 10], unitRaw: "%" })
|
|
95
|
+
expect(parseNumeral("COVID-19 cases")?.value).toBe("19")
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test("ranges carry both endpoints and share the unit", () => {
|
|
99
|
+
expect(parseNumeral("5-10 m")).toMatchObject({ value: "5", valueEnd: "10", range: true, raw: "5-10 m", span: [0, 6], unitRaw: "m" })
|
|
100
|
+
expect(parseNumeral("5 to 10 m")).toMatchObject({ value: "5", valueEnd: "10", range: true, raw: "5 to 10 m", unitRaw: "m" })
|
|
101
|
+
expect(parseNumeral("5~10%")).toMatchObject({ value: "5", valueEnd: "10", range: true, unitRaw: "%" })
|
|
102
|
+
expect(parseNumeral("5\u201310%")).toMatchObject({ value: "5", valueEnd: "10", range: true, unitRaw: "%" })
|
|
103
|
+
expect(parseNumeral("3~5억 원")).toMatchObject({ value: "300000000", valueEnd: "500000000", range: true, unitRaw: "원" })
|
|
104
|
+
expect(parseNumeral("320 m")).not.toHaveProperty("valueEnd")
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test("Korean scale words and units need a word boundary", () => {
|
|
108
|
+
expect(parseNumeral("5조각")).toMatchObject({ value: "5", raw: "5", unitRaw: null })
|
|
109
|
+
expect(parseNumeral("3분의 1")).toMatchObject({ value: "3", raw: "3", unitRaw: null })
|
|
110
|
+
expect(parseNumeral("3분의 휴식")?.unitRaw).toBe("분")
|
|
111
|
+
expect(parseNumeral("매출 10억은")?.value).toBe("1000000000")
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
test("from skips earlier numerals", () => {
|
|
115
|
+
const text = "320 m and 15%"
|
|
116
|
+
const first = parseNumeral(text)
|
|
117
|
+
expect(parseNumeral(text, first?.span[1])).toMatchObject({ value: "15", unitRaw: "%" })
|
|
118
|
+
expect(parseNumeral("no numbers here")).toBeNull()
|
|
119
|
+
})
|
|
120
|
+
})
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
// outcome.mjs - the per-run outcome manifest (`outcome.json`) and the closing briefing.
|
|
2
|
-
//
|
|
3
|
-
// manifest, the sources-ledger text, and an injected `now`. node: builtins only.
|
|
1
|
+
// outcome.mjs - the per-run outcome manifest (`outcome.json`) and the closing briefing. Manifest functions are pure (they return a new manifest).
|
|
2
|
+
// The briefing reads only the manifest, the sources-ledger text, and an injected `now`. node: builtins only.
|
|
4
3
|
|
|
5
4
|
import * as nodeFs from "node:fs"
|
|
6
5
|
import { basename, dirname, join } from "node:path"
|
|
7
6
|
|
|
8
|
-
import { DELIVERABLE_FORMATS, DELIVERABLE_STATUSES, GATE_NAMES, GATE_STATUSES, LANES } from "./contracts.mjs"
|
|
9
|
-
import { MANIFEST_CODES, SCHEMA_VERSION, validateOutcomeManifest } from "./contracts.mjs"
|
|
7
|
+
import { DELIVERABLE_FORMATS, DELIVERABLE_STATUSES, GATE_NAMES, GATE_STATUSES, LANES, MANIFEST_CODES, normalizeManifest, SCHEMA_VERSION, validateOutcomeManifest } from "./contracts.mjs"
|
|
10
8
|
|
|
11
9
|
export const MANIFEST_FILENAME = "outcome.json"
|
|
12
10
|
const MISSING = MANIFEST_CODES[0]
|
|
@@ -87,7 +85,7 @@ export function setDeliverable(manifest, format, status, detail = {}) {
|
|
|
87
85
|
const next = clone(manifest)
|
|
88
86
|
const index = next.deliverables.findIndex((row) => row.format === format)
|
|
89
87
|
const row = { format, status, promised: index >= 0 ? next.deliverables[index].promised : false, updatedAt: stamp(detail.now) }
|
|
90
|
-
for (const key of ["path", "reason", "pages", "bytes"]) {
|
|
88
|
+
for (const key of ["path", "reason", "pages", "bytes", "verifiedBy", "renderedFrom"]) {
|
|
91
89
|
if (detail[key] !== undefined) row[key] = detail[key]
|
|
92
90
|
}
|
|
93
91
|
if (index >= 0) next.deliverables[index] = row
|
|
@@ -95,11 +93,13 @@ export function setDeliverable(manifest, format, status, detail = {}) {
|
|
|
95
93
|
return next
|
|
96
94
|
}
|
|
97
95
|
|
|
98
|
-
export function setGate(manifest, gate, status) {
|
|
96
|
+
export function setGate(manifest, gate, status, detail = {}) {
|
|
99
97
|
assertOneOf("unknown_gate", "gate", gate, GATE_NAMES)
|
|
100
98
|
assertOneOf("unknown_gate_status", "gate status", status, GATE_STATUSES)
|
|
99
|
+
if (gate === "fidelity" && status === "not_run" && (typeof detail.reason !== "string" || !detail.reason.trim())) throw new OutcomeError("gate_reason_required", "fidelity not_run requires a non-empty --reason")
|
|
101
100
|
const next = clone(manifest)
|
|
102
101
|
next.gates[gate] = status
|
|
102
|
+
next.gateHistory = [...(next.gateHistory ?? []), { gate, status, ...(detail.reason !== undefined ? { reason: detail.reason.trim() } : {}), at: stamp(detail.now) }]
|
|
103
103
|
return next
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -226,7 +226,7 @@ function assertValid(manifest, where) {
|
|
|
226
226
|
export function readManifest(file) {
|
|
227
227
|
let manifest
|
|
228
228
|
try {
|
|
229
|
-
manifest = JSON.parse(nodeFs.readFileSync(file, "utf8"))
|
|
229
|
+
manifest = normalizeManifest(JSON.parse(nodeFs.readFileSync(file, "utf8")))
|
|
230
230
|
} catch (error) {
|
|
231
231
|
if (error instanceof SyntaxError) throw new OutcomeError("invalid_manifest", `${file}: not valid JSON (${error.message})`)
|
|
232
232
|
throw error
|
|
@@ -258,6 +258,35 @@ describe("readManifest and writeManifest", () => {
|
|
|
258
258
|
writeFileSync(file, "{not json")
|
|
259
259
|
expect(() => readManifest(file)).toThrow(OutcomeError)
|
|
260
260
|
})
|
|
261
|
+
|
|
262
|
+
test("#given a manifest file written before the fidelity gate (four gates) #when read and written back #then it loads and the file gains fidelity not_run", () => {
|
|
263
|
+
// given
|
|
264
|
+
const dir = sessionDir()
|
|
265
|
+
const file = join(dir, MANIFEST_FILENAME)
|
|
266
|
+
const old = {
|
|
267
|
+
schemaVersion: 1,
|
|
268
|
+
sessionDir: dir,
|
|
269
|
+
startedAt: START_LOCAL.toISOString(),
|
|
270
|
+
finishedAt: null,
|
|
271
|
+
elapsedMinutes: null,
|
|
272
|
+
lane: "no-format",
|
|
273
|
+
interview: null,
|
|
274
|
+
deliverables: [{ format: "pdf", status: "pending", promised: true, updatedAt: START_LOCAL.toISOString() }],
|
|
275
|
+
renders: [],
|
|
276
|
+
gates: { static: "pass", layout: "not_run", visual: "fail", proofread: "not_run" },
|
|
277
|
+
residualDefects: [],
|
|
278
|
+
repair: null,
|
|
279
|
+
sources: null,
|
|
280
|
+
}
|
|
281
|
+
writeFileSync(file, JSON.stringify(old))
|
|
282
|
+
// when
|
|
283
|
+
const manifest = readManifest(file)
|
|
284
|
+
writeManifest(file, manifest)
|
|
285
|
+
// then
|
|
286
|
+
const written = JSON.parse(readFileSync(file, "utf8"))
|
|
287
|
+
expect(written.gates).toEqual({ static: "pass", fidelity: "not_run", layout: "not_run", visual: "fail", proofread: "not_run" })
|
|
288
|
+
expect(written.schemaVersion).toBe(1)
|
|
289
|
+
})
|
|
261
290
|
})
|
|
262
291
|
|
|
263
292
|
describe("round trip in a stamped session dir", () => {
|