Harden herdr /close teardown against silent orphan tabs - #20
Merged
Conversation
close-tab now closes AND verifies the tab is gone (one retry), printing closed|still-open|unverified via a new tab_status/tab-status helper, so a close that silently didn't take is never reported as success. The self-exit poller window is raised 30s->120s (a closing turn that archives+commits+pushes can outlast the old guess, and a timeout injects nothing -> an idle orphan), and after injecting /exit on idle it now confirms the pane vanished and re-injects once. close/SKILL.md step 12: Scenario A branches on the close-tab result and names the tab on failure; Scenario B always appends an explicit 'close by hand: <tab>' line, since a self-close fires asynchronously after the turn and cannot be confirmed in-turn. Bump work-system 1.5.0 -> 1.5.1; update herdr-close-automation knowledge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HSMdpBst3jMCb47Nuq4ueg
- close-tab: drop require_herdr (exit 1 + empty stdout broke its own 'always exit 0 / closed|still-open|unverified' contract, leaving a silent orphan when herdr is unresolvable at step 12); herdr-absent now yields 'unverified'. Poll the status a few times (re-close while present, retry on transient 'unverified') instead of a single 0.3s retry to cut false alarms. - self-exit poller: drop the speculative second /exit re-injection — it can't distinguish a dropped first /exit from a user who reopened the tab and is momentarily idle, and would kill that live session. Inject once; the always-printed manual-close line + SessionEnd hook are the backups. - close/SKILL.md: fix the step-11 summary advertising status words the script never emits; resolve the duplicated Ctrl+D directive (B-hook now names the tab; item 5 is scoped to the B-inject path). - Sync the herdr-close-automation knowledge entry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HSMdpBst3jMCb47Nuq4ueg
- extract_tab_present: a valid-but-empty panes array now reads 'unverified', not 'gone' — a transiently empty list (e.g. just after a herdr restart) would otherwise let close-tab falsely report a live tab as 'closed' (the silent orphan this feature prevents). Verified with a fake herdr. - close-tab: short-circuit to 'unverified' when herdr/python3 is missing instead of spinning the verify loop ~1.2s on a condition that can't change; re-read the status once after the loop so a close landing on the final iteration is verified, not misreported as 'still-open'. - Remove the dead 'tab-status' subcommand (no production caller; it was a third copy of the present|gone|unverified vocabulary) — the internal tab_status helper that close-tab uses stays. - close/SKILL.md: fix 'one retry' prose to match the actual poll-a-few-times loop. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HSMdpBst3jMCb47Nuq4ueg
- extract_status: apply the same empty-but-valid panes guard the round-2 fix added to extract_tab_present. The __delayed-inject poller used extract_status, which still returned __gone__ for a transiently empty list and bailed without injecting /exit — re-creating the silent orphan on the path the wider window was meant to protect. Now __gone__ fires only for a POPULATED list missing the pane; an empty list prints nothing so the poller keeps polling. - close-tab: close ONCE then poll until gone, instead of re-issuing the close on every 'present' read — a re-issue could land on a tab id herdr recycled onto a new tab and kill it. A genuinely-failed close still surfaces as 'still-open' for the manual-close fallback. Drops the off-by-one final re-read too. - extract_tab_present: str()-coerce tab_id so a numeric id from herdr still matches (avoids a false 'gone' -> false 'closed'). - README: distinguish 'still-open' (close not taken) from 'unverified' (couldn't re-query) instead of collapsing both into 'didn't take'. - Sync the herdr-close-automation knowledge entry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HSMdpBst3jMCb47Nuq4ueg
- Revert the speculative str() tab_id coercion added last round: herdr ids are 'wN:tM'/'wN:pM' strings (never numeric), so no snippet needs it — and coercing only extract_tab_present left it inconsistent with extract_tab's --exclude compare. Document that all id compares are as-is. - Fix doc drift: the script header and close/SKILL.md still described close-tab as 're-issuing the close while still present', but it deliberately closes once and polls. Match the shipped behavior. - close-tab: skip the sleep after the final status read (its result is the one reported), so the verify loop does 5 reads / 4 waits instead of a wasted trailing 0.3s. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HSMdpBst3jMCb47Nuq4ueg
gering
added a commit
that referenced
this pull request
Jul 16, 2026
Second dogfooding round at the deepest profile (/swarm:review --max --fix: per-lens split live — gate pruned style, 10 xhigh finders + codex gpt-5.6-sol@xhigh + grok-4.5 + composer; 24 findings, 10 consensus). Fixes the agreed findings: - Never auto-accept an all-untagged consensus cluster: no tagged lens backs it, so it is verified like a solo. Verify/auto-accept now derive from ONE needsVerify predicate (structural exactly-once partition); kind vote in clearer every-form (#1, consensus critical; #21, #22) - Design verifier sees the finding's recommendation — the proposal the applicability rubric actually tests — and carries an escape hatch: a genuine defect mis-filed under a design lens is not refuted away (#12, #3); "solo" dropped from verifier prompts (#4) - Untagged findings from multi-lens cluster finders fall back to 'unspecified' (safe defect bucket), not lenses[0] (#7); merge-agent free-text lens validated, majority-member fallback (#9) - Improvement invitation scoped to design finder units — defect-lens finders stay defect-only (#8); merge prompt clusters by issue, not only defect (#13); schema descriptions generalized for design findings (#14) - LENS_BRIEF startup assertion (#6); gate prompt interpolates LENS_CLUSTERS.design (#19); new test_lens_sync.py guards all lens mirrors: SKILL HDR prompt, LENS_BRIEF, pr-post DESIGN_LENSES (#5) - Workflow assigns stable finding num (defects first, shared sequence); presenter/pr-post render it verbatim (#20) - pr-post.py: design lens is the backup kind signal when the handoff drops kind (explicit defect still wins); single-pass partition (#17, #24) - Balance: REFUTED is its own segment (refuted ⊄ solo since design consensus can be refuted) (#2); LOCKED design-table column precedence in --loop rounds clarified (#11); "verifies solos" doc sweep across manifests/README/knowledge (#4); cluster failure-isolation trade-off documented (#10); knowledge index line trimmed (#15) Declined: #16 (JS test harness for sandbox code; lens-sync test covers the drift class), #18 (gate-fail under --max runs all lenses — the documented never-silently-narrower degrade), #23 (micro-opt vs readability). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0126cGxsBYxqgEFH4AcpriNk
gering
added a commit
that referenced
this pull request
Jul 17, 2026
Second dogfooding round at the deepest profile (/swarm:review --max --fix: per-lens split live — gate pruned style, 10 xhigh finders + codex gpt-5.6-sol@xhigh + grok-4.5 + composer; 24 findings, 10 consensus). Fixes the agreed findings: - Never auto-accept an all-untagged consensus cluster: no tagged lens backs it, so it is verified like a solo. Verify/auto-accept now derive from ONE needsVerify predicate (structural exactly-once partition); kind vote in clearer every-form (#1, consensus critical; #21, #22) - Design verifier sees the finding's recommendation — the proposal the applicability rubric actually tests — and carries an escape hatch: a genuine defect mis-filed under a design lens is not refuted away (#12, #3); "solo" dropped from verifier prompts (#4) - Untagged findings from multi-lens cluster finders fall back to 'unspecified' (safe defect bucket), not lenses[0] (#7); merge-agent free-text lens validated, majority-member fallback (#9) - Improvement invitation scoped to design finder units — defect-lens finders stay defect-only (#8); merge prompt clusters by issue, not only defect (#13); schema descriptions generalized for design findings (#14) - LENS_BRIEF startup assertion (#6); gate prompt interpolates LENS_CLUSTERS.design (#19); new test_lens_sync.py guards all lens mirrors: SKILL HDR prompt, LENS_BRIEF, pr-post DESIGN_LENSES (#5) - Workflow assigns stable finding num (defects first, shared sequence); presenter/pr-post render it verbatim (#20) - pr-post.py: design lens is the backup kind signal when the handoff drops kind (explicit defect still wins); single-pass partition (#17, #24) - Balance: REFUTED is its own segment (refuted ⊄ solo since design consensus can be refuted) (#2); LOCKED design-table column precedence in --loop rounds clarified (#11); "verifies solos" doc sweep across manifests/README/knowledge (#4); cluster failure-isolation trade-off documented (#10); knowledge index line trimmed (#15) Declined: #16 (JS test harness for sandbox code; lens-sync test covers the drift class), #18 (gate-fail under --max runs all lenses — the documented never-silently-narrower degrade), #23 (micro-opt vs readability). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0126cGxsBYxqgEFH4AcpriNk
gering
added a commit
that referenced
this pull request
Jul 17, 2026
Second dogfooding round at the deepest profile (/swarm:review --max --fix: per-lens split live — gate pruned style, 10 xhigh finders + codex gpt-5.6-sol@xhigh + grok-4.5 + composer; 24 findings, 10 consensus). Fixes the agreed findings: - Never auto-accept an all-untagged consensus cluster: no tagged lens backs it, so it is verified like a solo. Verify/auto-accept now derive from ONE needsVerify predicate (structural exactly-once partition); kind vote in clearer every-form (#1, consensus critical; #21, #22) - Design verifier sees the finding's recommendation — the proposal the applicability rubric actually tests — and carries an escape hatch: a genuine defect mis-filed under a design lens is not refuted away (#12, #3); "solo" dropped from verifier prompts (#4) - Untagged findings from multi-lens cluster finders fall back to 'unspecified' (safe defect bucket), not lenses[0] (#7); merge-agent free-text lens validated, majority-member fallback (#9) - Improvement invitation scoped to design finder units — defect-lens finders stay defect-only (#8); merge prompt clusters by issue, not only defect (#13); schema descriptions generalized for design findings (#14) - LENS_BRIEF startup assertion (#6); gate prompt interpolates LENS_CLUSTERS.design (#19); new test_lens_sync.py guards all lens mirrors: SKILL HDR prompt, LENS_BRIEF, pr-post DESIGN_LENSES (#5) - Workflow assigns stable finding num (defects first, shared sequence); presenter/pr-post render it verbatim (#20) - pr-post.py: design lens is the backup kind signal when the handoff drops kind (explicit defect still wins); single-pass partition (#17, #24) - Balance: REFUTED is its own segment (refuted ⊄ solo since design consensus can be refuted) (#2); LOCKED design-table column precedence in --loop rounds clarified (#11); "verifies solos" doc sweep across manifests/README/knowledge (#4); cluster failure-isolation trade-off documented (#10); knowledge index line trimmed (#15) Declined: #16 (JS test harness for sandbox code; lens-sync test covers the drift class), #18 (gate-fail under --max runs all lenses — the documented never-silently-narrower degrade), #23 (micro-opt vs readability). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0126cGxsBYxqgEFH4AcpriNk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
/closeleaving an idle orphan herdr tab (observed closingarchive-tasks-on-close, PR Archive task file on /close instead of deleting it #19): the task's tab stayed open and idle even with the 1.4.1+ teardown active./exitwas never delivered — the old 30s poll window timing out, a finished status settling tounknown, or a dropped/exit.nohup … & disownpoller survives past the Bash-tool boundary (so the detached mechanism is sound), and herdr'sagent_statusis one ofidle|working|done|unknown(the injector'sidle|donetargets do exist).Changes
herdr-teardown.sh: newtab_status/tab-statushelper (present|gone|unverified);close-tabnow closes and re-checks the tab is gone (one retry) and printsclosed|still-open|unverified;__delayed-injectpoll window raised 30s→120s plus a post-inject confirm with one re-inject.close/SKILL.mdstep 12: Scenario A branches on theclose-tabresult and names the tab on failure; Scenario B always appends an explicit "close by hand:<tab>" fallback line (a self-close can't be confirmed in-turn).herdr-close-automation.md) updated to document the verify-or-name behavior and the empirical findings.Readiness
check-structure.py): 0 errorsbash -n) cleantab-status+ fullclose-tabpath on a throwaway tab (present →closed→ gone)Test plan
/closea merged task from the main herdr session → tab closes; if it can't, an explicit "close by hand:<tab>" line appears/closefrom inside the worktree tab → session exits cleanly and the tab auto-closes; the fallback line names the tab in case it lingers/closeis unaffected🤖 Generated with Claude Code