From f5c0889b7d85135c56041420485709324180ee25 Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Thu, 22 Oct 2026 06:58:50 +0200 Subject: [PATCH 1/2] Post-merge check of #328, #329 and private #40-#43: main checkout 50 commits behind, PR-create refusals cost 29 h (@orchestrator) - Lead time: #329 18 h 43 min, 10 h 35 min of it a refused gh pr create (double-quoted title); #328 and private #43 lost 10 h 53 min and 8 h 03 min the same way. - The main checkout, where every hook runs, is at f058b206 (#317), 50 commits behind origin/master: #329's guard changes are not in force. - The check of private #38 (worktree-retro-1021e, 1730e0fd) was left out of the morning report and the 04:11 pushes; unreviewed. - Private #42 was pushed without a review, beside #327; push questions gave no review status. - #327's rule is clear (stated twice); the gap is a check at push time. - Proposals: duties.py flags a stale main checkout; guard_push shows the review status on an attended push; a refused PR create may be redone in the form its reason names. Rule text 11,714 words (+51, all #329). Co-Authored-By: Claude Opus 5.5 --- docs/retrospectives/2026-10-22-merges-329.md | 160 +++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 docs/retrospectives/2026-10-22-merges-329.md diff --git a/docs/retrospectives/2026-10-22-merges-329.md b/docs/retrospectives/2026-10-22-merges-329.md new file mode 100644 index 00000000..8935d5a4 --- /dev/null +++ b/docs/retrospectives/2026-10-22-merges-329.md @@ -0,0 +1,160 @@ +# 2026-10-22 post-merge check: public #328 and #329, private #40 to #43 + +Times are UTC. Sources: `git log` on `origin/master` and `private/master`, +`gh pr view`, `.git/harness/reviews.jsonl` and `windows.jsonl`, `pmset -g +log`, the main session's transcript (`3b8a12b2-…jsonl`, "line N") and +`.claude/current-task/session.md`. What `2026-10-22-night.md` (PR #330) +records is pointed to, not repeated: the idle tail, the refusal table, the +push redone under a later grant, the defaults missing from the morning +report, #327's missing review. + +## 0. Duties owed + +`python3 tools/duties.py` at the start: "post-merge check (1 merge since +the last retrospective, 2026-10-22 04:24 UTC: #329)". #329 merged at +04:43:10 (`gh pr view 329`); this check was spawned at 04:49:08: **6 min**. +The recap in the main checkout also lists "push 2 retrospective branches +(worktree-retro-1021e, worktree-worktree-retro-1022b)"; for +`worktree-retro-1021e` see finding 3: owed since 20:37:10 yesterday, **8 h +18 min** at 04:55, 55 min of it with Ola back. + +## 1. Lead time + +| PR | Built | Waited | Of the wait, a refused `gh pr create` | Total | +|---|---|---|---|---| +| #329 harness: verdict line, redo after review, amends after `cd` | 7 h 18 min (spawn 10:00 to record 17:36) | 11 h 06 min | **10 h 35 min** (17:37:33 to 04:12:56) | 18 h 43 min | +| #328 check of #326 | 24 min (16:49 to 17:13) | 11 h 29 min | **10 h 53 min** (17:20:13 to 04:12:51) | 11 h 54 min | +| private #43 forcing evidence | 7 h 06 min (10:50 to 17:56) | 10 h 36 min | **8 h 03 min** (20:09:35 to 04:13:01) | 17 h 42 min | +| private #40 sun evaluator (89) | 2 h 23 min (design 01:18 to 03:41) | 50 min | none; Ola away to 04:00 | 3 h 13 min | +| private #41 snow-cover check | 4 min | 3 h 35 min | none; Ola away | 3 h 39 min | +| private #42 ideas note | 2 min | 3 h 47 min | none; Ola away | 3 h 49 min | + +`docs/retrospectives/lead_time.py 2026-10-21` agrees in decimal hours, but +counts from the first commit (#329: 10:18, not the 10:00 spawn) and dates +the merge from the queue's merge commit (04:28), 15 min before `mergedAt`. + +#329's build: design and two design reviews 52 min; **red step 4 h 03 min** +(`@tester` spawned 10:52:28, red `4e45ea0f` 14:55:18, 235 lines added); green 78 +min; 27 min from green to the code review's spawn; review 53 min. The Mac +slept on battery from 11:52 to 12:23 and a few minutes besides (`pmset -g +log`); the rest of the red step's length is not checked here. + +Three PRs lost **29 h 31 min** together to one cause: the away grant's form +for `gh pr create` (finding 1). + +## 2. Findings + +1. **The refusal named the wrong cause, the main session passed on an + unchecked one, and no route could redo the act.** The three opens were + refused for a double-quoted `--title`; the grant takes only single quotes + (`tools/push_grant.py`, `unexpandable`). The refusal says "the grant takes + only one bare command, every word written out (no variable, no &&, ;, + pipe, redirect or heredoc)" (line 20368): nothing about quotes. Probe, + on `origin/master` after #329: `unexpandable` returns False for `--title + "A b"` and True for `--title 'A b'`, and `NOT_BARE` is unchanged, so #329 + did not fix it. At 20:04:45 the main session told Ola "Away mode refused + to open their pull requests, which looks like a fault in the away check + itself" (line 22025): a cause not checked (`CLAUDE.md` §3, *Report only + finished, verified results*). It found the real cause at 20:10 (line + 22225), after Ola had left. Even then the rule forbade the redo: since + #329 a refused pull request is redone only when its sole reason is an + unreviewed head. The single-quote form is stated only in the main + session's memory and the h20 design. The proposal to name the form in the + refusal is Q-A of `2026-10-21-merge-lsm38.md`, which is itself unpushed + (finding 3). +2. **#329's harness changes are not in force: the main checkout is 50 + commits behind.** `/Users/skavhaug/projects/rasputin` is at `f058b206` + (#317, 2026-10-20 15:36); `git rev-list --count HEAD..origin/master` + gives 50. Its last fast-forward was 2026-10-20 16:06 (`git reflog + master`). `.claude/settings.json` runs every hook from + `$CLAUDE_PROJECT_DIR`, that checkout: `grep -c AMEND_FORM + .claude/hooks/guard_push.py` there gives 0, so the amend after `cd` and + the new refusal texts of #329 are inactive, and the main session's + `CLAUDE.md` is the one before #323 and #329. Twelve PRs (#318 to #329) + merged since. Nothing in the recap or `duties.py` says so. The rule + "after merging a change to `CLAUDE.md` or `.claude/agents/`, restart" + (`.claude/REQUIRED-READING.md`) assumes the checkout holds the merge. + Personas read their file from their own worktree (the brief says so), + which is cut from `origin/master`; the hooks and the main session's + instructions are not. +3. **A retrospective dropped from the morning.** The check of private #38 + (`worktree-retro-1021e`, `1730e0fd`, handed back 20:37:10) holds three + proposals; `session.md` line 65 has its `ASK OLA`. The morning report + (line 24843) names only the check of #39, and the 04:11 pushes left it + out. It has no review either (`reviews.jsonl`: none on `1730e0fd`). The + recap listed it as a duty on every prompt. +4. **Two branches pushed without a review, in one batch.** Besides #327 + (recorded in `2026-10-22-night.md`), private #42 (`36f69487`, + `41100996`): an ideas note, a 123-line Python script and its JSON output, + no `@reviewer` spawn on `lsm-snowcover-2016` and no record in + `reviews.jsonl`. Both `ASK OLA` lines (session.md lines 77 and 79) gave no + review status, while the reviewed branches' lines named the verdict (line + 94). Ola's yes at 04:11 covered all four without that difference shown. +5. **A commit after the review on #40.** `1a53c9ce` (one line, the kill + record from `@tester`'s check) came after the recording commit + `4daa2b88`; `docs/increments/README.md` step 4 lets only the recording + commit follow a review. The `ASK OLA` line (94) disclosed it. Small, prose + only; noted for the rule, not as harm. +6. **`session.md` times.** Lines written about 04:45 say "05:xx UTC"; the + transcript stamps them 04:47 to 04:49. One hour off, in a file the next + check reads as a clock. + +Checked and in order: #329's record (design rounds 1 and 2, code review +round 1 on `01aa6c17`, APPROVED, in `reviews.jsonl` at 17:36:38); its head +`a4cb795e` is the recording commit only; #328's head `9e25721a` applies the +four wordings of the approved `cf64183d`; #40, #41 and #43 each carry an +approving record on their reviewed head; all four private PRs passed their +one check before the hand merge (`gh pr checks`). + +## 3. Is #327's rule clear enough? + +Yes. `.claude/REQUIRED-READING.md` says "`@reviewer` runs once on any branch +before its first push, whether or not the branch contains production code", +and `docs/increments/README.md` lists "`@reviewer` before every push" among +what is never dropped. The rule is stated twice and was still missed twice +in one batch (finding 4), so a third statement will not help. What is +missing is a check at the moment of the push: the away grant reads +`reviews.jsonl`, an attended push does not. Proposal 2. + +## 4. Lessons, recorded + +1. **(This check.) A merged harness change works only once the main + checkout holds it.** Hooks and the main session's `CLAUDE.md` come from + there, not from `origin/master` (finding 2). +2. **(This check.) A push question states the head's review: verdict and + sha, or "not reviewed".** Without it Ola cannot tell the reviewed + branches from the rest (finding 4). +3. **(This check.) Before telling Ola why a guard refused, read the + refusal's own check.** The cause given at 20:04 was a guess (finding 1). + +## 5. Proposals + +1. **Keep the main checkout current.** `tools/duties.py` gains a duty + "fast-forward the main checkout (N commits behind; hooks or rules + changed)" when `HEAD..origin/master` touches `.claude/`, `CLAUDE.md` or + `tools/`, so it shows in the recap, on every prompt and in Ola's status + line. The main session then runs `git pull --ff-only origin master` in + the main checkout, a ref write that needs Ola's yes. Evidence: finding 2. + Cost: about 12 lines in a governed tool, by day; no rule words. +2. **The push guard shows the review.** When `guard_push.py` asks before an + attended push, pull request or merge, its question adds "no approving + review recorded for " when `reviews.jsonl` holds none, reusing + `push_grant.review_of`. Evidence: finding 4, and section 3 of the night check (PR #330). + Cost: about 10 lines, governed, by day; no rule words. +3. **A refused pull request may be redone in the form its reason names.** + `NOT_BARE` names the single-quoted `--title`, and the redo sentence of + *Unattended mode* reads "a refused amend or pull request, only in the + form its reason names". Evidence: §1, 29 h 31 min across three PRs. + Supersedes Q-A of `2026-10-21-merge-lsm38.md`. Cost: about 4 rule words + and 2 lines in `tools/push_grant.py`. + +## 6. Rule text + +`python3 tools/rule_sizes.py`: **11,714 words, +51** since +`2026-10-22-merge-lsm39.md`, all from #329: `.claude/REQUIRED-READING.md` ++44, `.claude/agents/architect.md` +10, `CLAUDE.md` −3. Proposals 1 and 2 +add none; 3 adds about 4. Cut: `docs/increments/README.md`'s *Never +dropped* list restates "`@reviewer` before every push, Ola's yes for each +push" from `.claude/REQUIRED-READING.md`; replace both items with "the +approval boundary (`.claude/REQUIRED-READING.md`)" (−5 words). No anecdote +added to a rule file by #329. From 3575b3b87fae6d590e0aa14ab6d4883bf494669f Mon Sep 17 00:00:00 2001 From: Ola Skavhaug Date: Thu, 22 Oct 2026 07:05:13 +0200 Subject: [PATCH 2/2] Post-merge check of #329: review APPROVED WITH WORDINGS on f5c0889b (@reviewer), five wordings applied Co-Authored-By: Claude Opus 5.5 --- docs/retrospectives/2026-10-22-merges-329.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/docs/retrospectives/2026-10-22-merges-329.md b/docs/retrospectives/2026-10-22-merges-329.md index 8935d5a4..e4bfd172 100644 --- a/docs/retrospectives/2026-10-22-merges-329.md +++ b/docs/retrospectives/2026-10-22-merges-329.md @@ -56,14 +56,10 @@ for `gh pr create` (finding 1). to open their pull requests, which looks like a fault in the away check itself" (line 22025): a cause not checked (`CLAUDE.md` §3, *Report only finished, verified results*). It found the real cause at 20:10 (line - 22225), after Ola had left. Even then the rule forbade the redo: since - #329 a refused pull request is redone only when its sole reason is an - unreviewed head. The single-quote form is stated only in the main - session's memory and the h20 design. The proposal to name the form in the - refusal is Q-A of `2026-10-21-merge-lsm38.md`, which is itself unpushed + 22225), after Ola had left. Even then the rule forbade the redo: the rule in force then (`f058b206`) never redid "a refused push", and #329's rule still would not allow the redo. The single-quote form is stated only in the main session's memory and `docs/increments/h20-away-push-grants.md`; the docstring of `push_grant.unexpandable` also states the single-quote form. The proposal to name the form in the + refusal is proposal 1 of `2026-10-21-merge-lsm38.md`, which is itself unpushed (finding 3). -2. **#329's harness changes are not in force: the main checkout is 50 - commits behind.** `/Users/skavhaug/projects/rasputin` is at `f058b206` +2. **#329's harness changes were not in force until 04:59:57 UTC: the main checkout was 50 commits behind.** `/Users/skavhaug/projects/rasputin` is at `f058b206` (#317, 2026-10-20 15:36); `git rev-list --count HEAD..origin/master` gives 50. Its last fast-forward was 2026-10-20 16:06 (`git reflog master`). `.claude/settings.json` runs every hook from @@ -77,6 +73,7 @@ for `gh pr create` (finding 1). Personas read their file from their own worktree (the brief says so), which is cut from `origin/master`; the hooks and the main session's instructions are not. + Resolved after this check was written: with Ola's yes the main checkout was fast-forwarded to `2818da34` at 04:59:57 UTC (`git reflog master`), which brought in #318 to #329. Proposal 1 stands, so it does not happen again. 3. **A retrospective dropped from the morning.** The check of private #38 (`worktree-retro-1021e`, `1730e0fd`, handed back 20:37:10) holds three proposals; `session.md` line 65 has its `ASK OLA`. The morning report @@ -145,13 +142,12 @@ missing is a check at the moment of the push: the away grant reads `NOT_BARE` names the single-quoted `--title`, and the redo sentence of *Unattended mode* reads "a refused amend or pull request, only in the form its reason names". Evidence: §1, 29 h 31 min across three PRs. - Supersedes Q-A of `2026-10-21-merge-lsm38.md`. Cost: about 4 rule words + Supersedes proposal 1 of `2026-10-21-merge-lsm38.md`. Cost: about 4 rule words and 2 lines in `tools/push_grant.py`. ## 6. Rule text -`python3 tools/rule_sizes.py`: **11,714 words, +51** since -`2026-10-22-merge-lsm39.md`, all from #329: `.claude/REQUIRED-READING.md` +`python3 tools/rule_sizes.py`: **11,714 words, +51** since `2026-10-22-night.md` (11,663), all from #329: `.claude/REQUIRED-READING.md` +44, `.claude/agents/architect.md` +10, `CLAUDE.md` −3. Proposals 1 and 2 add none; 3 adds about 4. Cut: `docs/increments/README.md`'s *Never dropped* list restates "`@reviewer` before every push, Ola's yes for each