oh-my-opencode 5.1.48 → 5.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/refusal-rule.d.ts +7 -0
- package/dist/cli/index.js +1194 -752
- package/dist/cli/service-auth/accounts-gate.d.ts +11 -0
- package/dist/cli/service-auth/commands.d.ts +2 -4
- package/dist/cli/service-auth/index.d.ts +1 -1
- package/dist/cli/service-auth/keystore.d.ts +26 -2
- package/dist/cli/service-auth/protocol.d.ts +7 -1
- package/dist/cli/service-auth/session.d.ts +6 -0
- package/dist/cli/service-auth/signed-out-marker.d.ts +11 -0
- package/dist/cli-node/index.js +1194 -752
- package/dist/config/schema/hooks.d.ts +1 -0
- package/dist/create-hooks.d.ts +1 -0
- package/dist/features/boulder-state/storage.d.ts +1 -1
- package/dist/features/boulder-state/types.d.ts +1 -1
- package/dist/hooks/atlas/idle-continuation.d.ts +2 -1
- package/dist/hooks/atlas/types.d.ts +9 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/momus-review-persistence/hook.d.ts +19 -0
- package/dist/hooks/momus-review-persistence/index.d.ts +1 -0
- package/dist/hooks/momus-review-persistence/review-resolution.d.ts +20 -0
- package/dist/hooks/prometheus-md-only/constants.d.ts +2 -0
- package/dist/hooks/prometheus-md-only/fixtures/denial-log.d.ts +1 -0
- package/dist/hooks/prometheus-md-only/fixtures/guarded-hashline.d.ts +1 -0
- package/dist/hooks/prometheus-md-only/fixtures/invoke-before.d.ts +7 -0
- package/dist/hooks/prometheus-md-only/fixtures/opencode-patch-parser.d.ts +2 -0
- package/dist/hooks/prometheus-md-only/hook.d.ts +5 -1
- package/dist/hooks/prometheus-md-only/path-policy.d.ts +2 -9
- package/dist/hooks/prometheus-md-only/vendor/opencode-patch-parser.d.ts +22 -0
- package/dist/index.js +2322 -1045
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -0
- package/dist/plugin/hooks/create-tool-guard-hooks.d.ts +2 -1
- package/dist/shared/posthog-activity-state.d.ts +1 -0
- package/dist/shared/posthog.d.ts +1 -2
- package/dist/shared/resolve-file-write-path.d.ts +6 -0
- package/dist/skills/ast-grep/README.md +1 -1
- package/dist/skills/ast-grep/install.ps1 +5 -3
- package/dist/skills/ast-grep/install.sh +4 -5
- package/dist/skills/ast-grep/references/install.md +2 -2
- package/dist/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
- package/dist/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/dist/skills/coding-agent-sessions/SKILL.md +11 -11
- package/dist/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/dist/skills/coding-agent-sessions/references/codex.md +4 -4
- package/dist/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/dist/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/dist/skills/frontend/ATTRIBUTION.md +1 -1
- package/dist/skills/frontend/SKILL.md +34 -12
- package/dist/skills/frontend/references/design/README.md +2 -2
- package/dist/skills/frontend/references/design/_INDEX.md +2 -1
- package/dist/skills/frontend/references/design/component-catalogs.md +14 -10
- package/dist/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/dist/skills/frontend/references/design/grounding.md +29 -0
- package/dist/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/dist/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/dist/skills/ultimate-browsing/SKILL.md +5 -4
- package/dist/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/dist/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/dist/skills/ultimate-browsing/engine/summary.py +1 -1
- package/dist/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/dist/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/dist/skills/ulw-research/AGENTS.md +8 -4
- package/dist/skills/ulw-research/SKILL.md +7 -316
- package/dist/skills/ulw-research/references/deliverable-phase.md +23 -21
- package/dist/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/dist/skills/ulw-research/references/fact-ledger.md +42 -8
- package/dist/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/dist/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/dist/skills/ulw-research/references/report-gates.md +12 -5
- package/dist/skills/ulw-research/references/research-collection.md +195 -0
- package/dist/skills/ulw-research/references/research-delivery.md +123 -0
- package/dist/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/dist/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
- package/dist/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
- package/dist/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
- package/dist/skills/ulw-research/scripts/contracts.mjs +13 -4
- package/dist/skills/ulw-research/scripts/contracts.test.ts +6 -110
- package/dist/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
- package/dist/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
- package/dist/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
- package/dist/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/dist/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
- package/dist/skills/ulw-research/scripts/copy-returns-polish.test.ts +4 -3
- package/dist/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/dist/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/dist/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
- package/dist/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
- package/dist/skills/ulw-research/scripts/entities.mjs +13 -4
- package/dist/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
- package/dist/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
- package/dist/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
- package/dist/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
- package/dist/skills/ulw-research/scripts/fact-commands.mjs +35 -16
- package/dist/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
- package/dist/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/dist/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
- package/dist/skills/ulw-research/scripts/fact-fill.mjs +6 -4
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/dist/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
- package/dist/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
- package/dist/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/dist/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
- package/dist/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
- package/dist/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
- package/dist/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/dist/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/dist/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/dist/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/dist/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
- package/dist/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/dist/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/dist/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/dist/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/dist/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/dist/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/dist/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
- package/dist/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/dist/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
- package/dist/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/dist/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/dist/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/dist/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/dist/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
- package/dist/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/dist/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
- package/dist/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/dist/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/dist/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
- package/dist/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/dist/skills/ulw-research/scripts/numerals.test.ts +3 -0
- package/dist/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/dist/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
- package/dist/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/dist/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
- package/dist/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/dist/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/dist/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
- package/dist/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
- package/dist/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/dist/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/dist/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
- package/dist/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
- package/dist/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
- package/dist/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
- package/dist/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
- package/dist/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/dist/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/dist/skills/ulw-research/scripts/report-tools.test.ts +5 -800
- package/dist/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
- package/dist/skills/ulw-research/scripts/source-commands.mjs +79 -26
- package/dist/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
- package/dist/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/dist/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/dist/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
- package/dist/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/dist/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/dist/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
- package/dist/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
- package/dist/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
- package/dist/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
- package/dist/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
- package/dist/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
- package/dist/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
- package/dist/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
- package/dist/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
- package/dist/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
- package/dist/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
- package/dist/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
- package/dist/skills/ulw-research/scripts/units.mjs +14 -3
- package/dist/skills/ulw-research/scripts/units.test.ts +10 -0
- package/dist/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
- package/dist/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
- package/dist/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
- package/dist/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
- package/dist/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
- package/dist/tools/delegate-task/executor-types.d.ts +1 -0
- package/dist/tools/delegate-task/sync-session-existence.d.ts +4 -0
- package/dist/tools/delegate-task/types.d.ts +1 -0
- package/dist/tools/skill/description-formatter.d.ts +2 -1
- package/dist/tools/skill/types.d.ts +6 -0
- package/dist/tui.js +131 -33
- package/package.json +18 -15
- package/packages/git-bash-mcp/package.json +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/README.md +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/SKILL.md +7 -7
- package/packages/omo-codex/plugin/components/lcx/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +2 -2
- package/packages/omo-codex/plugin/components/telemetry/README.md +2 -2
- package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +52 -15
- package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +52 -19
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/src/codex-hook.ts +2 -9
- package/packages/omo-codex/plugin/components/telemetry/src/posthog.ts +4 -13
- package/packages/omo-codex/plugin/components/telemetry/src/product-identity.ts +0 -2
- package/packages/omo-codex/plugin/components/telemetry/test/codex-hook-diagnostics.test.ts +0 -3
- package/packages/omo-codex/plugin/components/telemetry/test/codex-hook.test.ts +3 -8
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +3 -0
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +16 -16
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/shared/package.json +1 -1
- package/packages/omo-codex/plugin/skills/ast-grep/README.md +1 -1
- package/packages/omo-codex/plugin/skills/ast-grep/install.ps1 +5 -3
- package/packages/omo-codex/plugin/skills/ast-grep/install.sh +4 -5
- package/packages/omo-codex/plugin/skills/ast-grep/references/install.md +2 -2
- package/packages/omo-codex/plugin/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/SKILL.md +11 -11
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/references/codex.md +4 -4
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/packages/omo-codex/plugin/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +1 -1
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +34 -12
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +2 -1
- package/packages/omo-codex/plugin/skills/frontend/references/design/component-catalogs.md +14 -10
- package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/packages/omo-codex/plugin/skills/frontend/references/design/grounding.md +29 -0
- package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/packages/omo-codex/plugin/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +51 -55
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +7 -7
- package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +5 -4
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/summary.py +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/packages/omo-codex/plugin/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +3 -0
- package/packages/omo-codex/plugin/skills/ulw-research/AGENTS.md +8 -4
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +7 -316
- package/packages/omo-codex/plugin/skills/ulw-research/references/deliverable-phase.md +23 -21
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/packages/omo-codex/plugin/skills/ulw-research/references/fact-ledger.md +42 -8
- package/packages/omo-codex/plugin/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/report-gates.md +12 -5
- package/packages/omo-codex/plugin/skills/ulw-research/references/research-collection.md +195 -0
- package/packages/omo-codex/plugin/skills/ulw-research/references/research-delivery.md +123 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/contracts.mjs +13 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/entities.mjs +13 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-commands.mjs +35 -16
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-fill.mjs +6 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-commands.mjs +79 -26
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/packages/omo-codex/plugin/skills/ulw-research/scripts/units.mjs +14 -3
- package/packages/omo-codex/plugin/test/payload-equivalence.test.mjs +6 -4
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -3
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +338 -237
- package/packages/prompts-core/prompts/ultrawork/codex.md +3 -0
- package/packages/shared-skills/skill-source-filter.mjs +6 -2
- package/packages/shared-skills/skills/ast-grep/README.md +1 -1
- package/packages/shared-skills/skills/ast-grep/install.ps1 +5 -3
- package/packages/shared-skills/skills/ast-grep/install.sh +4 -5
- package/packages/shared-skills/skills/ast-grep/references/install.md +2 -2
- package/packages/shared-skills/skills/ast-grep/scripts/ast_grep_helper.py +13 -3
- package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
- package/packages/shared-skills/skills/coding-agent-sessions/AGENTS.md +5 -4
- package/packages/shared-skills/skills/coding-agent-sessions/SKILL.md +11 -11
- package/packages/shared-skills/skills/coding-agent-sessions/references/all-platforms.md +2 -2
- package/packages/shared-skills/skills/coding-agent-sessions/references/codex.md +4 -4
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/agent_sessions/cli.py +1 -1
- package/packages/shared-skills/skills/coding-agent-sessions/scripts/find-agent-sessions.py +6 -3
- package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +1 -1
- package/packages/shared-skills/skills/frontend/SKILL.md +34 -12
- package/packages/shared-skills/skills/frontend/references/design/README.md +2 -2
- package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +2 -1
- package/packages/shared-skills/skills/frontend/references/design/component-catalogs.md +14 -10
- package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +19 -6
- package/packages/shared-skills/skills/frontend/references/design/grounding.md +29 -0
- package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/design_system.py +2 -0
- package/packages/shared-skills/skills/frontend/references/ui-ux-db/scripts/search.py +2 -0
- package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +5 -4
- package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +2 -2
- package/packages/shared-skills/skills/ultimate-browsing/engine/__main__.py +9 -6
- package/packages/shared-skills/skills/ultimate-browsing/engine/summary.py +1 -1
- package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +4 -4
- package/packages/shared-skills/skills/ultimate-browsing/scripts/extract_cookies.py +4 -2
- package/packages/shared-skills/skills/ulw-research/AGENTS.md +8 -4
- package/packages/shared-skills/skills/ulw-research/SKILL.md +7 -316
- package/packages/shared-skills/skills/ulw-research/references/deliverable-phase.md +23 -21
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger-grammar.md +7 -3
- package/packages/shared-skills/skills/ulw-research/references/fact-ledger.md +42 -8
- package/packages/shared-skills/skills/ulw-research/references/fidelity-claim-kinds.md +1 -1
- package/packages/shared-skills/skills/ulw-research/references/markdown-blocks.md +12 -0
- package/packages/shared-skills/skills/ulw-research/references/report-gates.md +12 -5
- package/packages/shared-skills/skills/ulw-research/references/research-collection.md +195 -0
- package/packages/shared-skills/skills/ulw-research/references/research-delivery.md +123 -0
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.mjs +33 -5
- package/packages/shared-skills/skills/ulw-research/scripts/claim-kinds.test.ts +37 -2
- package/packages/shared-skills/skills/ulw-research/scripts/claim-prefix.test.ts +76 -0
- package/packages/shared-skills/skills/ulw-research/scripts/contracts-validation.test.ts +83 -0
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.mjs +13 -4
- package/packages/shared-skills/skills/ulw-research/scripts/contracts.test.ts +6 -110
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-altered.test.ts +154 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-originals.test.ts +297 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe-classes.test.ts +361 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.mjs +229 -0
- package/packages/shared-skills/skills/ulw-research/scripts/conversion-probe.test.ts +218 -0
- package/packages/shared-skills/skills/ulw-research/scripts/copy-returns-polish.test.ts +4 -3
- package/packages/shared-skills/skills/ulw-research/scripts/css-hiding.mjs +158 -0
- package/packages/shared-skills/skills/ulw-research/scripts/css-lite.mjs +4 -2
- package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier-meaning.test.ts +186 -0
- package/packages/shared-skills/skills/ulw-research/scripts/delivery-barrier.test.ts +40 -87
- package/packages/shared-skills/skills/ulw-research/scripts/entities.mjs +13 -4
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-fill.mjs +11 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-meaning.mjs +13 -9
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-polish.mjs +40 -18
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands-verify.mjs +25 -8
- package/packages/shared-skills/skills/ulw-research/scripts/fact-commands.mjs +35 -16
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive-2.test.ts +149 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.mjs +7 -2
- package/packages/shared-skills/skills/ulw-research/scripts/fact-derive.test.ts +7 -192
- package/packages/shared-skills/skills/ulw-research/scripts/fact-fill.mjs +6 -4
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.mjs +6 -7
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-kinds.test.ts +2 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-ledger-legacy.test.ts +2 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fact-locate.mjs +15 -9
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions-2.test.ts +149 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-dimensions.test.ts +4 -168
- package/packages/shared-skills/skills/ulw-research/scripts/fact-unit-rules.test.ts +3 -229
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-claims.mjs +4 -3
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-csv.mjs +32 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-lexicon.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-originals.mjs +161 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-policy.mjs +4 -4
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-records.mjs +81 -14
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-snapshot-readability.mjs +49 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-sources.mjs +76 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-trace.mjs +25 -11
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-value-unit.mjs +202 -0
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.mjs +40 -39
- package/packages/shared-skills/skills/ulw-research/scripts/fidelity-values.test.ts +8 -29
- package/packages/shared-skills/skills/ulw-research/scripts/gates-fidelity.mjs +8 -6
- package/packages/shared-skills/skills/ulw-research/scripts/gates-static-headings.test.ts +17 -0
- package/packages/shared-skills/skills/ulw-research/scripts/gates-static.mjs +10 -1
- package/packages/shared-skills/skills/ulw-research/scripts/html-lite.mjs +6 -2
- package/packages/shared-skills/skills/ulw-research/scripts/html-markup.mjs +2 -2
- package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.mjs +140 -0
- package/packages/shared-skills/skills/ulw-research/scripts/html-text-map.test.ts +148 -0
- package/packages/shared-skills/skills/ulw-research/scripts/markdown-closers.mjs +126 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite-boundaries.test.ts +46 -0
- package/packages/shared-skills/skills/ulw-research/scripts/md-lite.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.mjs +33 -0
- package/packages/shared-skills/skills/ulw-research/scripts/number-grammar.test.ts +36 -0
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.mjs +18 -7
- package/packages/shared-skills/skills/ulw-research/scripts/numerals.test.ts +3 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.mjs +66 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome-start.test.ts +109 -0
- package/packages/shared-skills/skills/ulw-research/scripts/outcome.mjs +11 -22
- package/packages/shared-skills/skills/ulw-research/scripts/page-source-locator.test.ts +187 -0
- package/packages/shared-skills/skills/ulw-research/scripts/placeholder.mjs +18 -6
- package/packages/shared-skills/skills/ulw-research/scripts/polish-apply.mjs +3 -2
- package/packages/shared-skills/skills/ulw-research/scripts/polish-markup.test.ts +15 -1
- package/packages/shared-skills/skills/ulw-research/scripts/polish-partial.test.ts +42 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-decode.mjs +110 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-locator.mjs +47 -0
- package/packages/shared-skills/skills/ulw-research/scripts/raw-snapshot.test.ts +139 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-2.test.ts +233 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-3.test.ts +141 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-4.test.ts +135 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-5.test.ts +231 -0
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools-commands.mjs +1 -1
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.mjs +7 -1
- package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +5 -800
- package/packages/shared-skills/skills/ulw-research/scripts/snapshot-readability.test.ts +116 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-commands.mjs +79 -26
- package/packages/shared-skills/skills/ulw-research/scripts/source-integrity.test.ts +243 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-refresh.mjs +82 -0
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.mjs +73 -69
- package/packages/shared-skills/skills/ulw-research/scripts/source-snapshot.test.ts +7 -4
- package/packages/shared-skills/skills/ulw-research/scripts/source-view.mjs +89 -0
- package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.mjs +203 -0
- package/packages/shared-skills/skills/ulw-research/scripts/sources-citation.test.ts +236 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/contracts-support.ts +147 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/delivery-barrier-session.ts +59 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/derive-clock.ts +4 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-derive-support.ts +65 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-dimensions-support.ts +41 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fact-unit-rules-support.ts +241 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fidelity-values-support.ts +30 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/conversion-probe.html +14 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/fixtures/sources-shapes.html +20 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/numeral-classes.ts +196 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/report-tools-support.ts +182 -0
- package/packages/shared-skills/skills/ulw-research/scripts/tests/value-mode-support.ts +48 -0
- package/packages/shared-skills/skills/ulw-research/scripts/units.mjs +14 -3
- package/packages/shared-skills/skills/ulw-research/scripts/units.test.ts +10 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit-classes.test.ts +255 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-mode-unit.test.ts +179 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-cli.test.ts +157 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-followups.test.ts +72 -0
- package/packages/shared-skills/skills/ulw-research/scripts/value-rules-priority.test.ts +80 -0
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/amp_redirect_stub.html +0 -7
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/search_interstitial.html +0 -19
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_available.json +0 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/fixtures/wayback_snapshot.html +0 -1128
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_fetch_chain.py +0 -77
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_stealth.py +0 -86
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_playwright_templates.py +0 -279
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate.py +0 -252
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/tests/test_surrogate_validators.py +0 -78
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// css-hiding.mjs - whether CSS hides an element of a page from every reader, from its inline style and
|
|
2
|
+
// the page's own <style> rules (external stylesheets and scripts are not evaluated).
|
|
3
|
+
//
|
|
4
|
+
// Text counts as reader-visible when it is visible under at least one condition. So a rule can hide
|
|
5
|
+
// only when it certainly applies: it sits outside every at-rule except @layer (and @media all), its
|
|
6
|
+
// <style> element applies everywhere (no media other than all, CSS type, not in <noscript>), and its
|
|
7
|
+
// selector is one tag (case-insensitive), one .class or one #id (case-sensitive). A rule that only
|
|
8
|
+
// might apply (inside @media, @supports, @container, @scope, @starting-style, @document, @keyframes,
|
|
9
|
+
// nested in another style rule, or with a compound, combinator, attribute or pseudo-class selector)
|
|
10
|
+
// never hides, but it can make text visible when its subject could match and it outranks the hiding
|
|
11
|
+
// declaration in the cascade (origin and importance, inline style, layer order, specificity, order).
|
|
12
|
+
// Hiding properties: display:none and content-visibility:hidden remove the subtree;
|
|
13
|
+
// visibility:hidden|collapse is inherited and a descendant's visibility:visible shows it again.
|
|
14
|
+
// The hidden attribute (except hidden="until-found") and a closed <dialog> are the user-agent
|
|
15
|
+
// display:none, which any author display declaration overrides. opacity, clip, zero size, font-size,
|
|
16
|
+
// off-screen positions and colors do not hide: the text still renders or is read aloud.
|
|
17
|
+
// Plain Node ESM, sibling imports only.
|
|
18
|
+
|
|
19
|
+
import { scanRules } from "./css-lite.mjs"
|
|
20
|
+
|
|
21
|
+
const PROPS = new Set(["display", "visibility", "content-visibility"])
|
|
22
|
+
const INHERITED = new Set(["inherit", "unset", "revert", "revert-layer"])
|
|
23
|
+
const IMPORTANT = /!\s*important\s*$/i
|
|
24
|
+
const SIMPLE = /^(?:[.#][^\s.#:[\]>+~,()*"'\\]+|[a-z][a-z0-9-]*)$/iu
|
|
25
|
+
const PSEUDO_ELEMENT = /::|:(?:before|after|first-line|first-letter)\b/i
|
|
26
|
+
const NAME = "[^\\s.#:[\\]>+~,()*\"'\\\\]+"
|
|
27
|
+
const STYLE_IN_SVG = /<style\b[^>]*>([\s\S]*?)<\/style\s*>/gi
|
|
28
|
+
|
|
29
|
+
/** [tier (0 user agent, 1 author, 2 author !important), inline, layer, specificity, order]: higher wins. */
|
|
30
|
+
const outranks = (a, b) => {
|
|
31
|
+
for (let k = 0; k < a.length; k++) if (a[k] !== b[k]) return a[k] > b[k]
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function specificity(selector) {
|
|
36
|
+
const core = selector.replace(/\[[^\]]*\]/g, " [] ")
|
|
37
|
+
const ids = core.match(new RegExp(`#${NAME}`, "gu"))?.length ?? 0
|
|
38
|
+
const classes = (core.match(new RegExp(`\\.${NAME}`, "gu"))?.length ?? 0) + (core.match(/\[\]|:(?!:)[\w-]+/g)?.length ?? 0)
|
|
39
|
+
const tags = core.match(/(?:^|[\s>+~(])[a-z][\w-]*/gi)?.length ?? 0
|
|
40
|
+
return ids * 10000 + classes * 100 + tags
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The subject compound of a selector: its tag, classes and ids (attribute and pseudo-class parts dropped). */
|
|
44
|
+
function subject(selector) {
|
|
45
|
+
const compound = selector.trim().match(/(?:[^\s>+~[\]()]|\[[^\]]*\]|\([^)]*\))+$/)?.[0] ?? ""
|
|
46
|
+
const core = compound.replace(/\([^)]*\)/g, "").replace(/\[[^\]]*\]/g, "").replace(/:+[\w-]+/g, "")
|
|
47
|
+
const tag = core.match(/^[a-z][a-z0-9-]*/i)?.[0].toLowerCase() ?? null
|
|
48
|
+
const classes = [...core.matchAll(new RegExp(`\\.(${NAME})`, "gu"))].map((match) => match[1])
|
|
49
|
+
const ids = [...core.matchAll(new RegExp(`#(${NAME})`, "gu"))].map((match) => match[1])
|
|
50
|
+
return { tag, classes, ids, key: ids[0] ? `#${ids[0]}` : classes[0] ? `.${classes[0]}` : tag ?? "*" }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const classList = (node) => String(node.attrs?.class ?? "").split(/[ \t\n\f\r]+/).filter(Boolean)
|
|
54
|
+
|
|
55
|
+
function matches(sub, node) {
|
|
56
|
+
if (sub.tag !== null && sub.tag !== node.tag) return false
|
|
57
|
+
const names = classList(node)
|
|
58
|
+
return sub.classes.every((name) => names.includes(name)) && sub.ids.every((id) => id === node.attrs?.id)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Whether every enclosing at-rule leaves the rule unconditional (@layer, @media all), its layer name, and keyframe steps. */
|
|
62
|
+
function context(atRule) {
|
|
63
|
+
const entries = atRule ? atRule.split(/\s(?=@)/) : []
|
|
64
|
+
const layers = entries.filter((entry) => /^@layer\b/i.test(entry)).map((entry) => entry.slice(6).trim())
|
|
65
|
+
const conditional = entries.some((entry) => !/^@layer\b/i.test(entry) && !/^@media(?:\s+all)?\s*$/i.test(entry))
|
|
66
|
+
return { conditional, layer: layers.length ? layers.join(".") : null, keyframe: entries.some((entry) => /^@(?:-[a-z]+-)?keyframes\b/i.test(entry)) }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const declaration = (value) => ({ value: value.replace(IMPORTANT, "").trim().toLowerCase(), important: IMPORTANT.test(value) })
|
|
70
|
+
|
|
71
|
+
/** Every <style> text that applies to the page, with whether it applies only under a condition. */
|
|
72
|
+
function sheets(root) {
|
|
73
|
+
const found = []
|
|
74
|
+
const visit = (node, conditional) => {
|
|
75
|
+
if (node.tag === "template") return
|
|
76
|
+
const inner = conditional || node.tag === "noscript"
|
|
77
|
+
if (node.tag === "style") {
|
|
78
|
+
const type = String(node.attrs?.type ?? "").trim().toLowerCase()
|
|
79
|
+
const media = String(node.attrs?.media ?? "").trim().toLowerCase()
|
|
80
|
+
if (type === "" || type === "text/css") found.push({ css: String(node.raw ?? ""), conditional: inner || (media !== "" && media !== "all") })
|
|
81
|
+
}
|
|
82
|
+
if (node.tag === "svg") for (const match of String(node.raw ?? "").matchAll(STYLE_IN_SVG)) found.push({ css: match[1], conditional: inner })
|
|
83
|
+
for (const child of node.children ?? []) visit(child, inner)
|
|
84
|
+
}
|
|
85
|
+
visit(root, false)
|
|
86
|
+
return found
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Index every rule declaring display, visibility or content-visibility by its subject key. */
|
|
90
|
+
function indexRules(root) {
|
|
91
|
+
const index = new Map()
|
|
92
|
+
const layers = []
|
|
93
|
+
const layerRank = (name, important) => {
|
|
94
|
+
if (name === null) return important ? -1 : 1e6
|
|
95
|
+
if (!layers.includes(name)) layers.push(name)
|
|
96
|
+
return important ? 1e6 - layers.indexOf(name) : layers.indexOf(name)
|
|
97
|
+
}
|
|
98
|
+
let order = 0
|
|
99
|
+
for (const sheet of sheets(root)) {
|
|
100
|
+
const css = sheet.css.replace(/<!--|-->/g, " ")
|
|
101
|
+
for (const match of css.replace(/\/\*[\s\S]*?(?:\*\/|$)/g, "").matchAll(/@layer\s+([^{;]*)[{;]/gi)) {
|
|
102
|
+
for (const name of match[1].split(",").map((part) => part.trim())) if (name && !layers.includes(name)) layers.push(name)
|
|
103
|
+
}
|
|
104
|
+
for (const rule of scanRules(css)) {
|
|
105
|
+
const decls = rule.declarations.filter((entry) => PROPS.has(entry.prop))
|
|
106
|
+
if (!decls.length || rule.selector.startsWith("@")) continue
|
|
107
|
+
const { conditional, layer, keyframe } = context(rule.atRule)
|
|
108
|
+
if (keyframe) continue
|
|
109
|
+
for (const selector of rule.selector.split(/,(?![^()[\]]*[)\]])/).map((part) => part.trim())) {
|
|
110
|
+
if (!selector || PSEUDO_ELEMENT.test(selector)) continue
|
|
111
|
+
const sub = subject(selector)
|
|
112
|
+
const certain = !sheet.conditional && !conditional && !rule.nested && SIMPLE.test(selector)
|
|
113
|
+
const spec = specificity(selector)
|
|
114
|
+
for (const entry of decls) {
|
|
115
|
+
const { value, important } = declaration(entry.value)
|
|
116
|
+
const rank = [important ? 2 : 1, 0, layerRank(layer, important), spec, order++]
|
|
117
|
+
if (!index.has(sub.key)) index.set(sub.key, [])
|
|
118
|
+
index.get(sub.key).push({ sub, prop: entry.prop, value, rank, certain })
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return index
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** The values a property can take on the element: the certain winner, plus every possible rule that outranks it. */
|
|
127
|
+
function possible(candidates, prop) {
|
|
128
|
+
let top = null
|
|
129
|
+
for (const entry of candidates) if (entry.prop === prop && entry.certain && (top === null || outranks(entry.rank, top.rank))) top = entry
|
|
130
|
+
const values = [top === null ? "unset" : top.value]
|
|
131
|
+
for (const entry of candidates) if (entry.prop === prop && !entry.certain && (top === null || outranks(entry.rank, top.rank))) values.push(entry.value)
|
|
132
|
+
return values
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* cssVisibility(root) -> (node, parentShown) => null when CSS removes the element and its subtree from every
|
|
137
|
+
* rendering, else whether its own text can be seen (visibility, inherited from parentShown).
|
|
138
|
+
*/
|
|
139
|
+
export function cssVisibility(root) {
|
|
140
|
+
const index = indexRules(root)
|
|
141
|
+
return (node, parentShown) => {
|
|
142
|
+
const attrs = node.attrs ?? {}
|
|
143
|
+
const keys = ["*", node.tag, ...classList(node).map((name) => `.${name}`), attrs.id === undefined ? null : `#${attrs.id}`]
|
|
144
|
+
const candidates = []
|
|
145
|
+
for (const key of new Set(keys)) for (const entry of index.get(key) ?? []) if (matches(entry.sub, node)) candidates.push(entry)
|
|
146
|
+
const hiddenAttr = attrs.hidden !== undefined && String(attrs.hidden).trim().toLowerCase() !== "until-found"
|
|
147
|
+
if (hiddenAttr || (node.tag === "dialog" && attrs.open === undefined)) candidates.push({ prop: "display", value: "none", rank: [0, 0, 0, 0, -1], certain: true })
|
|
148
|
+
const inline = attrs.style ? (scanRules(`x{${attrs.style}}`)[0]?.declarations ?? []) : []
|
|
149
|
+
inline.forEach((entry, at) => {
|
|
150
|
+
if (!PROPS.has(entry.prop)) return
|
|
151
|
+
const { value, important } = declaration(entry.value)
|
|
152
|
+
candidates.push({ prop: entry.prop, value, rank: [important ? 2 : 1, 1, 0, 0, at], certain: true })
|
|
153
|
+
})
|
|
154
|
+
if (possible(candidates, "display").every((value) => value === "none")) return null
|
|
155
|
+
if (possible(candidates, "content-visibility").every((value) => value === "hidden")) return null
|
|
156
|
+
return possible(candidates, "visibility").some((value) => (INHERITED.has(value) ? parentShown : value !== "hidden" && value !== "collapse"))
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -57,8 +57,9 @@ function parseDeclarations(text) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
|
-
* @returns {{ selector: string, declarations: { prop: string, value: string }[], atRule?: string }[]}
|
|
61
|
-
* atRule is the chain of enclosing grouping at-rule preludes joined by a space
|
|
60
|
+
* @returns {{ selector: string, declarations: { prop: string, value: string }[], atRule?: string, nested?: true }[]}
|
|
61
|
+
* atRule is the chain of enclosing grouping at-rule preludes joined by a space; nested is true for a
|
|
62
|
+
* rule written inside another style rule (CSS nesting).
|
|
62
63
|
*/
|
|
63
64
|
export function scanRules(css) {
|
|
64
65
|
if (typeof css !== "string") return []
|
|
@@ -88,6 +89,7 @@ export function scanRules(css) {
|
|
|
88
89
|
const atChain = stack.filter((entry) => entry.group).map((entry) => entry.prelude)
|
|
89
90
|
const rule = { selector: frame.prelude, declarations: parseDeclarations(frame.body + buffer) }
|
|
90
91
|
if (atChain.length) rule.atRule = atChain.join(" ")
|
|
92
|
+
if (stack.some((entry) => !entry.group)) rule.nested = true
|
|
91
93
|
slots[frame.slot] = rule
|
|
92
94
|
}
|
|
93
95
|
buffer = ""
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
// entities.mjs - HTML character reference decoding for html-lite.mjs.
|
|
2
2
|
//
|
|
3
|
-
// Supports a fixed set of named entities plus decimal and hex numeric references.
|
|
4
|
-
// malformed references stay verbatim. Single indexOf scan; plain Node ESM, no dependencies.
|
|
3
|
+
// Supports a fixed set of named entities (NAMED_ENTITIES) plus decimal and hex numeric references.
|
|
4
|
+
// Unknown or malformed references stay verbatim. Single indexOf scan; plain Node ESM, no dependencies.
|
|
5
5
|
|
|
6
|
+
// Beyond the common set, every named reference whose character a number reader treats specially is
|
|
7
|
+
// listed (signs, dashes, the space separators that group digits, and Markdown punctuation), so the
|
|
8
|
+
// conversion probe reads `−5` and `1 234` as a converter does.
|
|
6
9
|
const NAMED_ENTITIES = {
|
|
7
10
|
amp: "&", lt: "<", gt: ">", quot: '"', apos: "'", nbsp: "\u00a0", mdash: "\u2014", ndash: "\u2013",
|
|
8
11
|
hellip: "\u2026", copy: "\u00a9", reg: "\u00ae", times: "\u00d7", middot: "\u00b7",
|
|
12
|
+
minus: "\u2212", hyphen: "\u2010", dash: "\u2010", NonBreakingSpace: "\u00a0", thinsp: "\u2009",
|
|
13
|
+
ThinSpace: "\u2009", numsp: "\u2007", puncsp: "\u2008", hairsp: "\u200a", VeryThinSpace: "\u200a",
|
|
14
|
+
ensp: "\u2002", emsp: "\u2003", emsp13: "\u2004", emsp14: "\u2005", MediumSpace: "\u205f",
|
|
15
|
+
comma: ",", period: ".", plus: "+", lowbar: "_", UnderBar: "_", ast: "*", midast: "*", grave: "`",
|
|
16
|
+
DiacriticalGrave: "`", lpar: "(", rpar: ")", lsqb: "[", lbrack: "[", rsqb: "]", rbrack: "]",
|
|
9
17
|
}
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
/** One reference body (between `&` and `;`), or null when unsupported. */
|
|
20
|
+
export function decodeOne(body) {
|
|
12
21
|
if (Object.hasOwn(NAMED_ENTITIES, body)) return NAMED_ENTITIES[body]
|
|
13
22
|
if (body[0] !== "#") return null
|
|
14
23
|
const hex = body[1] === "x" || body[1] === "X"
|
|
@@ -26,7 +35,7 @@ export function decodeEntities(text) {
|
|
|
26
35
|
let at = source.indexOf("&")
|
|
27
36
|
while (at !== -1) {
|
|
28
37
|
const semi = source.indexOf(";", at + 1)
|
|
29
|
-
const decoded = semi !== -1 && semi - at <=
|
|
38
|
+
const decoded = semi !== -1 && semi - at <= 32 ? decodeOne(source.slice(at + 1, semi)) : null
|
|
30
39
|
if (decoded !== null) {
|
|
31
40
|
out += source.slice(last, at) + decoded
|
|
32
41
|
last = semi + 1
|
|
@@ -13,10 +13,13 @@ import { dirname, extname, resolve } from "node:path"
|
|
|
13
13
|
|
|
14
14
|
import { CliError, sha256Of, writeJsonAtomic } from "./cli-support.mjs"
|
|
15
15
|
import { DEFECT_CODES } from "./contracts.mjs"
|
|
16
|
+
import { markdownAsHtml } from "./fact-commands-verify.mjs"
|
|
16
17
|
import { FILL_FORMATS, fill } from "./fact-fill.mjs"
|
|
17
18
|
import { readLedger, writeLedger } from "./fact-ledger.mjs"
|
|
18
19
|
import { briefHashOf, fillPolicy, lockedBrief, policyErrors } from "./fidelity-policy.mjs"
|
|
19
20
|
import { readState, writeState } from "./fidelity-state.mjs"
|
|
21
|
+
import { withBoundUnits } from "./fidelity-value-unit.mjs"
|
|
22
|
+
import { parseMarkdown } from "./md-lite.mjs"
|
|
20
23
|
import { findPlaceholders } from "./placeholder.mjs"
|
|
21
24
|
import { sessionDirOf } from "./source-commands.mjs"
|
|
22
25
|
|
|
@@ -28,6 +31,13 @@ export const FILL_INPUT_FORMAT_RULES = Object.freeze({
|
|
|
28
31
|
export const FILL_INPUT_FORMATS = Object.freeze([...FILL_FORMATS, ...Object.keys(FILL_INPUT_FORMAT_RULES.aliases)])
|
|
29
32
|
const fillFormatOf = (format) => FILL_INPUT_FORMAT_RULES.aliases[format] ?? format
|
|
30
33
|
|
|
34
|
+
/** Value-mode entries carry the unit fill saw bound to them (unit_bound), read off the edition verify will check. */
|
|
35
|
+
function boundUnits(out, entries, ledger, format) {
|
|
36
|
+
if (!entries.some((entry) => entry.mode === "value")) return entries
|
|
37
|
+
const html = fillFormatOf(format) === "html" ? out : parseMarkdown(out).problems.length === 0 ? markdownAsHtml(out, entries, ledger).html : null
|
|
38
|
+
return html === null ? entries : withBoundUnits(html, entries, new Map(ledger.rows.map((row) => [row.fact_id, row])))
|
|
39
|
+
}
|
|
40
|
+
|
|
31
41
|
function readText(path) {
|
|
32
42
|
try {
|
|
33
43
|
return readFileSync(path, "utf8")
|
|
@@ -80,7 +90,7 @@ export function runFactFill(args) {
|
|
|
80
90
|
const artifact = sha256Of(out)
|
|
81
91
|
const map = `${resolve(flags.out)}.fill-map.json`
|
|
82
92
|
const brief = briefHashOf(parsed)
|
|
83
|
-
writeJsonAtomic(map, { schemaVersion: 1, format, artifact: resolve(flags.out), artifact_sha256: artifact, brief_sha256: brief, entries })
|
|
93
|
+
writeJsonAtomic(map, { schemaVersion: 1, format, artifact: resolve(flags.out), artifact_sha256: artifact, brief_sha256: brief, entries: boundUnits(out, entries, ledger, format) })
|
|
84
94
|
const data = { out: resolve(flags.out), map, artifact_sha256: artifact, brief_sha256: brief, occurrences: entries.length, warnings }
|
|
85
95
|
return { json: { command: "fact fill", status: "pass", exitCode: 0, defects: [], data }, summary: `fact fill: wrote ${flags.out} (${entries.length} pointer(s))`, exitCode: 0 }
|
|
86
96
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// fact-commands-meaning.mjs - `fact pairs` and `fact meaning-check`, the script half of the
|
|
2
2
|
// separate-session meaning verifier. fact pairs writes the (source, report sentence) pairs a
|
|
3
|
-
// reader lane gets and nothing else (with --changed-since, only the pairs whose sentence
|
|
4
|
-
//
|
|
3
|
+
// reader lane gets and nothing else (with --changed-since, only the pairs whose sentence changed
|
|
4
|
+
// since the pre-polish fill map's artifact, or all when those bytes are gone); it honours the brief's `meaning check` field and writes
|
|
5
5
|
// `{ skipped: true }` when it is off. fact meaning-check ingests the readers' verdict files
|
|
6
|
-
// (meaning-check.mjs), writes
|
|
6
|
+
// (meaning-check.mjs), writes the meaning record of one edition (fidelity-meaning-defects.<format>.json
|
|
7
|
+
// with --edition, else the legacy fidelity-meaning-defects.json; a fidelity record bound to the pairs'
|
|
7
8
|
// artifact_sha256 and report, through writeFidelityRecord), and records the run in
|
|
8
9
|
// fidelity-calibration.jsonl: a header with the promotion criterion on first write, then per run
|
|
9
10
|
// one run line plus one line per flagged pair. The run id hashes the inputs only, so a rerun or a
|
|
@@ -15,13 +16,13 @@ import { existsSync, readFileSync } from "node:fs"
|
|
|
15
16
|
import { dirname, join, resolve } from "node:path"
|
|
16
17
|
|
|
17
18
|
import { CliError, parseArgs, readJsonFile, sha256Of, writeJsonAtomic, writeTextAtomic } from "./cli-support.mjs"
|
|
18
|
-
import { countDefects } from "./contracts.mjs"
|
|
19
|
+
import { countDefects, DELIVERABLE_FORMATS } from "./contracts.mjs"
|
|
19
20
|
import { ledgerFrom, markdownAsHtml, readEdition, sessionOf } from "./fact-commands-verify.mjs"
|
|
20
21
|
import { pairsChangedSince } from "./fact-commands-polish.mjs"
|
|
21
22
|
import { extractPairs, PAIR_KINDS } from "./fact-pairs.mjs"
|
|
22
23
|
import { FIDELITY_INCOMPLETE } from "./fidelity-brief.mjs"
|
|
23
24
|
import { briefPathOf, lockedBrief, meaningCheckEnabled } from "./fidelity-policy.mjs"
|
|
24
|
-
import { MEANING_RECORD, writeFidelityRecord } from "./fidelity-records.mjs"
|
|
25
|
+
import { MEANING_RECORD, meaningRecordName, writeFidelityRecord } from "./fidelity-records.mjs"
|
|
25
26
|
import { blocksDelivery } from "./gates-fidelity.mjs"
|
|
26
27
|
import { ingestVerdicts, unverifiedDefects, VERDICT_SCHEMA } from "./meaning-check.mjs"
|
|
27
28
|
|
|
@@ -57,10 +58,12 @@ function runPairs(args) {
|
|
|
57
58
|
const ledger = ledgerFrom(flags, session)
|
|
58
59
|
const html = edition.format === "md" ? markdownAsHtml(edition.text, edition.entries, ledger).html : edition.text
|
|
59
60
|
const all = extractPairs(html, ledger, claims)
|
|
60
|
-
const
|
|
61
|
+
const since = flags["changed-since"] === undefined ? null : pairsChangedSince(all, flags["changed-since"], ledger, claims)
|
|
62
|
+
const pairs = since?.pairs ?? all
|
|
61
63
|
const counts = { total: pairs.length, force_strong: pairs.filter((pair) => pair.force_strong).length }
|
|
62
64
|
for (const kind of PAIR_KINDS) counts[kind] = pairs.filter((pair) => pair.kind === kind).length
|
|
63
|
-
const
|
|
65
|
+
const changed = since === null ? {} : { changed_since: { fill_map: since.fill_map, narrowed: since.narrowed } }
|
|
66
|
+
const record = { schemaVersion: 1, skipped: false, report: resolve(report), format: edition.format, artifact_sha256: sha256Of(edition.text), pairs_sha256: sha256Of(JSON.stringify(pairs)), counts, ...changed, verdict_schema: VERDICT_SCHEMA, pairs }
|
|
64
67
|
writeJsonAtomic(out, record)
|
|
65
68
|
const summary = `fact pairs: ${counts.total} pair(s), ${counts.force_strong} routed to the strong reader`
|
|
66
69
|
return { json: { command: "fact pairs", status: "pass", exitCode: 0, data: { out, ...record, pairs: undefined } }, summary, exitCode: 0 }
|
|
@@ -124,8 +127,9 @@ function skippedRefusal(flags, parsed) {
|
|
|
124
127
|
|
|
125
128
|
function runMeaningCheck(args) {
|
|
126
129
|
const { flags } = args
|
|
127
|
-
const usage = "usage: fact meaning-check --pairs pairs.json --verdicts cheap.json [--verdicts strong.json] [--dispute P<n> --reason TEXT] [--run-id ID] [--calibration F] [--brief brief.md] [--out F]"
|
|
130
|
+
const usage = "usage: fact meaning-check --pairs pairs.json --verdicts cheap.json [--verdicts strong.json] [--dispute P<n> --reason TEXT] [--run-id ID] [--calibration F] [--brief brief.md] [--edition FORMAT] [--out F]"
|
|
128
131
|
if (flags.pairs === undefined || !Array.isArray(flags.verdicts)) throw new CliError(usage)
|
|
132
|
+
if (flags.edition !== undefined && !DELIVERABLE_FORMATS.includes(flags.edition)) throw new CliError(`--edition must be a deliverable format (${DELIVERABLE_FORMATS.join(", ")}), got ${flags.edition}`)
|
|
129
133
|
const session = sessionOf(flags)
|
|
130
134
|
const { parsed, refusal } = lockedBrief("fact meaning-check", flags)
|
|
131
135
|
if (refusal) return { ...refusal, json: { ...refusal.json, error: FIDELITY_INCOMPLETE } }
|
|
@@ -146,7 +150,7 @@ function runMeaningCheck(args) {
|
|
|
146
150
|
} catch (error) {
|
|
147
151
|
throw new CliError(`invalid verdict input: ${error.message}`)
|
|
148
152
|
}
|
|
149
|
-
const out = resolve(flags.out ?? inSession(session, MEANING_RECORD, "--out"))
|
|
153
|
+
const out = resolve(flags.out ?? inSession(session, flags.edition === undefined ? MEANING_RECORD : meaningRecordName(flags.edition), "--out"))
|
|
150
154
|
const calibration = resolve(flags.calibration ?? join(session ?? dirname(out), CALIBRATION_FILE))
|
|
151
155
|
const runId = flags["run-id"] ?? sha256Of([pairsText, ...verdictTexts].join("\0")).slice(0, 16)
|
|
152
156
|
const pairsSha = pairsDoc.pairs_sha256 ?? sha256Of(pairsText)
|
|
@@ -6,18 +6,24 @@
|
|
|
6
6
|
// rejected (major) or unlocated (minor) edit leaves its region unchanged and is listed with its
|
|
7
7
|
// index and reason so the orchestrator can re-send it; every rejection reason is printed verbatim, one
|
|
8
8
|
// line per rejected edit (index and the first 60 characters of its before), in the summary and in
|
|
9
|
-
// data.rejected_reasons. It exits 0 whenever it wrote the output.
|
|
9
|
+
// data.rejected_reasons. It exits 0 whenever it wrote the output; json.applied is false when
|
|
10
|
+
// any edit was rejected or unlocated, independently of data.applied's accepted-edit count.
|
|
11
|
+
// polish-apply never rewrites a fill map: it checks that the edition's map (fillMapOf) records the
|
|
12
|
+
// input's bytes, refusing otherwise, and writes `<out>.polished.fill-map.json` beside the output
|
|
13
|
+
// (artifact = the polished file, its hash, and polishedFrom = the fill map's path and sha256).
|
|
10
14
|
// polish-diff is the whole-document backstop (polish-diff.mjs) between the pre-polish artifact
|
|
11
|
-
// and any later copy; it exits 1 on a blocker. pairsChangedSince
|
|
12
|
-
//
|
|
13
|
-
// meaning readers re-check exactly what
|
|
15
|
+
// and any later copy; it exits 1 on a blocker. pairsChangedSince always reads the pre-polish fill
|
|
16
|
+
// map (following a polished map's polishedFrom) and keeps only the pairs whose sentence text
|
|
17
|
+
// (normalized) is not in the artifact it names, so the meaning readers re-check exactly what
|
|
18
|
+
// changed; when that artifact no longer hashes to the map (polished in place, or edited) nothing
|
|
19
|
+
// can be ruled out and every pair is kept. No model, no network.
|
|
14
20
|
|
|
15
21
|
import { existsSync, readFileSync } from "node:fs"
|
|
16
22
|
import { join, resolve } from "node:path"
|
|
17
23
|
|
|
18
24
|
import { CliError, parseArgs, readJsonFile, sha256Of, writeJsonAtomic, writeTextAtomic } from "./cli-support.mjs"
|
|
19
25
|
import { countDefects, DEFECT_CODES } from "./contracts.mjs"
|
|
20
|
-
import { editionFormatOf, ledgerFrom, markdownAsHtml, sessionOf } from "./fact-commands-verify.mjs"
|
|
26
|
+
import { editionFormatOf, fillMapOf, ledgerFrom, markdownAsHtml, sessionOf } from "./fact-commands-verify.mjs"
|
|
21
27
|
import { extractPairs } from "./fact-pairs.mjs"
|
|
22
28
|
import { lockedBrief } from "./fidelity-policy.mjs"
|
|
23
29
|
import { applyEdits, POLISH_EDIT_FIELDS, validatePolishEdits } from "./polish-apply.mjs"
|
|
@@ -58,7 +64,7 @@ const rejectionLine = (entry, edit) => ` rejected edit ${entry.index} ("${befor
|
|
|
58
64
|
function runPolishApply(args) {
|
|
59
65
|
const [report] = args.positionals
|
|
60
66
|
const { flags } = args
|
|
61
|
-
if (!report || flags.edits === undefined || flags.out === undefined) throw new CliError("usage: fact polish-apply <report.html> --edits polish-edits.json --out <report.polished.html> [--ledger F] [--brief brief.md] [--session-dir D]")
|
|
67
|
+
if (!report || flags.edits === undefined || flags.out === undefined) throw new CliError("usage: fact polish-apply <report.html> --edits polish-edits.json --out <report.polished.html> [--fill-map F] [--ledger F] [--brief brief.md] [--session-dir D]")
|
|
62
68
|
if (editionFormatOf(report, flags.format) === "md") throw new CliError("fact polish-apply takes the filled HTML edition; polish a Markdown edition through its HTML source")
|
|
63
69
|
const { refusal } = lockedBrief("fact polish-apply", flags)
|
|
64
70
|
if (refusal) return refusal
|
|
@@ -71,13 +77,18 @@ function runPolishApply(args) {
|
|
|
71
77
|
throw new CliError(`${flags.edits} must be an array of {${Object.keys(POLISH_EDIT_FIELDS).join(", ")}} objects: ${error.message}`)
|
|
72
78
|
}
|
|
73
79
|
const result = applyEdits(html, edits, ledgerFrom(flags))
|
|
80
|
+
const mapPath = fillMapOf(report, html, flags["fill-map"])
|
|
81
|
+
const map = mapPath === null ? null : readJsonFile(mapPath)
|
|
82
|
+
if (map !== null && map.artifact_sha256 !== sha256Of(html)) throw new CliError(`${mapPath} does not record the bytes of ${report} (artifact_sha256 differs); refusing to carry its brief binding to the polished edition`)
|
|
83
|
+
const polishedFrom = map?.polishedFrom ?? (map && { map: resolve(mapPath), sha256: sha256Of(readText(mapPath)) })
|
|
74
84
|
writeTextAtomic(flags.out, result.out)
|
|
85
|
+
if (map) writeJsonAtomic(`${resolve(flags.out)}.polished.fill-map.json`, { ...map, artifact: resolve(flags.out), artifact_sha256: sha256Of(result.out), polishedFrom })
|
|
75
86
|
const defects = [...result.rejected, ...result.unlocated].sort((a, b) => a.index - b.index).map(withSeverity)
|
|
76
87
|
const counts = { applied: result.applied.length, rejected: result.rejected.length, unlocated: result.unlocated.length }
|
|
77
88
|
const data = { out: resolve(flags.out), ...counts, applied_indexes: result.applied, input_sha256: sha256Of(html), edits_sha256: sha256Of(editsText), out_sha256: sha256Of(result.out) }
|
|
78
89
|
const rejected_reasons = result.rejected.map((entry) => rejectionLine(entry, edits[entry.index]))
|
|
79
90
|
const summary = [`fact polish-apply: ${counts.applied} applied, ${counts.rejected} rejected, ${counts.unlocated} unlocated of ${edits.length} edit(s)`, ...rejected_reasons].join("\n")
|
|
80
|
-
return { json: { command: "fact polish-apply", status: "pass", exitCode: 0, defects, data: { ...data, rejected_reasons } }, summary, exitCode: 0 }
|
|
91
|
+
return { json: { command: "fact polish-apply", status: "pass", exitCode: 0, applied: counts.rejected === 0 && counts.unlocated === 0, defects, data: { ...data, rejected_reasons } }, summary, exitCode: 0 }
|
|
81
92
|
}
|
|
82
93
|
|
|
83
94
|
function runPolishDiff(args) {
|
|
@@ -100,29 +111,40 @@ function runPolishDiff(args) {
|
|
|
100
111
|
|
|
101
112
|
const pairKey = (pair) => [pair.kind, pair.sentence_key, pair.claim_id ?? "", normalizeText(pair.sentence)].join("\u0000")
|
|
102
113
|
|
|
114
|
+
/** The pre-polish fill map behind --changed-since: a polished map leads to it through polishedFrom, whose sha256 it must still match. */
|
|
115
|
+
function prePolishMap(mapPath) {
|
|
116
|
+
const given = readJsonFile(mapPath)
|
|
117
|
+
if (given.polishedFrom === undefined) return { path: resolve(mapPath), map: given }
|
|
118
|
+
const { map: path, sha256 } = given.polishedFrom
|
|
119
|
+
if (typeof path !== "string" || sha256Of(readText(path)) !== sha256) throw new CliError(`${path} changed after polish wrote ${mapPath}; --changed-since needs the pre-polish fill map it names`)
|
|
120
|
+
return { path, map: readJsonFile(path) }
|
|
121
|
+
}
|
|
122
|
+
|
|
103
123
|
/**
|
|
104
|
-
* The pairs whose sentence text changed since the pre-polish artifact
|
|
105
|
-
* kept unless the same (kind, sentence key, normalized sentence) occurs in that artifact.
|
|
106
|
-
*
|
|
107
|
-
*
|
|
124
|
+
* The pairs whose sentence text changed since the pre-polish artifact the fill map names: a pair is
|
|
125
|
+
* kept unless the same (kind, sentence key, normalized sentence) occurs in that artifact. Only bytes
|
|
126
|
+
* that still hash to the map's `artifact_sha256` narrow the set; otherwise (the report was polished
|
|
127
|
+
* in place, or edited) every pair is kept, so nothing changed escapes the meaning re-check.
|
|
108
128
|
* @param {object[]} pairs extractPairs output on the polished edition
|
|
109
|
-
* @param {string} mapPath
|
|
129
|
+
* @param {string} mapPath fact fill's `<out>.fill-map.json`, or polish-apply's `<out>.polished.fill-map.json`
|
|
130
|
+
* @returns {{ pairs: object[], fill_map: string, narrowed: boolean }}
|
|
110
131
|
*/
|
|
111
132
|
export function pairsChangedSince(pairs, mapPath, ledger, claims) {
|
|
112
|
-
const map =
|
|
113
|
-
if (typeof map.artifact !== "string"
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (sha256Of(text) !== map.artifact_sha256)
|
|
133
|
+
const { path, map } = prePolishMap(mapPath)
|
|
134
|
+
if (typeof map.artifact !== "string") throw new CliError(`${path} names no pre-polish artifact`)
|
|
135
|
+
if (typeof map.artifact_sha256 !== "string") throw new CliError(`${path} fill map has no artifact_sha256; --changed-since needs the pre-polish hash`)
|
|
136
|
+
const text = existsSync(map.artifact) ? readText(map.artifact) : null
|
|
137
|
+
if (text === null || sha256Of(text) !== map.artifact_sha256) return { pairs, fill_map: path, narrowed: false }
|
|
117
138
|
const html = map.format === "md" ? markdownAsHtml(text, map.entries, ledger).html : text
|
|
118
139
|
const before = new Map()
|
|
119
140
|
for (const pair of extractPairs(html, ledger, claims)) before.set(pairKey(pair), (before.get(pairKey(pair)) ?? 0) + 1)
|
|
120
|
-
|
|
141
|
+
const kept = pairs.filter((pair) => {
|
|
121
142
|
const left = before.get(pairKey(pair)) ?? 0
|
|
122
143
|
if (left === 0) return true
|
|
123
144
|
before.set(pairKey(pair), left - 1)
|
|
124
145
|
return false
|
|
125
146
|
})
|
|
147
|
+
return { pairs: kept, fill_map: path, narrowed: true }
|
|
126
148
|
}
|
|
127
149
|
|
|
128
150
|
/** @param {string[]} argv the arguments after `fact polish-apply` or `fact polish-diff` */
|
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
// escaped HTML projection (same line numbers) and checked with its fill map, which is the only
|
|
9
9
|
// record of where its values sit; LaTeX is verified through its Markdown source.
|
|
10
10
|
//
|
|
11
|
-
// The locked brief is re-judged here, not only at fill:
|
|
12
|
-
// `<report>.fill-map.json`
|
|
13
|
-
//
|
|
14
|
-
//
|
|
11
|
+
// The locked brief is re-judged here, not only at fill: every edition (HTML or Markdown) needs its
|
|
12
|
+
// fill map (fillMapOf: --fill-map, else whichever of `<report>.fill-map.json` and the
|
|
13
|
+
// `<report>.polished.fill-map.json` polish-apply writes records the edition's bytes), and that map
|
|
14
|
+
// must carry the current brief's brief_sha256; no map, or another hash, is brief_changed. policyErrors
|
|
15
|
+
// re-runs over every data-fact the edition carries, so an edition that violates the current
|
|
16
|
+
// derived-numbers or source-floor rule fails here too.
|
|
15
17
|
//
|
|
16
18
|
// The delivery barrier that reads these records (and every other fidelity record) is
|
|
17
19
|
// fidelity-records.mjs.
|
|
@@ -66,8 +68,8 @@ function entryHtml(text, entry, rows) {
|
|
|
66
68
|
if (entry.translates) return `<span data-translates="${escapeHtml(entry.translates)}">${projectMarkdownInline(body.replace(/^<!--[\s\S]*?-->\s*/u, ""))}</span>`
|
|
67
69
|
if (!row) return `<data data-fact="${escapeHtml(entry.fact_id)}">${escapeHtml(body)}</data>`
|
|
68
70
|
const quoted = row.kind === "quote" ? parseRenderedQuote(body) : null
|
|
69
|
-
if (quoted) return renderQuote(row, entry.selector ?? "full", quoted.text, "html")
|
|
70
|
-
return row.kind === "quote" ? null : renderValueHtml(row, body)
|
|
71
|
+
if (quoted) return renderQuote(row, entry.selector ?? "full", quoted.text, "html", entry.claimKind)
|
|
72
|
+
return row.kind === "quote" ? null : renderValueHtml(row, body, { mode: entry.mode, claimKind: entry.claimKind ?? null })
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
/** [from, to) of the filled text as HTML, each intact fill-map entry rebuilt as its fill element. */
|
|
@@ -149,6 +151,17 @@ const MARKDOWN_EXTENSION = /\.(md|markdown)$/iu
|
|
|
149
151
|
/** The edition format: the explicit flag, else md for a .md or .markdown path (any case), else html. */
|
|
150
152
|
export const editionFormatOf = (path, flag) => flag ?? (MARKDOWN_EXTENSION.test(path) ? "md" : "html")
|
|
151
153
|
|
|
154
|
+
/**
|
|
155
|
+
* An edition's fill map path: --fill-map, else whichever of fact fill's `<report>.fill-map.json` and
|
|
156
|
+
* polish-apply's `<report>.polished.fill-map.json` records these bytes as its artifact_sha256, else the
|
|
157
|
+
* first of them that exists; null when neither exists.
|
|
158
|
+
*/
|
|
159
|
+
export function fillMapOf(report, text, flag) {
|
|
160
|
+
if (flag !== undefined) return flag
|
|
161
|
+
const found = [`${report}.fill-map.json`, `${report}.polished.fill-map.json`].filter((path) => existsSync(path))
|
|
162
|
+
return found.find((path) => readJsonFile(path).artifact_sha256 === sha256Of(text)) ?? found[0] ?? null
|
|
163
|
+
}
|
|
164
|
+
|
|
152
165
|
/** The report's format (from --format or its extension) and, for Markdown, its fill map entries. */
|
|
153
166
|
export function readEdition(report, flags) {
|
|
154
167
|
const format = editionFormatOf(report, flags.format)
|
|
@@ -174,19 +187,23 @@ export function runFactVerify(args) {
|
|
|
174
187
|
const briefPath = flags.brief ?? join(sessionDir, "brief.md")
|
|
175
188
|
const ledger = readLedger(sessionDir)
|
|
176
189
|
const projected = edition.format === "md" ? markdownAsHtml(edition.text, edition.entries, ledger) : null
|
|
190
|
+
// The entries whose element is in the checked HTML: an HTML edition's own map, or the projection's intact entries.
|
|
191
|
+
const ownMap = flags["fill-map"] ?? `${report}.fill-map.json`
|
|
192
|
+
const fillEntries = projected ? edition.entries.filter((entry) => !projected.stale.includes(entry)) : existsSync(ownMap) ? readJsonFile(ownMap).entries : null
|
|
177
193
|
const result = checkFidelity(projected?.html ?? edition.text, {
|
|
178
194
|
ledger,
|
|
179
195
|
sourcesDir: resolve(flags.sources ?? join(sessionDir, "sources")),
|
|
180
196
|
claims: flags.claims !== undefined || existsSync(claimsPath) ? readJsonFile(claimsPath) : null,
|
|
181
197
|
fillMap: projected?.stale ?? edition.entries,
|
|
198
|
+
fillEntries,
|
|
182
199
|
brief: existsSync(briefPath) ? readText(briefPath) : "",
|
|
183
200
|
baseDir: dirname(resolve(report)),
|
|
184
201
|
recompute,
|
|
185
202
|
})
|
|
186
203
|
const checked = projected?.html ?? edition.text
|
|
187
204
|
const pointers = [...checked.matchAll(/\bdata-fact="([^"]+)"/gu)].map((match) => ({ token: match[0], start: match.index, parsed: { id: match[1] } }))
|
|
188
|
-
const mapPath = flags["fill-map"]
|
|
189
|
-
const bound =
|
|
205
|
+
const mapPath = fillMapOf(report, edition.text, flags["fill-map"])
|
|
206
|
+
const bound = briefBindingErrors(mapPath === null ? null : readJsonFile(mapPath), mapPath ?? `${report}.fill-map.json`, parsed)
|
|
190
207
|
const defects = [...bound, ...policyErrors(checked, pointers, ledger, sessionDir, policy).errors, ...result.defects]
|
|
191
208
|
const counts = countDefects(defects)
|
|
192
209
|
const status = defects.some(blocksDelivery) ? "fail" : "pass"
|
|
@@ -10,11 +10,14 @@ import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs"
|
|
|
10
10
|
import { join, resolve } from "node:path"
|
|
11
11
|
|
|
12
12
|
import { CliError, parseArgs, sha256Of } from "./cli-support.mjs"
|
|
13
|
+
import { DEFECT_CODES } from "./contracts.mjs"
|
|
14
|
+
import { alteredIn } from "./conversion-probe.mjs"
|
|
13
15
|
import { derive } from "./fact-derive.mjs"
|
|
14
16
|
import { addRow, readLedger, retractRow, supersedeRow, writeLedger } from "./fact-ledger.mjs"
|
|
15
17
|
import { locateFind, locateMatch, locatePageLine, locateTable } from "./fact-locate.mjs"
|
|
16
18
|
import { QUALIFIER_FORMS } from "./fidelity-lexicon.mjs"
|
|
17
19
|
import { exportRows } from "./fidelity-values.mjs"
|
|
20
|
+
import { rawLocated } from "./raw-locator.mjs"
|
|
18
21
|
import { pointedFlag, sessionDirOf, snapshotOf } from "./source-commands.mjs"
|
|
19
22
|
|
|
20
23
|
const CONTEXT = 60
|
|
@@ -30,26 +33,30 @@ function readText(path) {
|
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
/** A caller-supplied --now value as ISO; parsing only, never the clock. */
|
|
37
|
+
function isoOf(text) {
|
|
38
|
+
const date = new Date(text)
|
|
35
39
|
if (Number.isNaN(date.getTime())) throw new CliError("--now requires an ISO timestamp")
|
|
36
40
|
return date.toISOString()
|
|
37
41
|
}
|
|
38
42
|
|
|
43
|
+
const nowIso = (flags) => (flags.now === undefined ? new Date().toISOString() : isoOf(flags.now))
|
|
44
|
+
|
|
39
45
|
function failure(command, error, detail) {
|
|
40
46
|
const json = { command, status: "fail", exitCode: 1, error, ...detail }
|
|
41
47
|
return { json, summary: `${command}: ${error}${detail.message ? ` (${detail.message})` : ""}`, exitCode: 1 }
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
|
|
50
|
+
/** Every locator reads the view text (visible text nodes for a page), never raw markup. */
|
|
51
|
+
function locate(flags, view, id, quote) {
|
|
45
52
|
const sourceId = id
|
|
46
|
-
if (quote) return locateMatch(
|
|
47
|
-
if (flags.find !== undefined) return locateFind(
|
|
53
|
+
if (quote) return locateMatch(view.text, { match: flags.match, sourceId })
|
|
54
|
+
if (flags.find !== undefined) return locateFind(view.text, { find: flags.find, unitExpected: flags["unit-expected"], sourceId })
|
|
48
55
|
if (flags.table !== undefined) {
|
|
49
|
-
if (
|
|
50
|
-
return locateTable(
|
|
56
|
+
if (view.root === null) throw new CliError(`source ${id} is not an HTML page snapshot; --table needs one`)
|
|
57
|
+
return locateTable(view, { table: flags.table, row: flags.row, col: flags.col, sourceId })
|
|
51
58
|
}
|
|
52
|
-
if (flags.page !== undefined) return locatePageLine(
|
|
59
|
+
if (flags.page !== undefined) return locatePageLine(view.text, { page: Number(flags.page), line: Number(flags.line), sourceId })
|
|
53
60
|
throw new CliError("usage: fact add --source S<n> (--find TEXT | --table T --row R --col C | --page P --line L) --label LABEL")
|
|
54
61
|
}
|
|
55
62
|
|
|
@@ -76,11 +83,17 @@ function runAdd(args, quote) {
|
|
|
76
83
|
const id = args.required("source")
|
|
77
84
|
if (quote) args.required("match")
|
|
78
85
|
const sessionDir = sessionDirOf(flags)
|
|
79
|
-
const
|
|
80
|
-
const
|
|
86
|
+
const { meta, view, sha256, drift } = snapshotOf(sessionDir, id)
|
|
87
|
+
const converted = view.mode === "converted"
|
|
88
|
+
if (drift.length > 0) return failure(command, "snapshot_hash_mismatch", { message: `${id}: ${drift.join("; ")}` })
|
|
89
|
+
if (view.mode === "legacy") return failure(command, "snapshot_not_raw", { message: `${id} is a legacy text.txt snapshot without raw.bin; snapshot the source again under a new id` })
|
|
90
|
+
if (converted && !quote) return failure(command, "snapshot_not_raw", { message: `${id} is converted-only (no raw bytes); measured rows need source snapshot --raw <original> --text <extraction>` })
|
|
91
|
+
const located = locate(flags, view, id, quote)
|
|
92
|
+
const found = rawLocated(view, located)
|
|
81
93
|
if (found.error === "unit_expected_mismatch") return failure(command, "unit_mismatch", { message: `expected ${found.expected}, found ${found.found}`, candidates: found.candidates })
|
|
82
94
|
if (found.error) return failure(command, found.error, { message: `no single match in ${id}`, candidates: found.candidates })
|
|
83
|
-
const
|
|
95
|
+
const altered = alteredIn(join(sessionDir, "sources", id), [found.raw, found.quote])
|
|
96
|
+
if (altered.length > 0) return failure(command, "conversion_altered_number", { message: `${id}'s conversion altered ${[...new Set(altered)].join(", ")} (sources/${id}/conversion.json); the converted text cannot back this number, even where the source also shows it unaltered: write a verified text of the source and run \`source snapshot --id ${id} --text <verified.txt> --replace\`, then add the fact again`, tokens: altered, hint: DEFECT_CODES.conversion_altered_number.hint })
|
|
84
97
|
const host = URL.canParse(meta.url) ? new URL(meta.url).hostname : null
|
|
85
98
|
const labelSource = flags.find ?? flags.match ?? (flags.table !== undefined ? `${flags.row}_${flags.col}` : `p${flags.page}_l${flags.line}`)
|
|
86
99
|
const ledger = readLedger(sessionDir)
|
|
@@ -89,9 +102,10 @@ function runAdd(args, quote) {
|
|
|
89
102
|
...(quote ? {} : { value: found.value, unit: found.unit, value_span: found.value_span, qualifier: QUALIFIER_OF.get(found.qualifier) ?? null }),
|
|
90
103
|
raw: found.raw,
|
|
91
104
|
quote: found.quote,
|
|
105
|
+
context: found.context,
|
|
92
106
|
source_id: id,
|
|
93
107
|
locator: { ...found.locator, source_kind: meta.kind, url: meta.url, host, pointed: pointedFlag(sessionDir, meta.url) },
|
|
94
|
-
snapshot_sha256:
|
|
108
|
+
snapshot_sha256: sha256,
|
|
95
109
|
sentence_id: found.sentence_id ?? null,
|
|
96
110
|
extracted_by: meta.extractor,
|
|
97
111
|
observed_at: nowIso(flags),
|
|
@@ -104,11 +118,14 @@ function runAdd(args, quote) {
|
|
|
104
118
|
fuzzy: found.fuzzy === true,
|
|
105
119
|
})
|
|
106
120
|
writeLedger(sessionDir, ledger)
|
|
107
|
-
const context = contextOf(
|
|
108
|
-
const
|
|
121
|
+
const context = contextOf(view.text, located)
|
|
122
|
+
const { severity, integrity } = DEFECT_CODES.quote_converted_only
|
|
123
|
+
const defects = converted ? [{ code: "quote_converted_only", severity, integrity, message: `${id} is converted-only; the quote is not bound to the original fetched bytes` }] : []
|
|
124
|
+
const json = { command, status: "pass", exitCode: 0, defects, data: { row, context } }
|
|
109
125
|
if (flags.json) return { json, exitCode: 0 }
|
|
110
126
|
const shown = quote ? row.quote : `${row.raw} (value ${row.value}${row.unit ? `, unit ${row.unit}` : ""})`
|
|
111
|
-
|
|
127
|
+
const warnings = defects.map((defect) => `\n warning ${defect.code}: ${defect.message}`).join("")
|
|
128
|
+
return { text: `${row.fact_id} ${row.label}: ${shown} [${id} ${row.sentence_id ?? "-"}]\n ...${context.before}[${context.match}]${context.after}...${warnings}`, exitCode: 0 }
|
|
112
129
|
}
|
|
113
130
|
|
|
114
131
|
function runRetract(args) {
|
|
@@ -157,11 +174,13 @@ function runExport(args) {
|
|
|
157
174
|
function runDerive(args) {
|
|
158
175
|
const { flags } = args
|
|
159
176
|
const op = args.required("op")
|
|
177
|
+
// Time is the caller's: without --now the CLI refuses with the library's missing_now code (exit 2) and never reads the clock.
|
|
178
|
+
if (flags.now === undefined) throw new CliError("missing_now: fact derive needs --now <ISO timestamp> (the row's observed_at); no row written")
|
|
160
179
|
const sessionDir = sessionDirOf(flags)
|
|
161
180
|
const ledger = readLedger(sessionDir)
|
|
162
181
|
// The registry refuses any field outside the operation's inputFields, so only the given flags go in; --unit null declares a dimensionless result.
|
|
163
182
|
const picked = ["to", "from", "num", "den", "part", "whole", "reference", "rationale", "label"].filter((key) => flags[key] !== undefined)
|
|
164
|
-
const spec = { op, now:
|
|
183
|
+
const spec = { op, now: isoOf(flags.now), ...Object.fromEntries(picked.map((key) => [key, flags[key]])) }
|
|
165
184
|
if (op === "expr") spec.formula = args.positionals[0]
|
|
166
185
|
else if (args.positionals.length > 0) spec.operands = args.positionals
|
|
167
186
|
if (flags.disjoint === true) spec.disjoint = true
|