Skip to content

feat(hooks): push-block precision, degradation visibility, unpushed-work detection, config ask-gate (U1/U9/U2/U4 — PR1) - #45

Merged
dralgorhythm merged 6 commits into
mainfrom
claude/hardening-pr1-hooks
Jul 24, 2026
Merged

feat(hooks): push-block precision, degradation visibility, unpushed-work detection, config ask-gate (U1/U9/U2/U4 — PR1)#45
dralgorhythm merged 6 commits into
mainfrom
claude/hardening-pr1-hooks

Conversation

@dralgorhythm

Copy link
Copy Markdown
Owner

Summary

PR1 hooks lane from plan_framework_hardening.md: U1 (push-block precision + jq-free fallback + visible degradation), U9 (post-compaction re-orientation), U2 (remote-aware unpushed-work detection), U4 (config-write ask-gate + Bash secret-write scan), plus one orchestrator landing commit.

What changed

  • U1: field-scoped sed -E extraction (fixing a BSD/macOS BRE alternation portability bug in the copied idiom); trailing-token branch equality — pushing a branch named feature/main-cleanup is no longer denied (this exact false positive fired live on the orchestrator twice mid-session, including once while composing this very PR); jq-free fallback denies bare/implicit pushes on the default branch via git rev-parse; [HOOK DEGRADATION] announcement at session start when jq is missing.
  • U9: compact/resume sessions get a re-orientation context block; Stale Context Check extended to plan level.
  • U2: stop-validator warns on committed-but-unpushed work (ahead-of-upstream count, no-upstream guidance) and stays silent in zero-remote repos.
  • U4: ask-tier protection for .claude/settings.json, .claude/rules/*, CLAUDE.md (tailor's promise now hook-backed); stale opposite-claiming comment removed; six secret patterns now also scan Bash redirect/heredoc writes. Two pre-existing bugs fixed in passing: a BASH_COMMAND special-variable collision, and a leading-dash grep bug that silenced the private-key pattern on macOS.
  • Landing commit: baseline case updated for U1's deliberate behavior change; a fixture-hermeticity bug fixed (a shared bare remote made a stop-validator case order-dependent); validator registered under the Bash matcher so the U4 scan is live (plain Bash smoke-verified silent).

Provenance

  • Author: worker-builder (sonnet) via /swarm-execute (resumed once after a turn-ceiling stop; recovery per AGENTS.md Mode A); unit commits rebased onto current main by the orchestrator; independent gate re-run on the lane tip.
  • Gates: test-hooks.sh ALL GREEN (no skips — lane + baseline cases), check-invariants.sh ALL GREEN (23 checks), CI-equivalent shellcheck clean.

Risk tier

medium — behavior-visible hook changes (new ask prompts, new session-start context, validator on every Bash call). MIGRATION.md entries included for U1/U9 and U4. Blast radius bounded: all changes fail open per doctrine; the permissions.deny backstop is untouched.

Test plan

  • False-positive branch names allowed; explicit and implicit default-branch pushes denied with and without jq
  • compact emits re-orientation; startup does not
  • ahead / no-upstream / zero-remote stop-validator matrix
  • Config-write ask, heredoc-secret ask, plain Bash silent
  • Follow-up filed: branch-pr-discipline.sh carries the same BSD sed bug (out of lane scope)

🤖 Generated with Claude Code

dralgorhythm and others added 6 commits July 24, 2026 10:28
… degradation (U1)

pre-push-main-blocker.sh compared main/master as a substring anywhere in the
push command, so branch names like feature/main-cleanup or
domain-master-list were wrongly denied (a live, reproduced false positive).
Rewrite the check to compare the actual destination-branch token for
equality instead, and extract tool_name/command via a jq-free, field-scoped
sed idiom (generalized from branch-pr-discipline.sh's, ported to sed -E
for BSD/GNU portability - the literal BRE alternation form silently
extracts nothing on BSD/macOS sed). Dropping jq entirely for this hook also
closes the prior gap where a missing jq skipped it altogether, including
the bare/implicit push-while-on-main case permissions.deny has no literal
branch name to pattern-match against.

session-start-loader.sh now checks for jq before anything else and, when
absent, emits a [HOOK DEGRADATION] context block naming exactly what's
degraded (secret detection + file locks, dangerous-command warnings,
pre-commit reminders) instead of failing open silently - permissions.deny
and the branch-block are both unaffected regardless.

docs/hooks.md gains a consolidated Degradation visibility subsection;
MIGRATION.md documents the new session-start output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
session-start-loader.sh now branches on SOURCE: a "compact" or "resume"
session start appends a [POST-COMPACTION RE-ORIENTATION] block reminding the
session to check the native task list, re-read any active plan artifact,
and re-read files before editing them rather than trusting what compaction
(or a fresh resume) may have dropped or never carried over.

debugging-protocol.md's Stale Context Check gains three matching lines:
apply the same re-orientation discipline at the plan level, externalize
long plans to a file before starting so there is something durable to
re-read, and delegate bulk exploration to workers instead of accumulating
it in the orchestrator's own context.

Extends the U1 MIGRATION.md entry (same new-SessionStart-output category)
rather than adding a second one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
stop-validator.sh only warned about uncommitted files. It now also warns
about unpushed commits, remote-aware: if `git remote` is empty the whole
check is skipped (a naive "commits not on any remote" count would warn on
every commit in every local-only repo, which is noise, not a reminder).
With a remote configured: count commits ahead of the configured upstream
via `git rev-list --count @{upstream}..HEAD` when an upstream tracking
branch exists, or commits unreachable from any remote-tracking ref via
`git rev-list --count HEAD --not --remotes` when it doesn't (with `git push
-u` guidance instead). Warns only when the count is greater than zero.

docs/hooks.md's Built-in Hooks row and Session Management capability list
both mention the new check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pre-tool-use-validator.sh now asks (not silently proceeds) before a direct
Write/Edit to .claude/settings.json, any file under .claude/rules/, or the
root CLAUDE.md - the paths /tailor proposes changes to rather than writing
directly. Removes the now-contradicting comment claiming these paths are
deliberately unprotected/user-configurable.

Extends the six secret-shape checks (refactored into a shared detect_secret
function to avoid duplicating them) to also scan Bash commands that
redirect or heredoc content into a file, closing the blind spot where a
heredoc'd .env write bypassed Write/Edit-only detection entirely.

Along the way, fixed two bugs surfaced while building this (both
pre-existing, not introduced by this change):
- A local variable named BASH_COMMAND collided with bash's own special
  variable of that name (always reflects "the command about to execute"),
  silently getting clobbered on the next statement rather than holding the
  extracted value - renamed to TOOL_COMMAND.
- The private-key pattern's leading dashes were parsed as grep options on
  BSD/macOS grep instead of a pattern, so that check silently never matched
  on those systems; added -e to all six patterns.

tailor/SKILL.md gains one line noting the propose-only contract is now
hook-backed; docs/hooks.md gets a table-row update, a new Config-Write
Ask-Gate section, and an honest limitation note (Trivy CI remains the
actual backstop). New MIGRATION.md entry for the new ask prompts.

Known gap (flagged, not fixed here - out of this unit's file list): the
Bash-command scan only fires in practice if pre-tool-use-validator.sh is
also registered under the PreToolUse "Bash" matcher in settings.json,
which today only wires it to Write/Edit/MultiEdit/NotebookEdit. The
harness cases exercise the script directly and pass regardless, but the
capability is inert against real Bash tool calls until that wiring is
added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r for Bash (PR1 follow-through)

Orchestrator landing fixes for the hooks lane:
- 00-baseline.sh: session-start-loader's jq-absent case now expects the
  [HOOK DEGRADATION] announcement U1 deliberately introduced (the old
  silent-exit pin is superseded; the other four hooks keep it).
- 10-hooks-lane.sh: the clean-and-pushed stop-validator fixture gets its
  own bare remote — sharing one across unrelated-history fixtures let the
  second setup silently fail non-fast-forward, making the case
  order-dependent (deterministically red on landing).
- settings.json: register pre-tool-use-validator under the Bash matcher so
  U4's redirect/heredoc secret scan actually fires for Bash tool calls;
  smoke-verified plain commands stay silent-allowed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dralgorhythm
dralgorhythm merged commit 971c46f into main Jul 24, 2026
8 of 10 checks passed
@dralgorhythm
dralgorhythm deleted the claude/hardening-pr1-hooks branch July 24, 2026 16:37
dralgorhythm added a commit that referenced this pull request Jul 24, 2026
…ix (U16/O14 — PR8) (#47)

* feat(loop): correction capture — log, retro promotion, stop nudge (U16/O14)

Implements research option O14 (artifacts/research_ai_coding_frustrations.md
Part 3) — the standing correction-capture loop so mid-session corrections
stop getting silently repeated instead of retained.

- core-directives.md: new "Correction Capture" subsection — a user
  correction that contradicts standing rules/skills/instructions gets
  appended as one line to scratchpad/corrections.log; ordinary task
  instructions are not logged.
- land-the-plane/SKILL.md: Handoff section gains a Retro step — map each
  non-empty log entry to the strongest enforcement rung it can support
  (rule/skill/hook/CI, same discipline as postmortem's Prevention step),
  promote via a small PR or filed issue, then remove the processed line.
- stop-validator.sh: compact, position-independent reminder block — when
  scratchpad/corrections.log is non-empty, name the pending count and
  point at the retro step. Silent when absent or zero-byte. Kept minimal
  per the note that PR #45 also touches this file.
- New scripts/hook-tests.d/40-self-improvement.sh: hermetic harness cases
  for the 2-line, absent, and zero-byte log states.
- CHANGELOG.md (Unreleased/Added) and MIGRATION.md entries.

rules-lines budget: 419 -> 423 lines (budget 500; core-directives.md only
touched rule file, +4 lines).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(loop): auto-memory vs rules guidance + re-audit cadence (U16/O14)

Completes O14 (artifacts/research_ai_coding_frustrations.md Part 3 /
"The standing iteration loop") alongside the prior mechanics commit.

- docs/customization.md: new "Session Learning: Auto-Memory vs. Repo
  Rules" section — Claude Code's per-user auto-memory is for personal
  preferences and machine quirks (ride the platform feature, don't
  rebuild it); team-shared, reviewed, CI-checked guidance belongs in
  .claude/rules//.claude/skills//.claude/hooks/, and corrections.log
  promotions land there, never only in memory. Cross-links
  core-directives.md's Correction Capture convention.
- CONTRIBUTING.md: new "Standing self-improvement loop" section
  extending the eval-first/retirement policies with the full
  capture -> escalate -> verify -> re-audit cycle, including re-running
  the failure-taxonomy coverage audit from
  artifacts/research_ai_coding_frustrations.md at each
  model-generation bump.
- CHANGELOG.md (Unreleased/Added) entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(hooks): BSD sed fallback in branch-pr-discipline + docs row (landing follow-through)

- sed -E in the no-jq command extraction: the BRE alternation form
  silently never matched on BSD/macOS sed, so every warning this hook
  gives was inert whenever jq was absent (task #26; same bug class the
  U1 rewrite fixed in the push blocker). Hermetic regression cases pin
  the fallback via GIT_DIR/GIT_WORK_TREE fixtures.
- docs/hooks.md stop-validator row now mentions the corrections-log
  reminder (flagged by the U16 builder as outside its file list).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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