@reticlehq/server 2.4.0 → 2.6.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/SKILL.md +719 -0
- package/dist/bridge/auth-failure-reason.d.ts +19 -0
- package/dist/bridge/auth-failure-reason.js +29 -0
- package/dist/{bridge-security.d.ts → bridge/bridge-security.d.ts} +1 -1
- package/dist/{bridge-security.js → bridge/bridge-security.js} +1 -1
- package/dist/{bridge.d.ts → bridge/bridge.d.ts} +13 -1
- package/dist/{bridge.js → bridge/bridge.js} +95 -15
- package/dist/{bridge.test-harness.d.ts → bridge/bridge.test-harness.d.ts} +9 -11
- package/dist/{bridge.test-harness.js → bridge/bridge.test-harness.js} +18 -10
- package/dist/{pairing-token.js → bridge/pairing-token.js} +1 -1
- package/dist/capsule/capsule-store.d.ts +8 -3
- package/dist/capsule/capsule-store.js +7 -2
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +8 -0
- package/dist/capsule/causal-summary.js +22 -12
- package/dist/capsule/divergence.d.ts +9 -4
- package/dist/capsule/divergence.js +8 -8
- package/dist/capsule/predicate-to-links.js +11 -8
- package/dist/cli/cli-doctor.d.ts +1 -0
- package/dist/cli/cli-doctor.js +135 -0
- package/dist/{cli-flow-commands.d.ts → cli/cli-flow-commands.d.ts} +2 -2
- package/dist/{cli-flow-commands.js → cli/cli-flow-commands.js} +15 -15
- package/dist/{cli-launch.d.ts → cli/cli-launch.d.ts} +32 -3
- package/dist/cli/cli-launch.js +171 -0
- package/dist/cli/cli-parse-feedback.d.ts +31 -0
- package/dist/cli/cli-parse-feedback.js +56 -0
- package/dist/{cli-parse.d.ts → cli/cli-parse.d.ts} +6 -19
- package/dist/{cli-parse.js → cli/cli-parse.js} +118 -81
- package/dist/{cli-port.d.ts → cli/cli-port.d.ts} +12 -0
- package/dist/{cli-port.js → cli/cli-port.js} +9 -9
- package/dist/cli/cli-update-commands.d.ts +4 -0
- package/dist/cli/cli-update-commands.js +93 -0
- package/dist/{cli-verify.js → cli/cli-verify.js} +14 -14
- package/dist/{cloud-cli.js → cli/cloud-cli.js} +29 -24
- package/dist/cli/doctor-daemon-line.d.ts +37 -0
- package/dist/cli/doctor-daemon-line.js +35 -0
- package/dist/cli/port-holder.d.ts +43 -0
- package/dist/cli/port-holder.js +99 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +302 -100
- package/dist/cloud/cloud-config.d.ts +0 -1
- package/dist/cloud/cloud-config.js +9 -9
- package/dist/cloud/cloud-sync.d.ts +1 -7
- package/dist/cloud/cloud-sync.js +12 -12
- package/dist/crawl/crawl-empty.d.ts +16 -0
- package/dist/crawl/crawl-empty.js +24 -0
- package/dist/crawl/crawl-tools.js +4 -1
- package/dist/crawl/crawl.js +26 -7
- package/dist/daemon/daemon-resilience.d.ts +73 -0
- package/dist/daemon/daemon-resilience.js +279 -0
- package/dist/daemon/daemon-usefulness.d.ts +61 -0
- package/dist/daemon/daemon-usefulness.js +62 -0
- package/dist/{daemon.d.ts → daemon/daemon.d.ts} +33 -8
- package/dist/{daemon.js → daemon/daemon.js} +105 -48
- package/dist/daemon/heartbeat.d.ts +115 -0
- package/dist/daemon/heartbeat.js +141 -0
- package/dist/daemon/idle-grace.d.ts +32 -0
- package/dist/daemon/idle-grace.js +41 -0
- package/dist/{idle-shutdown.d.ts → daemon/idle-shutdown.d.ts} +23 -2
- package/dist/{idle-shutdown.js → daemon/idle-shutdown.js} +37 -6
- package/dist/daemon/port-presence.d.ts +76 -0
- package/dist/daemon/port-presence.js +93 -0
- package/dist/daemon/state-dir.d.ts +7 -0
- package/dist/daemon/state-dir.js +59 -0
- package/dist/daemon/unclean-predecessor.d.ts +23 -0
- package/dist/daemon/unclean-predecessor.js +27 -0
- package/dist/domain/domain-model.js +2 -2
- package/dist/events/body-failures.js +8 -8
- package/dist/events/contradictions.d.ts +1 -1
- package/dist/events/contradictions.js +60 -10
- package/dist/events/echo-mismatch.js +10 -10
- package/dist/events/event-filters.js +6 -6
- package/dist/events/json-salvage.d.ts +2 -1
- package/dist/events/json-salvage.js +6 -6
- package/dist/events/observed-in-window.d.ts +12 -0
- package/dist/events/observed-in-window.js +21 -0
- package/dist/events/predicate-eval.d.ts +53 -20
- package/dist/events/predicate-eval.js +179 -55
- package/dist/events/predicate-parse.d.ts +23 -0
- package/dist/events/predicate-parse.js +54 -0
- package/dist/events/predicate.d.ts +7 -2
- package/dist/events/predicate.js +126 -35
- package/dist/events/reconcile.js +5 -5
- package/dist/events/ring-buffer.js +2 -17
- package/dist/events/stale-response.js +6 -6
- package/dist/events/testid-near-miss.d.ts +22 -0
- package/dist/events/testid-near-miss.js +33 -0
- package/dist/events/unit-mismatch.js +8 -8
- package/dist/flows/affected-tools.js +19 -4
- package/dist/flows/affected.js +1 -1
- package/dist/flows/annotate-target.d.ts +1 -0
- package/dist/flows/annotate-target.js +20 -0
- package/dist/flows/annotate-tools.js +25 -3
- package/dist/flows/annotate.js +17 -4
- package/dist/flows/assertion-integrity.d.ts +2 -1
- package/dist/flows/assertion-tiers-store.d.ts +3 -66
- package/dist/flows/assertion-tiers-store.js +2 -2
- package/dist/flows/attributed-failure.d.ts +17 -0
- package/dist/flows/attributed-failure.js +20 -0
- package/dist/flows/buddy-status.d.ts +2 -1
- package/dist/flows/buddy-status.js +2 -2
- package/dist/flows/coverage.d.ts +3 -2
- package/dist/flows/coverage.js +2 -2
- package/dist/flows/decision.js +24 -9
- package/dist/flows/empty-flow.d.ts +6 -0
- package/dist/flows/empty-flow.js +24 -0
- package/dist/flows/flake-store.d.ts +0 -1
- package/dist/flows/flake-store.js +1 -1
- package/dist/flows/flake.d.ts +2 -1
- package/dist/flows/flake.js +2 -2
- package/dist/flows/flow-classify.js +12 -0
- package/dist/flows/flow-replay-run.js +4 -4
- package/dist/flows/flow-replay.d.ts +6 -8
- package/dist/flows/flow-replay.js +132 -48
- package/dist/flows/flow-report.js +1 -1
- package/dist/flows/flow-scope.d.ts +1 -6
- package/dist/flows/flow-step-runners.d.ts +26 -6
- package/dist/flows/flow-step-runners.js +156 -6
- package/dist/flows/flow-success.js +20 -15
- package/dist/flows/flow-tools.js +63 -21
- package/dist/flows/flows.js +15 -11
- package/dist/flows/gate.d.ts +4 -3
- package/dist/flows/gate.js +1 -1
- package/dist/flows/heal-run.d.ts +0 -8
- package/dist/flows/heal-run.js +5 -5
- package/dist/flows/heal.js +3 -3
- package/dist/flows/predicate-to-expect.d.ts +38 -0
- package/dist/flows/predicate-to-expect.js +95 -0
- package/dist/flows/replay.d.ts +16 -1
- package/dist/flows/replay.js +58 -22
- package/dist/flows/role-anchor-nearest.d.ts +6 -0
- package/dist/flows/role-anchor-nearest.js +45 -0
- package/dist/flows/role-drift-reason.d.ts +20 -0
- package/dist/flows/role-drift-reason.js +39 -0
- package/dist/flows/server-verify.d.ts +1 -1
- package/dist/flows/server-verify.js +5 -5
- package/dist/flows/suite-flakes.d.ts +33 -0
- package/dist/flows/suite-flakes.js +32 -0
- package/dist/flows/verify-change-tools.js +32 -10
- package/dist/flows/verify-change-verdict.d.ts +15 -0
- package/dist/flows/verify-change-verdict.js +25 -0
- package/dist/flows/watch-batcher.d.ts +3 -2
- package/dist/honesty/already-true.d.ts +2 -0
- package/dist/honesty/already-true.js +31 -0
- package/dist/honesty/blind-spots.d.ts +35 -3
- package/dist/honesty/blind-spots.js +57 -12
- package/dist/honesty/honesty.d.ts +3 -2
- package/dist/honesty/honesty.js +4 -4
- package/dist/honesty/uncaptured-bodies.js +1 -1
- package/dist/honesty/unread-outcome.js +2 -2
- package/dist/honesty/verified.d.ts +31 -3
- package/dist/honesty/verified.js +64 -8
- package/dist/http-server.js +23 -7
- package/dist/hunt/hunt-report.d.ts +2 -1
- package/dist/hunt/hunt-report.js +2 -2
- package/dist/index.d.ts +9 -6
- package/dist/index.js +41 -25
- package/dist/init/agent-rules.d.ts +20 -1
- package/dist/init/agent-rules.js +64 -8
- package/dist/init/app-choice.d.ts +20 -0
- package/dist/init/app-choice.js +25 -0
- package/dist/init/astro-host.d.ts +21 -0
- package/dist/init/astro-host.js +27 -0
- package/dist/init/astro-patch.d.ts +16 -0
- package/dist/init/astro-patch.js +240 -0
- package/dist/init/capabilities.js +4 -1
- package/dist/init/cra.d.ts +50 -0
- package/dist/init/cra.js +106 -0
- package/dist/init/cursor.js +39 -4
- package/dist/init/desktop-doctor.d.ts +3 -2
- package/dist/init/desktop-doctor.js +4 -4
- package/dist/init/detect.d.ts +9 -2
- package/dist/init/detect.js +24 -5
- package/dist/init/mcp-clients.d.ts +81 -0
- package/dist/init/mcp-clients.js +248 -0
- package/dist/init/mcp-registered.d.ts +12 -0
- package/dist/init/mcp-registered.js +14 -0
- package/dist/init/mcp.d.ts +27 -2
- package/dist/init/mcp.js +40 -4
- package/dist/init/next-patch.d.ts +2 -4
- package/dist/init/next-patch.js +109 -19
- package/dist/init/node-io.js +29 -6
- package/dist/init/plan-framework.d.ts +16 -1
- package/dist/init/plan-framework.js +166 -8
- package/dist/init/plan.d.ts +85 -6
- package/dist/init/plan.js +196 -33
- package/dist/init/project-id.d.ts +5 -9
- package/dist/init/project-id.js +10 -22
- package/dist/init/run.d.ts +23 -1
- package/dist/init/run.js +338 -47
- package/dist/init/slash-command.d.ts +9 -0
- package/dist/init/slash-command.js +10 -1
- package/dist/init/snippets.d.ts +7 -10
- package/dist/init/snippets.js +44 -15
- package/dist/init/vite-config.js +1 -1
- package/dist/init/windows-quote.d.ts +38 -0
- package/dist/init/windows-quote.js +70 -0
- package/dist/init/workspace-apps.d.ts +23 -0
- package/dist/init/workspace-apps.js +68 -0
- package/dist/input/driven-redaction.js +1 -1
- package/dist/input/network-detail.js +8 -8
- package/dist/input/network-mock-tools.js +2 -2
- package/dist/input/network-mock.js +4 -4
- package/dist/input/real-input.js +8 -3
- package/dist/input/scroll-find.js +2 -2
- package/dist/input/scroll-tools.d.ts +0 -5
- package/dist/input/scroll-tools.js +11 -4
- package/dist/input/viewport-tools.js +2 -2
- package/dist/journal/ambient-store.d.ts +0 -2
- package/dist/journal/ambient-store.js +1 -1
- package/dist/journal/ambient.js +1 -1
- package/dist/journal/attach-journal.d.ts +2 -1
- package/dist/journal/deviation-report.js +6 -6
- package/dist/journal/deviation-service.js +1 -1
- package/dist/journal/envelope-store.d.ts +0 -2
- package/dist/journal/envelope-store.js +1 -1
- package/dist/journal/envelope.d.ts +2 -3
- package/dist/journal/envelope.js +3 -3
- package/dist/journal/journal-query.js +1 -1
- package/dist/journal/journal-recorder.d.ts +2 -1
- package/dist/journal/journal-recorder.js +2 -2
- package/dist/journal/rollups.js +9 -9
- package/dist/journal/session-end.d.ts +2 -1
- package/dist/journal/session-journal.js +4 -4
- package/dist/license/license.d.ts +0 -14
- package/dist/license/license.js +8 -8
- package/dist/log.d.ts +10 -0
- package/dist/log.js +11 -1
- package/dist/mcp/mcp-is-error.d.ts +13 -0
- package/dist/mcp/mcp-is-error.js +18 -0
- package/dist/mcp/mcp-outage.d.ts +32 -0
- package/dist/mcp/mcp-outage.js +55 -0
- package/dist/mcp/mcp-proxy.d.ts +134 -0
- package/dist/mcp/mcp-proxy.js +720 -0
- package/dist/{mcp.d.ts → mcp/mcp.d.ts} +13 -10
- package/dist/{mcp.js → mcp/mcp.js} +139 -37
- package/dist/mcp/proxy-handshake.d.ts +3 -0
- package/dist/mcp/proxy-handshake.js +54 -0
- package/dist/mcp/proxy-lifecycle.d.ts +64 -0
- package/dist/mcp/proxy-lifecycle.js +70 -0
- package/dist/mcp/tool-catalog-cache.d.ts +29 -0
- package/dist/mcp/tool-catalog-cache.js +55 -0
- package/dist/oracles/propose-consequences.d.ts +2 -1
- package/dist/oracles/propose-consequences.js +16 -11
- package/dist/oracles/self-instrument.d.ts +2 -1
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/phenomena/phenomena.js +3 -3
- package/dist/platform.d.ts +16 -0
- package/dist/platform.js +16 -0
- package/dist/pool/browser-pool.d.ts +9 -0
- package/dist/pool/browser-pool.js +38 -9
- package/dist/pool/playwright-launcher.d.ts +15 -2
- package/dist/pool/playwright-launcher.js +21 -2
- package/dist/project/fs-port.js +1 -1
- package/dist/project/project-tools.js +2 -2
- package/dist/runs/build-verification-run.js +1 -1
- package/dist/runs/repair-prompt.d.ts +0 -6
- package/dist/runs/repair-prompt.js +3 -3
- package/dist/runs/risk-classify.js +1 -1
- package/dist/runs/run-diff.d.ts +0 -2
- package/dist/runs/run-diff.js +3 -3
- package/dist/runs/run-tools.js +2 -2
- package/dist/runs/verification-sync.js +2 -2
- package/dist/runs/verify-http.js +1 -1
- package/dist/runs/verify-server.js +1 -1
- package/dist/session/attachment-history.d.ts +46 -0
- package/dist/session/attachment-history.js +65 -0
- package/dist/session/command-timeout.d.ts +11 -3
- package/dist/session/command-timeout.js +2 -2
- package/dist/session/control-envelope.d.ts +6 -0
- package/dist/session/control-envelope.js +7 -1
- package/dist/session/dev-server-probe.d.ts +13 -0
- package/dist/session/dev-server-probe.js +52 -0
- package/dist/session/last-act.d.ts +17 -3
- package/dist/session/last-act.js +16 -4
- package/dist/session/live-control-tools.js +7 -2
- package/dist/session/live-control.js +1 -1
- package/dist/session/looks-like-dev-server.d.ts +14 -0
- package/dist/session/looks-like-dev-server.js +25 -0
- package/dist/session/no-session-diagnosis.d.ts +45 -0
- package/dist/session/no-session-diagnosis.js +120 -0
- package/dist/session/no-session-watch.d.ts +21 -0
- package/dist/session/no-session-watch.js +69 -0
- package/dist/session/observed-state.d.ts +4 -0
- package/dist/session/observed-state.js +19 -1
- package/dist/session/output-budget.js +1 -1
- package/dist/session/pending-commands.js +1 -0
- package/dist/session/session-health.d.ts +8 -1
- package/dist/session/session-health.js +8 -7
- package/dist/session/session-info.d.ts +43 -8
- package/dist/session/session-info.js +46 -1
- package/dist/session/session-lease.d.ts +0 -11
- package/dist/session/session-lease.js +1 -1
- package/dist/session/session-manager.d.ts +6 -0
- package/dist/session/session-manager.js +124 -12
- package/dist/session/session-readiness.d.ts +1 -1
- package/dist/session/session-readiness.js +6 -1
- package/dist/session/session-reaper.js +1 -1
- package/dist/session/session-reconnect.d.ts +32 -0
- package/dist/session/session-reconnect.js +42 -0
- package/dist/session/session.d.ts +15 -2
- package/dist/session/session.js +77 -26
- package/dist/session/state-select.d.ts +1 -1
- package/dist/session/state-select.js +1 -1
- package/dist/status-payload.d.ts +19 -0
- package/dist/status-payload.js +11 -0
- package/dist/telemetry/argument-shape.js +10 -1
- package/dist/telemetry/browser-mode.d.ts +27 -0
- package/dist/telemetry/browser-mode.js +33 -0
- package/dist/telemetry/bug-found.d.ts +17 -21
- package/dist/telemetry/bug-found.js +120 -8
- package/dist/telemetry/cli-telemetry.d.ts +2 -12
- package/dist/telemetry/cli-telemetry.js +27 -4
- package/dist/telemetry/crash-cause.d.ts +47 -0
- package/dist/telemetry/crash-cause.js +104 -0
- package/dist/telemetry/daemon-telemetry.d.ts +38 -1
- package/dist/telemetry/daemon-telemetry.js +37 -12
- package/dist/telemetry/dev-repo.js +4 -4
- package/dist/telemetry/error-class.d.ts +26 -0
- package/dist/telemetry/error-class.js +52 -0
- package/dist/telemetry/error-fingerprint.js +21 -2
- package/dist/telemetry/feedback-cli.d.ts +8 -3
- package/dist/telemetry/feedback-cli.js +52 -8
- package/dist/telemetry/feedback-context.d.ts +23 -6
- package/dist/telemetry/feedback-context.js +105 -5
- package/dist/telemetry/feedback-delivery.d.ts +20 -0
- package/dist/telemetry/feedback-delivery.js +30 -0
- package/dist/telemetry/feedback-outbox.d.ts +22 -0
- package/dist/telemetry/feedback-outbox.js +87 -0
- package/dist/telemetry/feedback.d.ts +22 -1
- package/dist/telemetry/feedback.js +67 -8
- package/dist/telemetry/git-facts.d.ts +7 -0
- package/dist/telemetry/git-facts.js +5 -5
- package/dist/telemetry/project-profile.js +2 -1
- package/dist/telemetry/session-metrics.d.ts +47 -3
- package/dist/telemetry/session-metrics.js +315 -15
- package/dist/telemetry/telemetry.d.ts +45 -2
- package/dist/telemetry/telemetry.js +150 -37
- package/dist/telemetry/verification-of.d.ts +22 -0
- package/dist/telemetry/verification-of.js +84 -0
- package/dist/tools/act-capsule.js +2 -2
- package/dist/tools/act-danger.d.ts +12 -0
- package/dist/tools/act-danger.js +20 -3
- package/dist/tools/act-helpers.js +4 -4
- package/dist/tools/act-tools.d.ts +0 -8
- package/dist/tools/act-tools.js +114 -118
- package/dist/tools/act-view.js +7 -7
- package/dist/tools/alias-args.d.ts +17 -0
- package/dist/tools/alias-args.js +25 -0
- package/dist/tools/assert-grade.js +9 -9
- package/dist/tools/assert-verdict.d.ts +9 -1
- package/dist/tools/assert-verdict.js +21 -6
- package/dist/tools/browser-tools.js +36 -11
- package/dist/tools/contract-tools.d.ts +0 -5
- package/dist/tools/contract-tools.js +27 -1
- package/dist/tools/coverage-identity.d.ts +34 -0
- package/dist/tools/coverage-identity.js +46 -0
- package/dist/tools/coverage-tools.js +10 -14
- package/dist/tools/dynamic-tools.d.ts +2 -1
- package/dist/tools/dynamic-tools.js +96 -19
- package/dist/tools/error-recovery.d.ts +34 -2
- package/dist/tools/error-recovery.js +255 -4
- package/dist/tools/feedback-invite.d.ts +48 -0
- package/dist/tools/feedback-invite.js +71 -0
- package/dist/tools/feedback-tools.d.ts +12 -1
- package/dist/tools/feedback-tools.js +28 -5
- package/dist/tools/invoke-tool.d.ts +0 -12
- package/dist/tools/invoke-tool.js +126 -33
- package/dist/tools/lease-hint.d.ts +17 -0
- package/dist/tools/lease-hint.js +24 -0
- package/dist/tools/lease-tools.js +6 -5
- package/dist/tools/merge-tools.d.ts +10 -1
- package/dist/tools/merge-tools.js +9 -3
- package/dist/tools/merged-name-redirect.d.ts +13 -0
- package/dist/tools/merged-name-redirect.js +55 -0
- package/dist/tools/navigate-arrival.d.ts +26 -0
- package/dist/tools/navigate-arrival.js +60 -0
- package/dist/tools/navigate-result.d.ts +31 -0
- package/dist/tools/navigate-result.js +38 -0
- package/dist/tools/observe-tools.js +72 -20
- package/dist/tools/observed-nothing.d.ts +25 -0
- package/dist/tools/observed-nothing.js +33 -0
- package/dist/tools/query-paginate.js +2 -2
- package/dist/tools/query-shape.d.ts +18 -0
- package/dist/tools/query-shape.js +39 -0
- package/dist/tools/read-tools.js +81 -20
- package/dist/tools/real-input-attempt.d.ts +28 -0
- package/dist/tools/real-input-attempt.js +93 -0
- package/dist/tools/reconcile-tools.js +7 -7
- package/dist/tools/reload-result.d.ts +14 -0
- package/dist/tools/reload-result.js +29 -0
- package/dist/tools/snapshot-delta.d.ts +4 -5
- package/dist/tools/snapshot-delta.js +0 -0
- package/dist/tools/tool-kit.d.ts +16 -0
- package/dist/tools/tool-surface.d.ts +83 -0
- package/dist/tools/tool-surface.js +217 -0
- package/dist/tools/tools-helpers.js +6 -6
- package/dist/tools/tools.d.ts +15 -0
- package/dist/tools/tools.js +53 -17
- package/dist/tools/unadvertised-help.d.ts +5 -0
- package/dist/tools/unadvertised-help.js +39 -0
- package/dist/trace.d.ts +69 -0
- package/dist/trace.js +150 -0
- package/dist/update/nudge-credit.d.ts +4 -0
- package/dist/update/nudge-credit.js +53 -0
- package/dist/update/sdk-sync.d.ts +21 -0
- package/dist/update/sdk-sync.js +42 -0
- package/dist/update/update-checker.js +3 -3
- package/dist/update/update-nudge.d.ts +63 -6
- package/dist/update/update-nudge.js +128 -15
- package/dist/update/updater.d.ts +1 -9
- package/dist/update/updater.js +13 -8
- package/dist/version/peer-announce.d.ts +4 -0
- package/dist/version/peer-announce.js +30 -0
- package/dist/{server-version.js → version/server-version.js} +5 -1
- package/dist/version/version-nudge.d.ts +28 -0
- package/dist/version/version-nudge.js +40 -0
- package/dist/version/version-skew.d.ts +66 -0
- package/dist/version/version-skew.js +71 -0
- package/dist/visual/visual-diff.js +2 -2
- package/dist/visual/visual-tools.js +4 -4
- package/docs/README.md +55 -0
- package/docs/agent-cheatsheet.md +85 -0
- package/docs/architecture.md +113 -0
- package/docs/benchmarks.md +255 -0
- package/docs/debugging.md +102 -0
- package/docs/deploy-checks.md +69 -0
- package/docs/desktop-apps.md +256 -0
- package/docs/enterprise.md +62 -0
- package/docs/fixtures-dispatch-receiver.yml +76 -0
- package/docs/fixtures.md +45 -0
- package/docs/flows.md +166 -0
- package/docs/gate-plan.md +98 -0
- package/docs/gates.md +117 -0
- package/docs/getting-started.md +469 -0
- package/docs/human-control.md +63 -0
- package/docs/integration-patterns.md +171 -0
- package/docs/local-registry.md +126 -0
- package/docs/matrix/2.5.0/claude-code-machine.json +19 -0
- package/docs/matrix/2.5.0/codex-machine.json +20 -0
- package/docs/matrix/2.5.0/cursor-machine.json +22 -0
- package/docs/matrix/2.5.0/gemini-machine.json +22 -0
- package/docs/matrix/2.5.0/opencode-machine.json +22 -0
- package/docs/matrix/2.5.0/vscode-machine.json +22 -0
- package/docs/matrix/2.5.0/windsurf-machine.json +22 -0
- package/docs/matrix/MATRIX.md +26 -0
- package/docs/matrix/README.md +59 -0
- package/docs/mint.json +61 -0
- package/docs/multi-agent-testing.md +55 -0
- package/docs/platform-integration.md +122 -0
- package/docs/system-map.md +152 -0
- package/docs/telemetry-contract.md +214 -0
- package/docs/telemetry.md +191 -0
- package/docs/testing.md +82 -0
- package/docs/token-efficiency.md +80 -0
- package/docs/usage.md +1046 -0
- package/package.json +8 -5
- package/dist/cli-doctor.d.ts +0 -6
- package/dist/cli-doctor.js +0 -60
- package/dist/cli-launch.js +0 -112
- package/dist/daemon-resilience.d.ts +0 -22
- package/dist/daemon-resilience.js +0 -85
- package/dist/mcp-proxy.d.ts +0 -67
- package/dist/mcp-proxy.js +0 -337
- package/dist/tools/profiles.d.ts +0 -37
- package/dist/tools/profiles.js +0 -139
- /package/dist/{pairing-token.d.ts → bridge/pairing-token.d.ts} +0 -0
- /package/dist/{token-auth.d.ts → bridge/token-auth.d.ts} +0 -0
- /package/dist/{token-auth.js → bridge/token-auth.js} +0 -0
- /package/dist/{cli-verify.d.ts → cli/cli-verify.d.ts} +0 -0
- /package/dist/{cloud-cli.d.ts → cli/cloud-cli.d.ts} +0 -0
- /package/dist/{server-version.d.ts → version/server-version.d.ts} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { aliasParam } from '../tools/alias-args.js';
|
|
2
3
|
import { AnnotationErrorCode, AnnotationSchema } from '@reticlehq/core';
|
|
3
4
|
import { RECOVERY } from '../tools/error-recovery.js';
|
|
4
5
|
/**
|
|
@@ -12,7 +13,7 @@ const UNKNOWN_KIND_RECOVERY = 'Annotations are structured, never free text. Pass
|
|
|
12
13
|
import { ReticleTool } from '../tools/tool-names.js';
|
|
13
14
|
import { asString } from '../tools/tools-helpers.js';
|
|
14
15
|
import { compileAnnotation } from './annotate.js';
|
|
15
|
-
|
|
16
|
+
import { resolveAnnotateTarget } from './annotate-target.js';
|
|
16
17
|
/**
|
|
17
18
|
* The reticle_annotate tool. A STRUCTURED annotation (the AnnotationSchema
|
|
18
19
|
* discriminated union) is compiled into the live recording's per-step expect / flow dynamic[] /
|
|
@@ -48,6 +49,12 @@ export const ANNOTATE_TOOLS = [
|
|
|
48
49
|
inputSchema: {
|
|
49
50
|
// `flow` selects the recording; `name`/`signal`/`testid`/`dataMatches` are the annotation fields.
|
|
50
51
|
flow: z.string().optional().describe("Named recording to annotate. Defaults to 'default'."),
|
|
52
|
+
// Every other flow tool says `flowName`, and reticle_flow_save's own description sends the
|
|
53
|
+
// agent here — so the spelling it arrives with is the one this tool did not accept.
|
|
54
|
+
flowName: z
|
|
55
|
+
.string()
|
|
56
|
+
.optional()
|
|
57
|
+
.describe('Alias for `flow` (the name every other flow tool uses).'),
|
|
51
58
|
kind: z
|
|
52
59
|
.string()
|
|
53
60
|
.describe('Annotation kind: assert-signal | assert-visible | assert-state | mark-dynamic | success-state | intent.'),
|
|
@@ -114,7 +121,10 @@ export const ANNOTATE_TOOLS = [
|
|
|
114
121
|
code: z.string().optional(),
|
|
115
122
|
},
|
|
116
123
|
handler: (deps, args) => {
|
|
117
|
-
|
|
124
|
+
// The recording actually RUNNING when there is exactly one, not the literal name `default`.
|
|
125
|
+
// Reproduced on three apps: a named recording plus an unnamed annotate reported
|
|
126
|
+
// `annotate_no_step` while the steps sat in the recording the agent had started.
|
|
127
|
+
const name = resolveAnnotateTarget(asString(aliasParam(args, 'flow', ['flowName'])['flow']), deps.recordings.active());
|
|
118
128
|
// Structured boundary: a free NL string / unknown kind fails the schema → UNKNOWN_KIND.
|
|
119
129
|
const parsed = AnnotationSchema.safeParse(args);
|
|
120
130
|
if (!parsed.success) {
|
|
@@ -134,8 +144,20 @@ export const ANNOTATE_TOOLS = [
|
|
|
134
144
|
});
|
|
135
145
|
}
|
|
136
146
|
const outcome = compileAnnotation(parsed.data, stepCount ?? 0);
|
|
137
|
-
if (!outcome.result.ok)
|
|
147
|
+
if (!outcome.result.ok) {
|
|
148
|
+
// Name what IS recording. `annotate_no_step` alone sent the agent off to record MORE steps —
|
|
149
|
+
// into the same empty recording — when the steps it already has are in another one.
|
|
150
|
+
const active = deps.recordings.active();
|
|
151
|
+
const elsewhere = active.filter((r) => r !== name && (deps.recordings.stepCount(r) ?? 0) > 0);
|
|
152
|
+
if (outcome.result.code === AnnotationErrorCode.NO_STEP_TO_ANNOTATE &&
|
|
153
|
+
elsewhere.length > 0) {
|
|
154
|
+
return Promise.resolve({
|
|
155
|
+
...outcome.result,
|
|
156
|
+
recovery: `'${name}' has no steps yet, but ${elsewhere.map((r) => `'${r}'`).join(', ')} does. Pass \`flow\` to say which recording this belongs to, or drive an action first.`,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
138
159
|
return Promise.resolve(outcome.result);
|
|
160
|
+
}
|
|
139
161
|
const patch = outcome.patch;
|
|
140
162
|
if (patch !== undefined) {
|
|
141
163
|
if (patch.dynamicAdd !== undefined)
|
package/dist/flows/annotate.js
CHANGED
|
@@ -16,7 +16,7 @@ import { AnnotationErrorCode, AnnotationKind, AnnotationTarget, COMPILED_PREDICA
|
|
|
16
16
|
export function compileAnnotation(a, stepCount) {
|
|
17
17
|
switch (a.kind) {
|
|
18
18
|
case AnnotationKind.ASSERT_SIGNAL: {
|
|
19
|
-
if (
|
|
19
|
+
if (0 === stepCount)
|
|
20
20
|
return noStep();
|
|
21
21
|
const expect = { signal: a.name };
|
|
22
22
|
if (a.dataMatches !== undefined)
|
|
@@ -24,12 +24,12 @@ export function compileAnnotation(a, stepCount) {
|
|
|
24
24
|
return stepPatch(a, stepCount, expect);
|
|
25
25
|
}
|
|
26
26
|
case AnnotationKind.ASSERT_VISIBLE: {
|
|
27
|
-
if (
|
|
27
|
+
if (0 === stepCount)
|
|
28
28
|
return noStep();
|
|
29
29
|
return stepPatch(a, stepCount, { element: { testid: a.testid } });
|
|
30
30
|
}
|
|
31
31
|
case AnnotationKind.ASSERT_STATE: {
|
|
32
|
-
if (
|
|
32
|
+
if (0 === stepCount)
|
|
33
33
|
return noStep();
|
|
34
34
|
const state = { path: a.statePath };
|
|
35
35
|
if (a.store !== undefined)
|
|
@@ -38,6 +38,11 @@ export function compileAnnotation(a, stepCount) {
|
|
|
38
38
|
state.equals = a.equals;
|
|
39
39
|
return stepPatch(a, stepCount, { state });
|
|
40
40
|
}
|
|
41
|
+
case AnnotationKind.ASSERT_NET: {
|
|
42
|
+
if (0 === stepCount)
|
|
43
|
+
return noStep();
|
|
44
|
+
return stepPatch(a, stepCount, { net: a.net });
|
|
45
|
+
}
|
|
41
46
|
case AnnotationKind.MARK_DYNAMIC:
|
|
42
47
|
return {
|
|
43
48
|
result: { ok: true, target: AnnotationTarget.FLOW, compiled: describeCompiled(a) },
|
|
@@ -109,6 +114,14 @@ export function describeCompiled(a) {
|
|
|
109
114
|
return `${COMPILED_PREDICATE_PREFIX} assert ${a.testid} visible`;
|
|
110
115
|
case AnnotationKind.ASSERT_STATE:
|
|
111
116
|
return `${COMPILED_PREDICATE_PREFIX} assert state ${a.statePath}${a.equals !== undefined ? ` == ${JSON.stringify(a.equals)}` : ''}`;
|
|
117
|
+
case AnnotationKind.ASSERT_NET: {
|
|
118
|
+
// Say the COUNT out loud when there is one. "will assert POST /refund" and "will assert
|
|
119
|
+
// exactly 1x POST /refund" are different gates, and the confirmation is the only place the
|
|
120
|
+
// author sees which one they just attached.
|
|
121
|
+
const what = `${a.net.method ?? 'any'} ${a.net.urlContains ?? '(any url)'}`;
|
|
122
|
+
const times = a.net.count === undefined ? '' : ` exactly ${String(a.net.count)}x`;
|
|
123
|
+
return `${COMPILED_PREDICATE_PREFIX} assert${times} request ${what}`;
|
|
124
|
+
}
|
|
112
125
|
case AnnotationKind.MARK_DYNAMIC:
|
|
113
126
|
return `${COMPILED_PREDICATE_PREFIX} ignore ${a.testid} (dynamic)`;
|
|
114
127
|
case AnnotationKind.SUCCESS_STATE:
|
|
@@ -124,7 +137,7 @@ export function describeCompiled(a) {
|
|
|
124
137
|
}
|
|
125
138
|
if (a.console !== undefined) {
|
|
126
139
|
const level = a.console.level ?? 'error';
|
|
127
|
-
return `${COMPILED_PREDICATE_PREFIX} succeed when ${a.console.absent
|
|
140
|
+
return `${COMPILED_PREDICATE_PREFIX} succeed when ${true === a.console.absent ? `no console.${level}` : `console.${level}`}`;
|
|
128
141
|
}
|
|
129
142
|
return `${COMPILED_PREDICATE_PREFIX} succeed when ${a.testid ?? ''} visible`;
|
|
130
143
|
case AnnotationKind.INTENT:
|
|
@@ -12,7 +12,7 @@ export interface StepExpect {
|
|
|
12
12
|
step: number;
|
|
13
13
|
expect?: FlowExpect;
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
interface DowngradeFinding {
|
|
16
16
|
step: number;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
@@ -21,3 +21,4 @@ export interface DowngradeFinding {
|
|
|
21
21
|
* lives with the affected index.
|
|
22
22
|
*/
|
|
23
23
|
export declare function detectDowngrades(before: readonly StepExpect[], after: readonly StepExpect[]): DowngradeFinding[];
|
|
24
|
+
export {};
|
|
@@ -1,74 +1,10 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
1
|
import type { FileSystemPort } from '../project/fs-port.js';
|
|
3
2
|
import type { StepExpect } from './assertion-integrity.js';
|
|
4
|
-
|
|
5
|
-
* The anti-reward-hacking baseline. Detecting an assertion DOWNGRADE requires knowing what the
|
|
6
|
-
* flow asserted the last time it PASSED — otherwise a weakened flow simply looks like a flow that always
|
|
7
|
-
* asserted little. Flows are git-checked and agent-editable, so we cannot trust the file itself as its own
|
|
8
|
-
* history.
|
|
9
|
-
*
|
|
10
|
-
* So: on every passing replay we record that flow's per-step assertion shape here, and the gate diffs the
|
|
11
|
-
* current file against it. Recorded only on PASS, deliberately — a failing run must never become the
|
|
12
|
-
* baseline a later weakening is measured against.
|
|
13
|
-
*
|
|
14
|
-
* Loads never throw: a missing/corrupt ledger degrades to "no baseline", which means no downgrade can be
|
|
15
|
-
* claimed. Failing OPEN is right here — accusing an agent of gaming the gate on the strength of an
|
|
16
|
-
* unreadable file would be worse than missing one downgrade.
|
|
17
|
-
*/
|
|
18
|
-
export declare const AssertionTiersFileSchema: z.ZodObject<{
|
|
19
|
-
version: z.ZodLiteral<1>;
|
|
20
|
-
flows: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
21
|
-
steps: z.ZodArray<z.ZodObject<{
|
|
22
|
-
step: z.ZodNumber;
|
|
23
|
-
expect: z.ZodOptional<z.ZodUnknown>;
|
|
24
|
-
}, "strip", z.ZodTypeAny, {
|
|
25
|
-
step: number;
|
|
26
|
-
expect?: unknown;
|
|
27
|
-
}, {
|
|
28
|
-
step: number;
|
|
29
|
-
expect?: unknown;
|
|
30
|
-
}>, "many">;
|
|
31
|
-
/** Source files this flow covered when it last passed — lets the gate scope DELETION findings to
|
|
32
|
-
* the files that actually changed, instead of flagging every intentionally-removed flow. */
|
|
33
|
-
sources: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
34
|
-
}, "strip", z.ZodTypeAny, {
|
|
35
|
-
steps: {
|
|
36
|
-
step: number;
|
|
37
|
-
expect?: unknown;
|
|
38
|
-
}[];
|
|
39
|
-
sources: string[];
|
|
40
|
-
}, {
|
|
41
|
-
steps: {
|
|
42
|
-
step: number;
|
|
43
|
-
expect?: unknown;
|
|
44
|
-
}[];
|
|
45
|
-
sources?: string[] | undefined;
|
|
46
|
-
}>>;
|
|
47
|
-
}, "strip", z.ZodTypeAny, {
|
|
48
|
-
version: 1;
|
|
49
|
-
flows: Record<string, {
|
|
50
|
-
steps: {
|
|
51
|
-
step: number;
|
|
52
|
-
expect?: unknown;
|
|
53
|
-
}[];
|
|
54
|
-
sources: string[];
|
|
55
|
-
}>;
|
|
56
|
-
}, {
|
|
57
|
-
version: 1;
|
|
58
|
-
flows: Record<string, {
|
|
59
|
-
steps: {
|
|
60
|
-
step: number;
|
|
61
|
-
expect?: unknown;
|
|
62
|
-
}[];
|
|
63
|
-
sources?: string[] | undefined;
|
|
64
|
-
}>;
|
|
65
|
-
}>;
|
|
66
|
-
export interface FlowBaseline {
|
|
3
|
+
interface FlowBaseline {
|
|
67
4
|
steps: StepExpect[];
|
|
68
5
|
sources: string[];
|
|
69
6
|
}
|
|
70
|
-
|
|
71
|
-
export declare const ASSERTION_TIERS_VERSION = 1;
|
|
7
|
+
type FlowTiers = Record<string, FlowBaseline>;
|
|
72
8
|
export declare class AssertionTiersStore {
|
|
73
9
|
#private;
|
|
74
10
|
constructor(fs: FileSystemPort, root: string);
|
|
@@ -77,3 +13,4 @@ export declare class AssertionTiersStore {
|
|
|
77
13
|
/** Record one flow's assertion shape + covered sources after it PASSED. Never breaks a replay. */
|
|
78
14
|
recordPassing(name: string, steps: readonly StepExpect[], sources?: readonly string[]): Promise<void>;
|
|
79
15
|
}
|
|
16
|
+
export {};
|
|
@@ -16,7 +16,7 @@ import { withFileLock } from '../project/file-lock.js';
|
|
|
16
16
|
* claimed. Failing OPEN is right here — accusing an agent of gaming the gate on the strength of an
|
|
17
17
|
* unreadable file would be worse than missing one downgrade.
|
|
18
18
|
*/
|
|
19
|
-
|
|
19
|
+
const AssertionTiersFileSchema = z.object({
|
|
20
20
|
version: z.literal(1),
|
|
21
21
|
flows: z.record(z.object({
|
|
22
22
|
steps: z.array(z.object({ step: z.number(), expect: z.unknown().optional() })),
|
|
@@ -25,7 +25,7 @@ export const AssertionTiersFileSchema = z.object({
|
|
|
25
25
|
sources: z.array(z.string()).default([]),
|
|
26
26
|
})),
|
|
27
27
|
});
|
|
28
|
-
|
|
28
|
+
const ASSERTION_TIERS_VERSION = 1;
|
|
29
29
|
export class AssertionTiersStore {
|
|
30
30
|
#fs;
|
|
31
31
|
#path;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which failing flows can support a verdict ABOUT THE CHANGED FILES.
|
|
3
|
+
*
|
|
4
|
+
* `affectedSavedFlows` deliberately re-runs any flow whose covered sources it cannot determine —
|
|
5
|
+
* over-running beats silently skipping — and reports those names in `unknownProvenance`. The verdict
|
|
6
|
+
* then ignored that and answered NO whenever anything failed, producing the observed
|
|
7
|
+
*
|
|
8
|
+
* verified: "no", because "1 of 1 covering flows failed
|
|
9
|
+
* (1 of them re-run only because Reticle cannot tell which sources they cover)"
|
|
10
|
+
*
|
|
11
|
+
* — a negative claim about `src/App.tsx` whose own explanation admits the evidence is not tied to it.
|
|
12
|
+
*
|
|
13
|
+
* This tool already refuses the mirror-image error: an uncovered change is UNKNOWN, never a green,
|
|
14
|
+
* because "nothing ran, so nothing was proved". A red nothing earned is the same mistake reversed.
|
|
15
|
+
*/
|
|
16
|
+
/** Failing flows minus the ones that only ran because their provenance is unknown. */
|
|
17
|
+
export declare function attributedFailures(failingFlows: readonly string[], unknownProvenance: readonly string[]): string[];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which failing flows can support a verdict ABOUT THE CHANGED FILES.
|
|
3
|
+
*
|
|
4
|
+
* `affectedSavedFlows` deliberately re-runs any flow whose covered sources it cannot determine —
|
|
5
|
+
* over-running beats silently skipping — and reports those names in `unknownProvenance`. The verdict
|
|
6
|
+
* then ignored that and answered NO whenever anything failed, producing the observed
|
|
7
|
+
*
|
|
8
|
+
* verified: "no", because "1 of 1 covering flows failed
|
|
9
|
+
* (1 of them re-run only because Reticle cannot tell which sources they cover)"
|
|
10
|
+
*
|
|
11
|
+
* — a negative claim about `src/App.tsx` whose own explanation admits the evidence is not tied to it.
|
|
12
|
+
*
|
|
13
|
+
* This tool already refuses the mirror-image error: an uncovered change is UNKNOWN, never a green,
|
|
14
|
+
* because "nothing ran, so nothing was proved". A red nothing earned is the same mistake reversed.
|
|
15
|
+
*/
|
|
16
|
+
/** Failing flows minus the ones that only ran because their provenance is unknown. */
|
|
17
|
+
export function attributedFailures(failingFlows, unknownProvenance) {
|
|
18
|
+
const unattributable = new Set(unknownProvenance);
|
|
19
|
+
return failingFlows.filter((flow) => !unattributable.has(flow));
|
|
20
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* nominal" needs no detail; a deviation needs its name, because a status line that says only "1 deviation"
|
|
7
7
|
* forces the human to go digging, which is exactly the work the buddy exists to remove.
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
interface BuddyStatusInput {
|
|
10
10
|
/** Every saved flow. */
|
|
11
11
|
total: number;
|
|
12
12
|
/** Flows with a passing artifact. */
|
|
@@ -22,3 +22,4 @@ export interface BuddyStatusInput {
|
|
|
22
22
|
* `✗ 2 deviations: checkout, billing · 39 nominal · 1 flaky quarantined`
|
|
23
23
|
*/
|
|
24
24
|
export declare function formatBuddyStatus(input: BuddyStatusInput): string;
|
|
25
|
+
export {};
|
|
@@ -16,13 +16,13 @@ const MAX_NAMED = 2;
|
|
|
16
16
|
export function formatBuddyStatus(input) {
|
|
17
17
|
const parts = [];
|
|
18
18
|
const deviations = input.deviations.length;
|
|
19
|
-
if (
|
|
19
|
+
if (0 === deviations) {
|
|
20
20
|
parts.push(`✓ ${String(input.passing)}/${String(input.total)} flows nominal`);
|
|
21
21
|
}
|
|
22
22
|
else {
|
|
23
23
|
const named = input.deviations.slice(0, MAX_NAMED).join(', ');
|
|
24
24
|
const rest = deviations - Math.min(MAX_NAMED, deviations);
|
|
25
|
-
parts.push(`✗ ${String(deviations)} deviation${
|
|
25
|
+
parts.push(`✗ ${String(deviations)} deviation${1 === deviations ? '' : 's'}: ${named}${rest > 0 ? ` +${String(rest)} more` : ''}`);
|
|
26
26
|
parts.push(`${String(input.passing)} nominal`);
|
|
27
27
|
}
|
|
28
28
|
if (input.quarantined.length > 0) {
|
package/dist/flows/coverage.d.ts
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
* (contract testids/signals/flows) actually touched by passing verification, so "everything" is honest
|
|
5
5
|
* about what everything means. Pure set math; the deviation report + gate carry the number.
|
|
6
6
|
*/
|
|
7
|
-
|
|
7
|
+
interface DeclaredSurface {
|
|
8
8
|
testids: readonly string[];
|
|
9
9
|
signals: readonly string[];
|
|
10
10
|
flows: readonly string[];
|
|
11
11
|
}
|
|
12
|
-
|
|
12
|
+
interface CoverageDimension {
|
|
13
13
|
total: number;
|
|
14
14
|
covered: number;
|
|
15
15
|
/** Percent covered, rounded; 100 when nothing is declared (vacuously complete). */
|
|
@@ -25,3 +25,4 @@ export interface Coverage {
|
|
|
25
25
|
overallPct: number;
|
|
26
26
|
}
|
|
27
27
|
export declare function computeCoverage(declared: DeclaredSurface, exercised: DeclaredSurface): Coverage;
|
|
28
|
+
export {};
|
package/dist/flows/coverage.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
function dimension(declared, exercised) {
|
|
8
8
|
const uncovered = declared.filter((d) => !exercised.has(d));
|
|
9
9
|
const covered = declared.length - uncovered.length;
|
|
10
|
-
const pct = declared.length
|
|
10
|
+
const pct = 0 === declared.length ? 100 : Math.round((covered / declared.length) * 100);
|
|
11
11
|
return { total: declared.length, covered, pct, uncovered };
|
|
12
12
|
}
|
|
13
13
|
export function computeCoverage(declared, exercised) {
|
|
@@ -16,6 +16,6 @@ export function computeCoverage(declared, exercised) {
|
|
|
16
16
|
const flows = dimension(declared.flows, new Set(exercised.flows));
|
|
17
17
|
const total = testids.total + signals.total + flows.total;
|
|
18
18
|
const covered = testids.covered + signals.covered + flows.covered;
|
|
19
|
-
const overallPct =
|
|
19
|
+
const overallPct = 0 === total ? 100 : Math.round((covered / total) * 100);
|
|
20
20
|
return { testids, signals, flows, overallPct };
|
|
21
21
|
}
|
package/dist/flows/decision.js
CHANGED
|
@@ -35,7 +35,7 @@ export function buildDecision(result, flow) {
|
|
|
35
35
|
// Green — but is it green-for-the-right-reason? A flow that asserts no consequence can pass while
|
|
36
36
|
// broken, so the honest next action is to add one.
|
|
37
37
|
const grade = flow !== undefined ? classifyFlowAssertions(flow) : undefined;
|
|
38
|
-
const verifiesOutcome = grade?.hasConsequenceAssertion
|
|
38
|
+
const verifiesOutcome = true === grade?.hasConsequenceAssertion;
|
|
39
39
|
const intent = flow?.intent;
|
|
40
40
|
return {
|
|
41
41
|
verdict: 'pass',
|
|
@@ -103,7 +103,7 @@ function suiteVerdictOf(status) {
|
|
|
103
103
|
function unverifiableReason(flow) {
|
|
104
104
|
if (flow === undefined)
|
|
105
105
|
return undefined;
|
|
106
|
-
if (flow.steps.length
|
|
106
|
+
if (0 === flow.steps.length) {
|
|
107
107
|
return 'the flow has no steps — it replays green whatever the app does. Record it again, or add steps with reticle_annotate.';
|
|
108
108
|
}
|
|
109
109
|
const c = classifyFlowAssertions(flow);
|
|
@@ -129,6 +129,14 @@ export function buildSuiteVerdict(runs) {
|
|
|
129
129
|
}
|
|
130
130
|
const decision = replay.decision ?? buildDecision(replay, flow);
|
|
131
131
|
const row = { flow: replay.name, verdict: suiteVerdictOf(replay.status) };
|
|
132
|
+
// Zero step results PLUS an error is the signature of a replay that never STARTED — the flow file
|
|
133
|
+
// failed to load (before the session is even resolved), or its leased context never came up. A
|
|
134
|
+
// flow that ran and failed always carries its failing step; a flow with no steps replays OK and
|
|
135
|
+
// never reaches here. Both arrive as ReplayStatus.ERROR, so without this the suite cannot tell
|
|
136
|
+
// "the app broke" from "Reticle could not run this" — and the bug metric reported the second as
|
|
137
|
+
// the first, 8 times in one sweep.
|
|
138
|
+
if (0 === replay.steps.length && replay.error !== undefined)
|
|
139
|
+
row.couldNotRun = true;
|
|
132
140
|
if (decision.whatChanged !== undefined)
|
|
133
141
|
row.whatChanged = decision.whatChanged;
|
|
134
142
|
if (decision.whereInSource !== undefined)
|
|
@@ -139,15 +147,22 @@ export function buildSuiteVerdict(runs) {
|
|
|
139
147
|
const total = runs.length;
|
|
140
148
|
const failed = failures.length;
|
|
141
149
|
// A real failure outranks an unverifiable flow: a broken flow is worse news than an empty one.
|
|
142
|
-
|
|
143
|
-
|
|
150
|
+
// An EMPTY suite is unverifiable for the same reason a flow that asserts nothing is — it is the
|
|
151
|
+
// purest green that cannot go red. It used to report `pass` with the summary "all 0 flows pass",
|
|
152
|
+
// which meant `reticle verify` went green on every project that had not written a flow yet, and on
|
|
153
|
+
// any project where the flows directory failed to resolve. Found by the adversarial MCP sweep; it
|
|
154
|
+
// was the only invented answer in 994 calls.
|
|
155
|
+
const status = failed > 0 ? 'fail' : unverifiable.length > 0 || 0 === total ? 'unverifiable' : 'pass';
|
|
156
|
+
const cannotFail = 0 === unverifiable.length
|
|
144
157
|
? ''
|
|
145
158
|
: ` — ${String(unverifiable.length)} verified nothing (${unverifiable.map((u) => u.flow).join(', ')})`;
|
|
146
|
-
const summary =
|
|
147
|
-
?
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
159
|
+
const summary = 0 === total
|
|
160
|
+
? 'no flows to verify — nothing was checked. Record one with reticle_record { action: "start" }, then reticle_flow_save.'
|
|
161
|
+
: 0 === failed
|
|
162
|
+
? 0 === unverifiable.length
|
|
163
|
+
? `all ${total} flow${1 === total ? '' : 's'} pass`
|
|
164
|
+
: `${String(passed)}/${String(total)} flows verified${cannotFail}`
|
|
165
|
+
: `${passed}/${total} flows pass — ${failed} need attention: ${failures.map((f) => f.flow).join(', ')}${cannotFail}`;
|
|
151
166
|
return {
|
|
152
167
|
status,
|
|
153
168
|
total,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Refuse to save a flow with no steps.
|
|
3
|
+
*
|
|
4
|
+
* It used to succeed: `flow_save` returned `{ stepCount: 0, grade: "assertion-free" }` with no error,
|
|
5
|
+
* `flow_list` then showed one more flow, and `flow_verify` called it `unverifiable` — forever. The
|
|
6
|
+
* agent believes it saved a regression test; what it wrote is a permanent suite entry that can never
|
|
7
|
+
* go green or red.
|
|
8
|
+
*
|
|
9
|
+
* Zero steps is not a degraded flow, it is the absence of one. And the refusal matters most on the
|
|
10
|
+
* stacks where the recorder is known to capture nothing, because there the empty save is the only
|
|
11
|
+
* signal the agent would ever get that something went wrong.
|
|
12
|
+
*/
|
|
13
|
+
import { ReticleTool } from '../tools/tool-names.js';
|
|
14
|
+
export function emptyFlowRefusal(stepCount, name) {
|
|
15
|
+
if (stepCount > 0)
|
|
16
|
+
return undefined;
|
|
17
|
+
return {
|
|
18
|
+
error: `'${name}' has no steps, so there is nothing to save — a flow with no steps can never pass or fail, and would sit in the suite reporting "unverifiable" forever`,
|
|
19
|
+
recovery: `Nothing was captured between ${ReticleTool.RECORD} { action: "start" } and { action: "stop" }. ` +
|
|
20
|
+
`Drive at least one action with ${ReticleTool.ACT} or ${ReticleTool.ACT_AND_WAIT} while the ` +
|
|
21
|
+
`recording is open, then save. If you DID act and this still says zero, the recorder captured ` +
|
|
22
|
+
`nothing for this stack — report it with ${ReticleTool.FEEDBACK}.`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { FileSystemPort } from '../project/fs-port.js';
|
|
2
2
|
import { type FlakeLedger } from './flake.js';
|
|
3
|
-
export declare const FLAKE_FILE_VERSION = 1;
|
|
4
3
|
/**
|
|
5
4
|
* Persists the per-flow flake ledger at `.reticle/flake.json`. Local single-project memory (stays OSS —
|
|
6
5
|
* OSS-VS-SERVER). Loads never throw: missing/malformed/wrong-version degrades to an empty ledger, so
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { dirname } from 'node:path';
|
|
2
2
|
import { reticleDirPaths } from '../project/reticle-dir.js';
|
|
3
3
|
import { FlakeFileSchema, emptyRecord, isFlaky, recordOutcome } from './flake.js';
|
|
4
|
-
|
|
4
|
+
const FLAKE_FILE_VERSION = 1;
|
|
5
5
|
/**
|
|
6
6
|
* Persists the per-flow flake ledger at `.reticle/flake.json`. Local single-project memory (stays OSS —
|
|
7
7
|
* OSS-VS-SERVER). Loads never throw: missing/malformed/wrong-version degrades to an empty ledger, so
|
package/dist/flows/flake.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { z } from 'zod';
|
|
|
5
5
|
* (excluded from gate-blocking) and surfaced for repair, never silently blocking. The caller accrues an
|
|
6
6
|
* outcome ONLY for unchanged-code replays (a fail after a code change is a legitimate red, not flake).
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
declare const FlakeRecordSchema: z.ZodObject<{
|
|
9
9
|
runs: z.ZodNumber;
|
|
10
10
|
fails: z.ZodNumber;
|
|
11
11
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -55,3 +55,4 @@ export declare function recordOutcome(record: FlakeRecord, passed: boolean): Fla
|
|
|
55
55
|
export declare function isFlaky(record: FlakeRecord, minRuns?: number): boolean;
|
|
56
56
|
/** Observed intermittent-failure rate (0 when too few runs to judge). */
|
|
57
57
|
export declare function flakeRate(record: FlakeRecord): number;
|
|
58
|
+
export {};
|
package/dist/flows/flake.js
CHANGED
|
@@ -5,7 +5,7 @@ import { z } from 'zod';
|
|
|
5
5
|
* (excluded from gate-blocking) and surfaced for repair, never silently blocking. The caller accrues an
|
|
6
6
|
* outcome ONLY for unchanged-code replays (a fail after a code change is a legitimate red, not flake).
|
|
7
7
|
*/
|
|
8
|
-
|
|
8
|
+
const FlakeRecordSchema = z.object({
|
|
9
9
|
runs: z.number().int().min(0),
|
|
10
10
|
fails: z.number().int().min(0),
|
|
11
11
|
});
|
|
@@ -31,5 +31,5 @@ export function isFlaky(record, minRuns = DEFAULT_MIN_FLAKE_RUNS) {
|
|
|
31
31
|
}
|
|
32
32
|
/** Observed intermittent-failure rate (0 when too few runs to judge). */
|
|
33
33
|
export function flakeRate(record) {
|
|
34
|
-
return record.runs
|
|
34
|
+
return 0 === record.runs ? 0 : record.fails / record.runs;
|
|
35
35
|
}
|
|
@@ -33,6 +33,18 @@ const INTENT_WITHOUT_OUTCOME_WARNING = 'This flow declares a business intent but
|
|
|
33
33
|
// Consequence vs presence-only classification lives in @reticlehq/core (one source for all graders).
|
|
34
34
|
const expectIsConsequence = flowExpectHasConsequence;
|
|
35
35
|
const expectIsWeak = flowExpectIsPresenceOnly;
|
|
36
|
+
/**
|
|
37
|
+
* A step expect counts the same as a flow-level one, because replay now evaluates the same set.
|
|
38
|
+
*
|
|
39
|
+
* This was briefly narrowed to `state` only — correctly, at the time: replay checked element presence
|
|
40
|
+
* and state and nothing else, so grading a step signal/net as a consequence produced a flow that
|
|
41
|
+
* reported `grade: "asserted"` and could not go red. `assertStepExpect` closed that by compiling
|
|
42
|
+
* every step expect through the same `successToPredicate` the flow-level path uses, so the narrow
|
|
43
|
+
* rule is no longer true and would now UNDER-grade real assertions.
|
|
44
|
+
*
|
|
45
|
+
* The invariant to keep: this function and what replay enforces must move together. If one ever
|
|
46
|
+
* describes more than the other, the difference is a false green or a lost verification.
|
|
47
|
+
*/
|
|
36
48
|
/** Walk steps + act_sequence sub-steps so an expect on either level is counted. */
|
|
37
49
|
function flattenSteps(steps) {
|
|
38
50
|
const out = [];
|
|
@@ -64,7 +64,7 @@ async function recordReplayRun(deps, name, status, driftSteps, durationMs, proje
|
|
|
64
64
|
});
|
|
65
65
|
// Per-project cloud: push memory outcomes only when cloud is attached AND memory sync is enabled.
|
|
66
66
|
const cloud = await resolveProjectCloud(deps.fs, deps.reticleRoot, homedir(), process.env);
|
|
67
|
-
if (cloud.config
|
|
67
|
+
if (null === cloud.config || !cloud.policy.memory)
|
|
68
68
|
return; // not attached / memory disabled → local only
|
|
69
69
|
const result = await syncRunRecordToCloud({ kind: RunKind.FLOW_REPLAY, name, status: runStatus, at: deps.now(), durationMs }, projectId, cloud.config, (url, init) => fetch(url, init));
|
|
70
70
|
if (result.outcome !== SyncOutcome.SYNCED) {
|
|
@@ -82,7 +82,7 @@ async function recordReplayRun(deps, name, status, driftSteps, durationMs, proje
|
|
|
82
82
|
*/
|
|
83
83
|
export function startPathMismatchHint(flow, session) {
|
|
84
84
|
const startPath = flow.startPath;
|
|
85
|
-
if (startPath === undefined || startPath.length
|
|
85
|
+
if (startPath === undefined || 0 === startPath.length)
|
|
86
86
|
return undefined;
|
|
87
87
|
const routes = session.eventsSince(0).filter((e) => e.type === EventType.ROUTE_CHANGE);
|
|
88
88
|
const last = routes.at(-1);
|
|
@@ -127,7 +127,7 @@ export async function replayNamedFlow(deps, args) {
|
|
|
127
127
|
// Floor the success oracle at the start of THIS replay so a stale signal from a prior run
|
|
128
128
|
// in the same session can't fake a pass.
|
|
129
129
|
const replayFloor = session.elapsed();
|
|
130
|
-
const steps = await replayFlow(session, loaded.value, waitForPredicate, FLOW_SIGNAL_TIMEOUT_MS, args['confirmDangerous']
|
|
130
|
+
const steps = await replayFlow(session, loaded.value, waitForPredicate, FLOW_SIGNAL_TIMEOUT_MS, true === args['confirmDangerous']);
|
|
131
131
|
// "green means intent satisfied": when every step ran clean, assert the flow's success
|
|
132
132
|
// end-condition as a real consequence. A signal/net success that never fires FAILS the replay
|
|
133
133
|
// even though all locators resolved — the regression a healed-but-wrong locator ships green.
|
|
@@ -188,7 +188,7 @@ export async function replayNamedFlow(deps, args) {
|
|
|
188
188
|
async function computeReplayDeviation(deps, session, floor) {
|
|
189
189
|
try {
|
|
190
190
|
const segments = computeSegments(session.eventsSince(floor));
|
|
191
|
-
if (segments.length
|
|
191
|
+
if (0 === segments.length)
|
|
192
192
|
return undefined;
|
|
193
193
|
return await reportAndAccumulate(new EnvelopeStore(deps.fs, deps.reticleRoot), segments);
|
|
194
194
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnchorKind, type CommandResult, type FlowAnchor, type FlowFile, type FlowStepResult, type ReticleEvent, type QueryEmptyHint } from '@reticlehq/core';
|
|
1
|
+
import { AnchorKind, type CommandResult, type Drift, type FlowAnchor, type FlowFile, type FlowStepResult, type ReticleEvent, type QueryEmptyHint } from '@reticlehq/core';
|
|
2
2
|
import type { EvalResult, Predicate } from '../events/predicate.js';
|
|
3
3
|
/**
|
|
4
4
|
* The session surface flow-replay needs: QUERY to re-resolve a testid anchor against the live
|
|
@@ -47,13 +47,9 @@ export type Sleep = (ms: number) => Promise<void>;
|
|
|
47
47
|
* Such a drift is surfaced (with a nearest) but never auto-healed.
|
|
48
48
|
*/
|
|
49
49
|
export declare function nearestIsAmbiguous(missing: string, present: string[]): boolean;
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
* so a present anchor costs one query; a genuinely missing one costs the full (bounded) settle and
|
|
54
|
-
* then drifts. The last result's near-miss hint is returned for the drift record.
|
|
55
|
-
*/
|
|
56
|
-
export declare function resolveQuery(session: FlowReplaySession, queryArgs: Record<string, unknown>, sleep: Sleep): Promise<{
|
|
50
|
+
/** Build the legible-drift record for a testid anchor that resolved to zero live elements. */
|
|
51
|
+
export declare function testidDrift(value: string, hint: QueryEmptyHint | undefined): Drift;
|
|
52
|
+
export declare function resolveQuery(session: FlowReplaySession, queryArgs: Record<string, unknown>, sleep: Sleep, now?: () => number): Promise<{
|
|
57
53
|
refs: string[];
|
|
58
54
|
hint?: QueryEmptyHint;
|
|
59
55
|
}>;
|
|
@@ -61,6 +57,8 @@ export declare function resolveQuery(session: FlowReplaySession, queryArgs: Reco
|
|
|
61
57
|
export declare function componentLabel(anchor: Extract<FlowAnchor, {
|
|
62
58
|
kind: typeof AnchorKind.COMPONENT;
|
|
63
59
|
}>): string;
|
|
60
|
+
/** The value of a step's primary anchor, for labelling the result row. */
|
|
61
|
+
export declare function anchorLabel(anchor: FlowAnchor): string;
|
|
64
62
|
/** QUERY args for a component auto-anchor — source (precise) + component name (coarse) as given. */
|
|
65
63
|
export declare function componentQueryArgs(anchor: Extract<FlowAnchor, {
|
|
66
64
|
kind: typeof AnchorKind.COMPONENT;
|