Files
vmux/.planning/phases/03-hook-server/03-01-PLAN.md
2026-03-23 19:33:24 +01:00

10 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
03-hook-server 01 execute 1
hook.go
hook_test.go
protocol.go
protocol_test.go
true
STATE-03
truths artifacts key_links
HookEvent struct parse le JSON de Claude Code hooks (session_id, hook_event_name, notification_type, etc.)
processHookEvent mappe correctement Notification/permission_prompt vers WaitType=permission
processHookEvent mappe correctement Notification/idle_prompt vers WaitType=idle
processHookEvent mappe correctement Stop vers WaitType=question
processHookEvent mappe correctement PostToolUse/PreToolUse vers State=Working et clear WaitType
UpdateFromHook cree une entree si la session n'existe pas encore dans le registre
SessionInfo contient le champ WaitType serialise en JSON
HTTP handler POST /hook retourne 200 et met a jour le registre
HTTP handler refuse les methodes non-POST (405)
HTTP handler refuse les payloads invalides (400)
HTTP handler limite la taille du body (MaxBytesReader 64KB)
path provides
hook.go HookEvent struct, handleHook HTTP handler, processHookEvent, UpdateFromHook
path provides
hook_test.go Tests unitaires pour tous les mappings hook event et le handler HTTP
path provides
protocol.go SessionInfo avec champ WaitType
path provides
protocol_test.go Test serialisation JSON de WaitType
from to via pattern
hook.go daemon.go UpdateFromHook modifie le SessionRegistry registry.UpdateFromHook
from to via pattern
hook.go protocol.go SessionInfo.WaitType WaitType
Hook event processing core: types, mapping, handler HTTP, et mise a jour du registre.

Purpose: Poser toute la logique de traitement des events Claude Code hooks. Chaque type d'event (Notification, Stop, PostToolUse, PreToolUse) est mappe vers le bon State et WaitType. Output: hook.go avec la logique, hook_test.go avec les tests, protocol.go enrichi avec WaitType.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/03-hook-server/03-RESEARCH.md

From protocol.go:

type SessionInfo struct {
    PID          int        `json:"pid"`
    SessionID    string     `json:"session_id"`
    Cwd          string     `json:"cwd"`
    GitBranch    string     `json:"git_branch"`
    State        string     `json:"state"`
    Preview      string     `json:"preview"`
    Workspace    string     `json:"workspace"`
    Label        string     `json:"label,omitempty"`
    WaitingSince *time.Time `json:"waiting_since,omitempty"`
}

From daemon.go:

type TrackedSession struct {
    Info      SessionInfo
    PrevState string
}

type SessionRegistry struct {
    mu       sync.RWMutex
    sessions map[string]*TrackedSession
}

func (r *SessionRegistry) Update(info SessionInfo)
func (r *SessionRegistry) List() []SessionInfo
func (r *SessionRegistry) RemoveStale(activeIDs map[string]bool)

From daemon_test.go:

func newTestDaemon(t *testing.T) *Daemon
Task 1: HookEvent struct, processHookEvent mapping, UpdateFromHook, WaitType dans SessionInfo hook.go, hook_test.go, protocol.go, protocol_test.go - protocol.go (SessionInfo actuel, a enrichir avec WaitType) - daemon.go (SessionRegistry, TrackedSession, pattern Update) - daemon_test.go (newTestDaemon helper, patterns de test) - .planning/phases/03-hook-server/03-RESEARCH.md (payload JSON Claude Code, mapping events) - TestProcessHookNotificationPermission: event Notification + notification_type=permission_prompt donne State="Needs Input", WaitType="permission" - TestProcessHookNotificationIdle: event Notification + notification_type=idle_prompt donne State="Needs Input", WaitType="idle" - TestProcessHookNotificationUnknown: event Notification + notification_type inconnu donne State="Needs Input", WaitType="question" - TestProcessHookStop: event Stop donne State="Needs Input", WaitType="question" - TestProcessHookPostToolUse: event PostToolUse donne State="Working", WaitType="" - TestProcessHookPreToolUse: event PreToolUse donne State="Working", WaitType="" - TestProcessHookIgnoresEmptySessionID: event avec session_id="" ne modifie pas le registre - TestProcessHookIgnoresUnknownEvent: event avec hook_event_name inconnu ne modifie pas le registre - TestUpdateFromHookCreatesNewEntry: session_id inconnu du registre cree une nouvelle entree - TestUpdateFromHookSetsWaitingSince: transition Working vers NeedsInput met WaitingSince - TestUpdateFromHookClearsWaitingSince: transition NeedsInput vers Working efface WaitingSince - TestSessionInfoWaitTypeJSON: WaitType="permission" est serialise dans le JSON, WaitType="" est omis (omitempty) 1. Ajouter `WaitType string json:"wait_type,omitempty"` a SessionInfo dans protocol.go 2. Creer hook.go avec : - HookEvent struct (session_id, transcript_path, cwd, hook_event_name, notification_type, message, title, last_assistant_message, stop_hook_active, tool_name). Tous les champs JSON avec tags corrects et omitempty pour les optionnels. - `func (d *Daemon) processHookEvent(event HookEvent)` : switch sur event.HookEventName pour mapper vers state+waitType, puis appel a registry.UpdateFromHook. Ignorer session_id vide et events inconnus. - `func (r *SessionRegistry) UpdateFromHook(sessionID, state, waitType, cwd string)` : lock, creer l'entree si absente, mettre a jour Info.SessionID/State/WaitType/Cwd, gerer la transition WaitingSince (meme logique que Update mais sans ecraser les autres champs). 3. Creer hook_test.go avec les 12 tests ci-dessus. Utiliser newTestDaemon pour les tests processHookEvent, NewRegistry directement pour les tests UpdateFromHook. 4. Ajouter un test dans protocol_test.go pour la serialisation JSON de WaitType.
NE PAS ajouter le handler HTTP dans cette tache (c'est la tache 2).
NE PAS modifier daemon.go (pas de startHookServer, pas de hookPort).
nix-shell --run "go test -v -run 'TestProcessHook|TestUpdateFromHook|TestSessionInfoWaitType' ./..." - grep -q 'WaitType.*string.*json:"wait_type' protocol.go - grep -q 'type HookEvent struct' hook.go - grep -q 'func.*Daemon.*processHookEvent' hook.go - grep -q 'func.*SessionRegistry.*UpdateFromHook' hook.go - grep -q 'TestProcessHookNotificationPermission' hook_test.go - grep -q 'TestProcessHookStop' hook_test.go - grep -q 'TestUpdateFromHookCreatesNewEntry' hook_test.go - grep -q 'TestSessionInfoWaitTypeJSON' protocol_test.go Les 12 tests passent. HookEvent parse le JSON Claude Code. processHookEvent mappe les 4 types d'events vers le bon State/WaitType. UpdateFromHook gere les nouvelles sessions et les transitions WaitingSince. SessionInfo.WaitType est serialise en JSON. Task 2: HTTP handler POST /hook avec validation et protection hook.go, hook_test.go - hook.go (HookEvent et processHookEvent crees par Task 1) - daemon_test.go (newTestDaemon, patterns httptest) - .planning/phases/03-hook-server/03-RESEARCH.md (handler pattern, MaxBytesReader) - TestHandleHookPostOK: POST /hook avec payload Notification valide retourne 200 et met a jour le registre - TestHandleHookMethodNotAllowed: GET /hook retourne 405 - TestHandleHookBadJSON: POST /hook avec body invalide retourne 400 - TestHandleHookBodyTooLarge: POST /hook avec body > 64KB retourne 400 1. Ajouter dans hook.go : - `func (d *Daemon) handleHook(w http.ResponseWriter, r *http.Request)` : verifie Method==POST (sinon 405), applique http.MaxBytesReader(w, r.Body, 64*1024), decode JSON dans HookEvent (sinon 400), appelle processHookEvent, retourne 200 OK. 2. Ajouter les 4 tests dans hook_test.go. Utiliser httptest.NewRequest + httptest.NewRecorder pour tester le handler directement (pas besoin d'ouvrir un port). 3. Pour TestHandleHookPostOK, verifier que le registre contient la session avec le bon WaitType apres l'appel. 4. Pour TestHandleHookBodyTooLarge, generer un body de 65KB.
NE PAS demarrer de serveur HTTP dans cette tache. Le handler est teste unitairement via httptest.
nix-shell --run "go test -v -run 'TestHandleHook' ./..." - grep -q 'func.*Daemon.*handleHook' hook.go - grep -q 'MaxBytesReader' hook.go - grep -q 'StatusMethodNotAllowed' hook.go - grep -q 'TestHandleHookPostOK' hook_test.go - grep -q 'TestHandleHookMethodNotAllowed' hook_test.go - grep -q 'TestHandleHookBadJSON' hook_test.go - grep -q 'TestHandleHookBodyTooLarge' hook_test.go Le handler HTTP /hook accepte les POST valides (200), rejette les non-POST (405), rejette le JSON invalide (400), limite la taille du body a 64KB. 4 tests passent. nix-shell --run "go test -v -run 'TestProcessHook|TestUpdateFromHook|TestHandleHook|TestSessionInfoWaitType' ./..." nix-shell --run "go vet ./..."

<success_criteria>

  • 16 tests passent (12 task 1 + 4 task 2)
  • HookEvent struct couvre tous les champs du payload Claude Code
  • processHookEvent mappe les 4 types d'events correctement
  • UpdateFromHook gere les sessions inconnues et les transitions WaitingSince
  • handleHook valide method, body size, et JSON
  • SessionInfo.WaitType visible dans le JSON
  • Aucune regression sur les tests existants </success_criteria>
After completion, create `.planning/phases/03-hook-server/03-01-SUMMARY.md`