docs(09-02): complete enqueue-site signal wiring plan
- 09-02-SUMMARY.md: 4 signalOutboxDrain() sites + initOutboxTrigger() startup wiring - STATE.md: plan position advanced, metrics recorded - ROADMAP.md: plan progress updated - REQUIREMENTS.md: CAL-15 marked complete
This commit is contained in:
@@ -0,0 +1,100 @@
|
|||||||
|
---
|
||||||
|
phase: 09-faster-write-back
|
||||||
|
plan: "02"
|
||||||
|
subsystem: api-routes-startup
|
||||||
|
tags: [outbox, event-driven, drain-signal, caldav, startup-wiring]
|
||||||
|
dependency_graph:
|
||||||
|
requires:
|
||||||
|
- signalOutboxDrain (apps/api/src/lib/outboxTrigger.ts — Plan 01)
|
||||||
|
- initOutboxTrigger (apps/api/src/broker/outboxWorker.ts — Plan 01)
|
||||||
|
provides:
|
||||||
|
- Four post-commit signalOutboxDrain() publish sites in events.ts (create, edit-as-move, same-cal update, delete)
|
||||||
|
- initOutboxTrigger() wired at startup in index.ts under isMainModule()
|
||||||
|
affects:
|
||||||
|
- apps/api/src/routes/events.ts
|
||||||
|
- apps/api/src/index.ts
|
||||||
|
tech_stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- Fire-and-forget in-process EventEmitter signal after each outbox enqueue commit (D-04)
|
||||||
|
- isMainModule() guard for startup-only listener registration (Pitfall 4)
|
||||||
|
- edit-as-move signal fires after await db.transaction() resolves, not inside callback (D-03)
|
||||||
|
key_files:
|
||||||
|
created: []
|
||||||
|
modified:
|
||||||
|
- apps/api/src/routes/events.ts
|
||||||
|
- apps/api/src/index.ts
|
||||||
|
decisions:
|
||||||
|
- "D-03-signal-placement: signalOutboxDrain() for edit-as-move placed AFTER await db.transaction() resolves (line after the statement), never inside the async callback — guarantees DELETE+CREATE rows are durably committed before any drain observes them (SC-3 / Pitfall 1)"
|
||||||
|
- "D-04-fire-and-forget: signalOutboxDrain() is never awaited — synchronous emit to in-process EventEmitter, cannot block the 202 response (SC-2 / T-09-07)"
|
||||||
|
- "Pitfall4-guard: initOutboxTrigger() called only inside isMainModule() block — tests importing app never register the drain listener, no open handles in test process (T-09-06)"
|
||||||
|
metrics:
|
||||||
|
duration_seconds: 240
|
||||||
|
completed_date: "2026-06-12"
|
||||||
|
tasks_completed: 2
|
||||||
|
files_changed: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 09 Plan 02: Enqueue-Site Signal Wiring Summary
|
||||||
|
|
||||||
|
**One-liner:** Four fire-and-forget `signalOutboxDrain()` publish sites added to events.ts (create, edit-as-move-after-transaction, same-cal update, delete) and `initOutboxTrigger()` wired at startup in index.ts under `isMainModule()`, connecting the Plan 01 EventEmitter mechanism to live writes and eliminating the up-to-15s polling delay.
|
||||||
|
|
||||||
|
## What Was Built
|
||||||
|
|
||||||
|
### Task 1: `apps/api/src/routes/events.ts` — Four publish sites
|
||||||
|
|
||||||
|
Import added: `import { signalOutboxDrain } from '../lib/outboxTrigger.js';`
|
||||||
|
|
||||||
|
Four `signalOutboxDrain()` calls inserted, each fire-and-forget (never awaited):
|
||||||
|
|
||||||
|
| Site | Route | Placement |
|
||||||
|
|------|-------|-----------|
|
||||||
|
| 1 | POST /create | After `await db.insert(calendarOutbox).values({...})`, before `return c.json({ uid }, 202)` |
|
||||||
|
| 2 | PATCH /:uid/edit (edit-as-move) | After `await db.transaction(async (tx) => {...})` RESOLVES, before `return c.json({ uid: newUid }, 202)` — critical: NOT inside the callback |
|
||||||
|
| 3 | PATCH /:uid/edit (same-cal update) | After `await db.insert(calendarOutbox).values({ operation: 'update', ... })`, before `return c.json({ uid }, 202)` |
|
||||||
|
| 4 | DELETE /:uid | After `await db.insert(calendarOutbox).values({ operation: 'delete', ... })`, before `return c.json({ uid }, 202)` |
|
||||||
|
|
||||||
|
No route status codes, response bodies, validation, or ownership checks were modified. No inline CalDAV calls added. No signal inside the `db.transaction` callback. No signal inside any `catch` block.
|
||||||
|
|
||||||
|
### Task 2: `apps/api/src/index.ts` — Startup wiring
|
||||||
|
|
||||||
|
- Import extended: `import { startOutboxWorker, initOutboxTrigger } from './broker/outboxWorker.js';`
|
||||||
|
- `initOutboxTrigger();` added immediately after `startOutboxWorker();` inside the `if (isMainModule())` block (line 139), with comment `// subscribe drain signal listener (D-01)`
|
||||||
|
- `isMainModule()`, `startBrokerPoller`, `startReminderScheduler`, and `serve(...)` unchanged
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
None — plan executed exactly as written.
|
||||||
|
|
||||||
|
## Verification Evidence
|
||||||
|
|
||||||
|
```
|
||||||
|
grep -c "signalOutboxDrain()" apps/api/src/routes/events.ts → 4
|
||||||
|
grep -n "await signalOutboxDrain" apps/api/src/routes/events.ts → (none)
|
||||||
|
initOutboxTrigger() at line 139 — inside isMainModule() guard
|
||||||
|
|
||||||
|
npx tsc --noEmit → (no output, clean)
|
||||||
|
|
||||||
|
npx vitest run tests/routes/events.test.ts tests/broker/outboxWorker.test.ts
|
||||||
|
Test Files 2 passed (2)
|
||||||
|
Tests 53 passed (53)
|
||||||
|
|
||||||
|
Full suite: 240 passed, 1 failed (known pre-existing flaky timeout in lists.test.ts:
|
||||||
|
"toggling isShared false→true re-populates list_shares for other members" — 5028ms,
|
||||||
|
exceeds 5000ms global testTimeout; passes with --testTimeout=30000; unrelated to this plan)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Known Stubs
|
||||||
|
|
||||||
|
None.
|
||||||
|
|
||||||
|
## Threat Flags
|
||||||
|
|
||||||
|
None — this plan introduces no new network endpoints, auth paths, file access patterns, or schema changes. T-09-05 (edit-as-move signal placement) and T-09-06 (listener registration gating) are both verified: signal is after the transaction resolves, and `initOutboxTrigger` is inside `isMainModule()`.
|
||||||
|
|
||||||
|
## Self-Check: PASSED
|
||||||
|
|
||||||
|
- `apps/api/src/routes/events.ts` — FOUND; 4 signalOutboxDrain() calls verified
|
||||||
|
- `apps/api/src/index.ts` — FOUND; initOutboxTrigger() inside isMainModule() at line 139 verified
|
||||||
|
- Task 1 commit `30eff3d` — FOUND
|
||||||
|
- Task 2 commit `0ebdf48` — FOUND
|
||||||
Reference in New Issue
Block a user