Files
Angelo B. J. Luidens c24ea815f3 docs: record Gate 0.8 resolution + Gate 0.9 LinkedIn app / CMA progress
- 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>
2026-06-03 15:31:02 -04:00

12 KiB
Executable File
Raw Permalink Blame History

created, updated, tags
created updated tags
2026-05-13T12:35 2026-05-13T12:35

Publishing Engine — Session Handoff 2026-05-13

Purpose: let the next session pick up cold with full state of the Stargue Publishing Engine build. Source: this vault note + mirror at docs/handoffs/2026-05-13-handoff.md in the repo.

Repo: https://git.stargue.net/admin/stargue-publishing-engine (local: /home/devuser/projects/stargue-publishing-engine/ via WSL Debian) Plan v2 (SSoT): Plan - Phase 1 Automated Publishing Engine (vault) + docs/plans/2026-04-19-phase1-plan.md (repo mirror) PRD: Publishing Engine PRD Panel verdicts: docs/plans/bmad-panel-verdicts-publishing-engine-phase1-20260419.json (1 APPROVE, 6 REVISE, 0 REJECT — all principles ≥3) Doctrine: .clinerules/12-foundational-principles.md adopted from DQMS


Where we stand — quick read

Stage Status Commit Tests Notes
0 — Governance + scaffolding Complete 1dc1a1a (2026-04-19) n/a 11 workspaces typecheck clean
1 — Shared packages Complete e529651 (2026-04-26) 50/50 4 packages: schema, sanitize, linkedin-client (stubs), observability
2 — DB + middleware + API 🟡 Partial c73b7e4 (2026-04-26) 79/79 cumulative Live-DB integration tests blocked by gate 0.8
3 — LinkedIn MCP server 🟡 Partial 7aa3137 (2026-04-26) 106/106 cumulative Live-LinkedIn blocked by gate 0.9
4 — Scheduler + cadence 🟡 Partial a4e4306 (2026-04-26) 117/117 cumulative BullMQ + integration blocked by gates 0.8 + 0.9
5 — Admin dashboard (Next.js + A11y) Not started Independent of gates; can start anytime
6 — stargue-com/.net integration Not started Independent of gates
7 — Content + metrics bootstrap Not started 7.1 + 7.2 independent of gates

Last activity: 2026-04-26 (commit a4e4306). Repo idle since. Today is 2026-05-13 — ~2.5 weeks of pause.


Deferred gates (the real blockers)

Gate 0.8 — Postgres + Redis on PaaS

Status: Dokploy MCP was loaded in the 2026-04-19 session but verification not completed before pause. Resume action: with Dokploy MCP attached, run mcp__dokploy__project-all → identify Stargue PaaS project → mcp__dokploy__postgres-search + mcp__dokploy__redis-search to see existing instances; provision new ones in a stargue-publishing-engine project if none are shareable.

Why it blocks: Stage 2 live-DB integration tests, Stage 3 token-store live tests, Stage 4 BullMQ worker, Stage 5 admin auth flow.

Next steps when resolved:

  1. Run 0000_initial_schema.sql migration on the new DB
  2. Run bun run db:migrate:rehearse (script lives at packages/schema/db/migrations/rehearse.ts)
  3. Wire connection strings into Dokploy secrets DATABASE_URL, REDIS_URL
  4. Re-enable the integration tests gated behind INTEGRATION=1

Gate 0.9 — LinkedIn Developer Portal apps

Status: Pending Angelo's manual action. Checklist ready at docs/linkedin-apps.md.

Why it blocks: Stage 3.13.7 live posting, Stage 4 end-to-end publish, Stage 7.4 first scheduled post.

Next steps when resolved:

  1. Angelo registers app at linkedin.com/developers/ linked to Stargue Company Page urn:li:organization:2605890
  2. Enables Sign-In with OpenID Connect + Share on LinkedIn (instant self-service)
  3. Submits Community Management API + MDP partner-access requests (multi-week review — file ASAP to start the clock)
  4. Drops LINKEDIN_CLIENT_ID, LINKEDIN_CLIENT_SECRET, LINKEDIN_TOKEN_ENCRYPTION_KEY into Dokploy secrets
  5. Update docs/linkedin-apps.md registry with app IDs

What's already built (don't re-invent)

packages/schema (full SSoT for data shapes)

  • frontmatter.ts — Zod schema for vault frontmatter (status, language, outlets[], sanitize, scheduled, version)
  • db.ts — Drizzle schema for 7 tables: content, publications (with idempotency_key UNIQUE), approvals, metrics, linkedin_tokens, audit, outlet_feature_flags
  • rate-limit.ts — token-bucket implementation, pure-function
  • csrf.ts — double-submit cookie pattern
  • authz.ts — Cerbos-equivalent policy primitives
  • api-contracts.ts — Zod request/response schemas for admin API
  • drizzle.config.ts + db/migrations/0000_initial_schema.sql + 0000_initial_schema.down.sql + db/migrations/rehearse.ts

packages/sanitize (fail-closed content pipeline)

  • rules.ts — PRIVATE_PATH_PREFIXES (Family Matters, Financial Matters, Journal, Clients NDA…), PRIVATE_PATH_PATTERNS, PRIVATE_TAGS (#private, #heal-internal, #confidential), OUTLET_LENGTH_LIMITS
  • errors.ts — SanitizeError with codes: PRIVATE_PATH_BLOCKED, PRIVATE_TAG_BLOCKED, WIKILINK_TO_PRIVATE_PATH, OUTLET_LENGTH_EXCEEDED
  • index.tssanitize(markdown, opts) returns { body, contentHash, warnings } — strips wikilinks/embeds/dataview/callouts, enforces blocklist + tag firewall + length, computes SHA-256 hash
  • corpus.test.ts — Stage 1.2 DoD test: 12 private + 6 clean fixtures round-trip correctly

packages/linkedin-client (Posts API client + token store)

  • types.ts — LinkedIn Posts API request/response types (Posts API v202404, not legacy UGC)
  • client.ts — HTTP client; respects LinkedIn-Version: 202404 + X-Restli-Protocol-Version: 2.0.0
  • token-store.ts — encrypted persistence; uses AES-256-GCM via node:crypto (NOT libsodium — see D-001 in docs/deferred-gates.md for the deviation rationale: libsodium-wrappers ESM bug under Bun)
  • client.test.ts + token-store.test.ts — 4/4 round-trip tests pass

packages/observability (Pino + correlation IDs)

  • index.ts — Pino logger with PII-redaction paths (access_token, refresh_token, client_secret, *_ct fields), newCorrelationId(), child(bindings) helper

apps/mcp-linkedin (Stage 3 partial — structural complete, live blocked)

  • server.tscreateServer factory pattern, injectable deps (KillSwitch, AdvisoryLock); MCP_SPEC_VERSION pinned 2024-11-05; validateToolCall, listTools, outletForAuthorUrn helpers
  • tools.ts — 9 Zod tool schemas: whoami, auth_status, create_post, create_article, upload_media, create_post_with_media, delete_post, get_post_metrics, get_profile_stats
  • oauth.ts — auth-code URL builder + HMAC-protected state parameter (no PKCE — LinkedIn 3LO doesn't support it)
  • kill-switch.tsoutlet_feature_flags reader with cache invalidation
  • refresh-lock.ts — Postgres advisory-lock primitive; 4-concurrent rotation test passes (TEA gap 3 closed)

apps/scheduler (Stage 4 partial — pure pieces complete)

  • cadence.ts — Beta(1,19) posterior optimizer, propose(history, config) → [Proposal]; all 4 thresholds verified (≥8 samples, ≥4 weeks, ≥20% ratio, disjoint 95% CIs); never auto-applies (RBR)
  • publish-loop.ts — state-machine transition function pending→queued→dispatching→published, retry-vs-DLQ branch, cancellation path
  • worker.ts — entry stub (BullMQ wiring blocked by Redis on gate 0.8)

apps/admin (not started)

  • src/index.ts — stub only. Next.js scaffold + Authentik SSO + queue/publications/auth/metrics pages all pending.

Architecture decisions worth remembering

  1. D-001 token cipher: AES-256-GCM, not libsodium secretbox. Bun ESM bug in libsodium-wrappers. Same AEAD security profile. Documented in docs/deferred-gates.md. Remediation path noted if libsodium fixes the bug.
  2. Scheduler imports linkedin-client directly — MCP transport is stdio-only for interactive Claude Code use. No HTTP MCP transport in Phase 1 (architect gap 2).
  3. apps/api collapsed into apps/admin — Next.js App Router route handlers cover the API surface; less infra (architect gap 1).
  4. LinkedIn Posts API /rest/posts — NOT the legacy /v2/ugcPosts. Headers: LinkedIn-Version: 202404, X-Restli-Protocol-Version: 2.0.0.
  5. No PKCE in OAuth — LinkedIn 3LO doesn't document it; confidential-client with HMAC state parameter is what we use.
  6. idempotency_key UNIQUE on publications — sha256(content_id || outlet || scheduled_at). Prevents double-post on retry.
  7. Kill-switch via outlet_feature_flags table — fast disable without redeploy. Cached at MCP server with invalidation on table change.

Next session — priority order

  1. Gate 0.8 — Dokploy MCP query → provision Postgres + Redis in stargue-publishing-engine project on sg-paas-s1.stargue.net
  2. Gate 0.9 — Angelo files LinkedIn Dev Portal apps (~30 min for OIDC + Share; submit Community Management API + MDP requests to start the partner-approval clock)
  3. With both gates open, unblock Stages 2.6, 3.43.9, 4.14.3 live integration tests
  4. Move to Stage 5 (admin dashboard)

Path B: Continue parallel work (if gates remain blocked)

  1. Stage 5 — Admin dashboard scaffold: Next.js App Router + shadcn/ui + Nine Laws design tokens; pages /queue, /publications/:id, /auth/linkedin, /metrics; A11y CI (axe-core + @axe-core/playwright). Authentik stub for local dev.
  2. Stage 6 — Add @stargue/sanitize + @stargue/schema as workspace deps to stargue-com + stargue-net; replace ad-hoc frontmatter parsing; assert zero wikilinks in rendered HTML.
  3. Stage 7.1 — Run /market audit baseline on stargue.com + stargue.net; save to docs/marketing-baseline-2026-05.json.
  4. Stage 7.2 — Draft 7 Cs posts 28 (Commitment, Co-Creation, Connection, Communication, Celebration & Course Correction, Caring & Crossing, Synthesis) — long-form for stargue.com + LinkedIn-optimized variants. Source content from 7 Cs LinkedIn Series - Continuation Plan.md. Run each through autoresearch optimization to composite ≥8.

Path C: Mixed (most realistic)

  • Angelo does Gate 0.9 in parallel (~30 min self-service; partner approvals run async for weeks)
  • Claude provisions Gate 0.8 via Dokploy MCP
  • Concurrently start Stage 5 and 7.2

Open items / things to verify

  1. MDP + Community Management API timeline — once filed, track approval state weekly. MDP rejection is the trigger for the 60-day-re-auth fallback.
  2. Authentik instance reachability — plan §8 q3 said "shared instance for all Stargue services"; needs runtime verification before Stage 5.2.
  3. Neon free-tier mirror — Stage 7.5 DR test pending. Set up nightly pg_dump → Neon cron when gate 0.8 resolved.
  4. stargue-com/.net package.json sync — collision was fixed (commit fd43214 on stargue-net) — verify both repos still align before Stage 6.
  5. LinkedIn algorithm-reach risk — plan §9 row added by analyst panel. Once first 4 posts ship, monitor engagement-delta vs Angelo's existing manual baseline; if suppression detected, fall back to draft-and-prompt workflow.

Files to read first in next session

In order, for cold start:

  1. This handoff
  2. docs/plans/2026-04-19-phase1-plan.md (in the repo) — full plan v2
  3. docs/deferred-gates.md — for D-001 + gate status
  4. CLAUDE.md in the repo — for governance + canonical LinkedIn facts
  5. Last 4 commits via git log — for exact stage progress
  6. docs/linkedin-apps.md — for Angelo's manual checklist

Memory notes already saved

  • project_publishing_engine.md — overall project state
  • D-001 cipher deviation is in docs/deferred-gates.md (in repo) — no separate memory note needed since the deviation is project-local
  • BMAD panel verdicts cached at docs/plans/bmad-panel-verdicts-publishing-engine-phase1-20260419.json

Commands cheatsheet

# WSL Debian — always run there
wsl -d Debian -- bash -c 'cd /home/devuser/projects/stargue-publishing-engine && <cmd>'

bun install                       # workspace deps
bun run typecheck                 # all 11 workspaces
bun run test                      # 117/117 currently passing
bun run db:migrate                # apply Drizzle migrations (needs DATABASE_URL)
bun run db:migrate:rehearse       # expand-then-contract rehearsal

# Gitea
git push                          # auto-deploys via Dokploy webhook (Phase 0 sites);
                                  # Publishing Engine apps deploy on separate Dokploy stack (Stage 5+)