diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-31 18:15:03 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-31 18:15:03 -0500 |
| commit | 18f6c4c877940d740406b859f61a540fc48f13f5 (patch) | |
| tree | 0096f8e661a7c22af3037528e8923da63d757a42 /.ai/workflows/wrap-it-up.org | |
| parent | aee7793ddf1802c1574c2d23bf5acd823e7eba1e (diff) | |
| download | rulesets-18f6c4c877940d740406b859f61a540fc48f13f5.tar.gz rulesets-18f6c4c877940d740406b859f61a540fc48f13f5.zip | |
fix(workflows): commit template-sync churn deterministically
Phase A's startup rsync copies template updates from rulesets into each project's .ai/, but nothing committed that churn, so it accumulated across sessions and eventually blocked Phase A.0's auto-fast-forward (git won't ff a dirty tree). Two projects hit it the same day.
I added a Step 4.0 to wrap-it-up.org that commits the churn as its own chore commit before the session-work commit, guarded so it only auto-commits synced .ai paths matching rulesets canonical byte-for-byte and surfaces anything that doesn't. startup.org Phase C now surfaces leftover churn at session start as the crashed-session safety net. Both skip the rulesets repo, where .ai/ is a committed mirror.
I also moved four misplaced PROPERTIES drawers in todo.org (DONE tasks) from after the resolution prose to immediately under the CLOSED line, so org parses them as real drawers.
Diffstat (limited to '.ai/workflows/wrap-it-up.org')
| -rw-r--r-- | .ai/workflows/wrap-it-up.org | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/.ai/workflows/wrap-it-up.org b/.ai/workflows/wrap-it-up.org index 7fc86e4..593cb93 100644 --- a/.ai/workflows/wrap-it-up.org +++ b/.ai/workflows/wrap-it-up.org @@ -254,6 +254,46 @@ Skip the step entirely if the project doesn't use Linear (e.g. personal projects ** Step 4: Git commit + push +*** Step 4.0: Commit template-sync churn first (consuming projects) + +The startup workflow's Phase A rsyncs template updates from rulesets into this project's =.ai/= (=protocols.org=, =workflows/=, =scripts/=) every session that rulesets has advanced. Nothing commits that churn, so without this step it accumulates across sessions and eventually blocks Phase A.0's auto-fast-forward (git refuses to ff a dirty tree). Commit it here, as its own =chore:= commit, before the session-work commit — so the sync stays separate from what the session actually shipped and the tree ends clean. + +The guard is conservative: only auto-commit a dirty synced path when it matches the rulesets canonical byte-for-byte (a modified/new file equals canonical, or a deletion pairs with a file retired upstream). If any synced path is dirty but /doesn't/ match canonical — a local hand-edit to a file that's supposed to be sync-managed — surface it and don't auto-commit. Anything outside the three synced paths is untouched here; the normal Step 4 commit and the worktree-leftover step handle it. + +#+begin_src bash +# Skip in the rulesets repo itself: there .ai/ is a committed mirror of +# claude-templates/.ai/, kept in sync by the pre-commit hook and committed +# alongside template edits — not downstream sync churn. The presence of +# claude-templates/.ai/ in this repo is the tell. +if [ ! -d claude-templates/.ai ] && [ -d "$HOME/code/rulesets/claude-templates/.ai" ]; then + canon="$HOME/code/rulesets/claude-templates/.ai" + safe=1 + commitlist=() + while IFS= read -r line; do + f="${line:3}" # strip the 2-char status + space + rel="${f#.ai/}" + if [ -e "$f" ] && [ -e "$canon/$rel" ] && diff -q "$f" "$canon/$rel" >/dev/null 2>&1; then + commitlist+=("$f") # modified/new here, matches canonical + elif [ ! -e "$f" ] && [ ! -e "$canon/$rel" ]; then + commitlist+=("$f") # deleted here AND retired upstream + else + safe=0 # synced path dirty but != canonical + fi + done < <(git status --porcelain -- .ai/protocols.org .ai/workflows/ .ai/scripts/) + + if [ "$safe" -eq 1 ] && [ "${#commitlist[@]}" -gt 0 ]; then + git add -- "${commitlist[@]}" + git commit -q -m "chore: sync .ai tooling from templates" + echo "wrap-up: committed ${#commitlist[@]} synced .ai file(s) as a template-sync chore." + elif [ "$safe" -eq 0 ]; then + echo "wrap-up: synced .ai paths are dirty but not all match rulesets canonical — NOT auto-committing. Resolve manually:" + git status --porcelain -- .ai/protocols.org .ai/workflows/ .ai/scripts/ | sed 's/^/ /' + fi +fi +#+end_src + +The commit isn't pushed here — the push step below pushes the current branch, which carries both this chore commit and the session-work commit. A crashed session that never reaches wrap-up leaves the churn for the next startup, which surfaces it (see startup.org Phase C) so it never silently accumulates. + *** Review changes #+begin_src bash @@ -421,6 +461,7 @@ Before considering wrap-up complete: - [ ] Any orphan-planning-line warnings reviewed (fix or accept) - [ ] Inbox carries nothing but expected pipeline artifacts (=.gitkeep=, =lint-followups.org=, =PROCESSED-*= prefixes), OR each remaining handoff has an explicit deferral logged in the valediction - [ ] Linear Dev-Review sweep ran; any merged-PR tickets moved to Done or PM Acceptance (skip if project doesn't use Linear) +- [ ] Template-sync churn committed as its own =chore: sync .ai tooling from templates= (consuming projects only; skipped in rulesets), or surfaced if a synced path didn't match canonical - [ ] After wrap-up commit + push, =git status --short= is empty OR every remaining line has an explicit user-deferred decision logged in the valediction - [ ] Each leftover was investigated and the user saw a concrete resolution recommendation - [ ] Runtime artifacts added to =.gitignore=, follow-up commit pushed, =git status= re-verified |
