Skip to content

docs: the nudge-hook pattern, with a worked example, and the approver carve-out cross-reference - #34

Merged
MCKRUZ merged 1 commit into
mainfrom
docs/nudge-pattern-and-approver-crossref
Jul 31, 2026
Merged

docs: the nudge-hook pattern, with a worked example, and the approver carve-out cross-reference#34
MCKRUZ merged 1 commit into
mainfrom
docs/nudge-pattern-and-approver-crossref

Conversation

@MCKRUZ

@MCKRUZ MCKRUZ commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Closes #7. Closes #33.

#7 — the pattern the kit used but never documented

Every hook in kit/hooks/ refuses. This adds the other half: a hook that fires on the same event, returns no decision, and drops a sentence into the agent's context at the moment of action.

The rule it settles is gate vs. nudge. Gate when the rule is absolute and the violation is mechanically provable — build red, receipt missing. Nudge when the judgement belongs to the agent, or when a wrong block would cost more than a missed reminder. Without that middle option, those rules go in CLAUDE.md and get forgotten, which is the argument the README already makes for the gates.

Two corrections to the issue as filed:

  • It describes the harvested MAH nudges as PostToolUse. They are PreToolUse, and the difference is load-bearing — a reminder that arrives after the edit is too late to change it.
  • The contract is not in the reference. The docs cover additionalContext and cover permissionDecision, but never the case where you omit the decision entirely — which is the whole trick. Given that section is headed "verified — do not 'improve' these", it was probed rather than inferred, and the README records both the result and the date.

The worked example

sensitive-edit-nudge.{ps1,sh} fires when the agent edits an auth, identity, security, migrations, or infra/ path and reminds it the change is risk:high. Installed but not registered — a nudge encodes one team's policy, so the kit ships the mechanism and leaves the rule to the adopter. Registration snippet is in the README.

Two defects found and fixed in it during verification: PowerShell mangled an em dash in the message to a hyphen, and its hashtable emitted JSON keys in arbitrary order (it flipped between runs). The message is ASCII-only now and the JSON is [ordered], both with comments explaining why — the twins have to emit identical bytes or neither can be pinned by a test.

#33 — the sole-approver cross-reference

docs/anti-patterns.md stated the rule as "a hard rule at every team size", the one place it appeared without pointing at the single-maintainer carve-out in docs/team.md. Both statements were true in their own scope — anti-pattern 2 describes delivery pods, where there genuinely is always a second person — and that is exactly why a reader who had seen both could not tell which governed. Now it points. The glossary already did.

Test plan

  • Advisory contract probed in a live session: the tool ran and the reminder arrived
  • Both twins across nine payloads — sensitive paths, Windows separators, near-miss directories (src/authors/), missing file_path, malformed JSON, empty stdin — byte-identical output, fail-open throughout, exit 0 every time
  • Shipped script end-to-end: fired on src/Api/Auth/TokenService.cs, silent on src/Api/Orders/OrderService.cs
  • python scripts/check_standard.py — no drift
  • pytest scripts/tests — 37 passed
  • .sh twin committed mode 755, LF endings, matching the other three

Follow-ups (not in this PR)

  • The plugin needs scripts/sync_kit.py re-run so claude-code-sdlc/harness/ picks up the new hook — separate repo, separate PR, being opened alongside this one.
  • Gap worth its own issue: nothing verifies the kit's file inventory against the docs that enumerate it. Four lists needed updating by hand here (kit/README.md, two trees in GOLD-STANDARD.md, its HTML twin) and check_standard.py would not have caught a miss — the same class of drift it was built to catch.

Approval

Self-approved under the solo-maintainer carve-out in docs/team.md: internal repo, no second maintainer, mechanical ladder green, named here rather than taken silently.

🤖 Generated with Claude Code

https://claude.ai/code/session_014aesqvsMqJpzEdpDXDTjvF

… carve-out cross-reference

Closes #7. Closes #33.

#7 — the kit documented only how a hook *refuses*. Adds the advisory variant: a
PreToolUse hook that returns no permissionDecision, emits additionalContext, and lets
the call through. The rule it settles is gate-vs-nudge — gate when the violation is
mechanically provable, nudge when the judgement belongs to the agent or a wrong block
would cost more than a missed reminder.

Two corrections to the issue as filed. The MAH nudges it harvests fire on PreToolUse,
not PostToolUse, and the difference is load-bearing: a reminder that arrives after the
edit is too late to change it. And the contract itself is not in the reference — the
docs describe additionalContext but never the omit-the-decision case — so it was probed
rather than inferred, and the README says so.

Ships sensitive-edit-nudge.{ps1,sh} as the worked example: fires on auth/identity/
security/migrations/infra paths, reminds the agent the change is risk:high. Installed
but deliberately unregistered — the mechanism is the kit's, the policy is the team's.
Message is ASCII-only and the JSON is [ordered] because the two twins must emit
identical bytes, and PowerShell mangled an em dash and shuffled hashtable keys.

#33 — anti-patterns.md stated the sole-approver rule as unconditional, the one place it
appeared without pointing at the single-maintainer carve-out. Both statements were true
in their own scope, which is exactly why a reader could not tell which governed.

Verified: hook contract probed live; both twins across nine payloads (sensitive paths,
Windows separators, near-miss directories, malformed JSON, empty stdin) emit identical
bytes and fail open; the shipped script fired end-to-end on a gated path and stayed
silent on an ordinary one; check_standard.py clean; 37 tests pass.

Self-approved under the solo-maintainer carve-out in docs/team.md: internal repo, no
second maintainer, mechanical ladder green, and named here rather than taken silently.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aesqvsMqJpzEdpDXDTjvF
@MCKRUZ
MCKRUZ merged commit 39e92d7 into main Jul 31, 2026
1 check passed
@MCKRUZ
MCKRUZ deleted the docs/nudge-pattern-and-approver-crossref branch July 31, 2026 18:42
MCKRUZ added a commit to MCKRUZ/claude-code-sdlc that referenced this pull request Jul 31, 2026
Regenerates the bundled payload from delivery-standard/kit after
MCKRUZ/intent-driven-development#34, which added the nudge-hook pattern to the kit's
hooks README and shipped sensitive-edit-nudge.{ps1,sh} as its worked example. Without
this the plugin installs a README describing a hook the adopter does not have.

`sync_kit.py` ran clean (123 files, --check reports in sync), but two things needed a
hand afterward and neither is caught by the sync itself:

- shutil.copytree does not carry the git executable bit on Windows, so the new .sh
  landed 100644 while its three siblings are 100755. Installed non-executable it dies
  with 'Permission denied' — the exact failure install_harness.py warns about. Set with
  update-index --chmod=+x. Every future .sh added to the kit will need the same, which
  is worth fixing in sync_kit.py rather than remembering.
- The golden installed-tree snapshot caught the two new files, as designed. Regenerated
  via GOLDEN_REGEN=1; the diff is exactly the two expected paths and nothing else.

531 passed, 6 skipped.

Self-approved under the solo-maintainer carve-out: internal repo, no second maintainer,
suite green, and named here rather than taken silently.


Claude-Session: https://claude.ai/code/session_014aesqvsMqJpzEdpDXDTjvF

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
MCKRUZ added a commit that referenced this pull request Jul 31, 2026
#35 was filed after the list in #34 was written, so the list went stale the same day it
was refreshed. Which is the argument #35 itself makes.


Claude-Session: https://claude.ai/code/session_014aesqvsMqJpzEdpDXDTjvF

Co-authored-by: Claude Opus 5 (1M context) <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.

docs: anti-patterns.md states the sole-approver rule without the carve-out docs: nudge-hook pattern paragraph in kit/hooks/README.md

1 participant