Skip to content

fix(driver): the pin would have walked every fire into a probe-hang — gate on onboarding (#558) - #667

Merged
sunholo-voight-kampff merged 1 commit into
devfrom
fix/driver-pin-onboarding-guard
Aug 12, 2026
Merged

fix(driver): the pin would have walked every fire into a probe-hang — gate on onboarding (#558)#667
sunholo-voight-kampff merged 1 commit into
devfrom
fix/driver-pin-onboarding-guard

Conversation

@sunholo-voight-kampff

Copy link
Copy Markdown
Collaborator

Follow-up to #666. Two defects, one of which would have taken the V1 loop down on the first fire after the reconcile. Found by reading 76ee4056c, which landed on dev while #666 was in review.

Defect 1 — the pin walks into a dead checkout

A pin worktree is by construction a path Claude Code has never seen, and an un-onboarded checkout makes headless claude -p block on a trust dialog it cannot display. Every model probe then hangs to its timeout (120s × 2 × 3 models ≈ 12 min) and the driver refuses with NO usable model in prefs — which reads as a quota outage, not as the harness fault it is. That had just cost the motoko mission its entire first unattended fire (charter V22).

Measured before writing any code:

onboarded=true    /Users/voightkampff/dev/sunholo-data/ailang
onboarded=ABSENT  /Users/voightkampff/.ailang-driver-pin/v1     <- the pin target

So #666, the moment the shared clone received it, would have converted a working-but-stale loop into a dead one. Stale-but-working strictly beats fresh-and-dead, so the pin now refuses when projects[<wt>].hasCompletedProjectOnboarding is absent — reported on both human channels like every other pin failure, with the exact one-line fix in the message. Undeterminable (no jq) counts as un-onboarded on purpose: pinning blind risks 12 minutes of hung probes; refusing costs staleness that is already reported.

It gates on hasCompletedProjectOnboarding, not the hasTrustDialogAccepted the error text names — 76ee4056c measured ailang-world at trust=false/onboarded=true and working, the control proving the named flag is not the gate. Test 8 pins both arms so a future edit cannot quietly switch to the wrong flag and stay green.

Defect 2 — a fix that disables itself after one use

Worktree existence was decided by string-matching git worktree list, but git records the resolved realpath, so on any path reached through a symlink (/var/private/var, the macOS everyday case) the match silently fails. Fire 1 pins; fire 2 hits worktree add on an existing directory and refuses — from then on, permanently. Loud, but useless. Now decided by asking the worktree itself.

Caught by the new repeat-fire test, not by review: the original suite only ever fired once.

Consequence for deployment

Reconciling the shared clone is now safe with no further action — the pin declines and says why. Pinning switches on when a human runs, once per checkout:

cd ~/.ailang-driver-pin/<mission> && claude

Testing

48 assertions (31 + 17), make test-launchd-drivers, bash 3.2.57, CI job on macOS. New coverage: un-onboarded → refuse + fire still runs + never reports pinned; trust-flag-alone does not satisfy while onboarding-flag does; no-jq fails safe; second consecutive fire pins.

The happy path runs against a synthetic ~/.claude.json in a fake HOME rather than with the check disabled — so a gate regressed to always-refuse fails tests 1–6, and one regressed to always-allow fails test 7.

🤖 Generated with Claude Code

… gate on onboarding (#558)

TWO DEFECTS IN #666, one of which would have taken the V1 loop down on the first fire after
the reconcile. Found by reading 76ee405, which landed on dev while #666 was in review.

DEFECT 1 — the pin walks into a dead checkout. A pin worktree is BY CONSTRUCTION a path
Claude Code has never seen, and an un-onboarded checkout makes headless `claude -p` block on
a trust dialog it cannot display. Every model probe then hangs to its timeout (120s x2 x3
models ~= 12 min) and the driver refuses with "NO usable model in prefs" — which reads as a
quota outage, not as the harness fault it is. That had just cost the motoko mission its whole
first unattended fire (charter V22). Measured here before writing any code:

    onboarded=true    /Users/voightkampff/dev/sunholo-data/ailang
    onboarded=ABSENT  /Users/voightkampff/.ailang-driver-pin/v1     <- the pin target

So #666, the moment the shared clone received it, would have converted a working-but-stale
loop into a dead one. Stale-but-working strictly beats fresh-and-dead, so the pin now reads
projects[<wt>].hasCompletedProjectOnboarding from ~/.claude.json and REFUSES when it is absent
— reported on both human channels like every other pin failure, with the exact one-line human
fix in the message. Undeterminable (no jq) is treated as un-onboarded on purpose: pinning
blind risks 12 minutes of hung probes, refusing costs staleness that is already reported.
jq is /usr/bin/jq, inside launchd's default PATH.

It gates on hasCompletedProjectOnboarding, NOT the hasTrustDialogAccepted the error text
names: 76ee405 measured ailang-world at trust=false/onboarded=true and WORKING, which is the
control proving the flag the message points at is not the gate. Test 8 pins both arms so a
future edit cannot quietly switch to the wrong flag and stay green.

DEFECT 2 — a fix that disables itself after one use. Worktree existence was decided by
string-matching `git worktree list`, but git records the RESOLVED realpath, so on any path
reached through a symlink (/var -> /private/var, the macOS everyday case) the match silently
fails, fire 2 takes the `add` branch against an existing directory, and the pin refuses from
then on. Loud, but permanently useless. Now decided by asking the worktree itself. Caught by
the new repeat-fire test, not by review — the first suite only ever fired once.

CONSEQUENCE FOR DEPLOYMENT: reconciling the shared clone is now safe with no further action —
the pin declines and says why. Pinning switches ON when a human runs, once per checkout:
    cd ~/.ailang-driver-pin/<mission> && claude

TESTED: 48 assertions (31 + 17), `make test-launchd-drivers`, bash 3.2.57, CI job on macOS.
New: un-onboarded => refuse + fire still runs + never reports pinned; trust-flag-alone does NOT
satisfy while onboarding-flag does; no-jq fails safe; second consecutive fire pins. The happy
path runs against a synthetic ~/.claude.json in a fake HOME rather than with the check disabled,
so a gate regressed to always-refuse fails tests 1-6 and one regressed to always-allow fails 7.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sunholo-voight-kampff
sunholo-voight-kampff merged commit 019568c into dev Aug 12, 2026
20 checks passed
@sunholo-voight-kampff
sunholo-voight-kampff deleted the fix/driver-pin-onboarding-guard branch August 12, 2026 09:19
sunholo-voight-kampff added a commit that referenced this pull request Aug 12, 2026
…ng is the discriminator" was wrong

The counterfactual arrived and refuted it. Mark set hasTrustDialogAccepted=true
and left hasCompletedProjectOnboarding ABSENT; the 10:32 fire then probed
`controller=claude-opus-5 via probe ok` and iteration 1 ran to rc=0.

  ailang         trust=T  onboarded=T       works
  ailang-world   trust=F  onboarded=T       works
  ailang-motoko  trust=F  onboarded=absent  FAILED
  ailang-motoko  trust=T  onboarded=absent  WORKS   <- the counterfactual

So EITHER flag suffices and motoko simply had neither. The error message's own
advice ("accept the trust dialog, or set hasTrustDialogAccepted") was correct,
and my "NOT the flag the error names" claim was not.

The reasoning error is the part worth keeping: I had three config snapshots and
picked the variable that CORRELATED, with no way to test the counterfactual.
ailang-world licensed "trust=false can work"; it never licensed "trust=true
alone cannot". With N=3 and no counterfactual, a correlation is not a mechanism
— the guide now carries that as a caution beside the fix.

Onboarding IS separately load-bearing for the #558 driver pin: PR #667 (landed
by this mission's iteration 1) gates the pin root on it, after finding the pin
would otherwise walk every fire into this same probe-hang.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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