Skip to content

refactor(hooks): hand rules 7-8 over with the first hwip_ask result - #3

Merged
tak-bro merged 4 commits into
mainfrom
refactor/normal-inject-diet
Oct 25, 2026
Merged

tak-bro merged 4 commits into
mainfrom
refactor/normal-inject-diet

Conversation

@tak-bro

@tak-bro tak-bro commented Oct 25, 2026 •

Copy link
Copy Markdown
Member

Summary

NORMAL 상태로 시작하는 모든 세션이 내던 SessionStart 주입문을 3,348자에서 2,489자로 줄였다(−859자, 기본 quiet 다이얼 기준). 모델이 할 수 있는 일은 그대로다.

  • 규칙 7·8("Handling the response")은 첫 hwip_ask 결과와 함께 들어간다. 이 규칙은 hwip_ask 답을 다룰 때만 쓰인다. 그래서 시작 주입문에서 빼고, ^mcp__.*__hwip_ask$ 에 거는 새 PostToolUse 훅이 세션 컨텍스트마다 한 번 주입한다. 주입하는 문구는 docs/CORE_RULES.md 의 같은 섹션을 그대로 잘라 쓴다.
  • quiet Push Signal 문단을 592자에서 397자로 줄였다. 담긴 사실은 하나도 빼지 않았다. 다른 하네스로 나가는 추출본에는 Push Signal 이 들어가지 않으므로 추출 결과도 바뀌지 않는다.

REMOTE·one-way·muted 분기, Rule 9, hwip-remote.sh 출력, mcp-server 는 건드리지 않았다.

Changes

  • hooks/hwip-ask-result.js(신규): PostToolUse 훅.
    • 마크 handling-injected-<sid> 로 한 번만 주입한다.
    • subagent 호출(agent_id 있음)이면 마크를 읽지도 쓰지도 않는다.
    • ask 훅이 쓴 마크는 30분이 지나면 무효가 된다.
    • 그 밖의 실패는 fail open 이다.
  • hooks/core-rules.js(신규): section·pickBranch·상태 경로·훅 입력 읽기를 두 훅이 같이 쓰도록 모았다.
  • hooks/hwip-setup.js: normal() 에서 Handling 을 뺐다. 마크 처리를 일반화했다.
    • 모든 SessionStart 가 마크를 지운다.
    • REMOTE 계약을 내보내는 시작은 session-start 마크를 남긴다(그 계약에 규칙 7·8 이 이미 들어 있다).
  • .claude-plugin/plugin.json: PostToolUse matcher 항목을 추가했다(timeout 5).
  • docs/CORE_RULES.md: quiet Push Signal 문구를 바꾸고 Last updated 를 갱신했다.
  • tests/test-hwip-ask-result.sh(신규, 28개)와 tests/test-hwip-setup.sh(+8개): 위 동작을 고정한다.
  • docs/HOOKS-EXPLAINED.md·docs/ARCHITECTURE.md·README.md·tests/README.md: 새 훅과 크기를 반영했다.

How to Test

bash tests/run-all.sh            # Suites: 17/17 passed
bash tests/test-hwip-ask-result.sh   # Total: 28 | Failed: 0
node scripts/lint-rules-sync.js  # Sync contract holds

Evidence

  • before(구현 전 red):
    • bash tests/test-hwip-setup.sh 에서 ✗ NORMAL leaves rules 7·8 to the hwip_ask result 와 ✗ quiet Push Signal fits in 400 chars(블록 592자)가 실패했다.
    • bash tests/test-hwip-ask-result.sh 는 훅이 없어 ✗ injects rules 7·8 로 실패했다.
  • after: 같은 명령이 모두 green 이다. 전체 suite 는 17/17 통과다.
  • 엔진 probe(이번 세션에서 실측):
    • PostToolUse additionalContext 는 빠른 반환(claude -p)이면 tool result 와 함께 모델에 들어간다.
    • 120s 를 넘겨 백그라운드로 옮겨진 hwip_ask(interactive tmux)에서는 "moved to background" 결과에 붙어 최종 답보다 먼저 도착한다.
    • subagent 안 툴 호출의 훅 입력에는 agent_id·agent_type 이 들어 있고, session_id 는 부모와 같다. main 호출에는 둘 다 없다.

Acceptance

  • AC-1 · PASS · s01-normal green | ✓ NORMAL leaves rules 7·8 to the hwip_ask result
  • AC-2 · PASS · s01-post green | Total: 28 | Passed: 28 | Failed: 0
  • AC-3 · PASS · s01-post green | Total: 28 | Passed: 28 | Failed: 0
  • AC-4 · PASS · s02-quiet green | ✓ quiet Push Signal fits in 400 chars
  • AC-5 · PASS · test green | Suites: 17/17 passed
  • audit: ok (5 checked rows at 32f59cc)

Risks

  • cli 미러는 바꾸지 않았다(의도한 것). cli src/templates.ts 의 PROMPT_HOOK_CORE 는 여전히 Handling 을 담는다. 다른 하네스에는 PostToolUse 가 없어서 시작 주입문에 계속 실어야 한다. 이 PR 은 Claude Code 플러그인 주입만 줄인다.
  • 기존 문제로 중복 주입이 한 번 생길 수 있다. scripts/hwip-mode.sh:77 이 session_id 없이 setup 을 다시 돌리면 모든 세션의 handling-injected-* 마크가 지워진다. 결과는 세션마다 규칙이 한 번 더 들어가는 것뿐이다(fail open). 이 PR 이 만든 문제가 아니다.
  • /rewind·restore 가 규칙을 실은 결과를 지울 수 있다. 이때는 SessionStart 가 없어 마크가 남는다. 그래서 ask 훅이 쓴 마크에 30분 TTL 을 두었고, 최악의 경우에도 30분이 지나면 규칙이 다시 들어간다.

Merge danger

  • Door: two-way. 되돌리면 이전 주입문으로 돌아간다. 외부 계약·스키마는 바뀌지 않는다.
  • Blast radius: plugin-hooks

Alternatives considered

  • 규칙 7·8 을 mcp-server 의 hwip_ask 결과 JSON 에 넣는 안은 버렸다. ask.test.ts 가 결과 JSON 을 toEqual 로 고정하고 있어 서버 릴리스와 플러그인 버전이 어긋난다.
  • 두 훅이 블로킹 stdin reader 하나를 같이 쓰는 안도 버렸다. hwip-mode.sh 는 호출자 stdin 을 물려받아 setup 을 돌리는데, 이 경로에서 블로킹 읽기가 멈춘다. setup 은 원래의 non-blocking 읽기를 유지한다.
  • Rule 13(Persistence, 179자)을 빼는 안도 버렸다. 테스트가 고정하고 있고 compaction 뒤에도 규칙을 붙잡아 두는 역할을 한다.

Auto-decided

  • 마크 수명은 remote-injected-<sid> 와 같은 방식이다. 모든 SessionStart 가 마크를 지우고, REMOTE 계약을 내보내는 시작이 마크를 세운다.
  • PostToolUse 훅은 mute·hookId 를 검사하지 않는다. hwip_ask 가 실제로 돌아왔다면 그 답에는 규칙 7·8 이 필요하기 때문이다.
  • Rule 13 은 유지한다.
  • 리뷰에서 기각한 Nice 항목:
    • core-rules.js lazy require: 기존 notify-failure require 와 같은 패턴이다.
    • !apiKey 조기 종료 경로의 마크 처리: 설정이 없으면 hwip_ask 가 돌 수 없다.
    • 상대 경로 XDG_STATE_HOME: 셸 훅들과 공유하는 기존 공식이다.

폰 실사용 확인 (운영 hwip, 브랜치 플러그인 --plugin-dir)

  • SessionStart 주입문에는 "Handling the response" 가 0건이었다.
  • 첫 hwip_ask 결과에 PostToolUse 가 규칙 7·8 을 한 번 넣었고, 마크 ask 가 남았다.
  • 폰에서 "echo 실행" 을 누르자 다시 묻지 않고 바로 echo hwip-ok 를 실행했다(규칙 7).
  • 결과가 hwipState: REMOTE 여서 모델이 다음 응답을 hwip_ask 로 끝냈다. 이 두 번째 결과에는 규칙이 다시 주입되지 않았다.
  • "완료" 를 누르자 hwipState: NORMAL 로 돌아왔다.

Release

  • plugin·package 버전을 0.1.2 로 올렸다(사용자 결정). Claude Code 는 버전이 바뀌어야 설치본을 갱신한다.

Checklist

  • bash tests/run-all.sh 17/17
  • node scripts/lint-rules-sync.js Sync contract holds
  • 슬라이스별 리뷰 게이트 PASS, /review --full Approve(범프 뒤 재리뷰 포함)
  • 폰 hwip_ask 실사용 확인(운영)

🤖 Generated with Claude Code

The NORMAL SessionStart text carried "Handling the response" in every
session, although those rules only govern a hwip_ask answer. A
PostToolUse hook on hwip_ask now injects the same CORE_RULES section
once per session context, so terminal sessions that never ask stop
paying for it. Claude Code attaches the context to the background-move
result too, so the rules still land before a slow answer.

The handling-injected mark follows remote-injected: every SessionStart
drops it, and a REMOTE start (which ships the rules) sets it. A mark
the ask hook wrote ages out after 30 minutes because /rewind can drop
the carrying result without a SessionStart, and a subagent call never
touches it. Slicing moves to hooks/core-rules.js so both hooks read
the same text.
Every NORMAL session on the default quiet dial pays for this block.
The rewording keeps each fact (only high counts, skip/push are inert,
once on an awaited completion, never routine, ignored on a hwip_ask
turn, not to reach a user who left) and cuts the block from 592 to
397 chars. A length check plus clause-level fact checks pin it. Push
Signal stays out of the cross-harness extraction, so the extract is
unchanged; the one-way copy is left as is.
Ship the slimmer NORMAL injection and the hwip_ask result hook to
existing installs; Claude Code only refreshes a plugin when its
version changes.
ARCHITECTURE.md named only the SessionStart writes; the ask hook also
writes the mark, which ages out after 30 minutes, and subagent calls
never touch it. The quiet-block length check now counts code points
with node, since wc -m under LC_ALL=C counted the heading's em dash as
three and left one char of headroom.
@tak-bro
tak-bro marked this pull request as ready for review October 25, 2026 12:20
@tak-bro
tak-bro merged commit 820697b into main Oct 25, 2026
2 checks passed
@tak-bro
tak-bro deleted the refactor/normal-inject-diet branch October 25, 2026 12:20
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