Skip to content

feat(implement): default model route flips clodex → claude (C6) - #35

Open
SSFSKIM wants to merge 3 commits into
mainfrom
worktree-33-c6-implement-worker-default-rout
Open

feat(implement): default model route flips clodex → claude (C6)#35
SSFSKIM wants to merge 3 commits into
mainfrom
worktree-33-c6-implement-worker-default-rout

Conversation

@SSFSKIM

@SSFSKIM SSFSKIM commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Closes #33

Roadmap child C6 of docs/doperpowers/specs/2026-07-26-claude-review-stack-roadmap.md.

implement-dispatch.sh resolved a label-less ticket to the clodex gateway route
(GPT models through the local cliproxy). Opus 5-class models now beat that route
for implementation work, so the final fallback becomes claude. The gateway
route is left entirely intact — retiring clodex is a separate, later unit.

What moved

  • skills/implementing-tickets/scripts/implement-dispatch.sh — the fallback
    literal, plus the WORKER_ENGINE header docs.
  • skills/issue-tracker/SKILL.md — dispatch-ritual step 2 (the default) and
    step 3 (the claude route now reads first, as the default, with the codex
    gateway prefix second).
  • skills/issue-tracker/references/sweep-setup.md — the knobs table now names
    both lane defaults, because WORKER_ENGINE is shared with the review/land
    dispatchers, which deliberately keep codex until C5: review-loop worker default route clodex → claude + plumbing removal #32 (C5).

X4 (route label semantics) is preserved. engine:codex on a ticket still
routes to the gateway; engine:claude is redundant now but still valid and
still wins over the env; WORKER_ENGINE=codex still overrides the default.
Each of those four is a separate test case below.

Resume consistency needed no code. daemon-spawn.sh writes the
settings/effort meta keys only when DAEMON_CLAUDE_SETTINGS /
DAEMON_CLAUDE_EFFORT are non-empty, and daemon-resume.sh re-adds
--settings/--effort only when those meta keys are non-empty. A claude-route
worker therefore persists nothing and resumes plain — the invariant holds by
construction. Verified by reading both scripts (daemon-spawn.sh:46-60,
daemon-resume.sh:37-70), and asserted spawn-side by the settings=;effort=
cases.

Out of scope by the ticket's own constraint: review-dispatch.sh and
land-dispatch.sh keep their codex default (roadmap C5 / #32), and no
gateway plumbing was removed.

Validation Evidence

Claim: the change is genuinely test-driven — the new assertions fail against
the old default.
With only the one-line fallback reverted to codex and every
other file at its new state, bash tests/implementing-tickets/test-implement-dispatch.sh
reported exactly the three new/changed assertions as failures:

  [FAIL] default engine claude passes no gateway env
  [FAIL] claude route pins no model — it inherits unless IMPLEMENT_MODEL says otherwise
  [FAIL] sweep sends only the engine:codex ticket through the gateway — every other worker rides the claude default
3 test(s) FAILED

The revert was then undone (grep -n "WORKER_ENGINE:-" … confirms
${WORKER_ENGINE:-claude} on line 179; git status shows no stray .bak).

Claim: a label-less implement dispatch spawns a plain-Claude worker.
bash tests/implementing-tickets/test-implement-dispatch.shall tests passed,
exit 0, 41 passing assertions. The success criteria are covered by these cases:

Case Assertion
label-less ticket #1 default engine claude passes no gateway env (settings=;effort=)
label-less ticket #1 claude route pins no model — it inherits unless IMPLEMENT_MODEL says otherwise
engine:codex ticket #7 (new seed row) engine:codex label opts back into the gateway route
engine:codex ticket #7 label-selected codex route pins the gateway model alias (model=fable)
engine:claude ticket #5 engine:claude label … still suppresses the gateway env
WORKER_ENGINE=codex + label-less #1 WORKER_ENGINE=codex overrides the claude default on a label-less ticket
WORKER_ENGINE=codex + engine:claude #5 engine:claude label still wins over WORKER_ENGINE=codex

Claim: the sweep path picks up the new default too (the ticket asked for this
path to be checked explicitly). --sweep mode shares dispatch_one, and the new
sweep assertion pins it behaviorally: across a full sweep of the seeded board,
the gateway settings string appears exactly once in the spawn log — the
engine:codex ticket — while every other worker rides the claude default.

Claim: nothing else regressed. Each run separately, all exit 0:

  • bash tests/implementing-tickets/test-protocol-content.shall tests passed
  • bash tests/issue-tracker/test-board-scripts.shall tests passed
  • bash tests/issue-tracker/test-board-sweep.shall tests passed
  • bash tests/reviewing-prs/test-review-dispatch.shall tests passed (the
    review lane's codex default is untouched and still asserted)
  • bash tests/reviewing-prs/test-land-dispatch.shall tests passed
  • bash tests/skill-links/test-cross-doc-refs.shall cross-doc references resolve

Claim: the changed shell is lint-clean. bash scripts/lint-shell.sh → exit 0,
and shellcheck -S warning run directly on both changed shell files → exit 0,
no output. (See Confusions — the repo lint script alone does not cover them.)

Confusions

  • The parent roadmap spec is not on origin/main. The ticket cites
    docs/doperpowers/specs/2026-07-26-claude-review-stack-roadmap.md, which
    exists only on the local main (commits 7a93b3c, 5ecdd5b, 1f42bc5),
    unpushed from a parallel session. This branch is cut from origin/main
    (8a0cefe), so the file is absent from my worktree; I read the C6 section via
    git show main:… to confirm the acceptance criteria. The unpushed commits
    touch only that one doc, so there is no file overlap with this branch and no
    rebase was warranted — but a reviewer following the ticket's spec link on
    origin/main will not find the file until that session pushes.
  • scripts/lint-shell.sh reports "Linting 2 shell files" — it is a curated
    baseline, not a full sweep, and neither file I changed is in it. Its passing
    is therefore not evidence about this diff, which is why I ran shellcheck
    directly on both changed files as well.

Follow-ups

FOLLOW-UPS: none.

The one residual I considered — WORKER_ENGINE is a single env var now carrying
different defaults per lane (claude for implement/spike, codex for review/land) —
is transient and already owned: roadmap C5 (#32) flips the review and land
dispatchers to the same default, at which point the asymmetry disappears. I
documented it in the knobs table rather than registering a duplicate ticket.

implement-dispatch.sh resolved a label-less ticket to the clodex gateway
route (GPT models via the local cliproxy). Opus 5-class models now beat
that route for implementation work, so the fallback becomes `claude`.

The gateway route itself is untouched — `engine:codex` on a ticket, or
WORKER_ENGINE=codex in the environment, still spawns a gateway-routed
worker with the same settings/effort plumbing. Only the default moved.

Resume consistency needs no code: daemon-spawn writes the settings/effort
meta keys only when the env vars are non-empty, and daemon-resume re-adds
--settings/--effort only when those keys are non-empty, so a claude-route
worker persists nothing and resumes plain.

Prose that named the old default moves with it (issue-tracker dispatch
ritual steps 2-3, sweep-setup knobs table). The knobs table now states
both lane defaults, since WORKER_ENGINE is shared with the review/land
dispatchers, which keep codex.

Closes #33

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 12fad0ff0f

ℹ️ 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".


engine="${T_ENGINE_LABEL:-}"
[ -n "$engine" ] || engine="${WORKER_ENGINE:-codex}"
[ -n "$engine" ] || engine="${WORKER_ENGINE:-claude}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop seeding the old engine override

On the documented Linux worker-host path, this fallback is never reached: infra/worker-host/README.md tells operators to copy env.example into ~/.env, and that file still exports WORKER_ENGINE=codex. For new or rebuilt worker hosts using the supported setup, label-less implement/spike tickets will therefore continue to route through the gateway instead of the new plain-Claude default, while review/land also remain forced to codex. Please update or remove the seeded WORKER_ENGINE value (or split per-lane knobs) so the intended default flip actually applies in that environment.

Useful? React with 👍 / 👎.

@SSFSKIM SSFSKIM added the engine:claude worker model route: plain Claude (vs default clodex gateway) label Jul 26, 2026
The label-less `claude` default entered the plain spawn branch with no env
prefix, so a dispatcher running inside a gateway-routed daemon leaked its own
DAEMON_CLAUDE_SETTINGS/DAEMON_CLAUDE_EFFORT into daemon-spawn.sh. That applied
--settings/--effort AND persisted both into the registry meta, so
daemon-resume.sh restored them on every later fork: the worker rode the gateway
forever while the dispatch log said engine=claude.

Clear both on the plain spawn, the same command-prefix shape the codex branch
already uses. The test suite now exports an ambient gateway route for its whole
run, which turns the three existing "no gateway env" assertions into regression
tests (they fail on the pre-fix dispatcher).
The shipped worker-host env pinned WORKER_ENGINE=codex globally. That file
is sourced under `set -a` from ~/.bashrc and copied verbatim into the
Actions runner env (README §2.6), so on the documented host every sweep
tick and event dispatch reached implement-dispatch.sh with WORKER_ENGINE
already exported — the new claude default for a label-less implement/spike
ticket never took effect in production.

Removing the assignment is safe for the other lanes: review-dispatch.sh:265
and land-dispatch.sh:154 each carry their own ${WORKER_ENGINE:-codex}
fallback, so they stay on the gateway with the var unset. The comment now
states the per-lane defaults instead of the retired global one.
@SSFSKIM

SSFSKIM commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

Review trail — PR #35 (ticket #33)

Verdict: approve, HUMAN tier. confident-ready granted. Two real defects were
found and fixed on this branch; three findings were routed off to new tickets or
tech-debt. The default flip itself was correct as submitted.

Reviewed at head 12fad0ff, base origin/main @ 8a0cefe. Final head
d0338e0. Post-fix diff: 5 files, 93 changed lines. Primary and only linked
ticket: #33 (no secondaries).

Engine

Engine: native codex exec review --base origin/main, run as a pure correctness
review (no criteria, no ticket or spec input). Model gpt-5.6-sol, effort
xhigh. 3 rounds (the cap), all exit 0:

Round Head reviewed Result
r1 12fad0ff 1 finding (P2)
r2 d27fbcc 2 findings (P1, P1)
r3 d0338e0 3 findings (P2, P2, P3) — no new blocker

Exit condition met: no new blocker at r3. The two P2/P3 items r3 raised are new
non-blockers (routed below); its second P2 is a re-flag of an already-routed
finding.

Compliance audit

Verdict: no PROTOCOL BLOCKER. One SPEC FINDING (found by me, independently
raised by the engine at r2 — fixed in wave 2, see below).

  • Was the ticket substantively ready? Yes. Scope explicitly bounded ("default
    flip ONLY", non-goals named), 4 constraints, 3 behavior-phrased success
    criteria, and the no-bench decision recorded as a human ruling. The [gate] pass comment (2026-07-26T09:31:00Z) is not a bare stamp — it names the three
    forks and where each is answered.
  • Human-grade forks: none unauthorized. The ticket's one Open Question (should
    $WORKER_ENGINE's env default flip too) is worker-grade — an internal
    dev-tooling knob, no user-visible behavior, wording, scope, or destructive
    policy — and is in any case settled in the issue body itself, which records the
    recommendation the implementation followed exactly.
  • Mid-flight discipline: no fork arose, no park. The Confusions section
    discloses two genuine frictions honestly.
  • Constraint verified independently: the resume-consistency claim holds by
    construction. daemon-spawn.sh:46-50 writes settings/effort into the
    registry meta only when non-empty, and daemon-resume.sh:39,68-70 restores the
    flags only when those keys are non-empty, so a claude-route worker persists
    nothing and resumes plain. The PR's cited line ranges check out.

AUDIT NOTES (trail only, never merge blockers):

  • AN-1 — the parent spec is not resolvable from the base ref. The ticket cites
    docs/doperpowers/specs/2026-07-26-claude-review-stack-roadmap.md; verified
    ABSENT from origin/main and from the PR head, present only on an unpushed
    local main (7 commits ahead). Under the review protocol's specification
    hierarchy that document is inadmissible, so this audit ran against the issue
    body alone — which was self-sufficient. The implementer disclosed this. Those 7
    commits have zero file overlap with this PR and the PR reports MERGEABLE/CLEAN.
  • AN-2 — post-gate body edit was mechanical only. The issue body was edited at
    09:57:31Z, 26 minutes after the gate. I reconstructed all three revisions from
    Issue.userContentEdits: the five spec sections are byte-identical across every
    revision, and the only delta is the <!-- board:meta --> block written by
    board-transition.sh. No material spec drift; auditing the current body is
    equivalent to auditing the at-gate body.
  • AN-3 — the PR's Confusions section mischaracterizes scripts/lint-shell.sh.
    It is described as "a curated baseline, not a full sweep, and neither file I
    changed is in it". Reading the script: with no arguments it lints the shell files
    that differ from HEAD in the working tree, plus staged and untracked ones
    (--all is the full tracked baseline). So in the implementer's dirty worktree,
    the "Linting 2 shell files" it reported were their two changed files — it did
    cover the diff. In my clean detached worktree the same script correctly reports
    "No shell files found". The evidence claim itself is unaffected (they also ran
    shellcheck directly, and I reproduced that clean), but the characterization
    would mislead a future reader into distrusting a script that works.

Closing-artifact cross-check (Validation Evidence)

Every command-backed claim was re-run. Results:

  • tests/implementing-tickets/test-implement-dispatch.sh — exit 0, "all tests
    passed", 41 assertions, exactly as claimed.

  • The TDD (RED) claim reproduces exactly. With only the fallback literal
    reverted to codex in a scratch checkout, the suite reported the same three
    assertion names the PR quotes, verbatim: default engine claude passes no gateway env, claude route pins no model …, sweep sends only the engine:codex ticket through the gateway …. The claim was not decorative.

  • test-protocol-content.sh, test-board-sweep.sh, test-land-dispatch.sh,
    test-cross-doc-refs.sh — all exit 0.

  • shellcheck -S warning on both changed shell files — exit 0, no output.

  • ⚠️ scripts/lint-shell.sh — exit 0 as claimed, but reports "No shell files
    found" from a clean worktree rather than "Linting 2 shell files". Explained by
    AN-3; not a defect.

  • ⚠️ test-board-scripts.sh and test-review-dispatch.sh failed for me, and
    neither failure is this PR's doing.
    Both were run down to root cause rather
    than reported as regressions:

    Net: the PR's "all tests passed" claims are accurate; the two suites are
    non-hermetic in ways only a reviewer running inside review-pr-35 would hit.

Findings and routing

# Source Rank Bin Disposition
r1-1 engine P2 WAVE 1 Claude route inherited an ambient DAEMON_CLAUDE_SETTINGS/EFFORT. FIXED, graded, pushed as d27fbcc.
a-1 audit + engine r2 P1 WAVE 2 Shipped infra/worker-host/env.example exported WORKER_ENGINE=codex, so the new default was dead on the documented host. FIXED, graded, pushed as d0338e0.
r2-2 / r3-2 engine P1 → P2 TOO BIG#38 Plain spawns inherit the gateway's transport env (ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN, model aliases) from a gateway session. Re-flagged at r3; already routed, nothing further.
reviewer TOO BIG#37 The same DAEMON_CLAUDE_* leak exists in review-dispatch.sh and land-dispatch.sh. Out of scope by #33's own fence (review/land belong to #32).
r3-1 engine P2 TOO BIG#39 Editing env.example does not fix already-provisioned hosts: ~/.env and ~/runner/.env are persistent layer-3 state that still carry the old key.
r3-3 engine P3 LOG#26 engine:claude called "redundant" is only true when no WORKER_ENGINE override is set.
reviewer LOG#26 The two non-hermetic test suites described above.

Routing departures, stated: r1-1 was ranked P2 by the engine (default LOG) but
I waved it — it falsified this PR's own headline success criterion on the exact
line the PR changed, and the fix was a two-variable env clear with an assertion the
existing suite already shaped. r2-2 was ranked P1 (default WAVE) but I routed it
TOO BIG — the correct fix is a design fork at the substrate spawn contract, since
the gateway's variable set lives in a user-local settings file the repo does not
own, so no per-dispatcher edit can be correct by construction.

Fix waves

Both waves: one item, dispatched to a general-purpose fixer, task tree quiesced
(three consecutive matching content fingerprints across HEAD, diffs, untracked
paths, board, and ledger), board snapshotted read-only, graded from the snapshot
only.

Wave 1 — W1-1 FIXED:d27fbcc — ACCEPTED. The plain spawn branch now clears
both variables with the same command-prefix shape the codex branch already uses.
Graded: commit exists, touches the cited file plus its test, sole claimant of the
range, no board file committed, no attribution lines. Independently verified —
suite green (41 assertions) both with a clean environment and with the gateway
variables exported into the outer shell; and with only the dispatcher hunk
reverted, the three "no gateway env" assertions fail, so the new regression
coverage genuinely catches the pre-fix code. The fixer's choice to export an
ambient gateway route for the whole suite is better than the extra case I
expected: it converts every existing "no gateway env" assertion into a leak
regression and simultaneously strengthens the gateway assertions into proof that
the prefix overrides an ambient value rather than coinciding with an empty one.

Wave 2 — W2-1 FIXED:d0338e0 — ACCEPTED. WORKER_ENGINE=codex removed from
the shipped env template, and the stale label > WORKER_ENGINE > codex comment
replaced with the actual per-lane defaults. Safe for the fenced-off lanes because
review-dispatch.sh:265 and land-dispatch.sh:154 each carry their own
${WORKER_ENGINE:-codex} fallback — verified. The fixer correctly declined to
invent a test for infra/ content and said so plainly; I verified the dispatcher
suite still passes at 41 assertions, that nothing under infra/ sets the variable
any more, and that the new comment's §2.6 cross-reference resolves (section 2,
step 6 — the Actions runner step that copies the env file).

Tier judgment

HUMAN tier. The self-merge tier was not satisfied — this is not
observation mode. It fails two independent clauses:

  • the PR base (main) is the repo default branch, and self-merge lands only
    on integration branches;
  • the repo reports no CI checks on this branch, which disqualifies self-merge
    with no exceptions.

Auto-merge is off in any case. The clauses it did meet: final verdict approve;
no unresolved PROTOCOL BLOCKER or SPEC FINDING; post-fix diff 5 files / 93 lines
(within ~150 lines and ≤5 files); zero risk-surface touches (no CI/workflows,
auth/security, migrations/schema, release/versioning, or manifest files — and this
repo ships no .doperpowers/risk-surfaces.md, so only the always-on categories
applied).

One thing to know before merging

#39 is an operational follow-up, not a code one: if any worker host was
provisioned from the previous template, this change does not take effect there
until WORKER_ENGINE=codex is removed from both ~/.env and ~/runner/.env and
the runner is restarted. I could not verify whether such a host exists — there is
no /data/worker/.env or ~/.env on the machine this review ran on. I did not
treat it as a merge blocker because no diff can change a remote host's persistent
state, but it is the difference between this PR being true in the repo and true in
production.


Reviewed by the autonomous review worker (review-pr-35). Engine: codex exec review, gpt-5.6-sol / xhigh, 3 rounds. 2 fix waves, both accepted and pushed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

confident-ready review loop: reviewed and confident, human merges engine:claude worker model route: plain Claude (vs default clodex gateway)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

C6: implement worker default route clodex → claude

1 participant