Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ reusables implement, review, and sync status back to Bonsai.

## Status & versions

Latest tag **`v1.10.0`** (`b394c6d`) — **not** kit-only: it adds a **sixth reusable**,
`bonsai-status-sync.yml`, converted from a copied per-repo workflow on 2026-08-02. Its caller stub
landed in the repin commit for this tag, per step 2 of the release order below. The other five
reusables have been byte-identical since `v1.6.0`'s `0a3934f`.
Latest tag **`v1.11.0`** (`90f0d06`, 2026-08-02) — **not** kit-only. It carries three things: the
`bonsai-status-sync.yml` **caller stub** (the reusable itself landed one tag earlier at `v1.10.0` =
`b394c6d`, so the conversion spans the two), the `DRIVER_AGENTS_REF` bump, and the Shopify operator
tripwire. The other five reusables have been byte-identical since `v1.6.0`'s `0a3934f`. All six stubs
are pinned to `90f0d06`.

**Unreleased on `main` (2026-08-02) — `DRIVER_AGENTS_REF` → `4d63371`, lockstep in `claude.yml` +
**In `v1.11.0` — `DRIVER_AGENTS_REF` → `4d63371`, lockstep in `claude.yml` +
`shopify-tool-smoke.yml`.** The previous pin `0bbb125` predated the Admin API wrapper:
`graphql_guard.py` does not exist at that SHA, so every fleet runner executes `admin-graphql.sh`
with **no fail-closed allowlist** and the Driver Engineering scope grant is the only control on
Expand All @@ -39,16 +40,16 @@ driver-agents `docs/agent-instructions-shopify.md`, which is canonical — edit
the kit-side scope lead-in that precedes it), so the fleet gets the wrapper and its instruction block in
one wave.

**Decided 2026-08-02: this ships as `v1.11.0`, not folded into the v1.10.0 wave.** Folding it in would
put `claude.yml` content on 18 branches that exists in no tag, and `tools/fleet-pin-audit.sh` compares
only stub pin lines against the latest tag — never `templates/` content — so it would report the fleet
uniform and green over the gap. Sequence: merge the repin PR → tag `v1.11.0` at that merge commit →
repin all six stubs to `v1.11.0` → **one** wave. That is what `v1.7.0` and `v1.8.0` each did.
**Decided 2026-08-02: shipped as `v1.11.0` rather than folded into the v1.10.0 wave.** Folding it in
would have put `claude.yml` content on 18 branches that exists in no tag, and `tools/fleet-pin-audit.sh`
compares only stub pin lines against the latest tag — never `templates/` content — so it would have
reported the fleet uniform and green over the gap. Executed as: merge → tag `v1.11.0` at that merge
commit → repin all six stubs to `v1.11.0` → **one** wave. That is what `v1.7.0` and `v1.8.0` each did.

**Note on the pin sequence:** `v1.9.0` (`a54c91e`, the store-secret rename) never got its kit repin
commit — the kit's stubs sat at `v1.8.0`'s SHA through that release and jump straight to `v1.10.0`
here. Deployed fleet stubs were repinned to `v1.9.0` by the 2026-08-01 wave, so between then and this
tag the fleet was *ahead* of the kit templates. The v1.10.0 wave resolves both. Deployed fleet stubs are
tag the fleet was *ahead* of the kit templates. The v1.11.0 wave resolves both. Deployed fleet stubs are
repinned by **manual waves** — Dependabot does NOT bump these reusable-workflow pins in practice
(zero such PRs fleet-wide; debugging why is on the backlog). Org Actions secrets (`AGENTS_GH_PAT`,
`CLAUDE_CODE_OAUTH_TOKEN`, `BONSAI_BEARER_TOKEN`, `SHOPIFY_ALERT_WEBHOOK`) and cross-repo Actions
Expand Down Expand Up @@ -129,12 +130,12 @@ npm-install fallback for lockfile-less repos + `actions/checkout` v7) → `v1.5.
- **If the release ADDS a reusable**, its stub lands *in this step*, not in the PR that added the
reusable — the tag it must pin does not exist until step 1. That is why
`dependabot-keep-current`'s reusable and its stub landed in different commits, and how
`bonsai-status-sync.yml`'s stub landed at `v1.10.0`. `lint.yml` fails the build on any stub left
`bonsai-status-sync.yml`'s stub landed at `v1.11.0`. `lint.yml` fails the build on any stub left
carrying a placeholder pin, so this step cannot be silently skipped.
3. Only then re-copy `templates/github/` into consumer repos (`tools/fleet-pin-audit.sh --stale`
to confirm the fleet converged afterwards).
- **When a full workflow becomes a stub** (as `bonsai-status-sync.yml` did at `v1.10.0` — this
applies to the v1.10.0 wave specifically), the wave diff
- **When a full workflow becomes a stub** (as `bonsai-status-sync.yml` did — this applies to the
v1.11.0 wave specifically), the wave diff
contains a `templates/github/` path AND a `.github/workflows/` path with the SAME basename. The
wave script rewrites `templates/github/` → `.github/workflows/`, so assert the rewritten diff
touches no destination path twice before applying — otherwise the reusable can land in a client
Expand Down Expand Up @@ -176,13 +177,13 @@ produces a silent `startup_failure` — no check run, no notification).
carries the six caller stubs above plus `claude.yml` (the implementer, still a full per-repo workflow),
`shopify-tool-smoke.yml` (store repos only) and `pull_request_template.md`.

**`bonsai-status-sync.yml` finished converting at `v1.10.0`.** The reusable landed 2026-08-02 and its stub
**`bonsai-status-sync.yml` finished converting at `v1.11.0`.** The reusable landed 2026-08-02 and its stub
landed in this tag's repin commit, so the kit now installs a 66-line stub instead of the old 190-line copy —
see *Release + repin order* above and [`docs/reusable-conversion-scope.md`](docs/reusable-conversion-scope.md).
The two-step was deliberate and matches how `dependabot-keep-current` was added: a new reusable's stub cannot
be pinned until the tag containing that reusable exists, so the reusable lands first and the stub follows in
the repin commit. `lint.yml` fails the build on any stub still carrying a placeholder pin. **The fleet has not
been waved yet** — consumer repos still run the 190-line copy until the v1.10.0 wave.
been waved yet** — consumer repos still run the 190-line copy until the v1.11.0 wave.

**`claude.yml` stays a per-repo copy** — that half of the conversion is tabled pending the OIDC spike (whether
Claude App token minting survives inside a cross-repo reusable), so it remains the kit's main drift surface
Expand Down
6 changes: 3 additions & 3 deletions docs/identity-unification-scope.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ revise loop mid-cutover, then drop the old literal.
>
> Note `209825114` appears in **no workflow file** in this repo (`git grep 209825114 -- ':!docs/'` → zero
> hits; the only occurrences are in this doc), while all **11** occurrences of `261291955` — recounted
> 2026-08-02 after the v1.10.0 repin, and path-qualified because the same basename exists in both halves:
> 2026-08-02 after the v1.11.0 repin, and path-qualified because the same basename exists in both halves:
> `templates/github/claude.yml` (6), `templates/github/ticketed-review.yml` (2),
> `.github/workflows/bonsai-status-sync.yml` (2) and `README.md` (1) — are bound to `driver-digital-agents`.
> **`templates/github/bonsai-status-sync.yml` no longer contains the id at all**: it held 2 occurrences until
Expand All @@ -234,15 +234,15 @@ mode is selected, `checkContainsTrigger` returns false, and `run.ts:212` logs "N

Four things must move in one commit: the four `contains()` clauses
(`templates/github/claude.yml:91,95,99,101`), `.github/workflows/bonsai-status-sync.yml:139`'s grep (it moved
out of `templates/github/` at the v1.10.0 repin — the kit file is now a stub carrying neither), a
out of `templates/github/` at the v1.11.0 repin — the kit file is now a stub carrying neither), a
`trigger_phrase` input on the action, and **the out-of-repo cron orchestrator that writes `@claude` into issue
bodies**.

Note `trigger_phrase` **does not exist in this repo today** (`git grep` → zero hits) — the action runs on its
default phrase. So it must be *added* in the same commit, not edited. That is a small but real difference: the
first time it appears is the first time it can disagree with the workflow gate, which is the #148 signature.

> **Moved (2026-08-02, v1.10.0 repin).** `bonsai-status-sync.yml` finished converting. The grep and the
> **Moved (2026-08-02, v1.11.0 repin).** `bonsai-status-sync.yml` finished converting. The grep and the
> `261291955` gate cited above now live ONLY in `.github/workflows/bonsai-status-sync.yml` (`:139` and
> `:141`); `templates/github/bonsai-status-sync.yml` is a stub containing neither. Note the *fleet* still
> runs the old 190-line copy until the wave, so until then a consumer repo still carries its own gate at the
Expand Down
13 changes: 7 additions & 6 deletions docs/reusable-conversion-scope.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ The README has flagged this as "future work" since the repo split. This document
>
> **APPROVED and IN PROGRESS — `bonsai-status-sync.yml` (Phases 1–3).** The copy-per-repo cost is real
> and this half was never gated on anything. Phase 1 landed 2026-08-02 (`b394c6d`, tag `v1.10.0`): the
> reusable is `.github/workflows/bonsai-status-sync.yml`. The caller stub followed in the v1.10.0 repin
> reusable is `.github/workflows/bonsai-status-sync.yml` (tag `v1.10.0`). The caller stub followed in the
> v1.11.0 repin
> (PR #26, same day) — `templates/github/bonsai-status-sync.yml` is now a 67-line stub, not the 190-line
> copy. **The fleet has NOT been waved**: consumer repos still run the 190-line copy. See *Phase 1 landed*
> below, and read README release-order step 3 before waving — this is the wave its traps were written for.
Expand All @@ -35,7 +36,7 @@ The README has flagged this as "future work" since the repo split. This document
## Why

*As of the Phase 1 conversion (2026-08-02) this argument is half-resolved: `bonsai-status-sync.yml` now has a
central reusable, and the kit copy dropped from 190 lines to a 67-line stub at the v1.10.0 repin. **`claude.yml`'s 477
central reusable, and the kit copy dropped from 190 lines to a 67-line stub at the v1.11.0 repin. **`claude.yml`'s 477
lines remain copied** — still the single largest thing in the kit, and the drift surface that outlives this
conversion. The original framing follows.*

Expand Down Expand Up @@ -315,7 +316,7 @@ is ordered by dependency, not by gating — do not read Phase 0 sitting at the t

`bonsai-status-sync.yml` goes first because **it is the only one of the two with any pre-merge test path at
all**. It has no OIDC path and one secret (`BONSAI_BEARER_TOKEN`,
`.github/workflows/bonsai-status-sync.yml:175` — it moved out of the kit file at the v1.10.0 repin; the stub
`.github/workflows/bonsai-status-sync.yml:175` — it moved out of the kit file at the v1.11.0 repin; the stub
now just forwards it).

Be precise about how much of it is provable pre-merge, because it is **one leg of three**. Its `on:` block
Expand Down Expand Up @@ -346,7 +347,7 @@ so in the PR body. Merge on review of the diff alone; validate after merge.
| 1 | Convert `bonsai-status-sync.yml` + stub + docs + lint | 3h | **done 2026-08-02** |
| 2 | Pilot it (only the `pull_request` leg is testable pre-merge — see *Sequencing*) | 2h | **next** |
| 3 | Fleet wave for `bonsai-status-sync` — 18 repo@branch pairs across 11 repos | 3–4h | after Phase 2 |
| 5 | Tag + repin the kit stubs (README's mandatory 3-step release order) | 1h | **done 2026-08-02** — tag `v1.10.0` + PR #26 |
| 5 | Tag + repin the kit stubs (README's mandatory 3-step release order) | 1h | **done 2026-08-02** — tag `v1.11.0` + PRs #26/#27 |
| | **Approved subtotal** | **9–10h** | Phases 1 + 5 done → **pilot + wave left** |
| 0 | Spike: go/no-go on OIDC-in-reusable | 3–4h | **tabled** |
| 4 | Convert `claude.yml` — move the 477 lines **faithfully** | 7–9h | **tabled** |
Expand All @@ -364,7 +365,7 @@ the old copy except one added comment; actionlint + shellcheck clean; `BONSAI_BE
**Deferred out of Phase 1, landed at the repin: the caller stub.** A new reusable's stub cannot be pinned
until the tag containing that reusable exists, so it landed in step 2 of the release order, not here. This is
the house precedent — `dependabot-keep-current`'s reusable landed in `c362604` and its stub arrived later
already carrying a real SHA. **Landed 2026-08-02 in PR #26 (`3056f75`)**, pinned at `b394c6d` = `v1.10.0`;
already carrying a real SHA. **Landed 2026-08-02 in PR #26**, and repinned to `90f0d06` = `v1.11.0`;
the kit now installs the 67-line stub. The *fleet* still runs the 190-line copy until the wave.

**The stub as landed** (it replaced `templates/github/bonsai-status-sync.yml` wholesale; the pin below is the
Expand Down Expand Up @@ -429,7 +430,7 @@ jobs:
# NOTE: the required-status-check context for this job is `sync / sync` (caller job id /
# reusable job id), NOT the bare `sync` it was as a full workflow. Verified 2026-08-02 that
# no branch in the org pins either, so this rename breaks nothing — re-check before adding one.
uses: DriverDigital/workflows/.github/workflows/bonsai-status-sync.yml@b394c6d73c3060ba3d0b352f51ad589af9d548f5 # v1.10.0
uses: DriverDigital/workflows/.github/workflows/bonsai-status-sync.yml@90f0d066c140c356c44d5b3d83d795c0256a7b82 # v1.11.0
# Explicit, NOT `secrets: inherit`. Two reasons: (1) `inherit` passes whatever set exists, which
# defeats the reusable's `required: true` — a missing secret would reach the curl and surface as
# an opaque 401 on the first real flip instead of failing at startup; (2) least privilege — this
Expand Down
4 changes: 2 additions & 2 deletions templates/github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ trailing `# vX.Y.Z` comment on the `uses:` line is the only place the version is

| File | Rail |
|---|---|
| `bonsai-status-sync.yml` | Deterministic (no-agent) Bonsai status flips on issue/PR events; on a PR it resolves the **linked issue** (`closingIssuesReferences`) and reads the task URL from the **issue** body — never from the PR body. Converted from a 190-line per-repo copy at v1.10.0. |
| `bonsai-status-sync.yml` | Deterministic (no-agent) Bonsai status flips on issue/PR events; on a PR it resolves the **linked issue** (`closingIssuesReferences`) and reads the task URL from the **issue** body — never from the PR body. Converted from a 190-line per-repo copy at v1.11.0. |
| `pr-first-review.yml` | Human, no-ticket PR → `/code-review` comments + request a human reviewer. |
| `ticketed-review.yml` | `claude[bot]` **ticketed** PR → capped `/code-review` revise loop (max 3 passes) → Bonsai reviewer handoff. |
| `dependabot-validate.yml` | Credential-less install/build/test → uploads an inert artifact. Carries **no `secrets:` line** — deliberate, do not add one. |
Expand Down Expand Up @@ -109,7 +109,7 @@ Ready to Deploy → Delivered / Deployed / Completed. The workflows never set th
```
**Re-copying into a repo that already has the kit?** Preserve that repo's own Dependabot action
pins — re-copy the workflow bodies, but don't clobber pins Dependabot has since bumped there.
5. **Board strings are no longer edited here.** As of v1.10.0 `bonsai-status-sync.yml` is a caller
5. **Board strings are no longer edited here.** As of v1.11.0 `bonsai-status-sync.yml` is a caller
stub; the exact Bonsai status strings live only in the central reusable
(`DriverDigital/workflows/.github/workflows/bonsai-status-sync.yml`). A board rename is therefore
a kit release + fleet repin, not a local edit — changing the strings in one repo does nothing.
Expand Down
2 changes: 1 addition & 1 deletion templates/github/bonsai-status-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
# NOTE: the required-status-check context for this job is `sync / sync` (caller job id /
# reusable job id), NOT the bare `sync` it was as a full workflow. Verified 2026-08-02 that
# no branch in the org pins either, so this rename breaks nothing — re-check before adding one.
uses: DriverDigital/workflows/.github/workflows/bonsai-status-sync.yml@b394c6d73c3060ba3d0b352f51ad589af9d548f5 # v1.10.0
uses: DriverDigital/workflows/.github/workflows/bonsai-status-sync.yml@90f0d066c140c356c44d5b3d83d795c0256a7b82 # v1.11.0
# Explicit, NOT `secrets: inherit`. Two reasons: (1) `inherit` passes whatever set exists, which
# defeats the reusable's `required: true` — a missing secret would reach the curl and surface as
# an opaque 401 on the first real flip instead of failing at startup; (2) least privilege — this
Expand Down
2 changes: 1 addition & 1 deletion templates/github/dependabot-keep-current.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ permissions:

jobs:
keep-current:
uses: DriverDigital/workflows/.github/workflows/dependabot-keep-current.yml@b394c6d73c3060ba3d0b352f51ad589af9d548f5 # v1.10.0
uses: DriverDigital/workflows/.github/workflows/dependabot-keep-current.yml@90f0d066c140c356c44d5b3d83d795c0256a7b82 # v1.11.0
secrets:
AGENTS_GH_PAT: ${{ secrets.AGENTS_GH_PAT }}
2 changes: 1 addition & 1 deletion templates/github/dependabot-report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ concurrency:

jobs:
report:
uses: DriverDigital/workflows/.github/workflows/dependabot-report.yml@b394c6d73c3060ba3d0b352f51ad589af9d548f5 # v1.10.0
uses: DriverDigital/workflows/.github/workflows/dependabot-report.yml@90f0d066c140c356c44d5b3d83d795c0256a7b82 # v1.11.0
with:
triggering-run-id: ${{ github.event.workflow_run.id }}
head-repository: ${{ github.event.workflow_run.head_repository.full_name }}
Expand Down
2 changes: 1 addition & 1 deletion templates/github/dependabot-validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ concurrency:

jobs:
validate:
uses: DriverDigital/workflows/.github/workflows/dependabot-validate.yml@b394c6d73c3060ba3d0b352f51ad589af9d548f5 # v1.10.0
uses: DriverDigital/workflows/.github/workflows/dependabot-validate.yml@90f0d066c140c356c44d5b3d83d795c0256a7b82 # v1.11.0
# NO `secrets:` line — intentional and load-bearing. Do not add one.
2 changes: 1 addition & 1 deletion templates/github/pr-first-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ jobs:
# Only review real, published PRs — skip drafts (many repos open a draft PR first for build-test CI).
# `ready_for_review` (in the trigger list above) covers the draft→ready transition.
if: ${{ github.event.pull_request.draft == false }}
uses: DriverDigital/workflows/.github/workflows/pr-first-review.yml@b394c6d73c3060ba3d0b352f51ad589af9d548f5 # v1.10.0
uses: DriverDigital/workflows/.github/workflows/pr-first-review.yml@90f0d066c140c356c44d5b3d83d795c0256a7b82 # v1.11.0
secrets: inherit
2 changes: 1 addition & 1 deletion templates/github/ticketed-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
contains(github.event.comment.body, '<!-- request-ticketed-review -->') &&
github.event.comment.user.login == 'driver-digital-agents' &&
github.event.comment.user.id == 261291955)
uses: DriverDigital/workflows/.github/workflows/ticketed-review.yml@b394c6d73c3060ba3d0b352f51ad589af9d548f5 # v1.10.0
uses: DriverDigital/workflows/.github/workflows/ticketed-review.yml@90f0d066c140c356c44d5b3d83d795c0256a7b82 # v1.11.0
with:
pr_number: ${{ github.event.pull_request.number || github.event.issue.number }}
secrets:
Expand Down