lazycodex-ai 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/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/docs/reference/web-terminal-visual-qa.md +8 -0
- package/package.json +1 -1
- 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,117 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
|
|
3
|
+
import { guardedTerms, isKoreanTerm, lexicalPattern, lexiconChecks, QUALIFIER_FORMS } from "./fidelity-lexicon.mjs"
|
|
4
|
+
import { HEDGES, QUALIFIERS } from "./lexicon.mjs"
|
|
5
|
+
|
|
6
|
+
type Pair = { pair_id: string; quote: string; sentence: string; span?: [number, number] | null; qualifier?: string | null; acronyms?: Record<string, string> }
|
|
7
|
+
type Finding = { pair_id: string; code: string; message: string }
|
|
8
|
+
type Result = { findings: Finding[]; forceStrong: string[] }
|
|
9
|
+
|
|
10
|
+
function check(pair: Omit<Pair, "pair_id">): Result {
|
|
11
|
+
return lexiconChecks([{ pair_id: "P1", ...pair }]) as Result
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const codes = (result: Result) => result.findings.map((finding) => finding.code)
|
|
15
|
+
|
|
16
|
+
function withSpan(quote: string, sentence: string, fact: string, qualifier: string | null): Omit<Pair, "pair_id"> {
|
|
17
|
+
const at = sentence.indexOf(fact)
|
|
18
|
+
return { quote, sentence, span: [at, at + fact.length], qualifier }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("source-versus-sentence lexicon", () => {
|
|
22
|
+
test("#given a hedged source and an unhedged sentence #when checked #then hedge_dropped", () => {
|
|
23
|
+
expect(codes(check({ quote: "estimated 100억", sentence: "100억 달성" }))).toEqual(["hedge_dropped"])
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
test("#given the hedge kept #when checked #then clean", () => {
|
|
27
|
+
expect(codes(check({ quote: "estimated 100억", sentence: "estimated 100억 달성" }))).toEqual([])
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test("#given a source that falls and a sentence that rises #when checked #then direction_flipped", () => {
|
|
31
|
+
expect(codes(check({ quote: "Costs were reduced last year.", sentence: "Costs increased last year." }))).toEqual(["direction_flipped"])
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test("#given a negated sentence #when checked #then direction is routed, not flagged", () => {
|
|
35
|
+
expect(codes(check({ quote: "Costs were reduced last year.", sentence: "Costs did not increase last year." }))).toEqual([])
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test("#given a causal connective the source lacks #when checked #then causality_added", () => {
|
|
39
|
+
expect(codes(check({ quote: "Sales rose and prices fell.", sentence: "Sales rose because prices fell." }))).toEqual(["causality_added"])
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test("#given a narrow source scope and a broad sentence scope #when checked #then scope_widened_warn and force_strong", () => {
|
|
43
|
+
const result = check({ quote: "북미 매출이 늘었다", sentence: "전 세계 매출이 늘었다" })
|
|
44
|
+
expect(codes(result)).toEqual(["scope_widened_warn"])
|
|
45
|
+
expect(result.forceStrong).toEqual(["P1"])
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe("qualifiers", () => {
|
|
50
|
+
const QUOTE = "The bridge span is up to 320 m long."
|
|
51
|
+
|
|
52
|
+
test("#given an upper-bounded row and a sentence without the bound #when checked #then qualifier_dropped", () => {
|
|
53
|
+
expect(codes(check(withSpan(QUOTE, "The span is 320 m long.", "320 m", "upper_inclusive")))).toEqual(["qualifier_dropped"])
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test("#given the :q rendering inside the fact span #when checked #then clean", () => {
|
|
57
|
+
expect(codes(check(withSpan(QUOTE, "The span is up to 320 m long.", "up to 320 m", "upper_inclusive")))).toEqual([])
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test("#given the Korean surface form after the value #when checked #then the closed mapping accepts it", () => {
|
|
61
|
+
expect(codes(check(withSpan("최대 320 m", "길이는 320 m 이하 수준이다", "320 m", "upper_inclusive")))).toEqual([])
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
test("#given a qualifier more than six tokens away #when checked #then it does not count", () => {
|
|
65
|
+
const sentence = "Up to now the bridge, built over the long river, spans 320 m."
|
|
66
|
+
expect(codes(check(withSpan(QUOTE, sentence, "320 m", "upper_inclusive")))).toEqual(["qualifier_dropped"])
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
describe("names and routing", () => {
|
|
71
|
+
test("#given an acronym in neither the quote nor the source's acronym table #when checked #then name_outside_quote and force_strong", () => {
|
|
72
|
+
const result = check({ quote: "Demand rose in 2026.", sentence: "HBM demand rose in 2026.", acronyms: {} })
|
|
73
|
+
expect(codes(result)).toEqual(["name_outside_quote"])
|
|
74
|
+
expect(result.forceStrong).toEqual(["P1"])
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test("#given the acronym listed in the source's acronym table #when checked #then clean", () => {
|
|
78
|
+
expect(codes(check({ quote: "Demand rose in 2026.", sentence: "HBM demand rose in 2026.", acronyms: { HBM: "High Bandwidth Memory" } }))).toEqual([])
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test("#given a hedge before a concessive in the sentence while the source hedges its final clause #when checked #then force_strong only", () => {
|
|
82
|
+
const result = check({
|
|
83
|
+
quote: "Revenue rose, but the margin is estimated to fall.",
|
|
84
|
+
sentence: "The margin is estimated to fall, but revenue rose.",
|
|
85
|
+
})
|
|
86
|
+
expect(codes(result)).toEqual([])
|
|
87
|
+
expect(result.forceStrong).toEqual(["P1"])
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
describe("shared lexical owners used by polish (duplication sweep)", () => {
|
|
92
|
+
const hits = (text: string, term: string, mode: string) => [...text.matchAll(lexicalPattern(term, mode))].length
|
|
93
|
+
|
|
94
|
+
test("guardedTerms is the union of HEDGES, QUALIFIERS and every QUALIFIER_FORMS form", () => {
|
|
95
|
+
const union = new Set([...HEDGES, ...QUALIFIERS, ...Object.values(QUALIFIER_FORMS).flat()])
|
|
96
|
+
expect(new Set(guardedTerms())).toEqual(union)
|
|
97
|
+
expect(guardedTerms().length).toBe(union.size)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
test("isKoreanTerm detects Hangul", () => {
|
|
101
|
+
expect(isKoreanTerm("\uC57D")).toBe(true)
|
|
102
|
+
expect(isKoreanTerm("about")).toBe(false)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test("guard mode: single Latin words inflect, multiword terms do not, Korean matches as a substring", () => {
|
|
106
|
+
expect(hits("it was estimatedly so", "estimated", "guard")).toBe(1)
|
|
107
|
+
expect(hits("more thanks", "more than", "guard")).toBe(0)
|
|
108
|
+
expect(hits("more than", "more than", "guard")).toBe(1)
|
|
109
|
+
expect(hits("\uC608\uC57D", "\uC57D", "guard")).toBe(1)
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
test("stem mode keeps its own rules: multiword terms inflect and a one-syllable Korean term needs a boundary", () => {
|
|
113
|
+
expect(hits("more thanks", "more than", "stem")).toBe(1)
|
|
114
|
+
expect(hits("\uC608\uC57D", "\uC57D", "stem")).toBe(0)
|
|
115
|
+
expect(hits("\uC57D 3", "\uC57D", "stem")).toBe(1)
|
|
116
|
+
})
|
|
117
|
+
})
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// fidelity-records.mjs - the fidelity record schema and the delivery barrier of `outcome verify`
|
|
2
|
+
// and `outcome finish`.
|
|
3
|
+
//
|
|
4
|
+
// The table and the schema are the spec for the barrier. Every fidelity record is written through
|
|
5
|
+
// writeFidelityRecord, which stamps one envelope (RECORD_ENVELOPE: record_kind, artifact_sha256,
|
|
6
|
+
// report, written_at, defects) plus the fields its kind declares, and read through
|
|
7
|
+
// readFidelityRecord, which refuses a missing, unknown, or ill-typed field (fail closed).
|
|
8
|
+
// FIDELITY_RECORDS holds every per-record rule as a column: where the records are (targets), when a
|
|
9
|
+
// delivered session must carry one (requiredWhen), which envelope field binds it to the delivered
|
|
10
|
+
// bytes (bindsTo), and which defects block delivery (blocking). The barrier is one loop over the
|
|
11
|
+
// table with no record-specific branch; a new fidelity step adds a row here and nothing else.
|
|
12
|
+
//
|
|
13
|
+
// HTML/PDF/DOCX need a gate record or an explicitly logged not_run reason, even without a ledger.
|
|
14
|
+
// That logged exemption covers deliveries this harness never gated: once a ledger or any session
|
|
15
|
+
// fidelity record exists, the barrier refuses the unverified edition regardless of it, and the same
|
|
16
|
+
// exemption is the only way a required record may be absent.
|
|
17
|
+
|
|
18
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
19
|
+
import { join } from "node:path"
|
|
20
|
+
|
|
21
|
+
import { CliError, readJsonFile, sha256Of, writeJsonAtomic } from "./cli-support.mjs"
|
|
22
|
+
import { LEDGER_FILENAME } from "./fact-ledger.mjs"
|
|
23
|
+
import { meaningCheckEnabled, requireLockedBrief } from "./fidelity-brief.mjs"
|
|
24
|
+
import { blocksDelivery, deliveryBlockReason } from "./gates-fidelity.mjs"
|
|
25
|
+
|
|
26
|
+
export const FIDELITY_RECORD = "fidelity-defects.json"
|
|
27
|
+
export const MEANING_RECORD = "fidelity-meaning-defects.json"
|
|
28
|
+
|
|
29
|
+
/** The fields every fidelity record carries, whatever its kind. */
|
|
30
|
+
export const RECORD_ENVELOPE = Object.freeze(["record_kind", "artifact_sha256", "report", "written_at", "defects"])
|
|
31
|
+
|
|
32
|
+
/** Formats that need a fidelity record even in a session with no ledger and no record. */
|
|
33
|
+
const GATED_FORMATS = Object.freeze(["html", "pdf", "docx"])
|
|
34
|
+
|
|
35
|
+
/** The meaning record is required unless the locked brief turns the check off; a malformed block cannot prove off. */
|
|
36
|
+
function meaningRequired({ sessionDir }) {
|
|
37
|
+
const lock = requireLockedBrief(join(sessionDir, "brief.md"))
|
|
38
|
+
return lock.exitCode === 2 || (lock.locked && meaningCheckEnabled(lock.parsed))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const subjectOf = (row) => row.renderedFrom ?? row.path ?? null
|
|
42
|
+
|
|
43
|
+
/** One record per delivered edition: the file its row names with --verified-by, bound to that edition's bytes. */
|
|
44
|
+
const perEdition = (sessionDir, delivered) =>
|
|
45
|
+
delivered.map((row) => ({ path: row.verifiedBy ?? null, declared: row.verifiedBy !== undefined, format: row.format, at: `${row.format}: `, subjects: [subjectOf(row)].filter((subject) => subject !== null) }))
|
|
46
|
+
|
|
47
|
+
/** One record per session, in the session directory, bound to any delivered edition's bytes. */
|
|
48
|
+
const perSession = (file) => (sessionDir, delivered) => [{ path: join(sessionDir, file), declared: false, format: null, at: "", subjects: delivered.map(subjectOf).filter((subject) => subject !== null) }]
|
|
49
|
+
|
|
50
|
+
/** Every fidelity record the barrier reads, in check order; each column is one rule the barrier applies. */
|
|
51
|
+
export const FIDELITY_RECORDS = Object.freeze([
|
|
52
|
+
Object.freeze({
|
|
53
|
+
kind: "fidelity",
|
|
54
|
+
file: FIDELITY_RECORD,
|
|
55
|
+
step: "fidelity gate",
|
|
56
|
+
targets: perEdition,
|
|
57
|
+
requiredWhen: ({ hasFidelity }, target) => hasFidelity || GATED_FORMATS.includes(target.format),
|
|
58
|
+
remedy: (target) => `run fact verify on ${target.subjects[0] ?? "the edition"} and set it with --verified-by`,
|
|
59
|
+
bindsTo: "artifact_sha256",
|
|
60
|
+
blocking: blocksDelivery,
|
|
61
|
+
payload: Object.freeze(["command", "status", "exitCode", "format", "counts", "gatesRun"]),
|
|
62
|
+
}),
|
|
63
|
+
Object.freeze({
|
|
64
|
+
kind: "meaning",
|
|
65
|
+
file: MEANING_RECORD,
|
|
66
|
+
step: "meaning gate",
|
|
67
|
+
targets: perSession(MEANING_RECORD),
|
|
68
|
+
requiredWhen: meaningRequired,
|
|
69
|
+
remedy: () => "run fact pairs and fact meaning-check before delivery",
|
|
70
|
+
bindsTo: "artifact_sha256",
|
|
71
|
+
blocking: blocksDelivery,
|
|
72
|
+
payload: Object.freeze(["command", "status", "exitCode", "run_id", "pairs_sha256", "counts", "dropped", "unverified", "calibration_path", "calibration_written", "calibration_replaced", "promotion_eligibility"]),
|
|
73
|
+
}),
|
|
74
|
+
])
|
|
75
|
+
|
|
76
|
+
const entryOf = (kind) => {
|
|
77
|
+
const entry = FIDELITY_RECORDS.find((row) => row.kind === kind)
|
|
78
|
+
if (entry === undefined) throw new CliError(`unknown fidelity record kind ${kind}`)
|
|
79
|
+
return entry
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Why a value is not a valid record of this kind, or null. */
|
|
83
|
+
function envelopeProblem(record, entry) {
|
|
84
|
+
if (record === null || typeof record !== "object" || Array.isArray(record)) return "not a record object"
|
|
85
|
+
const missing = RECORD_ENVELOPE.filter((key) => !Object.hasOwn(record, key))
|
|
86
|
+
if (missing.length > 0) return `missing ${missing.join(", ")}`
|
|
87
|
+
const unknown = Object.keys(record).filter((key) => !RECORD_ENVELOPE.includes(key) && !entry.payload.includes(key))
|
|
88
|
+
if (unknown.length > 0) return `unknown field(s) ${unknown.join(", ")}`
|
|
89
|
+
if (record.record_kind !== entry.kind) return `record_kind ${JSON.stringify(record.record_kind)} is not ${entry.kind}`
|
|
90
|
+
if (typeof record.artifact_sha256 !== "string" || !/^[0-9a-f]{64}$/u.test(record.artifact_sha256)) return "artifact_sha256 is not a sha256 hex digest"
|
|
91
|
+
if (typeof record.report !== "string" || record.report === "") return "report is not a path"
|
|
92
|
+
if (typeof record.written_at !== "string" || Number.isNaN(Date.parse(record.written_at))) return "written_at is not an ISO time"
|
|
93
|
+
if (!Array.isArray(record.defects) || !record.defects.every((defect) => typeof defect?.code === "string")) return "defects is not a list of coded defects"
|
|
94
|
+
return null
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Write one fidelity record of a kind in the table: the envelope plus that kind's payload fields.
|
|
99
|
+
* @param {string} path
|
|
100
|
+
* @param {string} kind a FIDELITY_RECORDS kind
|
|
101
|
+
* @param {{ artifact_sha256: string, report: string, defects: object[], [field: string]: unknown }} fields the envelope values plus the kind's payload
|
|
102
|
+
*/
|
|
103
|
+
export function writeFidelityRecord(path, kind, { artifact_sha256, report, defects, ...payload }, now = new Date()) {
|
|
104
|
+
const entry = entryOf(kind)
|
|
105
|
+
const record = { record_kind: kind, artifact_sha256, report, written_at: now.toISOString(), defects, ...payload }
|
|
106
|
+
const problem = envelopeProblem(record, entry)
|
|
107
|
+
if (problem !== null) throw new CliError(`refusing to write a malformed ${kind} record: ${problem}`)
|
|
108
|
+
writeJsonAtomic(path, record)
|
|
109
|
+
return record
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Read one fidelity record of a kind; anything but a valid envelope is a CliError naming the path. */
|
|
113
|
+
export function readFidelityRecord(path, kind) {
|
|
114
|
+
const entry = entryOf(kind)
|
|
115
|
+
let record
|
|
116
|
+
try {
|
|
117
|
+
record = readJsonFile(path)
|
|
118
|
+
} catch (error) {
|
|
119
|
+
throw new CliError(`${kind} record ${path} is malformed: ${error.message}`)
|
|
120
|
+
}
|
|
121
|
+
const problem = envelopeProblem(record, entry)
|
|
122
|
+
if (problem !== null) throw new CliError(`${kind} record ${path} is malformed: ${problem}`)
|
|
123
|
+
return record
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const unresolved = (format, codes, path) => ({ code: "fidelity_unresolved", format, codes, message: codes.map((code) => deliveryBlockReason({ code })).join("; "), path })
|
|
127
|
+
const hashOf = (path) => (existsSync(path) ? sha256Of(readFileSync(path)) : null)
|
|
128
|
+
|
|
129
|
+
/** Barrier problems for `outcome verify` and `outcome finish`; empty when nothing blocks delivery. */
|
|
130
|
+
export function fidelityProblems(sessionDir, manifest) {
|
|
131
|
+
const delivered = manifest.deliverables.filter((row) => row.status === "delivered")
|
|
132
|
+
const hasFidelity = existsSync(join(sessionDir, LEDGER_FILENAME)) || FIDELITY_RECORDS.some((entry) => existsSync(join(sessionDir, entry.file)))
|
|
133
|
+
const lastFidelity = manifest.gateHistory?.findLast((entry) => entry.gate === "fidelity")
|
|
134
|
+
const exempt = !hasFidelity && manifest.gates.fidelity === "not_run" && lastFidelity?.status === "not_run" && Boolean(lastFidelity.reason?.trim())
|
|
135
|
+
const context = { sessionDir, hasFidelity }
|
|
136
|
+
const problems = []
|
|
137
|
+
const counted = new Set()
|
|
138
|
+
for (const entry of FIDELITY_RECORDS) {
|
|
139
|
+
for (const target of entry.targets(sessionDir, delivered)) {
|
|
140
|
+
const refuse = (message, path = target.path) => problems.push({ code: "edition_unverified", format: target.format, message: `${target.at}${message}`, path })
|
|
141
|
+
if (target.path === null || !existsSync(target.path)) {
|
|
142
|
+
if (target.declared) refuse(`${entry.kind} record ${target.path} is missing`)
|
|
143
|
+
else if (delivered.length > 0 && !exempt && entry.requiredWhen(context, target)) refuse(`${entry.step} not run; ${entry.remedy(target)}`, target.path ?? target.subjects[0] ?? null)
|
|
144
|
+
continue
|
|
145
|
+
}
|
|
146
|
+
let record
|
|
147
|
+
try {
|
|
148
|
+
record = readFidelityRecord(target.path, entry.kind)
|
|
149
|
+
} catch (error) {
|
|
150
|
+
refuse(error.message)
|
|
151
|
+
continue
|
|
152
|
+
}
|
|
153
|
+
if (target.subjects.length > 0 && !target.subjects.some((subject) => hashOf(subject) === record[entry.bindsTo])) {
|
|
154
|
+
refuse(`${entry.kind} record ${target.path} is for a different edition than ${target.subjects.join(", ")}`)
|
|
155
|
+
}
|
|
156
|
+
if (counted.has(target.path)) continue
|
|
157
|
+
counted.add(target.path)
|
|
158
|
+
const codes = [...new Set(record.defects.filter(entry.blocking).map((defect) => defect.code))].sort()
|
|
159
|
+
if (codes.length > 0) problems.push(unresolved(target.format, codes, target.path))
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return problems
|
|
163
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// fidelity-state.mjs - per-sentence failure counts for the fidelity repair loop. A sentence is
|
|
2
|
+
// keyed by its section slug and the sorted fact ids it cites, so a reworded sentence that
|
|
3
|
+
// cites the same facts keeps its count. The exact slug and id rules are in the comment above
|
|
4
|
+
// slugify in fact-fill.mjs. node: builtins only.
|
|
5
|
+
|
|
6
|
+
import { existsSync } from "node:fs"
|
|
7
|
+
import { join } from "node:path"
|
|
8
|
+
|
|
9
|
+
import { CliError, readJsonFile, writeJsonAtomic } from "./cli-support.mjs"
|
|
10
|
+
|
|
11
|
+
export const STATE_FILENAME = "fidelity-state.json"
|
|
12
|
+
export const STATE_SCHEMA_VERSION = 1
|
|
13
|
+
|
|
14
|
+
/** slug + '#' + the fact ids sorted and joined by ','. */
|
|
15
|
+
export function sentenceKey(sectionSlug, factIds) {
|
|
16
|
+
return `${sectionSlug}#${[...factIds].sort().join(",")}`
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function emptyState() {
|
|
20
|
+
return { schemaVersion: STATE_SCHEMA_VERSION, failures: {}, replaced: [] }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isValid(state) {
|
|
24
|
+
return (
|
|
25
|
+
state !== null &&
|
|
26
|
+
typeof state === "object" &&
|
|
27
|
+
state.schemaVersion === STATE_SCHEMA_VERSION &&
|
|
28
|
+
state.failures !== null &&
|
|
29
|
+
typeof state.failures === "object" &&
|
|
30
|
+
!Array.isArray(state.failures) &&
|
|
31
|
+
Object.values(state.failures).every((count) => Number.isInteger(count) && count >= 0) &&
|
|
32
|
+
Array.isArray(state.replaced) &&
|
|
33
|
+
state.replaced.every((key) => typeof key === "string")
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function readState(dir) {
|
|
38
|
+
const file = join(dir, STATE_FILENAME)
|
|
39
|
+
if (!existsSync(file)) return emptyState()
|
|
40
|
+
const state = readJsonFile(file)
|
|
41
|
+
if (!isValid(state)) throw new CliError(`${file} is not a valid fidelity state`)
|
|
42
|
+
return state
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function recordFailure(state, key) {
|
|
46
|
+
state.failures[key] = (state.failures[key] ?? 0) + 1
|
|
47
|
+
return state.failures[key]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function failureCount(state, key) {
|
|
51
|
+
return state.failures[key] ?? 0
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function markReplaced(state, key) {
|
|
55
|
+
if (!state.replaced.includes(key)) state.replaced.push(key)
|
|
56
|
+
return state
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function writeState(dir, state) {
|
|
60
|
+
if (!isValid(state)) throw new CliError("refusing to write an invalid fidelity state")
|
|
61
|
+
const file = join(dir, STATE_FILENAME)
|
|
62
|
+
writeJsonAtomic(file, state)
|
|
63
|
+
return file
|
|
64
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, onTestFinished, test } from "bun:test"
|
|
2
|
+
import { mkdtempSync, readdirSync, rmSync } from "node:fs"
|
|
3
|
+
import { tmpdir } from "node:os"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
|
|
6
|
+
import { failureCount, markReplaced, readState, recordFailure, sentenceKey, writeState } from "./fidelity-state.mjs"
|
|
7
|
+
|
|
8
|
+
function tempDir(): string {
|
|
9
|
+
const dir = mkdtempSync(join(tmpdir(), "ulw-fidelity-"))
|
|
10
|
+
onTestFinished(() => rmSync(dir, { recursive: true, force: true }))
|
|
11
|
+
return dir
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe("fidelity state", () => {
|
|
15
|
+
test("sentenceKey is order-independent on ids", () => {
|
|
16
|
+
expect(sentenceKey("findings", ["F3", "Q1", "F12"])).toBe(sentenceKey("findings", ["F12", "F3", "Q1"]))
|
|
17
|
+
expect(sentenceKey("findings", ["F2", "F1"])).toBe("findings#F1,F2")
|
|
18
|
+
expect(sentenceKey("findings", ["F1"])).not.toBe(sentenceKey("other", ["F1"]))
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test("failureCount increments per key", () => {
|
|
22
|
+
const dir = tempDir()
|
|
23
|
+
const state = readState(dir)
|
|
24
|
+
const key = sentenceKey("findings", ["F1"])
|
|
25
|
+
expect(failureCount(state, key)).toBe(0)
|
|
26
|
+
recordFailure(state, key)
|
|
27
|
+
recordFailure(state, key)
|
|
28
|
+
expect(failureCount(state, key)).toBe(2)
|
|
29
|
+
expect(failureCount(state, sentenceKey("findings", ["F2"]))).toBe(0)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test("markReplaced records the key and survives a write/read", () => {
|
|
33
|
+
const dir = tempDir()
|
|
34
|
+
const state = readState(dir)
|
|
35
|
+
const key = sentenceKey("findings", ["Q1"])
|
|
36
|
+
recordFailure(state, key)
|
|
37
|
+
markReplaced(state, key)
|
|
38
|
+
markReplaced(state, key)
|
|
39
|
+
expect(state.replaced).toEqual([key])
|
|
40
|
+
writeState(dir, state)
|
|
41
|
+
expect(readdirSync(dir)).toEqual(["fidelity-state.json"])
|
|
42
|
+
const loaded = readState(dir)
|
|
43
|
+
expect(loaded).toEqual(state)
|
|
44
|
+
expect(failureCount(loaded, key)).toBe(1)
|
|
45
|
+
})
|
|
46
|
+
})
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// fidelity-trace.mjs - the numeral and quotation trace of the deterministic fidelity gate.
|
|
2
|
+
//
|
|
3
|
+
// Every numeral in prose and table cells outside sources, toc and chart figures (dates included)
|
|
4
|
+
// must sit in a data-fact, data-translates, q or blockquote element, or at the site of a fill-map
|
|
5
|
+
// entry; otherwise untraceable_number. Quoted runs with no Q row are quote_unbound, and leftover
|
|
6
|
+
// {{...}} pointers unresolved_placeholder. No model, no network.
|
|
7
|
+
//
|
|
8
|
+
// Fill-map assumption (fact-fill.mjs fill(), #11141): format "html" wraps every value and quote in
|
|
9
|
+
// <data data-fact> / <q data-fact> / <blockquote data-fact>, which this trace skips by element, so
|
|
10
|
+
// it needs no fill map. Format "md" writes bare text, and each fillMap entry is
|
|
11
|
+
// { fact_id, rendered, start, end } with start/end indexing the filled Markdown, not the HTML this
|
|
12
|
+
// gate reads. fill() pushes entries in output order and the Markdown-to-HTML step keeps text order,
|
|
13
|
+
// so each entry, in array order, is matched to the next untraced occurrence of its rendered text in
|
|
14
|
+
// document order that is a whole numeral and reads as the entry's row there (fits: "1,237 million"
|
|
15
|
+
// is not F2's 1,237), and strips that one occurrence only. A copy of the same text that no entry
|
|
16
|
+
// accounts for stays in the scan; an entry whose text is gone strips nothing.
|
|
17
|
+
|
|
18
|
+
import { NOT_CLAIMS } from "./numerals.mjs"
|
|
19
|
+
|
|
20
|
+
/** Structural numerals: versions, figure/table/section references, times (NOT_CLAIMS 7-11), pages, citations, placeholders. */
|
|
21
|
+
export const STRUCTURAL = [...NOT_CLAIMS.slice(7, 12), /\b(?:p|pp|page|pages)\.?\s*\d+(?:\s*[-\u2013]\s*\d+)?/gi, /\[(?:S|Source\s*)\d+\]|\[\^\d+\]/gi, /\{\{[^}]*\}\}/g]
|
|
22
|
+
const NUMERAL = /\d+(?:[.,\-/:]\d+)*/g
|
|
23
|
+
const TRACED = "\u0000"
|
|
24
|
+
const QUOTE_RUNS = [/"([^"]+)"/g, /\u201C([^\u201C\u201D]+)\u201D/g, /\u300C([^\u300C\u300D]+)\u300D/g, /\u300E([^\u300E\u300F]+)\u300F/g]
|
|
25
|
+
|
|
26
|
+
export const clip = (text) => String(text).replace(/\s+/g, " ").trim().slice(0, 80)
|
|
27
|
+
|
|
28
|
+
function ownerTexts(ctx, charts) {
|
|
29
|
+
const owners = new Map()
|
|
30
|
+
for (const entry of ctx.texts) {
|
|
31
|
+
const kind = ctx.kindOf(entry.scope)
|
|
32
|
+
if (kind === "sources" || kind === "toc") continue
|
|
33
|
+
if (charts.has(ctx.util.closest(entry.node, (cur) => cur.tag === "figure"))) continue
|
|
34
|
+
const hidden = ctx.util.closest(entry.node, (cur) => cur.attrs["data-fact"] !== undefined || cur.attrs["data-translates"] !== undefined || cur.tag === "q" || cur.tag === "blockquote")
|
|
35
|
+
owners.set(entry.owner, (owners.get(entry.owner) ?? "") + (hidden ? ` ${TRACED} ` : entry.node.text))
|
|
36
|
+
}
|
|
37
|
+
return owners
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function scanOwner(ctx, owner, raw, flag) {
|
|
41
|
+
let text = raw
|
|
42
|
+
if (/^h[1-6]$/.test(owner.tag)) text = text.replace(/^\s*\d+(?:[.-]\d+)*[.):]?\s+/, " ")
|
|
43
|
+
if (text.includes("{{")) flag("unresolved_placeholder", owner, `leftover placeholder: "${clip(text.slice(text.indexOf("{{")))}"`)
|
|
44
|
+
for (const re of QUOTE_RUNS) {
|
|
45
|
+
for (const match of text.matchAll(re)) {
|
|
46
|
+
const inner = match[1].replaceAll(TRACED, " ").trim()
|
|
47
|
+
const after = text.slice(match.index + match[0].length).trim().split(/\s+/).slice(0, 6).join(" ")
|
|
48
|
+
if (inner && (inner.split(/\s+/).length >= 3 || ctx.util.citationMarker.test(after))) flag("quote_unbound", owner, `direct quotation with no Q row: "${clip(match[0])}"`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const rest = STRUCTURAL.reduce((acc, re) => acc.replace(re, " "), text)
|
|
52
|
+
for (const match of rest.matchAll(NUMERAL)) flag("untraceable_number", owner, `number "${match[0]}" has no ledger row: "${clip(rest.slice(Math.max(0, match.index - 30), match.index + 30))}"`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Strip each fill-map entry at its own fill site: the next untraced occurrence after the previous entry's site. */
|
|
56
|
+
function traceFills(owners, fillMap, fits) {
|
|
57
|
+
const keys = [...owners.keys()]
|
|
58
|
+
const texts = [...owners.values()]
|
|
59
|
+
let [index, from] = [0, 0]
|
|
60
|
+
for (const entry of fillMap ?? []) {
|
|
61
|
+
const { rendered } = entry
|
|
62
|
+
if (!rendered) continue
|
|
63
|
+
for (let i = index, at = from; i < texts.length; i += 1, at = 0) {
|
|
64
|
+
let hit = texts[i].indexOf(rendered, at)
|
|
65
|
+
const glued = (pos) => /\d/.test(texts[i][pos - 1] ?? "") || /\d/.test(texts[i][pos + rendered.length] ?? "")
|
|
66
|
+
while (hit !== -1 && (glued(hit) || !fits(entry, texts[i].slice(hit)))) hit = texts[i].indexOf(rendered, hit + 1)
|
|
67
|
+
if (hit === -1) continue
|
|
68
|
+
texts[i] = `${texts[i].slice(0, hit)}${TRACED}${texts[i].slice(hit + rendered.length)}`
|
|
69
|
+
;[index, from] = [i, hit + TRACED.length]
|
|
70
|
+
break
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return new Map(keys.map((key, i) => [key, texts[i]]))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {object} ctx buildContext(...) from gates-static.mjs
|
|
78
|
+
* @param {Map<object, unknown>} charts chart figures, whose numerals chart checks own
|
|
79
|
+
* @param {{ fact_id: string, rendered: string, start?: number, end?: number }[] | null} fillMap Markdown fill map, or null
|
|
80
|
+
* @param {(code: string, node: object, message: string) => void} flag
|
|
81
|
+
* @param {(entry: object, tail: string) => boolean} fits whether the text from a candidate site on reads as the entry's row
|
|
82
|
+
*/
|
|
83
|
+
export function traceChecks(ctx, charts, fillMap, flag, fits) {
|
|
84
|
+
for (const [owner, text] of traceFills(ownerTexts(ctx, charts), fillMap, fits)) scanOwner(ctx, owner, text, flag)
|
|
85
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test"
|
|
2
|
+
import { readFileSync } from "node:fs"
|
|
3
|
+
import { join } from "node:path"
|
|
4
|
+
|
|
5
|
+
import { indexClaims } from "./claims-index.mjs"
|
|
6
|
+
import { checkFidelity } from "./gates-fidelity.mjs"
|
|
7
|
+
|
|
8
|
+
type Defect = { code: string; message: string }
|
|
9
|
+
|
|
10
|
+
const FIXTURES = join(import.meta.dir, "tests", "fixtures")
|
|
11
|
+
const FIX = join(FIXTURES, "fidelity")
|
|
12
|
+
const read = (path: string) => readFileSync(path, "utf8")
|
|
13
|
+
const GOOD = read(join(FIX, "report-good.html"))
|
|
14
|
+
const OPTIONS = {
|
|
15
|
+
ledger: JSON.parse(read(join(FIX, "ledger-verify.json"))),
|
|
16
|
+
sourcesDir: join(FIX, "sources"),
|
|
17
|
+
claims: indexClaims(read(join(FIX, "claim-graph.md"))),
|
|
18
|
+
fillMap: null,
|
|
19
|
+
brief: read(join(FIXTURES, "fidelity-brief-locked.md")),
|
|
20
|
+
baseDir: FIX,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const run = (html: string, extra: Record<string, unknown> = {}) => (checkFidelity(html, { ...OPTIONS, ...extra }) as { defects: Defect[] }).defects
|
|
24
|
+
const codes = (defects: Defect[]) => defects.map((defect) => defect.code)
|
|
25
|
+
const F1_SENTENCE = "long [S4].</p>"
|
|
26
|
+
const F2_FILL = [{ token: "{{F2:units_sold}}", fact_id: "F2", start: 0, end: 5, rendered: "1,237" }]
|
|
27
|
+
|
|
28
|
+
describe("fill-site trace (Markdown fill map)", () => {
|
|
29
|
+
test("#given a filled 1,237 and a second bare 1,237 with no ledger row #when checked #then untraceable_number", () => {
|
|
30
|
+
const html = GOOD.replace("<h2>Appendix</h2>", "<p>Revenue was 1,237 million [S4].</p>\n<h2>Appendix</h2>")
|
|
31
|
+
expect(codes(run(html, { fillMap: F2_FILL }))).toEqual(["untraceable_number"])
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test("#given the filled occurrence alone #when checked #then it stays traced", () => {
|
|
35
|
+
const html = GOOD.replace("<h2>Appendix</h2>", "<p>Units sold were 1,237 [S4].</p>\n<h2>Appendix</h2>")
|
|
36
|
+
expect(run(html, { fillMap: F2_FILL })).toEqual([])
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test("#given one fill entry and two plain 1,237 sites #when checked #then the unfilled copy is untraceable_number", () => {
|
|
40
|
+
const html = GOOD.replace("<h2>Appendix</h2>", "<p>Units sold were 1,237 [S4].</p>\n<p>Again, 1,237 units [S4].</p>\n<h2>Appendix</h2>")
|
|
41
|
+
expect(codes(run(html, { fillMap: F2_FILL }))).toEqual(["untraceable_number"])
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test("#given two fill entries for two occurrences #when checked #then both are traced", () => {
|
|
45
|
+
const html = GOOD.replace("<h2>Appendix</h2>", "<p>Units sold were 1,237 [S4].</p>\n<p>Again, 1,237 units [S4].</p>\n<h2>Appendix</h2>")
|
|
46
|
+
expect(run(html, { fillMap: [...F2_FILL, { ...F2_FILL[0], start: 40, end: 45 }] })).toEqual([])
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe("verification claims are judged by their mark, not their wording", () => {
|
|
51
|
+
test("#given the reviewer's prose 'as confirmed with the annual inspection report' #when checked #then no unsupported_verification", () => {
|
|
52
|
+
expect(run(GOOD.replace(F1_SENTENCE, "long, as confirmed with the annual inspection report [S4].</p>"))).toEqual([])
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test("#given report-good's F7 marked verified but pointed at a page-sourced row #when checked #then unsupported_verification", () => {
|
|
56
|
+
expect(codes(run(GOOD.replace('value="42" data-fact="F7" data-claim-kind="verified" data-lineage="MEASURED">42 passed', 'value="320" data-fact="F1" data-unit="m" data-claim-kind="verified" data-lineage="MEASURED">up to 320 m')))).toEqual(["unsupported_verification"])
|
|
57
|
+
})
|
|
58
|
+
})
|