Skip to content

fix(babysitter): fail closed on metadata gaps and retry unplaced claims - #486

Merged
kjgbot merged 2 commits into
mainfrom
lane/babysitter-failclosed-0906
Sep 6, 2026
Merged

fix(babysitter): fail closed on metadata gaps and retry unplaced claims#486
kjgbot merged 2 commits into
mainfrom
lane/babysitter-failclosed-0906

Conversation

@kjgbot

@kjgbot kjgbot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

Factory-created PR handoffs could spawn without readable PR metadata, and a failure before worker placement could permanently retain the PR generation claim. This follow-up to #484 addresses both review findings.

  • Defer activation until PR state, draft status, and labels are readable; retry the handoff without claiming or placing a worker.
  • Release only the matching generation after confirmed non-placement. Fleet preflight failures carry explicit evidence; uncertain placement and failures after placement retain the claim and log repo, PR, generation, and reason.
  • Preserve the fence during durable recovery and synthesized missing-worker exits. A live roster worker can be adopted without replay; a planned name alone cannot authorize a restart.

Validation:

  • P1/P2 regressions failed before implementation and pass afterward; the uncertain-plan restart regression also failed before its fix.
  • Full local suite: 2,575 passed, 1 skipped (inherited broker environment isolated).
  • Focused factory suite: 110 passed. Build and featuremap pass.
  • Packed validation: 6 run-cost tests and 12 end-to-end checks, attested to f32d2af34fc2daf6489cd4f4fe47ae4c99a3350b.
  • CI run: https://github.com/AgentWorkforce/software-garden/actions/runs/34050001744 (all five jobs passed on the current head).

Routed activation remains disabled. No merge performed.

Review status: review threads were inspected after CI completed; none are present yet. Independent review is pending.

Session-Id: 01a077c7-5a1f-7a50-9b0f-e5b918dcc472
@coderabbitai

coderabbitai Bot commented Sep 6, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 4d718c8d-5dc3-4a26-b7b8-98a55c74daee


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kjgbot
kjgbot marked this pull request as ready for review September 6, 2026 17:57
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai review

Requested for exact head f32d2af34fc2daf6489cd4f4fe47ae4c99a3350b.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 8 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/fleet/relay-fleet-client.ts
Comment thread src/orchestrator/factory.ts Outdated
`FleetSpawnNotCreatedError` wrapped every pre-placement failure and stamped
its own name over the cause's. The bit it adds — "no worker was created" — has
one reader, and that reader uses `instanceof`; every other reader of a dispatch
failure reads a class NAME off the OUTERMOST error. So the wrapper was erasing
`FactoryAgentRegistrationError`, `ReadOnlyFleetIdentityError` and
`RelaySpawnAckTimeoutError` from `perItemDispatchSkipReason`'s operator
sentence, from the hosted orchestrator's `errorClass`, and from the head of the
#355 class-name vocabulary — collapsing an enrolment refusal, a read-only
identity and a bootstrap ack timeout into one indistinguishable bucket, on
exactly the surfaces an operator has during an outage.

The wrapper now adopts the cause's class name as its own (and exposes it as
`causeClass`), guarded by `isTelemetryErrorClassName` so a dependency-controlled
`name` still cannot choose what crosses a telemetry boundary. `instanceof`, and
therefore `placement.status = 'not-created'`, is unchanged.

Durable recovery of a factory-created babysitter also ran two roster-based
admission passes back to back: an adoption pass that only no-ops when no live
roster entry exists, then `#ensureBabysitter`'s real admission repeating the
same `retryOnTimeout(roster(), { attempts: 3, delayMs: 2000 })`. The adoption
pass now hands its read forward through `#admissionRoster`, keyed by PR
identity because a reconciled agent exit can reach that admission first. The
hand-off is single-use and age-bounded, so a slow gate chain re-reads rather
than placing against a roster that has stopped describing the fleet; the
durable `factory-created:<pr>` claim, not adoption, remains what keeps one PR
to one worker.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Ld4S9gUGzTjhVhbtX9cTd

Session-Id: fe8515ad-c2bc-4f9a-9b39-88b2ba12f73e
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai review

Requested for exact head 5fad4da0941ac8a2a8c6482c9bed016976a75000.

@kjgbot
kjgbot merged commit 1170d40 into main Sep 6, 2026
8 checks passed
@kjgbot
kjgbot deleted the lane/babysitter-failclosed-0906 branch September 6, 2026 19:46
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