Files
stargue-publishing-engine/CLAUDE.md
Angelo B. J. Luidens 1dc1a1a07a Stage 0: governance scaffolding + monorepo bootstrap
Phase 1 foundation for the Stargue Publishing Engine (plan v2, BMAD
panel-reviewed 2026-04-19 — 1 APPROVE, 6 REVISE, 0 REJECT; all principles >=3).

- Governance doctrine adopted from DQMS
  (.clinerules/12-foundational-principles.md,
  .claude/hooks/gate-plan-exit.sh, .claude/skills/bmad-plan/SKILL.md)
- Bun workspaces + Turbo; apps/{mcp-linkedin,scheduler,admin};
  packages/{schema,sanitize,linkedin-client,observability}
- Drizzle schema (content, publications, approvals, metrics,
  linkedin_tokens, audit, outlet_feature_flags) with idempotency_key
  UNIQUE and kill-switch table per TEA/dev panel revisions
- LinkedIn API canon: Posts API /rest/posts (not legacy UGC); OAuth
  auth-code without PKCE; secretbox (not sealed-box); Community
  Management API as separate approval gate from MDP
- Frontmatter Zod schema (status, language, outlets[], sanitize,
  scheduled, version)
- Pino observability with PII redaction
- Expand-then-contract migration runbook
- Plan + panel verdicts mirrored to docs/plans/
- Deferred gates logged (Dokploy PaaS verification, LinkedIn Dev
  Portal app registration)

bun install + bun run typecheck both exit 0 across 11 workspaces.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-19 07:22:07 -04:00

98 lines
6.7 KiB
Markdown

# CLAUDE.md — Stargue Publishing Engine
Project-level context for Claude Code. Loaded every turn.
## Purpose
Automated two-channel Publishing Engine: Obsidian vault → stargue.com/.net + LinkedIn (personal profile + Stargue Company Page) via a Stargue-owned LinkedIn MCP server. Phase 1 scope is tracked in `docs/plans/2026-04-19-phase1-plan.md` (mirrored from the vault SSoT at `Stargue/Projects/Publishing Engine/Plan - Phase 1 Automated Publishing Engine.md`).
## Source-of-Truth Precedence
1. **Vault** (`c:\Users\aluid\OneDrive\Documents\Obsidian\Angelo'sSBO\`) — content notes, design philosophy (Nine Laws of the Hearth), outlet profiles, PRDs, plans. SSoT for *intent*.
2. **This repo** (`/home/devuser/projects/stargue-publishing-engine/`) — code implementation. SSoT for *behavior*.
3. **stargue-com + stargue-net** (`/home/devuser/projects/stargue-{com,net}/`) — Next.js static sites that consume `packages/sanitize` + `packages/schema` as workspace dependencies for build-time content transformation.
4. **Production state** (Dokploy on sg-paas-s1.stargue.net, Postgres, Redis, LinkedIn) — SSoT for *runtime*. Never assume; always verify via Dokploy MCP, DB query, or LinkedIn `whoami`.
## Governance
- **Foundational principles** (Tier-1 always + declared Tier-2): `.clinerules/12-foundational-principles.md` — adopted 2026-04-19 from DQMS (ref: `/home/devuser/projects/dqms/.clinerules/12-foundational-principles.md`). DQMS is the evolving SSoT for Stargue's BMAD application; this file is a dated snapshot.
- **Plan-review gate** (7-agent BMAD panel): enforced by `.claude/hooks/gate-plan-exit.sh` on `ExitPlanMode`. Runs the verdicts validator against `/tmp/bmad-panel-verdicts-${CLAUDE_SESSION_ID}.json`.
- **Panel charters** (roles for analyst, architect, pm, sm, ux-designer, dev, tea): referenced by path from DQMS at `/home/devuser/projects/dqms/_bmad/bmm/agents/*.md`. No local install in Phase 1 (per user directive 2026-04-19).
- **Tier-2 applicability**: every plan declares `applicable_tier2 = [RBR | A11y | Testability]`. See `.clinerules/12-foundational-principles.md > Tier-2 Principles`.
## Architecture
Monorepo (Bun workspaces + Turbo):
```
apps/mcp-linkedin/ MCP server (stdio transport) for Claude Code to drive LinkedIn operations
apps/scheduler/ BullMQ worker — imports packages/linkedin-client directly; no HTTP to MCP
apps/admin/ Next.js App Router — UI + /api/* route handlers
packages/sanitize/ remark plugin + blocklist + length validation + test corpus
packages/schema/ Zod schemas, Drizzle DB schema, frontmatter parser (SSoT for data shapes)
packages/linkedin-client/ LinkedIn Posts API client + encrypted token store
packages/observability/ Pino + OTel + correlation IDs
```
Scheduler imports `packages/linkedin-client` directly — MCP is **not** on the scheduler's hot path. MCP exists so Claude Code can invoke LinkedIn operations interactively (posting, metrics queries, auth status). HTTP MCP transport is deliberately deferred.
## LinkedIn — canonical facts (verified 2026-04-19)
- API: **Posts API** `https://api.linkedin.com/rest/posts` (the UGC Posts API is **legacy** — do not use). Required headers: `LinkedIn-Version: 202404` (YYYYMM, update on cadence), `X-Restli-Protocol-Version: 2.0.0`, `Authorization: Bearer <token>`.
- Auth: OAuth 2.0 Authorization Code flow. **PKCE is NOT supported** by LinkedIn 3LO per `https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow`. Use confidential-client with `client_secret` + CSRF-protected `state`.
- Products in Dev Portal (each separately enabled/approved):
- `Sign In with LinkedIn using OpenID Connect` → scopes `openid profile email`
- `Share on LinkedIn``w_member_social` (personal-profile posts)
- `Community Management API``w_organization_social`, `r_organization_social` (organization posts — **requires LinkedIn partner approval**)
- `Marketing Developer Platform (MDP)` → programmatic refresh tokens (requires LinkedIn partner approval; without it, access tokens expire in 60 days with **no refresh** — re-auth required)
- Stargue Company Page URN: `urn:li:organization:2605890`
- Primary identity: Angelo personal profile (`angeloluidens`), resolved at OAuth time to `urn:li:person:<id>`
- Token encryption: `libsodium crypto_secretbox` (symmetric XSalsa20-Poly1305) — **not sealed-box**. Key stored in Dokploy secrets.
## Commands
```bash
bun install # install workspace deps
bun run typecheck # workspace-wide tsc --noEmit
bun run lint # workspace-wide lint
bun run test # workspace-wide vitest
bun run build # turbo build
bun run dev # turbo dev (admin, scheduler, mcp-linkedin)
# Migration commands
bun run db:migrate # drizzle-kit migrate
bun run db:migrate:rehearse # expand-then-contract rehearsal on staging
# MCP invocation (stdio)
docker exec -i mcp-linkedin node apps/mcp-linkedin/dist/server.js
```
## Deployment
Dokploy stack `stargue-publishing-engine` on `sg-paas-s1.stargue.net`:
- `admin` — Next.js standalone, Traefik at `publishing.stargue.net`, Authentik SSO
- `scheduler` — BullMQ worker
- `mcp-linkedin` — stdio-only; started on demand by Claude Code via `docker exec`
- `postgres` + `redis` — shared PaaS cluster
DR: nightly `pg_dump` to Neon free tier. RPO ~24h.
## Rules
- **Additive by default** — never delete production data or published content without explicit user confirmation.
- **No PII in logs** — content body is hashed, not logged; tokens never printed.
- **Every publish is approval-gated** — no auto-publish without a row in `approvals` for the (content_id, outlet) pair.
- **Kill-switch respected** — every dispatch checks `outlet_feature_flags` before making LinkedIn calls.
- **Idempotency enforced** — every LinkedIn API call carries an idempotency key derived from (content_id, outlet, scheduled_at).
- **Expand-then-contract migrations** — every schema change has a rollback runbook in `db/migrations/README.md`.
- **All operations in WSL Debian** — per vault-level rule. `wsl -d Debian -- bash -c '…'` for any shell invocation from Windows.
## Related (vault paths)
- `Stargue/Projects/Publishing Engine/Plan - Phase 1 Automated Publishing Engine.md` — current plan (SSoT)
- `Stargue/Projects/Publishing Engine/Publishing Engine PRD.md` — PRD v2.2
- `Stargue/Projects/Publishing Engine/Design Philosophy - The Nine Laws of the Hearth.md` — visual + interaction SSoT
- `Stargue/Projects/Publishing Engine/Outlet Profiles/LinkedIn.md` — outlet specs
- `Stargue/Projects/Publishing Engine/7 Cs LinkedIn Series - Continuation Plan.md` — first content program
- `Stargue/Projects/Publishing Engine/Implementation Progress.md` — running state