Skip to content

test: detach the suite from the ambient repo's config (closes #48)#49

Merged
topij merged 4 commits into
mainfrom
fix/tests-must-not-read-ambient-config
Jul 26, 2026
Merged

test: detach the suite from the ambient repo's config (closes #48)#49
topij merged 4 commits into
mainfrom
fix/tests-must-not-read-ambient-config

Conversation

@topij

@topij topij commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Closes #48. Found by a real adopter making a one-value config change (topij/OpenKitchen#257), which it blocked.

The bug

pr_watch resolves its review config at import time into module-level constants:

_REVIEW_CONFIG = _load_review_config()   # reads the ambient repo's config
_REVIEW_BOTS   = _REVIEW_CONFIG.bots

_load_pr_watch() executes the module, so ~32 tests exercised bot behaviour through those constants and silently required the surrounding repo to configure a review bot.

OpenKitchen has CodeRabbit installed but on a plan where it never returns a verdict (#45), so review.bots: [] is the truthful value. Setting it turned 32 kit-owned tests red — on assertions about engine behaviour. Reproduced here: on main, review.bots: [] gives 33 failed (the 33rd is a followup-hook test OpenKitchen does not vendor); on this branch, 323 passed.

Why it mattered

The kit's founding invariant is "engines are kit-owned; config is adopter-owned." The converse has to hold too, and didn't: a legitimate adopter value broke kit-owned tests, and the only in-repo fix was editing them — which makes them un-upgradable, the exact failure the kit exists to prevent.

It also meant kit test results were not portable: a green suite here said nothing about a green suite in an adopter, because part of what the suite tested was the adopter's configuration.

The fix

_load_pr_watch() pins the loaded module to the engine defaults, via _load_review_config's FileNotFoundError branch (returns defaults, stays quiet). _load_pr_watch(pin_defaults=False) exists for tests whose subject is the ambient config. Pinning is the default because an ambient-reading test fails invisibly here and only reddens in somebody else's repo.

Review history — three rounds, and the first two cuts were wrong

CodeRabbit reviewed the first commit (one valid nitpick, applied), then went rate-limited — detected on both the comment and status-check surfaces, which is exactly the #23 machinery working. So the fallback panel carried rounds 2 and 3.

Round 1 found two HIGH issues, both real:

  • The regression test guarded 2 of 7 pinned fields. A per-line sweep showed _REVIEW_BOTS — the field that caused the bug — was unguarded: deleting its pin left the suite green here while restoring the entire failure for an adopter.
  • The pinning silently gutted test_shipped_config_preserves_the_engine_defaults_behavior, which took its module from the pinned loader and so asserted that the defaults classify like the defaults. It was not even one of the 32 broken tests — it was healthy coverage taken out as collateral.

Round 2 found the fix to round 1 was also wrong:

  • The new guard-on-the-guard failed open: it computed its skip predicate using the function under test, so a broken opt-out was indistinguishable from a coinciding config — and it skipped. Changing if pin_defaults: to if True: gave "322 passed, 1 skipped" while the regression its docstring claims to prevent became reachable.
  • "EVERY config-derived global" was a hand-written list of 7; adding an 8th passed.
  • The marker axes still reddened a legitimate adopter — round 1 closed the bots axis only.
  • Two assertions that could not fail, and a test name that outlived its body.

Corrections to earlier versions of this description

Recorded rather than quietly edited, because the same drift recurred:

  • An earlier body claimed "the one test that deliberately reads the shipped config is untouched." That was false — see round 1 above. It survived one round of corrections because I fixed the commit message and not this body, which is the artifact a merger actually reads.
  • An earlier commit said corrupting the shipped config went from "7 tests fail" to green. The real number is at most 2, and three of six corruption styles fail none. The direction held; the magnitude was invented, and it had been baked into a source docstring.
  • Earlier counts said 321 and listed 3 config shapes; both are stale below.

Verification — the property, not a green run

All runs with PYTHONDONTWRITEBYTECODE=1 and __pycache__ cleared, per #50: during this PR a same-length mutation left stale bytecode that survived a git-clean restore, so the suite ran mutant code for several minutes.

config shape result
shipped 323 passed
review.bots: [] 323 passed
review.require_ci: false 323 passed
review.informational_checks: [] 323 passed
review.noise_markers: [] 322 passed, 1 skipped
coherent no-bot adopter (all axes empty) 322 passed, 1 skipped

Per-field mutation sweep of _pin_engine_defaults: 7/7 killed. Adding an 8th ReviewConfig field now fails until the map is extended.

Known residual, deliberately not fixed

Removing review.fallback_panel, or the whole review: section, fails test_portability.py::test_init_migrates_the_previous_runtime_schema. That test's subject is the shipped config versus migration output, and a config lacking those keys is a pre-init.sh state that init.sh repairs — a different question from "a legitimate steady-state adopter config must not break kit tests".

Scope

Tests only — no engine, no config, no doctrine. kit_doctor: 24 unchanged, 0 differ, 0 missing, 0 unknown (test files are outside KIT_OWNED, which is #47's subject).

Unblocks

topij/OpenKitchen#257, once these tests are refreshed there.

https://claude.ai/code/session_01V27wAgECMEApqAvmJMFyqm

Summary by CodeRabbit

  • Tests
    • Improved test reliability by isolating tests from ambient repository configuration.
    • Added coverage for configured review bots, panel-based reminders, and fallback behavior.
    • Strengthened validation of CI requirements, default settings, and optional configuration values.
    • Reduced dependence on fixed branch names and specific configuration literals.

Closes #48.

`pr_watch` resolves its review config at IMPORT time into module-level
constants, so a module loaded by `_load_pr_watch()` inherited whatever
`config/dev-model.yaml` the surrounding repo happened to have. About 32
tests then exercised bot behaviour through those constants, carrying an
undeclared precondition: the ambient repo must configure a review bot.

That broke a real adopter. OpenKitchen has CodeRabbit installed but on a
plan where it never returns a verdict, so `review.bots: []` is the
truthful value — and setting it turned 32 kit-owned tests red on
assertions about ENGINE behaviour that have nothing to do with config:

    review.bots: []            ->  32 failed, 127 passed
    review.bots: [coderabbit]  ->  159 passed

Same tree, one config value. The kit's invariant is that engines are
kit-owned and config is adopter-owned; the converse has to hold too —
a legitimate adopter value must never break a kit-owned test. It also
meant kit test results were not portable: a green suite here said
nothing about a green suite in an adopter, because part of what the
suite tested was the adopter's configuration.

`_load_pr_watch()` now pins the loaded module to the ENGINE defaults, by
taking `_load_review_config`'s FileNotFoundError branch (which returns
defaults and stays deliberately quiet — the standalone-engine state).
Tests that want other config still pass their own path; the one test
that deliberately reads the SHIPPED config is untouched.

Verified the actual property rather than just a green run — the full
suite under three different values of review.bots:

    [coderabbit]            321 passed
    []                      321 passed
    [otherbot, thirdbot]    321 passed

Two further points worth recording:

`test_the_loaded_engine_is_detached_from_the_ambient_repo_config` pins
this, and it asserts on `noise_markers` rather than `bots` deliberately.
In THIS repo the shipped `bots` and `_DEFAULT_REVIEW_BOTS` are both
`coderabbit`, so a bots-only assertion would pass whether or not the
pinning happens — a test that cannot fail, which is worse than none.
`noise_markers` differ (5 shipped vs 7 default) and do detect it.
Mutation-checked: removing the pinning call fails this test from inside
this repo, with the shipped config.

`test_reminder_names_configured_bots_not_a_hardcoded_bot` had the same
disease in a sharper form. It asserted `"coderabbit" in reminder` while
this repo's config says `coderabbit` — so it could not fail for the
reason it named: a hardcoded literal satisfied it exactly as well as a
config read. It now injects a sentinel bot name that appears in no
config and no source file. Mutation-checked: hardcoding the bot name in
`build_reminder` fails the new test and PASSES the old assertion.

Claude-Session: https://claude.ai/code/session_01V27wAgECMEApqAvmJMFyqm
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 42a2548e-e2c8-4a5f-be2d-0dbb2a255c95

📥 Commits

Reviewing files that changed from the base of the PR and between bcb4f5e and 29da37d.

📒 Files selected for processing (3)
  • scripts/tests/test_kitconfig.py
  • scripts/tests/test_pr_followup_hook.py
  • scripts/tests/test_pr_watch.py

📝 Walkthrough

Walkthrough

Tests now isolate review behavior from ambient repository configuration by pinning dynamically loaded pr_watch defaults, injecting deterministic follow-up reminder configuration, and relaxing assertions that depended on fixed shipped-config values.

Changes

Configuration-isolated test coverage

Layer / File(s) Summary
Pin dynamically loaded engine defaults
scripts/tests/test_pr_watch.py
The test loader optionally pins all config-derived engine constants to defaults, verifies pinning and opt-out behavior, and validates require_ci-driven check summaries.
Inject deterministic follow-up reminder configuration
scripts/tests/test_pr_followup_hook.py
Reminder tests inject sentinel bots, panels, and lenses, then assert configured wording appears without ambient bot names or degraded command wording.
Relax shipped configuration assumptions
scripts/tests/test_kitconfig.py
Configuration tests accept any non-empty protected branch and conditionally validate unavailable markers while preserving marker-list disjointness.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly captures the main change: detaching the test suite from ambient repository config.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/tests-must-not-read-ambient-config

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
scripts/tests/test_pr_followup_hook.py (1)

126-130: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep the mocked loader result within its declared contract.

scripts/hooks/pr_followup_hook.py:48-102 returns lists and a string panel_source, but this fixture supplies tuples and None. Use valid list/string placeholders so future reminder changes cannot fail this bot-isolation test for unrelated type-shape reasons.

Proposed fix
-        lambda: (("zzz-sentinel-bot",), "/code-review", "scripts", (), None),
+        lambda: (["zzz-sentinel-bot"], "/code-review", "scripts", [], "test-source"),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/tests/test_pr_followup_hook.py` around lines 126 - 130, Update the
_load_review_config mock in the test to return the declared contract shape:
lists for the bot and reviewer collections and a string placeholder for
panel_source, while preserving the existing sentinel bot and other fixture
values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@scripts/tests/test_pr_followup_hook.py`:
- Around line 126-130: Update the _load_review_config mock in the test to return
the declared contract shape: lists for the bot and reviewer collections and a
string placeholder for panel_source, while preserving the existing sentinel bot
and other fixture values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0c86bf7c-c116-4c38-a099-b36b92af082d

📥 Commits

Reviewing files that changed from the base of the PR and between 5165f8a and bcb4f5e.

📒 Files selected for processing (2)
  • scripts/tests/test_pr_followup_hook.py
  • scripts/tests/test_pr_watch.py

CodeRabbit review on #49. Valid, and checked against the source rather
than taken on trust.

The mock returned `(tuple, str, str, tuple, None)` where
`_load_review_config` declares
`tuple[list[str], str, str, list[str], str]`.

The `None` is the part that matters: `panel_source` is type-and-blank
guarded on the success path AND defaulted to
`_DEFAULT_PANEL_RECEIPT_SOURCE` on the except path, so `None` is not a
value the real function can return. A mock returning one can mask a
type-shape bug rather than expose it — if `build_reminder` later grew a
`panel_source.strip()`, the mock would diverge from reality in whichever
direction happened to be convenient.

Suite still 321 passed, and the mutation check still holds: hardcoding
the bot name in `build_reminder` fails this test.

Claude-Session: https://claude.ai/code/session_01V27wAgECMEApqAvmJMFyqm
@topij

topij commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

Thanks — valid finding, applied in 75306b0.

Checked it against the source rather than taking it on trust, and the substance holds. _load_review_config declares tuple[list[str], str, str, list[str], str], and panel_source cannot be None: it is type-and-blank guarded on the success path and defaulted to _DEFAULT_PANEL_RECEIPT_SOURCE on the except path. So the mock was returning a value the real function has no way to produce — which is the failure mode worth caring about here, more than the tuple/list distinction: a mock outside its contract can mask a type-shape bug rather than expose it.

Used "fallback:test-panel" rather than "test-source" so the placeholder looks like a real receipt_source value, and left a comment explaining why the shape matters — this file is now the second place in the suite where a fixture's fidelity is load-bearing.

Re-verified after the change:

  • suite: 321 passed
  • the mutation check still holds — hardcoding the bot name in build_reminder fails this test

Worth noting for the record: this was the one substantive finding on the PR, and it landed on the fixture rather than the change itself — which is a reasonable outcome for a diff whose entire purpose is making fixtures stop depending on ambient state.

Two HIGH findings from the fallback panel on this PR. Both were real and
both were mine.

1. THE REGRESSION TEST GUARDED THE WRONG FIELDS.

A per-line mutation sweep showed 5 of the 7 pins were unguarded —
including `_REVIEW_BOTS`, the one field whose ambient value caused the
bug this PR exists to fix. Deleting its pin left the suite green here
while restoring the entire failure for an adopter with `review.bots: []`.

The cause is the mistake the test's own docstring half-diagnosed: an
"equals the defaults" assertion cannot fail for any field where this
repo's config already agrees with the default. I spotted that for `bots`
and wrongly concluded `noise_markers` compensated — it only detects
removal of the whole call, not per-field decay.

`test_every_config_derived_global_is_pinned` now seeds every global with
a sentinel the defaults cannot equal, then pins. Each assertion fails if
and only if its own line is missing, whatever the ambient config says.
Mutation-swept all 7: 7/7 killed.

2. THE PINNING SILENTLY VOIDED THE SHIPPED-CONFIG TEST.

`test_shipped_config_preserves_the_engine_defaults_behavior` took its
module from `_load_pr_watch()`, so after pinning it asserted that the
defaults classify like the defaults. Corrupting the shipped
`noise_markers` went from "7 tests fail" to "the whole suite is green".

Worse, it was NOT one of the 32 broken tests — it passed on main under
`review.bots: []`. It was healthy coverage taken out as collateral,
while three places in the artifact (both commit messages and the
in-diff docstring) asserted it was untouched.

`_load_pr_watch(pin_defaults=False)` now exists for tests whose SUBJECT
is the ambient config. Pinning stays the default because the failure
mode of an ambient-reading test is invisible here and only appears in
somebody else's repo.

Also fixed, same class:

  - `test_require_ci_defaults_to_the_configured_value` was vacuous under
    pinning, and its `is True` literal failed a legitimate
    `require_ci: false`. Now asserts the wiring, not the value.
  - the shipped-config test's `informational_checks`/`require_ci`
    literals, same treatment.
  - `test_reminder_points_at_the_panel_not_the_degraded_one_lens_mode`
    took a `monkeypatch` it never used and read ambient config; it went
    red when `review.fallback_panel` was absent, a supported state.
  - the sentinel bot test only covered the DEGRADED branch (no lenses),
    so a bot literal hardcoded into the PANEL wording survived. Added
    `test_reminder_names_configured_bots_on_the_panel_branch_too`.
  - `test_kitconfig.py` pinned `vcs.protected_branch == "main"`; a repo
    whose trunk is `master` is a supported configuration.

Acceptance is the property, not a green run — full suite under four
config shapes, all 323 passed:

    shipped                      323
    review.bots: []              323
    review.require_ci: false     323
    review.informational_checks: []  323

Claude-Session: https://claude.ai/code/session_01V27wAgECMEApqAvmJMFyqm
Round 2 of the fallback panel. Both lenses found real things; the
adversarial one found the sharpest.

THE GUARD ON THE GUARD FAILED OPEN.

`test_the_default_loader_pins_but_the_opt_out_does_not` computed its skip
predicate USING the function under test. So "the opt-out is broken" and
"this repo's config equals the defaults" were indistinguishable to it —
and it resolved that toward skip. Demonstrated: changing `if pin_defaults:`
to `if True:` gives "322 passed, 1 skipped" while the exact regression the
docstring claims to prevent becomes fully reachable. It now reads the
config independently of `_load_pr_watch`, so a broken opt-out FAILS and
only a genuinely indistinguishable config skips.

"EVERY" WAS A HAND-WRITTEN LIST OF SEVEN.

Proven by adding an 8th config-derived global and watching the test pass.
The field map is now checked against `ReviewConfig._fields`, so adding a
field fails the test until the map is extended.

THE MARKER AXES STILL REDDENED A LEGITIMATE ADOPTER.

The previous round closed the `bots` axis and left the marker axes open.
`_load_review_config`'s own docstring documents `noise_markers: []` as
supported, and `adopt.md` tells adopters to run this suite against their
own config — so an OpenKitchen-shaped repo still got a red kit suite, just
for a different field. The shipped-config test now skips for an adopter
who has emptied a marker list, and `test_review_skipped_lives_only_in_
unavailable_markers` asserts its disjointness property unconditionally but
its marker-placement claim only when the list is populated.

Also fixed, all reported by a lens:

  - `_REVIEW_CONFIG` was asserted only `!= sentinel`, so re-reading the
    AMBIENT config there passed. Now compared against the defaults object.
  - the `all(name == name.lower())` assertion cannot fail — the loader
    lowercases by construction and the set is empty under
    `informational_checks: []`. Removed rather than left as decoration;
    the behaviour it appeared to cover is pinned elsewhere.
  - `test_require_ci_defaults_to_the_configured_value` renamed to
    `test_require_ci_is_honoured_by_summarize_checks`: the body stopped
    checking a configured value in the last round and the name did not
    follow.

CORRECTIONS TO MY OWN ACCOUNTING (found by the correctness lens):

  - "7 tests fail" was invented. Corrupting the shipped `noise_markers`
    six different ways on main fails at most TWO tests, and three of the
    six fail none. The direction was right, the magnitude was not — and I
    had baked the wrong number into a docstring, not just a commit
    message. Now states the property without a count.
  - the previous commit said the false "untouched" claim lived in "both
    commit messages and the in-diff docstring". Only ONE commit message
    carried it. The real third place was the PR BODY, which that commit
    did not name and did not fix. Fixed there now.
  - `test_normal_coderabbit_walkthrough_remains_noise` also lost
    shipped-config coverage to the pinning. Left pinned deliberately —
    its subject is engine behaviour — but the previous commit's
    "THE shipped-config test", singular, was incomplete.

Acceptance, all with `PYTHONDONTWRITEBYTECODE=1` and `__pycache__`
cleared (see #50 — a same-length mutation left stale bytecode that
survived a restore during this PR):

    shipped                            323 passed
    review.bots: []                    323 passed
    review.require_ci: false           323 passed
    review.informational_checks: []    323 passed
    review.noise_markers: []           322 passed, 1 skipped
    coherent no-bot adopter (all axes) 322 passed, 1 skipped

KNOWN RESIDUAL, not fixed here: removing `review.fallback_panel`, or the
whole `review:` section, fails
`test_portability.py::test_init_migrates_the_previous_runtime_schema`.
That test's subject IS the shipped config versus migration output, and a
config lacking those keys is a pre-`init.sh` state that `init.sh` itself
repairs — so it is a different question from "an adopter's legitimate
steady-state config must not break kit tests". Recorded rather than
silently left.

Claude-Session: https://claude.ai/code/session_01V27wAgECMEApqAvmJMFyqm
@topij

topij commented Jul 26, 2026

Copy link
Copy Markdown
Owner Author

Review record — fallback panel, 2 rounds

CodeRabbit reviewed the first commit (one valid nitpick, applied in 75306b0), then went rate-limited on every subsequent head — detected on both the comment surface (review limit reached) and the status-check description (review rate limited), which is the #23 machinery working as designed. Its last review covers bcb4f5e, not the current head, and pr_watch says so on every poll. So the fallback panel carried rounds 2 and 3.

Round 1 — 2 HIGH, both real, both mine: the regression test guarded 2 of 7 pinned fields (missing _REVIEW_BOTS, the field that caused the bug), and the pinning silently gutted test_shipped_config_preserves_the_engine_defaults_behavior.

Round 2 — found the fix to round 1 was itself wrong: the new guard-on-the-guard failed open (skip predicate computed with the function under test), "EVERY config-derived global" was a hand-written list of 7, and the marker axes still reddened a legitimate adopter. Plus the correctness lens caught that the PR body still carried a claim round 1 had proven false — because I'd fixed the commit message and not the body.

All addressed in 29da37d. The adversarial lens re-verified the core mechanism independently and reported it could not break the pin sweep.

Stopping criterion applied — blast radius, not round count

Per fallback-review-panel.md. This is test infrastructure, not a gate, send path, or destructive operation. Worst case of a residual defect is a kit test being weaker than believed — not an unreviewed change landing. Severity decayed across rounds (HIGH → MED/HIGH → MED → LOW), the core property has been independently mutation-verified twice, and round 2's remaining findings were about claims rather than mechanism.

Recording honestly what that does not cover: 29da37d itself has not been through a panel round. The counter-argument for continuing is real — round 2 found a MED/HIGH inside round 1's fix, so fix rounds on this file have a demonstrated defect rate. Stopping here is a judgment that the blast radius does not justify a fourth round, made by the operator, not a claim that the code is now known-clean.

Verification

Six config shapes, all with PYTHONDONTWRITEBYTECODE=1 and __pycache__ cleared (see #50 — a same-length mutation left stale bytecode that survived a git-clean restore during this PR, so the suite ran mutant code for several minutes):

shipped                            323 passed
review.bots: []                    323 passed
review.require_ci: false           323 passed
review.informational_checks: []    323 passed
review.noise_markers: []           322 passed, 1 skipped
coherent no-bot adopter            322 passed, 1 skipped

Per-field sweep of _pin_engine_defaults: 7/7 killed.

Receipt: fallback:panel, lenses adversarial, correctness, bound to 29da37d.

@topij
topij merged commit 567d566 into main Jul 26, 2026
3 checks passed
@topij
topij deleted the fix/tests-must-not-read-ambient-config branch July 26, 2026 11:43
topij added a commit that referenced this pull request Jul 26, 2026
… filed (#52)

Session block for the adopter-upgrade session. Five PRs merged across two
repos: OpenKitchen #256 (pre-v2 install to schema v2), #257 (review.bots
emptied), #258 (doc sync); kit #43 (panel isolation doctrine) and #49 (test
suite detached from ambient config, closing #48).

The upgrade worked. What it mostly produced was evidence about the kit: 17
issues, and the recurring shape is that the kit's quality mechanisms each
cover a different subset of the kit and nothing checks they agree —
KIT_OWNED tracks 24 of 37 shipped files, the manifest tracks no test files,
the suite covers no lines of pre-push. That gap is the root cause of #36,
#40, #41 and #51, and #47 is the check that closes the class.

Also recorded because it casts doubt backwards: #50 — a mutation preserving
source LENGTH leaves stale bytecode Python treats as valid, so a git-clean
restore does not restore. Mutation evidence gathered earlier in the session
is suspect, including on the already-merged #256.

Ran archive_plan_sessions.py --keep 4 (447 -> 377 lines); the 2026-07-25
block moved to kit-handoff-history.md verbatim.

No friction-log entry: the log is AT its 150-line budget with an explicit
"sweep required before the next entry" tripwire, and this session's friction
was routed straight to Issues — the Principle #2 routing, not a neglected
inbox.

CodeRabbit found one real issue in review: the archive pointer references a
section that exists nowhere and claims a folding step the script never
performs. Filed as #53 rather than fixed here — the text is engine-generated,
so a hand-edit would be overwritten by the next sweep and reproduces in every
adopter.

Next session: cs-toolkit Phase 1 only (additive install, zero behaviour
change). Deferring the pr_watch swap (nightly fixer in active development)
and the pre-push swap (blocked on #46).

Claude-Session: https://claude.ai/code/session_01V27wAgECMEApqAvmJMFyqm
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Kit tests silently depend on the adopter's config — a legitimate review.bots value breaks 32 of them

1 participant