Replaces the plain-text invite email with a standalone react-email
template (apps/broker/src/emails/mesh-invitation.tsx) using
@react-email/components + Tailwind. Rendered on demand in
handleCliMeshInvite and sent as both HtmlBody and TextBody via
Postmark (or html+text via Resend).
Self-contained — no dependency on @turbostarter/email, i18n, or ui
packages. Adds react, react-dom, @react-email/components, @react-email/render
to broker deps. Enables tsconfig jsx: react-jsx and .tsx includes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Files: MinIO-backed file sharing built into the broker.
share_file for persistent mesh files, send_message(file:) for
ephemeral attachments. Presigned URLs for download, access
tracking per peer.
Broker infra: MinIO in docker-compose, internal network.
HTTP POST /upload endpoint. WS handlers for get_file,
list_files, file_status, delete_file.
Multi-target: send_message(to:) accepts string or array.
Targets deduplicated before delivery.
Targeted views: MCP instructions teach Claude to send
tailored messages per audience instead of generic broadcasts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
21 integration tests (14 broker behavior + 7 path encoding), all
passing in ~1s against a real Postgres (claudemesh_test database on
the dev container).
Test infrastructure:
- apps/broker/vitest.config.ts extends @turbostarter/vitest-config/base
- tests/helpers.ts: setupTestMesh() creates a fresh mesh + 2 members
per test with a unique slug, returns cleanup function that cascades
the delete. cleanupAllTestMeshes() as an afterAll safety net.
- Mesh isolation in broker logic means tests don't interfere even when
they share a database — no per-test TRUNCATE needed.
Ported behavior tests (broker.test.ts, 14 tests):
- hook flips status + queued "next" messages unblock
- "now"-priority bypasses the working gate
- DND is sacred (hooks cannot unset it)
- hook source stays fresh through jsonl refresh
- source decays to jsonl when hook signal goes stale
- isHookFresh freshness window + source-type rules
- TTL sweep flips stuck "working" → idle
- TTL sweep leaves DND alone
- first-turn race: hook fired pre-connect stashed in pending_status
- applyPendingHookStatus picks newest matching entry
- expired pending entries are ignored on connect
- broadcast targetSpec (*) reaches all members
- pubkey mismatch → message not drained
- mesh isolation: peer in mesh X doesn't drain from mesh Y
Ported encoding tests (encoding.test.ts, 7 tests):
- macOS, Linux, Windows path encoding first-candidate correctness
- Roberto's H:\Claude → H--Claude regression test (2026-04-04)
- Candidate dedup, drive-stripped fallback, leading-dash fallback
How to run: from apps/broker,
DATABASE_URL="postgresql://.../claudemesh_test" pnpm test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Step 3 pruned packages/ai + packages/cms but left workspace refs in
apps/web/package.json, which blocked pnpm install. Removes the two
dangling entries.
apps/web source imports remain broken until a later cleanup pass —
scope limited to unblocking the broker smoke test. Cleanup debt
inventory: 48 files import @turbostarter/ai, 5 files import
@turbostarter/cms (53 total, mostly .tsx under src/).
Also pins apps/broker's drizzle-orm to 0.44.7 (same as packages/db)
since there's no catalog entry for drizzle-orm.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- @claudemesh/broker package with bun dev/start scripts
- src/index.ts stub: WS server on BROKER_PORT, SIGTERM cleanup
- src/env.ts: Zod-validated env (BROKER_PORT, DATABASE_URL, STATUS_TTL_SECONDS, HOOK_FRESH_WINDOW_SECONDS)
- src/db.ts: re-exports Drizzle client from @turbostarter/db
- src/broker.ts + src/types.ts: placeholders for step 8 port
- README documents run commands, env vars, deploy targets
- tsconfig extends @turbostarter/tsconfig base
- eslint.config.js extends @turbostarter/eslint-config/base
Dependencies declared but not installed yet (ws, drizzle-orm, zod,
libsodium-wrappers + workspace deps). turbo.json unchanged: the global
dev task already has persistent=true + cache=false which is what the
broker needs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>