diff --git a/README.md b/README.md index 5df8548..959a41f 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/docs/identity-unification-scope.md b/docs/identity-unification-scope.md index 2a65f41..abaf483 100644 --- a/docs/identity-unification-scope.md +++ b/docs/identity-unification-scope.md @@ -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 @@ -234,7 +234,7 @@ 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**. @@ -242,7 +242,7 @@ Note `trigger_phrase` **does not exist in this repo today** (`git grep` → zero 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 diff --git a/docs/reusable-conversion-scope.md b/docs/reusable-conversion-scope.md index 52c6f6b..02315db 100644 --- a/docs/reusable-conversion-scope.md +++ b/docs/reusable-conversion-scope.md @@ -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. @@ -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.* @@ -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 @@ -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** | @@ -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 @@ -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 diff --git a/templates/github/README.md b/templates/github/README.md index b5cb3e8..0773763 100644 --- a/templates/github/README.md +++ b/templates/github/README.md @@ -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. | @@ -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. diff --git a/templates/github/bonsai-status-sync.yml b/templates/github/bonsai-status-sync.yml index c14f7fb..02efc3f 100644 --- a/templates/github/bonsai-status-sync.yml +++ b/templates/github/bonsai-status-sync.yml @@ -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 diff --git a/templates/github/dependabot-keep-current.yml b/templates/github/dependabot-keep-current.yml index d34fb4c..8fbf815 100644 --- a/templates/github/dependabot-keep-current.yml +++ b/templates/github/dependabot-keep-current.yml @@ -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 }} diff --git a/templates/github/dependabot-report.yml b/templates/github/dependabot-report.yml index a2f9ea7..90545f0 100644 --- a/templates/github/dependabot-report.yml +++ b/templates/github/dependabot-report.yml @@ -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 }} diff --git a/templates/github/dependabot-validate.yml b/templates/github/dependabot-validate.yml index 122a738..100f53c 100644 --- a/templates/github/dependabot-validate.yml +++ b/templates/github/dependabot-validate.yml @@ -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. diff --git a/templates/github/pr-first-review.yml b/templates/github/pr-first-review.yml index 60eb18e..9cab0ef 100644 --- a/templates/github/pr-first-review.yml +++ b/templates/github/pr-first-review.yml @@ -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 diff --git a/templates/github/ticketed-review.yml b/templates/github/ticketed-review.yml index 78dc3a3..e06e829 100644 --- a/templates/github/ticketed-review.yml +++ b/templates/github/ticketed-review.yml @@ -40,7 +40,7 @@ jobs: contains(github.event.comment.body, '') && 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: