aide-build-b4-notifications · git:20260823.df7cbde · 2026-08-23 · sha256 2e95a8ad9318374c
aide-build-b4-notifications git:20260823.df7cbdeC
Immutable. This exact content is served forever at /api/v1/blob/2e95a8ad9318374c.
# B4 — Notification Harness (OS toasts + in-app center + task hooks)
Phase skill for AIDE BUILD-series B4. Master router: aide-master-roadmap. Research base: VS Code notification API patterns, ntfy.sh semantics, OSC 9 / 777 / 99 terminal sequences, Claude Code hook model (PreToolUse/PostToolUse/Notification/Stop events with configurable commands).
## What & Why
Long tasks (builds, training jobs, agent runs) need to tell the human "done/failed" WITHOUT the user staring at the terminal. Users will compare against VS Code/Cline where task completion is visible. B4 = one harness, three sinks:
1. **In-app center**: stacked toasts (bottom-right), severity levels, actions ("Show output", "Reveal problem"), persistent list.
2. **OS native toast**: Windows toast via PowerShell `New-BurntToastNotification`-free approach — use `Windows.UI.Notifications` through a tiny bundled helper OR fallback `[System.Windows.Forms]` balloon; MUST work offline, no deps. Gate behind user setting `notifications.os.enabled` (default ON for long tasks only).
3. **Terminal OSC**: when a task runs in the integrated terminal, emit `\x1b]9;<msg>\x07` (and 777 for notify-send ecosystems) so tmux/terminal notifiers pick it up.
## Hook Model (Claude-Code-inspired, local-only)
Workspace file `.aide/hooks.json`:
```json
{ "hooks": { "task.completed": [{ "command": "git rev-parse --short HEAD", "show": true }],
"task.failed": [{ "command": "npm run log-error" }] } }
```
- Events v1: task.started, task.completed, task.failed, diagnostics.new (B2 feed).
- Hooks run through the EXISTING process manager (spawn/execFile, tree kill on Windows, timeout). Never shell:true. Output captured, truncated at 4KB, shown as collapsible detail in the toast.
- Hooks are LOCAL commands only — if a command contains http(s):// or curl/wget/Invoke-WebRequest tokens, require explicit one-time consent per pattern (No-Phone-Home Law; V1 audits this).
## Contract
```ts
Notification: { id, severity:'info'|'warn'|'error'|'success', source:'task'|'hook'|'daemon'|'user', title, body?, jobId?, createdAt, read:boolean }
HookEvent = 'task.started'|'task.completed'|'task.failed'|'diagnostics.new'
HookConfig: { event, command:string[], show?:boolean, timeoutMs?:number(default 10_000) }
```
## Routes
- `GET /api/notifications?unread=true` -> list
- `POST /api/notifications/{id}/read`, `POST /api/notifications/read-all`
- `GET /api/hooks`, `PUT /api/hooks` (validate against contract; reject unknown events)
- WS channel `notifications` pushes new notifications live.
## Tests FIRST
1. Task completes via B1 run() -> notification emitted on WS with jobId linkage.
2. Hook fires on task.failed with correct exit-code context; output captured+truncated.
3. Hook timeout -> killed (process tree gone — assert no orphan node child), notification shows 'hook timed out'.
4. Network-suspicious hook requires consent -> without consent it's rejected with envelope error.
5. OS toast helper: unit test only asserts the generated PS script text contains expected literal + setting gate respected (don't actually pop toasts in CI).
6. Arch tests: routes validate strict bodies (unknown field -> 400), error envelope shape.
## Pitfalls
- Windows toast APIs differ across builds: wrap in try/catch, degrade silently to in-app only. Never crash the daemon over a toast.
- Do NOT reuse the B1 cmd bridge blindly here: hooks are argv arrays (execFile-style), which is SAFER than string commands — keep them arrays end-to-end from JSON to spawn.
- Don't spam: coalesce identical notifications within 2s window.
## Gate
Unit+arch green, openapi zero-diff after regen, e2e: run failing task -> toast appears + hook wrote marker file, all offline. Journal.