Task 9.5: /health now reports the egress provider backing each channel (sandbox
vs http). scripts/smoke-capability.mjs proves the slice end to end: sandbox egress
SENT through the broker; EMAIL routed to the env-gated HttpProvider and actually
delivered to a LOCAL echo server (real egress, no external network, no credentials);
idempotent replay; /health bindings. App wires CapabilityModule.
P9 slice-1 verification: 103 tests green, pnpm -r build all packages+demos,
boundary clean, capability smoke PASS, all prior smokes (realtime/inbox/support/
adapter/route/ai/calendar) still PASS through the broker.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 9.4: certifyProvider runs the P9 conformance checklist a provider must pass
before backing a channel — declares explicit capabilities, returns providerRef +
recognized outcome (never throws), repeatable/idempotent, tolerates empty payload.
2 tests: SandboxProvider passes; a throwing/no-capability provider fails with the
specific failing checks listed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 9.3: OutboundService now delegates the execute step to CapabilityBroker
(policy-gated + obligation-enforced) instead of calling a provider directly, while
keeping idempotency + rate-limit + the command/attempt ledger. BLOCKED obligations
→ command BLOCKED; a fail-closed throw marks the command FAILED then propagates.
AdaptersModule imports CapabilityModule; P4/P6/P8 egress now flows through the
broker unchanged. Updated 6 spec constructions; added a DENY_OUTBOUND test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 7.6: apps/ai-studio (Vite, port 5177) — ingest a message, run CLASSIFY/
SUMMARIZE/EXTRACT, see proposal cards with confidence, evidence citations,
abstention, model/cost + a budget meter, and accept/reject; built on the ai-web
SDK via AiProvider. scripts/smoke-ai.mjs proves the P7 guarantees end to end:
auto-CLASSIFY → AI flag → routing REVIEW/DENY never ALLOW (KG-05), summary cites
evidence, EXTRACT stays DISCUSSION (Scenario 7), replay-once, accept.
P7 verification: 79 tests green (3x stable), pnpm -r build all packages+demos,
boundary passes and fails on ai-web->service, smoke-ai PASS, prior smokes
(realtime/inbox/support/adapter/route) PASS.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 7.4: route-simulator now unions RULE flags with persisted AI moderation
flags before decide() — so AI flags influence routing exactly like rule flags,
only ever REVIEW/DENY, never ALLOW. SUMMARY/TRANSCRIPT/DIGEST previews render a
proposed AI summary (aiSourced flag) instead of the P6 stub, still preview-only.
AiProjector auto-runs CLASSIFY on interaction.normalized (channel-bearing only),
idempotent via claim(). 4 tests prove KG-05: AI flag forces REVIEW on an
auto-ALLOW binding, DENY on CHILD, AI summary in preview with 0 sends, projector
idempotency. Full suite 79 green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 7.3: AiJobService runs scoped/purposed AI jobs that only propose — fails
closed via OPA (KG-03), degrades on budget (AiBudgetGuard sums costUnits, KG-12),
then persists job+modelRun+artifact(PROPOSED)+claims+evidence with full
provenance. CLASSIFY also proposes IiosModerationFlag(proposedBy:'AI'). accept/
reject record human feedback + emit events. Idempotent per job key. INFERENCE_PORT
binds LocalDeterministicInference for nest start; specs inject testkit FakeInference.
8 tests: classify/summarize/extract, fail-closed, budget-degrade, idempotent, accept.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 7.2: FakeInference implements the InferencePort with keyword vocabulary
mirroring the P6 RuleEngine; deterministic (no clock/random) so AI artifacts are
replayable. Golden fixtures (AI_GOLDEN) + inference.spec.ts assert byte-identical
replay, abstention, and Scenario-7 (EVENT never exceeds DISCUSSION certainty).
Controls: abstain()/deny()/setCost().
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
POST /v1/adapters/:type/webhook verifies HMAC (fail-closed) -> stores raw ->
emits raw.received -> 202. RawEventProjector consumes it off the relay, normalizes
via the adapter, reuses IngestService (traceId threaded raw->interaction).
Deduped on (channelType, externalEventId). rawBody enabled. 48 tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Root cause of 'waiting for escalations': no queue/membership meant tickets were
never assigned. Now: createTicket find-or-creates a default queue; new joinDefault
endpoint + useGoOnline hook; agent-demo joins+goes-AVAILABLE on load (assignPending
picks up any waiting ticket). smoke-support self-seeds (no seed script needed).
Also: vitest singleFork to end cross-file DB races (45 tests deterministic).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
message-demo gains a per-person Inbox rail (useInbox, done/snooze); sending
surfaces a NEEDS_REPLY item in the other pane; reading auto-resolves it.
smoke-inbox.mjs proves the event-driven flow end-to-end (send -> relay ->
projector -> inbox -> read -> DONE). Realtime smoke still passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
InboxService.list (fail-closed) + transition (OPEN/SNOOZED/DONE/STALE/ARCHIVED
state machine, owner-only, state-history). GET /v1/inbox/items + PATCH
/v1/inbox/items/:id. Moved SessionVerifier to global IdentityModule (shared by
message + inbox, no circular imports). 35 tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Event-driven projector creates/collapses NEEDS_REPLY items from message.sent
(traceId propagated; inbox.item.created emitted) and resolves them to DONE on
message.read; idempotent via processed-event ledger. markRead now emits a
message.read outbox event (atomic). OutboxRelay runs on a 500ms timer in the
app (off in tests). Shared resetDb (TRUNCATE CASCADE) fixes cross-spec FK
isolation. 32 tests green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
apps/message-demo: Alice creates a thread, Bob joins; live two-way chat, typing,
read receipts (useMessages now exposes reads[]). Dev-only /v1/dev/token endpoint
(gated by IIOS_DEV_TOKENS) so the browser can auth. .env autoloaded (dotenv).
Realtime smoke script passes end-to-end (message + unread + receipt).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reuses support-service bootstrap/health/prisma patterns. Kernel-only Prisma
schema (scope/source_handle/actor/channel/thread/participant/interaction/
message_part/outbox/processed_event) with orgId/appId NOT NULL and
@@unique([scopeId, idempotencyKey]). Health verified: {status:ok,db:true}.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>