fix(platform): automation equipment, wizard model pin, and drive chain - #3118
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 hardcodedconnectors: []in bothlistAutomationCapabilitiesandlistProjectCapabilities, 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
modelstring. 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. Newblank-automation-dialog.test.tsxpins all three behaviours.3.
no job mapping for scheduled function: automations/agent_host:driveWorkflowAgentTurnEvery 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
driveWorkflowAgentTurninternalAction (the turn's self-chaining drainer) was deleted in the Postgres cutover (#3107), butcontinueOrSettlestill schedules it andautomationShimScheduleronly 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 newautomation.agent_drivejob (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.tscron never made it to 0.5 — the cron-roster audit had conflated it with the liveness sweep, which only re-pokeswakeAt-overdue runs and deliberately leaves a healthywaitingpark alone (MIGRATION.md carries the correction).Fix —
watchdog.automation_agents(*/2) runsdomains/automations/reattach.ts, the workflow twin of the task re-attach: listwaitingruns 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: anawaiting_humanop 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 intodomains/sandbox/recovery.ts(their 0.4 home was the sandbox domain) and shared with the task lane, whose behaviour is byte-identical.Verification
{model, modelProvider: "openrouter", connectors: ["github"]}→ editor shows the pinned pair with no unpinned warning.Note for demo05: the failed
triage-glitchtip-issuesrun burned 4 turns of LLM spend through the auto-retry ladder; after deploying this, a rerun is all it needs.