oh-my-opencode 5.1.47 → 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 -259
- package/dist/skills/ulw-research/references/deliverable-phase.md +68 -24
- 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 +14 -6
- 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 +16 -15
- package/dist/skills/ulw-research/scripts/contracts.test.ts +6 -109
- 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-fidelity-policy.test.ts +156 -0
- package/dist/skills/ulw-research/scripts/copy-returns-polish.test.ts +10 -9
- 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 +96 -0
- package/dist/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
- package/dist/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
- package/dist/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
- package/dist/skills/ulw-research/scripts/fact-commands.mjs +40 -62
- 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 +9 -6
- 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 +183 -0
- 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 +6 -560
- package/dist/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
- package/dist/skills/ulw-research/scripts/source-commands.mjs +87 -29
- 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 -259
- package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +68 -24
- 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 +14 -6
- 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 +16 -15
- 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 +96 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +40 -62
- 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 +9 -6
- 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 +183 -0
- 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 +87 -29
- 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 -259
- package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +68 -24
- 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 +14 -6
- 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 +16 -15
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +6 -109
- 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-fidelity-policy.test.ts +156 -0
- package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-polish.test.ts +10 -9
- 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 +96 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +26 -30
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-polish.mjs +46 -19
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +37 -6
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +40 -62
- 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 +9 -6
- 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 +183 -0
- 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 +6 -560
- 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 +87 -29
- 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,183 @@
|
|
|
1
|
+
// fidelity-policy.mjs - what the locked `## Fidelity` block of brief.md means to the CLI stages.
|
|
2
|
+
//
|
|
3
|
+
// lockedBrief is the gate the six reader-facing stages (fact fill, verify, pairs, meaning-check,
|
|
4
|
+
// polish-apply, polish-diff) run before reading or writing anything: an unlocked brief returns
|
|
5
|
+
// one fidelity_interview_incomplete defect (exit 1), a malformed block is a usage error (exit 2).
|
|
6
|
+
// fillPolicy reads the fields fact fill consumes (a value off its table is a usage error, exit 2),
|
|
7
|
+
// and policyErrors refuses a placeholder whose row the brief forbids: derived_not_allowed
|
|
8
|
+
// (`derived numbers: none` against a derived, converted, or assumed row) and source_outside_floor
|
|
9
|
+
// (the source floor is evaluated live against each source's meta.json at every fill and verify;
|
|
10
|
+
// under any floor but `standard` a row resting on no source is refused too; a row's outside_floor
|
|
11
|
+
// is only the cached result). briefHashOf binds the locked block into the fill map, and
|
|
12
|
+
// briefBindingErrors refuses an edition whose map was filled under another brief (brief_changed).
|
|
13
|
+
// Collection stages never call this module. node: builtins and sibling imports only.
|
|
14
|
+
|
|
15
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
16
|
+
import { join, resolve } from "node:path"
|
|
17
|
+
|
|
18
|
+
import { CliError, sha256Of } from "./cli-support.mjs"
|
|
19
|
+
import { DEFECT_CODES } from "./contracts.mjs"
|
|
20
|
+
import { KIND_RULES } from "./fact-ledger-kinds.mjs"
|
|
21
|
+
import { FIDELITY_FIELDS, meaningCheckEnabled, requireLockedBrief } from "./fidelity-brief.mjs"
|
|
22
|
+
|
|
23
|
+
export { meaningCheckEnabled }
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Ledger kinds a brief with `derived numbers: none` refuses. A policy list, not the ledger's
|
|
27
|
+
* derivation set: it also names assumed. Every name must be a KIND_RULES kind (checked at load).
|
|
28
|
+
*/
|
|
29
|
+
const DERIVED_KINDS = Object.freeze(["derived", "converted", "assumed"])
|
|
30
|
+
for (const kind of DERIVED_KINDS) {
|
|
31
|
+
if (!Object.hasOwn(KIND_RULES, kind)) throw new Error(`fidelity-policy: ${kind} is not a KIND_RULES kind`)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Fields whose only legal values are a closed table: value (any case) -> its reading. */
|
|
35
|
+
const VALUE_TABLES = Object.freeze({
|
|
36
|
+
"derived numbers": Object.freeze({ allowed: false, none: true }),
|
|
37
|
+
fallback: Object.freeze({ "block-quote": "quote", omit: "omit" }),
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
/** The reading of a table value; any other text is a usage error naming the field and the value. */
|
|
41
|
+
function tableValue(field, value) {
|
|
42
|
+
const table = VALUE_TABLES[field]
|
|
43
|
+
const key = value.trim().toLowerCase()
|
|
44
|
+
if (Object.hasOwn(table, key)) return table[key]
|
|
45
|
+
throw new CliError(`${field} "${value}" is not ${Object.keys(table).join(", ")}`)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** How each field value the fill stages consume is read; the single owner of those readings. */
|
|
49
|
+
export const FIDELITY_VALUE_RULES = Object.freeze({
|
|
50
|
+
tables: VALUE_TABLES,
|
|
51
|
+
derivedKinds: DERIVED_KINDS,
|
|
52
|
+
derivedRefused: (kind) => DERIVED_KINDS.includes(kind),
|
|
53
|
+
derivedNone: (value) => tableValue("derived numbers", value),
|
|
54
|
+
fallback: (value) => tableValue("fallback", value),
|
|
55
|
+
translations: (value) => /with translation beside/iu.test(value),
|
|
56
|
+
meaningCheck: (value) => meaningCheckEnabled({ fields: { "meaning check": value } }),
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
/** The `source floor` forms: `pattern` reads one, `listed` forms carry a comma list. */
|
|
60
|
+
export const SOURCE_FLOOR_RULES = Object.freeze([
|
|
61
|
+
Object.freeze({ kind: "standard", form: "standard", pattern: /^standard$/iu, listed: false }),
|
|
62
|
+
Object.freeze({ kind: "primary", form: "primary only", pattern: /^primary only$/iu, listed: false }),
|
|
63
|
+
Object.freeze({ kind: "pointed", form: "pointed only: <list>", pattern: /^pointed only:\s*(.+)$/iu, listed: true }),
|
|
64
|
+
Object.freeze({ kind: "classes", form: "classes: <list>", pattern: /^classes:\s*(.+)$/iu, listed: true }),
|
|
65
|
+
])
|
|
66
|
+
const lineOf = (text, at) => text.slice(0, at).split("\n").length
|
|
67
|
+
const listOf = (text) => text.split(",").map((entry) => entry.trim()).filter(Boolean)
|
|
68
|
+
|
|
69
|
+
/** brief.md from --brief, else the session's; a usage error when neither is known. */
|
|
70
|
+
export function briefPathOf(flags) {
|
|
71
|
+
if (flags.brief !== undefined && flags.brief !== true) return resolve(flags.brief)
|
|
72
|
+
const dir = flags["session-dir"] ?? process.env.SESSION_DIR
|
|
73
|
+
if (!dir || dir === true) throw new CliError("--brief is required without --session-dir (or SESSION_DIR)")
|
|
74
|
+
return join(resolve(dir), "brief.md")
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @returns {{ parsed: object, refusal: null } | { parsed: null, refusal: { json: object, summary: string, exitCode: 1 } }}
|
|
79
|
+
*/
|
|
80
|
+
export function lockedBrief(command, flags) {
|
|
81
|
+
const path = briefPathOf(flags)
|
|
82
|
+
const lock = requireLockedBrief(path)
|
|
83
|
+
if (lock.exitCode === 2) throw new CliError(`${path}: ${lock.errors.join("; ")}`)
|
|
84
|
+
if (lock.locked) return { parsed: lock.parsed, refusal: null }
|
|
85
|
+
const json = { command, status: "fail", exitCode: 1, defects: lock.defects }
|
|
86
|
+
return { parsed: null, refusal: { json, summary: `${command}: ${lock.defects[0].message}`, exitCode: 1 } }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The source floor as a rule, or null for `standard`; a usage error for any other text.
|
|
91
|
+
* @returns {null | { kind: "primary" } | { kind: "pointed" | "classes", list: string[] }}
|
|
92
|
+
*/
|
|
93
|
+
export function parseSourceFloor(value) {
|
|
94
|
+
const text = value.trim()
|
|
95
|
+
for (const rule of SOURCE_FLOOR_RULES) {
|
|
96
|
+
const match = rule.pattern.exec(text)
|
|
97
|
+
if (!match) continue
|
|
98
|
+
if (rule.kind === "standard") return null
|
|
99
|
+
return rule.listed ? { kind: rule.kind, list: listOf(match[1]) } : { kind: rule.kind }
|
|
100
|
+
}
|
|
101
|
+
throw new CliError(`source floor "${value}" is not ${SOURCE_FLOOR_RULES.map((rule) => rule.form).join(", ")}`)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function hostOf(url) {
|
|
105
|
+
return typeof url === "string" && URL.canParse(url) ? new URL(url).hostname.toLowerCase() : null
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Whether one source (id and meta.json) satisfies a parsed floor rule. */
|
|
109
|
+
export function sourceInsideFloor(rule, sourceId, meta) {
|
|
110
|
+
if (rule.kind === "primary") return meta.provenance === "primary"
|
|
111
|
+
if (rule.kind === "pointed") return rule.list.includes(sourceId) || rule.list.includes(meta.url)
|
|
112
|
+
const host = hostOf(meta.url)
|
|
113
|
+
return host !== null && rule.list.some((entry) => host === entry.toLowerCase() || host.endsWith(`.${entry.toLowerCase()}`))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** The fields fact fill consumes: the fallback form and whether a translation binds to its quote. */
|
|
117
|
+
export function fillPolicy(parsed) {
|
|
118
|
+
const field = (name) => parsed.fields[name]
|
|
119
|
+
return {
|
|
120
|
+
fallback: FIDELITY_VALUE_RULES.fallback(field("fallback")),
|
|
121
|
+
translations: FIDELITY_VALUE_RULES.translations(field("languages")),
|
|
122
|
+
derivedNone: FIDELITY_VALUE_RULES.derivedNone(field("derived numbers")),
|
|
123
|
+
floor: parseSourceFloor(field("source floor")),
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Source ids behind a row: its own, or for a derived row its operands' sources; null marks a leaf with no source. */
|
|
128
|
+
function sourcesOf(row, rows, seen = new Set()) {
|
|
129
|
+
if (seen.has(row.fact_id)) return []
|
|
130
|
+
seen.add(row.fact_id)
|
|
131
|
+
if (row.source_id) return [row.source_id]
|
|
132
|
+
const inputs = [...(row.operands ?? []), ...(row.derived_from ?? [])]
|
|
133
|
+
if (inputs.length === 0) return [null]
|
|
134
|
+
return inputs.flatMap((id) => (rows.has(id) ? sourcesOf(rows.get(id), rows, seen) : [null]))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** sha256 of the locked `## Fidelity` block normalized: the seven fields in FIDELITY_FIELDS order, `field: value` per line. */
|
|
138
|
+
export const briefHashOf = (parsed) => sha256Of(FIDELITY_FIELDS.map((field) => `${field}: ${parsed.fields[field]}`).join("\n"))
|
|
139
|
+
|
|
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
|
+
export function briefBindingErrors(map, mapPath, parsed) {
|
|
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
|
+
}
|
|
146
|
+
|
|
147
|
+
function metaOf(sessionDir, sourceId, cache) {
|
|
148
|
+
if (!cache.has(sourceId)) {
|
|
149
|
+
const file = join(sessionDir, "sources", sourceId, "meta.json")
|
|
150
|
+
cache.set(sourceId, existsSync(file) ? JSON.parse(readFileSync(file, "utf8")) : { url: null, provenance: "unknown" })
|
|
151
|
+
}
|
|
152
|
+
return cache.get(sourceId)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const defect = (code, pointer, text, message) => ({ code, token: pointer.token, line: lineOf(text, pointer.start), message, severity: DEFECT_CODES[code].severity, hint: DEFECT_CODES[code].hint })
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Refusals for the placeholders of one fill source (or the data-fact elements of one edition at
|
|
159
|
+
* verify). Sets each referenced row's outside_floor to this evaluation and reports whether any
|
|
160
|
+
* cached value changed.
|
|
161
|
+
* @param {{ token: string, start: number, parsed: { id?: string } }[]} found findPlaceholders output
|
|
162
|
+
* @returns {{ errors: object[], cacheChanged: boolean }}
|
|
163
|
+
*/
|
|
164
|
+
export function policyErrors(text, found, ledger, sessionDir, policy) {
|
|
165
|
+
const rows = new Map(ledger.rows.map((row) => [row.fact_id, row]))
|
|
166
|
+
const metas = new Map()
|
|
167
|
+
const errors = []
|
|
168
|
+
let cacheChanged = false
|
|
169
|
+
for (const pointer of found) {
|
|
170
|
+
const row = rows.get(pointer.parsed?.id)
|
|
171
|
+
if (row === undefined || row.status !== "active") continue
|
|
172
|
+
if (policy.derivedNone && FIDELITY_VALUE_RULES.derivedRefused(row.kind)) {
|
|
173
|
+
errors.push(defect("derived_not_allowed", pointer, text, `${row.fact_id} is ${row.kind}; the brief records derived numbers: none`))
|
|
174
|
+
}
|
|
175
|
+
const sources = sourcesOf(row, rows)
|
|
176
|
+
const outside = policy.floor === null ? [] : [...new Set(sources.filter((id) => id === null || !sourceInsideFloor(policy.floor, id, metaOf(sessionDir, id, metas))))]
|
|
177
|
+
if (row.outside_floor !== outside.length > 0) cacheChanged = true
|
|
178
|
+
row.outside_floor = outside.length > 0
|
|
179
|
+
const named = outside.map((id) => id ?? "no source").join(", ")
|
|
180
|
+
if (outside.length > 0) errors.push(defect("source_outside_floor", pointer, text, `${row.fact_id} rests on ${named}, outside the source floor`))
|
|
181
|
+
}
|
|
182
|
+
return { errors, cacheChanged }
|
|
183
|
+
}
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Availability is checked before hash/locator checks, including the raw-byte snapshot contract.
|
|
2
|
+
// One code (snapshot_hash_mismatch) for every unreadable or changed snapshot; the message names the cause.
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
import { sha256Of } from "./cli-support.mjs"
|
|
6
|
+
|
|
7
|
+
function read(dir, file) {
|
|
8
|
+
try {
|
|
9
|
+
return readFileSync(join(dir, file))
|
|
10
|
+
} catch (error) {
|
|
11
|
+
if (error.code === "ENOENT") throw new Error(`${file} is missing`)
|
|
12
|
+
if (error.code === "EISDIR") throw new Error(`${file} is not a readable file`)
|
|
13
|
+
throw new Error(`${file} is unreadable (${error.code ?? error.message})`)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Why the snapshot behind `row` cannot be trusted, or null when it reads and matches its pinned hash. */
|
|
18
|
+
export function snapshotCause(sourcesDir, row) {
|
|
19
|
+
if (!sourcesDir) return "has no sources directory"
|
|
20
|
+
const dir = join(sourcesDir, row.source_id)
|
|
21
|
+
try {
|
|
22
|
+
let meta
|
|
23
|
+
try {
|
|
24
|
+
meta = JSON.parse(read(dir, "meta.json").toString("utf8"))
|
|
25
|
+
} catch (error) {
|
|
26
|
+
throw error instanceof SyntaxError ? new Error("meta.json is not valid JSON") : error
|
|
27
|
+
}
|
|
28
|
+
if (!meta || typeof meta.sha256 !== "string") return "has invalid snapshot metadata (meta.json has no sha256)"
|
|
29
|
+
const needsRaw = ["bytes", "utf8-bytes"].includes(row.locator?.unit) && meta.kind !== "converted-only"
|
|
30
|
+
const file = existsSync(join(dir, "raw.bin")) || needsRaw ? "raw.bin" : "text.txt"
|
|
31
|
+
return sha256Of(read(dir, file)) === meta.sha256 ? null : `hash mismatch: ${file} changed after its snapshot was pinned`
|
|
32
|
+
} catch (error) {
|
|
33
|
+
return error.message
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Flags each cited row whose own snapshot is unreadable; returns those source ids so healthy sources keep their metadata. */
|
|
38
|
+
export function unreadableSnapshots(ctx, rows, cited, sourcesDir, out) {
|
|
39
|
+
const unreadable = new Set()
|
|
40
|
+
for (const [id, node] of cited) {
|
|
41
|
+
const row = rows.get(id)
|
|
42
|
+
if (!row?.source_id) continue
|
|
43
|
+
const cause = snapshotCause(sourcesDir, row)
|
|
44
|
+
if (cause === null) continue
|
|
45
|
+
unreadable.add(row.source_id)
|
|
46
|
+
out.push({ ...ctx.defect("snapshot_hash_mismatch", node, `${id}: snapshot ${row.source_id} ${cause}`), fact_id: id })
|
|
47
|
+
}
|
|
48
|
+
return unreadable
|
|
49
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Source integrity and raw quote anchoring, shared by the fidelity value checks. Fails closed:
|
|
2
|
+
// a cited source-backed row whose sources dir, meta.json, raw.bin, or text.txt cannot be read, or
|
|
3
|
+
// whose snapshot no longer matches its pin, gets snapshot_hash_mismatch (with fact_id) naming the
|
|
4
|
+
// cause; healthy sources keep their metadata and checks. flag is (code, node, message, extra).
|
|
5
|
+
import { join } from "node:path"
|
|
6
|
+
|
|
7
|
+
import { proseText } from "./html-lite.mjs"
|
|
8
|
+
import { reanchor } from "./raw-locator.mjs"
|
|
9
|
+
import { readSnapshot } from "./source-view.mjs"
|
|
10
|
+
|
|
11
|
+
const short = (hash) => String(hash ?? "none").slice(0, 12)
|
|
12
|
+
|
|
13
|
+
function snapshotOf(sourcesDir, id) {
|
|
14
|
+
if (!sourcesDir) throw new Error("has no sources directory")
|
|
15
|
+
return readSnapshot(join(sourcesDir, id))
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const blocked = (flag, cited, row, id, cause) => flag("snapshot_hash_mismatch", cited.get(row.fact_id), `${row.fact_id}: snapshot ${id} ${cause}`, { fact_id: row.fact_id })
|
|
19
|
+
|
|
20
|
+
/** One source's checks for the rows that cite it; byte-located rows are re-anchored on the view. */
|
|
21
|
+
function rowChecks(snap, id, referenced, cited, flag) {
|
|
22
|
+
const converted = snap.view.mode === "converted"
|
|
23
|
+
for (const row of referenced) {
|
|
24
|
+
const node = cited.get(row.fact_id)
|
|
25
|
+
// No locator unit marks a legacy text.txt row: it holds only on the legacy snapshot it pins.
|
|
26
|
+
if (row.locator?.unit === undefined) {
|
|
27
|
+
if (snap.view.mode !== "legacy") blocked(flag, cited, row, id, `is no longer the legacy text.txt snapshot the row was located on (now ${snap.view.mode}); add the row again from the new snapshot`)
|
|
28
|
+
else if (row.snapshot_sha256 !== snap.sha256) blocked(flag, cited, row, id, `holds ${short(snap.sha256)} but the legacy row pins ${short(row.snapshot_sha256)}`)
|
|
29
|
+
continue
|
|
30
|
+
}
|
|
31
|
+
if (snap.view.mode === "legacy") {
|
|
32
|
+
blocked(flag, cited, row, id, "raw.bin is missing (the row is byte-located)")
|
|
33
|
+
continue
|
|
34
|
+
}
|
|
35
|
+
if (converted && row.kind !== "quote") {
|
|
36
|
+
flag("snapshot_not_raw", node, `${row.fact_id} cannot measure converted-only source ${id}`)
|
|
37
|
+
continue
|
|
38
|
+
}
|
|
39
|
+
if (row.snapshot_sha256 !== snap.sha256) {
|
|
40
|
+
blocked(flag, cited, row, id, `holds ${short(snap.sha256)} but the row pins ${short(row.snapshot_sha256)}; run source refresh ${id} to re-anchor`)
|
|
41
|
+
continue
|
|
42
|
+
}
|
|
43
|
+
const anchor = row.locator.artifact === snap.view.artifact ? reanchor(snap.view, row) : null
|
|
44
|
+
if (!anchor) flag("quote_not_verbatim", node, `${row.fact_id}: no unique exact quote and context in ${id}/${snap.view.artifact}`)
|
|
45
|
+
else if (anchor.relocated) flag("relocated", node, `${row.fact_id}: ${row.locator.char_start}:${row.locator.char_end} -> ${anchor.start}:${anchor.end} (bytes of ${id}/${snap.view.artifact})`)
|
|
46
|
+
if (converted && row.kind === "quote") flag("quote_converted_only", node, `${row.fact_id} quotes converted-only source ${id}, not the original fetched bytes`)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function sourceChecks(ctx, rows, cited, sourcesDir, flag, unreadable = new Set()) {
|
|
51
|
+
const referenced = new Map()
|
|
52
|
+
for (const key of cited.keys()) {
|
|
53
|
+
const row = rows.get(key)
|
|
54
|
+
if (row?.source_id) referenced.set(row.source_id, [...(referenced.get(row.source_id) ?? []), row])
|
|
55
|
+
}
|
|
56
|
+
const ids = new Set(referenced.keys())
|
|
57
|
+
for (const match of proseText(ctx.body).matchAll(/\[S(\d+)\]/g)) ids.add(`S${match[1]}`)
|
|
58
|
+
const meta = new Map()
|
|
59
|
+
for (const id of ids) {
|
|
60
|
+
if (unreadable.has(id)) continue
|
|
61
|
+
const backing = referenced.get(id) ?? []
|
|
62
|
+
let snap
|
|
63
|
+
try {
|
|
64
|
+
snap = snapshotOf(sourcesDir, id)
|
|
65
|
+
} catch (error) {
|
|
66
|
+
// A source only named in prose ([S<n>]) backs no row: an absent snapshot is not a finding.
|
|
67
|
+
for (const row of backing) blocked(flag, cited, row, id, error.message)
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
meta.set(id, snap.meta)
|
|
71
|
+
if (snap.drift.length === 0) rowChecks(snap, id, backing, cited, flag)
|
|
72
|
+
else if (backing.length === 0) flag("snapshot_hash_mismatch", ctx.body, `snapshot ${id} ${snap.drift.join("; ")}`)
|
|
73
|
+
else for (const row of backing) blocked(flag, cited, row, id, snap.drift.join("; "))
|
|
74
|
+
}
|
|
75
|
+
return meta
|
|
76
|
+
}
|
|
@@ -13,9 +13,12 @@
|
|
|
13
13
|
// so each entry, in array order, is matched to the next untraced occurrence of its rendered text in
|
|
14
14
|
// document order that is a whole numeral and reads as the entry's row there (fits: "1,237 million"
|
|
15
15
|
// is not F2's 1,237), and strips that one occurrence only. A copy of the same text that no entry
|
|
16
|
-
// accounts for stays in the scan; an entry whose text is gone strips nothing.
|
|
16
|
+
// accounts for stays in the scan; an entry whose text is gone strips nothing. A derived or
|
|
17
|
+
// converted entry is located under the bracket grammar of fidelity-originals.mjs (renderLocator).
|
|
17
18
|
|
|
19
|
+
import { PROSE_TAGS } from "./html-lite.mjs"
|
|
18
20
|
import { NOT_CLAIMS } from "./numerals.mjs"
|
|
21
|
+
import { sourcesClaims, textClaims } from "./sources-citation.mjs"
|
|
19
22
|
|
|
20
23
|
/** Structural numerals: versions, figure/table/section references, times (NOT_CLAIMS 7-11), pages, citations, placeholders. */
|
|
21
24
|
export const STRUCTURAL = [...NOT_CLAIMS.slice(7, 12), /\b(?:p|pp|page|pages)\.?\s*\d+(?:\s*[-\u2013]\s*\d+)?/gi, /\[(?:S|Source\s*)\d+\]|\[\^\d+\]/gi, /\{\{[^}]*\}\}/g]
|
|
@@ -29,7 +32,12 @@ function ownerTexts(ctx, charts) {
|
|
|
29
32
|
const owners = new Map()
|
|
30
33
|
for (const entry of ctx.texts) {
|
|
31
34
|
const kind = ctx.kindOf(entry.scope)
|
|
32
|
-
if (kind === "
|
|
35
|
+
if (kind === "toc") continue
|
|
36
|
+
if (kind === "sources") {
|
|
37
|
+
if (!PROSE_TAGS.has(entry.owner.tag)) owners.set(entry.owner, `${owners.get(entry.owner) ?? ""} ${textClaims(entry.node.text).join(" ")}`)
|
|
38
|
+
else if (!owners.has(entry.owner)) owners.set(entry.owner, sourcesClaims(entry.owner).join(" "))
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
33
41
|
if (charts.has(ctx.util.closest(entry.node, (cur) => cur.tag === "figure"))) continue
|
|
34
42
|
const hidden = ctx.util.closest(entry.node, (cur) => cur.attrs["data-fact"] !== undefined || cur.attrs["data-translates"] !== undefined || cur.tag === "q" || cur.tag === "blockquote")
|
|
35
43
|
owners.set(entry.owner, (owners.get(entry.owner) ?? "") + (hidden ? ` ${TRACED} ` : entry.node.text))
|
|
@@ -52,8 +60,13 @@ function scanOwner(ctx, owner, raw, flag) {
|
|
|
52
60
|
for (const match of rest.matchAll(NUMERAL)) flag("untraceable_number", owner, `number "${match[0]}" has no ledger row: "${clip(rest.slice(Math.max(0, match.index - 30), match.index + 30))}"`)
|
|
53
61
|
}
|
|
54
62
|
|
|
63
|
+
const plainSite = (entry, text, from) => {
|
|
64
|
+
const hit = text.indexOf(entry.rendered, from)
|
|
65
|
+
return hit === -1 ? null : [hit, hit + entry.rendered.length]
|
|
66
|
+
}
|
|
67
|
+
|
|
55
68
|
/** Strip each fill-map entry at its own fill site: the next untraced occurrence after the previous entry's site. */
|
|
56
|
-
function traceFills(owners, fillMap, fits) {
|
|
69
|
+
function traceFills(owners, fillMap, fits, locate = plainSite) {
|
|
57
70
|
const keys = [...owners.keys()]
|
|
58
71
|
const texts = [...owners.values()]
|
|
59
72
|
let [index, from] = [0, 0]
|
|
@@ -61,12 +74,12 @@ function traceFills(owners, fillMap, fits) {
|
|
|
61
74
|
const { rendered } = entry
|
|
62
75
|
if (!rendered) continue
|
|
63
76
|
for (let i = index, at = from; i < texts.length; i += 1, at = 0) {
|
|
64
|
-
let hit = texts[i]
|
|
65
|
-
const glued = (
|
|
66
|
-
while (hit
|
|
67
|
-
if (hit
|
|
68
|
-
texts[i] = `${texts[i].slice(0, hit)}${TRACED}${texts[i].slice(hit
|
|
69
|
-
;[index, from] = [i, hit + TRACED.length]
|
|
77
|
+
let hit = locate(entry, texts[i], at)
|
|
78
|
+
const glued = ([start, end]) => (/^\d/.test(rendered) && /\d/.test(texts[i][start - 1] ?? "")) || (/\d$/.test(rendered) && /\d/.test(texts[i][end] ?? ""))
|
|
79
|
+
while (hit && (glued(hit) || !fits(entry, texts[i].slice(hit[0])))) hit = locate(entry, texts[i], hit[0] + 1)
|
|
80
|
+
if (!hit) continue
|
|
81
|
+
texts[i] = `${texts[i].slice(0, hit[0])}${TRACED}${texts[i].slice(hit[1])}`
|
|
82
|
+
;[index, from] = [i, hit[0] + TRACED.length]
|
|
70
83
|
break
|
|
71
84
|
}
|
|
72
85
|
}
|
|
@@ -79,7 +92,8 @@ function traceFills(owners, fillMap, fits) {
|
|
|
79
92
|
* @param {{ fact_id: string, rendered: string, start?: number, end?: number }[] | null} fillMap Markdown fill map, or null
|
|
80
93
|
* @param {(code: string, node: object, message: string) => void} flag
|
|
81
94
|
* @param {(entry: object, tail: string) => boolean} fits whether the text from a candidate site on reads as the entry's row
|
|
95
|
+
* @param {(entry: object, text: string, from: number) => [number, number] | null} [locate] the next candidate site of an entry's text
|
|
82
96
|
*/
|
|
83
|
-
export function traceChecks(ctx, charts, fillMap, flag, fits) {
|
|
84
|
-
for (const [owner, text] of traceFills(ownerTexts(ctx, charts), fillMap, fits)) scanOwner(ctx, owner, text, flag)
|
|
97
|
+
export function traceChecks(ctx, charts, fillMap, flag, fits, locate) {
|
|
98
|
+
for (const [owner, text] of traceFills(ownerTexts(ctx, charts), fillMap, fits, locate)) scanOwner(ctx, owner, text, flag)
|
|
85
99
|
}
|