Spun out of #1334's PR validation (#1369). Not a new mechanism — a recurrence of #1355 after #1357 shipped.
Symptom
pr-validation.yml's E2E job exits at its shared Collect models pre-flight, so the lane never runs the PR's specs at all — its step list goes Collect models → Run destructive lane, with no impacted-specs step in between.
build axis: ✅ openai
build axis: ✅ anthropic
build axis: ✅ google
⚠️ collect-models: no credential write observed for provider "anthropic" within 180s of clicking Save.
The panel stays busy while a write is in flight, so the NEXT provider is what pays for this (#1355).
⚠️ collect-models: no credential write observed for provider "google" within 180s of clicking Save.
1 failed → Process completed with exit code 1
All three keys validate (build axis: ✅ on each) — this is not a drained or revoked credential. The first attempt surfaced the downstream form of the same thing: anthropic — no models collected from the providers panel, which collect-models.spec.ts:166 classifies as a non-billing hard failure and fails on, rather than the billing path that only warns (#952/#980).
Occurrences (2026-08-07, all on 1.12.0.dev19)
| Run |
Branch |
Outcome |
| 31175663512 11:51 |
chore/quarantine-daily-1361 |
no credential write observed for provider "anthropic" within 180s |
| 31188034419 14:31 |
test/issue-1334-oc-credential-axis |
anthropic — no models collected from the providers panel, job exit 1 |
| same run, re-run 14:44 |
same |
anthropic and google both stalled, job exit 1 |
| same run, 3rd attempt 14:55 |
same |
passed (5.0m), all three providers collected |
Two independent branches, and 2 of 3 attempts on one of them. The provider that stalls moves between attempts (anthropic; then anthropic + google) and a plain re-run clears it — the load-dependent shape, not a consequence of any diff.
Preliminary read (descriptive — NOT a verdict)
#1355 was "clicks a Save button still aria-busy from the previous provider", and #1357 replaced a fixed 60 s clock with waiting for the credential write, measured at 103 s. The warning text above is #1357's own — it fires when that wait expires, now at 180 s. So the mechanism #1355 described is still reachable; what is unmeasured is whether 180 s is simply short on a loaded runner, or whether the write can fail to be observed at all (in which case a longer clock never fixes it, which is the lesson #1357's own header records).
Not claimed here: that the threshold is the answer. Raising a timeout because a timeout expired is the move #1357 argued against.
Why this matters beyond one red
Collect models is a hard gate on this lane. When it exits 1 the PR gets no E2E coverage of its own change, and the only signal is a red check whose cause is three steps away from the diff. #1369 merged on an ambient-red justification for exactly this (comment) — that reasoning was sound for one PR, but it is not a mechanism, and the next PR pays the same cost.
Investigation directive
Instrument before tuning. (a) Establish what the 180 s wait actually observes on a stalling run — is the POST|PATCH /api/v1/variables/ never issued, issued and never answered, or answered after the deadline? The distinction decides whether any clock helps. (b) Only then decide between a longer deadline, a per-provider retry, and making the sweep resilient to one provider stalling (the other two collected fine in every occurrence above). (c) Consider whether a Collect models failure should abort the PR lane at all: daily-stable.yml deliberately does not let it kill the shard (#980), and the asymmetry is undocumented.
Deliverables (Done when)
Spun out of #1334's PR validation (#1369). Not a new mechanism — a recurrence of #1355 after #1357 shipped.
Symptom
pr-validation.yml's E2E job exits at its sharedCollect modelspre-flight, so the lane never runs the PR's specs at all — its step list goesCollect models→Run destructive lane, with no impacted-specs step in between.All three keys validate (
build axis: ✅on each) — this is not a drained or revoked credential. The first attempt surfaced the downstream form of the same thing:anthropic — no models collected from the providers panel, whichcollect-models.spec.ts:166classifies as a non-billing hard failure and fails on, rather than the billing path that only warns (#952/#980).Occurrences (2026-08-07, all on
1.12.0.dev19)chore/quarantine-daily-1361no credential write observed for provider "anthropic" within 180stest/issue-1334-oc-credential-axisanthropic — no models collected from the providers panel, job exit 1anthropicandgoogleboth stalled, job exit 1Two independent branches, and 2 of 3 attempts on one of them. The provider that stalls moves between attempts (anthropic; then anthropic + google) and a plain re-run clears it — the load-dependent shape, not a consequence of any diff.
Preliminary read (descriptive — NOT a verdict)
#1355 was "clicks a Save button still aria-busy from the previous provider", and #1357 replaced a fixed 60 s clock with waiting for the credential write, measured at 103 s. The warning text above is #1357's own — it fires when that wait expires, now at 180 s. So the mechanism #1355 described is still reachable; what is unmeasured is whether 180 s is simply short on a loaded runner, or whether the write can fail to be observed at all (in which case a longer clock never fixes it, which is the lesson #1357's own header records).
Not claimed here: that the threshold is the answer. Raising a timeout because a timeout expired is the move #1357 argued against.
Why this matters beyond one red
Collect modelsis a hard gate on this lane. When it exits 1 the PR gets no E2E coverage of its own change, and the only signal is a red check whose cause is three steps away from the diff. #1369 merged on anambient-redjustification for exactly this (comment) — that reasoning was sound for one PR, but it is not a mechanism, and the next PR pays the same cost.Investigation directive
Instrument before tuning. (a) Establish what the 180 s wait actually observes on a stalling run — is the
POST|PATCH /api/v1/variables/never issued, issued and never answered, or answered after the deadline? The distinction decides whether any clock helps. (b) Only then decide between a longer deadline, a per-provider retry, and making the sweep resilient to one provider stalling (the other two collected fine in every occurrence above). (c) Consider whether aCollect modelsfailure should abort the PR lane at all:daily-stable.ymldeliberately does not let it kill the shard (#980), and the asymmetry is undocumented.Deliverables (Done when)
Collect modelsis either aligned or written down as intentional.npm run test:scripts/npm run test:unitswhere the logic is ours.