Skip to content

chore(mise): declare bun 1.4.0 and node 24 as text, and pin CI to the same values #85

Closed

Description

@ivndev001

What to build

Declare this repo's runtimes as text so a fleet box installs them instead of relying on what it
happens to have. Add mise.toml with [tools] bun = "1.4.0" and node = "24" (the values
developerz-ai/wurk pinned in wurk#533 and the fleet proved green on 2026-09-17), and pin CI to the
same values: .github/workflows/ci.yml currently uses oven-sh/setup-bun@v2 with
bun-version: latest and sets up no node at all — it passes on ubuntu-latest by accident of
what that image ships. Add a comment in mise.toml naming ci.yml as the other copy so the next
bump moves both. bin/setup already mise-guards (if command -v mise); do not change it.

Evidence this is the cause: fleet task tsk_f2d3b5466fda46da8c6c436193e2f707 for #80 failed on box
fleet-2 with ./bin/setup: line 73: node: command not found immediately after mise all tools are installed, while the same script passed on sebastian-3, which happened to have a node. 13 of the
fleet's 14 exit-127 gate events in 7 days are this repo. Boxes carry no per-language runtime by design
(developerz.ai scripts/attach-vm.sh: "a box is a system and a bun, and nothing per-language; a
repo's toolchain is text (mise.toml) the runner user installs itself").

Acceptance criteria

  • mise.toml declares bun = "1.4.0" and node = "24", with a comment naming ci.yml as the paired copy
  • ci.yml pins bun-version: "1.4.0" and adds actions/setup-node with node-version: "24" before the install step
  • gates green: bun run lint, bun run typecheck, bun test (CI)
  • no change to bin/setup, bin/check, package.json or src/
  • live test: post-merge, the next fleet coding run on this repo records a team_verify verdict (green or red) rather than gate_unrunnable / exit 127

AFK / HITL

AFK.

Live test

set -euo pipefail
command -v gh >/dev/null || { echo "LIVE_TEST_INCONCLUSIVE: gh is missing on this runner"; exit 1; }
# The merged file must carry both pins. Contents-API read, with a same-scope control (CLAUDE.md pre-exists the merge).
out="$(gh api repos/developerz-ai/ui-debugger-mcp/contents/mise.toml -H 'Accept: application/vnd.github.raw' 2>&1)" || {
  ctl="$(gh api repos/developerz-ai/ui-debugger-mcp/contents/CLAUDE.md -H 'Accept: application/vnd.github.raw' 2>&1)" || {
    printf '%s\n' "$ctl" | tail -c 300; echo "LIVE_TEST_INCONCLUSIVE: this runner cannot read the repo's CONTENTS (control read failed)"; exit 1; }
  printf '%s\n' "$out" | tail -c 300; echo "FAIL: mise.toml is absent on the default branch after merge"; exit 1; }
printf '%s' "$out" | grep -E '^bun *= *"1\.4\.0"' >/dev/null || { echo "FAIL: mise.toml does not pin bun = \"1.4.0\""; exit 1; }
printf '%s' "$out" | grep -E '^node *= *"24"' >/dev/null   || { echo "FAIL: mise.toml does not pin node = \"24\""; exit 1; }
echo "PASS: mise.toml pins bun 1.4.0 and node 24 on the default branch"

Affected paths

mise.toml, .github/workflows/ci.yml

Blocked by

None, can start immediately.

Parent

developerz-ai/developerz.ai#4277

Verified against

83903bc

Activity

  1. ivndev001 commented on Sep 17, 2026

    @ivndev001
    ContributorAuthor

    spec-gate verdict: READY

    issue: 85
    repo: developerz-ai/ui-debugger-mcp
    bodyRevision: live
    verdict: READY
    checklist:
      - id: C1
        name: No-op against the stated problem
        status: pass
        evidence: |
          bin/setup:36-42 does `mise install` AND `eval "$(mise env -s bash)"`, so a committed
          mise.toml declaring node 24 puts node on PATH in THIS shell before bin/setup:73
          (`PLAYWRIGHT_REV=$(node -p 'require("./node_modules/playwright-core/browsers.json")...')`),
          the exact line the draft's exit-127 evidence names. Not inert. The CI half also changes
          behavior: ci.yml today has no `actions/setup-node` and `bun-version: latest`.
          Corroborated by developerz.ai scripts/attach-vm.sh:616-618 ("the runner user runs
          `mise install` in a checkout and gets whatever `mise.toml` / `.tool-versions` declares").
      - id: C2
        name: Standing ruling / authorization
        status: pass
        evidence: |
          Reverses nothing — it COMPLIES with the owner ruling at
          developerz-ai/developerz.ai scripts/attach-vm.sh:605 ("a box is a system and a bun
          (owner ruling, 2026-09-03)"), whose :150-151 text the draft quotes VERBATIM.
          ui-debugger-mcp CLAUDE.md / CONTRIBUTING.md record no toolchain or CI posture this
          would overturn. Change is not fail-open, so the ordinary leg applies and is met by
          epic developerz-ai/developerz.ai#4277 (see C7 limb (a)).
          Owner set resolved read-only: repo owner is an Organization and CLAUDE.md declares no
          handles, so the inferred leg applies — `orgs/developerz-ai/members?role=admin` returns
          awaismehr, ivndev001, MeepCastana, sebyx07. `ivndev001` authored both the draft and #4277.
      - id: C3
        name: Repo-wide lockout
        status: pass
        evidence: |
          Ruleset `main-protection` (id 20556697) carries deletion + non_fast_forward +
          pull_request with required_approving_review_count: 0 and NO `required_status_checks`
          rule — no context can be bricked, and the job name `bun (lint + typecheck + test)` is
          unchanged by the prescription. bun 1.4.0 exists (bun-v1.4.0, 2026-08-20), so the pin is
          not a phantom version. Any 1.4.0-specific defect (incl. `configVersion: 1` in bun.lock
          under dummy/web's `bun install --frozen-lockfile`) reds the INTRODUCING PR's own CI and
          never lands — `.maintainer.yml` `pr.auto_merge: true` requires green. Self-gating.
      - id: C4
        name: Live-test provenance
        status: pass
        evidence: |
          Non-empty fenced runnable block under `## Live test`. #298-compliant: the 404 control is a
          SECOND contents-API read (`repos/.../contents/CLAUDE.md`), not the bare repo route, and
          CLAUDE.md pre-exists the merge (present at HEAD 83903bc; the merge adds mise.toml and
          edits ci.yml only). Signalling is sound against live-test.ts's status ternary and
          `isUnverifiableOutput`: it echoes `LIVE_TEST_INCONCLUSIVE` on gh-absent and on
          control-read failure then exits non-zero, never prints the sentinel on the assertion
          path (the two grep FAIL branches say FAIL), and the sentinel is the last line of its
          branch. No exit code is relied on to carry inconclusive meaning.
      - id: C5
        name: Checkable acceptance
        status: pass
        evidence: |
          Mutation-sensitive and red today: criterion 1 ("mise.toml declares bun = 1.4.0 and
          node = 24") — no mise.toml exists at HEAD (verified by `ls` on the clone), and it names
          the behavior rather than asserting a suite is green. Criterion 2 is grep-checkable
          against ci.yml. Criterion 4 is a diff-scope fence checkable by `git diff --name-only`.
          No bare absolute counts; no unanchored line-number criteria; no unfalsifiable
          "improve/clean up" wording.
      - id: C6
        name: Evidence holds up
        status: pass
        evidence: |
          Verified: `## Verified against` 83903bc4c43e6654037e679666f9537061aa722f IS current
          main HEAD. bin/setup:73 is exactly the `node -p` invocation cited. ci.yml uses
          `oven-sh/setup-bun@v2` with `bun-version: latest` and sets up NO node — both quotes
          byte-exact. wurk#533 (a PR — admissible as C9(a) evidence) touched only mise.toml, and
          developerz-ai/wurk's mise.toml on main pins `bun = "1.4.0"` / `node = "24"` with the
          same paired-copy comment convention the draft prescribes. The attach-vm.sh quote is
          verbatim at :150-151. ONE misstatement, NOTE not FAIL (see notes 1): the runner is
          `blacksmith-2vcpu-ubuntu-2204`, not `ubuntu-latest`. File, quotes and mechanism all hold;
          only the runner's NAME is wrong, and it enters neither the prescription nor any criterion.
          "It passes" is true today — 6 consecutive green ci.yml runs since c5c488b (2026-09-08).
      - id: C7
        name: Fail-open / hard class
        status: pass
        evidence: |
          IN the hard class by FUNCTION: `.maintainer.yml` states CI-green is what stands behind
          `pr.auto_merge: true` ("ci.yml (lint + typecheck + test + build) runs on every PR"), so
          ci.yml decides whether a PR may merge here. NOT fail-open on any of the five triggers —
          it ADDS an `actions/setup-node` step and replaces a floating `latest` with a fixed
          version; every existing check (lint, typecheck, test, build, chromium launch gate) still
          runs and nothing stops being verified. No `hitl` label, so no waiver is applied or needed.
          Limb (a): epic developerz-ai/developerz.ai#4277 — owner-authored (ivndev001), an ISSUE
          (`pull_request` key absent), outside the draft's body, created 2026-09-17T05:55:04Z vs the
          draft's 2026-09-17T18:05:06Z, and on-point to the point of naming this slice by number in
          its children table: "developerz-ai/ui-debugger-mcp#85 | declare bun 1.4.0 + node 24 as
          text; pin CI | afk | —". Ordinary leg cleanly met.
          Limb (b): criterion 5 goes red in the unsafe direction — if the toolchain declaration
          fails to take on a fleet box, the next coding run records `gate_unrunnable` / exit 127
          rather than a `team_verify` verdict, which is exactly the defect being fixed.
          `## Blocked by` is "None, can start immediately" — nothing to resolve; #4277's table
          independently records no blocker for this child.
      - id: C8
        name: Blast radius and reversibility
        status: pass
        evidence: |
          One concern (declare the runtimes as text; pin CI to the same values), two files
          (mise.toml, .github/workflows/ci.yml) — one PR's worth. No mutation a revert cannot
          reach: no repo settings, no rulesets, no other issues' labels or bodies, no deletions.
          No secret is rotated, minted, or read — HITL is not triggered; `afk` is correct.
          Reversibility is bounded further than usual here: `revert.armOnLiveTestFail` is a
          fail-closed, governsRepo-gated opt-in (run.ts:392,
          `loaded.config.revert?.armOnLiveTestFail === true`) and this repo commits no
          agent-pipeline config, so a live-test FAIL escalates to a human rather than
          auto-reverting.
      - id: C9
        name: Work is real
        status: pass
        evidence: |
          (a) Multiple citations resolve and were opened: bin/setup:73, ci.yml, wurk#533 +
          wurk's mise.toml, attach-vm.sh:150-151. (b) `## Parent`
          developerz-ai/developerz.ai#4277 resolves via `gh api repos/developerz-ai/developerz.ai/issues/4277`,
          carries no `pull_request` key (it is an ISSUE), is open, and names this issue as a child.
    blockers: []
    notes: |
      Audited the LIVE body (no caller-pinned revision; `gh issue view 85 --json comments` returns
      zero comments, so nothing rides outside the body). Labels: area/infra, afk, size/30m — no
      `hitl`, so no C7 waiver and no label-hygiene contradiction.
    
      1. FACTUAL ERROR IN THE BODY (NOTE, not a blocker). The draft says ci.yml "passes on
         `ubuntu-latest` by accident of what that image ships". The workflow declares
         `runs-on: blacksmith-2vcpu-ubuntu-2204`. This is stale-in-NAME only, not stale in
         substance: the file exists, both quoted tokens match byte-for-byte, and the mechanism
         claim — no `actions/setup-node`, so the `install chromium` step's `node
         node_modules/playwright-core/cli.js` resolves to whatever the image ships — is true and
         verified. It does not enter the prescription or any of the five acceptance criteria.
         Worth correcting so an executor does not grep for a label that is not there.
    
      2. THE PIN IS A DOWNGRADE FROM THE EXERCISED VERSION. Two OPEN `area/ci` issues in this
         repo concern the bun version the draft pins, and the draft mentions neither: #66
         ("bun-version 'latest' floated 1.3.14 -> 1.4.0; auth wrong-credentials e2e now times out
         on every PR") and #70 ("main is red and every PR is a coin flip"; its fix direction is
         "pin it to a KNOWN-GOOD version", acceptance "ten consecutive green runs on main"). Both
         were symptom-fixed by c5c488b / PR #75 (2026-09-08, "stop awaiting the bad-credentials
         login through expect().rejects" — a test-side `.rejects` starvation bug, not a bun bug)
         and neither was closed. Since that commit ci.yml has been green 6 consecutive runs — but
         every one of those resolved `latest` = bun 1.4.2 (1.4.1 2026-09-04, 1.4.2 2026-09-05).
         **bun 1.4.0 has never had a green run on this repo.** Not a blocker: the introducing PR
         runs its own CI on 1.4.0 and `pr.auto_merge: true` requires green, so a 1.4.0-specific
         defect reds that PR and never lands — and #70's own table shows the slowdown was
         family-wide across 1.4.x, so 1.4.0-vs-1.4.2 is not the axis that mattered. The caller may
         still prefer 1.4.2 (matching what the repo has actually proven) over 1.4.0 (matching
         wurk). Either choice is defensible; the draft does not say which tradeoff it took.
         Same bucket: root `bun.lock` and `dummy/web/bun.lock` carry `configVersion: 1`, and
         whether bun 1.4.0 parses that field is not establishable read-only — but dummy/web's CI
         step is `bun install --frozen-lockfile`, so this too reds the introducing PR if it bites.
    
      3. REPO-CONFIG. This repo commits NO `.dz/pipeline/agent-pipeline.json` and no legacy root
         `.agent-pipeline.json`, so `merge.reviewGate`, `policy.module`, `policy.defaultTier` and
         `revert.armOnLiveTestFail` are all undefined. Consequences, all benign here: the draft
         carries a fence, so the fenceless-GREEN carve-out never arises and `runLiveTest` runs the
         block post-merge; a FAIL escalates rather than arming `revert.ts` (opt-in absent). The
         repo's actual merge policy lives in `.maintainer.yml` (`pr.auto_merge: true`,
         `wait_for_other_bots: [coderabbit, copilot]`, `review.approval: false`), and the
         `main-protection` ruleset carries no `required_status_checks`. No draft edit reaches any
         of this, which is why it is here and not in `blockers`.
    
      4. SETTLE-RACE EXPOSURE (bounded). The fence's assertion reads `contents/mise.toml` with no
         ref immediately post-merge, and nothing in `run.ts`/`merge.ts` invokes
         `probe-contents-settle.ts` (its own header: "Operator tool, not a pipeline verb"). A
         contents view that has not caught up therefore reads 404, passes the CLAUDE.md control,
         and scores FAIL. Per note 3 that escalates rather than reverting, so the exposure is a
         human park on a healthy merge, not a bad revert. The fence's control-read design is
         correct; this residue is the pipeline's, not the draft's.
    
      5. CRITERION 5 IS NOT CLOSEABLE BY THIS GATE. "the next fleet coding run records a
         `team_verify` verdict rather than `gate_unrunnable` / exit 127" is falsifiable and
         precisely worded, but needs developerz.ai platform access (the `task_run_events` stream;
         #4277 carries the SQL). It passes C5 — that is an access requirement, not author-private
         context — but whoever accepts this issue must be someone with fleet access, not the
         spec-gate caller. Same bucket: the cited fleet task id
         `tsk_f2d3b5466fda46da8c6c436193e2f707` and "13 of the fleet's 14 exit-127 gate events in
         7 days" are unverifiable read-only from here (no `dz` on PATH). Scored NOT load-bearing:
         the causal mechanism is independently established from bin/setup:73 + ci.yml + the
         absence of mise.toml + attach-vm.sh's ruling, and #4277's body — which I could read —
         records the same failure class on this repo verbatim ("tsc: command not found / exit 127
         ... graded `gate_unrunnable`").
    
      6. EXECUTOR TRAP WORTH FLAGGING. `mise.toml` unprefixed is the correct filename (verified
         against wurk's own file and attach-vm.sh:609, "the setup lane writes `bin/setup`,
         `mise.toml` and `CLAUDE.md`"). But `bin/setup:11`'s comment still says
         "`.mise.toml` / `.tool-versions`" — and criterion 4 forbids touching `bin/setup`. An
         executor that "helpfully" refreshes that stale comment trips the draft's own acceptance
         criterion. Leave it; it is a separate docs slice.

    run: a8c76a12b13c4f55d
    provider: anthropic
    model: claude-opus-5

  2. developerz-ai commented on Sep 17, 2026

    @developerz-ai
    Contributor

    chore(mise): declare bun 1.4.0 and node 24 as text, and pin CI to the same values

    Work landed on #86 — watching it toward merge.

    1m · 16 tool calls · 5 messages

    Sessions: coding run


    🤖 developerz.ai maintainer bot — sebby-ovh-3 (developerz-ai[bot]) · model MiniMax-M3 on router.ai.developerz.ai

  3. ivndev001 commented on Sep 17, 2026

    @ivndev001
    ContributorAuthor

    Two corrections from the spec-gate's notes, recorded here rather than edited into the body so the
    ready label keeps pointing at the exact text the gate read.

    1. The runner name in the body is wrong; the mechanism around it is not. The body says ci.yml
    "passes on ubuntu-latest by accident of what that image ships". The workflow actually declares
    runs-on: blacksmith-2vcpu-ubuntu-2204. Do not grep for ubuntu-latest — it is not there. What the
    sentence was pointing at is true and unchanged: ci.yml sets up no node, so the install chromium
    step's node node_modules/playwright-core/cli.js resolves to whatever the runner image happens to
    ship, which is exactly the accident this issue removes.

    2. bun 1.4.0 has never had a green run on THIS repo, and that is a deliberate choice, not an
    oversight.
    The six consecutive greens since c5c488b all resolved bun-version: latest to 1.4.2
    (1.4.1 landed 2026-09-04, 1.4.2 on 2026-09-05). The pin is 1.4.0 because the selection criterion was
    proven on the fleet boxes — 1.4.0 is what developerz-ai/wurk#533 proved there, while this repo's
    1.4.2 greens are all blacksmith-hosted CI, which is a different machine class and the one that has
    never been the problem. The choice is self-gating either way: a 1.4.0-specific defect reds the
    introducing PR's own CI, and pr.auto_merge: true requires green, so it cannot land silently.

    Related open issues worth reading before touching the pin, neither of which this issue closes: #66
    (bun-version: latest floated 1.3.14 -> 1.4.0; auth e2e timeouts) and #70 (main red, "pin it to a
    KNOWN-GOOD version"). Both were symptom-fixed by PR #75 — a test-side expect().rejects starvation
    bug, not a bun bug — and left open.

    3. Leave bin/setup alone. Its line 11 comment still says ".mise.toml / .tool-versions"
    while the correct filename here is unprefixed mise.toml. Acceptance criterion 4 forbids touching
    bin/setup, so refreshing that comment would trip this issue's own gate. It is a separate docs slice.

  4. developerz-ai commented on Sep 17, 2026

    @developerz-ai
    Contributor

    Thanks for the corrections — noted for the record:

    1. Runner name: blacksmith-2vcpu-ubuntu-2204, not ubuntu-latest. The mechanism (no node setup) is unchanged.
    2. bun 1.4.0: Deliberate pin proven on fleet boxes, not on this repo's CI. Self-gating via the introducing PR's CI + auto_merge: true.
    3. bin/setup: Left untouched per acceptance criterion 4.

    PR #86 is proceeding with these understandings.

    🤖 Posted by developerz.ai — the maintainer agent, not a human.

  5. added a commit that references this issue on Sep 17, 2026
    3912678
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions