feat(web): F12 — stale + blind-spot push notifications (PRD-007/RFC-006)#37
Merged
Conversation
Implements PRD-007 + RFC-006. Browser Notification API (no Service
Worker) fires on workspace health changes detected by the 10s
/api/health poll diff.
Detection (entities/health/lib/notify.svelte.ts):
- detectBreaches(prev, next) returns 3 categories:
- new blind_spot — id appeared in next.blind_spots that wasn't in prev
- stale — next.stale_count > prev.staleCount
- orphan — next.orphan_count > prev.orphanCount
- All pure functions; 9 vitest unit tests cover snapshot extraction,
every breach category, permission branches (granted/denied/missing),
throttle window with injectable now() clock.
Permission UX (HealthBar.svelte):
- 🔔/🔕 toggle button with active state when granted+opted-in.
- First click → requestPermission(); on grant, sets notify=true.
- denied → button disabled with tooltip "Re-enable in browser site
settings".
- Feature-detect: hidden when Notification API absent (NFR-003).
- Hidden aria-live="polite" .sr-only mirror for screen-readers.
Wiring (HomePage.svelte):
- One $effect watches healthPoller.state.data — diffs against
prevHealthSnapshot, runs detectBreaches, fires per-breach
notification with onClick → focusArtifact(id).
- Second $effect watches notifyBus.pendingFocus — when set,
selectNode({id}) and clears. Bus pattern lets notification's
onclick (foreign closure) reach Svelte 5 state without prop drill.
Settings (settings.ts):
- notify: boolean added to PersistedSettings + ResolvedSettings.
- Default false. Loaded/saved through existing localStorage flow.
Throttle:
- ≥ 60s per category (FR-007).
- silent: true on Notification ctor (no audio).
- tag: `forgeplan.${kind}` collapses repeats in the OS notification
centre.
- No body content in payload — only id + title for blind_spots,
count delta for stale/orphan (NFR-002 privacy).
Vitest config:
- pool: 'threads' (was default 'forks'). Default per-file child
process spawning blew through macOS kern.maxprocperuid at 8
test files; threads share heap, no spawn() per file.
Verify:
- svelte-check 0/0/434.
- npm test 62/62 (53 baseline + 9 new).
- node scripts/smoke.mjs PASS.
Refs: PRD-007 RFC-006
EVID-014 (Tactical, supports / CL3 / test) — DOM-verified F11 acceptance: PR #36 merged to develop, 13 new vitest tests, 3-OS smoke matrix green. SC-1..SC-6, SC-8, SC-9 all pass; SC-7 (bundle delta) tracked but pending per-bundle measurement. Linked: informs PRD-006, informs RFC-005, builds-on EVID-013. Status transitions: - PRD-006: draft → active - RFC-005: draft → active These activations ride along with the F12 PR for atomic delivery. F11 code already shipped in PR #36; this commit only updates artifact metadata and adds the evidence pack. Refs: PRD-006 RFC-005 EVID-014
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements PRD-007 + RFC-006. Browser Notification API (no Service Worker) fires when the 10s
/api/healthpoll detects workspace decay. Plus EVID-014 closes F11 and activates PRD-006 + RFC-005.Detection
entities/health/lib/notify.svelte.tsexports pure helpers:snapshotFromHealth/detectBreaches/notificationsSupported/notificationPermission/requestPermission/fire.now()for tests.Permission UX
requestPermission(); grant flips toggle on.'Notification' in window === false(Firefox no-API users, SSR).Click → focus
onclickwrites to anotifyBus.pendingFocus$state singleton.$effectreads it and callsselectNode({id}). Cross-scope state without prop drill.Privacy & a11y
silent: trueon the Notification (no audio).aria-live="polite".sr-onlymirror in HealthBar — screen readers announce regardless of OS notification visibility.Settings
notify: booleanfield added; persisted via existingloadSettings/saveSettingslocalStorage flow.Vitest config
pool: 'threads'(was defaultforks). Default per-file child process spawning blew through macOSkern.maxprocperuidat 8 test files; threads share heap, nospawn()per file. Fix landed during build.EVID-014 / activations
Verify
npx svelte-check— 0/0/434 files.npm test— 62/62 (53 baseline + 9 new).node scripts/smoke.mjs— PASS.Test plan
forgeplan mark-stale <id>in the workspace; observe browser notification; click it; tab focuses + artifact panel opens.Refs:
PRD-006RFC-005EVID-014PRD-007RFC-006🤖 Generated with Claude Code