oh-my-opencode 5.1.48 → 5.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/refusal-rule.d.ts +7 -0
- package/dist/cli/index.js +1194 -752
- package/dist/cli/service-auth/accounts-gate.d.ts +11 -0
- package/dist/cli/service-auth/commands.d.ts +2 -4
- package/dist/cli/service-auth/index.d.ts +1 -1
- package/dist/cli/service-auth/keystore.d.ts +26 -2
- package/dist/cli/service-auth/protocol.d.ts +7 -1
- package/dist/cli/service-auth/session.d.ts +6 -0
- package/dist/cli/service-auth/signed-out-marker.d.ts +11 -0
- package/dist/cli-node/index.js +1194 -752
- package/dist/config/schema/hooks.d.ts +1 -0
- package/dist/create-hooks.d.ts +1 -0
- package/dist/features/boulder-state/storage.d.ts +1 -1
- package/dist/features/boulder-state/types.d.ts +1 -1
- package/dist/hooks/atlas/idle-continuation.d.ts +2 -1
- package/dist/hooks/atlas/types.d.ts +9 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/momus-review-persistence/hook.d.ts +19 -0
- package/dist/hooks/momus-review-persistence/index.d.ts +1 -0
- package/dist/hooks/momus-review-persistence/review-resolution.d.ts +20 -0
- package/dist/hooks/prometheus-md-only/constants.d.ts +2 -0
- package/dist/hooks/prometheus-md-only/fixtures/denial-log.d.ts +1 -0
- package/dist/hooks/prometheus-md-only/fixtures/guarded-hashline.d.ts +1 -0
- package/dist/hooks/prometheus-md-only/fixtures/invoke-before.d.ts +7 -0
- package/dist/hooks/prometheus-md-only/fixtures/opencode-patch-parser.d.ts +2 -0
- package/dist/hooks/prometheus-md-only/hook.d.ts +5 -1
- package/dist/hooks/prometheus-md-only/path-policy.d.ts +2 -9
- package/dist/hooks/prometheus-md-only/vendor/opencode-patch-parser.d.ts +22 -0
- package/dist/index.js +2322 -1045
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
- package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +2 -1
- package/dist/shared/posthog-activity-state.d.ts +1 -0
- package/dist/shared/posthog.d.ts +1 -2
- package/dist/shared/resolve-file-write-path.d.ts +6 -0
- package/dist/skills/ast-grep/README.md +1 -1
- package/dist/skills/ast-grep/install.ps1 +5 -3
- package/dist/skills/ast-grep/install.sh +4 -5
- package/dist/skills/ast-grep/references/install.md +2 -2
- package/dist/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
- package/dist/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/dist/skills/coding-agent-sessions/SKILL.md +11 -11
- package/dist/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/dist/skills/coding-agent-sessions/references/codex.md +4 -4
- package/dist/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/dist/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/dist/skills/frontend/ATTRIBUTION.md +1 -1
- package/dist/skills/frontend/SKILL.md +34 -12
- package/dist/skills/frontend/references/design/README.md +2 -2
- package/dist/skills/frontend/references/design/_INDEX.md +2 -1
- package/dist/skills/frontend/references/design/component-catalogs.md +14 -10
- package/dist/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/dist/skills/frontend/references/design/grounding.md +29 -0
- package/dist/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/dist/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/dist/skills/ultimate-browsing/SKILL.md +5 -4
- package/dist/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/dist/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/dist/skills/ultimate-browsing/engine/summary.py +1 -1
- package/dist/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/dist/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/dist/skills/ulw-research/AGENTS.md +8 -4
- package/dist/skills/ulw-research/SKILL.md +7 -316
- package/dist/skills/ulw-research/references/deliverable-phase.md +23 -21
- package/dist/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/dist/skills/ulw-research/references/fact-ledger.md +42 -8
- package/dist/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/dist/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/dist/skills/ulw-research/references/report-gates.md +12 -5
- package/dist/skills/ulw-research/references/research-collection.md +195 -0
- package/dist/skills/ulw-research/references/research-delivery.md +123 -0
- package/dist/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/dist/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
- package/dist/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
- package/dist/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
- package/dist/skills/ulw-research/scripts/contracts.mjs +13 -4
- package/dist/skills/ulw-research/scripts/contracts.test.ts +6 -110
- package/dist/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
- package/dist/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
- package/dist/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
- package/dist/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/dist/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
- package/dist/skills/ulw-research/scripts/copy-returns-polish.test.ts +4 -3
- package/dist/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/dist/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/dist/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
- package/dist/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
- package/dist/skills/ulw-research/scripts/entities.mjs +13 -4
- package/dist/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
- package/dist/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
- package/dist/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
- package/dist/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
- package/dist/skills/ulw-research/scripts/fact-commands.mjs +35 -16
- package/dist/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
- package/dist/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/dist/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
- package/dist/skills/ulw-research/scripts/fact-fill.mjs +6 -4
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
- package/dist/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
- package/dist/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/dist/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
- package/dist/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
- package/dist/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
- package/dist/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/dist/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/dist/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/dist/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/dist/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
- package/dist/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/dist/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/dist/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/dist/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/dist/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/dist/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/dist/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
- package/dist/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/dist/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
- package/dist/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/dist/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/dist/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/dist/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/dist/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
- package/dist/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/dist/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
- package/dist/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/dist/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/dist/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
- package/dist/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/dist/skills/ulw-research/scripts/numerals.test.ts +3 -0
- package/dist/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/dist/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
- package/dist/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/dist/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
- package/dist/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/dist/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/dist/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
- package/dist/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
- package/dist/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/dist/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/dist/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
- package/dist/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
- package/dist/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
- package/dist/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
- package/dist/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
- package/dist/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/dist/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/dist/skills/ulw-research/scripts/report-tools.test.ts +5 -800
- package/dist/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
- package/dist/skills/ulw-research/scripts/source-commands.mjs +79 -26
- package/dist/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
- package/dist/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/dist/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/dist/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
- package/dist/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/dist/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/dist/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
- package/dist/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
- package/dist/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
- package/dist/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
- package/dist/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
- package/dist/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
- package/dist/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
- package/dist/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
- package/dist/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
- package/dist/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
- package/dist/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
- package/dist/skills/ulw-research/scripts/units.mjs +14 -3
- package/dist/skills/ulw-research/scripts/units.test.ts +10 -0
- package/dist/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
- package/dist/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
- package/dist/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
- package/dist/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
- package/dist/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
- package/dist/tools/delegate-task/executor-types.d.ts +1 -0
- package/dist/tools/delegate-task/sync-session-existence.d.ts +4 -0
- package/dist/tools/delegate-task/types.d.ts +1 -0
- package/dist/tools/skill/description-formatter.d.ts +2 -1
- package/dist/tools/skill/types.d.ts +6 -0
- package/dist/tui.js +131 -33
- package/package.json +18 -15
- package/packages/git-bash-mcp/package.json +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/README.md +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/SKILL.md +7 -7
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +2 -2
- package/packages/omo-codex/plugin/components/telemetry/README.md +2 -2
- package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +52 -15
- package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +52 -19
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/src/codex-hook.ts +2 -9
- package/packages/omo-codex/plugin/components/telemetry/src/posthog.ts +4 -13
- package/packages/omo-codex/plugin/components/telemetry/src/product-identity.ts +0 -2
- package/packages/omo-codex/plugin/components/telemetry/test/codex-hook-diagnostics.test.ts +0 -3
- package/packages/omo-codex/plugin/components/telemetry/test/codex-hook.test.ts +3 -8
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +3 -0
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +16 -16
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/shared/package.json +1 -1
- package/packages/omo-codex/plugin/skills/ast-grep/README.md +1 -1
- package/packages/omo-codex/plugin/skills/ast-grep/install.ps1 +5 -3
- package/packages/omo-codex/plugin/skills/ast-grep/install.sh +4 -5
- package/packages/omo-codex/plugin/skills/ast-grep/references/install.md +2 -2
- package/packages/omo-codex/plugin/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +11 -11
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/codex.md +4 -4
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +1 -1
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +34 -12
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +2 -1
- package/packages/omo-codex/plugin/skills/frontend/references/design/component-catalogs.md +14 -10
- package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/packages/omo-codex/plugin/skills/frontend/references/design/grounding.md +29 -0
- package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +7 -7
- package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +5 -4
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/summary.py +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +3 -0
- package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +8 -4
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +7 -316
- package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +23 -21
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +42 -8
- package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +12 -5
- package/packages/omo-codex/plugin/skills/ulw-research/references/research-collection.md +195 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/research-delivery.md +123 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +13 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/entities.mjs +13 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +35 -16
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +6 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +79 -26
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +14 -3
- package/packages/omo-codex/plugin/test/payload-equivalence.test.mjs +6 -4
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -3
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +338 -237
- package/packages/prompts-core/prompts/ultrawork/codex.md +3 -0
- package/packages/shared-skills/skill-source-filter.mjs +6 -2
- package/packages/shared-skills/skills/ast-grep/README.md +1 -1
- package/packages/shared-skills/skills/ast-grep/install.ps1 +5 -3
- package/packages/shared-skills/skills/ast-grep/install.sh +4 -5
- package/packages/shared-skills/skills/ast-grep/references/install.md +2 -2
- package/packages/shared-skills/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/shared-skills/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +11 -11
- package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/packages/shared-skills/skills/coding-agent-sessions/references/codex.md +4 -4
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +1 -1
- package/packages/shared-skills/skills/frontend/SKILL.md +34 -12
- package/packages/shared-skills/skills/frontend/references/design/README.md +2 -2
- package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +2 -1
- package/packages/shared-skills/skills/frontend/references/design/component-catalogs.md +14 -10
- package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/packages/shared-skills/skills/frontend/references/design/grounding.md +29 -0
- package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +5 -4
- package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/packages/shared-skills/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/packages/shared-skills/skills/ultimate-browsing/engine/summary.py +1 -1
- package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/packages/shared-skills/skills/ulw-research/AGENTS.md +8 -4
- package/packages/shared-skills/skills/ulw-research/SKILL.md +7 -316
- package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +23 -21
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +42 -8
- package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/packages/shared-skills/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/packages/shared-skills/skills/ulw-research/references/report-gates.md +12 -5
- package/packages/shared-skills/skills/ulw-research/references/research-collection.md +195 -0
- package/packages/shared-skills/skills/ulw-research/references/research-delivery.md +123 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
- package/packages/shared-skills/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
- package/packages/shared-skills/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +13 -4
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +6 -110
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
- package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-polish.test.ts +4 -3
- package/packages/shared-skills/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/packages/shared-skills/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
- package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
- package/packages/shared-skills/skills/ulw-research/scripts/entities.mjs +13 -4
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +35 -16
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
- package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +6 -4
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
- package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/packages/shared-skills/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
- package/packages/shared-skills/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +3 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/packages/shared-skills/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
- package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
- package/packages/shared-skills/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +5 -800
- package/packages/shared-skills/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +79 -26
- package/packages/shared-skills/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
- package/packages/shared-skills/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
- package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +14 -3
- package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +10 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/amp_redirect_stub.html +0 -7
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/search_interstitial.html +0 -19
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_available.json +0 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_snapshot.html +0 -1128
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_fetch_chain.py +0 -77
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +0 -86
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +0 -279
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate.py +0 -252
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate_validators.py +0 -78
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
## Phase 3 — Verify contested claims by running code
|
|
2
|
+
|
|
3
|
+
Settle with executed code, not judgment, whenever sources disagree, a behavior is undocumented, a claim is performance- or compatibility-shaped, or the honest answer is "it should work". Spawn one verification worker per claim:
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
task(category="deep-low", run_in_background=true, prompt="TASK: verify by execution: <claim>.
|
|
7
|
+
SOURCE: <where it came from>; CONTRADICTION: <opposing source, if any>.
|
|
8
|
+
Write a minimal self-contained script that tests the claim; run it (uv run --with <deps> python / bun / direct compile); capture full stdout+stderr; pin versions.
|
|
9
|
+
Reply with: the exact code, the full output, environment (OS, runtime, dependency versions), and a verdict — CONFIRMED / REFUTED / PARTIAL — grounded in the output.")
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Journal each verdict to `verify-<slug>.md`.
|
|
13
|
+
|
|
14
|
+
## Phase 3b — Lock non-code claims through the claim graph
|
|
15
|
+
|
|
16
|
+
Code settles code-shaped claims (Phase 3). Numeric, market-share, legal, dated, causal, and financial claims cannot be run — so they pass through a data-flow-lock instead (the verification idea adapted from fivetaku/insane-research): the synthesis may assert a high-risk non-code claim **only** if it cleared this gate, and the gate's output is the sole allowlist the synthesis draws from. Skip the gate and there is nothing to synthesize — the lock is self-enforcing.
|
|
17
|
+
|
|
18
|
+
The claim graph is orchestrator-owned. Workers only return verified-claim markers, observation candidates, and claim candidates as message text, the same channel as EXPAND markers — never a file (except a mass-research dag node's handoff file). As leads resolve, you record one node per asserted claim in `claim-graph.md` and compute its status; workers report claim candidates in their replies, and you decide. The graph is the single claim store: final synthesis may not draw from free-form claims that skipped it.
|
|
19
|
+
|
|
20
|
+
A high-risk claim clears the gate to `verified-claims` only when all hold:
|
|
21
|
+
|
|
22
|
+
- **>= 2 independent source domains** corroborate it (two pages on the same domain count once).
|
|
23
|
+
- **>= 2 independent observation groups** converge on it, unless the graph records why a primary-only source is the correct single-source exception.
|
|
24
|
+
- **One counter-search** actively looked for a refutation and did not find a stronger one.
|
|
25
|
+
- **A primary source** (the standard, filing, dataset, or first-party doc) backs it, not only secondary commentary.
|
|
26
|
+
- **Temporal evidence is explicit**: each supporting observation records `observed_at` and either `valid_at` or `claim_valid_at`, so branch-only, historical, release, and current-runtime claims cannot be conflated.
|
|
27
|
+
|
|
28
|
+
Anything that fails goes to an `Unresolved` (insufficient evidence) or `Refuted` (counter-search won) annex — abstention is a correct outcome, not a gap to paper over. Record each gate outcome on the claim node itself — risk tier, independent source domains, counter-search result, primary source backing, and status — and mirror the cleared nodes into the `verified-claims` digest section at the top of `claim-graph.md`. Worker reply marker (message text, same channel as EXPAND):
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
## CLAIMS
|
|
32
|
+
- CLAIM: <non-code assertion> — RISK: high|normal — SOURCES: <domain1, domain2> — COUNTER: <refutation search result> — PRIMARY: <primary source or none>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
**The claim table is machine-read.** Keep the nodes of `claim-graph.md` in one Markdown table whose header names `claim_id | statement | risk tier | status` (plus `fact_ids`, the ledger rows the node rests on, and any other node fields as further columns), one row per node: ids `C<n>`, risk `high` or `normal`, status `supported`, `partial`, `refuted`, or `unresolved`. List the nodes that clear the Phase 3b gate under a `## verified-claims` heading as `- C<n>: <statement>`. `node "$SKILL_DIR/scripts/report-tools.mjs" claims index "$SESSION_DIR/claim-graph.md" --out "$SESSION_DIR/claims.json"` indexes it, and the fidelity gate reads `claims.json` to check every `data-claim` binding in the report.
|
|
36
|
+
|
|
37
|
+
## Phase 4 — Synthesize
|
|
38
|
+
|
|
39
|
+
After convergence and all verifications, re-read the whole journal, start from `intent-diff.md`, `claim-graph.md`, and `observation-manifest.md`, then write `SYNTHESIS.md`:
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
# ULW-Research Synthesis: <query>
|
|
43
|
+
Workers: <total> · Waves: <count> · Excursions: <count> · Sources: <count> (<unique domains> domains) · Verifications: <count> · Elapsed: <minutes> min
|
|
44
|
+
|
|
45
|
+
## Executive summary — 2-3 paragraphs answering the core question
|
|
46
|
+
## Findings by theme — per theme: consensus, evidence links, key quote (<20 words, attributed), verified yes/no
|
|
47
|
+
## Codebase findings — absolute paths with line references
|
|
48
|
+
## Sources (ranked) — URL, what it contains, reliability, access date
|
|
49
|
+
## Verified claims — code: claim | verdict | verify-<slug>.md · non-code: only rows cleared into verified-claims
|
|
50
|
+
## Epistemic instrumentation — intent-vs-reality diff closure, claim graph coverage, observation manifest coverage, independent-observation convergence, verification economics summary, cause-disappearance records
|
|
51
|
+
## Contradictions — source A vs source B, resolution with evidence
|
|
52
|
+
## Gaps — what saturation could not answer · unresolved/refuted claim-graph nodes
|
|
53
|
+
## Expansion trace — per wave: workers → markers; convergence reason
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`SYNTHESIS.md` is the citation source of truth for final materials: every claim carries inline `[Source N]` citations, and every high-risk non-code claim you assert must be a verified-claims row from Phase 3b. Assert nothing the gate left in the unresolved/refuted annex.
|
|
57
|
+
|
|
58
|
+
**Write numbers and quotes by reference.** `SYNTHESIS.md` and every report source carry placeholders, never typed values: `{{F12:span_len}}` renders the row's value and unit, `{{F12:span_len:value}}` or `:unit` one part, `:q` the value with its source qualifier (up to, at least, about); `{{Q7:completion}}` renders the whole quotation, `{{Q7:completion:lead=8}}` its first words, `{{Q7:completion:from="no earlier":to="2027"}}` one span of it. A derived number is computed by the script, never by hand: `fact derive --op sum|diff|growth|ratio|share|convert|expr ...` checks the operands' units, periods, and populations, refuses an incompatible operation, and prints the new row id the placeholder uses. A value nobody measured is registered with `fact add --assumed <value> --unit <unit> --rationale <why> --label <label>` and renders as ASSUMED. Every findings sentence binds to a fact, a quote, a `[S<n>]` citation, or a claim node (`data-claim="C3"`). Reviewers and the refinement team read the rendered text through `node "$SKILL_DIR/scripts/report-tools.mjs" fact fill "$SESSION_DIR/SYNTHESIS.md" --view --session-dir "$SESSION_DIR"`, never the raw placeholders.
|
|
59
|
+
|
|
60
|
+
**Write the skeleton early and fill it as claims lock.** The moment the brief records the deliverable, create the deliverable file with its section headings and a `STATUS: draft — <n> sections open` line as line 2 (an HTML comment in HTML); that line is the partial-state marker `outcome state` reads, removed only when the deliverable is complete. An interrupted run must leave a partial report on disk, never an empty directory and a lost conversation.
|
|
61
|
+
|
|
62
|
+
**Keep sourced numbers, assumptions, and derived results visibly apart.** Every quantitative claim carries its lineage: `MEASURED` (a number a source states, cited), `ASSUMED` (a coefficient, distribution, or scope you chose — say why), `DERIVED` (computed from those, showing the formula), plus a sensitivity line whenever the assumption moves the answer. Presenting a derived estimate with the confidence of a measured one is the most damaging thing this mode can ship.
|
|
63
|
+
|
|
64
|
+
**Search in English, deliver in the user's language.** Retrieval stays English-first (Search craft), but the synthesis and every final material are written in the language the user wrote to you in unless they ask otherwise — and a translated report still quotes its original-language sources verbatim.
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## Search craft
|
|
68
|
+
|
|
69
|
+
English first: run every search in English by default — it is the largest, most authoritative corpus on every engine, GitHub, and documentation site. Add a secondary local-language sweep (1-2 librarians) only after the English sweep, when the topic is inherently local, or when the user asks for sources in a specific language.
|
|
70
|
+
|
|
71
|
+
Vary operators on every query — same query twice wastes a worker:
|
|
72
|
+
|
|
73
|
+
| Operator | Example | Use |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| `site:` | `site:github.com <topic>` | Restrict to a domain |
|
|
76
|
+
| `filetype:` | `filetype:pdf <topic> survey` | Papers, specs |
|
|
77
|
+
| `intitle:` / `inurl:` | `intitle:benchmark <topic>` | Targeted pages |
|
|
78
|
+
| `"exact"` / `-term` | `"<exact phrase>" -tutorial` | Precision, exclusion |
|
|
79
|
+
| `OR` | `<a> OR <b> <topic>` | Coverage |
|
|
80
|
+
| `before:` / `after:` | `<topic> after:2025-06-01` | Recency control |
|
|
81
|
+
|
|
82
|
+
High-yield combinations: official docs (`site:<docs domain>`), GitHub implementations (`site:github.com`), recent discussion (`site:reddit.com OR site:news.ycombinator.com after:<date>`), academic (`site:arxiv.org OR filetype:pdf survey`), changelog hunting (`changelog OR "release notes" <version>`), alternatives (`vs OR alternative OR comparison`).
|
|
83
|
+
|
|
84
|
+
## Failure modes
|
|
85
|
+
|
|
86
|
+
| Failure | Correction |
|
|
87
|
+
|---|---|
|
|
88
|
+
| Sequential spawning, or trimming the first wave | All first-wave workers in one turn, background, scaling floor respected |
|
|
89
|
+
| A team member hoards leads for one final dump | Raise law — every lead, finding, and dead end broadcast the moment it surfaces |
|
|
90
|
+
| Worker reply without the EXPAND tail | One follow-up demanding it; the lane stays open until it lands |
|
|
91
|
+
| Stopping after wave 1 because "enough was found" | Convergence rules only: 2+ expansion waves, leads run dry |
|
|
92
|
+
| Obeying a surrounding "stop exploring" rule mid-research | Authority section — those rules do not bind this mode |
|
|
93
|
+
| Asking a worker to write journal or session files | Workers are read-only (except a mass-research dag node's handoff file); you journal every return |
|
|
94
|
+
| Two workers given the same angle | One unique angle per worker, always |
|
|
95
|
+
| A web worker that returned only fetched text, or a `Collection: browser` run whose workers never opened a tab | Sources are read in the user's browser through the `browser` skill; `webfetch` is for raw files; every web worker carries the skill from the first wave |
|
|
96
|
+
| Contested claim settled by judgment | Phase 3 — run code, capture output, verdict |
|
|
97
|
+
| Deliverable claims without citations | Every claim cites a source or a verification artifact |
|
|
98
|
+
| Guessing the deliverable format | Derive it from the request and destination; ask the empty-info interview only for what is still missing, and record `answered_by` for every field |
|
|
99
|
+
| Blocking the collection wave on the format question | Ask non-blocking and start collecting; unanswered fields run on the shown defaults |
|
|
100
|
+
| A roster smaller than the harness ceiling | Fill every member slot; split the broadest axis until the team is full |
|
|
101
|
+
| One tier across the whole roster | Mixed tiers by design — cheap breadth, premium attack |
|
|
102
|
+
| Silently re-routing a "quick"/"fast" instruction | Routing words are literal; journal requested -> spawned -> fallback per slot |
|
|
103
|
+
| A worker or member that starts its own research swarm | Members research one axis and report; orchestration is yours alone |
|
|
104
|
+
| Expanding on an entity the first wave never disambiguated | Settle canonical identity and first-party source before any later spawn asserts it |
|
|
105
|
+
| Batching findings into an end-of-run journal dump | Journal each return as it lands; the journal is what survives a compaction |
|
|
106
|
+
| Ending the run because every worker finished | Reserve the final fifth of the run for synthesis and materials |
|
|
107
|
+
| A derived estimate presented as a measured number | MEASURED / ASSUMED / DERIVED lineage on every quantitative claim, plus a sensitivity line |
|
|
108
|
+
| A number typed into the report or `SYNTHESIS.md` | Register it (`fact add`) and write `{{F<n>:label}}`; `fact verify` reports a typed number as `untraceable_number`, and `outcome verify` refuses delivery until it is a ledger row, an `assumed` row with its rationale, or gone |
|
|
109
|
+
| A filled value edited by hand | Never edit a filled value: fix the ledger row or the placeholder and re-run `fact fill`, then `fact verify` (`value_mismatch`, `unit_mismatch`) |
|
|
110
|
+
| A findings sentence bound to nothing | Bind it to a fact, a quote, a `[S<n>]` citation, or a claim node with `data-claim`; an `unbound_sentence` blocks delivery like an untraceable number |
|
|
111
|
+
| Delivering before the delivery gates pass | Static and layout gates, visual QA on rendered pages, plus the harness's proofread gate — a typo the user finds means a gate did not run |
|
|
112
|
+
| Skipping the static gates | `report-tools check` runs before any pixel review; its defects are cheaper than a visual pass |
|
|
113
|
+
| Delivering with a pending manifest row | `outcome verify` must pass; every promised format ends `delivered`, `blocked_capability`, `skipped`, or `failed` with a reason |
|
|
114
|
+
| Repairing past the tracker's stop decision | `repair decide` owns the budget; `deliver` ships with the residual list, `block` stops delivery |
|
|
115
|
+
| Guessing a design-spec value the extractor marked `TODO: ask` | Fill it from the interview answers or the reference's defaults, never by guessing |
|
|
116
|
+
| Referencing an asset that is not on disk | Verify the asset manifest before rendering; re-render whatever is missing |
|
|
117
|
+
| A figure stretched, cropped, or styled off the report's design language | `design-spec.md` binds every asset: fixed containers, contain-fit with aspect preserved, spec fonts and palette in charts and Mermaid |
|
|
118
|
+
| Chasing an interesting find with no ENTER trigger | Excursions need a named trigger; everything else stays a queued lead |
|
|
119
|
+
| An excursion that never came back, or drifted into a new mission | EXIT rules are unconditional; depth 3 means promote it to an axis or record it as a gap |
|
|
120
|
+
| An excursion whose result was never folded back | Every EXIT writes what it changed in the top-level answer, `none` included, and mirrors into the loop ledger |
|
|
121
|
+
| Ending with file paths while an inline HTML page tool is available | Show the report inline first (the gated `report.html` or its inline edition), then the paths and the summary |
|
|
122
|
+
| A visual reproduction checked only against itself | Reference compare: real source frames next to renders at the same moments, a critique per pair, fixes, and the pairs in the report |
|
|
123
|
+
| Delivering without the closing briefing | Paste the block `outcome briefing` prints; never compute or recall its numbers by hand |
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// emits it as the data-claim-kind attribute on the element it renders, and the fidelity gate
|
|
5
5
|
// (fidelity-claims.mjs) checks that every marked element cites a row the kind accepts. An
|
|
6
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`:
|
|
7
|
+
// The one lexical piece is `unmarked`: first-person run and check patterns that raise a minor warning
|
|
8
8
|
// when a sentence reads like the kind but carries no mark; they never raise a major. The table in
|
|
9
9
|
// references/fidelity-claim-kinds.md is rendered from CLAIM_KINDS (`node claim-kinds.mjs --render-doc`).
|
|
10
10
|
// Pure; node: builtins only. Korean phrases are \u escapes.
|
|
@@ -23,6 +23,32 @@ const KO_WE_RUN = "(?:\uC2E4\uD589(?:\uD588|\uD558\uC5EC|\uD574)|\uB3CC[\uB838\u
|
|
|
23
23
|
const KO_RUN = "(?:\uC2E4\uD589|\uB3CC[\uB9AC\uB824\uB838\uB9B0\uB9B4])"
|
|
24
24
|
const KO_SUBJECT = "[^\\s,.;!?]*(?:[\uAC00\uC774\uC740\uB294]|\uAED8\uC11C)(?=\\s)"
|
|
25
25
|
|
|
26
|
+
// One cue list; the rule text below is built from it. A check verb fires only in first person with
|
|
27
|
+
// a result object (a result noun or a digit) in its clause, and never when that clause names a document.
|
|
28
|
+
// The document guard ends where the clause ends (a comma, a semicolon, "as", a relative pronoun; in Korean a
|
|
29
|
+
// comma, a semicolon, cheoreom, deusi, gati), and a document word followed by a number is a locator ("page 3").
|
|
30
|
+
const EN_RUN = ["ran", "run", "re-ran", "re-run", "executed", "verified by"]
|
|
31
|
+
const EN_CHECK = ["verified", "checked", "confirmed", "measured", "tested", "double-checked", "cross-checked"]
|
|
32
|
+
const EN_RESULT = ["value", "values", "result", "results", "figure", "figures", "number", "numbers", "total", "totals", "count", "counts", "measurement", "measurements", "reading", "readings", "output", "outputs", "span", "length", "rate", "rates", "latency", "throughput", "score", "scores", "benchmark", "benchmarks", "metric", "metrics", "data", "estimate", "estimates", "speed", "duration", "size", "ratio", "growth"]
|
|
33
|
+
const EN_DOCUMENT = ["report", "reports", "filing", "filings", "document", "documents", "paper", "papers", "article", "articles", "publication", "statement", "disclosure", "website", "page", "docs", "documentation"]
|
|
34
|
+
// KO_CHECK = hwagin, geomjeung, cheukjeong, jeomgeom (+ haet/hayeot/hayeo/hae/han); KO_RESULT = gyeolgwa, gap, suchi, gil-i, hapgye,
|
|
35
|
+
// biyul, sokdo, jipyo, deiteo; KO_DOCUMENT = bogoseo, munseo, gongsi, nonmun, gisa, jaryo.
|
|
36
|
+
const KO_CHECK = ["\uD655\uC778", "\uAC80\uC99D", "\uCE21\uC815", "\uC810\uAC80"]
|
|
37
|
+
const KO_DONE = ["\uD588", "\uD558\uC600", "\uD558\uC5EC", "\uD574", "\uD55C"]
|
|
38
|
+
const KO_RESULT = ["\uACB0\uACFC", "\uAC12", "\uC218\uCE58", "\uAE38\uC774", "\uD569\uACC4", "\uBE44\uC728", "\uC18D\uB3C4", "\uC9C0\uD45C", "\uB370\uC774\uD130"]
|
|
39
|
+
const KO_DOCUMENT = ["\uBCF4\uACE0\uC11C", "\uBB38\uC11C", "\uACF5\uC2DC", "\uB17C\uBB38", "\uAE30\uC0AC", "\uC790\uB8CC"]
|
|
40
|
+
const any = (words) => words.map((word) => word.replace(/-/g, "-?").replace(/ /g, "\\s+")).join("|")
|
|
41
|
+
const EN_WE = "(?:\\b[Ww]e|\\bI)(?:'ve|'d)?\\s+(?:(?:have|had|also|then|first|just|again|independently|manually|directly)\\s+)*"
|
|
42
|
+
const CLAUSE = "(?:[^.;!?]|\\.\\d)*?"
|
|
43
|
+
const EN_BOUNDARY = ["as", "which", "who", "whom", "whose", "where"]
|
|
44
|
+
const KO_BOUNDARY = ["\uCC98\uB7FC", "\uB4EF\uC774", "\uAC19\uC774"]
|
|
45
|
+
const EN_GUARD = `(?:(?!,|\\b(?:${any(EN_BOUNDARY)})\\b)[^.;!?]|\\.\\d)*?`
|
|
46
|
+
const KO_GUARD = `(?:(?!,|${any(KO_BOUNDARY)})[^.;!?]|\\.\\d)*?`
|
|
47
|
+
const EN_OBJECT = `(?=${CLAUSE}(?:\\b(?:${any(EN_RESULT)})\\b|\\d))(?!${EN_GUARD}\\b(?:${any(EN_DOCUMENT)})\\b(?!\\s*\\d))`
|
|
48
|
+
const KO_OBJECT = `(?=${CLAUSE}(?:${any(KO_RESULT)}|\\d))(?!${KO_GUARD}(?:${any(KO_DOCUMENT)})(?!\\s*\\d))`
|
|
49
|
+
const escapes = (text) => text.replace(/[^\x00-\x7f]/g, (c) => `\\u${c.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0")}`)
|
|
50
|
+
const list = (words) => escapes(words.join(", "))
|
|
51
|
+
|
|
26
52
|
/**
|
|
27
53
|
* kind -> { prefix: placeholder prefix (null = the default form), placeholder: an example,
|
|
28
54
|
* rowKinds: ledger row kinds it accepts, sourceKinds: meta.json kinds the snapshot holding the
|
|
@@ -50,11 +76,13 @@ export const CLAIM_KINDS = Object.freeze({
|
|
|
50
76
|
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
77
|
unmarked: {
|
|
52
78
|
code: "verification_claim_unmarked",
|
|
53
|
-
rule:
|
|
79
|
+
rule: `a first-person run: we/I (have) ${list(EN_RUN)} (not ran out of), or by running after we/our in the sentence; a first-person check: we/I (have) ${list(EN_CHECK)} (not checked in/into/out) with a result object in the clause (${list(EN_RESULT)}, or a digit) and no document named before the clause ends (${list(EN_DOCUMENT)}; a comma, a semicolon, or ${list(EN_BOUNDARY)} ends it, and a document word followed by a number is a locator); \\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, or then ${list(KO_CHECK)} + ${list(KO_DONE)} with a result object in the clause (${list(KO_RESULT)}, or a digit) and no document before the clause ends (${list(KO_DOCUMENT)}; a comma, a semicolon, or ${list(KO_BOUNDARY)} ends it, and a document word followed by a number is a locator); \\uC9C1\\uC811 with no third-party subject, then \\uC2E4\\uD589 or \\uB3CC\\uB9AC in the clause; a curly apostrophe reads as a straight one`,
|
|
54
80
|
patterns: [
|
|
55
|
-
|
|
56
|
-
|
|
81
|
+
new RegExp(`${EN_WE}(?:${any(EN_RUN)})\\b(?!\\s+out\\s+of\\b)`),
|
|
82
|
+
new RegExp(`${EN_WE}(?:${any(EN_CHECK)})\\b(?!\\s+(?:in|into|out)\\b)${EN_OBJECT}`),
|
|
83
|
+
/\b(?:[Ww]e|[Oo]ur)\b.*?\bby\s+running\b/,
|
|
57
84
|
new RegExp(`${KO_WE_SUBJECT}(?:(?!${KO_SUBJECT})[^\\s,.;!?]+\\s+)*?${KO_WE_RUN}`),
|
|
85
|
+
new RegExp(`${KO_WE_SUBJECT}${KO_OBJECT}(?:(?!${KO_SUBJECT})[^\\s,.;!?]+\\s+)*?(?:${any(KO_CHECK)})(?:${any(KO_DONE)})`),
|
|
58
86
|
new RegExp(`(?:^|[,.;!?])\\s*(?:(?!${KO_SUBJECT})[^\\s,.;!?]+\\s+)*\uC9C1\uC811\\s*(?:[^\\s,.;!?]+\\s*)??${KO_RUN}`),
|
|
59
87
|
],
|
|
60
88
|
},
|
|
@@ -86,7 +114,7 @@ export function unmarkedRequires(row) {
|
|
|
86
114
|
/** The kinds whose unmarked patterns match the sentence. */
|
|
87
115
|
export function unmarkedKinds(text) {
|
|
88
116
|
return Object.entries(CLAIM_KINDS)
|
|
89
|
-
.filter(([, entry]) => entry.unmarked?.patterns.some((re) => re.test(text)))
|
|
117
|
+
.filter(([, entry]) => entry.unmarked?.patterns.some((re) => re.test(String(text).replace(/\u2019/g, "'"))))
|
|
90
118
|
.map(([name]) => name)
|
|
91
119
|
}
|
|
92
120
|
|
|
@@ -31,7 +31,7 @@ export const DEFECT_CODES = deepFreeze({
|
|
|
31
31
|
en_dash_in_prose: { severity: "minor", integrity: false, hint: "Use a hyphen for compounds; keep the en dash only between numbers." },
|
|
32
32
|
heading_too_long: { severity: "minor", integrity: false, hint: "Shorten the heading to one line (26 Korean characters or 12 English words)." },
|
|
33
33
|
heading_generic: { severity: "minor", integrity: false, hint: "Make the heading carry the section's claim instead of a generic label." },
|
|
34
|
-
unsourced_number: { severity: "major", integrity: false, hint: "Tag the number MEASURED / ASSUMED / DERIVED or cite its source in the same paragraph." },
|
|
34
|
+
unsourced_number: { severity: "major", integrity: false, hint: "Tag the number MEASURED / ASSUMED / DERIVED or cite its source in the same paragraph; under Sources only citation-field numbers and names (a hash, #12, GPT-4, Q3, 10-Q) are exempt, so quote a reference title that carries a code or a figure, or wrap it in <cite>: an unquoted one is checked, and 10-K / 8-K stay numerals because K is a multiplier. A slashed N/M reads as a month/day, so a fraction or a score (2/3, 7/12) under Sources is not checked (#11386): write it as a percent or tag it." },
|
|
35
35
|
chart_without_figure: { severity: "major", integrity: false, hint: "Wrap the image or chart in a figure container with a caption." },
|
|
36
36
|
chart_svg_no_text: { severity: "major", integrity: false, hint: "Give the chart a title, axis labels, units, and value labels as SVG text." },
|
|
37
37
|
chart_svg_missing_labels: { severity: "minor", integrity: false, hint: "Add the missing title, axis ticks, or unit label to the chart." },
|
|
@@ -58,12 +58,14 @@ export const DEFECT_CODES = deepFreeze({
|
|
|
58
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
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
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
|
+
conversion_altered_number: { severity: "blocker", integrity: true, hint: "The source conversion altered this number (sources/S<n>/conversion.json lists it); snapshot a verified text of the source over it with `source snapshot --id S<n> --text <verified.txt> --replace` (refused while an active row cites S<n>; then retract those rows or use a new id), then re-add the fact." },
|
|
61
62
|
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
63
|
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
64
|
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
65
|
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
66
|
brief_changed: { severity: "blocker", integrity: true, hint: "The edition's fill map was not written under the current ## Fidelity block (it changed after fill, or the map predates the binding); re-run fact fill, then fact verify." },
|
|
66
|
-
snapshot_hash_mismatch: { severity: "blocker", integrity: true, hint: "The source snapshot changed after it was pinned;
|
|
67
|
+
snapshot_hash_mismatch: { severity: "blocker", integrity: true, hint: "The source snapshot changed after it was pinned or cannot be read; restore it, or run source refresh to re-anchor its rows." },
|
|
68
|
+
snapshot_not_raw: { severity: "blocker", integrity: true, hint: "Measured rows need the original bytes: snapshot the source with --url, --html, or --raw <bytes> --text <extraction>; converted-only sources support warned quotations only." },
|
|
67
69
|
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." },
|
|
68
70
|
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." },
|
|
69
71
|
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." },
|
|
@@ -82,13 +84,18 @@ export const DEFECT_CODES = deepFreeze({
|
|
|
82
84
|
polish_hedging_decreased: { severity: "major", integrity: false, hint: "The polish pass removed qualifiers; restore them." },
|
|
83
85
|
polish_text_churn: { severity: "major", integrity: false, hint: "More than half the text changed during polish; the pass composed instead of proofreading." },
|
|
84
86
|
snapshot_mismatch: { severity: "major", integrity: false, hint: "The operands come from different collection windows; use rows from one window or state the windows separately." },
|
|
87
|
+
operand_timestamp_missing: { severity: "major", integrity: false, hint: "Record the operand's observed_at timestamp before checking its collection window." },
|
|
88
|
+
operand_missing: { severity: "major", integrity: false, hint: "A windowed operation names an operand that is not in the fact ledger; restore the row or re-derive from existing rows." },
|
|
85
89
|
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." },
|
|
86
90
|
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." },
|
|
87
91
|
quote_fuzzy_match: { severity: "minor", integrity: false, hint: "The source text is OCR-noisy; confirm the snapshot and prefer the raw document." },
|
|
92
|
+
relocated: { severity: "minor", integrity: false, hint: "After source refresh the exact quote and context sit at one new byte range; review the old and new offsets (verification never rewrites the row)." },
|
|
93
|
+
quote_converted_only: { severity: "minor", integrity: false, hint: "The quote comes from a converted-only source with no raw bytes; re-snapshot the original with --raw and --text, or keep the warned quote." },
|
|
88
94
|
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." },
|
|
89
95
|
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." },
|
|
90
96
|
scope_widened_warn: { severity: "minor", integrity: false, hint: "The sentence uses a broader scope word than the quote; confirm the scope or narrow it." },
|
|
91
97
|
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." },
|
|
98
|
+
unit_unbound: { severity: "minor", integrity: false, hint: "Use the full fact placeholder, or write the unit right after :value (with or without a space; a Korean particle may follow), as its :unit element, or in the matching table column header." },
|
|
92
99
|
fallback_without_entity: { severity: "minor", integrity: false, hint: "The replacement quote has no entity for attribution; set one on the row." },
|
|
93
100
|
meaning_drift_warn: { severity: "minor", integrity: false, hint: "A separate reader found the sentence changes scope, subject, or certainty; rewrite from the quote." },
|
|
94
101
|
polish_edit_unlocated: { severity: "minor", integrity: false, hint: "The edit's before-text was not found exactly once; re-propose against the current text." },
|
|
@@ -98,15 +105,17 @@ export const DEFECT_CODES = deepFreeze({
|
|
|
98
105
|
|
|
99
106
|
/** Codes only the fidelity verifier raises (fact ledger, fill, verify, meaning, polish). */
|
|
100
107
|
export const FIDELITY_CODES = Object.freeze([
|
|
108
|
+
"unit_unbound",
|
|
101
109
|
"unresolved_placeholder", "value_mismatch", "unit_mismatch", "quote_not_verbatim", "claim_status_invalid",
|
|
102
110
|
"incompatible_operands", "pointer_label_mismatch", "quote_anchor_unresolved", "unit_duplicated", "polish_diff_fail",
|
|
103
111
|
"fidelity_interview_incomplete", "derived_not_allowed", "source_outside_floor", "brief_changed", "snapshot_hash_mismatch",
|
|
112
|
+
"conversion_altered_number",
|
|
104
113
|
"chart_value_untraceable", "quote_unbound", "edition_unverified", "untraceable_number", "unsupported_verification", "claim_kind_mismatch",
|
|
105
114
|
"conflicting_readings", "hedge_dropped", "direction_flipped", "causality_added", "qualifier_dropped", "unbound_sentence",
|
|
106
115
|
"meaning_drift_confirmed", "meaning_unverified", "polish_edit_rejected", "polish_hedging_decreased", "polish_text_churn", "snapshot_mismatch",
|
|
107
|
-
"join_integrity_fail", "quote_stale_version", "quote_fuzzy_match", "name_outside_quote", "verification_claim_unmarked", "scope_widened_warn",
|
|
116
|
+
"operand_timestamp_missing", "operand_missing", "join_integrity_fail", "quote_stale_version", "quote_fuzzy_match", "name_outside_quote", "verification_claim_unmarked", "scope_widened_warn",
|
|
108
117
|
"particle_unverified", "fallback_without_entity", "meaning_drift_warn", "polish_edit_unlocated", "polish_link_changed",
|
|
109
|
-
"polish_name_changed",
|
|
118
|
+
"polish_name_changed", "snapshot_not_raw", "relocated", "quote_converted_only",
|
|
110
119
|
])
|
|
111
120
|
const FIDELITY_SET = new Set(FIDELITY_CODES)
|
|
112
121
|
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// conversion-probe.mjs - numeric multiset differences after source conversion.
|
|
2
|
+
// Inline markup may split one number; superscripts must not merge into it. Numbers are read with the
|
|
3
|
+
// one grouped-number grammar (number-grammar.mjs). A cell, row, list-item, block or line break is a
|
|
4
|
+
// hard boundary: a space never groups digits across it. A sign counts when no Latin, Greek or
|
|
5
|
+
// Cyrillic letter, digit or underscore touches it (an en dash before a digit is a minus too), and a
|
|
6
|
+
// "-" or U+2212 counts after ONE such letter ("\u0394\u22125") or after an exponent ("1e-5"); a run of
|
|
7
|
+
// two or more ("cite_note-7", "AB-12", "UTF-8", "2019-2023") is an identifier or range and carries no
|
|
8
|
+
// negative. Markdown markup a converter writes beside a sign never decides it: the closer run before it
|
|
9
|
+
// (read with the CommonMark inline grammar in markdown-closers.mjs: emphasis, code, strike, titled and
|
|
10
|
+
// parenthesised link and image targets, reference endings, backslash-escaped closers) is skipped to
|
|
11
|
+
// the first character it closes ("**2019**-2023" and '[1990](/wiki/1990 "1990")-2000' are ranges), and
|
|
12
|
+
// an opener run between the sign and its digits ("-**5**") keeps the sign. A raw-side character
|
|
13
|
+
// reference (`−`, ` `) is decoded on BOTH sides with the one entities.mjs table, so a
|
|
14
|
+
// verbatim copy probes clean. Raw inline HTML a converter leaves in the Markdown (pandoc's gfm writer
|
|
15
|
+
// emits `<sup>`, `<sub>`, `<img ... width=320>`, `<span>`, `<br>`) is read with the html-lite tag
|
|
16
|
+
// reader: sup, sub and break tags are boundaries, as on the raw side, and any other tag or comment is
|
|
17
|
+
// dropped with its attributes, so it is a closer or opener like Markdown markup. Digits a converter
|
|
18
|
+
// writes out from a link target, an image source, alt text, or a title cover a converted number only
|
|
19
|
+
// inside Markdown link or image syntax (`](...)` with any title and balanced parentheses,
|
|
20
|
+
// ``, `<https://...>`, `[ref]: url "title"` with the title on the same or the next line),
|
|
21
|
+
// never in prose; a footnote
|
|
22
|
+
// definition's text ("[^1]: ...") is prose. Converter syntax is not a number: a line-start list marker
|
|
23
|
+
// ("1. ", "2) ", "(3) ", indented or after "> ") is exempt only while the source has an <ol> item with
|
|
24
|
+
// that number and the markers so far do not outnumber the <ol> items (so lazy "1. 1. 1." numbering
|
|
25
|
+
// over three items passes), a numeric reference label ("[x][1]" with its "[1]: url") only while both
|
|
26
|
+
// halves are present, and a footnote definition label ("[^1]:") only while its "[^1]" is used;
|
|
27
|
+
// otherwise each is a candidate. A converter's hard wrap may split a space-grouped number over one line
|
|
28
|
+
// break; the two halves read as one number only when the raw side holds that number and not both
|
|
29
|
+
// halves, so raw "12 345 and 12 and 345" wrapped inside "12 345" reports both halves (it fails safe).
|
|
30
|
+
// The built-in snapshot writes no attribute text, syntax, wraps or tags, and its text is already
|
|
31
|
+
// decoded, so it gets none of these allowances. Constructs outside this grammar are declared out of
|
|
32
|
+
// scope in references/fact-ledger.md: they report as altered (the safe direction).
|
|
33
|
+
|
|
34
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
35
|
+
import { join } from "node:path"
|
|
36
|
+
|
|
37
|
+
import { CliError } from "./cli-support.mjs"
|
|
38
|
+
import { decodeEntities, parseHtml, PROSE_BREAKS, readTag } from "./html-lite.mjs"
|
|
39
|
+
import { escapedAt, inlineLinkSpans, stripClosers } from "./markdown-closers.mjs"
|
|
40
|
+
import { NUMBER_SOURCE, plainDecimal } from "./number-grammar.mjs"
|
|
41
|
+
|
|
42
|
+
const LETTER = "\\p{Script=Latin}\\p{Script=Greek}\\p{Script=Cyrillic}"
|
|
43
|
+
const WORD = new RegExp(`[${LETTER}\\p{N}_]`, "u")
|
|
44
|
+
const ALPHA = new RegExp(`[${LETTER}]`, "u")
|
|
45
|
+
const EXPONENT = new RegExp(`(?<![${LETTER}\\p{N}_.,])\\d+(?:[.,]\\d+)?[eE]$`, "u")
|
|
46
|
+
/** A dash, then any Markdown opener run, then a number; signed() decides whether the dash is a minus. */
|
|
47
|
+
const NUMBERS = new RegExp(`(?:([-\\u2212\\u2013])([*_~\`[]*))?(${NUMBER_SOURCE})`, "gu")
|
|
48
|
+
/** Sup, sub, datetime chunks and every break join no neighbour: neither a digit, a separator, nor whitespace. */
|
|
49
|
+
const BOUNDARY = "\u0001"
|
|
50
|
+
const CARRIED = ["href", "src", "alt", "title"]
|
|
51
|
+
const LIST_MARKER = /(?:^|\u0001)(?: ?>)* ?(?:\d+[.)]|\(\d+\)) /gu
|
|
52
|
+
const REF_USE = /\]\[(\d+)\]/gu
|
|
53
|
+
const REF_DEF = /(?:^|\u0001)(?: ?>)* ?\[(\d+)\]:/gu
|
|
54
|
+
const NOTE_USE = /\[\^(\d+)\](?!:)/gu
|
|
55
|
+
const NOTE_DEF = /(?:^|\u0001)(?: ?>)* ?\[\^(\d+)\]:/gu
|
|
56
|
+
const TITLE = `(?:"[^"\u0001]*"|'[^'\u0001]*'|\\([^)\u0001]*\\))`
|
|
57
|
+
/** A reference definition's title may sit alone on the next line (only whitespace after it). */
|
|
58
|
+
const REF_DEFINITION = `(?:^|\u0001) ?\\[(?!\\^)[^\\]\u0001]+\\]: ?[^\\s\u0001]*(?: ${TITLE}| ?\u0001 ?${TITLE}(?= ?(?:\u0001|$)))?`
|
|
59
|
+
const LINK_SYNTAX = new RegExp(`!\\[[^\\]\u0001]*\\](?:\\([^)\u0001]*\\))?|\\]\\([^)\u0001]*\\)|<https?:\\/\\/[^>\\s\u0001]+>|${REF_DEFINITION}`, "gu")
|
|
60
|
+
const TAG_NAME = /^[a-z][a-z0-9-]*$/u
|
|
61
|
+
/** What a tag leaves in the numeric text on either side: sup, sub and every break join no neighbour. */
|
|
62
|
+
const edgeOf = (tag) => (tag === "sup" || tag === "sub" || PROSE_BREAKS.has(tag) ? BOUNDARY : "")
|
|
63
|
+
|
|
64
|
+
/** Prose text, attribute text and <ol> item numbers of an HTML document; a non-HTML document is prose only. */
|
|
65
|
+
function numericText(source, html) {
|
|
66
|
+
if (!html) return { prose: decodeEntities(source), carried: "", listed: [] }
|
|
67
|
+
const chunks = []
|
|
68
|
+
const carried = []
|
|
69
|
+
const listed = []
|
|
70
|
+
const walk = (node) => {
|
|
71
|
+
if (node.tag === "#text") { chunks.push(node.text.replace(/\s+/gu, " ")); return }
|
|
72
|
+
for (const name of CARRIED) if (node.attrs?.[name] !== undefined) carried.push(node.attrs[name])
|
|
73
|
+
if (node.tag === "ol") {
|
|
74
|
+
const start = /^\s*\d+\s*$/u.test(node.attrs.start ?? "") ? Number(node.attrs.start) : 1
|
|
75
|
+
node.children.filter((child) => child.tag === "li").forEach((_, index) => listed.push(String(start + index)))
|
|
76
|
+
}
|
|
77
|
+
const edge = edgeOf(node.tag)
|
|
78
|
+
chunks.push(edge)
|
|
79
|
+
if (node.tag === "time" && node.attrs.datetime !== undefined) chunks.push(`${BOUNDARY}${node.attrs.datetime}${BOUNDARY}`)
|
|
80
|
+
for (const child of node.children) walk(child)
|
|
81
|
+
chunks.push(edge)
|
|
82
|
+
}
|
|
83
|
+
walk(parseHtml(source))
|
|
84
|
+
return { prose: chunks.join(""), carried: carried.join(BOUNDARY), listed }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Raw inline HTML in converted Markdown: sup, sub and break tags become boundaries, other tags and comments go. */
|
|
88
|
+
function inlineHtml(text) {
|
|
89
|
+
let out = ""
|
|
90
|
+
let kept = 0
|
|
91
|
+
for (let lt = text.indexOf("<"); lt !== -1; lt = text.indexOf("<", Math.max(lt + 1, kept))) {
|
|
92
|
+
const comment = text.startsWith("<!--", lt)
|
|
93
|
+
const name = lt + (text[lt + 1] === "/" ? 2 : 1)
|
|
94
|
+
if (escapedAt(text, lt) || (!comment && !/[a-z]/iu.test(text[name] ?? ""))) continue
|
|
95
|
+
const tag = comment ? null : readTag(text, name)
|
|
96
|
+
const end = comment ? text.indexOf("-->", lt + 4) + 3 : tag.end
|
|
97
|
+
if (end <= lt || (tag && !(TAG_NAME.test(tag.name) && text[end - 1] === ">")) || /\n[ \t\r]*\n/u.test(text.slice(lt, end))) continue
|
|
98
|
+
out += text.slice(kept, lt) + (tag ? edgeOf(tag.name) : "")
|
|
99
|
+
kept = end
|
|
100
|
+
}
|
|
101
|
+
return out + text.slice(kept)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Start offsets of numeric reference labels whose use ("][1]") and definition ("[1]:") are both present,
|
|
106
|
+
* and of footnote definition labels ("[^1]:") whose "[^1]" is used (the use itself mirrors the source).
|
|
107
|
+
*/
|
|
108
|
+
function referenceLabels(flat) {
|
|
109
|
+
const uses = [...flat.matchAll(REF_USE)]
|
|
110
|
+
const defs = [...flat.matchAll(REF_DEF)]
|
|
111
|
+
const used = new Set(uses.map((match) => match[1]))
|
|
112
|
+
const defined = new Set(defs.map((match) => match[1]))
|
|
113
|
+
const paired = [...uses.filter((match) => defined.has(match[1])), ...defs.filter((match) => used.has(match[1]))]
|
|
114
|
+
const noted = new Set([...flat.matchAll(NOTE_USE)].map((match) => match[1]))
|
|
115
|
+
const notes = [...flat.matchAll(NOTE_DEF)].filter((match) => noted.has(match[1]))
|
|
116
|
+
return new Set([...paired, ...notes].map((match) => match.index + match[0].search(/\d/u)))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Whether the dash at `at` is a minus: Markdown closers (and a backslash escaping the dash) before it are skipped, as is an opener run before a lone letter. */
|
|
120
|
+
function signed(flat, at, dash) {
|
|
121
|
+
const before = stripClosers(flat.slice(Math.max(0, at - 512), escapedAt(flat, at) ? at - 1 : at))
|
|
122
|
+
const last = before.at(-1) ?? ""
|
|
123
|
+
if (!WORD.test(last)) return true
|
|
124
|
+
if (dash === "\u2013") return false
|
|
125
|
+
if (EXPONENT.test(before)) return true
|
|
126
|
+
return ALPHA.test(last) && !WORD.test(before.slice(0, -1).replace(/[*_~`[]+$/u, "").at(-1) ?? "")
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Line breaks become boundaries, then whitespace runs one space; `linked` marks tokens inside link
|
|
131
|
+
* syntax, `marker` a line-start list marker, `label` a paired reference label, `gap` the text since
|
|
132
|
+
* the previous number.
|
|
133
|
+
*/
|
|
134
|
+
function tokensOf(text, { links = false } = {}) {
|
|
135
|
+
const flat = text.replace(/\r\n?|\n|\f/gu, BOUNDARY).replace(/\s+/gu, " ")
|
|
136
|
+
const spans = links ? [...[...flat.matchAll(LINK_SYNTAX)].map((match) => [match.index, match.index + match[0].length]), ...inlineLinkSpans(flat)] : []
|
|
137
|
+
const markers = new Set([...flat.matchAll(LIST_MARKER)].map((match) => match.index + match[0].search(/\d/u)))
|
|
138
|
+
const labels = referenceLabels(flat)
|
|
139
|
+
let previousEnd = 0
|
|
140
|
+
return [...flat.matchAll(NUMBERS)].map((match, n) => {
|
|
141
|
+
const sign = match[1] !== undefined && signed(flat, match.index, match[1])
|
|
142
|
+
const at = sign ? match.index : match.index + match[0].length - match[3].length
|
|
143
|
+
const gap = flat.slice(previousEnd, at)
|
|
144
|
+
previousEnd = match.index + match[0].length
|
|
145
|
+
return {
|
|
146
|
+
n,
|
|
147
|
+
gap,
|
|
148
|
+
text: sign ? match[0] : match[3],
|
|
149
|
+
key: `${sign ? "-" : ""}${plainDecimal(match[3])}`,
|
|
150
|
+
linked: spans.some(([start, end]) => at >= start && at < end),
|
|
151
|
+
marker: markers.has(at),
|
|
152
|
+
label: labels.has(at),
|
|
153
|
+
}
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** The comparison key of one number token, so other tools can match a converted number against a probe record. */
|
|
158
|
+
export const numberKey = (token) => tokensOf(String(token))[0]?.key ?? null
|
|
159
|
+
|
|
160
|
+
/** The numbers in `texts` that the source's conversion probe recorded as altered (sources/<id>/conversion.json). */
|
|
161
|
+
export function alteredIn(sourceDir, texts) {
|
|
162
|
+
const record = join(sourceDir, "conversion.json")
|
|
163
|
+
let altered = []
|
|
164
|
+
if (existsSync(record)) {
|
|
165
|
+
try {
|
|
166
|
+
altered = JSON.parse(readFileSync(record, "utf8")).conversion_altered_numbers ?? []
|
|
167
|
+
} catch (error) {
|
|
168
|
+
throw new CliError(`${record} is not a readable conversion record (${error.message}); snapshot the source again`)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const keys = new Set(altered.map(numberKey))
|
|
172
|
+
return texts.flatMap((text) => tokensOf(String(text ?? ""))).filter((token) => keys.has(token.key)).map((token) => token.text)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Rejoin a grouped number a hard wrap split over one line break, when only the joined reading is in the raw counts. */
|
|
176
|
+
function joinWrapped(tokens, raw) {
|
|
177
|
+
const out = []
|
|
178
|
+
for (const token of tokens) {
|
|
179
|
+
const prev = out.at(-1)
|
|
180
|
+
const wrapped = prev?.n === token.n - 1 && /^ ?\u0001 ?$/u.test(token.gap) && !prev.linked && !token.linked
|
|
181
|
+
const [joined, ...more] = wrapped ? tokensOf(`${prev.text} ${token.text}`) : []
|
|
182
|
+
if (joined && more.length === 0 && raw.has(joined.key) && !(raw.has(prev.key) && raw.has(token.key))) {
|
|
183
|
+
out[out.length - 1] = { ...joined, n: token.n, text: `${prev.text}\n${token.text}` }
|
|
184
|
+
continue
|
|
185
|
+
}
|
|
186
|
+
out.push(token)
|
|
187
|
+
}
|
|
188
|
+
return out
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const countOf = (tokens) => tokens.reduce((counts, token) => counts.set(token.key, (counts.get(token.key) ?? 0) + 1), new Map())
|
|
192
|
+
|
|
193
|
+
/** The tokens `counts` cannot cover; each covered token uses up one count. */
|
|
194
|
+
function uncovered(tokens, counts) {
|
|
195
|
+
return tokens.filter((token) => {
|
|
196
|
+
const remaining = counts.get(token.key) ?? 0
|
|
197
|
+
if (remaining === 0) return true
|
|
198
|
+
counts.set(token.key, remaining - 1)
|
|
199
|
+
return false
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Preserve token spelling in diagnostics; the raw side is read as HTML, the converted side as Markdown
|
|
205
|
+
* with raw inline tags, both decoded with one entity table. `attributes: false`
|
|
206
|
+
* (the built-in snapshot) gives attribute digits no cover, converter syntax no exemption and no wrap rejoin. An exempt
|
|
207
|
+
* marker or label is not a converted number at all: it neither covers a raw number nor counts as altered.
|
|
208
|
+
*/
|
|
209
|
+
export function probeConversion(raw, converted, { attributes = true } = {}) {
|
|
210
|
+
const source = numericText(raw, /<[a-z!/]/i.test(raw))
|
|
211
|
+
const before = tokensOf(source.prose)
|
|
212
|
+
const listed = new Set(source.listed)
|
|
213
|
+
let items = attributes ? source.listed.length : 0
|
|
214
|
+
const syntax = tokensOf(attributes ? decodeEntities(inlineHtml(converted)) : converted, { links: attributes })
|
|
215
|
+
.filter((token) => !(attributes && token.label) && !(token.marker && listed.has(token.key) && items-- > 0))
|
|
216
|
+
const after = attributes ? joinWrapped(syntax, countOf(before)) : syntax
|
|
217
|
+
const counts = countOf(before)
|
|
218
|
+
const extra = new Set(uncovered(after.filter((token) => !token.linked), counts))
|
|
219
|
+
for (const token of tokensOf(source.carried)) counts.set(token.key, (counts.get(token.key) ?? 0) + 1)
|
|
220
|
+
for (const token of uncovered(after.filter((token) => token.linked), counts)) extra.add(token)
|
|
221
|
+
const lost = uncovered(before, countOf(after)).map((token) => token.text)
|
|
222
|
+
return { altered: after.filter((token) => extra.has(token)).map((token) => token.text), lost, raw_count: before.length, converted_count: after.length }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function runConversionProbe(argv) {
|
|
226
|
+
if (argv.length !== 2) throw new CliError("usage: source probe-conversion <raw-file> <converted-file>")
|
|
227
|
+
const result = probeConversion(readFileSync(argv[0], "utf8"), readFileSync(argv[1], "utf8"))
|
|
228
|
+
return { json: result, summary: `source probe-conversion: ${result.altered.length} altered, ${result.lost.length} lost`, exitCode: result.altered.length === 0 ? 0 : 1 }
|
|
229
|
+
}
|