regressions: red/green flows for the 2026-08-27 platform bugs (dormant until gates 2+6) - #5
Conversation
Four bugs found during the first dogfood runs, each written twice in the
flows v2 dialect: a red case that passes while the bug is present (the
executable bug report) and a green case that fails until it is fixed (the
acceptance test). A bug is closed when red starts failing and green starts
passing in the same run.
enrollment-token-bearer-auth mint gates on requireSessionAuth, so a CLI
bearer gets 403 and enrollment has no
headless path (cloud#3202)
worker-daemon-bun-argv --daemon re-execs with a $bunfs argv; the
daemon dies while the CLI reports a pid
cron-succeeded-into-void a schedule reports succeeded with no worker
to execute it (covenant 2)
cross-account-workspace-404 a not-found workspace is rendered to the
user as a permissions problem (covenant 1)
The suite is DORMANT: no on() trigger, no schedule, no deploy, nothing in a
drive loop or CI. It targets @relayflows/surface, which does not exist yet;
surface.d.ts is a declaration-only slice of the v2 surface carrying exactly
what these pairs need, so it doubles as a requirements list for gate-1 SDK
work. Typechecking is opt-in (regressions/tsconfig.json), not part of
`npm test`. MANIFEST.json records the gates each pair waits on so the Garden
can adopt them automatically once flows run in cloud. README names the four
things the dialect cannot yet say, rather than working around them silently.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 17 minutes. View limit detailsLimit details: You’ve used the included review currently available. Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Free Run ID: 📒 Files selected for processing (14)
Note 🎁 Summarized by CodeRabbit FreeYour organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/settings/billing. Comment |
|
Upstream tracking: AgentWorkforce/cloud#3202 covers three of the four bugs here (bearer-auth enrollment, cross-account 404 messaging, cron-succeeded-into-void). The Note the dependency edge already in MANIFEST.json: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 50c317a079
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| `-X ${input.method}`, | ||
| `"$(cat ${CLOUD_MOUNT}/base_url)${input.path}"`, | ||
| `-H "Authorization: Bearer $(cat ${CLOUD_MOUNT}/principals/${input.principal}/token)"`, | ||
| "-H 'Content-Type: application/json'", |
There was a problem hiding this comment.
Send browser-session credentials as cookies
When the enrollment and cross-account flows select browser-session or browser-session-member, this helper still sends the credential through Authorization: Bearer. The endpoint under test explicitly distinguishes session auth via requireSessionAuth, so these requests exercise bearer auth as well: the enrollment control receives the same 403 as the CLI case, and the cross-account cases never reach the workspace lookup. Emit the authentication mechanism appropriate to each principal rather than treating every mounted credential as a bearer token.
Useful? React with 👍 / 👎.
| // The durable half: local liveness is not enough, cloud has to see it. | ||
| // `awaitHeartbeat` compiles to a kernel wait, so this parks rather than polls. | ||
| await f.cloud.workers | ||
| .awaitHeartbeat({ workerId: "regression-daemon", as: "cli-bearer", within: "90s" }) |
There was a problem hiding this comment.
Query the worker ID actually started by the daemon
On any sandbox whose registered worker ID is not literally regression-daemon, this waits on an unrelated or nonexistent worker even if the daemon fix is correct. The flow neither registers a worker nor captures an ID before cloud worker start, while registration elsewhere returns a server-generated workerId; therefore this gate cannot establish that the process started above heartbeated. Capture the active worker's ID from registration or the local daemon record and pass that value here.
Useful? React with 👍 / 👎.
| ` --token-file ${enrolment.tokenPath} --name regression-cron-worker --json` + | ||
| " && agent-relay cloud worker start --daemon", | ||
| ) | ||
| .gate((out) => out.includes("daemon started"), "an executor now exists"); |
There was a problem hiding this comment.
Wait for the executor heartbeat before firing the schedule
When daemon startup returns before its first cloud heartbeat, this gate only proves that the CLI printed a success line, not that an eligible executor exists; the immediately following fire can consequently report no_executor on a fully fixed platform. This is especially relevant because the adjacent daemon regression is explicitly about the success message preceding real liveness. Wait for the newly registered worker to become online before triggering phase 2.
Useful? React with 👍 / 👎.
| "every step carrying a completionReason (AGENTS.md rule 4)", | ||
| ); | ||
|
|
||
| await f.cloud.schedules.remove({ scheduleId: schedule.id, as: "cli-bearer" }); |
There was a problem hiding this comment.
Clean up schedules after expected gate failures
This cleanup is reached only when every preceding gate passes, but the documented red/green protocol deliberately expects one twin to fail in either platform state. While the bug is open, the green flow stops at the no_executor assertion; after the fix, the red twin stops at its succeeded assertion, leaving their fixed-name disposable schedules behind on every suite run. Subsequent creates can conflict, and leaked schedules can later fire; arrange cleanup in a failure-safe finalizer or use a lifecycle that removes the schedule even when a gate fails.
Useful? React with 👍 / 👎.
Four platform bugs were found on 2026-08-27, during the first dogfood runs. This
PR writes each of them down as a pair of executable flows in the v2 dialect,
so they stop being prose in a report and start being something a machine re-runs.
The method: red, then green
<slug>.red.flow.tsreproduces the failure. Its gates assert the brokenbehaviour, so it passes while the bug is present. This is the bug report.
<slug>.green.flow.tsasserts the corrected behaviour. It fails whilethe bug is present and passes once it is fixed. This is the acceptance test.
A bug is closed only when its red case starts failing and its green case starts
passing, in the same run. Either half alone can lie — a green test that never ran
red proves nothing about the bug it claims to cover.
The four bugs
enrollment-token-bearer-authrequireSessionAuth, so a valid CLI bearer is answered 403. Worker enrollment has no headless, self-host, or CI path.cloud packages/web/app/api/v1/workers/enrollment-tokens/route.ts:113-115;{"error":"Forbidden"}(403). Tracked as cloud#3202.worker-daemon-bun-argvagent-relay cloud worker start --daemonre-execs the bun-compiled binary passingprocess.argv[1], which underbun build --compileis the virtual/$bunfs/root/agent-relay-darwin-arm64. The child dies immediately; the parent has already printed success.relay packages/cli/src/cli/commands/cloud-worker.ts:266(process.argv[1] ?? 'agent-relay'),:279(spawnprocess.execPath),:396(Cloud worker daemon started: <pid>);error: unknown command '/$bunfs/root/agent-relay-darwin-arm64'.cron-succeeded-into-voidlastTriggerStatus: "succeeded"as soon as the launch POST returns a runId — even with no worker to claim it. A schedule can be silently zero forever.cloud packages/web/app/api/v1/workflows/schedules/trigger/route.ts:178-192; observed twice on scheduleflows-drive: runs8e3e5916and740c3a27, bothsucceeded, nothing executed, no branch, no PR.cross-account-workspace-404cloud .../enrollment-tokens/route.ts:29-32(404{"error":"Workspace not found"}),cloud packages/web/components/workers/NewWorkerForm.tsx:91-92; workspaces50587328-441d-4acb-b8f3-dbe1b3c5de99(khaliq@agentrelay.com) vs0fb35c2e-861f-4d44-848e-fa3f5a3e192e(khaliqgant@gmail.com).Two of these are covenant violations rather than mere defects.
cron-succeeded-into-voidis covenant 2 verbatim — "a 'succeeded' that didnothing is by definition a kernel bug" — and
worker-daemon-bun-argvis thesame shape at the CLI: a reported pid for a process that is already gone.
cross-account-workspace-404is covenant 1: the error names the wrong conditionin the user's vocabulary.
This suite does not run yet — and must not
No flow here declares an
on()trigger, none is deployed, no schedule isregistered, and nothing outside
regressions/references the directory except asingle backlog line. Typechecking is opt-in
(
cd sdk && npx tsc -p ../regressions/tsconfig.json) and is deliberately not partof
npm test— which still passes, 54/54, unchanged.The flows target
@relayflows/surface, the v2 authoring surface, which does notexist yet.
regressions/surface.d.tsis a declaration-only slice of it carryingexactly the shapes these four pairs need, so it doubles as a requirements list
for gate-1 SDK work; delete it when the real surface ships.
Which gates unblock each pair (machine-readable in
regressions/MANIFEST.json):enrollment-token-bearer-authworker-daemon-bun-argvbun runcron-succeeded-into-voidcross-account-workspace-404cron-succeeded-into-void.greenandworker-daemon-bun-argvadditionally dependon cloud#3202 being fixed, since both need to enrol a worker headlessly.
What the dialect cannot say yet
Named in
regressions/README.mdrather than worked around silently:declared way". The surface has
.gate(), which causes a typed failure, butnothing that asserts one. Every red probe is therefore a deterministic step
that always exits 0 and prints
<body>\n<status>for a postfix gate to judge.The shape it wants is a first-class negative gate — gate-1 verification work.
cross-account pair asserts the API-level cause instead, and its green case
moves the user-facing message to the API, where the condition is known.
principals/<name>/tokenmount reads plus anas:argument, pending gate 8.Adoption
Once flows run in cloud, the Software Garden should adopt this suite: run each
red case expecting a pass (the standing proof the bug is still open) and each
green case expecting a fail, and flip the pair to green-only when the fix lands.
MANIFEST.jsoncarries the gates,blockedUntilnotes, anddependsOnedges sothat pickup can be automatic.
🤖 Generated with Claude Code