Skip to content

Latest commit

 

History

75 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DriverDigital/workflows — central reusable workflows

Single source for Driver's Bonsai→GitHub pipeline reusable workflows and the onboarding kit (templates/github/ in this repo). Each consuming repo installs a thin caller stub per workflow that pins an immutable commit SHA; a bot (Dependabot/Renovate) bumps the SHAs as new tags ship. This repo is public so cross-repo reusable calls resolve from any consuming repo (the org enforces a selected-actions allowlist at the org/enterprise tier).

How the repos fit together

  • workflows (this repo, public) — the reusable GitHub workflows + this onboarding kit. Fleet repos carry thin SHA-pinned caller stubs; these run the GitHub side (the implementer and the Dependabot rails) once an issue exists.
  • driver-agents (private) — the Shopify admin-tool wrapper and the canonical operator instructions, cloned at the pinned DRIVER_AGENTS_REF; also the pipeline dispatcher (pipeline-dispatch.yml, scheduled) that triages ready Bonsai tasks, opens those GitHub issues, and polls the Bonsai status back. It was also the box's headless claude -p cron runner.
  • driver-bonsai-mcp — archived 2026-09-11; it held the dispatcher and the Bonsai bridge server until 2026-09-10. Mentions of it below are history.

Flow: a Bonsai ticket assigned to Agents → the dispatcher triages it and opens a GitHub issue → the target repo's claude.yml implements it and opens the PR → the dispatcher moves the Bonsai task to Internal Review.

Status & versions

Latest tag v1.16.0 (ff3ff34, 2026-09-10) — every third-party action floats on its major tag, in the kit and the reusables, and Dependabot proposes majors only; hours after v1.15.0, which made the issue path unable to end green without a PR. Both waved to all 20 targets the same day — see v1.16.0 below. The three Dependabot stubs are pinned to ff3ff34.

State of play, open decisions and next steps: docs/HANDOFF.md.

Deployed fleet stubs are repinned by the wave, tools/fleet-wave.sh. Dependabot does bump these pins in a repo that has a github-actions block, but the wave repins within minutes of every tag so it rarely gets the chance — docs/fleet-operations.md. Org Actions secrets (AGENTS_GH_PAT, CLAUDE_CODE_OAUTH_TOKEN, SHOPIFY_ALERT_WEBHOOK) and cross-repo Actions access are already in place — no per-repo secret setup.

Tags are human labels + the bot's bump target; the caller stubs pin the SHA. History: v1.0.0 (initial rail) → v1.0.1 (no-ticket detection fix) → v1.0.2 (dependabot-report bot-actor fix) → v1.1.0 (add dependabot-keep-current + claude-code-action bump) → v1.1.1 (keep-current fail-loud fix) → v1.2.0 (PR-first house-style review) → v1.3.1 (PR-first: REQUIRE the allow-list naming the inline-comment poster) → v1.4.0 (PR-first outcome-aware marker) → v1.5.0 (add the ticketed-review reviewer-loop rail) → v1.5.1 (drop the gh-based author re-check that skipped every real PR) → v1.5.2 (allowed_bots: claude[bot], so the bot-opened round 1 actually reviews) → v1.5.3 (always-latest resilient Claude Code self-install in the three agent reusables) → v1.5.4 (dependabot-validate: npm-install fallback for lockfile-less repos + actions/checkout v7) → v1.5.5 (claude-code-action 1.0.161 → 1.0.168 in the agent reusables) → v1.6.0v1.7.0v1.8.0v1.9.0v1.10.0v1.11.0v1.12.0v1.13.0v1.14.0v1.15.0v1.16.0 (all below). v1.3.0 was never tagged.

v1.16.0 (ff3ff34, 2026-09-10)

Every third-party action floats on its major tag, in the kit and the reusables, and Dependabot proposes majors only. Canaried on vite-plugin-shopify-clean, then waved 2026-09-10 to all 20 targets (19 pushed, the canary already current). Audit the same day: 51 pins at ff3ff344, 88 files matching templates/, 2 drifted — the hand-installed lint.yml copies in driver-agents and driver-engineering-app, which the wave never reaches (neither carries claude.yml or a stub); a to-do is filed in each and the rows clear when they land.

  • Why. A SHA in templates/ is a pin nothing bumps (Dependabot scans .github/workflows/ only), while a fleet repo's Dependabot bumps its deployed copy when the action releases, so the fleet always ran ahead of the kit and each wave rolled it back — foundrae-blackridge #174 took claude-code-action 1.0.195 → 1.0.210 and the v1.15.0 wave put it back to 1.0.201. Maria's policy (2026-09-10): action refs stay current with no commit anywhere, and a breaking change is fixed after the fact rather than every patch shepherded across 20 branches.
  • Refs: actions/checkout@v7, actions/upload-artifact@v7, actions/download-artifact@v8 and anthropics/claude-code-action@v1 — in claude.yml, lint.yml and shopify-tool-smoke.yml, the three Dependabot reusables, the two retired review rails, and this repo's own lint.yml. A run resolves the tag fresh, so every minor and patch arrives on its own; Dependabot opens a PR only for a new major, and Anthropic re-points v1 within seconds of each release.
  • Dependabot, here and in the kit: minor and patch bumps of actions/* and anthropics/* are ignored (a floating ref already has them, and it closes a reported edge where Dependabot rewrites a major-tag ref into a pinned patch). The kit block names the two owners rather than *, so the DriverDigital/workflows stub bumps it exists for still flow — the tagged kit file said *; corrected on main the same day, before any repo installed it. Here, github-owned majors still auto-merge and claude-code-action majors land by hand.
  • The trade, recorded in claude.yml: v1 is a mutable tag a bot repoints, and the SHA covered one of two channels into a job holding the OAuth token — the CLI install was already unpinned — so a small reduction, not none. Macroscope's Critical on actions/checkout@v7 (#55) was assessed and kept, by decision, in its thread.
  • Docs updated in place: CLAUDE.md's invariant, README's "Template pins are manual", the audit's normalization note, fleet-operations.md, the kit README, HANDOFF.md's watch-items, and the conversion scope doc, whose "three action pins" secondary win is gone.

v1.15.0 (15a34e9, 2026-09-10)

The wave that carries #50's issue-path guard, plus the ride-alongs decided 2026-09-10. Canaried on vite-plugin-shopify-clean, then waved 2026-09-10 to all 20 targets (19 pushed, the canary already current). Audit the same day: 51 pins at 15a34e98, 90 files matching templates/, zero drift — the first audit that counts the PR template.

  • An issue run can no longer end green without a PR (#50): a guard step fails the job when no open PR exists from issue-<n> (Avara #195 pushed its branch and stopped, and nothing noticed). show_full_output: true keeps the transcript in the masked job log — the log, not an artifact, so registered secrets stay redacted (Macroscope on #50). CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 is set at the step env and through the settings env (claude-code-action#1499: a run that backgrounds subagents exits green with the work orphaned; the step-env form is what three independent reports measured, the settings path is what the CLI reads from disk).
  • claude-code-actionc81e3bc (v1.0.201) in the kit, matching the reusables since #47. foundrae-blackridge@staging had been moved to 1.0.210 by its own Dependabot (#174), not by hand; the wave brought it back to the kit's pin, and when Dependabot bumps it again the audit will read that as the kit being behind.
  • DRIVER_AGENTS_REFb4ea94e (driver-agents main, 2026-09-10) in both kit files. driver-agents PR #7: the guard reports a value-clearing write (value of "", [] or {}) on the allow path — a stdout line, not a gate — and the canonical blockquote names three exit-3 refusal kinds and adds two paragraphs (an empty value is a delete; the access.admin enum). Kit copy re-copied, 2,370 → 3,480 chars, parity verified whitespace-collapsed at the pin.
  • templates/github/dependabot.yml gains a cooldown block: GitHub's default 3 days stays for third-party actions; DriverDigital/workflows* is exempt, so a repo the wave skipped bumps the day the tag lands.
  • The wave replaces every kit file whole-file, the three stubs included (the pin-line sed let per-repo stub edits survive, but the audit reports those as drift and none existed), and pull_request_template.md joins the file set at .github/ — the fleet's copies still credited the retired status sync, invisible to the audit until it learned the path. Presence-based as before: replaced where present, never installed.
  • tools/fleet-pin-audit.sh selects the newest vX.Y.Z by semver across every tag page and fails closed on a failed page or an empty list (#51 — three Macroscope findings, each fixed).
  • dependabot-report headers (reusable and stub) no longer claim a reusable cannot see github.event.workflow_run; the explicit inputs exist for the provenance assertion (#51).

v1.14.0 (539d7ea, 2026-08-22)

The queued DRIVER_AGENTS_REF bump and tripwire re-copy, plus the kit's first Dependabot updater. Waved 2026-08-22 to 19 of the 20 repin targets — vite-plugin-shopify-clean deliberately skipped (--skip) to prove Dependabot bumps the stub pins unaided. Audit the same day: 48 pins at 539d7eab, 68 files matching templates/, and exactly the expected drift — 3 stale pins and 4 content rows, all vite-plugin-shopify-clean@main. fleet-pin-audit.sh --stale reads red by design until that repo catches up.

  • DRIVER_AGENTS_REF919ca79 (driver-agents main, committed 2026-08-07) in both kit files. Under tools/ the diff is one stderr message, jq summary phrases and a comment — no file, mode, env var, argument or exit-code change.
  • Tripwire blockquote re-copied from canonical, 978 → 2,370 chars: the two exit-3 refusals are split (allowlist vs argument guard), menuUpdate / metaobject values / collectionUpdate ruleSet are flagged as replace-not-patch, redirectNewHandle is required on a handle change, and the false "extend the allowlist with one reviewed line" blanket is gone. Parity verified whitespace-collapsed at the pin.
  • templates/github/dependabot.yml — a daily grouped github-actions block, hand-installed (it lives at .github/ and merges into a repo's existing file; the three install cases are in the kit README). The fleet's existing blocks are monthly and a Dependabot PR waits on a human merge, so the wave stays primary — docs/fleet-operations.md.
  • tools/fleet-wave.sh --skip <repo>, repeatable, resolved like --only so case or a typo cannot wave the repo it was meant to spare; both flags refuse an empty value. The default commit message no longer names the v1.13.0 retirement.
  • claude.yml ride-alongs: the dangling docs/phase2-github-setup.md pointer is gone and a Figma MCP caveat sits beside the WebSearch one above --allowedTools (docs/figma-mcp-in-ci.md).
  • this repo's own CI: the kit actionlint pass excludes dependabot.yml (not a workflow). CLAUDE.md added (#41).

v1.13.0 (f6d25d3, 2026-08-22)

The pass that sets the rail up to run without the box — see the headline for what and why. Piloted on vite-plugin-shopify-clean (canary below), then waved 2026-08-22 to all 20 repin targets (19 pushed, the pilot already current). Audit clean the same day: 51 pins at f6d25d34, 72 files matching templates/, zero drift across the 23 pairs audited.

  • claude.yml reads the dispatcher's issue body. Target branch: is the base for both gh issue develop --base and gh pr create --base; Reviewer: is requested with gh pr edit --add-reviewer after the PR exists, non-fatally — a bad handle can no longer abort PR creation; ## Instructions from the ticket directs the task but never overrides the steps, the repo rules or a system-prompt guard, and never authorises touching .github/ or exposing credentials. Branch name and handle are opaque data: one quoted argument each, never spliced into a larger command.
  • In-run pre-review. Push, then /code-review high <base>...HEAD — the explicit range is load-bearing, a bare invocation reviews commits ahead of upstream, which is nothing once the branch is pushed. Fix, push again, and the short PR body ends Pre-review: N findings, M fixed, K dismissed (or Pre-review: skipped (reason)).
  • Quality standard added to the static --append-system-prompt (9 items: CLAUDE.md/HANDOFF first, design before code, one author per file, adversarial pre-review, verify before done, conventional commits with no trailers, short PR body, repo conventions win, judgment over compliance). It is global — it reaches the ticketed revision rail too, so watch the first revision round against the 90-minute cap.
  • Attribution off mechanically: settings: { "attribution": { "commit": "", "pr": "", "sessionUrl": false } } — no Co-Authored-By, no "Generated with" line on the agent rails. A human @claude (tag mode) still gets the action's own co-author text.
  • --model fable --effort xhigh, and claude-code-actiond40ddef (v1.0.195), matching the reusables.
  • bonsai-status-sync retired — template and reusable both deleted, and the org secret BONSAI_BEARER_TOKEN deleted after a direct probe of every non-archived repo (+ Palmers main*) found no copy of the workflow left anywhere. The dispatcher polls the two remaining legs instead (issue opened → In Progress; non-draft dev-linked PR → Internal Review). The kit is six .yml files; five reusables remain — three Dependabot rails plus the two retired review rails; ticketed-review.yml's banner now says re-activation needs BONSAI_BEARER_TOKEN recreated.
  • tools/fleet-wave.sh — the wave is a checked-in script now. It discovers targets by presence (claude.yml or a Dependabot stub, which is what reaches the two stub-only pairs) and repins the pin line's SHA and its # vX.Y.Z trailer together. Guards: a real wave only from a clean main containing the tag, kit stubs must pin the latest tag, never waves this repo, actionlint before every write, no destination path twice, store handle survives, --dry-run touches nothing, zero targets is an error. Mechanics: docs/fleet-operations.md.
  • this repo's own CI: lint.yml now also fails on a stray brace in claude.yml's prompt: scalar — actionlint is blind to it, and a stray { is a silent dispatch failure fleet-wide.
  • DRIVER_AGENTS_REF held at 4d63371, re-checked against canonical at that pin (whitespace-collapsed parity: match). driver-agents main is 8 commits ahead with a much longer blockquote and small tool fixes; the queued re-copy + ref bump is in docs/HANDOFF.md.
  • Design: driver-bonsai-mcp docs/superpowers/specs/2026-08-21-box-retirement-dispatcher-design.md §5a.

Canary (vite-plugin-shopify-clean, 2026-08-21 23:12–23:17 UTC, torn down afterwards): Bonsai TSK-00923 → dispatcher run 32536010404 → issue #91 by driver-digital-agents carrying the target branch, reviewer and ticket instructions → claude.yml run 32536083273, success in 3m24s on claude-fable-5 → PR #92 with ktdriverdigital requested 4 s after creation, a 10-line body ending Pre-review: 1 finding, 1 fixed, 0 dismissed, no footer, 0 trailers on 2 commits, and the ticket's changelog instruction honoured. Timestamps prove the order: feat commit 23:15:12 → pre-review fix 23:16:41 → PR 23:16:49. Dispatcher reconcile run 32536361182 moved Bonsai to Internal Review.

v1.12.0 (b1fcb78, 2026-08-08)

The review-rail retirement: PR review became Macroscope's job alone (decided 2026-08-08) — the two review stubs left the kit, their reusables stayed here caller-less with retirement banners, and bonsai-status-sync lost its review leg (formal review → Revisions Requested / Ready for QA); the Dependabot rails, claude.yml and the two remaining status legs were untouched. Context and the Macroscope→Bonsai build plan: docs/macroscope-integration-scope.md. Waved 2026-08-08 to all 23 pairs: the two review stubs deleted from every fleet pair that carried them (41 pin rows removed), bonsai-status-sync.yml whole-file replaced on 18 pairs, remaining stubs repinned. Audit clean same day: 69 pins at b1fcb78c, 90 files matching templates/, zero drift. Three pairs' only pinned stub was pr-first-review.ymldriver-agents@main, driver-engineering-app@main, driver-bonsai-mcp@main — so they now carry no pins at all (still content-checked) and the repin-target count lands at 20 (see docs/fleet-operations.md).

  • Review rails retired, not removed. Stubs deleted (kit + fleet); reusables preserved inert with retirement banners carrying the re-activation path (restore stubs from git history, tag, wave). Deletion over gh workflow disable because disabled-state is invisible to the pin audit — the fleet's files should say what actually runs.
  • bonsai-status-sync review leg removed — the stub's pull_request_review trigger AND the reusable's handler, deliberately not actor-gated: Macroscope's bot behavior (formal reviews vs. comments) is unobserved as of this tag, and if it submits formal reviews the old mapping would flip statuses with the wrong semantics (a bot approval is not "Ready for QA"). Statuses past Internal Review are a PM's job until the Macroscope→Bonsai integration lands.
  • claude.yml deliberately untouched, including the ticketed-loop machinery (round-marker prompt branch, actor carve-out, re-request step). It looks dead with ticketed-review gone; it is the designed re-entry point for the Macroscope-driven revise loop — do not strip it in a future wave.
  • DRIVER_AGENTS_REF did not move. The queued canonical-blockquote re-copy stays pending for the next claude.yml-touching release — deliberately not folded in here, so this wave changes review behavior and nothing else.

v1.11.0 (90f0d06, 2026-08-02)

Waved to all 21 pairs on 2026-08-02; fleet uniform, 108 pins, zero stale.

  • bonsai-status-sync.yml conversion completed. The 190-line per-repo copy became a 67-line caller stub — the status machine, actor gate, linkage logic and cascade caveat now live in one central file. The reusable itself landed one tag earlier (see v1.10.0), so the conversion spans the two tags: a new reusable's stub cannot be pinned until the tag containing it exists.
  • kit claude.yml + shopify-tool-smoke.yml: DRIVER_AGENTS_REF4d63371. The previous pin 0bbb125 predated graphql_guard.py, so every fleet runner executed admin-graphql.sh with no fail-closed allowlist and the Driver Engineering scope grant was the only control on destructive mutations.
  • Shopify operator tripwire appended to claude.yml's static --append-system-prompt, pairing with that wrapper. The blockquote is copied verbatim from driver-agents docs/agent-instructions-shopify.md (canonical — edit there first); a non-canonical kit-side lead-in precedes it, un-scoping the block from the conduct rules above and telling the model how to report a trip on a rail that cannot set a job exit code.
  • this repo's own CI: lint.yml gained a tokenization guard asserting claude_args holds exactly four single quotes and the system prompt contains no apostrophe or $. One apostrophe typed into canonical upstream silently truncates the prompt — shell-quote does not throw, every flag still parses, and the wave would copy the truncated prompt fleet-wide green.
  • Piloted before the wave: vars.BONSAI_URL proven to resolve against the caller, so the per-repo tunnel override survives the conversion. See docs/fleet-operations.md.

v1.10.0 (b394c6d, 2026-08-02)

  • New sixth reusable: .github/workflows/bonsai-status-sync.yml. Its jobs: body is byte-identical to the old per-repo copy except one added comment. Deliberately shipped without its caller stub — see v1.11.0.
  • lint.yml placeholder-pin guard: fails the build on any kit stub still carrying an all-zero pin, so a stub that cannot resolve can never reach main.
  • Never waved on its own; superseded by v1.11.0 two commits later.

v1.9.0 (a54c91e, 2026-08-01, kit-only)

  • Store app secrets renamed DRIVER_AGENTS_SCOPES_CLIENT_ID/_SECRETDRIVER_ENGINEERING_APP_CLIENT_ID/_SECRET, tied to the per-org "Driver Engineering" app that replaced "Driver Agents Scopes" (retired 2026-08-01). Waved to all 21 pairs; Avara's smoke test green on the new names, old-name secrets deleted.
  • Reusables unchanged. Note this release never got its kit repin committemplates/ sat at v1.8.0's SHA while the deployed fleet was waved to a54c91e, leaving the fleet a release ahead of the kit templates until v1.11.0 closed it. That gap was invisible to tools/fleet-pin-audit.sh at the time; its reference check now catches exactly this shape — see docs/fleet-operations.md.

v1.8.0 (2026-07-31, kit-only)

  • kit claude.yml + shopify-tool-smoke.yml: DRIVER_AGENTS_REF0bbb125 (the audit pipeline: SHOPIFY_AUDIT_CONTEXT on the tool, plus the box's nightly audit-publish.sh — design at driver-agents docs/audit-data-model.md). Lockstep as always.
  • Audit context export: the provisioning step now builds SHOPIFY_AUDIT_CONTEXT (ticket/issue/run/host) so every tool call's audit line says on whose behalf it ran. The Bonsai task uuid is resolved from the ISSUE body (uuid= param — the same linkage bonsai-status-sync.yml greps), with a closingIssuesReferences fallback for the @claude-on-a-PR rails; a human's ad-hoc @claude has no ticket, correctly.
  • Audit artifact upload: a final if: always() actions/upload-artifact step (pinned v7.0.1) ships the runner's throwaway audit log to the box's nightly collector — github.run_attempt in the artifact name dodges the immutable-artifact collision on re-runs. Skips cleanly on repos without store tooling. Same step on the smoke test.
  • reusables: unchanged; stubs repinned to the new tag SHA for the pin-audit invariant.

v1.7.0 (2026-07-31, kit-only)

  • kit claude.yml + shopify-tool-smoke.yml: DRIVER_AGENTS_REF0404c4e (driver-agents main @ 2026-07-31) — picks up the human-readable Slack alert wording (driver-agents PR #2). The pin moves in both files together, per the lockstep rule.
  • CI alerting leg: the same provisioning step now writes the org-level SHOPIFY_ALERT_WEBHOOK secret (the #driver-agents-status incoming webhook) to the runner's throwaway disk and exports SHOPIFY_ALERT_WEBHOOK_FILE + SHOPIFY_ALERT_HOST_LABEL, so the admin tool's destructive/failed- call alerts post from Actions runs exactly as they did from the box. The alert's "where to look" label is the run URL — the runner's audit log doesn't outlive the job. Absent secret = alerts silently off, nothing else changes (the tool's own best-effort posture).
  • reusables: unchanged (byte-identical to v1.6.0's). The stubs are repinned to 3966041 regardless, purely so fleet-pin-audit.sh's latest-tag comparison stays meaningful.

v1.6.0 (0a3934f, 2026-07-31)

  • reusables: actions/checkoutv7.0.1, claude-code-actionv1.0.183; each Claude Code self-install attempt is now bounded by timeout — a stalled download used to hang one attempt until the job's wall-clock cap while the retry loop never advanced.
  • kit claude.yml: human @claude comments always get tag mode (never prompt-hijacked — foundrae-blackridge PR #148); the ticketed round-marker branch is author-gated on driver-digital-agents + id 261291955; optional self-skipping Shopify admin tool provisioning — pinned to a reviewed driver-agents revision, verified before any credential is written, and gated off the read-only /code-review rail (Avara PR #161); the same bounded self-install; actions/checkoutv7.0.1 and claude-code-actionv1.0.183.
  • new kit file shopify-tool-smoke.yml (store repos only): a manual diagnostic for the Shopify admin-tool wiring, upstreamed from Avara PR #161 so it is maintained here rather than reinvented per repo. It duplicates claude.yml's provisioning step by design — same wiring, loud failures instead of degrade — so the two must be kept in lockstep.
  • this repo's own CI: new lint.yml runs actionlint — plus shellcheck over every run: block — across the reusables and the kit, so a broken workflow can no longer reach consumer repos.

Release + repin order (don't skip a step — a wave is only safe once all three are done):

  1. Merge to main, then cut the new tag.
    • If the release moves DRIVER_AGENTS_REF, re-run the tripwire parity check first: extract the > lines from driver-agents docs/agent-instructions-shopify.md at the new pin, strip the > prefixes, NFC-normalize, collapse whitespace, and diff against the blockquote portion of claude.yml's --append-system-prompt. The whitespace collapse is mandatory — the kit flattens canonical's paragraph break to a single space (forced by the no-newline constraint), so a strict byte compare reports a false failure. Nothing else re-checks this. fleet-pin-audit.sh proves the fleet matches templates/github/claude.yml — it cannot prove that file's blockquote still matches canonical at the new pin, and DRIVER_AGENTS_REF is a raw SHA in an env var that no bot can bump. The audit catches a fleet that fell behind templates/; only this step catches templates/ falling behind driver-agents.
  2. Repin every caller stub in templates/github/ to that tag's SHA and its # vX.Y.Z trailer, then commit. Until this lands, the kit's stubs still point at the PREVIOUS tag's reusables.
    • 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.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 — it now checks waved file content against templates/, not just the pin line, and exits non-zero on any drift, so a wave can gate on it).
    • The wave is now a checked-in script: tools/fleet-wave.sh --dry-run first, then without.
    • Dependabot also bumps the stub pins in any repo with a github-actions block (the kit now ships one, templates/github/dependabot.yml, for the repos that had none) — on its schedule and through a PR a human merges, so the wave stays the primary path and Dependabot the backstop. --skip <repo> leaves every branch of a repo to it on purpose — only sound where Dependabot covers each kit branch (it scans the default branch unless a target-branch entry exists, so not Palmers as configured). See docs/fleet-operations.md.
    • 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 repo as the workflow, where it is workflow_call-only, fires on nothing, and looks green.
    • Sed pin lines; never git apply them. A pin hunk patches from whatever SHA the kit held, which is not necessarily what the fleet holds — at v1.11.0 the kit diff patched from 80c35fe (v1.8.0) while every deployed stub held a54c91e (v1.9.0), a SHA no kit revision had ever carried in a pin line, so no diff base produced a matching - line and git apply would have rejected all five files on target #1. The sed rewrites the SHA and the # vX.Y.Z trailer in one substitution (as fleet-wave.sh does) — the two halves are one pin, and a stale comment names the wrong release.
    • Wave mechanics, the guards worth keeping, and what the pin audit cannot see: docs/fleet-operations.md.

Template pins are manual — so the kit's third-party actions float. .github/dependabot.yml uses directory: "/", which only scans .github/workflows/ — nothing will ever bump a pin inside templates/, while a fleet repo running the github-actions updater bumps its deployed copy when the action itself releases, so a SHA there guaranteed the fleet ran ahead of the kit and each wave rolled it back (foundrae-blackridge #174). The three whole-file kit workflows therefore reference actions/checkout@v7, actions/upload-artifact@v7 and anthropics/claude-code-action@v1, which Dependabot leaves alone until a new major exists. The reusables float the same way, so the only action bump that ever gets a PR — here or in a fleet repo — is a major. DRIVER_AGENTS_REF stays manual — it appears in two kit files, claude.yml and shopify-tool-smoke.yml, and must carry the same pin in both or the smoke test verifies a revision the implementer never runs — and to the VERSION + SHA256 pair in lint.yml, which must be bumped together or the checksum check fails the job.

Onboarding a new repo: copy the matching stubs from this repo's templates/github/ into the repo's .github/workflows/, run a test PR (human + Dependabot), then pin the required check validate / validate + add a human-approver rule (see First-run / required-check below). Caller stubs MUST carry their own permissions: block (a repo whose default workflow token is read-only otherwise produces a silent startup_failure — no check run, no notification).

What's here

Reusable (.github/workflows/) Privilege Trigger (in the caller) Job
dependabot-validate.yml none (credential-less) pull_request mechanical install/build/test (+ optional theme/dev-smoke) → upload artifact
dependabot-report.yml secrets (PAT + OAuth) workflow_run reason over the inert artifact → verdict comment + request a human reviewer
dependabot-keep-current.yml PAT only pull_request (closed) rebase out-of-date Dependabot PRs on strict (require-up-to-date) repos; inert elsewhere

Two more reusables sit in .github/workflows/ retired (v1.12.0, 2026-08-08): pr-first-review.yml (human no-ticket PR → /code-review + reviewer request) and ticketed-review.yml (claude[bot] ticketed PR → capped revise loop → Bonsai reviewer handoff). Macroscope reviews all PRs now; both files are workflow_call-only with no callers anywhere, preserved for re-activation — see the banner in each and docs/macroscope-integration-scope.md.

The onboarding kit lives here: templates/github/ (moved from driver-bonsai-mcp 2026-07-15). It carries the three caller stubs above plus claude.yml (the implementer, still a full per-repo workflow), shopify-tool-smoke.yml (store repos only), lint.yml (actionlint over the installing repo's own workflows), pull_request_template.md (waved since v1.15.0) and dependabot.yml (the github-actions updater that bumps the stub pins between waves — installed by hand, merged into an existing file).

Not every repo takes the whole kit. A repo that is not on the Bonsai → PR pipeline can install lint.yml alone and skip the rest as inert weight. driver-agents and driver-engineering-app run that subset (they took pr-first-review.yml + lint.yml on 2026-08-02; the review stub was deleted in the v1.12.0 retirement wave). The trade-off is written up in templates/github/README.md under Partial install.

A kit-only addition does not need a tag or a wave. lint.yml shipped without either, and that was correct: it changes no reusable, repins no stub, and carries no uses: DriverDigital/workflows@<sha> of its own, so nothing deployed had to move. Note the direction of the obligation — cutting the tag is what creates the wave, because the moment the latest tag moves the audit's reference check fires against every stub in templates/ and they must be repinned and re-copied everywhere. Let a kit-only file ride along with the next release that actually changes a reusable.

claude.yml stays a per-repo copy — converting it to a reusable is tabled pending the OIDC spike (whether Claude App token minting survives inside a cross-repo reusable; scope in docs/reusable-conversion-scope.md), so it remains the kit's main drift surface and the reason re-copies still need care.

Two files in .github/workflows/ are this repo's own CI, not products — they are workflow_call-free and never ship to the fleet: lint.yml (actionlint + shellcheck over the reusables and the kit, so a broken workflow can't reach consumer repos) and dependabot-auto-merge.yml (auto-merges this repo's own github-owned Dependabot bumps; the claude-code-action group is deliberately excluded, so those land by hand). Both are quiet below a major: every third-party action here and in the kit floats on its major tag, so a run already has each minor and patch, and .github/dependabot.yml ignores everything but majors.

actionlint is a required status check on main (set 2026-08-02) — before that, lint.yml could report red without being able to block. Note the name collision: this repo's own lint.yml and the kit's templates/github/lint.yml are different files. The kit one runs actionlint over the installing repo's .github/workflows/ and nothing else; this one additionally lints templates/github/, gates on placeholder pins, and asserts claude.yml's system prompt still tokenizes. Both use the job id actionlint, so the required-check context string is the same either way. enforce_admins stays false here, deliberately — which means an admin can still merge past a red actionlint. Requiring the check makes it binding for everyone else and puts a red X in front of an admin who previously had nothing to override; that was worth having on its own. Flipping the flag would break this repo's own release habit — six commits on main, v1.9.0's included, were pushed directly with no PR. Detail in docs/fleet-operations.md.

The three identities

  • claude[bot] — the implementer (Phase 2 claude.yml), distinct from any reviewer.
  • driver-digital-agents (the AGENTS_GH_PAT fine-grained PAT) — the agent-rail actor: GH_TOKEN on every agent-acting gh step (the default GITHUB_TOKEN is used only where a post must not cascade — claude.yml's failed-run notice), dependabot-report's comment/reviewer-request identity, and the author of claude.yml's sentinel comments. It was the reviewer on the retired review rails.
  • Anthropic billingCLAUDE_CODE_OAUTH_TOKEN (Max). Never set anthropic_api_key (it overrides OAuth and bills at API rates).

All three secrets must be org-level Actions secrets available to each consuming repo.

The Dependabot security split (load-bearing)

dependabot-validate runs untrusted Dependabot code (install + PR-modifiable build) but holds no secrets — three independent layers: Dependabot forces a read-only token + no Actions secrets; the caller stub passes no secrets:; this reusable declares no workflow_call.secrets. dependabot-report holds the secrets but never checks out PR head and never runs PR code — its agent reads ONLY the inert artifact (build.log/result.json, never interpolated into a run: line) with a read/write-file-only tool surface (no gh), after a provenance assertion (event pull_request, actor dependabot[bot], non-empty PR number, same-repo head). Never use pull_request_target.

Consuming it (caller stubs)

Install the matching stubs from this repo's templates/github/ into a repo's .github/workflows/. Pin every uses: of this repo's reusables to an immutable commit SHA (decided 2026-06-17); a bot (Renovate/Dependabot) bumps the SHAs. The dependabot-validate stub's name: MUST stay byte-identical (Dependabot validate) across all repos — the dependabot-report stub's workflow_run trigger name-matches it exactly, and a drift silently disables the human-ping.

Per-repo validation override (target repo: .github/agent-validate.json)

{
  "install": "npm ci",
  "build": "npm run build",
  "test": "npm test",
  "themeCheck": "npx @shopify/cli theme check --fail-level error",
  "dev": "npm run dev",
  "devTimeoutSeconds": 90,
  "devReadyRegex": "compiled|ready|built in|Local:"
}

All keys optional. Defaults: package manager from the lockfile (npm is the house default), build/test run only if those package.json scripts exist, themeCheck/dev run only if configured.

Reviewer handoff

Retired with the review rails at v1.12.0 — nothing here reassigns a Bonsai task or requests a reviewer on review completion any more; the status moves are a PM's job until the Macroscope→Bonsai integration lands. Since v1.13.0 claude.yml requests the GitHub reviewer named on the issue body's Reviewer: line when it opens the PR, and since 2026-09-11 the dispatcher (driver-agents) assigns the reviewer in Bonsai when the PR reaches Internal Review. The bridge server that carried /tasks/reviewer-handoff is retired too; what replaces it for that build — the Bonsai public API, and the reviewer read off the issue body instead of the Reviewer field — is in docs/macroscope-integration-scope.md. dependabot-report still requests a human reviewer on Dependabot PRs (default mcarter-astronautdev, per-repo override via the PR_REVIEWER_HANDLE Actions variable). The live reviewer map is driver-agents/config/reviewers.json, read by the dispatcher to write the issue's **Reviewer:** line; this repo no longer carries a copy.

First-run / required-check

The dependabot-validate job always runs and branches internally (non-Dependabot PRs no-op green), and must never be if:-skipped. The mechanism is worth stating precisely, because the intuitive version is wrong: GitHub does accept a check run whose conclusion is skipped. The problem is that if:-skipping the caller job means the reusable never starts, so the nested validate / validate context is never created at all — and a required context with no check run for the head commit blocks forever. Reason about whether a check run exists for the head SHA, not about the word "skipped". After the first run on a test PR: pin the exact required-check context GitHub reports — for a reusable-workflow job it is <caller-job-id> / <reusable-job-id>, expected validate / validate (the workflow display name is NOT part of the context; copy the literal string from the first run's checks list). Require a human approver (e.g. CODEOWNERS) so no bot signal satisfies the merge gate.

About

GitHub actions and other workflows used across Driver Digital repositories

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages