From 1bc541ac934a2d80192121da5baf7e783173a73b Mon Sep 17 00:00:00 2001 From: m3taversal Date: Sun, 10 May 2026 19:10:49 +0100 Subject: [PATCH] fix(reaper): tighten research-session pattern to literal YYYY-MM-DD shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apply Ganymede review of 50b888a: MUST-FIX — pattern %/research-2% was broader than the comment claimed. Matched anything/research-2[anything] including agent-named branches like theseus/research-2nd-attempt-on-X or vida/research-2024-revisited. The documented invariant said "date suffix only" but the SQL didn't enforce it. Defense-in-depth was the framing; pattern needed to match the framing. Fix uses SQLite `_` single-char wildcards: research-20__-__-__ requires exactly research-20[2-char][-][2-char][-][2-char], i.e. literal YYYY-MM-DD shape. Threads the needle: - theseus/research-2026-04-30 ✓ (catches all 15 currently stuck) - rio/research-2099-12-31 ✓ (good through 2099) - theseus/research-2nd-attempt ✗ (correctly excluded) - vida/research-2024-revisited ✗ (correctly excluded — no -MM-DD shape) - rio/research-batch-agents-... ✗ (no date prefix at all) NIT — comment said "Three classes qualify" then listed four. Off-by-one fixed; comment now correctly says "Four classes." Pre-deploy verified: tighter pattern catches all 15 currently-stuck research PRs (clay/leo/astra/theseus/vida/rio research-2026-{04-28 through 05-02}). Zero false-positive risk on current branch namespace. Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/substantive_fixer.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/substantive_fixer.py b/lib/substantive_fixer.py index 907f6f2..e5878db 100644 --- a/lib/substantive_fixer.py +++ b/lib/substantive_fixer.py @@ -650,22 +650,25 @@ async def verdict_deadlock_reaper_cycle(conn) -> int: # # Branch allowlist invariant: the reaper closes ONLY disposable, pipeline- # generated branches — content the pipeline (or a daily cron) created and - # can recreate. Three classes qualify: + # can recreate. Four classes qualify: # # extract/* — per-source extraction PRs, regenerated next ingest cycle # reweave/* — nightly graph-edge maintenance, regenerated next reweave # fix/* — pipeline-internal fix branches - # */research-2* — daily {agent}/research-{YYYY-MM-DD} sessions; the date - # suffix scopes this to cron-generated outputs only and - # excludes hand-named research branches like - # rio/research-batch-agents-memory-harnesses, which are - # feature work owned by the agent. + # */research-YYYY-MM-DD — daily {agent}/research-{date} cron sessions. + # Matched via SQLite `_` single-char wildcards as + # `research-20__-__-__` to literally enforce the date- + # suffix shape. Excludes hand-named research branches + # (rio/research-batch-agents-memory-harnesses, + # theseus/research-2nd-attempt-on-X, etc.) which are + # feature work owned by the agent. Pattern good through + # 2099; revisit then. # # WIP agent feature branches (theseus/feature-foo, epimetheus/some-fix, # rio/research-thesis-name) are NEVER reaped — owners review their own PRs - # on their own cadence. The */research-2* pattern threads the needle: it - # picks up daily synthesis output that the agent will regenerate tomorrow - # while leaving manually-named research work alone. + # on their own cadence. The date-shaped pattern threads the needle: picks + # up daily synthesis output the agent regenerates tomorrow while leaving + # manually-named research work alone. rows = conn.execute( """SELECT number, branch, eval_issues, leo_verdict, domain_verdict, last_attempt, fix_attempts @@ -677,7 +680,7 @@ async def verdict_deadlock_reaper_cycle(conn) -> int: AND (branch LIKE 'extract/%' OR branch LIKE 'reweave/%' OR branch LIKE 'fix/%' - OR branch LIKE '%/research-2%') + OR branch LIKE '%/research-20__-__-__') AND ( (leo_verdict = 'request_changes' AND domain_verdict = 'approve') OR (leo_verdict = 'skipped' AND domain_verdict = 'request_changes')