Skip to content

orb(safety): visual-capture workflow_dispatch bypasses the write-suppression chokepoint and fires under dry_run #9067

Description

@JSONbored

The structural invariant is that every installation write routes through makeInstallationOctokit (src/github/client.ts ~707-730), whose request hook suppresses all mutations when mode ≠ live. Two raw timeoutFetch writers escape it:

1. src/review/visual/actions-fallback.ts ~103POST /repos/{o}/{r}/actions/workflows/visual-capture-fallback.yml/dispatches. Nothing in src/review/visual/** or visual-wire.ts consults resolveRepoActionMode/agentPaused. runVisualCapture short-circuits only on args.mode === "paused" (src/queue/processors.ts ~8088) — so under dry_run it proceeds and fires a real workflow_dispatch, starting a genuine Actions run and burning CI minutes in the target repo. That directly contradicts the documented dry-run contract ("suppresses the terminal GitHub-side write only").

2. src/github/app.ts ~612POST /actions/runs/{id}/cancel. Both current call sites happen to gate correctly (agent-action-executor.ts ~603-609 is inside the live branch; processors.ts ~6166-6177 checks mode !== "live"), so this is safe by caller discipline, not by construction — a third caller would silently escape.

Note this compounds with the kill-switch issue (#9049): dry-run is currently defeated fleet-wide by a manifest agentDryRun: false, so this path's misbehavior is presently masked rather than absent.

Fix

Thread AgentActionMode into dispatchVisualCaptureFallback and skip on non-live. Better: route both raw writers through a shared installationWrite() helper applying the same suppression check as the Octokit hook, so the invariant is structural again rather than conventional.

Metadata

Metadata

Assignees

Labels

maintainer-onlyOwner-only work — yields no Gittensor points.orbGittensory Orb related - maintainer self-hosting analytics.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions