feat(approvals): make the inbox opt-in (default OFF) and drop em-dashes

Owner decision: every Approvals Inbox UI surface (header bell, drawer,
phone overview answer strips, reload seeding) now requires enabling
approvalsInboxEnabled in App Settings -> Panels; only an explicit true
turns it on. The store, endpoints, and push Approve/Deny actions keep
running regardless (the push buttons are already opt-in per subscription).

Also replaces em-dashes with plain punctuation across the newly authored
comments, docs, and strings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-08-09 15:55:51 +02:00
parent ff10a50bc0
commit 6c744f8677
13 changed files with 54 additions and 51 deletions
+2 -2
View File
@@ -1,7 +1,7 @@
/**
* Approvals Inbox store unit tests (src/web/approval-inbox.ts).
*
* Pure in-memory registry — no ports, no server. Constructs its own
* Pure in-memory registry: no ports, no server. Constructs its own
* ApprovalInbox instances (never the process singleton) so tests cannot
* leak state into the route tests that share the module.
*/
@@ -118,7 +118,7 @@ describe('normalizeCapturedFrame', () => {
});
it('converts absolute row repaints (formatPaneSnapshot frames) into lines', () => {
// The visible tmux capture carries NO newlines — every row is painted at
// The visible tmux capture carries NO newlines; every row is painted at
// `ESC[<row>;1H`. Measured against a live dialog frame.
const raw = '\x1b[12;1H Which color do you prefer?\x1b[13;1H❯ 1. Red\x1b[14;1H Prefer red\x1b[15;1H 2. Blue';
const out = normalizeCapturedFrame(raw)!;
+5 -5
View File
@@ -1,11 +1,11 @@
/**
* Approvals Inbox route tests (src/web/routes/approval-routes.ts) via app.inject()
* — no live port. The hook-event route is registered alongside so items are
* Approvals Inbox route tests (src/web/routes/approval-routes.ts) via app.inject(),
* no live port. The hook-event route is registered alongside so items are
* created through the REAL ingestion path (sanitize → notePrompt with the
* terminal-buffer capture fallback), not by poking the store directly.
*
* The routes read the process-wide `approvalInbox` singleton, so every test
* drains it in afterEach — a leaked pending item would bleed into the next test.
* drains it in afterEach; a leaked pending item would bleed into the next test.
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import Fastify, { type FastifyInstance } from 'fastify';
@@ -203,7 +203,7 @@ describe('approval routes', () => {
it('refuses with 409 when the dialog left the screen since capture', async () => {
await postHook(harness, 'permission_prompt', {});
const [item] = await listApprovals(harness);
// The dialog scrolled away — the re-capture at answer time must refuse.
// The dialog scrolled away, so the re-capture at answer time must refuse.
session.terminalBuffer = 'claude is off doing something else now';
const res = await harness.app.inject({
method: 'POST',
@@ -301,7 +301,7 @@ describe('approval routes', () => {
});
});
describe('approval routes — multi-user scoping', () => {
describe('approval routes: multi-user scoping', () => {
const saved: Record<string, string | undefined> = {};
beforeEach(() => {