Skip to content

fix(hook-kit): reconcile the three diverged bash-guard copies into one union - #394

Open
DrumRobot wants to merge 1 commit into
next-fixfrom
fix/bash-guard-copy-drift-union
Open

fix(hook-kit): reconcile the three diverged bash-guard copies into one union#394
DrumRobot wants to merge 1 commit into
next-fixfrom
fix/bash-guard-copy-drift-union

Conversation

@DrumRobot

Copy link
Copy Markdown
Member

Problem

bash-guard.py was living as three independently edited copies. Fixes landed on whichever copy happened to be executing, and never travelled back to git — so the tracked file was missing six functions that were actively guarding real sessions.

Copy Fns Has Lacks Self-tests
tracked (staging branch) 11 staging base gate force-push fn, heredoc stripping 93
plugin cache (es6kr) 13 heredoc stripping, worktree-add exemption, pr-merge-ready check, transcript-aware pr-create-draft staging gate 99
~/.agents/skills copy 14 conditional force-push guard + 2 helpers the cache-only trio 93 (none for its own guard)

The runtime loads from the plugin cache, not the marketplace — confirmed from installed_plugins.json's installPath, not inferred from error-message paths. Two plugins (es6kr, task) cache the same hook, so both run, and they disagree.

Change

Union merge. The cache copy is the skeleton because its two overlapping functions are strict supersets of the tracked ones; the staging gate comes from the tracked copy and the force-push trio from the third. Three module constants and one transitive dependency came along with them.

Behaviour change worth review: force-pushing stops being an unconditional block. Targeting a protected branch is still denied; a lease-checked push to a feature branch is now allowed. That conditional-allow fix existed only in the untracked copy until now.

Test plan

  • --test self-suite: 105 cases pass (93 tracked + 6 cache-only + 6 staging)
  • Behaviour matrix returns the intended verdict for: protected-branch force-push (deny), lease-checked feature-branch push (allow), plain push (allow), staging base without a divergence check (deny), same with the check (allow), hard reset (deny), pr-create without draft (deny)
  • python3 -m py_compile
  • make lint, make verify-spec, make test (9 passed)
  • Reviewer confirms the force-push relaxation matches intended policy
  • Reviewer confirms no guard regressed relative to the copy they rely on

Known gap (not in this PR)

The copies will drift again: git tracks the marketplace source while the runtime reads the cache, and the sync target is two cache directories rather than one. A structural fix (symlink, or a copy-comparison step in the hook audit) is a separate change. The other staging branch already carries feat(hook-kit): check the runtime copies a hook actually has, which looks like the start of that work.

While committing this, the task cache copy — which lacks the heredoc stripping the es6kr copy has — denied the commit by matching a phrase inside the message body. That false positive is the drift's cost, observed rather than hypothesised.

…e union

bash-guard.py existed as three independently edited copies, and each carried
guard logic the others lacked. Past fixes were applied to whichever copy was
executing at the time and never travelled back to git, so the tracked file was
missing six functions that were actively guarding real sessions.

Divergence map (verified, not inferred):

  tracked (next-fix)      11 fns  staging gate, no force-push fn, 93 self-tests
  plugin cache (es6kr)    13 fns  heredoc stripping, worktree-add exemption,
                                  pr-merge-ready check, transcript-aware
                                  pr-create-draft; no staging gate, 99 tests
  ~/.agents/skills copy   14 fns  conditional force-push guard + 2 helpers,
                                  93 tests (no coverage for its own guard)

This commit takes the union. The cache copy is the skeleton because its two
overlapping functions are strict supersets of the tracked ones; the staging
gate comes from the tracked copy, and the force-push trio from the third.

Behaviour change worth calling out: force-pushing stops being an unconditional
block. Targeting a protected branch is still denied, but a lease-checked push
to a feature branch is now allowed, matching the conditional-allow fix that
until now lived only in the untracked copy.

Verified: 105 self-test cases pass (93 + 6 cache-only + 6 staging), and a
behaviour matrix over force-pushing, the staging base gate, hard reset and
pr-create-draft returns the intended verdict for each. Repo lint, verify-spec
and pytest all pass.

Demonstrated while committing this: the task-plugin cache copy lacks the
heredoc stripping the es6kr copy has, so it denied this very commit by matching
a phrase inside the message body. That false positive is the drift's cost.

Not addressed here: the copies still drift, because the runtime loads from the
plugin cache while git tracks the marketplace source, and both `es6kr` and
`task` cache the same hook - so a sync target is two directories, not one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019EQM3BG4bwWxdhqM8cPDUw
@DrumRobot
DrumRobot changed the base branch from next-fix to fix/rag-dispatch-config-resolved August 29, 2026 00:25
@DrumRobot
DrumRobot changed the base branch from fix/rag-dispatch-config-resolved to next-fix August 29, 2026 00:26
@DrumRobot
DrumRobot marked this pull request as ready for review August 29, 2026 01:00
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.

1 participant