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,157 @@
|
|
|
1
|
+
# Unit rules for derived facts
|
|
2
|
+
|
|
3
|
+
The one definition of which unit a derived or converted fact row carries, and the reviewer's checklist for
|
|
4
|
+
`scripts/unit-rules.mjs`. ONE registry (`OPERATION_RULES`) owns, per operation: arity, operand-unit guards, context
|
|
5
|
+
fields, result-unit inference, and malformed shapes. `derive()` (`fact-derive.mjs`) and `recompute()`
|
|
6
|
+
(`fact-recompute.mjs`) both call it; neither has op-specific unit code. `row.unit` is NEVER used to choose arithmetic;
|
|
7
|
+
numeric guards may read `exact` values, unit selection may not. The property tests in
|
|
8
|
+
`scripts/fact-unit-rules.test.ts` iterate the registry, so a new operation needs a table row here and cases there.
|
|
9
|
+
|
|
10
|
+
## Units
|
|
11
|
+
- Canonical unit text is stored and compared exactly (case-sensitive symbols). Registered aliases
|
|
12
|
+
(`pp` -> `%p`, `kilometre` -> `km`, ...) are canonicalized ONLY at input (derive/fact add); a relabel to an alias
|
|
13
|
+
at rest is a relabel and is detected. Unit text with no alias is stored as given: whitespace is trimmed only around
|
|
14
|
+
`*`, `/`, `^`, and parentheses, so `million people`, `metric ton`, and `USD per person` keep their inner spaces.
|
|
15
|
+
- A scale word (`million`, `M`, `bn`, `billion`, `thousand`, `e6`, ...; the `scale` rows of `units.mjs`) is a
|
|
16
|
+
multiplier, not a unit, and denomination scaling is out of scope, so it never becomes a unit atom: `million`,
|
|
17
|
+
`USD/M`, and `million*person` are refused at input (derive, and every operand read). Either store the scaled numeral
|
|
18
|
+
in the counted unit (5 million people as `5000000 person`) or keep the whole phrase as plain unit text:
|
|
19
|
+
`million people` has no table alias as a whole, so it is stored as given and sums only with `million people`, never
|
|
20
|
+
with `person`.
|
|
21
|
+
- `null` = dimensionless. Omitted unit on a derived spec is an error, not null (distinguish omission from declared null).
|
|
22
|
+
- Percent `%` stores percentage numerals (20% stores 20). Delta `%p` (percentage points). 1 basis point = 0.01 %p
|
|
23
|
+
(input alias `bp` -> `%p` with /100 scaling).
|
|
24
|
+
- Composite units are canonical algebraic strings built by the registry (`USD/kg`, `person`), never by concatenating
|
|
25
|
+
operand strings: equal factors cancel (`USD/(USD/person)` -> `person`).
|
|
26
|
+
- Affine scales (temperature: `C`, `F`) are flagged in the unit table; sum/diff/growth/share/ratio and every expr
|
|
27
|
+
node refuse them (temperatures are not summed or differenced as absolute values); only convert takes them.
|
|
28
|
+
Affine atoms in composites are refused before cancellation. Exponents are exact signed bigint integers, including zero.
|
|
29
|
+
|
|
30
|
+
## Operation table
|
|
31
|
+
|
|
32
|
+
| op | arity | operand guards | context fields (persisted on the row) | result unit | malformed -> recompute ok:false |
|
|
33
|
+
|---|---|---|---|---|---|
|
|
34
|
+
| sum | n>=2 | non-affine units, all equal after canonicalization; if `%`: `disjoint: true` AND `reference` (one denominator id/text) equal on every operand AND rationale | disjoint, reference, rationale | the common unit (`%` stays `%`) | <2 operands; missing id; no context; unequal/affine units; `%` without disjoint, reference, or rationale |
|
|
35
|
+
| diff | 2 | non-affine units; typed pairs only: U-U -> U (same non-% unit); %-% -> `%p`; %-%p -> `%`; %p-%p -> `%p`; %p-% REFUSED | none | per the pair rule | arity; invalid pair (`%p - %`, `m - person`); affine units; stray context |
|
|
36
|
+
| growth | 2 (from,to) | equal non-affine units; same entity; two different valid_at; from.exact > 0 | none | `%` (= 100(to-from)/from) | arity; unequal/affine units; entity/valid_at; from<=0; stray context |
|
|
37
|
+
| share | 2 (part,whole) | equal non-affine units; whole.exact > 0; 0 <= part <= whole | none | `%` (= 100 part/whole) | arity; units; whole<=0; part outside [0,whole]; stray context |
|
|
38
|
+
| ratio | 2 (num,den) | non-affine units; den.exact != 0; unequal `%`/`%p` REFUSED | none | registry composite: equal units -> null; else canonical `U(num)/U(den)` with cancellation | arity; den=0; stray context |
|
|
39
|
+
| convert | 1 | U(a) has a registered factor to `target`; currencies REFUSED (use expr with a dated rate row) | target (persisted as canonical text; the ONLY accepted declared unit; `row.unit` is compared to it exactly, never canonicalized at rest) | the persisted target | arity; no context; target missing; target not canonical (an alias at rest); currency target; no factor; declared unit != persisted target |
|
|
40
|
+
| expr | n>=1 | formula ids == operand set; node rules: `+`/`-` follow the sum/diff typed-pair rules, affine refusal included (so `20% - 10%` is `%p`, never `%`, and `25 C + 30 C` is refused); `+` also takes `%` + `%p` in either order -> `%` (as diff's `%-%p`), while `%` + `%` is refused (use sum); `*` and `/` compose canonical composites, `%`/`%p` operands normalize by /100 to dimensionless first; result must be fully inferred: NO rationale escape | none | the inferred unit; an explicit null->`%` conversion is a separate `convert`-like node (`pct()`) | formula/operand id mismatch; parse error; /0; uninferable unit; affine operands; stray context |
|
|
41
|
+
|
|
42
|
+
Every row's malformed column also holds the two operand-identity shapes below (`repeated operand`, `self reference`).
|
|
43
|
+
|
|
44
|
+
## Operand identity (applies to every row)
|
|
45
|
+
- Operand ids are DISTINCT within one derived row. sum of F1, F1 is not a sum of two shares, and diff, ratio, share,
|
|
46
|
+
growth, or convert of an operand with itself is meaningless and would dodge every pair guard. A repeated id is
|
|
47
|
+
refused by `derive()` (`operand F1 is repeated`) and is `ok:false` at `recompute()` (malformed shape
|
|
48
|
+
`repeated operand`). The check sits in the registry (`expectedUnit` and `malformed`), so no operation can skip it.
|
|
49
|
+
- expr may reference the same id in several leaves (`F1 * F1` is a legitimate square). Its stored operand list is the
|
|
50
|
+
formula's id SET, which must equal the formula ids (`formula id mismatch`); an id listed twice in that list is the
|
|
51
|
+
`repeated operand` shape too.
|
|
52
|
+
- A derived row may not cite itself, directly (`self reference`: its own id among its operands) or through nested
|
|
53
|
+
derived operands (a cycle). `recompute()` refuses both. `derive()` recomputes every derived operand first and
|
|
54
|
+
refuses one that does not recompute (tampered, cyclic, or self-citing), so nothing is built on such a row.
|
|
55
|
+
|
|
56
|
+
## Invariants (property tests iterate the table)
|
|
57
|
+
1. For each op: generate valid typed operands (rational boundaries, aliases at input, composites, %/%p, affine,
|
|
58
|
+
metadata, conversion targets, repeated expr refs), derive, assert `recompute(row).ok === true`.
|
|
59
|
+
2. Relabel: for each op, set `unit` to every different canonical string/null (incl. aliases and other valid targets),
|
|
60
|
+
regenerate render, keep exact: `recompute(row).ok === false` naming the expected unit; and `derive()` with that
|
|
61
|
+
declared unit is refused.
|
|
62
|
+
3. Malformed: per op, every shape in the table's malformed column has one case, named in the registry's
|
|
63
|
+
`OPERATION_RULES[op].malformed` list (the test asserts its case names equal that list): `recompute` returns
|
|
64
|
+
`ok:false` for that shape's own reason, never throws.
|
|
65
|
+
4. Nested derived rows are verified recursively (operands may be derived rows); cycles and self references are
|
|
66
|
+
refused, by `recompute()` and by `derive()` before it builds on a derived operand.
|
|
67
|
+
5. Render precision (`renderPrecision` in `unit-rules.mjs`): fewest decimal places for sum, diff, an expr whose root
|
|
68
|
+
node is `+` or `-`, and every `%p` result; fewest significant figures otherwise (an expr `*` or `/` root with a
|
|
69
|
+
non-`%p` result included). Operand values and expr literals both count.
|
|
70
|
+
|
|
71
|
+
## Decisions
|
|
72
|
+
- Q1: the canonical composite goes in `unit` (consumers compare `unit` text); no side field.
|
|
73
|
+
- Q2: expr with an unresolved unit is REFUSED; a known-unit `a*b/c` is inferable, so no rationale escape exists.
|
|
74
|
+
- Q3: canonical `%p`, input alias `pp` (matches units.mjs).
|
|
75
|
+
- Q4: strict null metadata: null matches only null; required metadata must be present.
|
|
76
|
+
|
|
77
|
+
## Out of scope (follow-ups)
|
|
78
|
+
Per-capita identity, named index/base and index-point differences, weighted means, dated FX / nominal-real bases,
|
|
79
|
+
same-currency denomination scaling (M/B), summing ratios over a common base. Each needs its own table row + tests.
|
|
80
|
+
|
|
81
|
+
## Dimensions
|
|
82
|
+
|
|
83
|
+
1. Enforced: kind and operation agree; a changed discriminator cannot disable recursive verification.
|
|
84
|
+
2. Enforced: expression and direct arithmetic check population, snapshot, and accounting metadata; common metadata propagates.
|
|
85
|
+
3. Enforced: stored `precision: { places, sigfigs }` carries operand precision independently of display trailing zeros.
|
|
86
|
+
4. Enforced: scaled aliases normalize at every composite factor and exponent, scaling the input numeral exactly.
|
|
87
|
+
5. Enforced: derive and recompute return classified refusals with `shape`, `expected`, and `detail`; derive never appends on refusal.
|
|
88
|
+
6. Enforced: aliases canonicalize at input; noncanonical stored results and conversion targets are refused, and a source unit that needs rescaling (a scaled alias) is refused as an operand.
|
|
89
|
+
7. Enforced: stored rationals are reduced integer strings with positive denominators and canonical zero.
|
|
90
|
+
8. Enforced: operation contexts have closed keys and types, including non-percent sums and context-free operations.
|
|
91
|
+
9. Enforced: formula, factor, derived_from, raw, render, metadata, precision, and compatibility are compared on replay.
|
|
92
|
+
10. Enforced: metadata object equality ignores key insertion order; missing and invalid metadata never equal explicit null.
|
|
93
|
+
11. Enforced: new derivations require active transitive dependencies; historical arithmetic remains reproducible after retraction.
|
|
94
|
+
12. Enforced: graph and expression traversal use explicit stacks; shared nodes are evaluated once per verification.
|
|
95
|
+
13. Enforced: factor exponents use bigint, preserve grouping and quotient direction, and canonicalize idempotently.
|
|
96
|
+
14. Enforced: affine atoms cannot hide in composites or cancellation; scale words are never unit atoms.
|
|
97
|
+
15. Enforced: disjoint percentage shares are individually in [0,100], total at most 100, and use exactly one reference.
|
|
98
|
+
16. Enforced: currency table conversion is refused; FX expressions require a dated rate and persist compatibility `unchecked`.
|
|
99
|
+
17. Enforced: symbols are case-sensitive; only registered spelling aliases normalize; opaque Unicode and phrases stay distinct.
|
|
100
|
+
18. Enforced: missing, undefined, and empty required fields do not substitute for explicit null; zero remains valid.
|
|
101
|
+
19. Enforced: identifiers allocate exactly beyond 2^53 using bigint; operand lists remain distinct.
|
|
102
|
+
20. Enforced: a supplied expression operand list must equal the formula's unique leaf set.
|
|
103
|
+
21. Enforced: additive expression roots and percentage-point results use decimal places; other roots count literal significant figures.
|
|
104
|
+
22. Enforced: cloned snapshots with the same spec and timestamp produce identical rows; replay appends a new identity.
|
|
105
|
+
23. Documented limitation: coherent rewrites of trusted operands, context, and all dependent audit fields require external provenance to detect.
|
|
106
|
+
24. Enforced: arity belongs to each table row; sums are permutation-invariant while ordered operations keep positional meaning.
|
|
107
|
+
25. Enforced: denominators, positive growth starts, and bounded shares obey numeric domains; ordinary negative arithmetic remains valid.
|
|
108
|
+
26. Enforced: percent, percentage-point, and fraction semantics follow typed pairs, multiplicative normalization, and explicit pct nodes.
|
|
109
|
+
27. Enforced: table factors and affine offsets compose exactly; unknown and cross-dimension conversions are refused.
|
|
110
|
+
28. Enforced: only decimal literals, fact identifiers, binary arithmetic, parentheses, and pct parse; unsupported syntax is refused.
|
|
111
|
+
29. Enforced: rounding is half away from zero; values are plain decimals and display uses canonical units and grouping.
|
|
112
|
+
30. Enforced: seeded factor trees and conversion compositions have expected values computed independently of OPERATION_RULES.
|
|
113
|
+
31. Enforced: a ledger written before `context`, `precision`, and `compatibility` existed still loads and accepts new derivations. On measured, quote, and assumed rows a missing column reads as null, and a registered alias unit is canonicalized on read (unknown or scaled text stays as given); the file changes only on the next write. Derived and converted rows without those columns load, but `recompute` reports shape `legacy derived row` and `derive` refuses them as operands with the same shape until they are regenerated.
|
|
114
|
+
32. Enforced: value/exact agreement. When a row stores both, `exact` must equal `value` at the value's decimal places; a legacy row that disagrees loads and is flagged `dimension 32`, and `addRow` refuses a new one.
|
|
115
|
+
|
|
116
|
+
## Precedence: dimension 31 wins at read time
|
|
117
|
+
|
|
118
|
+
Anything dev's shipped writer could write loads and validates. Every row is first read through `readRow`: exacts that
|
|
119
|
+
are integer pairs are reduced (sign on the numerator, no leading zeros, positive denominator), decimal values lose
|
|
120
|
+
leading zeros, and source rows get canonical units and null later columns. Measured exacts stay as stored because they
|
|
121
|
+
are always the reduced exact of `value`. Only then do the strict dimensions run. They apply in full to derived and
|
|
122
|
+
converted rows that carry this version's columns, and to every row this version writes. A source row that loads but
|
|
123
|
+
still breaks a strict rule (a scale word as unit, empty metadata, an operation or derivation fields) is flagged when it
|
|
124
|
+
is used: `derive` and `recompute` refuse it with shape `dimension 6`, `dimension 18`, or `dimension 1`, and `addRow`
|
|
125
|
+
refuses to write a new one. Normalization never strips user data: an `exact` carrying keys beyond `num` and `den` is left exactly as written and flagged `dimension 7` on use, never silently reduced to `{ num, den }`. Reading a normalized file again changes nothing. The legacy test fixture is generated by
|
|
126
|
+
dev's own `addRow` (`scripts/tests/fixtures/fidelity/gen-legacy-dev.mjs`, which pins the dev commit).
|
|
127
|
+
|
|
128
|
+
## Trust boundary
|
|
129
|
+
|
|
130
|
+
Recomputation verifies arithmetic against the ledger's trusted operands and operation context, not against an external
|
|
131
|
+
immutable source. A zero-metre conversion rewritten from km to cm together with its target, formula, factor, precision,
|
|
132
|
+
and display is indistinguishable from an honestly generated cm conversion. Single-field relabels are detected; a coherent
|
|
133
|
+
rewrite needs an immutable ledger baseline or source provenance outside this arithmetic verifier.
|
|
134
|
+
|
|
135
|
+
Rows carry no per-row schema marker, so a current derived row with `context`, `precision`, and `compatibility` all
|
|
136
|
+
deleted reads as a legacy row: `validateLedger` skips its at-rest unit and row-shape checks, and a relabeled unit on
|
|
137
|
+
it passes validation. It never becomes trusted again: `recompute` and `derive` refuse it as `legacy derived row`.
|
|
138
|
+
|
|
139
|
+
## Implementation notes
|
|
140
|
+
|
|
141
|
+
- Context fields live in the row's `context` column (null or an object on derived and converted rows, always null on
|
|
142
|
+
measured, quote, and assumed rows: a `KIND_RULES` column):
|
|
143
|
+
sum stores `{ disjoint, reference }`, convert stores `{ target }`. The sum rationale stays in `rationale`.
|
|
144
|
+
Schema and recomputation both require the operation's context. Context-free rows carry explicit null.
|
|
145
|
+
- `schemaVersion` remains 1. A derived or converted row from an older writer (no `context`, `precision`, or
|
|
146
|
+
`compatibility`) is kept as written and never used as an operand: regenerate it with `derive` to carry precision,
|
|
147
|
+
compatibility, and complete audit fields. Older source rows need nothing: they are read with null later columns.
|
|
148
|
+
- `reference` is either one text (the caller asserts one shared denominator) or a map from each operand id to its
|
|
149
|
+
denominator; the map passes only when every operand names the same non-empty denominator.
|
|
150
|
+
- `addRow` canonicalizes source units and scales alias numerals, including `100 bp/person` to `1 %p/person`.
|
|
151
|
+
At rest, source-row aliases are canonicalized on read (dimension 31); current derived rows with alias units are refused. A declared result unit with a nontrivial alias scale is refused.
|
|
152
|
+
Scale exponents with absolute value above 10000 are explicitly refused instead of allocating an unbounded numeral.
|
|
153
|
+
- A ratio over `%` or `%p` operands with unequal units is refused (use expr, where `%` enters `*` and `/` as a fraction).
|
|
154
|
+
- convert reads its target from `to`; a declared `unit` is optional and must equal that target.
|
|
155
|
+
- expr persists compatibility `unchecked` on the row as well as returning it. Population, snapshot, and accounting
|
|
156
|
+
predicates apply; a currency-rate operand needs a nonempty `valid_at`. This is arithmetic and unit verification,
|
|
157
|
+
not a guarantee of rate freshness, matching dates, population identity, or financial comparability.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// claim-kinds.mjs - the claim kinds a report sentence can carry, as one table, CLAIM_KINDS.
|
|
2
|
+
//
|
|
3
|
+
// A claim kind is structural: the writer marks it in the placeholder ({{V:F12:label}}), fill
|
|
4
|
+
// emits it as the data-claim-kind attribute on the element it renders, and the fidelity gate
|
|
5
|
+
// (fidelity-claims.mjs) checks that every marked element cites a row the kind accepts. An
|
|
6
|
+
// unmarked element is a plain fact or quote and is checked only by the value and quote checks.
|
|
7
|
+
// The one lexical piece is `unmarked`: a few first-person run patterns that raise a minor warning
|
|
8
|
+
// when a sentence reads like the kind but carries no mark; they never raise a major. The table in
|
|
9
|
+
// references/fidelity-claim-kinds.md is rendered from CLAIM_KINDS (`node claim-kinds.mjs --render-doc`).
|
|
10
|
+
// Pure; node: builtins only. Korean phrases are \u escapes.
|
|
11
|
+
|
|
12
|
+
import { isCliEntry } from "./entry-guard.mjs"
|
|
13
|
+
|
|
14
|
+
export const CLAIM_ATTRIBUTE = "data-claim-kind"
|
|
15
|
+
|
|
16
|
+
const kind = (entry) => Object.freeze({ ...entry, rowKinds: Object.freeze(entry.rowKinds), sourceKinds: entry.sourceKinds && Object.freeze(entry.sourceKinds), unmarked: entry.unmarked && Object.freeze({ ...entry.unmarked, patterns: Object.freeze(entry.unmarked.patterns) }) })
|
|
17
|
+
|
|
18
|
+
// Korean pieces as \u escapes: WE_SUBJECT = uri|jeohui as a standalone subject word (+ ga|neun, or " tim-i"),
|
|
19
|
+
// WE_RUN = silhaeng-haet/hayeo/hae | dol-lyeot/lyeo, DIRECT = jikjeop, RUN = silhaeng | dol-li/ryeo/ryeot/rin/ril,
|
|
20
|
+
// SUBJECT = a word ending in the particle ga|i|eun|neun|kkeseo (a third party ran it, not us).
|
|
21
|
+
const KO_WE_SUBJECT = "(?:^|[\\s,.;!?(])(?:\uC6B0\uB9AC|\uC800\uD76C)(?:[\uAC00\uB294]|\\s+\uD300\uC774)\\s+"
|
|
22
|
+
const KO_WE_RUN = "(?:\uC2E4\uD589(?:\uD588|\uD558\uC5EC|\uD574)|\uB3CC[\uB838\uB824])"
|
|
23
|
+
const KO_RUN = "(?:\uC2E4\uD589|\uB3CC[\uB9AC\uB824\uB838\uB9B0\uB9B4])"
|
|
24
|
+
const KO_SUBJECT = "[^\\s,.;!?]*(?:[\uAC00\uC774\uC740\uB294]|\uAED8\uC11C)(?=\\s)"
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* kind -> { prefix: placeholder prefix (null = the default form), placeholder: an example,
|
|
28
|
+
* rowKinds: ledger row kinds it accepts, sourceKinds: meta.json kinds the snapshot holding the
|
|
29
|
+
* cited row's own locator artifact must have (null = any), defect: code when a marked fact cites a
|
|
30
|
+
* row it refuses, note: the rule's limits, unmarked: { code, rule, patterns } for an unmarked
|
|
31
|
+
* sentence that reads like the kind (a minor warning only), or null }.
|
|
32
|
+
*/
|
|
33
|
+
export const CLAIM_KINDS = Object.freeze({
|
|
34
|
+
fact: kind({
|
|
35
|
+
prefix: null,
|
|
36
|
+
placeholder: "{{F12:label}}",
|
|
37
|
+
rowKinds: ["measured", "converted", "derived"],
|
|
38
|
+
sourceKinds: null,
|
|
39
|
+
defect: "claim_kind_mismatch",
|
|
40
|
+
note: "Accepts exactly what an unmarked numeral accepts, so marking fact is never stricter than not marking. Neither accepts an `assumed` row: an unmarked assumption reads as a fact, so it needs its `{{A:F12:label}}` mark (`claim_kind_mismatch` otherwise).",
|
|
41
|
+
unmarked: null,
|
|
42
|
+
}),
|
|
43
|
+
quote: kind({ prefix: null, placeholder: "{{Q7:label}}", rowKinds: ["quote"], sourceKinds: null, defect: "claim_kind_mismatch", note: "", unmarked: null }),
|
|
44
|
+
verified: kind({
|
|
45
|
+
prefix: "V",
|
|
46
|
+
placeholder: "{{V:F12:label}}",
|
|
47
|
+
rowKinds: ["measured", "quote"],
|
|
48
|
+
sourceKinds: ["command"],
|
|
49
|
+
defect: "unsupported_verification",
|
|
50
|
+
note: "The cited row itself must have a locator artifact inside `sources/<source_id>/` whose `meta.json` kind is `command`; another command row in the sentence never counts. Whether it is the right command for the claim is the meaning verifier's call (minor), not this gate's.",
|
|
51
|
+
unmarked: {
|
|
52
|
+
code: "verification_claim_unmarked",
|
|
53
|
+
rule: "a first-person run: we/I (have) ran, run, executed, verified by (not ran out of); by running after we/our in the sentence; \\uC6B0\\uB9AC or \\uC800\\uD76C as the subject word (+ \\uAC00, \\uB294, or \\uD300\\uC774), then \\uC2E4\\uD589\\uD588, \\uC2E4\\uD589\\uD558\\uC5EC, \\uC2E4\\uD589\\uD574, \\uB3CC\\uB838, or \\uB3CC\\uB824 in the clause with no third-party subject between; \\uC9C1\\uC811 with no third-party subject, then \\uC2E4\\uD589 or \\uB3CC\\uB9AC in the clause",
|
|
54
|
+
patterns: [
|
|
55
|
+
/\b(?:we|I)(?:'ve)?\s+(?:(?:have|had|also|then|first|just|again)\s+)*(?:ran|run|re-?ran|re-?run|executed|verified\s+by)\b(?!\s+out\s+of\b)/i,
|
|
56
|
+
/\b(?:we|our)\b.*?\bby\s+running\b/i,
|
|
57
|
+
new RegExp(`${KO_WE_SUBJECT}(?:(?!${KO_SUBJECT})[^\\s,.;!?]+\\s+)*?${KO_WE_RUN}`),
|
|
58
|
+
new RegExp(`(?:^|[,.;!?])\\s*(?:(?!${KO_SUBJECT})[^\\s,.;!?]+\\s+)*\uC9C1\uC811\\s*(?:[^\\s,.;!?]+\\s*)??${KO_RUN}`),
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
}),
|
|
62
|
+
derived: kind({ prefix: "D", placeholder: "{{D:F12:label}}", rowKinds: ["derived", "converted"], sourceKinds: null, defect: "claim_kind_mismatch", note: "", unmarked: null }),
|
|
63
|
+
assumed: kind({
|
|
64
|
+
prefix: "A",
|
|
65
|
+
placeholder: "{{A:F12:label}}",
|
|
66
|
+
rowKinds: ["assumed"],
|
|
67
|
+
sourceKinds: null,
|
|
68
|
+
defect: "claim_kind_mismatch",
|
|
69
|
+
note: "Numeric assumptions only (an assumed ledger row carries a decimal value); a qualitative assumption stays prose.",
|
|
70
|
+
unmarked: null,
|
|
71
|
+
}),
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
/** Whether a row satisfies a claim kind, given the meta.json kind of the snapshot holding its own locator artifact. */
|
|
75
|
+
export function claimSatisfied(name, row, sourceKind) {
|
|
76
|
+
const entry = CLAIM_KINDS[name]
|
|
77
|
+
return Boolean(entry && row && entry.rowKinds.includes(row.kind) && (entry.sourceKinds === null || entry.sourceKinds.includes(sourceKind)))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** The kind an unmarked element citing `row` must be marked with (an assumed row needs `assumed`), or null when unmarked is fine. */
|
|
81
|
+
export function unmarkedRequires(row) {
|
|
82
|
+
if (!row || ["fact", "quote"].some((name) => CLAIM_KINDS[name].rowKinds.includes(row.kind))) return null
|
|
83
|
+
return Object.keys(CLAIM_KINDS).find((name) => CLAIM_KINDS[name].rowKinds.includes(row.kind)) ?? null
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** The kinds whose unmarked patterns match the sentence. */
|
|
87
|
+
export function unmarkedKinds(text) {
|
|
88
|
+
return Object.entries(CLAIM_KINDS)
|
|
89
|
+
.filter(([, entry]) => entry.unmarked?.patterns.some((re) => re.test(text)))
|
|
90
|
+
.map(([name]) => name)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const CLAIM_TABLE_BEGIN = "<!-- claim-kinds:begin (rendered from CLAIM_KINDS; do not edit by hand) -->"
|
|
94
|
+
export const CLAIM_TABLE_END = "<!-- claim-kinds:end -->"
|
|
95
|
+
|
|
96
|
+
const code = (text) => `\`${text}\``
|
|
97
|
+
|
|
98
|
+
/** The Markdown table of references/fidelity-claim-kinds.md. */
|
|
99
|
+
export function renderClaimKindTable() {
|
|
100
|
+
const head = ["| Kind | Placeholder | Attribute fill emits | Accepted row kinds | Required source kind | Defect when unsatisfied | Unmarked warning | Notes |", "|---|---|---|---|---|---|---|---|"]
|
|
101
|
+
const lines = Object.entries(CLAIM_KINDS).map(([name, entry]) => {
|
|
102
|
+
const attribute = entry.prefix === null ? `none (or ${code(`${CLAIM_ATTRIBUTE}="${name}"`)})` : code(`${CLAIM_ATTRIBUTE}="${name}"`)
|
|
103
|
+
const source = entry.sourceKinds === null ? "any" : entry.sourceKinds.map(code).join(", ")
|
|
104
|
+
const warn = entry.unmarked ? `${code(entry.unmarked.code)} on ${entry.unmarked.rule}` : "none"
|
|
105
|
+
return `| ${code(name)} | ${code(entry.placeholder)} | ${attribute} | ${entry.rowKinds.map(code).join(", ")} | ${source} | ${code(entry.defect)} | ${warn} | ${entry.note || "-"} |`
|
|
106
|
+
})
|
|
107
|
+
return `${[...head, ...lines].join("\n")}\n`
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (isCliEntry(import.meta.url)) {
|
|
111
|
+
if (process.argv[2] === "--render-doc" && process.argv.length === 3) process.stdout.write(renderClaimKindTable())
|
|
112
|
+
else {
|
|
113
|
+
process.stderr.write("usage: node claim-kinds.mjs --render-doc\n")
|
|
114
|
+
process.exitCode = 2
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// claims-index.mjs - deterministic index of claim-graph.md: the one Markdown table whose header
|
|
2
|
+
// names claim_id, statement, risk tier, and status, plus the ids listed under the
|
|
3
|
+
// `## verified-claims` digest. Pure function of the text; node: builtins only.
|
|
4
|
+
|
|
5
|
+
const CLAIM_ID = /^C\d+$/
|
|
6
|
+
const REQUIRED_COLUMNS = Object.freeze({ claim_id: "claim_id", statement: "statement", risk: "risk tier", status: "status" })
|
|
7
|
+
|
|
8
|
+
/** Split one table line on unescaped pipes; drops the leading and trailing border cells. */
|
|
9
|
+
function splitRow(line) {
|
|
10
|
+
const cells = []
|
|
11
|
+
let current = ""
|
|
12
|
+
for (let index = 0; index < line.length; index += 1) {
|
|
13
|
+
const char = line[index]
|
|
14
|
+
if (char === "\\" && line[index + 1] === "|") {
|
|
15
|
+
current += "|"
|
|
16
|
+
index += 1
|
|
17
|
+
} else if (char === "|") {
|
|
18
|
+
cells.push(current)
|
|
19
|
+
current = ""
|
|
20
|
+
} else current += char
|
|
21
|
+
}
|
|
22
|
+
cells.push(current)
|
|
23
|
+
const trimmed = cells.map((value) => value.trim())
|
|
24
|
+
if (line.trim().startsWith("|")) trimmed.shift()
|
|
25
|
+
if (line.trim().endsWith("|")) trimmed.pop()
|
|
26
|
+
return trimmed
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function normalizeHeader(value) {
|
|
30
|
+
return value.replace(/`/g, "").trim().toLowerCase().replace(/[\s_-]+/g, " ")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function columnIndexes(header) {
|
|
34
|
+
const names = header.map(normalizeHeader)
|
|
35
|
+
const indexes = {}
|
|
36
|
+
for (const [key, name] of Object.entries(REQUIRED_COLUMNS)) indexes[key] = names.indexOf(normalizeHeader(name))
|
|
37
|
+
return Object.values(indexes).every((index) => index >= 0) ? indexes : null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isSeparator(line) {
|
|
41
|
+
return /^\s*\|?\s*:?-+:?\s*(\|\s*:?-+:?\s*)*\|?\s*$/.test(line)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const DIGEST_HEADING = normalizeHeader("verified-claims")
|
|
45
|
+
|
|
46
|
+
/** `offset` is the index of `lines[0]` in the document, for line numbers in errors. */
|
|
47
|
+
function verifiedIds(lines, offset, errors) {
|
|
48
|
+
const ids = []
|
|
49
|
+
let inside = false
|
|
50
|
+
for (const [index, line] of lines.entries()) {
|
|
51
|
+
const heading = /^(#{1,6})\s+(.*)$/.exec(line)
|
|
52
|
+
if (heading) {
|
|
53
|
+
const name = normalizeHeader(heading[2])
|
|
54
|
+
inside = name === DIGEST_HEADING
|
|
55
|
+
if (!inside && name.startsWith(DIGEST_HEADING)) {
|
|
56
|
+
errors.push(`line ${offset + index + 1}: heading "${line.trim()}" is not the digest heading "## verified-claims"; drop the suffix so its entries are read`)
|
|
57
|
+
}
|
|
58
|
+
continue
|
|
59
|
+
}
|
|
60
|
+
if (!inside) continue
|
|
61
|
+
// An entry is a list item or a table row; prose lines never verify. Only the id an entry
|
|
62
|
+
// starts with is verified; ids mentioned later in the line are not.
|
|
63
|
+
const leading = /^\s*(?:(?:[-*+]|\d+[.)])\s+|\|\s*)`?(C\d+)\b/.exec(line)
|
|
64
|
+
if (leading && !ids.includes(leading[1])) ids.push(leading[1])
|
|
65
|
+
}
|
|
66
|
+
return ids
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Blank every line of a fenced code block (``` or ~~~, fences included) so nothing in it is read. */
|
|
70
|
+
function unfenced(lines) {
|
|
71
|
+
let fence = null
|
|
72
|
+
return lines.map((line) => {
|
|
73
|
+
const marker = /^\s{0,3}(`{3,}|~{3,})/.exec(line)
|
|
74
|
+
if (fence === null) {
|
|
75
|
+
if (marker) fence = marker[1]
|
|
76
|
+
return marker ? "" : line
|
|
77
|
+
}
|
|
78
|
+
const closes = marker && marker[1][0] === fence[0] && marker[1].length >= fence.length && line.trim() === marker[1]
|
|
79
|
+
if (closes) fence = null
|
|
80
|
+
return ""
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** @returns {{ claims: { claim_id: string, statement: string, risk: string, status: string, in_verified: boolean }[], errors: string[] }} */
|
|
85
|
+
export function indexClaims(markdown) {
|
|
86
|
+
const lines = unfenced(markdown.split(/\r?\n/))
|
|
87
|
+
const errors = []
|
|
88
|
+
const headerAt = lines.findIndex((line) => line.trim().startsWith("|") && columnIndexes(splitRow(line)) !== null)
|
|
89
|
+
if (headerAt < 0) {
|
|
90
|
+
return { claims: [], errors: ["no claim table: a Markdown table with claim_id, statement, risk tier, and status columns is required"] }
|
|
91
|
+
}
|
|
92
|
+
const header = splitRow(lines[headerAt])
|
|
93
|
+
const columns = columnIndexes(header)
|
|
94
|
+
const hasSeparator = isSeparator(lines[headerAt + 1] ?? "")
|
|
95
|
+
if (!hasSeparator) errors.push(`line ${headerAt + 2}: claim table header must be followed by a separator row`)
|
|
96
|
+
const firstRow = headerAt + (hasSeparator ? 2 : 1)
|
|
97
|
+
const rows = []
|
|
98
|
+
let tableEnd = firstRow
|
|
99
|
+
for (let index = firstRow; index < lines.length && lines[index].trim().startsWith("|"); index += 1) {
|
|
100
|
+
tableEnd = index + 1
|
|
101
|
+
const cells = splitRow(lines[index])
|
|
102
|
+
const at = `line ${index + 1}`
|
|
103
|
+
if (cells.length !== header.length) {
|
|
104
|
+
errors.push(`${at}: expected ${header.length} cells, found ${cells.length}`)
|
|
105
|
+
continue
|
|
106
|
+
}
|
|
107
|
+
const claimId = cells[columns.claim_id].replace(/`/g, "")
|
|
108
|
+
if (!CLAIM_ID.test(claimId)) {
|
|
109
|
+
errors.push(`${at}: claim_id "${claimId}" must match C<n>`)
|
|
110
|
+
continue
|
|
111
|
+
}
|
|
112
|
+
if (rows.some((row) => row.claim_id === claimId)) {
|
|
113
|
+
errors.push(`${at}: duplicate claim_id ${claimId}`)
|
|
114
|
+
continue
|
|
115
|
+
}
|
|
116
|
+
rows.push({ claim_id: claimId, statement: cells[columns.statement], risk: cells[columns.risk], status: cells[columns.status] })
|
|
117
|
+
}
|
|
118
|
+
// The digest section ends at the claim table: scan each side on its own so it cannot carry across.
|
|
119
|
+
const verified = [...new Set([...verifiedIds(lines.slice(0, headerAt), 0, errors), ...verifiedIds(lines.slice(tableEnd), tableEnd, errors)])]
|
|
120
|
+
for (const id of verified) {
|
|
121
|
+
if (!rows.some((row) => row.claim_id === id)) errors.push(`verified-claims lists ${id}, which has no claim row`)
|
|
122
|
+
}
|
|
123
|
+
const claims = rows.map((row) => ({ ...row, in_verified: verified.includes(row.claim_id) }))
|
|
124
|
+
return { claims, errors }
|
|
125
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// cli-support.mjs - argument parsing, typed usage errors, exit-code mapping, and JSON file IO
|
|
2
2
|
// shared by every report-tools subcommand. node: builtins only.
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { createHash } from "node:crypto"
|
|
5
|
+
import { readFileSync, renameSync, rmSync, writeFileSync } from "node:fs"
|
|
5
6
|
import { basename, dirname, join } from "node:path"
|
|
6
7
|
|
|
7
8
|
/** A usage or input error: always exit code 2. */
|
|
@@ -87,11 +88,24 @@ export function readJsonFile(path) {
|
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
/**
|
|
91
|
-
export
|
|
91
|
+
/** The sha256 hex digest of a string or bytes: the one hash every record and receipt carries. */
|
|
92
|
+
export const sha256Of = (content) => createHash("sha256").update(content).digest("hex")
|
|
93
|
+
|
|
94
|
+
/** Write text through a sibling temp file + rename; a failed write removes the temp file. */
|
|
95
|
+
export function writeTextAtomic(path, text) {
|
|
92
96
|
const temp = join(dirname(path), `.${basename(path)}.${process.pid}.${Date.now()}.tmp`)
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
try {
|
|
98
|
+
writeFileSync(temp, text)
|
|
99
|
+
renameSync(temp, path)
|
|
100
|
+
} catch (error) {
|
|
101
|
+
rmSync(temp, { force: true })
|
|
102
|
+
throw error
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Write JSON through writeTextAtomic, so a crash never leaves a half-written file. */
|
|
107
|
+
export function writeJsonAtomic(path, value) {
|
|
108
|
+
writeTextAtomic(path, `${JSON.stringify(value, null, 2)}\n`)
|
|
95
109
|
}
|
|
96
110
|
|
|
97
111
|
/** Print a result: JSON on stdout, one summary line on stderr. */
|
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
|
|
7
7
|
export const SCHEMA_VERSION = 1
|
|
8
8
|
|
|
9
|
+
/** The severity vocabulary in rank order; every DEFECT_CODES entry uses one of these. */
|
|
10
|
+
export const SEVERITIES = Object.freeze(["blocker", "major", "minor"])
|
|
11
|
+
|
|
12
|
+
/** Defect counts per severity, then total. */
|
|
13
|
+
export function countDefects(defects) {
|
|
14
|
+
const counts = Object.fromEntries(SEVERITIES.map((severity) => [severity, 0]))
|
|
15
|
+
for (const defect of defects) counts[defect.severity] += 1
|
|
16
|
+
return { ...counts, total: defects.length }
|
|
17
|
+
}
|
|
18
|
+
|
|
9
19
|
function deepFreeze(value) {
|
|
10
20
|
if (value === null || typeof value !== "object") return value
|
|
11
21
|
for (const key of Object.keys(value)) deepFreeze(value[key])
|
|
@@ -39,12 +49,72 @@ export const DEFECT_CODES = deepFreeze({
|
|
|
39
49
|
layout_scroll_container: { severity: "minor", integrity: false, hint: "Scrolling content prints clipped; make the content fit or wrap the table in a scroll wrapper only for HTML deliverables." },
|
|
40
50
|
layout_image_distorted: { severity: "major", integrity: false, hint: "Preserve the image aspect ratio (object-fit: contain inside a fixed container)." },
|
|
41
51
|
layout_sibling_overlap: { severity: "major", integrity: false, hint: "Separate the overlapping elements (margins, grid, or a slide split)." },
|
|
52
|
+
unresolved_placeholder: { severity: "blocker", integrity: true, hint: "Register the fact with fact add or remove the sentence; a placeholder is never filled with a guess." },
|
|
53
|
+
value_mismatch: { severity: "blocker", integrity: true, hint: "Re-run fact fill; never edit a filled value by hand." },
|
|
54
|
+
unit_mismatch: { severity: "blocker", integrity: true, hint: "Re-run fact fill, or derive a converted row with fact derive --op convert." },
|
|
55
|
+
quote_not_verbatim: { severity: "blocker", integrity: true, hint: "Re-insert the quote through its {{Q<n>}} placeholder; after a second failure the sentence is replaced by the quote." },
|
|
56
|
+
claim_status_invalid: { severity: "blocker", integrity: true, hint: "The claim id is unknown, refuted, unresolved, or not in verified-claims; move the sentence to Gaps or Contradictions, or remove it." },
|
|
57
|
+
incompatible_operands: { severity: "blocker", integrity: true, hint: "The operands differ in unit, population, period, or basis for this operation; fix the rows or declare the operation that fits." },
|
|
58
|
+
pointer_label_mismatch: { severity: "blocker", integrity: true, hint: "The placeholder's label is not the row's label; check the id, then the label." },
|
|
59
|
+
quote_anchor_unresolved: { severity: "blocker", integrity: true, hint: "A from or to anchor is missing or occurs more than once in the quote; pick unique anchor words." },
|
|
60
|
+
unit_duplicated: { severity: "blocker", integrity: true, hint: "The unit after the placeholder repeats the row's unit; use :value or remove the unit." },
|
|
61
|
+
polish_diff_fail: { severity: "blocker", integrity: true, hint: "The polish pass changed numbers, tickers, table rows, claim ids, markup, or hidden content; revert to the pre-polish artifact and apply the language fixes only." },
|
|
62
|
+
fidelity_interview_incomplete: { severity: "blocker", integrity: true, hint: "A fidelity field is pending or missing in brief.md; answer the open question or record its default, then rerun." },
|
|
63
|
+
derived_not_allowed: { severity: "blocker", integrity: true, hint: "The brief allows no derived or assumed numbers; cite a source row directly or change the brief with the requester." },
|
|
64
|
+
source_outside_floor: { severity: "blocker", integrity: true, hint: "The row's source is outside the source floor the brief records; use a source the brief allows or remove the fact." },
|
|
65
|
+
snapshot_hash_mismatch: { severity: "blocker", integrity: true, hint: "The source snapshot changed after it was pinned; re-snapshot the source and re-register its rows." },
|
|
66
|
+
chart_value_untraceable: { severity: "blocker", integrity: true, hint: "A chart value, tick, or data file does not match its ledger-backed data source; re-render the chart from the exported data." },
|
|
67
|
+
quote_unbound: { severity: "blocker", integrity: true, hint: "A direct quotation has no ledger quote row; register it with fact quote and insert it through its placeholder." },
|
|
68
|
+
edition_unverified: { severity: "blocker", integrity: true, hint: "The delivered file has no matching fact verify record; run fact verify on that edition and record it with --verified-by." },
|
|
69
|
+
untraceable_number: { severity: "major", integrity: false, hint: "Replace the typed number with a {{F<n>}} placeholder backed by a ledger row, or register it as assumed with a rationale." },
|
|
70
|
+
unsupported_verification: { severity: "major", integrity: false, hint: "A sentence marked verified (data-claim-kind=\"verified\") cites a row that is not recorded command output; cite the recorded output row or drop the verified mark." },
|
|
71
|
+
claim_kind_mismatch: { severity: "major", integrity: false, hint: "An element marked with a claim kind (data-claim-kind) cites a row that kind does not accept, or the kind is unknown; cite a row of that kind or fix the mark (references/fidelity-claim-kinds.md)." },
|
|
72
|
+
conflicting_readings: { severity: "major", integrity: false, hint: "Two ledger rows with different periods or qualifiers are cited as one fact; split the sentence or retire one row." },
|
|
73
|
+
hedge_dropped: { severity: "major", integrity: false, hint: "Restore the qualifier the source uses (estimated, about, not yet)." },
|
|
74
|
+
direction_flipped: { severity: "major", integrity: false, hint: "The source states the opposite direction; rewrite from the quote." },
|
|
75
|
+
causality_added: { severity: "major", integrity: false, hint: "The source states no cause; state the correlation or quote the source." },
|
|
76
|
+
qualifier_dropped: { severity: "major", integrity: false, hint: "The source bounds this value (up to, at least, about); restore the qualifier or use :q." },
|
|
77
|
+
unbound_sentence: { severity: "major", integrity: false, hint: "Bind the sentence to a fact, a quote, a citation, or a claim-graph node with data-claim." },
|
|
78
|
+
meaning_drift_confirmed: { severity: "major", integrity: false, hint: "Two independent readers found the same drift; rewrite from the quote or dispute with a reason." },
|
|
79
|
+
meaning_unverified: { severity: "major", integrity: false, hint: "No meaning reader judged this pair, or no strong reader judged a force_strong pair; run the missing reader on pairs.json and rerun fact meaning-check." },
|
|
80
|
+
polish_edit_rejected: { severity: "major", integrity: false, hint: "The edit changed a number, a hedge, a qualifier, a link, or a claim id, or contained <, > or &; re-propose the language fix only, narrowed to the words around any entity or tag." },
|
|
81
|
+
polish_hedging_decreased: { severity: "major", integrity: false, hint: "The polish pass removed qualifiers; restore them." },
|
|
82
|
+
polish_text_churn: { severity: "major", integrity: false, hint: "More than half the text changed during polish; the pass composed instead of proofreading." },
|
|
83
|
+
snapshot_mismatch: { severity: "major", integrity: false, hint: "The operands come from different collection windows; use rows from one window or state the windows separately." },
|
|
84
|
+
join_integrity_fail: { severity: "major", integrity: false, hint: "A table row combines facts from different source observations; keep each row to one observation or split it." },
|
|
85
|
+
quote_stale_version: { severity: "major", integrity: false, hint: "The quoted source revision differs from the revision the report targets; re-snapshot the target revision or date the quote." },
|
|
86
|
+
quote_fuzzy_match: { severity: "minor", integrity: false, hint: "The source text is OCR-noisy; confirm the snapshot and prefer the raw document." },
|
|
87
|
+
name_outside_quote: { severity: "minor", integrity: false, hint: "A name in the sentence is not in the cited quote; add the source or remove the name." },
|
|
88
|
+
verification_claim_unmarked: { severity: "minor", integrity: false, hint: "The sentence reads as a check we ran but carries no verified mark; mark it {{V:F<n>:label}} on a recorded command-output row, or reword it." },
|
|
89
|
+
scope_widened_warn: { severity: "minor", integrity: false, hint: "The sentence uses a broader scope word than the quote; confirm the scope or narrow it." },
|
|
90
|
+
particle_unverified: { severity: "minor", integrity: false, hint: "The particle after this value could not be checked; read it aloud and fix it by hand if wrong." },
|
|
91
|
+
fallback_without_entity: { severity: "minor", integrity: false, hint: "The replacement quote has no entity for attribution; set one on the row." },
|
|
92
|
+
meaning_drift_warn: { severity: "minor", integrity: false, hint: "A separate reader found the sentence changes scope, subject, or certainty; rewrite from the quote." },
|
|
93
|
+
polish_edit_unlocated: { severity: "minor", integrity: false, hint: "The edit's before-text was not found exactly once; re-propose against the current text." },
|
|
94
|
+
polish_link_changed: { severity: "minor", integrity: false, hint: "A link was altered or removed during polish; restore it." },
|
|
95
|
+
polish_name_changed: { severity: "minor", integrity: false, hint: "A proper noun changed during polish; restore it." },
|
|
42
96
|
})
|
|
43
97
|
|
|
98
|
+
/** Codes only the fidelity verifier raises (fact ledger, fill, verify, meaning, polish). */
|
|
99
|
+
export const FIDELITY_CODES = Object.freeze([
|
|
100
|
+
"unresolved_placeholder", "value_mismatch", "unit_mismatch", "quote_not_verbatim", "claim_status_invalid",
|
|
101
|
+
"incompatible_operands", "pointer_label_mismatch", "quote_anchor_unresolved", "unit_duplicated", "polish_diff_fail",
|
|
102
|
+
"fidelity_interview_incomplete", "derived_not_allowed", "source_outside_floor", "snapshot_hash_mismatch",
|
|
103
|
+
"chart_value_untraceable", "quote_unbound", "edition_unverified", "untraceable_number", "unsupported_verification", "claim_kind_mismatch",
|
|
104
|
+
"conflicting_readings", "hedge_dropped", "direction_flipped", "causality_added", "qualifier_dropped", "unbound_sentence",
|
|
105
|
+
"meaning_drift_confirmed", "meaning_unverified", "polish_edit_rejected", "polish_hedging_decreased", "polish_text_churn", "snapshot_mismatch",
|
|
106
|
+
"join_integrity_fail", "quote_stale_version", "quote_fuzzy_match", "name_outside_quote", "verification_claim_unmarked", "scope_widened_warn",
|
|
107
|
+
"particle_unverified", "fallback_without_entity", "meaning_drift_warn", "polish_edit_unlocated", "polish_link_changed",
|
|
108
|
+
"polish_name_changed",
|
|
109
|
+
])
|
|
110
|
+
const FIDELITY_SET = new Set(FIDELITY_CODES)
|
|
111
|
+
|
|
44
112
|
const LAYOUT_PREFIX = "layout_"
|
|
45
113
|
/** Codes checkStatic can raise (G1-G17). */
|
|
46
114
|
export const STATIC_CODES = Object.freeze(
|
|
47
|
-
Object.keys(DEFECT_CODES).filter(
|
|
115
|
+
Object.keys(DEFECT_CODES).filter(
|
|
116
|
+
(code) => !code.startsWith(LAYOUT_PREFIX) && code !== "missing_promised_deliverable" && !FIDELITY_SET.has(code),
|
|
117
|
+
),
|
|
48
118
|
)
|
|
49
119
|
/** Codes checkLayout can raise (L1-L5). */
|
|
50
120
|
export const LAYOUT_CODES = Object.freeze(Object.keys(DEFECT_CODES).filter((code) => code.startsWith(LAYOUT_PREFIX)))
|
|
@@ -59,10 +129,10 @@ export const LANES = Object.freeze(["template-strict", "template-vibe", "no-form
|
|
|
59
129
|
export const STATES = Object.freeze(["none", "partial", "complete"])
|
|
60
130
|
export const DELIVERABLE_FORMATS = Object.freeze(["pdf", "docx", "html", "md", "latex", "slides", "post"])
|
|
61
131
|
export const DELIVERABLE_STATUSES = Object.freeze(["pending", "delivered", "blocked_capability", "skipped", "failed"])
|
|
62
|
-
export const GATE_NAMES = Object.freeze(["static", "layout", "visual", "proofread"])
|
|
132
|
+
export const GATE_NAMES = Object.freeze(["static", "fidelity", "layout", "visual", "proofread"])
|
|
63
133
|
export const GATE_STATUSES = Object.freeze(["pass", "fail", "not_run"])
|
|
64
134
|
export const LINEAGE_MODES = Object.freeze(["inline", "section"])
|
|
65
|
-
export const ANSWERED_BY = Object.freeze(["user", "default", "request"])
|
|
135
|
+
export const ANSWERED_BY = Object.freeze(["user", "default", "request", "pending"])
|
|
66
136
|
export const REPAIR_DEFAULTS = Object.freeze({ maxAttempts: 3, plateauLimit: 2, wallClockMs: 15 * 60 * 1000 })
|
|
67
137
|
export const DESIGN_SPEC_SECTIONS = Object.freeze([
|
|
68
138
|
"Extracted from",
|
|
@@ -127,6 +197,14 @@ export function validateOutcomeManifest(manifest) {
|
|
|
127
197
|
if (isObject(manifest.gates)) {
|
|
128
198
|
for (const gate of GATE_NAMES) check(errors, GATE_STATUSES.includes(manifest.gates[gate]), `gates.${gate} must be one of ${GATE_STATUSES.join(", ")}`)
|
|
129
199
|
} else errors.push("gates must be an object")
|
|
200
|
+
if (manifest.gateHistory !== undefined) {
|
|
201
|
+
if (!Array.isArray(manifest.gateHistory)) errors.push("gateHistory must be an array")
|
|
202
|
+
else for (const entry of manifest.gateHistory) {
|
|
203
|
+
check(errors, isObject(entry) && GATE_NAMES.includes(entry.gate) && GATE_STATUSES.includes(entry.status)
|
|
204
|
+
&& typeof entry.at === "string" && ISO_DATE.test(entry.at)
|
|
205
|
+
&& (entry.reason === undefined || typeof entry.reason === "string"), "gateHistory entries must have a known gate, status, timestamp, and optional reason")
|
|
206
|
+
}
|
|
207
|
+
}
|
|
130
208
|
if (Array.isArray(manifest.residualDefects)) manifest.residualDefects.forEach((defect, index) => validateDefect(defect, `residualDefects[${index}]`, errors))
|
|
131
209
|
else errors.push("residualDefects must be an array")
|
|
132
210
|
check(errors, manifest.repair === null || isObject(manifest.repair), "repair must be null or an object")
|
|
@@ -134,6 +212,14 @@ export function validateOutcomeManifest(manifest) {
|
|
|
134
212
|
return { ok: errors.length === 0, errors }
|
|
135
213
|
}
|
|
136
214
|
|
|
215
|
+
/** A copy of a manifest written before a gate existed: every absent gate key becomes not_run. */
|
|
216
|
+
export function normalizeManifest(manifest) {
|
|
217
|
+
if (!isObject(manifest) || !isObject(manifest.gates)) return manifest
|
|
218
|
+
const gates = { ...manifest.gates }
|
|
219
|
+
for (const gate of GATE_NAMES) if (!Object.hasOwn(gates, gate)) gates[gate] = "not_run"
|
|
220
|
+
return { ...manifest, gates }
|
|
221
|
+
}
|
|
222
|
+
|
|
137
223
|
function isMeasure(value) {
|
|
138
224
|
return Array.isArray(value) && value.length === 3 && value.every((n) => Number.isInteger(n) && n >= 0)
|
|
139
225
|
}
|