|
| 1 | +# Decisions — Living-docs regen automation |
| 2 | + |
| 3 | +**Status:** draft — awaiting decision on Approach. |
| 4 | +**Owner:** Patrick |
| 5 | + |
| 6 | +## Problem |
| 7 | + |
| 8 | +Almost every code-touching PR in attune-gui needs a follow-up |
| 9 | +"regenerate sidecar templates" commit added by hand. The |
| 10 | +sidecar generates living-docs templates (`.help/templates/`) |
| 11 | +and a Vite editor bundle (`editor-frontend/dist/`) that are |
| 12 | +**both committed** to the repo. When source changes, the |
| 13 | +generated artifacts drift, and a contributor (or assistant) |
| 14 | +has to remember to regenerate and commit them in a second |
| 15 | +step. |
| 16 | + |
| 17 | +Recent examples — every one of these triggered a manual |
| 18 | +follow-up regen commit: |
| 19 | + |
| 20 | +- `30cc722` fix(specs) → `ac6b552` regenerate |
| 21 | +- `d282f21` test(mcp) → `7e01b1a` regenerate |
| 22 | +- `1657c2f` feat(mcp) → `db4c562` regenerate |
| 23 | +- `05489c1` feat(mcp/tools) → regenerate |
| 24 | + |
| 25 | +Across ~10 PRs over recent weeks, that's ~10 extra commits of |
| 26 | +mechanical work. The risk is not "we forget once" — the risk |
| 27 | +is *we forget often enough that stale generated files become |
| 28 | +the norm* and the living-docs system silently drifts from the |
| 29 | +source of truth. |
| 30 | + |
| 31 | +## Current state (verified 2026-05-23) |
| 32 | + |
| 33 | +- No git hooks installed (`.pre-commit-config.yaml`, |
| 34 | + `.husky/`, `lefthook.yml` all absent). |
| 35 | +- A post-commit *webhook* exists (`scripts/install-living-docs-hook.sh`) |
| 36 | + but it only **notifies the sidecar to scan** — it does |
| 37 | + not regenerate. |
| 38 | +- CI (`.github/workflows/tests.yml`) runs ruff + pytest + |
| 39 | + vitest typecheck. **It does not check for stale generated |
| 40 | + artifacts.** |
| 41 | +- Generated artifacts are deterministic: |
| 42 | + - Editor bundle uses Vite content-hashed filenames, no |
| 43 | + timestamps in output. |
| 44 | + - Living-docs templates have `generated_at` (timestamp) |
| 45 | + and `source_hash` (content hash) frontmatter — `generated_at` |
| 46 | + is the only intrinsic source of churn, and it's not |
| 47 | + semantic. |
| 48 | + |
| 49 | +## Decision |
| 50 | + |
| 51 | +**Approach: CI-fail-if-stale + a single `make regen-all` target.** |
| 52 | + |
| 53 | +CI runs `make regen-all` then `git diff --exit-code`. If any |
| 54 | +generated file changed, CI fails with a clear message pointing |
| 55 | +the contributor at `make regen-all` to fix locally. No |
| 56 | +auto-commit, no bot, no pre-commit hook blocking local work. |
| 57 | + |
| 58 | +The contributor's loop becomes: |
| 59 | + |
| 60 | +```bash |
| 61 | +# After making source changes that touch templates or editor: |
| 62 | +make regen-all |
| 63 | +git add -u |
| 64 | +git commit -m "regenerate sidecar templates" |
| 65 | +``` |
| 66 | + |
| 67 | +If they forget, CI tells them in the same PR run. |
| 68 | + |
| 69 | +### Why this and not the alternatives |
| 70 | + |
| 71 | +| Option | Pro | Con | |
| 72 | +|---|---|---| |
| 73 | +| **Pre-commit hook** (chosen against) | Catches drift at commit time. | Blocks local commits; slow (editor build is ~7s + author maintain on top); requires Node + uv on every contributor's machine; regen is partly async — hard to wait for completion in a hook; many contributors disable hooks. | |
| 74 | +| **Post-merge bot commit** (chosen against) | Zero friction; always-correct main. | Needs bot write access to main; conflicts with stacked-PR rebases; adds noise (one bot commit per PR); audit trail becomes "Claude regenerated these" rather than "Patrick approved these"; opaque. | |
| 75 | +| **CI fail-if-stale** (chosen) | Visible; deterministic; no bot access; one command for contributors; aligns incentives — the PR author owns the regen. | Adds friction of one extra command + commit per affected PR. (Net win: it's the friction we already pay manually, but now it's enforced, so we stop forgetting.) | |
| 76 | +| **Auto-regen commit in CI** (chosen against) | No contributor friction. | Same bot-access concerns as post-merge; harder to attribute changes; stacked-PR conflict surface. | |
| 77 | + |
| 78 | +## Scope |
| 79 | + |
| 80 | +**In scope:** |
| 81 | + |
| 82 | +1. New `make regen-all` target that runs: |
| 83 | + - `make build-editor` (already exists at `Makefile:6-18`) |
| 84 | + - `attune-author maintain` for the sidecar living-docs corpus |
| 85 | + - Any other regen step we discover during implementation |
| 86 | +2. New CI job `regen-up-to-date` in `.github/workflows/tests.yml`: |
| 87 | + - Installs node + uv |
| 88 | + - Runs `make regen-all` |
| 89 | + - Runs `git diff --exit-code -- .help/ editor-frontend/dist/` (paths refined during impl) |
| 90 | + - On failure: prints a clear message with the fix command |
| 91 | +3. README update under `## Development` documenting the contract. |
| 92 | + |
| 93 | +**Out of scope:** |
| 94 | + |
| 95 | +- Pre-commit hooks (deliberately). |
| 96 | +- Any bot or automation that writes to main. |
| 97 | +- Migrating the post-commit-webhook scan trigger (separate concern). |
| 98 | +- Reducing the regen time itself (separate perf spec if it becomes painful). |
| 99 | + |
| 100 | +## Acceptance criteria |
| 101 | + |
| 102 | +- `make regen-all` is a single command that produces a clean |
| 103 | + working tree on a fresh checkout with no source changes. |
| 104 | +- CI fails a PR that modifies source-of-truth files without |
| 105 | + regenerating, and the failure message names the fix command. |
| 106 | +- README documents `make regen-all` in the existing |
| 107 | + `## Development` section. |
| 108 | +- No new commits land on `main` from any bot account. |
| 109 | + |
| 110 | +## Open questions |
| 111 | + |
| 112 | +1. What's the exact `attune-author maintain` invocation for |
| 113 | + this repo's federated config? (Phase 1 task.) |
| 114 | +2. Should we gate the CI check behind a label (e.g. only run |
| 115 | + `regen-up-to-date` when paths matching `sidecar/**` or |
| 116 | + `editor-frontend/src/**` change) to keep PR feedback fast |
| 117 | + for docs-only or test-only changes? Default: yes, conditional. |
| 118 | +3. CI runtime budget — `make build-editor` is ~7s; `attune-author maintain` |
| 119 | + time unknown. If the combined regen-check exceeds 60s, revisit |
| 120 | + whether to split it. |
| 121 | + |
| 122 | +## Phase outline (when this spec is approved) |
| 123 | + |
| 124 | +- **Phase 1** — Inventory the exact regen commands and write `make regen-all`. |
| 125 | +- **Phase 2** — Add the CI job, gated on relevant paths; verify it fails |
| 126 | + intentionally on a stale-artifact PR before flipping it to required. |
| 127 | +- **Phase 3** — README update; make the contract official. |
0 commit comments