Skip to content

fix(platform): automation equipment, wizard model pin, and drive chain - #3118

Merged
larryro merged 1 commit into
mainfrom
fix/automation-equip-pin-drive
Aug 31, 2026
Merged

fix(platform): automation equipment, wizard model pin, and drive chain#3118
larryro merged 1 commit into
mainfrom
fix/automation-equip-pin-drive

Conversation

@larryro

@larryro larryro commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Three defects Larry hit on demo05's automation agent lane, plus the recovery backstop the third one exposed. One branch because they share a blast radius: what an agent node is equipped with, which provider serves it, and whether its turn survives past the first drain window.

1. Equipment picker: Connectors always empty

The org had a GitHub credential, but the agent node's Equipment menu (and the project-agent dialog) showed "No connectors added yet."

Root cause — the 0.5 composer (backend/domains/chat/composer.ts) still hardcoded connectors: [] in both listAutomationCapabilities and listProjectCapabilities, from before the connector-credentials domain landed. The stub was never revisited.

Fix — a new listConnectedConnectorSlugs (distinct slugs holding an active credential; disabled/needs-reauth cannot serve a run) joined with the shipped catalog for label/description/icon. One helper serves both listings, so the automation node and the project-agent dialog cannot drift. The grant vocabulary stays the connector slug — exactly what the sandbox bridge gates on and what dispatch resolves to the pair's default credential.

2. Blank-automation wizard drops the provider pin

Picking the Anthropic-subscription Fable 5 in New automation saved a node that ran on OpenRouter.

Root cause — the wizard was the surface #3056/#3057 missed: it collapsed model options by id and stored a bare model string. The saved node was a pinless pick, and the run's two-pass walk serves direct providers first — OpenRouter's BYO key stole the subscription model.

Fix — the wizard now speaks the shared (provider, model) picker vocabulary (toModelOptions/findSelectedModel): one option per pair, subscription entries filtered to the host default harness (claude-code — the scaffolded node names none) and labelled "· Subscription", and the scaffold stores the {model, modelProvider} pair. New blank-automation-dialog.test.tsx pins all three behaviours.

3. no job mapping for scheduled function: automations/agent_host:driveWorkflowAgentTurn

Every agent turn spanning more than one drain window failed with this after 4 attempts — while the agent kept working in the sandbox.

Root cause — the 0.4 driveWorkflowAgentTurn internalAction (the turn's self-chaining drainer) was deleted in the Postgres cutover (#3107), but continueOrSettle still schedules it and automationShimScheduler only mapped start/ask-resume/step/poll. First window ends → drive scheduling throws → the start's catch settles the turn as failed → the stepper's in-node auto-retry burns three more identical turns → "(after 4 attempts)". The itest never caught it: its fake harness finishes in one terminal window, so nothing ever re-schedules (the task lane's seam comment names this exact trap).

Fix — the drive body is restored verbatim as driveWorkflowAgentTurnImpl (orphan check → deadline cut → attach window → continueOrSettle), the shim maps the ref onto a new automation.agent_drive job (retryLimit: 0, 12h expiry — the task-lane posture: a second drive would replay the ring buffer twice), and the worker runs it on the automation shim. A scheduler-seam probe now asserts the mapping directly, task-lane style.

4. Re-attach backstop for a dead drive chain

With 3 fixed, a drive chain lost to a worker restart would still strand its run until the 12h deadline: 0.4's automations/recover_agent_turns.ts cron never made it to 0.5 — the cron-roster audit had conflated it with the liveness sweep, which only re-pokes wakeAt-overdue runs and deliberately leaves a healthy waiting park alone (MIGRATION.md carries the correction).

Fixwatchdog.automation_agents (*/2) runs domains/automations/reattach.ts, the workflow twin of the task re-attach: list waiting runs whose cursor holds an unsettled agent turn and whose op lease went silent (a missing op row — a start that died before writing it — counts too), probe the exec first (a transport hiccup never reads as a dead agent), claim through the op-row fence, and enqueue one drive window. The ask-park is spared: an awaiting_human op is terminal with no result for up to 7 days by design, and re-attaching it would settle a question mid-wait. claimRecoveryResume + the staleness knob are hoisted into domains/sandbox/recovery.ts (their 0.4 home was the sandbox domain) and shared with the task lane, whose behaviour is byte-identical.

Verification

  • Backend integration check 276/276 — three new probes: a connected connector appears in the capability listing (before/after a credential), the drive scheduler seam maps the self-chained ref (and throws on an unmapped one), and the four-shape re-attach (stale re-attached, op-less re-attached with the claim creating the row, live left alone, ask-parked spared) plus the unreachable-spawner case.
  • Repo check gate green (lint, typecheck, vitest server + UI incl. the new wizard test).
  • Driven in a real browser against the fixed backend: GitHub credential created → Equipment lists GitHub + Google Drive → wizard pick saved as {model, modelProvider: "openrouter", connectors: ["github"]} → editor shows the pinned pair with no unpinned warning.

Note for demo05: the failed triage-glitchtip-issues run burned 4 turns of LLM spend through the auto-retry ladder; after deploying this, a rerun is all it needs.

Three demo05 defects on the automation agent lane, plus the recovery
backstop the third one exposed:

- The equipment picker's Connectors section was always empty: the 0.5
  composer still hardcoded connectors: [] from before the
  connector-credentials domain landed. Both capability listings now offer
  the org's connected set — every shipped connector holding an active
  credential, labelled from the catalog.
- The blank-automation wizard collapsed model options by id and stored a
  bare model string, so a subscription pick silently resolved to whichever
  direct provider the walk reached first. It now uses the shared
  (provider, model) picker vocabulary — subscription entries filtered to
  the host default harness — and scaffolds the pair onto the agent node.
- Any agent turn spanning more than one drain window died with "no job
  mapping for scheduled function: driveWorkflowAgentTurn" (then burned
  three auto-retries): the 0.4 drive action was dropped in the Postgres
  cutover while continueOrSettle kept scheduling it. The drive body is
  restored as driveWorkflowAgentTurnImpl, mapped onto the new
  automation.agent_drive job, and probed via a scheduler-seam check the
  single-window itest harness could never reach.
- The 0.4 recover_agent_turns cron never made it to 0.5 (the cron-roster
  audit conflated it with the liveness sweep), so a drive chain lost to a
  restart stranded its run until the 12h deadline. watchdog.automation_agents
  (*/2) now re-attaches abandoned workflow turns from the run cursor,
  sharing the sandbox domain's claimRecoveryResume fence with the task
  lane, and sparing the ask-park (awaiting_human) that legitimately idles.

Verified: backend integration check 276/276 (new probes: connected
connector offered as equipment; drive scheduler seam; four-shape
re-attach), repo check gate green, wizard pin covered by a new component
test, and the full flow driven in a real browser.
@larryro
larryro merged commit 5e67beb into main Aug 31, 2026
48 checks passed
@larryro
larryro deleted the fix/automation-equip-pin-drive branch August 31, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant