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
|
@@ -3,7 +3,7 @@ import { leaves, parseExpr } from "./fact-expr.mjs"
|
|
|
3
3
|
import { readRow } from "./fact-ledger-kinds.mjs"
|
|
4
4
|
import { addRow, nextId, validateLedger } from "./fact-ledger.mjs"
|
|
5
5
|
import { recompute, verifyGraph } from "./fact-recompute.mjs"
|
|
6
|
-
import { canonicalUnit, inputScale, refuse } from "./unit-algebra.mjs"
|
|
6
|
+
import { canonicalUnit, inputScale, refuse, RuleError } from "./unit-algebra.mjs"
|
|
7
7
|
import { refusal } from "./unit-errors.mjs"
|
|
8
8
|
import { object, sameDeclared } from "./unit-metadata.mjs"
|
|
9
9
|
import { display, renderValue, resultPrecision, toText } from "./unit-precision.mjs"
|
|
@@ -29,7 +29,11 @@ function declaredUnit(spec, rule) {
|
|
|
29
29
|
return canonicalUnit(declared)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/** Refused before any work, under its own code: time is the caller's (spec.now), never the clock. */
|
|
33
|
+
const MISSING_NOW = Object.freeze({ error: "missing_now", shape: "missing_now", expected: "an ISO timestamp in spec.now", detail: "derive needs a caller-supplied now timestamp" })
|
|
34
|
+
|
|
32
35
|
export function derive(ledger, spec) {
|
|
36
|
+
if (object(spec) && typeof spec.now !== "string") return { ...MISSING_NOW }
|
|
33
37
|
try {
|
|
34
38
|
if (!object(spec)) refuse("spec must be an object")
|
|
35
39
|
const errors = validateLedger(ledger)
|
|
@@ -54,13 +58,14 @@ export function derive(ledger, spec) {
|
|
|
54
58
|
const result = compute(spec.op, rows, expected, ast, context, spec.formula)
|
|
55
59
|
const row = addRow(ledger, {
|
|
56
60
|
kind: rule.kind, unit: expected, quote: "", extracted_by: "fact-derive",
|
|
57
|
-
observed_at: spec.now
|
|
61
|
+
observed_at: spec.now,
|
|
58
62
|
label: spec.label ?? `${spec.op}-${nextId(ledger, rule.kind).toLowerCase()}`,
|
|
59
63
|
...result, operation: spec.op, operands: ids, derived_from: ids,
|
|
60
64
|
rationale: spec.rationale ?? null, context,
|
|
61
65
|
})
|
|
62
66
|
return { row, compatibility: rule.compatibility }
|
|
63
67
|
} catch (error) {
|
|
68
|
+
if (!(error instanceof RuleError)) throw error
|
|
64
69
|
return { error: "incompatible_operands", ...refusal(error) }
|
|
65
70
|
}
|
|
66
71
|
}
|
|
@@ -9,6 +9,7 @@ import { finalReading, rewriteParticle } from "./particles-ko.mjs"
|
|
|
9
9
|
import { failureCount, markReplaced, sentenceKey } from "./fidelity-state.mjs"
|
|
10
10
|
import { findPlaceholders, lead, qualifiedText, renderBlockquote, renderQuote, renderValueHtml, subSpan, valueParts } from "./placeholder.mjs"
|
|
11
11
|
import { splitSentences } from "./sentence-split.mjs"
|
|
12
|
+
import { CLAIM_KINDS } from "./claim-kinds.mjs"
|
|
12
13
|
|
|
13
14
|
/** The output formats fill renders. */
|
|
14
15
|
export const FILL_FORMATS = Object.freeze(["html", "md"])
|
|
@@ -24,7 +25,7 @@ function resolveValue(row, parsed, format) {
|
|
|
24
25
|
const { valueText, unitText } = valueParts(row)
|
|
25
26
|
const text = { full: row.raw, value: valueText, unit: unitText, q: qualifiedText(row) }[parsed.mode]
|
|
26
27
|
if (text === null) return { code: "quote_anchor_unresolved", message: `${row.fact_id} has qualifier ${row.qualifier}, but its quote holds no qualifier beside "${row.raw}"` }
|
|
27
|
-
return { row, parsed, visible: text, rendered: format === "html" ? renderValueHtml(row, text) : text }
|
|
28
|
+
return { row, parsed, visible: text, rendered: format === "html" ? renderValueHtml(row, text, { mode: parsed.mode, claimKind: parsed.claimKind ?? null }) : text }
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
function resolveQuote(row, parsed, format) {
|
|
@@ -33,17 +34,18 @@ function resolveQuote(row, parsed, format) {
|
|
|
33
34
|
if (selection === null) {
|
|
34
35
|
return { code: "quote_anchor_unresolved", message: `from "${parsed.from}" and to "${parsed.to}" must each occur exactly once in ${row.fact_id}'s quote, in that order` }
|
|
35
36
|
}
|
|
36
|
-
const rendered = renderQuote(row, parsed.selector, selection.text, format)
|
|
37
|
+
const rendered = renderQuote(row, parsed.selector, selection.text, format, parsed.claimKind)
|
|
37
38
|
return { row, parsed, visible: `[${row.source_id}]`, rendered, selector: parsed.selector, quote_span: [selection.start, selection.end] }
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
function resolve(found, rows, format) {
|
|
41
42
|
const { parsed } = found
|
|
42
|
-
if (parsed.error) return { code: "unresolved_placeholder", message: parsed.error }
|
|
43
|
+
if (parsed.error) return { code: "unresolved_placeholder", message: `${parsed.error}; for literal opening braces, write {{ instead of {{ (including in code spans)` }
|
|
43
44
|
const row = rows.get(parsed.id)
|
|
44
45
|
if (row === undefined) return { code: "unresolved_placeholder", message: `${parsed.id} is not in the fact ledger` }
|
|
45
46
|
if (row.status !== "active") return { code: "unresolved_placeholder", message: `${parsed.id} is ${row.status}; cite its replacement` }
|
|
46
47
|
if (row.label !== parsed.label) return { code: "pointer_label_mismatch", message: `${parsed.id} has label "${row.label}", not "${parsed.label}"` }
|
|
48
|
+
if (parsed.claimKind && !CLAIM_KINDS[parsed.claimKind].rowKinds.includes(row.kind)) return { code: CLAIM_KINDS[parsed.claimKind].defect, message: `${parsed.claimKind} does not accept a ${row.kind} row (${parsed.id})` }
|
|
47
49
|
return parsed.kind === "Q" ? resolveQuote(row, parsed, format) : resolveValue(row, parsed, format)
|
|
48
50
|
}
|
|
49
51
|
|
|
@@ -146,7 +148,7 @@ function assemble(text, parts, context) {
|
|
|
146
148
|
flush(part.start)
|
|
147
149
|
const start = context.out.length
|
|
148
150
|
if (part.parsed) {
|
|
149
|
-
const extra = part.selector === undefined ? {} : { selector: part.selector, quote_span: part.quote_span }
|
|
151
|
+
const extra = { ...(part.selector === undefined ? {} : { selector: part.selector, quote_span: part.quote_span }), ...(part.parsed.claimKind ? { claimKind: part.parsed.claimKind } : {}), ...(part.parsed.mode === "value" ? { mode: "value" } : {}) }
|
|
150
152
|
context.fillMap.push({ token: part.token, fact_id: part.row.fact_id, start, end: start + part.rendered.length, rendered: part.rendered, ...extra })
|
|
151
153
|
} else if (part.rendered !== "") {
|
|
152
154
|
context.fillMap.push({ token: null, fact_id: part.source.fact_id, replaced: part.key, start, end: start + part.rendered.length, rendered: part.rendered })
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// per-kind choices (id prefix, measured exact) from it, and the per-kind table in
|
|
4
4
|
// references/fact-ledger-grammar.md is rendered from it (`node fact-ledger-kinds.mjs --render-grammar`).
|
|
5
5
|
// Also the value primitives the schema shares. Pure functions with no IO; node: builtins only.
|
|
6
|
-
|
|
7
6
|
import { isCliEntry } from "./entry-guard.mjs"
|
|
8
7
|
import { canonicalUnit, inputScale, RuleError } from "./unit-algebra.mjs"
|
|
9
8
|
import { metadataErrors } from "./unit-metadata.mjs"
|
|
@@ -15,7 +14,6 @@ export const DECIMAL = /^-?\d+(?:\.\d+)?$/
|
|
|
15
14
|
const INTEGER = /^-?\d+$/
|
|
16
15
|
const SHA256 = /^[0-9a-f]{64}$/
|
|
17
16
|
const SOURCE_ID = /^S\d+$/
|
|
18
|
-
|
|
19
17
|
export const isObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value)
|
|
20
18
|
export const isString = (value) => typeof value === "string"
|
|
21
19
|
export const isNonEmpty = (value) => typeof value === "string" && value.length > 0
|
|
@@ -48,6 +46,7 @@ export function locatorErrors(locator, at) {
|
|
|
48
46
|
const errors = []
|
|
49
47
|
if (!isObject(locator)) return errors
|
|
50
48
|
if (!isNonEmpty(locator.artifact)) errors.push(`${at}.artifact must be a non-empty string`)
|
|
49
|
+
if (locator.unit !== undefined && locator.unit !== "bytes") errors.push(`${at}.unit must be bytes`)
|
|
51
50
|
if (!(isCount(locator.char_start) && isCount(locator.char_end) && locator.char_start <= locator.char_end)) errors.push(`${at}.char_start/char_end must be integers with start <= end`)
|
|
52
51
|
for (const key of ["page", "line"]) {
|
|
53
52
|
if (locator[key] !== undefined && !(Number.isInteger(locator[key]) && locator[key] >= 1)) errors.push(`${at}.${key} must be an integer >= 1`)
|
|
@@ -87,6 +86,7 @@ export const RULES = Object.freeze({
|
|
|
87
86
|
rational_or_null: { cell: "rational or null", says: "null or { num: integer string, den: positive integer string }", test: orNull(isRational) },
|
|
88
87
|
by_derive: { cell: "reduced rational", says: "a reduced { num: integer string, den: positive integer string }", test: isRational },
|
|
89
88
|
context_by_derive: { cell: "written by derive (object or null)", says: "null or an object", test: orNull(isObject) },
|
|
89
|
+
source_context: { cell: "quote context or null (legacy)", says: "null or { prefix, suffix } up to 32 characters each", test: orNull((v) => isObject(v) && Object.keys(v).sort().join(",") === "prefix,suffix" && [v.prefix, v.suffix].every((s) => isString(s) && [...s].length <= 32)) },
|
|
90
90
|
derived_operation: { cell: "sum/diff/growth/share/ratio/expr", says: "a derived operation", test: (v) => Object.hasOwn(OPERATION_RULES, v) && OPERATION_RULES[v].kind === "derived" },
|
|
91
91
|
convert_operation: { cell: "`convert`", says: "convert", test: (v) => Object.hasOwn(OPERATION_RULES, v) && OPERATION_RULES[v].kind === "converted" },
|
|
92
92
|
precision: { cell: "`{ places, sigfigs }`", says: "nonnegative places and positive sigfigs", test: (v) => isObject(v) && Object.keys(v).sort().join(",") === "places,sigfigs" && Number.isSafeInteger(v.places) && v.places >= 0 && Number.isSafeInteger(v.sigfigs) && v.sigfigs >= 1 },
|
|
@@ -101,14 +101,13 @@ const unsourced = { source_id: "source_or_null", locator: "locator_or_null", sna
|
|
|
101
101
|
const row = (cells) => Object.freeze(Object.fromEntries(KIND_COLUMNS.map((column) => [column, cells[column] ?? (["precision", "compatibility"].includes(column) && cells.operation !== "null" ? column : "null")])))
|
|
102
102
|
|
|
103
103
|
export const KIND_RULES = Object.freeze({
|
|
104
|
-
measured: row({ fact_id: "f_id", value: "decimal", ...sourced, rationale: "string_or_null", exact: "from_value", context: "
|
|
105
|
-
quote: row({ fact_id: "q_id", value: "null", ...sourced, rationale: "string_or_null", exact: "null", context: "
|
|
104
|
+
measured: row({ fact_id: "f_id", value: "decimal", ...sourced, rationale: "string_or_null", exact: "from_value", context: "source_context", operation: "null" }),
|
|
105
|
+
quote: row({ fact_id: "q_id", value: "null", ...sourced, rationale: "string_or_null", exact: "null", context: "source_context", operation: "null" }),
|
|
106
106
|
derived: row({ fact_id: "f_id", value: "decimal", ...unsourced, rationale: "string_or_null", exact: "by_derive", context: "context_by_derive", operation: "derived_operation" }),
|
|
107
107
|
assumed: row({ fact_id: "f_id", value: "decimal", ...unsourced, rationale: "non_empty", exact: "rational_or_null", context: "null", operation: "null" }),
|
|
108
108
|
converted: row({ fact_id: "f_id", value: "decimal", ...unsourced, rationale: "string_or_null", exact: "by_derive", context: "context_by_derive", operation: "convert_operation" }),
|
|
109
109
|
})
|
|
110
110
|
|
|
111
|
-
/** The id prefix nextId assigns for a kind; an unknown kind gets F and fails validation later. */
|
|
112
111
|
export function idPrefix(kind) {
|
|
113
112
|
return RULES[KIND_RULES[kind]?.fact_id ?? "f_id"].prefix
|
|
114
113
|
}
|
|
@@ -169,7 +168,7 @@ const carriesDerivation = (row) => row.operation !== null || row.operands?.lengt
|
|
|
169
168
|
/** Dimension 32: a stored exact must equal the row's own value at the value's decimal places. */
|
|
170
169
|
function valueExactDisagreement(row) {
|
|
171
170
|
const exact = readExact(row.exact)
|
|
172
|
-
const value = readDecimal(row.value)
|
|
171
|
+
const value = isString(row.value) ? readDecimal(row.value).replace(/^-(0+(?:\.0+)?)$/, "$1") : row.value
|
|
173
172
|
if (!isRational(exact) || !isDecimal(value) || roundPlaces(toBig(exact), placesOf(value)) === value) return null
|
|
174
173
|
return `value ${value} disagrees with exact ${exact.num}/${exact.den} at ${placesOf(value)} decimal places`
|
|
175
174
|
}
|
|
@@ -212,7 +211,7 @@ export function kindRuleErrors(candidate, at) {
|
|
|
212
211
|
const value = candidate[column] === undefined && LATER_COLUMNS.includes(column) ? null : candidate[column]
|
|
213
212
|
const rule = RULES[legacy && column === "exact" ? "rational_or_null" : rules[column]]
|
|
214
213
|
// An exact with unknown keys loads as written (never stripped); readFlaws refuses it at use and at write.
|
|
215
|
-
if (column === "exact" && hasUnknownExactKeys(value) && !isCurrentDerived(candidate)) continue
|
|
214
|
+
if (column === "exact" && rules[column] !== "null" && hasUnknownExactKeys(value) && !isCurrentDerived(candidate)) continue
|
|
216
215
|
if (rule.test(value, candidate)) continue
|
|
217
216
|
errors.push(`${at}.${column} must be ${rule.says} for a ${candidate.kind} row`)
|
|
218
217
|
if (rule.detail) errors.push(...rule.detail(value, `${at}.${column}`))
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
// fact-locate.mjs - deterministic fact locators over a source snapshot.
|
|
1
|
+
// fact-locate.mjs - deterministic fact locators over a source snapshot's locator text.
|
|
2
2
|
//
|
|
3
3
|
// Each locator returns { value, unit, raw, quote, value_span, sentence_id, qualifier, locator } or
|
|
4
4
|
// { error: "fact_not_located" | "fact_ambiguous", candidates }; it never guesses. `quote` is the
|
|
5
|
-
// whole containing sentence, cell, or line of text.
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// whole containing sentence, cell, or line of the view text (source-view.mjs: the visible text
|
|
6
|
+
// nodes of a page, never markup), `value_span` is [start, end] inside `quote` with
|
|
7
|
+
// quote.slice(...value_span) === raw, and locator.char_start/char_end place `quote` in that text;
|
|
8
|
+
// raw-locator.mjs turns them into bytes. Every call takes `sourceId` (S<n>) so sentence ids match
|
|
9
|
+
// sentences.jsonl, which indexes the same text.
|
|
8
10
|
// Plain Node ESM, sibling imports only; no network, no OCR.
|
|
9
11
|
|
|
10
12
|
import { proseText } from "./html-lite.mjs"
|
|
@@ -101,7 +103,8 @@ export function locateFind(text, { find, unitExpected, sourceId }) {
|
|
|
101
103
|
return result.unit === expected ? result : { error: "unit_expected_mismatch", expected, found: result.unit, candidates: [result] }
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
|
|
106
|
+
/** A row's cells by visual column (a colspan cell repeats). */
|
|
107
|
+
export function cellsOf(row) {
|
|
105
108
|
const cells = []
|
|
106
109
|
for (const cell of row.children.filter((child) => child.tag === "td" || child.tag === "th")) {
|
|
107
110
|
const span = Math.max(1, Number.parseInt(cell.attrs.colspan ?? "1", 10) || 1)
|
|
@@ -110,7 +113,7 @@ function cellsOf(row) {
|
|
|
110
113
|
return cells
|
|
111
114
|
}
|
|
112
115
|
|
|
113
|
-
function rowsOf(table) {
|
|
116
|
+
export function rowsOf(table) {
|
|
114
117
|
const rows = []
|
|
115
118
|
const walk = (node) => {
|
|
116
119
|
for (const child of node.children) {
|
|
@@ -142,10 +145,13 @@ function tablesOf(root) {
|
|
|
142
145
|
const cellLabel = (cell) => label(proseText(cell))
|
|
143
146
|
const indexesOf = (labels, wanted) => labels.flatMap((value, index) => (value === wanted ? [index] : []))
|
|
144
147
|
|
|
145
|
-
/**
|
|
146
|
-
|
|
148
|
+
/**
|
|
149
|
+
* --table/--row/--col: the table by id, caption, or a header label; row and column by normalized label,
|
|
150
|
+
* never by index. `source` is page html or an extracted view ({ root, text, blocks }) of one.
|
|
151
|
+
*/
|
|
152
|
+
export function locateTable(source, { table, row, col, sourceId }) {
|
|
147
153
|
requireSource(sourceId)
|
|
148
|
-
const { root, text, blocks } = snapshotBlocks(
|
|
154
|
+
const { root, text, blocks } = typeof source === "string" ? snapshotBlocks(source) : source
|
|
149
155
|
const key = label(table)
|
|
150
156
|
const tables = tablesOf(root).filter((entry) => entry.table.attrs.id === table || entry.caption === key || (entry.header && cellsOf(entry.header).map(cellLabel).includes(key)))
|
|
151
157
|
if (tables.length !== 1) return tables.length === 0 ? notLocated() : ambiguous(tables.map((entry) => ({ table: entry.table.attrs.id ?? entry.caption })))
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import { CLAIM_ATTRIBUTE, CLAIM_KINDS, claimSatisfied, unmarkedKinds, unmarkedRequires } from "./claim-kinds.mjs"
|
|
15
15
|
import { proseSentences } from "./fidelity-binding.mjs"
|
|
16
16
|
import { clip } from "./fidelity-trace.mjs"
|
|
17
|
+
import { isClaimSegment } from "./sources-citation.mjs"
|
|
17
18
|
import { proseText } from "./html-lite.mjs"
|
|
18
19
|
|
|
19
20
|
const marked = (node) => node.attrs?.[CLAIM_ATTRIBUTE] !== undefined
|
|
@@ -63,7 +64,7 @@ export function claimChecks(ctx, rows, provenanceOf, flag) {
|
|
|
63
64
|
const id = fact.attrs["data-fact"]
|
|
64
65
|
if (!nearest) {
|
|
65
66
|
const need = unmarkedRequires(rows.get(id))
|
|
66
|
-
const mark = need && `{{${CLAIM_KINDS[need].prefix}:${id}}} (${CLAIM_ATTRIBUTE}="${need}")`
|
|
67
|
+
const mark = need && `{{${CLAIM_KINDS[need].prefix}:${id}:${rows.get(id).label}}} (${CLAIM_ATTRIBUTE}="${need}")`
|
|
67
68
|
if (need) flag("claim_kind_mismatch", fact, `${id} is a ${rows.get(id).kind} row cited with no mark; an unmarked numeral reads as a fact, so mark it ${mark}`)
|
|
68
69
|
continue
|
|
69
70
|
}
|
|
@@ -77,8 +78,8 @@ export function claimChecks(ctx, rows, provenanceOf, flag) {
|
|
|
77
78
|
flag(entry.defect, fact, `${row.fact_id} is marked ${name}, but it is a ${row.kind} row${source}`)
|
|
78
79
|
}
|
|
79
80
|
for (const block of proseSentences(ctx)) {
|
|
80
|
-
if (block.kind === "
|
|
81
|
-
for (const sentence of block.sentences) {
|
|
81
|
+
if (block.kind === "toc") continue
|
|
82
|
+
for (const sentence of block.kind === "sources" ? block.sentences.filter((s) => isClaimSegment(s.text)) : block.sentences) {
|
|
82
83
|
for (const name of unmarkedKinds(sentence.text)) {
|
|
83
84
|
const covered = sentence.facts.length > 0 ? sentence.facts.every((fact) => nearestKind(fact.node) === name) : nearestKind(block.owner) === name
|
|
84
85
|
if (covered) continue
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Read fact export CSV records, including quoted commas, escaped quotes and newlines.
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
3
|
+
|
|
4
|
+
export function readCsv(path) {
|
|
5
|
+
if (!existsSync(path)) return null
|
|
6
|
+
const text = readFileSync(path, "utf8")
|
|
7
|
+
const records = []
|
|
8
|
+
let cells = []
|
|
9
|
+
let cell = ""
|
|
10
|
+
let quoted = false
|
|
11
|
+
for (let i = 0; i <= text.length; i++) {
|
|
12
|
+
const char = text[i]
|
|
13
|
+
if (char === '"') {
|
|
14
|
+
if (quoted && text[i + 1] === '"') {
|
|
15
|
+
cell += '"'
|
|
16
|
+
i++
|
|
17
|
+
} else quoted = !quoted
|
|
18
|
+
} else if (!quoted && (char === "," || char === "\n" || char === undefined)) {
|
|
19
|
+
cells.push(cell.trim())
|
|
20
|
+
cell = ""
|
|
21
|
+
if (char !== ",") {
|
|
22
|
+
if (cells.some((value) => value !== "")) records.push(cells)
|
|
23
|
+
cells = []
|
|
24
|
+
}
|
|
25
|
+
} else if (char !== undefined) cell += char
|
|
26
|
+
}
|
|
27
|
+
if (quoted) return null
|
|
28
|
+
const [head = [], ...rows] = records
|
|
29
|
+
const [id, value, unit] = ["fact_id", "plot_value", "unit"].map((name) => head.indexOf(name))
|
|
30
|
+
if (id < 0 || value < 0 || unit < 0 || rows.length === 0) return null
|
|
31
|
+
return rows.map((c) => ({ fact_id: c[id], plot_value: c[value], unit: c[unit] ?? "" }))
|
|
32
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// causality_added, qualifier_dropped (six-token window around the fact span; a :q rendering puts
|
|
5
5
|
// the qualifier inside the span), scope_widened_warn, and the acronym-aware name_outside_quote.
|
|
6
6
|
// The hedge-position heuristic never yields a code; it only sets force_strong for the meaning
|
|
7
|
-
// verifier
|
|
7
|
+
// verifier; name_outside_quote and scope_widened_warn also emit minor defects. Latin terms match whole words (terms
|
|
8
8
|
// of four or more letters also match their inflections); Korean terms match as substrings, single
|
|
9
9
|
// syllables as tokens. No model, no IO. Plain Node ESM; Korean strings are \u escapes.
|
|
10
10
|
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
// Check displayed conversion originals against their operands, not the mutable render field.
|
|
2
|
+
import { sameValue, unitKey } from "./fidelity-binding.mjs"
|
|
3
|
+
import { proseText } from "./html-lite.mjs"
|
|
4
|
+
import { parseNumeral } from "./numerals.mjs"
|
|
5
|
+
import { normalizeText } from "./text-normalize.mjs"
|
|
6
|
+
import { display } from "./unit-precision.mjs"
|
|
7
|
+
import { dimensionOf } from "./units.mjs"
|
|
8
|
+
|
|
9
|
+
const DERIVED = new Set(["converted", "derived"])
|
|
10
|
+
/**
|
|
11
|
+
* The one bracket grammar of the skill. After NFKC (fullwidth parentheses and square brackets fold
|
|
12
|
+
* to ASCII), an original is a () [] 【】 「」 or 〔〕 group. Inside a derived or converted element every
|
|
13
|
+
* group is an original, never prose; a dash separator is not an original.
|
|
14
|
+
*/
|
|
15
|
+
const PAIRS = [["(", ")"], ["[", "]"], ["\u3010", "\u3011"], ["\u300C", "\u300D"], ["\u3014", "\u3015"]]
|
|
16
|
+
const [OPEN, CLOSE] = [PAIRS.map((pair) => pair[0]).join(""), PAIRS.map((pair) => pair[1]).join("")]
|
|
17
|
+
const inClass = (chars) => chars.replace(/[\\\]\[^-]/g, "\\$&")
|
|
18
|
+
const literal = (chars) => chars.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
19
|
+
const ANY = inClass(OPEN + CLOSE)
|
|
20
|
+
const GROUP = new RegExp(PAIRS.map(([open, close]) => `${literal(open)}([^${ANY}]*)${literal(close)}`).join("|"), "gu")
|
|
21
|
+
const BRACKET = new RegExp(`[${ANY}]`, "u")
|
|
22
|
+
const folded = (text) => String(text).normalize("NFKC")
|
|
23
|
+
const innerOf = (match) => match.slice(1).find((part) => part !== undefined)
|
|
24
|
+
const mismatch = (message) => ["value_mismatch", message, "original"]
|
|
25
|
+
|
|
26
|
+
const halfFits = (row, half) => {
|
|
27
|
+
const expected = normalizeText(folded(display(String(row.value), row.unit ?? null)))
|
|
28
|
+
const text = normalizeText(half)
|
|
29
|
+
return text === expected || (text.endsWith(` ${expected}`) && !/\d/.test(text.slice(0, -expected.length)))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The converted half (the text outside the originals) equals display(row.value, row.unit), rebuilt from the row. */
|
|
33
|
+
export function convertedHalfFits(row, shown) {
|
|
34
|
+
if (!DERIVED.has(row.kind) || row.value === null || row.value === undefined) return false
|
|
35
|
+
return halfFits(row, folded(shown).replace(GROUP, ""))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A second number outside the value verify reads and its originals ("320,000 mm - 999 m"). */
|
|
39
|
+
function strayNumber(row, half) {
|
|
40
|
+
if (row.value === null || row.value === undefined || halfFits(row, half)) return null
|
|
41
|
+
const read = parseNumeral(half)
|
|
42
|
+
if (!read || !sameValue(read.value, row.value)) return null
|
|
43
|
+
const rest = half.slice(0, read.span[0]) + half.slice(read.span[1])
|
|
44
|
+
return /\d/.test(row.unit ? rest.split(row.unit).join("") : rest) ? mismatch(`"${half.trim()}" carries a number besides the value and its originals`) : null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** `valueMode`: the element shows the bare value ({{F:x:value}}), so no original is due. */
|
|
48
|
+
export function originalProblem(row, shown, rows, valueMode = false) {
|
|
49
|
+
if (!DERIVED.has(row.kind)) return null
|
|
50
|
+
const text = folded(shown)
|
|
51
|
+
const groups = [...text.matchAll(GROUP)]
|
|
52
|
+
const half = text.replace(GROUP, "")
|
|
53
|
+
if (BRACKET.test(half)) return mismatch("original brackets are malformed or nested")
|
|
54
|
+
const operands = (row.derived_from ?? []).map((id) => rows.get(id))
|
|
55
|
+
for (const [index, match] of groups.entries()) {
|
|
56
|
+
const [inner, operand] = [innerOf(match), operands[index]]
|
|
57
|
+
const parsed = parseNumeral(inner)
|
|
58
|
+
if (!operand) return mismatch(`original "${inner}" has no operand of ${row.fact_id}`)
|
|
59
|
+
if (!parsed || !sameValue(parsed.value, operand.value)) return mismatch(`original "${inner}" differs from operand ${operand.fact_id}`)
|
|
60
|
+
if (unitKey(parsed.unitRaw ?? "") !== unitKey(operand.unit ?? "")) return ["unit_mismatch", `original "${inner}" is not in ${operand.unit}`, "original"]
|
|
61
|
+
const expected = normalizeText(folded(display(operand.value, operand.unit ?? null)))
|
|
62
|
+
if (normalizeText(inner) !== expected) return mismatch(`original "${inner}" differs from "${expected}"`)
|
|
63
|
+
}
|
|
64
|
+
if (groups.length > 0 && !/\d/.test(text.slice(0, groups[0].index))) return mismatch(`original "${innerOf(groups[0])}" precedes the value`)
|
|
65
|
+
if (row.kind === "converted" && !valueMode && /\d/.test(half) && groups.length < operands.length) return mismatch(`the original of ${row.derived_from[groups.length]} is missing after the value`)
|
|
66
|
+
return strayNumber(row, half)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Stands for a filled element or another fill-map value in the text after a fill site. */
|
|
70
|
+
const FILLED = "\uFFFC"
|
|
71
|
+
/** After a fill site the grammar also reads the brackets an element never carries as originals. */
|
|
72
|
+
const FOLLOW = [...PAIRS, ["\u300E", "\u300F"], ["\u300A", "\u300B"], ["\u3008", "\u3009"], ["\u27E8", "\u27E9"]]
|
|
73
|
+
const dimension = (unit) => dimensionOf(String(unit).replace(/-e\d+$/u, "")) ?? unitKey(unit)
|
|
74
|
+
|
|
75
|
+
/** What the text after a fill site opens: a bracket group's content, or the clause after a separator (`open`); else null. */
|
|
76
|
+
function openedGroup(text) {
|
|
77
|
+
const rest = folded(text).trimStart()
|
|
78
|
+
const pair = FOLLOW.find(([open]) => rest.startsWith(open))
|
|
79
|
+
const close = pair ? rest.indexOf(pair[1], 1) : -1
|
|
80
|
+
if (pair) return { group: rest.slice(1, close === -1 ? undefined : close).trim(), open: false }
|
|
81
|
+
return /^[-\u2013\u2014/]/u.test(rest) ? { group: rest.slice(1).split(/[,;:!?)\]]|\.(?!\d)/u)[0].trim(), open: true } : null
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Whether the text right after a fill site of `row` opens another original: a bracket group or a
|
|
86
|
+
* separator clause that is exactly a numeral with a unit of the row's or an operand's dimension (a
|
|
87
|
+
* separator clause may end in a glued particle, as Korean writes "- 999 m\uC774\uB2E4").
|
|
88
|
+
* A filled element, a fill-map value (`fills`), a placeholder, a unitless number (a year, a
|
|
89
|
+
* footnote) and prose with digits are not originals; untraceable_number still reads a stray number.
|
|
90
|
+
*/
|
|
91
|
+
export const followsOriginal = (row, rows, fills = []) => (text) => {
|
|
92
|
+
if (!DERIVED.has(row?.kind)) return false
|
|
93
|
+
const after = [...fills].filter(Boolean).sort((a, b) => b.length - a.length).reduce((cur, fill) => cur.split(fill).join(FILLED), String(text))
|
|
94
|
+
const { group, open } = openedGroup(after) ?? { group: "" }
|
|
95
|
+
if (!group || group.includes(FILLED) || group.includes("{{")) return false
|
|
96
|
+
const units = [row.unit, ...(row.derived_from ?? []).map((id) => rows.get(id)?.unit)].filter(Boolean)
|
|
97
|
+
return [group, ...(open ? [group.replace(/[\uAC00-\uD7A3]+$/u, "").trim()] : [])].some((body) => {
|
|
98
|
+
const read = parseNumeral(body, 0)
|
|
99
|
+
return Boolean(read?.unitRaw) && read.span[0] === 0 && read.span[1] === body.length && units.some((unit) => dimension(unit) === dimension(read.unitRaw))
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The prose after `node` in its parent, each filled element (data-fact) read as one FILLED mark. */
|
|
104
|
+
export const textAfter = (node) => {
|
|
105
|
+
const siblings = node.parent?.children.slice(node.parent.children.indexOf(node) + 1) ?? []
|
|
106
|
+
return proseText({ children: siblings.map((child) => (child.attrs?.["data-fact"] === undefined ? child : { tag: "#text", text: FILLED })) })
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const SPACE = "[\\s\\u3000]"
|
|
110
|
+
const DIGIT = "[0-9\\uFF10-\\uFF19]"
|
|
111
|
+
/** Each character with the fullwidth form NFKC folds onto it. */
|
|
112
|
+
const wide = (chars) => [...chars].map((ch) => ch + (/[!-~]/.test(ch) ? String.fromCharCode(ch.charCodeAt(0) + 0xfee0) : "")).join("")
|
|
113
|
+
const [WIDE_OPEN, WIDE_CLOSE] = [inClass(wide(OPEN)), inClass(wide(CLOSE))]
|
|
114
|
+
|
|
115
|
+
/** A pattern reading `rendered` modulo the bracket grammar: NFKC fullwidth forms, bracket style, spacing around brackets. */
|
|
116
|
+
function renderPattern(rendered) {
|
|
117
|
+
const text = folded(rendered).trim()
|
|
118
|
+
let body = ""
|
|
119
|
+
for (let at = 0; at < text.length; at += 1) {
|
|
120
|
+
const ch = text[at]
|
|
121
|
+
const beside = (OPEN + CLOSE).includes(text.slice(at).trimStart()[0]) || OPEN.includes(text[at - 1])
|
|
122
|
+
if (/\s/.test(ch)) body += beside ? "" : `${SPACE}+`
|
|
123
|
+
else if (OPEN.includes(ch)) body += `${SPACE}*([${WIDE_OPEN}])${SPACE}*`
|
|
124
|
+
else if (CLOSE.includes(ch)) body += `${SPACE}*([${WIDE_CLOSE}])`
|
|
125
|
+
else body += `[${inClass(wide(ch))}]`
|
|
126
|
+
}
|
|
127
|
+
// A digit may not run into a render's leading or trailing digit ("1,320 m" holds no "320 m"); a bracket ends the render.
|
|
128
|
+
return new RegExp(`${/^\d/.test(text) ? `(?<!${DIGIT})` : ""}${body}${/\d$/.test(text) ? `(?!${DIGIT})` : ""}`, "gu")
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const pairIndex = (ch, side) => [...side].findIndex((open) => open === folded(ch))
|
|
132
|
+
/** [start, end) of `rendered` in `text` from `from` under the bracket grammar, not followed by another original (`follows`); or null. */
|
|
133
|
+
export function findRender(text, rendered, from = 0, follows = () => false) {
|
|
134
|
+
const pattern = renderPattern(rendered)
|
|
135
|
+
pattern.lastIndex = from
|
|
136
|
+
for (let match = pattern.exec(text); match; match = pattern.exec(text)) {
|
|
137
|
+
const marks = match.slice(1)
|
|
138
|
+
const paired = marks.every((mark, i) => i % 2 === 1 || pairIndex(mark, OPEN) === pairIndex(marks[i + 1] ?? "", CLOSE))
|
|
139
|
+
const end = match.index + match[0].length
|
|
140
|
+
if (paired && !follows(text.slice(end))) return [match.index, end]
|
|
141
|
+
pattern.lastIndex = match.index + 1
|
|
142
|
+
}
|
|
143
|
+
return null
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Where a stale fill-map entry's text sits: derived and converted renders are read under the bracket grammar. */
|
|
147
|
+
export const renderLocator = (rows, fillMap) => (entry, text, from) => {
|
|
148
|
+
const row = rows.get(entry.fact_id)
|
|
149
|
+
if (DERIVED.has(row?.kind)) return findRender(text, entry.rendered, from, followsOriginal(row, rows, (fillMap ?? []).map((other) => other.rendered)))
|
|
150
|
+
const hit = text.indexOf(entry.rendered, from)
|
|
151
|
+
return hit === -1 ? null : [hit, hit + entry.rendered.length]
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Null when a stale derived render is still in the report; "original" when only its converted half is; "gone" otherwise. */
|
|
155
|
+
export function staleRender(row, rendered, text, follows) {
|
|
156
|
+
if (findRender(text, rendered, 0, follows) !== null) return null
|
|
157
|
+
const half = folded(rendered).replace(GROUP, "").trim()
|
|
158
|
+
return half !== "" && /\d/.test(half) && findRender(text, half) !== null ? "original" : "gone"
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const isDerivedRow = (row) => DERIVED.has(row?.kind)
|
|
@@ -137,11 +137,11 @@ function sourcesOf(row, rows, seen = new Set()) {
|
|
|
137
137
|
/** sha256 of the locked `## Fidelity` block normalized: the seven fields in FIDELITY_FIELDS order, `field: value` per line. */
|
|
138
138
|
export const briefHashOf = (parsed) => sha256Of(FIDELITY_FIELDS.map((field) => `${field}: ${parsed.fields[field]}`).join("\n"))
|
|
139
139
|
|
|
140
|
-
/** brief_changed when
|
|
140
|
+
/** brief_changed when there is no fill map (map null) or it was not written under the current brief (or predates the binding). */
|
|
141
141
|
export function briefBindingErrors(map, mapPath, parsed) {
|
|
142
|
-
if (map
|
|
143
|
-
const why = typeof map.brief_sha256 === "string" ? "was filled under a different ## Fidelity block" : "records no brief_sha256"
|
|
144
|
-
return [{ code: "brief_changed", token: null, line: null, message: `${
|
|
142
|
+
if (map?.brief_sha256 === briefHashOf(parsed)) return []
|
|
143
|
+
const why = map === null ? `no fill map at ${mapPath}` : `${mapPath} ${typeof map.brief_sha256 === "string" ? "was filled under a different ## Fidelity block" : "records no brief_sha256"}`
|
|
144
|
+
return [{ code: "brief_changed", token: null, line: null, message: `${why}; re-run fact fill under the current brief`, severity: DEFECT_CODES.brief_changed.severity, integrity: DEFECT_CODES.brief_changed.integrity, hint: DEFECT_CODES.brief_changed.hint }]
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
function metaOf(sessionDir, sourceId, cache) {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// fidelity record exists, the barrier refuses the unverified edition regardless of it, and the same
|
|
16
16
|
// exemption is the only way a required record may be absent.
|
|
17
17
|
|
|
18
|
-
import { existsSync, readFileSync } from "node:fs"
|
|
18
|
+
import { existsSync, readdirSync, readFileSync, realpathSync } from "node:fs"
|
|
19
19
|
import { join } from "node:path"
|
|
20
20
|
|
|
21
21
|
import { CliError, readJsonFile, sha256Of, writeJsonAtomic } from "./cli-support.mjs"
|
|
@@ -24,7 +24,10 @@ import { meaningCheckEnabled, requireLockedBrief } from "./fidelity-brief.mjs"
|
|
|
24
24
|
import { blocksDelivery, deliveryBlockReason } from "./gates-fidelity.mjs"
|
|
25
25
|
|
|
26
26
|
export const FIDELITY_RECORD = "fidelity-defects.json"
|
|
27
|
+
/** The legacy session meaning record: a candidate for every edition whose bytes it claims, like any edition record (newest run wins). */
|
|
27
28
|
export const MEANING_RECORD = "fidelity-meaning-defects.json"
|
|
29
|
+
/** The meaning record of one delivered edition, named by its outcome format (`fact meaning-check --edition`). */
|
|
30
|
+
export const meaningRecordName = (format) => `fidelity-meaning-defects.${format}.json`
|
|
28
31
|
|
|
29
32
|
/** The fields every fidelity record carries, whatever its kind. */
|
|
30
33
|
export const RECORD_ENVELOPE = Object.freeze(["record_kind", "artifact_sha256", "report", "written_at", "defects"])
|
|
@@ -39,13 +42,67 @@ function meaningRequired({ sessionDir }) {
|
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
const subjectOf = (row) => row.renderedFrom ?? row.path ?? null
|
|
45
|
+
const hashOf = (path) => (existsSync(path) ? sha256Of(readFileSync(path)) : null)
|
|
42
46
|
|
|
43
47
|
/** One record per delivered edition: the file its row names with --verified-by, bound to that edition's bytes. */
|
|
44
48
|
const perEdition = (sessionDir, delivered) =>
|
|
45
49
|
delivered.map((row) => ({ path: row.verifiedBy ?? null, declared: row.verifiedBy !== undefined, format: row.format, at: `${row.format}: `, subjects: [subjectOf(row)].filter((subject) => subject !== null) }))
|
|
46
50
|
|
|
47
|
-
|
|
48
|
-
|
|
51
|
+
const sessionTarget = (sessionDir, file) => ({ path: join(sessionDir, file), declared: false, format: null, at: "", subjects: [] })
|
|
52
|
+
|
|
53
|
+
/** Include every existing session fidelity record, even when no delivered row attaches it. */
|
|
54
|
+
const perEditionAndSession = (sessionDir, delivered) => [
|
|
55
|
+
...perEdition(sessionDir, delivered),
|
|
56
|
+
...(existsSync(join(sessionDir, FIDELITY_RECORD)) ? [sessionTarget(sessionDir, FIDELITY_RECORD)] : []),
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
/** Edition meaning records on disk (fidelity-meaning-defects.<format>.json). */
|
|
60
|
+
const EDITION_MEANING = /^fidelity-meaning-defects\.[^.]+\.json$/u
|
|
61
|
+
|
|
62
|
+
/** Verdict severity for equal times: a confirmed drift, then any other blocking defect (unverified), then clean. */
|
|
63
|
+
const severityOf = (defects) =>
|
|
64
|
+
defects.some((defect) => defect?.code === "meaning_drift_confirmed") ? 2 : defects.some((defect) => typeof defect?.code === "string" && blocksDelivery(defect)) ? 1 : 0
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* What selection reads from a meaning record: the bytes it claims, when it was written, how severe it is.
|
|
68
|
+
* An unreadable record matches no bytes (its own row still reads and refuses it); a record with no
|
|
69
|
+
* readable written_at sorts newest, so the barrier reads it and refuses it as malformed.
|
|
70
|
+
*/
|
|
71
|
+
function claimOf(path) {
|
|
72
|
+
let record
|
|
73
|
+
try {
|
|
74
|
+
record = readJsonFile(path)
|
|
75
|
+
} catch {
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
const at = Date.parse(record?.written_at)
|
|
79
|
+
return { path, hash: record?.artifact_sha256 ?? null, at: Number.isNaN(at) ? Infinity : at, severity: severityOf(Array.isArray(record?.defects) ? record.defects : []) }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Newest written_at first; on equal times the more severe verdict first. File names never decide. */
|
|
83
|
+
const newestFirst = (a, b) => (a.at === b.at ? b.severity - a.severity : b.at > a.at ? 1 : -1)
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* One meaning record per delivered edition, bound to that edition's bytes. Every record whose
|
|
87
|
+
* artifact_sha256 equals the edition's current bytes (a PDF counts its --rendered-from bytes) is a
|
|
88
|
+
* candidate: its own fidelity-meaning-defects.<format>.json, another edition's record (a
|
|
89
|
+
* byte-identical copy, or the html edition a PDF renders), and the legacy session record. The
|
|
90
|
+
* newest reader run wins; on equal times the more severe verdict wins. When no record claims these
|
|
91
|
+
* bytes the target is its own record (refused as for a different edition), else the legacy record,
|
|
92
|
+
* else its own name, so the refusal names the file to write (meaning gate not run).
|
|
93
|
+
*/
|
|
94
|
+
function perEditionMeaning(sessionDir, delivered) {
|
|
95
|
+
if (delivered.length === 0) return [sessionTarget(sessionDir, MEANING_RECORD)]
|
|
96
|
+
const legacy = join(sessionDir, MEANING_RECORD)
|
|
97
|
+
const editions = readdirSync(sessionDir).filter((name) => EDITION_MEANING.test(name)).map((name) => join(sessionDir, name))
|
|
98
|
+
const claims = [...editions, ...(existsSync(legacy) ? [legacy] : [])].map(claimOf).filter((claim) => claim !== null)
|
|
99
|
+
return perEdition(sessionDir, delivered).map((target) => {
|
|
100
|
+
const own = join(sessionDir, meaningRecordName(target.format))
|
|
101
|
+
const bytes = target.subjects.length > 0 ? hashOf(target.subjects[0]) : null
|
|
102
|
+
const [newest] = bytes === null ? [] : claims.filter((claim) => claim.hash === bytes).sort(newestFirst)
|
|
103
|
+
return { ...target, declared: false, path: newest?.path ?? (existsSync(own) ? own : existsSync(legacy) ? legacy : own) }
|
|
104
|
+
})
|
|
105
|
+
}
|
|
49
106
|
|
|
50
107
|
/** Every fidelity record the barrier reads, in check order; each column is one rule the barrier applies. */
|
|
51
108
|
export const FIDELITY_RECORDS = Object.freeze([
|
|
@@ -53,7 +110,7 @@ export const FIDELITY_RECORDS = Object.freeze([
|
|
|
53
110
|
kind: "fidelity",
|
|
54
111
|
file: FIDELITY_RECORD,
|
|
55
112
|
step: "fidelity gate",
|
|
56
|
-
targets:
|
|
113
|
+
targets: perEditionAndSession,
|
|
57
114
|
requiredWhen: ({ hasFidelity }, target) => hasFidelity || GATED_FORMATS.includes(target.format),
|
|
58
115
|
remedy: (target) => `run fact verify on ${target.subjects[0] ?? "the edition"} and set it with --verified-by`,
|
|
59
116
|
bindsTo: "artifact_sha256",
|
|
@@ -64,17 +121,25 @@ export const FIDELITY_RECORDS = Object.freeze([
|
|
|
64
121
|
kind: "meaning",
|
|
65
122
|
file: MEANING_RECORD,
|
|
66
123
|
step: "meaning gate",
|
|
67
|
-
targets:
|
|
124
|
+
targets: perEditionMeaning,
|
|
68
125
|
requiredWhen: meaningRequired,
|
|
69
|
-
remedy: () =>
|
|
126
|
+
remedy: (target) => `run fact pairs and fact meaning-check --edition ${target.format} on ${target.subjects[0] ?? "the edition"} before delivery`,
|
|
70
127
|
bindsTo: "artifact_sha256",
|
|
71
128
|
blocking: blocksDelivery,
|
|
72
129
|
payload: Object.freeze(["command", "status", "exitCode", "run_id", "pairs_sha256", "counts", "dropped", "unverified", "calibration_path", "calibration_written", "calibration_replaced", "promotion_eligibility"]),
|
|
73
130
|
}),
|
|
74
131
|
])
|
|
75
132
|
|
|
133
|
+
// Collection warnings use the same envelope, but are not delivery-gate records.
|
|
134
|
+
const SOURCE_RECORDS = Object.freeze([
|
|
135
|
+
Object.freeze({
|
|
136
|
+
kind: "source_conversion",
|
|
137
|
+
payload: Object.freeze(["conversion_altered_numbers", "lost", "raw_count", "converted_count"]),
|
|
138
|
+
}),
|
|
139
|
+
])
|
|
140
|
+
|
|
76
141
|
const entryOf = (kind) => {
|
|
77
|
-
const entry = FIDELITY_RECORDS.find((row) => row.kind === kind)
|
|
142
|
+
const entry = [...FIDELITY_RECORDS, ...SOURCE_RECORDS].find((row) => row.kind === kind)
|
|
78
143
|
if (entry === undefined) throw new CliError(`unknown fidelity record kind ${kind}`)
|
|
79
144
|
return entry
|
|
80
145
|
}
|
|
@@ -124,19 +189,20 @@ export function readFidelityRecord(path, kind) {
|
|
|
124
189
|
}
|
|
125
190
|
|
|
126
191
|
const unresolved = (format, codes, path) => ({ code: "fidelity_unresolved", format, codes, message: codes.map((code) => deliveryBlockReason({ code })).join("; "), path })
|
|
127
|
-
const hashOf = (path) => (existsSync(path) ? sha256Of(readFileSync(path)) : null)
|
|
128
192
|
|
|
129
193
|
/** Barrier problems for `outcome verify` and `outcome finish`; empty when nothing blocks delivery. */
|
|
130
194
|
export function fidelityProblems(sessionDir, manifest) {
|
|
131
195
|
const delivered = manifest.deliverables.filter((row) => row.status === "delivered")
|
|
132
|
-
const
|
|
196
|
+
const targets = FIDELITY_RECORDS.map((entry) => [entry, entry.targets(sessionDir, delivered)])
|
|
197
|
+
// Any session record a row did not name (a session fidelity record, a meaning record) is fidelity evidence.
|
|
198
|
+
const hasFidelity = existsSync(join(sessionDir, LEDGER_FILENAME)) || targets.some(([, list]) => list.some((target) => !target.declared && target.path !== null && existsSync(target.path)))
|
|
133
199
|
const lastFidelity = manifest.gateHistory?.findLast((entry) => entry.gate === "fidelity")
|
|
134
200
|
const exempt = !hasFidelity && manifest.gates.fidelity === "not_run" && lastFidelity?.status === "not_run" && Boolean(lastFidelity.reason?.trim())
|
|
135
201
|
const context = { sessionDir, hasFidelity }
|
|
136
202
|
const problems = []
|
|
137
203
|
const counted = new Set()
|
|
138
|
-
for (const entry of
|
|
139
|
-
for (const target of
|
|
204
|
+
for (const [entry, list] of targets) {
|
|
205
|
+
for (const target of list) {
|
|
140
206
|
const refuse = (message, path = target.path) => problems.push({ code: "edition_unverified", format: target.format, message: `${target.at}${message}`, path })
|
|
141
207
|
if (target.path === null || !existsSync(target.path)) {
|
|
142
208
|
if (target.declared) refuse(`${entry.kind} record ${target.path} is missing`)
|
|
@@ -151,10 +217,11 @@ export function fidelityProblems(sessionDir, manifest) {
|
|
|
151
217
|
continue
|
|
152
218
|
}
|
|
153
219
|
if (target.subjects.length > 0 && !target.subjects.some((subject) => hashOf(subject) === record[entry.bindsTo])) {
|
|
154
|
-
refuse(`${entry.kind} record ${target.path} is for a different edition than ${target.subjects.join(", ")}`)
|
|
220
|
+
refuse(`${entry.kind} record ${target.path} is for a different edition than ${target.subjects.join(", ")}; ${entry.remedy(target)}`)
|
|
155
221
|
}
|
|
156
|
-
|
|
157
|
-
counted.
|
|
222
|
+
const path = realpathSync.native(target.path)
|
|
223
|
+
if (counted.has(path)) continue
|
|
224
|
+
counted.add(path)
|
|
158
225
|
const codes = [...new Set(record.defects.filter(entry.blocking).map((defect) => defect.code))].sort()
|
|
159
226
|
if (codes.length > 0) problems.push(unresolved(target.format, codes, target.path))
|
|
160
227
|
}
|