From 3eb4de0e4e9a256e47d488df7b26ff76663ae146 Mon Sep 17 00:00:00 2001 From: Topi Jarvinen Date: Mon, 27 Jul 2026 09:31:32 +0300 Subject: [PATCH] docs: graduate the friction-log inbox to GitHub Issues (#70-#77) Thirteen un-graduated entries triaged: twelve graduated into eight issues, one recorded a measurement with "No change proposed". Four of the eight each merge two entries recorded on separate days, because the repeat is the evidence: the closing-keyword trap (three occurrences), the archive sweep's relative cross-references (two), the doc-budget remedy no-op (three), and the review lens's wrong-ref worktree (nine of nine). Sweep was narrowed by a frozen-inbox snapshot taken at draft time, so an entry added mid-session could not be archived unfiled. Nothing was added during the window: all three blocks byte-matched the snapshot. docs/kit-friction-log.md: 190 -> 50 lines, back under its 150 budget. --- docs/kit-friction-log-archive.md | 186 +++++++++++++++++++++++++++ docs/kit-friction-log.md | 214 ++++++------------------------- 2 files changed, 223 insertions(+), 177 deletions(-) diff --git a/docs/kit-friction-log-archive.md b/docs/kit-friction-log-archive.md index 1608616..b34335f 100644 --- a/docs/kit-friction-log-archive.md +++ b/docs/kit-friction-log-archive.md @@ -3,6 +3,192 @@ Graduated friction entries live here after they have been routed to the tracker (GitHub Issues on this repo) or promoted into a repeated-pattern rule. +## Graduated 2026-07-27 — GitHub Issues (#70–#77) + +Swept by the `triage-friction-log` workflow. Thirteen entries, fully accounted for: +**twelve graduated** into eight issues ([#70](https://github.com/topij/agentic-dev-kit/issues/70)–[#77](https://github.com/topij/agentic-dev-kit/issues/77)), +four of which each merge two entries recorded on separate days; **one** recorded a +measurement with *"No change proposed"*. All thirteen are kept below for the trail, +along with the prior graduation marker. + +### 2026-07-26 — Backlog migrated to GitHub Issues (#54–#56) + +The inbox was swept by the `triage-friction-log` workflow. Three entries graduated: + +- [#54](https://github.com/topij/agentic-dev-kit/issues/54) — every verification claim + must name the command that establishes it (supersedes the narrower "wrap-up should + fact-check the handoff" proposal). +- [#55](https://github.com/topij/agentic-dev-kit/issues/55) — `safety-critical-changes.md` + rule 1 should name a tightening threshold. +- [#56](https://github.com/topij/agentic-dev-kit/issues/56) — removing a mechanism + requires enumerating what it was rejecting. + +Three further entries needed no ticket: their proposed fixes had **already shipped** in +PR #31 — the panel contract now requires lenses to execute and mutation-test (contract +items 4–5), rule 3 carries the blast-radius stopping criterion, and contract item 7 +mandates isolated worktrees. + +The remaining five: four were already tagged with an issue id (#10, #18, #19, #33), and one +recorded a guard working correctly with *"No change proposed"*. Eleven entries in, eleven +accounted for. + +Everything above now lives in [`kit-friction-log-archive.md`](kit-friction-log-archive.md). + +### 2026-07-27 + +- **A mutation harness that restores outside `finally` leaves the repo mutated.** My + restore line was unreachable when the script died parsing pytest output (a bare + `python3` with no pytest returned no stdout, and `splitlines()[-1]` raised). The + working tree kept a live mutant until `git status` caught it. Neither existing warning + covers it: `#50` is about stale `.pyc`, and `fallback-review-panel.md` contract item 5 + warns only about **false kills** from a checksum/drift test (it does not mention + `.pyc` at all). Both frame the hazard as "the result may be wrong"; neither says the + repo may be left broken. **H** — + proposed fix: contract item 5 should say the restore belongs in a `finally` (or a + git-clean check) and that any harness must verify the tree is clean *after* it exits, + not only after a successful run. +- **The closing-keyword trap fired again, because I weakened a correct rule on the + strength of an experiment that did not test what I thought.** `#61` was closed by the + squash-merge of `#68` and reopened by hand. + + **What is actually measured** — three data points, with their confounds stated, + because two earlier write-ups of this stated more than the data supports: + + | artifact | form | outcome | + | --- | --- | --- | + | `#68` body | `Closes #61` inside a **fenced block** | inert (`closingIssuesReferences: []`) | + | `9c6ab3a` commit message | `Closes #61` in an **inline** span | **fired** — closed `#61` | + | `#63` body | `Does NOT close #60` **and** a plain `fix #60` | fired; which one is **not isolated** | + + **What is NOT measured, though I twice wrote as if it were:** whether an *inline* span + is inert in a PR body. The two backtick data points differ in **two** variables at once + — fenced-vs-inline *and* body-vs-commit — so "GitHub excludes inline code spans" and + "fenced blocks are inert everywhere, inline spans fire everywhere" fit the evidence + equally well. Under the second reading, the fix I proposed last round (strip inline + code from a PR body before checking) reopens the exact hole that closed `#61`. + + Likewise the *negation* hypothesis: `#63`'s body carries a plain, non-negated `fix #60` + alongside the negated mention, so a check that only flags keywords **near a negation** + would have passed that body clean. + + **H** — **stop deriving a mechanism; take the conservative rule.** Three attempts to + state this precisely have each been wrong, which is rule 1's threshold. The original + 2026-07-26 rule below — never write a closing keyword adjacent to an issue number you + do not intend to close, in any form, on any surface — would have prevented all three + incidents; the measurement talked me *out* of a correct rule. It stands as written, and + the proposed check follows it rather than any theory of markdown: + + - flag **every** match on **every** surface — PR body, every commit message, and the + squash message — with **no stripping** of code spans or fenced blocks; + - cover the forms the naive regex misses and GitHub honours: `owner/repo#61`, a full + issue URL, and `Closes: #61` (a colon, not whitespace); + - check the **squash message at merge time** specifically. It is composed *after* the + PR body was reviewed, and nothing reviews it — verified: `9c6ab3a`'s message matches + neither `#68`'s title, its body, nor any of its three branch commits, and no hook in + `scripts/` inspects commit messages at all; + - the operator confirms each match. Over-firing is the acceptable failure here. + + Occurrence count, corrected: **three occurrences across two sessions** (`#63` and `#64` + were the same session; `9c6ab3a` is this one). +- **A review bot with an incremental model keeps a stale review across a rewrite.** + CodeRabbit reviewed the pre-split head, then declined `@coderabbitai review` + ("does not re-review already reviewed commits") and hit its Fair Usage limit on + `@coderabbitai full review`. The PR was force-pushed and then substantially rewritten, + so the only bot review covered code that no longer existed. `pr_watch`'s + `bots_behind_head` recorded it correctly — the friction is that **nothing warns at + push time**, when re-requesting is still cheap. **M** — proposed fix: have `pr-watch` + surface `covers_head: false` as a distinct, louder line right after a push that + changes the diff shape, rather than only at receipt time. Related: `#27`, `#44`. +- **The panel's worth is disjointness, and this run measured it.** Round 1: the + adversarial lens found the enclosing-repo and `GIT_DIR` regressions; the correctness + lens independently found the `~`-expansion disagreement between `init.sh` and the new + detector. Almost no overlap. Round 2's correctness lens then found the *misattributed + defect* — a false sentence in the commit message written to describe round 1's own + outcome, which could not have existed when round 1 ran. **No change proposed** — + recording it because `fallback-review-panel.md` argues disjointness from one prior + data point, and this is a second, stronger one worth citing there. Note the honest + framing of what the panel beat: the four regressions were missed by CI, by the suite + **as it then stood**, and by the mutation run **at that head** — not by the 372-test + suite quoted elsewhere, which only exists *because* those findings were fixed. +- **The archive sweep broke a cross-reference again — and this time it broke one the + PREVIOUS sweep had written.** The 2026-07-26 entry below reports the sweep orphaning + *"see the Phase 3b block above"*. Today's sweep moved Phase 3b itself into the history + file, so the surviving text — *"see the Phase 3b block, still live in + kit-handoff.md"* — became false in a second way: the target is now in the very file + the sentence sits in. Fixed by hand in this commit. **M** — the entry below proposes + warning on `(above|below)`-style references in *moved* blocks; this instance shows the + warning must also cover references **into** a moved block from anywhere in either + document, since the block that moved was the reference's *target*, not its location. + Same family as `#53`. +- **The doc-budget remedy no-op reproduced a third time** (see the 2026-07-26 entry + below). `check_doc_budget` reported 421/400; the remedy it names printed *"nothing to + move: 5 session block(s) <= --keep 6"*; `--keep 4` was needed and found by guessing. + **No new fix proposed** — recording the third occurrence. The entry below already + records two; this makes it three, two of them in *this* repo, which is a pattern + rather than a run of bad luck. +- **A review lens's isolated worktree pointed at the wrong ref again — 5 of 5 this + session, 9 of 9 across two.** (Last session's entry below records 4 of 4; this session + ran 2 lenses × 2 rounds on `#65` plus 1 on `#68`.) Every lens detected it and cloned + the target itself, because the prompt required reporting the path and diff stat. + **H** — proposed fix: this is no longer a + caveat, it is the default behaviour. Contract item 7 should stop saying "verify" and + start saying "assume the worktree is wrong; clone the target yourself first", with the + path/diff-stat report as a required field of the lens output (the existing 2026-07-26 + entry proposed the report; this one is about inverting the default). + +### 2026-07-26 + +- **The doc-budget remedy does not fire at the default `--keep`.** `check_doc_budget` + measures **lines**; `archive_plan_sessions` keeps **blocks**. This handoff hit + 448/400 lines with 5 blocks, so the remedy the warning names printed *"nothing to + move: 5 session block(s) <= --keep 6"* and the file stayed over budget. The agent has + to guess a `--keep` and re-run until the line count drops — which is exactly the + manual fiddling the deterministic engine exists to remove. **M** — proposed fix: give + `archive_plan_sessions` a `--target-lines` mode (sweep oldest-first until under + budget), or have `check_doc_budget`'s remedy string compute and name the `--keep` that + would work. Reproduced twice this session (kit and cs-toolkit). +- **A runtime's isolated worktree points at the session's base ref, not the PR head.** + All **four** review-lens launches this session (2 lenses × 2 rounds) landed on `main` + with an empty `git diff main...HEAD`. Every one detected it and cloned the real target + — because the launch prompt required them to *report the path and diff stat they saw*. + `fallback-review-panel.md` contract item 7 says to verify; what actually surfaced it + was making the report **mandatory output**. **H** — proposed fix: promote "state the + path reviewed and the diff stat" from advice to a required field of the lens report in + contract item 8, and say plainly that a lens which cannot show a non-empty diff has not + reviewed anything. A clean pass over an empty diff is indistinguishable from a real one. +- **`--record-review` has no way to record honest partial coverage.** It correctly + refuses a receipt bound to a stale head (`PR head changed during review`), but the only + alternative is a receipt claiming the current head — which the panel did not review. So + the honest choice is **no receipt at all**, and the audit trail then loses the fact that + a two-lens panel ran at all. **M** — proposed fix: allow recording against the reviewed + sha with the head-gap represented rather than rejected (the existing `bots_behind_head` + field is the precedent). Related: #32. +- **Negating a GitHub closing keyword still arms it.** A PR body was edited before merge + to retract a closure claim; the retraction read *"Does NOT close #60"*. GitHub matched + `close #60`, and merging closed an issue documenting an **unfixed** bug. Caught after + the fact and reopened. **M** — proposed fix: one line in the wrap-up / pr-watch + doctrine — never write a closing keyword adjacent to an issue number you do not intend + to close, even negated; write "#60 stays open". cs-toolkit's `CLAUDE.md` already carries + the Linear-side twin of this hazard. +- **The cockpit edited the shared tree while lenses were reviewing it.** A lens reported + the shared checkout changing mid-run (5 files, mtimes inside its window) and correctly + noted it was not the author. Its own review was unaffected — it worked from an isolated + clone — but it had to spend output distinguishing "concurrent editor" from "corruption". + Contract item 7 constrains the **lenses**; nothing constrains the **cockpit**. **M** — + proposed fix: add a cockpit-side clause — do not mutate the shared tree between + launching a panel and reading its findings. +- **The archive sweep breaks *relative* cross-references, and its docstring says it + doesn't.** `archive_plan_sessions.py:20` claims *"It only ever moves content — every + cross-reference (ticket ids, PR links, commit shas, …) is preserved."* Every kind it + enumerates is **absolute**. A relative one is not preserved: this session's sweep moved + the Phase 3a block into the history file while Phase 3b stayed live, orphaning + *"see the Phase 3b block above"* — the target is now in a different file. Caught by + CodeRabbit on the wrap-up PR, not by the sweep, which reported success. **M** — + proposed fix: have the sweep scan moved blocks for `(above|below)`-style references + and warn (not rewrite — rewriting is the class of surgery `init.sh`'s deleted marker + migration proved dangerous). Same family as #53, which is about the pointer the sweep + *writes*; this is about the references it *carries*. + ## Graduated 2026-07-26 — GitHub Issues (#54–#56) Swept by the `triage-friction-log` workflow. Eleven entries, fully accounted for: diff --git a/docs/kit-friction-log.md b/docs/kit-friction-log.md index 578843c..5bed851 100644 --- a/docs/kit-friction-log.md +++ b/docs/kit-friction-log.md @@ -11,180 +11,40 @@ > > Tracker board: https://github.com/topij/agentic-dev-kit/issues -## 2026-07-26 — Backlog migrated to GitHub Issues (#54–#56) - -The inbox was swept by the `triage-friction-log` workflow. Three entries graduated: - -- [#54](https://github.com/topij/agentic-dev-kit/issues/54) — every verification claim - must name the command that establishes it (supersedes the narrower "wrap-up should - fact-check the handoff" proposal). -- [#55](https://github.com/topij/agentic-dev-kit/issues/55) — `safety-critical-changes.md` - rule 1 should name a tightening threshold. -- [#56](https://github.com/topij/agentic-dev-kit/issues/56) — removing a mechanism - requires enumerating what it was rejecting. - -Three further entries needed no ticket: their proposed fixes had **already shipped** in -PR #31 — the panel contract now requires lenses to execute and mutation-test (contract -items 4–5), rule 3 carries the blast-radius stopping criterion, and contract item 7 -mandates isolated worktrees. - -The remaining five: four were already tagged with an issue id (#10, #18, #19, #33), and one -recorded a guard working correctly with *"No change proposed"*. Eleven entries in, eleven -accounted for. - -Everything above now lives in [`kit-friction-log-archive.md`](kit-friction-log-archive.md). - -## 2026-07-27 - -- **A mutation harness that restores outside `finally` leaves the repo mutated.** My - restore line was unreachable when the script died parsing pytest output (a bare - `python3` with no pytest returned no stdout, and `splitlines()[-1]` raised). The - working tree kept a live mutant until `git status` caught it. Neither existing warning - covers it: `#50` is about stale `.pyc`, and `fallback-review-panel.md` contract item 5 - warns only about **false kills** from a checksum/drift test (it does not mention - `.pyc` at all). Both frame the hazard as "the result may be wrong"; neither says the - repo may be left broken. **H** — - proposed fix: contract item 5 should say the restore belongs in a `finally` (or a - git-clean check) and that any harness must verify the tree is clean *after* it exits, - not only after a successful run. -- **The closing-keyword trap fired again, because I weakened a correct rule on the - strength of an experiment that did not test what I thought.** `#61` was closed by the - squash-merge of `#68` and reopened by hand. - - **What is actually measured** — three data points, with their confounds stated, - because two earlier write-ups of this stated more than the data supports: - - | artifact | form | outcome | - | --- | --- | --- | - | `#68` body | `Closes #61` inside a **fenced block** | inert (`closingIssuesReferences: []`) | - | `9c6ab3a` commit message | `Closes #61` in an **inline** span | **fired** — closed `#61` | - | `#63` body | `Does NOT close #60` **and** a plain `fix #60` | fired; which one is **not isolated** | - - **What is NOT measured, though I twice wrote as if it were:** whether an *inline* span - is inert in a PR body. The two backtick data points differ in **two** variables at once - — fenced-vs-inline *and* body-vs-commit — so "GitHub excludes inline code spans" and - "fenced blocks are inert everywhere, inline spans fire everywhere" fit the evidence - equally well. Under the second reading, the fix I proposed last round (strip inline - code from a PR body before checking) reopens the exact hole that closed `#61`. - - Likewise the *negation* hypothesis: `#63`'s body carries a plain, non-negated `fix #60` - alongside the negated mention, so a check that only flags keywords **near a negation** - would have passed that body clean. - - **H** — **stop deriving a mechanism; take the conservative rule.** Three attempts to - state this precisely have each been wrong, which is rule 1's threshold. The original - 2026-07-26 rule below — never write a closing keyword adjacent to an issue number you - do not intend to close, in any form, on any surface — would have prevented all three - incidents; the measurement talked me *out* of a correct rule. It stands as written, and - the proposed check follows it rather than any theory of markdown: - - - flag **every** match on **every** surface — PR body, every commit message, and the - squash message — with **no stripping** of code spans or fenced blocks; - - cover the forms the naive regex misses and GitHub honours: `owner/repo#61`, a full - issue URL, and `Closes: #61` (a colon, not whitespace); - - check the **squash message at merge time** specifically. It is composed *after* the - PR body was reviewed, and nothing reviews it — verified: `9c6ab3a`'s message matches - neither `#68`'s title, its body, nor any of its three branch commits, and no hook in - `scripts/` inspects commit messages at all; - - the operator confirms each match. Over-firing is the acceptable failure here. - - Occurrence count, corrected: **three occurrences across two sessions** (`#63` and `#64` - were the same session; `9c6ab3a` is this one). -- **A review bot with an incremental model keeps a stale review across a rewrite.** - CodeRabbit reviewed the pre-split head, then declined `@coderabbitai review` - ("does not re-review already reviewed commits") and hit its Fair Usage limit on - `@coderabbitai full review`. The PR was force-pushed and then substantially rewritten, - so the only bot review covered code that no longer existed. `pr_watch`'s - `bots_behind_head` recorded it correctly — the friction is that **nothing warns at - push time**, when re-requesting is still cheap. **M** — proposed fix: have `pr-watch` - surface `covers_head: false` as a distinct, louder line right after a push that - changes the diff shape, rather than only at receipt time. Related: `#27`, `#44`. -- **The panel's worth is disjointness, and this run measured it.** Round 1: the - adversarial lens found the enclosing-repo and `GIT_DIR` regressions; the correctness - lens independently found the `~`-expansion disagreement between `init.sh` and the new - detector. Almost no overlap. Round 2's correctness lens then found the *misattributed - defect* — a false sentence in the commit message written to describe round 1's own - outcome, which could not have existed when round 1 ran. **No change proposed** — - recording it because `fallback-review-panel.md` argues disjointness from one prior - data point, and this is a second, stronger one worth citing there. Note the honest - framing of what the panel beat: the four regressions were missed by CI, by the suite - **as it then stood**, and by the mutation run **at that head** — not by the 372-test - suite quoted elsewhere, which only exists *because* those findings were fixed. -- **The archive sweep broke a cross-reference again — and this time it broke one the - PREVIOUS sweep had written.** The 2026-07-26 entry below reports the sweep orphaning - *"see the Phase 3b block above"*. Today's sweep moved Phase 3b itself into the history - file, so the surviving text — *"see the Phase 3b block, still live in - kit-handoff.md"* — became false in a second way: the target is now in the very file - the sentence sits in. Fixed by hand in this commit. **M** — the entry below proposes - warning on `(above|below)`-style references in *moved* blocks; this instance shows the - warning must also cover references **into** a moved block from anywhere in either - document, since the block that moved was the reference's *target*, not its location. - Same family as `#53`. -- **The doc-budget remedy no-op reproduced a third time** (see the 2026-07-26 entry - below). `check_doc_budget` reported 421/400; the remedy it names printed *"nothing to - move: 5 session block(s) <= --keep 6"*; `--keep 4` was needed and found by guessing. - **No new fix proposed** — recording the third occurrence. The entry below already - records two; this makes it three, two of them in *this* repo, which is a pattern - rather than a run of bad luck. -- **A review lens's isolated worktree pointed at the wrong ref again — 5 of 5 this - session, 9 of 9 across two.** (Last session's entry below records 4 of 4; this session - ran 2 lenses × 2 rounds on `#65` plus 1 on `#68`.) Every lens detected it and cloned - the target itself, because the prompt required reporting the path and diff stat. - **H** — proposed fix: this is no longer a - caveat, it is the default behaviour. Contract item 7 should stop saying "verify" and - start saying "assume the worktree is wrong; clone the target yourself first", with the - path/diff-stat report as a required field of the lens output (the existing 2026-07-26 - entry proposed the report; this one is about inverting the default). - -## 2026-07-26 - -- **The doc-budget remedy does not fire at the default `--keep`.** `check_doc_budget` - measures **lines**; `archive_plan_sessions` keeps **blocks**. This handoff hit - 448/400 lines with 5 blocks, so the remedy the warning names printed *"nothing to - move: 5 session block(s) <= --keep 6"* and the file stayed over budget. The agent has - to guess a `--keep` and re-run until the line count drops — which is exactly the - manual fiddling the deterministic engine exists to remove. **M** — proposed fix: give - `archive_plan_sessions` a `--target-lines` mode (sweep oldest-first until under - budget), or have `check_doc_budget`'s remedy string compute and name the `--keep` that - would work. Reproduced twice this session (kit and cs-toolkit). -- **A runtime's isolated worktree points at the session's base ref, not the PR head.** - All **four** review-lens launches this session (2 lenses × 2 rounds) landed on `main` - with an empty `git diff main...HEAD`. Every one detected it and cloned the real target - — because the launch prompt required them to *report the path and diff stat they saw*. - `fallback-review-panel.md` contract item 7 says to verify; what actually surfaced it - was making the report **mandatory output**. **H** — proposed fix: promote "state the - path reviewed and the diff stat" from advice to a required field of the lens report in - contract item 8, and say plainly that a lens which cannot show a non-empty diff has not - reviewed anything. A clean pass over an empty diff is indistinguishable from a real one. -- **`--record-review` has no way to record honest partial coverage.** It correctly - refuses a receipt bound to a stale head (`PR head changed during review`), but the only - alternative is a receipt claiming the current head — which the panel did not review. So - the honest choice is **no receipt at all**, and the audit trail then loses the fact that - a two-lens panel ran at all. **M** — proposed fix: allow recording against the reviewed - sha with the head-gap represented rather than rejected (the existing `bots_behind_head` - field is the precedent). Related: #32. -- **Negating a GitHub closing keyword still arms it.** A PR body was edited before merge - to retract a closure claim; the retraction read *"Does NOT close #60"*. GitHub matched - `close #60`, and merging closed an issue documenting an **unfixed** bug. Caught after - the fact and reopened. **M** — proposed fix: one line in the wrap-up / pr-watch - doctrine — never write a closing keyword adjacent to an issue number you do not intend - to close, even negated; write "#60 stays open". cs-toolkit's `CLAUDE.md` already carries - the Linear-side twin of this hazard. -- **The cockpit edited the shared tree while lenses were reviewing it.** A lens reported - the shared checkout changing mid-run (5 files, mtimes inside its window) and correctly - noted it was not the author. Its own review was unaffected — it worked from an isolated - clone — but it had to spend output distinguishing "concurrent editor" from "corruption". - Contract item 7 constrains the **lenses**; nothing constrains the **cockpit**. **M** — - proposed fix: add a cockpit-side clause — do not mutate the shared tree between - launching a panel and reading its findings. -- **The archive sweep breaks *relative* cross-references, and its docstring says it - doesn't.** `archive_plan_sessions.py:20` claims *"It only ever moves content — every - cross-reference (ticket ids, PR links, commit shas, …) is preserved."* Every kind it - enumerates is **absolute**. A relative one is not preserved: this session's sweep moved - the Phase 3a block into the history file while Phase 3b stayed live, orphaning - *"see the Phase 3b block above"* — the target is now in a different file. Caught by - CodeRabbit on the wrap-up PR, not by the sweep, which reported success. **M** — - proposed fix: have the sweep scan moved blocks for `(above|below)`-style references - and warn (not rewrite — rewriting is the class of surgery `init.sh`'s deleted marker - migration proved dangerous). Same family as #53, which is about the pointer the sweep - *writes*; this is about the references it *carries*. +## 2026-07-27 — Backlog migrated to GitHub Issues (#70–#77) + +The inbox was swept by the `triage-friction-log` workflow. Thirteen entries in, +thirteen accounted for: **twelve graduated** into eight issues, **one** recorded a +measurement with *"No change proposed"*. + +Four issues each merge **two** entries recorded on separate days, because the repeat is +the evidence — splitting them would lose the occurrence count that made them +issue-shaped: + +- [#70](https://github.com/topij/agentic-dev-kit/issues/70) — a mutation harness that + restores outside `finally` leaves the repo mutated; the tree must be checked *after* + the harness exits, not only after a successful run. +- [#71](https://github.com/topij/agentic-dev-kit/issues/71) — build the + closing-keyword guard: every match, every surface, no stripping, and the squash + message checked at merge time. *Three occurrences across two sessions.* +- [#72](https://github.com/topij/agentic-dev-kit/issues/72) — `pr-watch` should warn at + push time when a bot review no longer covers head, not only at receipt time. +- [#73](https://github.com/topij/agentic-dev-kit/issues/73) — the archive sweep must + warn on relative cross-references in **both** directions. *Two occurrences; the second + broke a reference the first sweep had written.* +- [#74](https://github.com/topij/agentic-dev-kit/issues/74) — the doc-budget remedy is a + no-op at the default `--keep` (it measures lines, the sweep keeps blocks). *Three + occurrences, two in this repo.* +- [#75](https://github.com/topij/agentic-dev-kit/issues/75) — invert contract item 7: + assume the isolated worktree points at the wrong ref. *Nine of nine across two + sessions.* +- [#76](https://github.com/topij/agentic-dev-kit/issues/76) — `--record-review` cannot + record honest partial coverage, so the honest choice erases the trail. +- [#77](https://github.com/topij/agentic-dev-kit/issues/77) — nothing constrains the + cockpit from editing the shared tree while a panel reviews it. + +The thirteenth entry — the panel-disjointness measurement — carried *"No change +proposed"*: it is a second, stronger data point for the disjointness argument in +`fallback-review-panel.md`, which currently rests on one. + +Everything swept now lives in [`kit-friction-log-archive.md`](kit-friction-log-archive.md).