X/Twitter (live posting gated on an X developer app, registered separately):
- schema: add 'twitter' to OutletSchema (sanitize 280-char limit already present).
- packages/twitter-client: X API v2 client (POST /2/tweets, OAuth2 user Bearer),
injectable fetch, 280-char guard; 4 contract tests.
- apps/scheduler/src/dispatch.ts: makeOutletDispatcher routes by outlet
(linkedin.* -> linkedin, twitter -> twitter), fail-closed for unconfigured
outlets; 4 tests. main.ts wires both outlets fail-closed until creds exist.
Monorepo fix: add a local tsconfig.json (extends root, include src) to each
package + non-admin app. Without it, every package's `tsc --noEmit` compiled the
WHOLE repo via the root config (no JSX/DOM) and choked on the admin .tsx — masked
until now by turbo build caching.
Tests: 135 passing + 13 gated across 12 tasks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- deferred-gates.md: Gate 0.8 RESOLVED — clean-room Postgres 17 + Redis 7 in a
dedicated Dokploy project; service names, hosts, env-var locations recorded.
- linkedin-apps.md: app created (Client ID 78s8f53y5spyo4), company-page +
business-email verified, Community Management API Development Tier submitted
(review in progress). Notes that other products grey out while CMA is pending.
- docs/handoffs/2026-05-13-handoff.md: committed (was untracked).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- apps/scheduler/src/queue.ts: PUBLISH_QUEUE, BullMQ-compatible Redis connection
factory, queue factory, default retry/backoff job opts.
- apps/scheduler/src/worker.ts: makePublishWorker factory drives the publish-loop
state machine (queued -> dispatching -> published | failed/dlq) and persists
each transition to publications. Dispatcher is injected (fake in tests; the
LinkedIn client swaps in once Gate 0.9 / CMA lands).
- apps/scheduler/src/main.ts: runnable entry; boots the worker; LinkedIn dispatch
is fail-closed until CMA is approved.
- apps/scheduler/src/worker.integration.test.ts: gated behind INTEGRATION=1;
verifies enqueue -> consume -> row published within 5s, and failure -> dlq with
error recorded. Run against real Redis + Postgres.
Closes the Stage 4.1 DoD (enqueue -> worker-consume -> DB row updated within 5s).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- packages/schema/src/client.ts: createDb(url) -> { db, sql } drizzle/postgres-js
factory. Exposed as the ./client subpath export so the node-only driver never
leaks into client bundles.
- packages/schema/src/db.integration.test.ts: 6 tests gated behind INTEGRATION=1
(content insert/read, vault_path UNIQUE, idempotency_key UNIQUE no-double-post,
content_id FK, audit append, kill-switch upsert). Verified against Postgres 17.
- Default CI suite unchanged: integration tests skip without INTEGRATION.
Closes the Stage 2.1 DoD against a live database (unblocked by Gate 0.8).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes 3 pre-existing TypeScript errors that vitest did not catch but
tsc --noEmit did:
- apps/mcp-linkedin/src/tools.test.ts: spread TOOL_NAMES before .sort()
(readonly tuple has no mutating sort)
- packages/sanitize/src/corpus.test.ts: cast through unknown to FixtureMeta
- db/migrations/rehearse.ts: add 'postgres' driver dependency (also needed
to run the migration itself)
Also: apps/admin test script uses --passWithNoTests (Stage 5 admin app has
no test files yet, vitest exited non-zero on empty).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pure-function pieces that need no Redis/DB to verify (11 new tests):
- apps/scheduler/src/cadence.ts: Beta posterior model, propose(history, config)→[Proposal]; threshold gates (≥8 samples, ≥4 weeks, ≥20% ratio, disjoint 95% CIs); never auto-applied (RBR)
- apps/scheduler/src/publish-loop.ts: state-machine transition function for pending→queued→dispatching→published with retry-vs-DLQ branch on failure and cancellation path
What defers to live Redis + Postgres + LinkedIn:
- 4.1 BullMQ queue + Redis enqueue→consume integration test
- 4.2 End-to-end publish loop trace (POST /api/content → DB → BullMQ → LinkedIn fake → publication row)
- 4.3 Chaos test for idempotency on retry
117/117 tests pass cumulative across all packages and apps.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>