diff --git a/CLAUDE.md b/CLAUDE.md index b915726..f26ff42 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -23,7 +23,7 @@ The public home of Driver's Bonsai→GitHub pipeline workflows. Two products liv four caller stubs pinning a reusable by immutable SHA (`claude.yml` and the Dependabot three), plus three whole-file workflows (`shopify-tool-smoke.yml` store repos only, `lint.yml`, `pr-bonsai-link.yml` beside `claude.yml`) and `pull_request_template.md` and `claude-standards.md` - (both live at `.github/`; the wave carries them) and `dependabot.yml` (hand-installed — merged into a repo's existing file, never copied over it). + (both live at `.github/`; the wave carries them and installs `claude-standards.md` beside `claude.yml`; the `@` import in each repo's `CLAUDE.md` is by hand) and `dependabot.yml` (hand-installed — merged into a repo's existing file, never copied over it). Kit install conventions: `templates/github/README.md`. PR review is Macroscope's, org-wide (Maria, 2026-09-12): Claude reviews a PR only when a person `@claude`s it diff --git a/README.md b/README.md index bdf9d5b..59808ba 100644 --- a/README.md +++ b/README.md @@ -451,7 +451,7 @@ all PRs — [`docs/macroscope-integration-scope.md`](docs/macroscope-integration carries a caller stub for each reusable above, plus `shopify-tool-smoke.yml` (store repos only), `lint.yml` (actionlint over the installing repo's own workflows), `pr-bonsai-link.yml` (fails a PR that names no Bonsai task; installed beside `claude.yml`), `pull_request_template.md` (waved since v1.15.0), `claude-standards.md` (the house commit and comment -standard, imported by each repo's `CLAUDE.md`, waved at `.github/`) and `dependabot.yml` (the `github-actions` updater that bumps +standard, installed beside `claude.yml` and waved at `.github/`; each repo's `CLAUDE.md` imports it) 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 diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 7363747..6a26dd5 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -62,6 +62,8 @@ To-dos are the open `todo` issues on this repo since 2026-10-01 (#61–#64 carry 1. **Make `bonsai-link` a required check per repo** — the wave has installed it everywhere `claude.yml` is; the context is the job id `bonsai-link` (kit README). First, because step 2 waits. + The wave also installs `claude-standards.md` beside `claude.yml` since 2026-10-01; the + `@.github/claude-standards.md` import is a `todo` issue in each repo that lacks it. 2. **One real ticket end to end** on the new rail, transcript read (private repos log it) — after driver-engineering-app's security hardening pass (Maria, 2026-10-01: a couple of weeks; she will not run tickets through the app before it). On Avara, the first store run's log must read diff --git a/docs/fleet-operations.md b/docs/fleet-operations.md index 8008f57..3feb20d 100644 --- a/docs/fleet-operations.md +++ b/docs/fleet-operations.md @@ -79,7 +79,9 @@ commit per file. Per target: the wave aborts a target whose deployed file still carries a handle the variable does not hold. The PR template and `claude-standards.md` are written under `.github/`, the rest under `.github/workflows/`. -2. `pr-bonsai-link.yml` ← written wherever `claude.yml` is, present or not. +2. `pr-bonsai-link.yml` and `claude-standards.md` ← written wherever `claude.yml` is, present or not. + The `@.github/claude-standards.md` import in `CLAUDE.md` stays a per-repo edit: the wave never + writes outside `.github/`. 3. Delete by presence anything the kit no longer ships (`bonsai-status-sync.yml` since v1.13.0). 4. `actionlint` every file about to be written, then one atomic commit (CI-skip token in the message) and patch the ref. diff --git a/templates/github/README.md b/templates/github/README.md index f0c1be7..7bcb4fd 100644 --- a/templates/github/README.md +++ b/templates/github/README.md @@ -11,7 +11,7 @@ workflow here touches it. |---|---|---| | `pr-bonsai-link.yml` | `.github/workflows/pr-bonsai-link.yml` — wherever `claude.yml` is | Fails a PR whose body names no Bonsai task: an `app.hellobonsai.com/tasks/` URL, or a `Bonsai task: none` line. Dependabot PRs are exempt. Check-run context is the job id, **`bonsai-link`**; make it required per repo only once the wave has installed it. | | `pull_request_template.md` | `.github/pull_request_template.md` | Gives human PRs the `Bonsai task: \| none` line `bonsai-link` checks for, and prompts them to **link the Bonsai issue** (`Closes #N`) so the dispatcher can resolve the task. AI PRs write both themselves. | -| `claude-standards.md` | `.github/claude-standards.md` | The house commit-message and code-comment standard. The repo's `CLAUDE.md` imports it with `@.github/claude-standards.md` (replacing any pasted copy), so local sessions load it (CI loading is unconfirmed — `../../docs/HANDOFF.md`). Lint-only repos are wave targets through it. | +| `claude-standards.md` | `.github/claude-standards.md` — wherever `claude.yml` is | The house commit-message and code-comment standard. The repo's `CLAUDE.md` imports it with `@.github/claude-standards.md` (replacing any pasted copy), so local sessions load it (CI loading is unconfirmed — `../../docs/HANDOFF.md`). Lint-only repos are wave targets through it. | | `shopify-tool-smoke.yml` | `.github/workflows/` — **STORE REPOS ONLY** | Manual (`workflow_dispatch`) diagnostic for the Shopify admin tool: secrets → `driver-agents` clone at the pin → token mint → Admin API, read-only. Fails **loudly** where `claude.yml` degrades — that's the point. Skip it in repos with no store. | | `lint.yml` | `.github/workflows/lint.yml` | actionlint + shellcheck over the installing repo's own `.github/workflows/`. Guards the one CI failure with no signal: a YAML or shell error surfaces as a `startup_failure` — no check run, no notification — which on the PR page is indistinguishable from checks that have not started. Check-run context is the job id, **`actionlint`**. Not the same file as this repo's own `.github/workflows/lint.yml`, which runs a superset and never ships. | @@ -122,8 +122,8 @@ Requested, approved → Ready for QA) were retired with the review leg at v1.12. Then add `@.github/claude-standards.md` to the repo's `CLAUDE.md` (a new repo: start `CLAUDE.md` as that line and let `/init` write the rest around it). Every file above is kept current by the wave afterwards (`tools/fleet-wave.sh`, presence-based: - it replaces what a branch already carries, and installs only `pr-bonsai-link.yml`, beside - `claude.yml`). + it replaces what a branch already carries, and installs `pr-bonsai-link.yml` and + `claude-standards.md` beside `claude.yml`; the `CLAUDE.md` import line is the one step it cannot do). **Then `dependabot.yml`, by hand** — it is the updater for the stub pins (without it nothing bumps the `uses: DriverDigital/workflows/...@` lines between waves), and most repos already have one, so never blind-copy it. No `.github/dependabot.yml` → copy the kit's. One without a diff --git a/templates/github/claude-standards.md b/templates/github/claude-standards.md index 26b8a37..8a22444 100644 --- a/templates/github/claude-standards.md +++ b/templates/github/claude-standards.md @@ -1,8 +1,9 @@ # House standards -Shared across every Driver repo. A repo copies this file to `.github/claude-standards.md` once and -imports it from its `CLAUDE.md` with `@.github/claude-standards.md`; the fleet wave keeps the copy -current. Edit it in `DriverDigital/workflows` (`templates/github/claude-standards.md`), never in place. +Shared across every Driver repo. The fleet wave installs this file at `.github/claude-standards.md` +beside the implementer and keeps it current (a repo without the implementer copies it by hand); the +repo's `CLAUDE.md` imports it with `@.github/claude-standards.md`. Edit it in `DriverDigital/workflows` +(`templates/github/claude-standards.md`), never in place. - **Commit messages** — Natural language, not strict conventional-commit formatting. Succinct: what changed, plus any rationale a senior developer would need later. No history, narratives, or diff --git a/tools/fleet-pin-audit.sh b/tools/fleet-pin-audit.sh index 47edaba..f52f4ab 100755 --- a/tools/fleet-pin-audit.sh +++ b/tools/fleet-pin-audit.sh @@ -101,16 +101,23 @@ scan_ref() { # repo ref [ -f "$KIT/$f" ] || continue content_row "$repo" "$ref" "$f" done - # The wave installs pr-bonsai-link.yml wherever claude.yml is, so its absence there is drift. + # The wave installs pr-bonsai-link.yml and claude-standards.md wherever claude.yml is, so their + # absence there is drift (the standards file is probed at .github/ below). if grep -qx claude.yml <<<"$files" && ! grep -qx pr-bonsai-link.yml <<<"$files"; then echo "CONTENT $repo@$ref pr-bonsai-link.yml DRIFT missing" fi # The kit files outside .github/workflows/: the PR template (waved since v1.15.0) and the house - # standards. + # standards. Presence comes from the directory listing, as above, so a failed API call skips the + # file rather than reading as "missing". + local dotgithub + dotgithub="$(gh api "repos/$ORG/$repo/contents/.github?ref=$ref" --jq '.[].name' 2>/dev/null)" || dotgithub="" for f in pull_request_template.md claude-standards.md; do - if gh api "repos/$ORG/$repo/contents/.github/$f?ref=$ref" \ - -H 'Accept: application/vnd.github.raw' > "$TMP/raw" 2>/dev/null; then + if grep -qx "$f" <<<"$dotgithub"; then + gh api "repos/$ORG/$repo/contents/.github/$f?ref=$ref" \ + -H 'Accept: application/vnd.github.raw' > "$TMP/raw" 2>/dev/null || continue content_row "$repo" "$ref" "$f" + elif [ -n "$dotgithub" ] && [ "$f" = claude-standards.md ] && grep -qx claude.yml <<<"$files"; then + echo "CONTENT $repo@$ref $f DRIFT missing" fi done } diff --git a/tools/fleet-wave.sh b/tools/fleet-wave.sh index deab38a..bfdb01e 100755 --- a/tools/fleet-wave.sh +++ b/tools/fleet-wave.sh @@ -15,7 +15,7 @@ # every kit file the branch already carries <- kit version; the PR template and the house # standards live at .github/, the rest at # .github/workflows/ -# pr-bonsai-link.yml <- also written wherever claude.yml is +# pr-bonsai-link.yml, claude-standards.md <- also written wherever claude.yml is # bonsai-status-sync.yml <- deleted if present (kit no longer ships it) # # Guards, in order: @@ -213,9 +213,12 @@ plan_and_push() { grep -qxFf <(printf '%s\n' "$kit_paths") <<<"$existing" \ || { echo " $repo@$branch: no kit file in the .github listings" >&2; exit 3; } + # Two files are installed beside claude.yml, not only refreshed where present: the Bonsai-link + # check and the house standards (whose CLAUDE.md import stays a per-repo edit — nothing outside + # .github/ is ever written). for f in "${FULL_FILES[@]}"; do grep -qxF "$(dest "$f")" <<<"$existing" \ - || { [ "$f" = pr-bonsai-link.yml ] && grep -qxF .github/workflows/claude.yml <<<"$existing"; } \ + || { case "$f" in pr-bonsai-link.yml|claude-standards.md) grep -qxF .github/workflows/claude.yml <<<"$existing" ;; *) false ;; esac; } \ || continue # errexit would abort on the cp's missing source anyway; this fails with a clear message and # exit 3 before the network fetch. If the kit dropped it on purpose it belongs in DELETE_FILES.