@reticlehq/server 3.2.0 → 3.4.0
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/LICENSE-ENTERPRISE +1 -1
- package/README.md +3 -3
- package/SKILL.md +23 -22
- package/dist/chromium-launch-options.d.ts +4 -3
- package/dist/chromium-launch-options.js +6 -2
- package/dist/command/cli/adhoc-suite.d.ts +41 -0
- package/dist/command/cli/adhoc-suite.js +159 -0
- package/dist/command/cli/adhoc-verdict.d.ts +58 -2
- package/dist/command/cli/adhoc-verdict.js +108 -13
- package/dist/command/cli/answers/gate-hook-message.d.ts +6 -0
- package/dist/command/cli/answers/gate-hook-message.js +6 -0
- package/dist/command/cli/auth/cloud-keystore.d.ts +0 -2
- package/dist/command/cli/auth/cloud-keystore.js +0 -3
- package/dist/command/cli/auth/cloud-session.d.ts +4 -0
- package/dist/command/cli/auth/cloud-session.js +2 -0
- package/dist/command/cli/cli-doctor.js +30 -8
- package/dist/command/cli/cli-flow-commands.d.ts +3 -6
- package/dist/command/cli/cli-flow-commands.js +67 -4
- package/dist/command/cli/cli-parse-grammar.d.ts +7 -0
- package/dist/command/cli/cli-parse-grammar.js +7 -0
- package/dist/command/cli/cli-parse-verify.d.ts +2 -5
- package/dist/command/cli/cli-parse-verify.js +86 -13
- package/dist/command/cli/cli-parse.d.ts +14 -1
- package/dist/command/cli/cli-parse.js +54 -8
- package/dist/command/cli/cli-update-commands.js +8 -1
- package/dist/command/cli/cli-verify.d.ts +80 -0
- package/dist/command/cli/cli-verify.js +181 -26
- package/dist/command/cli/cloud-cli.js +5 -2
- package/dist/command/cli/cloud-kit.d.ts +0 -2
- package/dist/command/cli/cloud-kit.js +3 -5
- package/dist/command/cli/cloud-login.js +21 -3
- package/dist/command/cli/demo/demo-page.js +6 -3
- package/dist/command/cli/doctor/browser/chromium-hint.d.ts +5 -0
- package/dist/command/cli/doctor/browser/chromium-hint.js +5 -0
- package/dist/command/cli/doctor/doctor-sessions-line.d.ts +2 -1
- package/dist/command/cli/launch/cli-launch.d.ts +9 -29
- package/dist/command/cli/launch/cli-launch.js +27 -49
- package/dist/command/cli/launch/sdk-version-match.d.ts +84 -0
- package/dist/command/cli/launch/sdk-version-match.js +206 -0
- package/dist/command/cli/lifecycle/daemon-lifecycle.js +2 -16
- package/dist/command/cli/ports/resolve/cli-port.d.ts +38 -0
- package/dist/command/cli/ports/resolve/cli-port.js +169 -15
- package/dist/command/cli/report-command.d.ts +16 -0
- package/dist/command/cli/report-command.js +97 -0
- package/dist/command/cli/setup-mcp-cli.js +13 -13
- package/dist/command/cli/tutorial.d.ts +2 -2
- package/dist/command/cli/tutorial.js +21 -7
- package/dist/command/cli.js +67 -10
- package/dist/command/daemon/binding/daemon-status-probe.d.ts +44 -2
- package/dist/command/daemon/binding/daemon-status-probe.js +79 -6
- package/dist/command/daemon/binding/port-presence.d.ts +1 -1
- package/dist/command/daemon/daemon-resolve.d.ts +9 -0
- package/dist/command/daemon/daemon-resolve.js +1 -1
- package/dist/command/daemon/idle-shutdown.d.ts +2 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.d.ts +7 -1
- package/dist/command/daemon/lifetime/daemon-usefulness.js +30 -7
- package/dist/command/dev/stale-issue-guard.d.ts +3 -4
- package/dist/command/dev/stale-issue-guard.js +8 -8
- package/dist/command/setup/bringup/connect-lease.d.ts +17 -0
- package/dist/command/setup/bringup/connect-lease.js +38 -0
- package/dist/command/setup/bringup/dev-server-files.d.ts +68 -0
- package/dist/command/setup/bringup/dev-server-files.js +131 -0
- package/dist/command/setup/bringup/dev-server-log-cap.d.ts +35 -0
- package/dist/command/setup/bringup/dev-server-log-cap.js +121 -0
- package/dist/command/setup/bringup/ensure-daemon.d.ts +65 -4
- package/dist/command/setup/bringup/ensure-daemon.js +135 -16
- package/dist/command/setup/bringup/static-page-server.d.ts +35 -0
- package/dist/command/setup/bringup/static-page-server.js +151 -0
- package/dist/command/setup/desktop-shape.d.ts +4 -0
- package/dist/command/setup/desktop-shape.js +18 -2
- package/dist/command/setup/init/init-host.js +8 -0
- package/dist/command/setup/init/init-port.d.ts +15 -0
- package/dist/command/setup/init/init-port.js +44 -0
- package/dist/command/setup/init/init-runtime.d.ts +1 -1
- package/dist/command/setup/init/init-runtime.js +39 -2
- package/dist/command/setup/node-effects.d.ts +27 -14
- package/dist/command/setup/node-effects.js +80 -44
- package/dist/command/setup/probe/loopback-probe-urls.d.ts +7 -0
- package/dist/command/setup/probe/loopback-probe-urls.js +40 -0
- package/dist/command/setup/probe/page-probe.d.ts +13 -2
- package/dist/command/setup/probe/page-probe.js +18 -3
- package/dist/command/setup/run-setup.d.ts +26 -1
- package/dist/command/setup/run-setup.js +96 -13
- package/dist/command/setup/session-pick.d.ts +13 -0
- package/dist/command/setup/session-pick.js +20 -0
- package/dist/command/setup/setup-command.d.ts +2 -14
- package/dist/command/setup/setup-command.js +105 -31
- package/dist/command/setup/setup-mcp.js +3 -3
- package/dist/command/setup/terminal/wait-progress.d.ts +8 -0
- package/dist/command/setup/terminal/wait-progress.js +17 -0
- package/dist/features/crawl/crawl-empty.js +1 -1
- package/dist/features/crawl/crawl-tools.d.ts +6 -0
- package/dist/features/crawl/crawl-tools.js +57 -0
- package/dist/features/crawl/crawl.d.ts +2 -0
- package/dist/features/crawl/crawl.js +171 -156
- package/dist/features/exhaust/app-coverage.d.ts +33 -0
- package/dist/features/exhaust/app-coverage.js +31 -0
- package/dist/features/exhaust/code-coverage.d.ts +47 -0
- package/dist/features/exhaust/code-coverage.js +77 -0
- package/dist/features/exhaust/exhaust-run.d.ts +33 -0
- package/dist/features/exhaust/exhaust-run.js +82 -0
- package/dist/features/exhaust/explorer.d.ts +103 -0
- package/dist/features/exhaust/explorer.js +195 -0
- package/dist/features/exhaust/failure-branches.d.ts +33 -0
- package/dist/features/exhaust/failure-branches.js +92 -0
- package/dist/features/exhaust/ledger.d.ts +202 -0
- package/dist/features/exhaust/ledger.js +252 -0
- package/dist/features/exhaust/session-fold.d.ts +31 -0
- package/dist/features/exhaust/session-fold.js +79 -0
- package/dist/features/exhaust/session-port.d.ts +36 -0
- package/dist/features/exhaust/session-port.js +102 -0
- package/dist/features/harness/drive-report.d.ts +0 -9
- package/dist/features/harness/drive-report.js +18 -0
- package/dist/features/harness/driver.js +4 -7
- package/dist/features/harness/fill-values.d.ts +6 -0
- package/dist/features/harness/fill-values.js +3 -0
- package/dist/features/harness/jev-driver.js +36 -22
- package/dist/features/harness/openai-driver.js +4 -7
- package/dist/features/harness/platform-config.d.ts +10 -0
- package/dist/features/harness/platform-config.js +13 -6
- package/dist/index.d.ts +10 -1
- package/dist/index.js +78 -104
- package/dist/judgement/capsule/capsule-store.d.ts +31 -2
- package/dist/judgement/capsule/capsule-store.js +58 -6
- package/dist/judgement/capsule/capsule.js +5 -0
- package/dist/judgement/capsule/causal-summary.d.ts +13 -1
- package/dist/judgement/capsule/causal-summary.js +7 -0
- package/dist/judgement/domain/domain-model.js +40 -9
- package/dist/judgement/oracles/self-instrument.js +4 -2
- package/dist/judgement/outcome/assertion-integrity.d.ts +3 -3
- package/dist/judgement/runs/artifact/gap-report.d.ts +17 -0
- package/dist/judgement/runs/artifact/gap-report.js +46 -0
- package/dist/judgement/runs/artifact/gap-summary.d.ts +58 -0
- package/dist/judgement/runs/artifact/gap-summary.js +85 -0
- package/dist/judgement/runs/artifact/render-report.js +4 -1
- package/dist/judgement/runs/artifact/replay-mapping.d.ts +2 -2
- package/dist/judgement/runs/artifact/replay-mapping.js +44 -9
- package/dist/judgement/runs/artifact/to-artifact.d.ts +7 -2
- package/dist/judgement/runs/artifact/to-artifact.js +1 -1
- package/dist/judgement/runs/context-tools.js +21 -3
- package/dist/judgement/runs/drive-run.js +7 -2
- package/dist/judgement/runs/verification-sync.d.ts +3 -1
- package/dist/judgement/runs/verification-sync.js +1 -1
- package/dist/language/flows/annotate-notes/annotate.d.ts +0 -15
- package/dist/language/flows/annotate-notes/annotate.js +66 -18
- package/dist/language/flows/change/affected.d.ts +9 -0
- package/dist/language/flows/change/affected.js +35 -0
- package/dist/language/flows/change/gate.d.ts +20 -0
- package/dist/language/flows/change/gate.js +12 -1
- package/dist/language/flows/flow-anchor.d.ts +12 -0
- package/dist/language/flows/flow-anchor.js +20 -0
- package/dist/language/flows/flow-author.d.ts +7 -0
- package/dist/language/flows/flow-author.js +19 -0
- package/dist/language/flows/flow-expect-grammar.d.ts +2 -2
- package/dist/language/flows/flow-expect-grammar.js +92 -11
- package/dist/language/flows/flow-intent.js +1 -1
- package/dist/language/flows/flow-journey.d.ts +54 -0
- package/dist/language/flows/flow-journey.js +119 -0
- package/dist/language/flows/flow-learning.d.ts +3 -0
- package/dist/language/flows/flow-learning.js +39 -8
- package/dist/language/flows/flow-replay-run.d.ts +33 -12
- package/dist/language/flows/flow-replay-run.js +131 -52
- package/dist/language/flows/flow-replay-types.d.ts +9 -0
- package/dist/language/flows/flow-replay.d.ts +27 -12
- package/dist/language/flows/flow-replay.js +127 -111
- package/dist/language/flows/flow-result.d.ts +2 -0
- package/dist/language/flows/flow-result.js +25 -0
- package/dist/language/flows/flow-step-runners.d.ts +26 -10
- package/dist/language/flows/flow-step-runners.js +20 -4
- package/dist/language/flows/flow-success.d.ts +21 -6
- package/dist/language/flows/flow-success.js +50 -138
- package/dist/language/flows/flow-tools.js +17 -5
- package/dist/language/flows/flows.d.ts +18 -5
- package/dist/language/flows/flows.js +101 -16
- package/dist/language/flows/heal.d.ts +9 -1
- package/dist/language/flows/heal.js +24 -2
- package/dist/language/flows/learned-sources.d.ts +21 -0
- package/dist/language/flows/learned-sources.js +84 -0
- package/dist/language/flows/recording/recording-backtrack.d.ts +0 -7
- package/dist/language/flows/recording/recording-backtrack.js +1 -1
- package/dist/language/flows/recording/replay-halt.d.ts +2 -1
- package/dist/language/flows/recording/replay-halt.js +3 -1
- package/dist/language/flows/recording/tape/recordings.d.ts +31 -2
- package/dist/language/flows/recording/tape/recordings.js +55 -1
- package/dist/language/flows/replay.d.ts +19 -0
- package/dist/language/flows/replay.js +53 -13
- package/dist/language/flows/stores/annotation-store.d.ts +5 -5
- package/dist/language/flows/stores/assertion-tiers-store.js +2 -3
- package/dist/language/flows/stores/flake-store.js +2 -3
- package/dist/language/flows/suite/coverage.d.ts +1 -1
- package/dist/language/flows/suite/parallel-suite.d.ts +2 -1
- package/dist/language/flows/verify-change-tools.js +19 -2
- package/dist/launch-chromium.d.ts +80 -0
- package/dist/launch-chromium.js +218 -0
- package/dist/memory/cloud/cloud-config.d.ts +11 -2
- package/dist/memory/cloud/cloud-config.js +47 -7
- package/dist/memory/cloud/cloud-sync.d.ts +4 -3
- package/dist/memory/cloud/cloud-sync.js +7 -10
- package/dist/memory/cloud/harness-config.d.ts +2 -0
- package/dist/memory/cloud/harness-offer.js +5 -6
- package/dist/memory/cloud/harness-switch.js +5 -6
- package/dist/memory/cloud/sync-cycle.d.ts +78 -1
- package/dist/memory/cloud/sync-cycle.js +321 -53
- package/dist/memory/cloud/sync-daemon.d.ts +13 -0
- package/dist/memory/cloud/sync-daemon.js +45 -20
- package/dist/memory/cloud/sync-disk.js +27 -8
- package/dist/memory/impact/impact-store.js +6 -0
- package/dist/memory/intent/inline-intent.js +8 -3
- package/dist/memory/intent/intent-shard-store.d.ts +33 -47
- package/dist/memory/intent/intent-shard-store.js +252 -140
- package/dist/memory/intent/intent-shard.d.ts +0 -23
- package/dist/memory/intent/intent-shard.js +1 -11
- package/dist/memory/intent/intent-store.d.ts +13 -8
- package/dist/memory/intent/intent-store.js +46 -90
- package/dist/memory/intent/intent-tools.js +34 -23
- package/dist/memory/journal/ambient-store.js +2 -3
- package/dist/memory/journal/deviation-report.js +4 -1
- package/dist/memory/journal/deviation-service.js +10 -2
- package/dist/memory/journal/drive-flow.d.ts +19 -14
- package/dist/memory/journal/drive-flow.js +65 -48
- package/dist/memory/journal/envelope-store.js +2 -3
- package/dist/memory/journal/journal-recorder.d.ts +21 -0
- package/dist/memory/journal/on-disk/retain-policy.d.ts +34 -0
- package/dist/memory/journal/on-disk/retain-policy.js +44 -0
- package/dist/memory/journal/on-disk/retention.d.ts +14 -10
- package/dist/memory/journal/on-disk/retention.js +13 -12
- package/dist/memory/journal/on-disk/startup-maintenance.d.ts +42 -0
- package/dist/memory/journal/on-disk/startup-maintenance.js +14 -0
- package/dist/memory/journal/on-disk/workspace-gitignore.d.ts +0 -11
- package/dist/memory/journal/on-disk/workspace-gitignore.js +6 -45
- package/dist/memory/journal/on-disk/workspace-tiers.d.ts +52 -0
- package/dist/memory/journal/on-disk/workspace-tiers.js +90 -0
- package/dist/memory/journal/route-template.d.ts +22 -0
- package/dist/memory/journal/route-template.js +44 -0
- package/dist/memory/journal/session-end.d.ts +13 -2
- package/dist/memory/journal/session-end.js +88 -18
- package/dist/memory/journal/session-journal.d.ts +8 -1
- package/dist/memory/journal/session-journal.js +10 -0
- package/dist/memory/project/baselines.d.ts +1 -1
- package/dist/memory/project/dir/reticle-dir.d.ts +3 -1
- package/dist/memory/project/dir/reticle-dir.js +1 -0
- package/dist/memory/project/fs/fs-port.js +44 -5
- package/dist/memory/project/fs/write-atomic.d.ts +26 -0
- package/dist/memory/project/fs/write-atomic.js +48 -0
- package/dist/memory/project/project-tools.js +3 -1
- package/dist/memory/recall/prior/connection-memory.d.ts +6 -0
- package/dist/memory/recall/prior/connection-memory.js +8 -0
- package/dist/memory/recall/registered-projects.d.ts +2 -1
- package/dist/portal/bridge/auth-failure-reason.d.ts +1 -1
- package/dist/portal/bridge/auth-failure-reason.js +9 -4
- package/dist/portal/bridge/bridge.d.ts +8 -0
- package/dist/portal/bridge/bridge.js +43 -2
- package/dist/portal/input/js-coverage.d.ts +25 -0
- package/dist/portal/input/js-coverage.js +30 -0
- package/dist/portal/input/real-input.d.ts +11 -0
- package/dist/portal/input/real-input.js +31 -5
- package/dist/portal/pool/browser-pool.d.ts +10 -0
- package/dist/portal/pool/browser-pool.js +10 -0
- package/dist/portal/pool/playwright-launcher.d.ts +6 -0
- package/dist/portal/pool/playwright-launcher.js +21 -13
- package/dist/portal/pool/pool-contract.d.ts +2 -0
- package/dist/portal/pool/storage-seed.d.ts +2 -1
- package/dist/portal/pool/zero-install.d.ts +27 -0
- package/dist/portal/pool/zero-install.js +54 -0
- package/dist/portal/realm/web-realm.js +6 -0
- package/dist/portal/session/dev-server/dev-command.d.ts +2 -2
- package/dist/portal/session/dev-server/dev-command.js +30 -12
- package/dist/portal/session/dev-server/dev-server-probe.d.ts +2 -1
- package/dist/portal/session/dev-server/dev-server-probe.js +17 -1
- package/dist/portal/session/dev-server/route-status-probe.d.ts +24 -0
- package/dist/portal/session/dev-server/route-status-probe.js +57 -0
- package/dist/portal/session/dev-server/served-document.d.ts +21 -0
- package/dist/portal/session/dev-server/served-document.js +57 -0
- package/dist/portal/session/facts/background-traffic.d.ts +9 -0
- package/dist/portal/session/facts/background-traffic.js +25 -0
- package/dist/portal/session/facts/observed-state.d.ts +11 -0
- package/dist/portal/session/facts/observed-state.js +34 -0
- package/dist/portal/session/live-control-tools.js +48 -3
- package/dist/portal/session/no-session-diagnosis.d.ts +29 -0
- package/dist/portal/session/no-session-diagnosis.js +62 -5
- package/dist/portal/session/no-session-next-action.d.ts +13 -0
- package/dist/portal/session/no-session-next-action.js +48 -9
- package/dist/portal/session/no-session-watch.d.ts +18 -0
- package/dist/portal/session/no-session-watch.js +88 -2
- package/dist/portal/session/presence/attachment-history.d.ts +13 -2
- package/dist/portal/session/session-health.d.ts +61 -0
- package/dist/portal/session/session-health.js +43 -3
- package/dist/portal/session/session-info.d.ts +6 -0
- package/dist/portal/session/session-manager.d.ts +29 -6
- package/dist/portal/session/session-manager.js +42 -5
- package/dist/portal/session/session-reaper.d.ts +28 -0
- package/dist/portal/session/session-reaper.js +49 -1
- package/dist/portal/session/session-successor.d.ts +11 -0
- package/dist/portal/session/session-successor.js +14 -1
- package/dist/portal/session/session-verdict-facts.d.ts +28 -0
- package/dist/portal/session/session-verdict-facts.js +15 -0
- package/dist/portal/session/session.d.ts +10 -0
- package/dist/portal/session/session.js +34 -26
- package/dist/surface/http-server.d.ts +2 -1
- package/dist/surface/http-server.js +4 -2
- package/dist/surface/mcp/mcp-proxy.d.ts +2 -2
- package/dist/surface/mcp/mcp-proxy.js +5 -5
- package/dist/surface/mcp/mcp.js +4 -2
- package/dist/surface/mcp/proxy/proxy-daemon-probe.d.ts +13 -1
- package/dist/surface/mcp/proxy/proxy-daemon-probe.js +27 -2
- package/dist/surface/mcp/proxy-idle-exit.d.ts +2 -1
- package/dist/surface/mcp/server-instructions.js +21 -8
- package/dist/surface/tools/act/after-match.d.ts +17 -0
- package/dist/surface/tools/act/after-match.js +30 -0
- package/dist/surface/tools/act/already-true.d.ts +40 -0
- package/dist/surface/tools/act/already-true.js +36 -0
- package/dist/surface/tools/act/durable.d.ts +69 -0
- package/dist/surface/tools/act/durable.js +139 -0
- package/dist/surface/tools/act/navigation/navigate-arrival.d.ts +19 -6
- package/dist/surface/tools/act/navigation/navigate-arrival.js +60 -10
- package/dist/surface/tools/act/navigation/navigate-result.d.ts +1 -12
- package/dist/surface/tools/act/navigation/navigate-result.js +21 -3
- package/dist/surface/tools/act/proved-controls.d.ts +9 -0
- package/dist/surface/tools/act/proved-controls.js +11 -0
- package/dist/surface/tools/act/sequence-grade.d.ts +5 -0
- package/dist/surface/tools/act/sequence-grade.js +32 -1
- package/dist/surface/tools/act/settle-in-flight.d.ts +6 -2
- package/dist/surface/tools/act/settle-in-flight.js +29 -6
- package/dist/surface/tools/act-capsule.d.ts +7 -0
- package/dist/surface/tools/act-capsule.js +68 -8
- package/dist/surface/tools/act-sequence-tool.js +25 -8
- package/dist/surface/tools/act-tools.js +51 -74
- package/dist/surface/tools/args/numeric-bounds.d.ts +15 -2
- package/dist/surface/tools/args/numeric-bounds.js +30 -3
- package/dist/surface/tools/assert/assert-grade.js +26 -3
- package/dist/surface/tools/assert/assert-verdict.js +32 -6
- package/dist/surface/tools/browser-tools.js +17 -4
- package/dist/surface/tools/coverage-tools.js +27 -1
- package/dist/surface/tools/dynamic-tools.js +6 -0
- package/dist/surface/tools/error-recovery.js +4 -4
- package/dist/surface/tools/feedback-invite.js +6 -4
- package/dist/surface/tools/feedback-tools.d.ts +2 -1
- package/dist/surface/tools/feedback-tools.js +1 -1
- package/dist/surface/tools/harness-explore.d.ts +3 -2
- package/dist/surface/tools/harness-explore.js +7 -6
- package/dist/surface/tools/invoke-tool.js +17 -4
- package/dist/surface/tools/lease-hint.d.ts +8 -0
- package/dist/surface/tools/lease-hint.js +10 -0
- package/dist/surface/tools/lease-tools.d.ts +16 -28
- package/dist/surface/tools/lease-tools.js +68 -11
- package/dist/surface/tools/lineage-tools.js +1 -1
- package/dist/surface/tools/live-call-text.js +4 -1
- package/dist/surface/tools/observe-tools.js +23 -7
- package/dist/surface/tools/real-input-attempt.d.ts +0 -5
- package/dist/surface/tools/real-input-attempt.js +1 -1
- package/dist/surface/tools/tool-kit.d.ts +5 -0
- package/dist/surface/tools/tool-kit.js +6 -1
- package/dist/surface/tools/tool-surface.js +17 -5
- package/dist/surface/tools/tools.js +68 -29
- package/dist/telemetry/argument-shape.d.ts +0 -2
- package/dist/telemetry/argument-shape.js +0 -4
- package/dist/telemetry/error-class.d.ts +2 -3
- package/dist/telemetry/error-class.js +2 -3
- package/dist/telemetry/feedback-local.d.ts +1 -0
- package/dist/telemetry/feedback-local.js +1 -3
- package/dist/telemetry/feedback.d.ts +1 -1
- package/dist/telemetry/harness-drive.d.ts +0 -2
- package/dist/telemetry/harness-drive.js +0 -4
- package/dist/telemetry/hud-metrics.d.ts +32 -0
- package/dist/telemetry/hud-metrics.js +95 -0
- package/dist/telemetry/session-metrics.d.ts +5 -0
- package/dist/telemetry/session-metrics.js +15 -1
- package/dist/wire-journal.d.ts +34 -0
- package/dist/wire-journal.js +104 -0
- package/dist/workspace-packages.d.ts +0 -2
- package/dist/workspace-packages.js +1 -9
- package/docs/README.md +5 -2
- package/docs/actions.mdx +3 -3
- package/docs/agent-cheatsheet.md +4 -4
- package/docs/architecture.md +6 -6
- package/docs/best-practices.mdx +1 -1
- package/docs/capabilities.mdx +32 -1
- package/docs/cli/cloud.mdx +21 -9
- package/docs/cli/doctor.mdx +4 -0
- package/docs/cli/feedback.mdx +2 -2
- package/docs/cli/gate.mdx +9 -4
- package/docs/cli/init.mdx +1 -0
- package/docs/cli/report.mdx +44 -0
- package/docs/cli/update.mdx +10 -0
- package/docs/cli.mdx +9 -0
- package/docs/desktop.mdx +14 -10
- package/docs/environment.mdx +87 -0
- package/docs/faq.mdx +18 -16
- package/docs/first-drive.md +5 -13
- package/docs/flows.md +32 -2
- package/docs/frameworks.mdx +140 -9
- package/docs/getting-started.md +3 -3
- package/docs/http-transport.md +6 -3
- package/docs/human-control.md +2 -2
- package/docs/index.mdx +16 -6
- package/docs/install-agentic.mdx +37 -1
- package/docs/install-manual.mdx +1 -1
- package/docs/integration-patterns.md +3 -3
- package/docs/multi-agent-testing.md +1 -1
- package/docs/packages/babel-plugin.mdx +1 -1
- package/docs/packages/browser.mdx +2 -2
- package/docs/packages/react.mdx +2 -2
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages/vite-plugin.mdx +2 -2
- package/docs/packages.mdx +9 -9
- package/docs/predicates.mdx +176 -7
- package/docs/quickstart.mdx +2 -2
- package/docs/recipes.mdx +2 -2
- package/docs/state-management.mdx +2 -2
- package/docs/system-map.md +1 -1
- package/docs/telemetry.md +1 -1
- package/docs/token-efficiency.md +3 -3
- package/docs/tools/act-and-wait.mdx +1 -0
- package/docs/tools/act-sequence.mdx +1 -1
- package/docs/tools/console.mdx +1 -1
- package/docs/tools/inspect.mdx +1 -1
- package/docs/tools/network.mdx +1 -1
- package/docs/tools/query.mdx +11 -11
- package/docs/tools/sessions.mdx +1 -1
- package/docs/tools/snapshot.mdx +1 -1
- package/docs/tools/state.mdx +1 -1
- package/docs/tools/wait-for.mdx +1 -1
- package/docs/usage.md +8 -7
- package/docs/use-cases.mdx +157 -0
- package/docs/vs-screenshots.mdx +3 -3
- package/docs/what-is-recorded.md +62 -0
- package/docs/what-is-reticle.mdx +17 -20
- package/docs/why-reticle.mdx +14 -21
- package/package.json +11 -10
- package/dist/judgement/capsule/minimize.d.ts +0 -12
- package/dist/judgement/capsule/minimize.js +0 -22
- package/dist/judgement/outcome/predicate-to-expect.d.ts +0 -44
- package/dist/judgement/outcome/predicate-to-expect.js +0 -148
- package/dist/judgement/phenomena/phenomena.d.ts +0 -26
- package/dist/judgement/phenomena/phenomena.js +0 -101
- package/dist/language/flows/flow-report.d.ts +0 -22
- package/dist/language/flows/flow-report.js +0 -106
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app-wide coverage ledger: what "everything" is, and how much of it has been proved.
|
|
3
|
+
*
|
|
4
|
+
* "Done" used to be the agent's opinion, which is the root of shallow verification: every rule it
|
|
5
|
+
* read pushed it to stop, and nothing it could read said what was left. This makes it a number per
|
|
6
|
+
* level, each with the missing items named, accumulated across every drive and every session.
|
|
7
|
+
*
|
|
8
|
+
* Six levels, each stricter than the last, and never averaged — an average is exactly how "the
|
|
9
|
+
* controls were clicked" hides "none of them were proved":
|
|
10
|
+
*
|
|
11
|
+
* reached — routes rendered, of routes discovered
|
|
12
|
+
* touched — controls acted on, of controls seen
|
|
13
|
+
* proved — controls with a `yes` verdict on a declared consequence, of controls seen
|
|
14
|
+
* completed — journeys proved at their end (count only: there is no honest denominator)
|
|
15
|
+
* branched — writes whose FAILURE path was driven, of writes seen
|
|
16
|
+
* executed — app functions that ran, of app functions loaded
|
|
17
|
+
*
|
|
18
|
+
* Union-only. A later drive can add coverage and never remove it, so the number only moves down when
|
|
19
|
+
* the app grows something nobody has covered yet — which is precisely what the ratchet is for.
|
|
20
|
+
*/
|
|
21
|
+
import { z } from 'zod';
|
|
22
|
+
import type { FileSystemPort } from '../../memory/project/fs/fs-port.js';
|
|
23
|
+
import type { CodeCoverage } from './code-coverage.js';
|
|
24
|
+
export declare const CoverageLevel: {
|
|
25
|
+
readonly REACHED: "reached";
|
|
26
|
+
readonly TOUCHED: "touched";
|
|
27
|
+
readonly PROVED: "proved";
|
|
28
|
+
readonly COMPLETED: "completed";
|
|
29
|
+
readonly BRANCHED: "branched";
|
|
30
|
+
readonly EXECUTED: "executed";
|
|
31
|
+
};
|
|
32
|
+
export type CoverageLevel = (typeof CoverageLevel)[keyof typeof CoverageLevel];
|
|
33
|
+
declare const LedgerSchema: z.ZodObject<{
|
|
34
|
+
routes: z.ZodObject<{
|
|
35
|
+
discovered: z.ZodArray<z.ZodString, "many">;
|
|
36
|
+
reached: z.ZodArray<z.ZodString, "many">;
|
|
37
|
+
}, "strip", z.ZodTypeAny, {
|
|
38
|
+
reached: string[];
|
|
39
|
+
discovered: string[];
|
|
40
|
+
}, {
|
|
41
|
+
reached: string[];
|
|
42
|
+
discovered: string[];
|
|
43
|
+
}>;
|
|
44
|
+
controls: z.ZodObject<{
|
|
45
|
+
seen: z.ZodArray<z.ZodString, "many">;
|
|
46
|
+
touched: z.ZodArray<z.ZodString, "many">;
|
|
47
|
+
proved: z.ZodArray<z.ZodString, "many">;
|
|
48
|
+
}, "strip", z.ZodTypeAny, {
|
|
49
|
+
proved: string[];
|
|
50
|
+
touched: string[];
|
|
51
|
+
seen: string[];
|
|
52
|
+
}, {
|
|
53
|
+
proved: string[];
|
|
54
|
+
touched: string[];
|
|
55
|
+
seen: string[];
|
|
56
|
+
}>;
|
|
57
|
+
journeys: z.ZodObject<{
|
|
58
|
+
completed: z.ZodArray<z.ZodString, "many">;
|
|
59
|
+
}, "strip", z.ZodTypeAny, {
|
|
60
|
+
completed: string[];
|
|
61
|
+
}, {
|
|
62
|
+
completed: string[];
|
|
63
|
+
}>;
|
|
64
|
+
writes: z.ZodObject<{
|
|
65
|
+
seen: z.ZodArray<z.ZodString, "many">;
|
|
66
|
+
branched: z.ZodArray<z.ZodString, "many">;
|
|
67
|
+
unhandled: z.ZodArray<z.ZodString, "many">;
|
|
68
|
+
}, "strip", z.ZodTypeAny, {
|
|
69
|
+
branched: string[];
|
|
70
|
+
seen: string[];
|
|
71
|
+
unhandled: string[];
|
|
72
|
+
}, {
|
|
73
|
+
branched: string[];
|
|
74
|
+
seen: string[];
|
|
75
|
+
unhandled: string[];
|
|
76
|
+
}>;
|
|
77
|
+
code: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
78
|
+
name: z.ZodString;
|
|
79
|
+
executed: z.ZodBoolean;
|
|
80
|
+
}, "strip", z.ZodTypeAny, {
|
|
81
|
+
name: string;
|
|
82
|
+
executed: boolean;
|
|
83
|
+
}, {
|
|
84
|
+
name: string;
|
|
85
|
+
executed: boolean;
|
|
86
|
+
}>>>;
|
|
87
|
+
/** Per file, when its code coverage was last taken. Absent in a ledger from before it existed. */
|
|
88
|
+
codeTaken: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
89
|
+
best: z.ZodRecord<z.ZodString, z.ZodNumber>;
|
|
90
|
+
}, "strip", z.ZodTypeAny, {
|
|
91
|
+
code: Record<string, Record<string, {
|
|
92
|
+
name: string;
|
|
93
|
+
executed: boolean;
|
|
94
|
+
}>>;
|
|
95
|
+
best: Record<string, number>;
|
|
96
|
+
routes: {
|
|
97
|
+
reached: string[];
|
|
98
|
+
discovered: string[];
|
|
99
|
+
};
|
|
100
|
+
controls: {
|
|
101
|
+
proved: string[];
|
|
102
|
+
touched: string[];
|
|
103
|
+
seen: string[];
|
|
104
|
+
};
|
|
105
|
+
journeys: {
|
|
106
|
+
completed: string[];
|
|
107
|
+
};
|
|
108
|
+
writes: {
|
|
109
|
+
branched: string[];
|
|
110
|
+
seen: string[];
|
|
111
|
+
unhandled: string[];
|
|
112
|
+
};
|
|
113
|
+
codeTaken: Record<string, number>;
|
|
114
|
+
}, {
|
|
115
|
+
code: Record<string, Record<string, {
|
|
116
|
+
name: string;
|
|
117
|
+
executed: boolean;
|
|
118
|
+
}>>;
|
|
119
|
+
best: Record<string, number>;
|
|
120
|
+
routes: {
|
|
121
|
+
reached: string[];
|
|
122
|
+
discovered: string[];
|
|
123
|
+
};
|
|
124
|
+
controls: {
|
|
125
|
+
proved: string[];
|
|
126
|
+
touched: string[];
|
|
127
|
+
seen: string[];
|
|
128
|
+
};
|
|
129
|
+
journeys: {
|
|
130
|
+
completed: string[];
|
|
131
|
+
};
|
|
132
|
+
writes: {
|
|
133
|
+
branched: string[];
|
|
134
|
+
seen: string[];
|
|
135
|
+
unhandled: string[];
|
|
136
|
+
};
|
|
137
|
+
codeTaken?: Record<string, number> | undefined;
|
|
138
|
+
}>;
|
|
139
|
+
export type AppLedger = z.infer<typeof LedgerSchema>;
|
|
140
|
+
/** What one producer contributes. Every part optional: a drive adds what it saw and nothing else. */
|
|
141
|
+
export interface LedgerDelta {
|
|
142
|
+
routes?: Partial<AppLedger['routes']>;
|
|
143
|
+
controls?: Partial<AppLedger['controls']>;
|
|
144
|
+
journeys?: Partial<AppLedger['journeys']>;
|
|
145
|
+
writes?: Partial<AppLedger['writes']>;
|
|
146
|
+
code?: CodeCoverage;
|
|
147
|
+
/** When `code` was taken, so a file edited since reads as not measured rather than as covered. */
|
|
148
|
+
codeTakenAt?: number;
|
|
149
|
+
}
|
|
150
|
+
export interface LevelReport {
|
|
151
|
+
level: CoverageLevel;
|
|
152
|
+
covered: number;
|
|
153
|
+
/** Absent where no honest denominator exists. */
|
|
154
|
+
total?: number;
|
|
155
|
+
/** Absent when nothing was measured: 0 of 0 is not 100%. */
|
|
156
|
+
pct?: number;
|
|
157
|
+
missing: string[];
|
|
158
|
+
/** How many more are missing than `missing` names. */
|
|
159
|
+
missingOverflow?: number;
|
|
160
|
+
}
|
|
161
|
+
export declare function emptyLedger(): AppLedger;
|
|
162
|
+
export declare function mergeLedger(into: AppLedger, delta: LedgerDelta): AppLedger;
|
|
163
|
+
export declare function levelsOf(ledger: AppLedger): LevelReport[];
|
|
164
|
+
/** The best percentage each level has reached, raised to today's where today is higher. */
|
|
165
|
+
export declare function raiseBest(ledger: AppLedger): AppLedger;
|
|
166
|
+
/**
|
|
167
|
+
* Today's levels become the best, so a drop someone decided to accept stops blocking. What was
|
|
168
|
+
* measured stays; only the bar moves, and only when asked (`reticle gate --accept-coverage`).
|
|
169
|
+
*/
|
|
170
|
+
export declare function acceptCurrent(ledger: AppLedger): AppLedger;
|
|
171
|
+
/** Levels now below the best they ever reached — something new appeared and nothing covered it. */
|
|
172
|
+
export declare function regressions(ledger: AppLedger): {
|
|
173
|
+
level: CoverageLevel;
|
|
174
|
+
was: number;
|
|
175
|
+
now: number;
|
|
176
|
+
}[];
|
|
177
|
+
/** Changed files the browser loaded and never ran a single function of. */
|
|
178
|
+
export declare function unexecutedChanged(code: CodeCoverage, changed: readonly string[]): string[];
|
|
179
|
+
/**
|
|
180
|
+
* Changed files whose coverage was last taken BEFORE they changed. Counts are kept per file across
|
|
181
|
+
* takes, so an edited file still carries the counts of the version before the edit, and "it ran"
|
|
182
|
+
* read from them is about code that no longer exists. A take with no recorded time is as old as
|
|
183
|
+
* any edit. A file with no modification time (deleted) has nothing left to run.
|
|
184
|
+
*/
|
|
185
|
+
export declare function staleChanged(ledger: AppLedger, changed: readonly string[], modifiedAt: (file: string) => number | undefined): string[];
|
|
186
|
+
/** `.reticle/coverage.json`. Loads never throw: a bad file degrades to an empty ledger. */
|
|
187
|
+
export declare class LedgerStore {
|
|
188
|
+
#private;
|
|
189
|
+
constructor(fs: FileSystemPort, root: string);
|
|
190
|
+
load(): Promise<AppLedger>;
|
|
191
|
+
/**
|
|
192
|
+
* Fold a delta in, raise the best, persist, and hand back the result. Under the file's lock: two
|
|
193
|
+
* sessions folding at once each loaded the same copy, and the later write discarded the other.
|
|
194
|
+
*
|
|
195
|
+
* ponytail: the lock is per daemon process. The CLI's `gate --accept-coverage` in another process
|
|
196
|
+
* can still race a fold; add a lock file if that ever shows up.
|
|
197
|
+
*/
|
|
198
|
+
merge(delta: LedgerDelta): Promise<AppLedger>;
|
|
199
|
+
/** Accept today's levels as the best. Returns the drops that were accepted. */
|
|
200
|
+
acceptCurrent(): Promise<ReturnType<typeof regressions>>;
|
|
201
|
+
}
|
|
202
|
+
export {};
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app-wide coverage ledger: what "everything" is, and how much of it has been proved.
|
|
3
|
+
*
|
|
4
|
+
* "Done" used to be the agent's opinion, which is the root of shallow verification: every rule it
|
|
5
|
+
* read pushed it to stop, and nothing it could read said what was left. This makes it a number per
|
|
6
|
+
* level, each with the missing items named, accumulated across every drive and every session.
|
|
7
|
+
*
|
|
8
|
+
* Six levels, each stricter than the last, and never averaged — an average is exactly how "the
|
|
9
|
+
* controls were clicked" hides "none of them were proved":
|
|
10
|
+
*
|
|
11
|
+
* reached — routes rendered, of routes discovered
|
|
12
|
+
* touched — controls acted on, of controls seen
|
|
13
|
+
* proved — controls with a `yes` verdict on a declared consequence, of controls seen
|
|
14
|
+
* completed — journeys proved at their end (count only: there is no honest denominator)
|
|
15
|
+
* branched — writes whose FAILURE path was driven, of writes seen
|
|
16
|
+
* executed — app functions that ran, of app functions loaded
|
|
17
|
+
*
|
|
18
|
+
* Union-only. A later drive can add coverage and never remove it, so the number only moves down when
|
|
19
|
+
* the app grows something nobody has covered yet — which is precisely what the ratchet is for.
|
|
20
|
+
*/
|
|
21
|
+
import { dirname } from 'node:path';
|
|
22
|
+
import { z } from 'zod';
|
|
23
|
+
import { writeFileAtomic } from '../../memory/project/fs/write-atomic.js';
|
|
24
|
+
import { withFileLock } from '../../memory/project/file-lock.js';
|
|
25
|
+
import { reticleDirPaths } from '../../memory/project/dir/reticle-dir.js';
|
|
26
|
+
export const CoverageLevel = {
|
|
27
|
+
REACHED: 'reached',
|
|
28
|
+
TOUCHED: 'touched',
|
|
29
|
+
PROVED: 'proved',
|
|
30
|
+
COMPLETED: 'completed',
|
|
31
|
+
BRANCHED: 'branched',
|
|
32
|
+
EXECUTED: 'executed',
|
|
33
|
+
};
|
|
34
|
+
const LEDGER_VERSION = 1;
|
|
35
|
+
/** How many missing items a level names before it only counts them. */
|
|
36
|
+
const MAX_MISSING = 50;
|
|
37
|
+
const names = z.array(z.string());
|
|
38
|
+
const LedgerSchema = z.object({
|
|
39
|
+
routes: z.object({ discovered: names, reached: names }),
|
|
40
|
+
controls: z.object({ seen: names, touched: names, proved: names }),
|
|
41
|
+
journeys: z.object({ completed: names }),
|
|
42
|
+
writes: z.object({ seen: names, branched: names, unhandled: names }),
|
|
43
|
+
code: z.record(z.record(z.object({ name: z.string(), executed: z.boolean() }))),
|
|
44
|
+
/** Per file, when its code coverage was last taken. Absent in a ledger from before it existed. */
|
|
45
|
+
codeTaken: z.record(z.number()).default({}),
|
|
46
|
+
best: z.record(z.number()),
|
|
47
|
+
});
|
|
48
|
+
export function emptyLedger() {
|
|
49
|
+
return {
|
|
50
|
+
routes: { discovered: [], reached: [] },
|
|
51
|
+
controls: { seen: [], touched: [], proved: [] },
|
|
52
|
+
journeys: { completed: [] },
|
|
53
|
+
writes: { seen: [], branched: [], unhandled: [] },
|
|
54
|
+
code: {},
|
|
55
|
+
codeTaken: {},
|
|
56
|
+
best: {},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const union = (a, b) => b === undefined ? [...a] : [...new Set([...a, ...b])];
|
|
60
|
+
export function mergeLedger(into, delta) {
|
|
61
|
+
const code = { ...into.code };
|
|
62
|
+
const codeTaken = { ...into.codeTaken };
|
|
63
|
+
for (const [file, fns] of Object.entries(delta.code ?? {})) {
|
|
64
|
+
const merged = { ...(code[file] ?? {}) };
|
|
65
|
+
for (const [key, fn] of Object.entries(fns)) {
|
|
66
|
+
merged[key] = { name: fn.name, executed: fn.executed || true === merged[key]?.executed };
|
|
67
|
+
}
|
|
68
|
+
code[file] = merged;
|
|
69
|
+
if (delta.codeTakenAt !== undefined)
|
|
70
|
+
codeTaken[file] = delta.codeTakenAt;
|
|
71
|
+
}
|
|
72
|
+
// Reaching a route discovers it; touching or proving a control means it was seen.
|
|
73
|
+
const reached = union(into.routes.reached, delta.routes?.reached);
|
|
74
|
+
const touched = union(into.controls.touched, delta.controls?.touched);
|
|
75
|
+
const proved = union(into.controls.proved, delta.controls?.proved);
|
|
76
|
+
const branched = union(into.writes.branched, delta.writes?.branched);
|
|
77
|
+
return {
|
|
78
|
+
routes: {
|
|
79
|
+
discovered: union(union(into.routes.discovered, delta.routes?.discovered), reached),
|
|
80
|
+
reached,
|
|
81
|
+
},
|
|
82
|
+
controls: {
|
|
83
|
+
seen: union(union(union(into.controls.seen, delta.controls?.seen), touched), proved),
|
|
84
|
+
touched: union(touched, proved),
|
|
85
|
+
proved,
|
|
86
|
+
},
|
|
87
|
+
journeys: { completed: union(into.journeys.completed, delta.journeys?.completed) },
|
|
88
|
+
writes: {
|
|
89
|
+
seen: union(union(into.writes.seen, delta.writes?.seen), branched),
|
|
90
|
+
branched,
|
|
91
|
+
unhandled: union(into.writes.unhandled, delta.writes?.unhandled),
|
|
92
|
+
},
|
|
93
|
+
code,
|
|
94
|
+
codeTaken,
|
|
95
|
+
best: { ...into.best },
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function level(name, universe, covered) {
|
|
99
|
+
const done = new Set(covered);
|
|
100
|
+
const missing = universe.filter((item) => !done.has(item));
|
|
101
|
+
const count = universe.length - missing.length;
|
|
102
|
+
return {
|
|
103
|
+
level: name,
|
|
104
|
+
covered: count,
|
|
105
|
+
total: universe.length,
|
|
106
|
+
...(0 === universe.length ? {} : { pct: Math.round((100 * count) / universe.length) }),
|
|
107
|
+
missing: missing.slice(0, MAX_MISSING),
|
|
108
|
+
...(missing.length > MAX_MISSING ? { missingOverflow: missing.length - MAX_MISSING } : {}),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/** Functions are identified by file + key (names repeat) and reported by file + name. */
|
|
112
|
+
function executedLevel(code) {
|
|
113
|
+
const fns = Object.entries(code).flatMap(([file, map]) => Object.values(map).map((fn) => ({ label: `${file}: ${fn.name}`, executed: fn.executed })));
|
|
114
|
+
const missing = fns.filter((fn) => !fn.executed).map((fn) => fn.label);
|
|
115
|
+
const covered = fns.length - missing.length;
|
|
116
|
+
return {
|
|
117
|
+
level: CoverageLevel.EXECUTED,
|
|
118
|
+
covered,
|
|
119
|
+
total: fns.length,
|
|
120
|
+
...(0 === fns.length ? {} : { pct: Math.round((100 * covered) / fns.length) }),
|
|
121
|
+
missing: missing.slice(0, MAX_MISSING),
|
|
122
|
+
...(missing.length > MAX_MISSING ? { missingOverflow: missing.length - MAX_MISSING } : {}),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export function levelsOf(ledger) {
|
|
126
|
+
return [
|
|
127
|
+
level(CoverageLevel.REACHED, ledger.routes.discovered, ledger.routes.reached),
|
|
128
|
+
level(CoverageLevel.TOUCHED, ledger.controls.seen, ledger.controls.touched),
|
|
129
|
+
level(CoverageLevel.PROVED, ledger.controls.seen, ledger.controls.proved),
|
|
130
|
+
{ level: CoverageLevel.COMPLETED, covered: ledger.journeys.completed.length, missing: [] },
|
|
131
|
+
level(CoverageLevel.BRANCHED, ledger.writes.seen, ledger.writes.branched),
|
|
132
|
+
executedLevel(ledger.code),
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
/** The best percentage each level has reached, raised to today's where today is higher. */
|
|
136
|
+
export function raiseBest(ledger) {
|
|
137
|
+
const best = { ...ledger.best };
|
|
138
|
+
for (const l of levelsOf(ledger)) {
|
|
139
|
+
if (l.pct !== undefined && l.pct > (best[l.level] ?? -1))
|
|
140
|
+
best[l.level] = l.pct;
|
|
141
|
+
}
|
|
142
|
+
return { ...ledger, best };
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Today's levels become the best, so a drop someone decided to accept stops blocking. What was
|
|
146
|
+
* measured stays; only the bar moves, and only when asked (`reticle gate --accept-coverage`).
|
|
147
|
+
*/
|
|
148
|
+
export function acceptCurrent(ledger) {
|
|
149
|
+
const best = {};
|
|
150
|
+
for (const l of levelsOf(ledger))
|
|
151
|
+
if (l.pct !== undefined)
|
|
152
|
+
best[l.level] = l.pct;
|
|
153
|
+
return { ...ledger, best };
|
|
154
|
+
}
|
|
155
|
+
/** Levels now below the best they ever reached — something new appeared and nothing covered it. */
|
|
156
|
+
export function regressions(ledger) {
|
|
157
|
+
const out = [];
|
|
158
|
+
for (const l of levelsOf(ledger)) {
|
|
159
|
+
const was = ledger.best[l.level];
|
|
160
|
+
if (l.pct !== undefined && was !== undefined && l.pct < was)
|
|
161
|
+
out.push({ level: l.level, was, now: l.pct });
|
|
162
|
+
}
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The ledger's name for a changed file. Matched by path suffix, the same way the flow index matches,
|
|
167
|
+
* so a repo-relative change finds a served `src/...` path.
|
|
168
|
+
*/
|
|
169
|
+
function servedAs(code, file) {
|
|
170
|
+
return Object.keys(code).find((served) => file === served || file.endsWith(`/${served}`) || served.endsWith(`/${file}`));
|
|
171
|
+
}
|
|
172
|
+
/** Changed files the browser loaded and never ran a single function of. */
|
|
173
|
+
export function unexecutedChanged(code, changed) {
|
|
174
|
+
return changed.filter((file) => {
|
|
175
|
+
const served = servedAs(code, file);
|
|
176
|
+
if (served === undefined)
|
|
177
|
+
return false;
|
|
178
|
+
const fns = Object.values(code[served] ?? {});
|
|
179
|
+
return fns.length > 0 && fns.every((fn) => !fn.executed);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Changed files whose coverage was last taken BEFORE they changed. Counts are kept per file across
|
|
184
|
+
* takes, so an edited file still carries the counts of the version before the edit, and "it ran"
|
|
185
|
+
* read from them is about code that no longer exists. A take with no recorded time is as old as
|
|
186
|
+
* any edit. A file with no modification time (deleted) has nothing left to run.
|
|
187
|
+
*/
|
|
188
|
+
export function staleChanged(ledger, changed, modifiedAt) {
|
|
189
|
+
return changed.filter((file) => {
|
|
190
|
+
const served = servedAs(ledger.code, file);
|
|
191
|
+
const modified = modifiedAt(file);
|
|
192
|
+
if (served === undefined || modified === undefined)
|
|
193
|
+
return false;
|
|
194
|
+
const taken = ledger.codeTaken[served];
|
|
195
|
+
return taken === undefined || modified > taken;
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
/** `.reticle/coverage.json`. Loads never throw: a bad file degrades to an empty ledger. */
|
|
199
|
+
export class LedgerStore {
|
|
200
|
+
#fs;
|
|
201
|
+
#path;
|
|
202
|
+
constructor(fs, root) {
|
|
203
|
+
this.#fs = fs;
|
|
204
|
+
this.#path = reticleDirPaths(root).coverage;
|
|
205
|
+
}
|
|
206
|
+
async load() {
|
|
207
|
+
let text;
|
|
208
|
+
try {
|
|
209
|
+
text = await this.#fs.readFile(this.#path);
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
if (this.#fs.isNotFound(error))
|
|
213
|
+
return emptyLedger();
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
const parsed = z
|
|
218
|
+
.object({ version: z.literal(LEDGER_VERSION), ledger: LedgerSchema })
|
|
219
|
+
.safeParse(JSON.parse(text));
|
|
220
|
+
return parsed.success ? parsed.data.ledger : emptyLedger();
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return emptyLedger();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Fold a delta in, raise the best, persist, and hand back the result. Under the file's lock: two
|
|
228
|
+
* sessions folding at once each loaded the same copy, and the later write discarded the other.
|
|
229
|
+
*
|
|
230
|
+
* ponytail: the lock is per daemon process. The CLI's `gate --accept-coverage` in another process
|
|
231
|
+
* can still race a fold; add a lock file if that ever shows up.
|
|
232
|
+
*/
|
|
233
|
+
merge(delta) {
|
|
234
|
+
return withFileLock(this.#path, async () => this.#save(raiseBest(mergeLedger(await this.load(), delta))));
|
|
235
|
+
}
|
|
236
|
+
/** Accept today's levels as the best. Returns the drops that were accepted. */
|
|
237
|
+
acceptCurrent() {
|
|
238
|
+
return withFileLock(this.#path, async () => {
|
|
239
|
+
const ledger = await this.load();
|
|
240
|
+
const accepted = regressions(ledger);
|
|
241
|
+
if (accepted.length > 0)
|
|
242
|
+
await this.#save(acceptCurrent(ledger));
|
|
243
|
+
return accepted;
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
async #save(next) {
|
|
247
|
+
await this.#fs.mkdir(dirname(this.#path));
|
|
248
|
+
const body = `${JSON.stringify({ version: LEDGER_VERSION, ledger: next })}\n`;
|
|
249
|
+
await writeFileAtomic(this.#fs, this.#path, body);
|
|
250
|
+
return next;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One session's evidence, as a contribution to the coverage ledger.
|
|
3
|
+
*
|
|
4
|
+
* Pure over what the caller read: the controls on the page, the labels the session drove and proved,
|
|
5
|
+
* where it is, and the events it holds. The ledger does the accumulating; this only says what one
|
|
6
|
+
* session saw.
|
|
7
|
+
*/
|
|
8
|
+
import { type ReticleEvent } from '@reticlehq/core';
|
|
9
|
+
import type { LedgerDelta } from './ledger.js';
|
|
10
|
+
/**
|
|
11
|
+
* `POST /api/orders/:id/refund` for a mutating request, else undefined. Ids are collapsed so the
|
|
12
|
+
* refund of order 41 and of order 42 are one endpoint with one failure path to drive, not two.
|
|
13
|
+
*/
|
|
14
|
+
export declare function writeKeyOf(event: ReticleEvent): string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* The ledger's identity for a control: the snapshot label, with numbers collapsed. The rows of a
|
|
17
|
+
* table — `button "Delete row 12"` — are one control to cover, not five hundred, which is what keeps
|
|
18
|
+
* `touched` meaningful on any page with a list.
|
|
19
|
+
*/
|
|
20
|
+
export declare function controlKey(label: string): string;
|
|
21
|
+
export declare function sessionDelta(input: {
|
|
22
|
+
seen: readonly string[];
|
|
23
|
+
acted: Iterable<string>;
|
|
24
|
+
proved: Iterable<string>;
|
|
25
|
+
url?: string;
|
|
26
|
+
events: readonly ReticleEvent[];
|
|
27
|
+
}): LedgerDelta;
|
|
28
|
+
/** An error the page threw or logged — a fault whatever the screen shows. */
|
|
29
|
+
export declare function isConsoleError(e: ReticleEvent): boolean;
|
|
30
|
+
/** Requests that answered at or above `floor`. */
|
|
31
|
+
export declare function failedRequests(events: ReticleEvent[], floor: number): ReticleEvent[];
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One session's evidence, as a contribution to the coverage ledger.
|
|
3
|
+
*
|
|
4
|
+
* Pure over what the caller read: the controls on the page, the labels the session drove and proved,
|
|
5
|
+
* where it is, and the events it holds. The ledger does the accumulating; this only says what one
|
|
6
|
+
* session saw.
|
|
7
|
+
*/
|
|
8
|
+
import { EventType, MUTATING_METHODS, asNumber, asString, } from '@reticlehq/core';
|
|
9
|
+
import { routeFromUrl, routesFromEvents } from '../../memory/project/learned-routes.js';
|
|
10
|
+
/** A path segment that is a record id rather than part of the endpoint's name. */
|
|
11
|
+
const ID_SEGMENT = /^(?:\d+|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}|[0-9a-f]{24,})$/i;
|
|
12
|
+
/**
|
|
13
|
+
* `POST /api/orders/:id/refund` for a mutating request, else undefined. Ids are collapsed so the
|
|
14
|
+
* refund of order 41 and of order 42 are one endpoint with one failure path to drive, not two.
|
|
15
|
+
*/
|
|
16
|
+
export function writeKeyOf(event) {
|
|
17
|
+
if (EventType.NET_REQUEST !== event.type)
|
|
18
|
+
return undefined;
|
|
19
|
+
const method = asString(event.data['method'])?.toUpperCase();
|
|
20
|
+
const url = asString(event.data['url']);
|
|
21
|
+
if (method === undefined || url === undefined || !MUTATING_METHODS.includes(method))
|
|
22
|
+
return undefined;
|
|
23
|
+
let path;
|
|
24
|
+
try {
|
|
25
|
+
path = new URL(url, 'http://localhost').pathname;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
const named = path
|
|
31
|
+
.split('/')
|
|
32
|
+
.map((segment) => (ID_SEGMENT.test(segment) ? ':id' : segment))
|
|
33
|
+
.join('/');
|
|
34
|
+
return `${method} ${named}`;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The ledger's identity for a control: the snapshot label, with numbers collapsed. The rows of a
|
|
38
|
+
* table — `button "Delete row 12"` — are one control to cover, not five hundred, which is what keeps
|
|
39
|
+
* `touched` meaningful on any page with a list.
|
|
40
|
+
*/
|
|
41
|
+
export function controlKey(label) {
|
|
42
|
+
const bare = label.replace(/^\s*-\s*/, '').replace(/\s*\(ref=e\d+\)/, '');
|
|
43
|
+
// Role and accessible name only. Attributes are the control's momentary STATE — the value typed
|
|
44
|
+
// into it, whether it has focus — and keying on them made one control several, and every
|
|
45
|
+
// keystroke a new page state. Found by driving the fixture.
|
|
46
|
+
const identity = /^(\S+(?:\s+"(?:[^"\\]|\\.)*")?)/.exec(bare)?.[1] ?? bare;
|
|
47
|
+
return identity.replace(/\d+/g, '#').trim();
|
|
48
|
+
}
|
|
49
|
+
export function sessionDelta(input) {
|
|
50
|
+
const here = input.url === undefined ? undefined : routeFromUrl(input.url);
|
|
51
|
+
const reached = [
|
|
52
|
+
...new Set([...(here === undefined ? [] : [here]), ...routesFromEvents(input.events)]),
|
|
53
|
+
];
|
|
54
|
+
const writes = [
|
|
55
|
+
...new Set(input.events.map(writeKeyOf).filter((k) => k !== undefined)),
|
|
56
|
+
];
|
|
57
|
+
return {
|
|
58
|
+
routes: { reached },
|
|
59
|
+
controls: {
|
|
60
|
+
seen: [...new Set([...input.seen].map(controlKey))],
|
|
61
|
+
touched: [...new Set([...input.acted].map(controlKey))],
|
|
62
|
+
proved: [...new Set([...input.proved].map(controlKey))],
|
|
63
|
+
},
|
|
64
|
+
writes: { seen: writes },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** An error the page threw or logged — a fault whatever the screen shows. */
|
|
68
|
+
export function isConsoleError(e) {
|
|
69
|
+
return e.type === EventType.CONSOLE_ERROR || e.type === EventType.ERROR_UNCAUGHT;
|
|
70
|
+
}
|
|
71
|
+
/** Requests that answered at or above `floor`. */
|
|
72
|
+
export function failedRequests(events, floor) {
|
|
73
|
+
return events.filter((e) => {
|
|
74
|
+
if (e.type !== EventType.NET_REQUEST)
|
|
75
|
+
return false;
|
|
76
|
+
const status = asNumber(e.data['status']);
|
|
77
|
+
return status !== undefined && status >= floor;
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A live session, as the explorer's port.
|
|
3
|
+
*
|
|
4
|
+
* The one hard part is that a full page load REPLACES the session: the SDK reconnects under the
|
|
5
|
+
* same id (sessionStorage keeps it) but as a new object, and a command in flight on the old one is
|
|
6
|
+
* rejected. So every call reads the CURRENT session, and anything that may have navigated waits for
|
|
7
|
+
* the successor before it answers — the same wait `navigate { reload }` does.
|
|
8
|
+
*/
|
|
9
|
+
import { type CommandResult, type ReticleEvent } from '@reticlehq/core';
|
|
10
|
+
import type { ExplorePort } from './explorer.js';
|
|
11
|
+
export interface PortSession {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly url: string;
|
|
14
|
+
command(name: string, args?: Record<string, unknown>): Promise<CommandResult>;
|
|
15
|
+
elapsed(): number;
|
|
16
|
+
eventsSince(cursor: number): ReticleEvent[];
|
|
17
|
+
beginAction?(tool: string, args: Record<string, unknown>): unknown;
|
|
18
|
+
finishAction?(): void;
|
|
19
|
+
}
|
|
20
|
+
export declare function sessionPort(input: {
|
|
21
|
+
sessions: {
|
|
22
|
+
get(id: string): PortSession | undefined;
|
|
23
|
+
};
|
|
24
|
+
start: PortSession;
|
|
25
|
+
startUrl: string;
|
|
26
|
+
settleMs: number;
|
|
27
|
+
now: () => number;
|
|
28
|
+
sleep: (ms: number) => Promise<void>;
|
|
29
|
+
setMocks?: (sessionUrl: string, rules: {
|
|
30
|
+
urlContains: string;
|
|
31
|
+
method?: string;
|
|
32
|
+
abort?: boolean;
|
|
33
|
+
}[]) => Promise<boolean>;
|
|
34
|
+
}): ExplorePort & {
|
|
35
|
+
current(): PortSession;
|
|
36
|
+
};
|