Files
stargue-publishing-engine/docs/plans/bmad-panel-verdicts-publishing-engine-phase1-20260419.json
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

194 lines
20 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"plan_file": "c:\\Users\\aluid\\OneDrive\\Documents\\Obsidian\\Angelo'sSBO\\Stargue\\Projects\\Publishing Engine\\Plan - Phase 1 Automated Publishing Engine.md",
"created_at": "2026-04-19T10:20:00-04:00",
"applicable_tier2": ["RBR", "A11y", "Testability"],
"verdicts": [
{
"agent": "analyst",
"verdict": "APPROVE",
"confidence": "medium",
"summary": "Plan is evidence-grounded and principle-aligned; approve subject to resolving Company-Page existence, timeline capacity, Bayesian spec, and LinkedIn algorithmic-reach competitive risk before Stage 3.",
"gaps": [
"§4 timeline: 7-day schedule is aggressive given PRD §2 production-problem framing; no FTE/capacity assumption stated",
"§2/§8: Stargue Company Page URL is both an Objective and an open gate — stakeholder alignment gap",
"§3.7 cadence optimizer: Bayesian posterior under-specified (prior, thresholds, sample-size beyond ≥8)",
"§10: multilingual (PAP/NL/ES) deferred, but PRD §5.3/NF-008 and 7 Cs plan assume PAP audience",
"§9: no competitive risk entry for LinkedIn algorithm/reach-suppression of automated-posted content"
],
"principles": {
"SEBP": {"score": 4, "rationale": "§3.1 monorepo with packages/{sanitize,schema,linkedin-client,observability} gives clear single-responsibility boundaries; scheduler + MCP both consume linkedin-client (no duplication)."},
"SSoT": {"score": 5, "rationale": "§3.4 sanitize config + §3.5 Drizzle schema + §5 design tokens generated from Nine Laws doc — each concept has one authoritative location."},
"FPT": {"score": 4, "rationale": "§1 cites retrieved LinkedIn MDP doc with date; §3 stack choices match verified stargue-com/stargue-net inventory. Minor: Bayesian optimizer under-specified."},
"DiDSP": {"score": 5, "rationale": "§3.3 libsodium sealed-box tokens, Authentik SSO, bearer tokens scheduler↔MCP, rate limits, no public exposure, Zod at every boundary."},
"PbD": {"score": 4, "rationale": "§5 notes token minimization, 13-month metrics retention, erasure path via token revoke + git delete. Gap: no DPIA note on LinkedIn-side audience data."},
"OF": {"score": 5, "rationale": "§3.3 correlation IDs, Pino→OpenObserve, PII-redacted content (length+hash only), append-only audit table, proactive T-5d auth-expiry notification."},
"RBR": {"score": 4, "rationale": "§3.3 fail-safe halt, §3.7 human-in-the-loop for cadence, §3.5 expand-then-contract, §4.3 DLQ. Gap: no rollback drill for published-to-LinkedIn content past 5-min edit window."},
"A11y": {"score": 4, "rationale": "§3.6 WCAG 2.2 AA, axe-core in CI, keyboard parity, prefers-reduced-motion, contrast on both themes; OAuth screen delegated to LinkedIn. Gap: no screen-reader manual test named."},
"Testability": {"score": 4, "rationale": "§3 pure core in packages; §7 contract tests against recorded LinkedIn exchanges; injectable clock/ID implied. Gap: cadence-optimizer seams not explicit."}
}
},
{
"agent": "architect",
"verdict": "REVISE",
"confidence": "high",
"summary": "Architecturally sound and stack-verified, but over-partitioned for a 7-day build — collapse api-into-admin, fix the libsodium primitive, and cite the rate-limit numbers before Stage 0.",
"gaps": [
"§3.1 Repo topology adds Turbo + 4 apps + 4 packages for a 7-day plan — architecturally heavy for one-engineer rollout; suggest collapsing apps/api into apps/admin route handlers",
"§3.3 MCP transport: Streamable HTTP spec version unnamed; scheduler-to-MCP over HTTP within same Docker network adds a hop vs direct library import — justify or use library",
"§3.3 Token store: libsodium sealed-box is public-key encryption; for symmetric at-rest DB encryption use secretbox or KMS envelope",
"§3.3 Rate limit numbers (~100/day per member) are uncited",
"§3.8 Postgres: says 'shared with other PaaS apps (existing cluster)' — verify; §8.5 open question contradicts",
"§4 Stages: 7-day timeline for OAuth×2 identities + 8 MCP tools + scheduler + admin + A11y CI + E2E is optimistic",
"§3.4 Sanitization: no test corpus named in Stage DoD (only in §9 risks)",
"§3.6 Admin: no CSRF strategy named for approval POSTs"
],
"principles": {
"SEBP": {"score": 4, "rationale": "Clean package boundaries (§3.1); mcp-linkedin consumes linkedin-client — but api+admin separation may violate YAGNI for Phase 1."},
"SSoT": {"score": 5, "rationale": "Frontmatter schema, blocklist, design tokens each single-homed (§3.4, §3.5, §3.6)."},
"FPT": {"score": 4, "rationale": "Stack verified against actual repos; LinkedIn docs cited with retrieval date. Loses a point on uncited rate-limit figures and MCP transport choice."},
"DiDSP": {"score": 4, "rationale": "Zod+Authentik+rate-limit+encrypted tokens+audit (§3.3) — solid. Loses point for libsodium sealed-box mis-selection and missing CSRF."},
"PbD": {"score": 4, "rationale": "Low PII surface, 13-month retention, erasure path named (§5). Token retention policy could be more explicit."},
"OF": {"score": 5, "rationale": "Correlation IDs end-to-end, OpenObserve integration, body-hash redaction, append-only audit (§3.3, §5)."},
"RBR": {"score": 4, "rationale": "Expand-only migrations, DLQ, human-gated cadence, fail-safe on auth. LinkedIn delete only within 5-min edit window — no compensating audit-before-publish preview enforcement."},
"A11y": {"score": 4, "rationale": "WCAG 2.2 AA target, axe-core in CI, keyboard parity, both themes contrast-checked. Screen-reader manual test unlisted in DoD."},
"Testability": {"score": 4, "rationale": "Pure core in packages, injectable clock, client behind interface, contract tests against recorded exchanges. No named fake for token-store rotation race."}
}
},
{
"agent": "pm",
"verdict": "REVISE",
"confidence": "high",
"summary": "Strong governance and architecture, but the plan inverts the PRD's revenue-first pivot and fails to reconcile scope with the 4h/week capacity cap — revise scope and timeline before exit.",
"gaps": [
"§4 Stages vs PRD §6.5: Plan compresses M1.1M1.4 (PRD 7 weeks @ 4h/wk = 28h) into ~7 days with no hours-per-stage stated; PRD §9 risk #1 is solo-founder capacity",
"§2 Objectives: zero mention of revenue conversion (PRD §6.4, F-003); PRD v2 pivot explicitly revenue-first, plan adds infrastructure only",
"§10: multilingual (PAP/NL/ES) deferred to Phase 2 but PRD F-021 places at P1 and 7 Cs plan publishes EN only",
"§4 Stage 7: 14 drafts committed in 2 days assumes autoresearch loop is near-free — no hours estimate",
"§3.6/§3.7 Admin + optimizer are M1.2/M2 scope in PRD; pulling into Phase 1 redefines Phase 1 boundary",
"§11: MDP application timeline not flagged for revenue-dependent features"
],
"principles": {
"SEBP": {"score": 4, "rationale": "Monorepo boundaries clean (§3.1); standard packages/apps split; no leaky abstractions."},
"SSoT": {"score": 5, "rationale": "Frontmatter schema, sanitize config, design tokens all single-location; eliminates existing ad-hoc parse."},
"FPT": {"score": 4, "rationale": "Cites LinkedIn MDP docs with retrieval date; verified repo state; real outlet profile. Does not probe 4h/wk capacity math."},
"DiDSP": {"score": 5, "rationale": "Zod at boundaries, Authentik SSO, libsodium, rate limiting, audit table, no public MCP exposure."},
"PbD": {"score": 4, "rationale": "Token retention + encryption + revocation path; 13-month analytics cap; content is public-by-design."},
"OF": {"score": 5, "rationale": "Correlation IDs through API→scheduler→MCP; OpenObserve dashboards; audit append-only; PII redaction."},
"RBR": {"score": 4, "rationale": "Human approval on every publish, DLQ, expand-only migrations, feature flag on adaptive optimizer, Telegram fail-safe. No staged rollout for MCP server itself."},
"A11y": {"score": 4, "rationale": "WCAG 2.2 AA target, axe-core CI, keyboard parity, prefers-reduced-motion, dual-theme contrast. Not yet tested with SR."},
"Testability": {"score": 4, "rationale": "Pure core in packages, injectable clock/ID, LinkedIn client behind interface with contract tests. Sandbox-or-contract fallback right."}
}
},
{
"agent": "sm",
"verdict": "REVISE",
"confidence": "high",
"summary": "Architecture and DoDs are largely sprint-ready, but stages must be re-timelined against the PRD's 4h/week cap and 6 DoD items need 'works'-language tightened before dev handoff.",
"gaps": [
"§4 DoD uses calendar 'days 17' but PRD caps at 4h/week; re-express as Week-1…Week-N at 4h/week or justify",
"§4 Stage 1.2 DoD 'Sanitizes sample vault note correctly' needs replacement with 'round-trips 12-file private-corpus fixture with 0 leaks and 12 build errors'",
"§4 Stage 3.4: no cleanup step; add 'test post deleted within 5-min edit window; deletion audited'",
"§4 Stage 4.2 'End-to-end happy path': specify trace (POST /content → approval → scheduled_at → git SHA + LinkedIn URN recorded, status=published, within 120s)",
"§4 Stage 5.3 'No violations above minor' needs axe-core severity gate (zero serious+critical) + WCAG contrast ratios",
"§4 Stage 7.4 'Observed live' not CI-demonstrable; add 'publication row external_url returns HTTP 200 from unauthenticated curl; metrics row at T+48h±1h'",
"§8 Auth gates block Stage 0 exit but Stage 0 has no 'gates answered' DoD row",
"§4 Stage 2.1 'expand-only migrations documented': needs pointer to doc location",
"No sprint sequencing artifact / story backlog — charter requires it for dev handoff"
],
"principles": {
"SEBP": {"score": 4, "rationale": "Clean monorepo boundaries (§3.1); shared packages reused — no duplicated logic named."},
"SSoT": {"score": 5, "rationale": "§3.4 + §3.5 put frontmatter, sanitize config, tokens in single packages; §5 SSoT row cites file paths."},
"FPT": {"score": 4, "rationale": "§1 cites LinkedIn MDP doc with retrieval date; verified repo state; rejected n8n with tradeoff. Miss on Phase 0 capacity constraint."},
"DiDSP": {"score": 5, "rationale": "§3.3 token encryption + §3.5 audit table + §3.6 Authentik SSO + §9 rate limits — layered, concrete."},
"PbD": {"score": 4, "rationale": "§5 PbD row names retention (13mo), erasure path (token revoke + hard delete), purpose limitation. Missing DPIA note."},
"OF": {"score": 5, "rationale": "§3.3 per-tool correlation ID + OpenObserve + redaction rule — structured and specific."},
"RBR": {"score": 3, "rationale": "§5 names approval-gate + DLQ + expand-contract + flagged adaptive cadence. Missing rollback runbook for a bad LinkedIn post past 5-min edit window."},
"A11y": {"score": 4, "rationale": "§3.6 + Stage 5.3/5.4 name axe-core, keyboard parity, reduced-motion, dual-theme contrast. 'No violations above minor' ambiguous."},
"Testability": {"score": 4, "rationale": "§7 splits unit/integration/E2E; §3.3 client behind interface; injectable clock mentioned. Cadence-optimizer Bayesian logic has no named test seams."}
}
},
{
"agent": "ux-designer",
"verdict": "REVISE",
"confidence": "high",
"summary": "Strong security/SSoT/observability spine with Nine-Laws-aware design intent, but A11y is declared-not-designed — add operator flows, SR protocol, dual-theme contrast verification, and reconcile Law-3 temperature with WCAG 1.4.1 before exit.",
"gaps": [
"§3.6/§5: A11y names mechanisms but omits user flows; no wireframes/task analysis/cognitive-load budget for /queue approval task (where slip leaks private content)",
"§3.6: aria-live polite/assertive level unspecified; no error-association spec for OAuth re-auth or approval form (WCAG 3.3.1, 3.3.3, 4.1.3)",
"§3.6: no screen-reader testing in CI or manual protocol (charter requires SR-tested for score 5)",
"§3.6: dual-theme contrast promised but neither theme's token values (warm #fefbf6 / cool #f8fafc + Forest/Lime accents) verified against 4.5:1 / 3:1",
"Nine Laws fit: admin dashboard is operator-tool — no temperature declared; risk of Law-4 amber leakage into status badges",
"§3.7 cadence optimizer heatmap is color-encoded — Law 3 temperature semantics conflict with WCAG 1.4.1 (color not sole means)",
"§4 Stage 5: no criterion for focus-trap on modal dialogs, skip-links, or reduced-motion honoring in section fade-in (P8.4)",
"§11: open questions omit 'primary operator persona and accessibility context'"
],
"principles": {
"SEBP": {"score": 4, "rationale": "Monorepo boundaries in §3.1 are clean; shared packages reused by scheduler + mcp; tradeoff named."},
"SSoT": {"score": 5, "rationale": "§3.1 shared packages, §3.4 Zod config, §3.6 design tokens inherited from Nine Laws doc — no duplication path."},
"FPT": {"score": 4, "rationale": "§1 cites LinkedIn docs with retrieval date and verifies repo state; rejects n8n on SSoT grounds."},
"DiDSP": {"score": 5, "rationale": "§3.3 sealed-box, §3.4 fail-closed sanitizer, §3.5 append-only audit, Authentik + bearer layered."},
"PbD": {"score": 4, "rationale": "§3.3 token minimization, 13-month analytics retention, erasure path named."},
"OF": {"score": 5, "rationale": "§3.3/§3.5 correlation IDs, structured Pino logs, OpenObserve dashboards, append-only audit."},
"RBR": {"score": 4, "rationale": "§3.7 human-in-loop, DLQ, expand-only migrations, feature flag on adaptive cadence."},
"A11y": {"score": 3, "rationale": "Targets WCAG 2.2 AA and names axe-core/keyboard/contrast — missing SR testing, user flows, cognitive-load analysis on approval task, heatmap color-only encoding risk, Law-3/Law-4 contrast verification. Meets floor, not exemplary."},
"Testability": {"score": 4, "rationale": "§7 names unit/integration/E2E/contract tests; §3.3 injectable token store; pure-core in packages."}
}
},
{
"agent": "dev",
"verdict": "REVISE",
"confidence": "high",
"summary": "Solid architecture and governance, but ship-blocker inaccuracies on LinkedIn auth (PKCE unverified) and endpoint (UGC Posts is Legacy; use Posts API with LinkedIn-Version header) must be corrected before Stage 3.",
"gaps": [
"§3.3 Auth: scopes 'openid profile email' are from 'Sign In with LinkedIn using OpenID Connect' product; 'w_member_social' requires 'Share on LinkedIn' product — must be declared per-product in Dev Portal. 'w_organization_social'/'r_organization_social' require Community Management API product approval (separate gate).",
"§3.3 Auth: PKCE is NOT documented as supported by LinkedIn's 3LO — Microsoft Learn authorization-code-flow shows only response_type=code + client_secret. Either cite PKCE support or drop it.",
"§3.3 Tools: UGC Post API is now Legacy; must use Posts API (/rest/posts) with 'LinkedIn-Version: YYYYMM' header and 'X-Restli-Protocol-Version: 2.0.0'. Building against /v2/ugcPosts = tech debt on day one.",
"§3.2/§3.5: no idempotency key on POST /content or BullMQ job dispatch — retries could double-post",
"§3.8: stargue-net/package.json is byte-identical to stargue-com/package.json (same 'name' field) — name collision to resolve before workspace link",
"§4 Stage 3.9: MCP Streamable HTTP transport is current spec (stdio+SSE deprecated); pin @modelcontextprotocol/sdk version",
"§7: no AC→Vitest mapping; contract-test recording tool unchosen (Polly.js? msw?)"
],
"principles": {
"SEBP": {"score": 4, "rationale": "Clean package boundaries; MCP client reused by scheduler; no duplicated domain logic across apps."},
"SSoT": {"score": 4, "rationale": "Zod schema + sanitize config + design tokens each single-homed. Risk: stargue-net package.json duplicates stargue-com verbatim — name collision to resolve."},
"FPT": {"score": 3, "rationale": "Plan cites repo state and doc retrieval date, but LinkedIn API claims don't match live docs: UGC Posts is Legacy (Posts API current), PKCE not documented as supported. Fix before impl."},
"DiDSP": {"score": 4, "rationale": "Authentik SSO + internal bearer + Zod + libsodium + rate limits + audit log + no public MCP exposure. Missing idempotency key to prevent replay/double-post."},
"PbD": {"score": 4, "rationale": "Low PII surface; token retention tied to lifetime; analytics capped 13mo; erasure path named."},
"OF": {"score": 5, "rationale": "Correlation IDs across API→scheduler→MCP; Pino JSON; OpenObserve; append-only audit; per-call latency+outcome; PII redaction."},
"RBR": {"score": 4, "rationale": "Approval gate + DLQ + expand-then-contract + feature flag + proactive re-auth notification."},
"A11y": {"score": 3, "rationale": "WCAG 2.2 AA + axe-core CI + keyboard parity + reduced-motion + dual-theme contrast. Lacks SR test plan and aria-live specifics."},
"Testability": {"score": 3, "rationale": "Pure core extracted; injectable clock/ID; client behind interface. Missing AC→test mapping, HTTP-recording lib, and Vitest setup for Bun workspaces."}
}
},
{
"agent": "tea",
"verdict": "REVISE",
"confidence": "high",
"summary": "Architecture is sound and SSoT/OF are exemplary; revise §4 DoDs into measurable gates, close refresh-race + idempotency + migration-rehearsal + kill-switch gaps, then re-score — currently REVISE.",
"gaps": [
"§4 Stage DoDs largely untestable as stated ('Smoke tests pass', 'Test post lands', 'End-to-end happy path on staging', 'Matches stargue.com visual system') — add measurable criteria",
"§7 omits flake controls: no frozen clock, seeded RNG, deterministic IDs, or VCR/contract-test refresh cadence",
"§3.3 refresh path lacks concurrency guard — two scheduler jobs on expired token simultaneously can double-rotate; specify SELECT FOR UPDATE / advisory lock",
"§3.5 'expand-only' migrations claimed but no shadow-column/backfill/contract sequence shown; add migration rehearsal DoD",
"§4 Stage 4 DLQ DoD (4.3) names backoff but not idempotency key — retries could duplicate public posts",
"§4 Stage 3 lacks rate-limit test DoD — add burst-21-calls test asserting 429 on #21",
"§3.6 A11y: axe-core 'baseline' and 'no violations above minor' is vague — specify ruleset version, severity gate, SR smoke",
"§3.4 sanitizer: no DoD for the private-path corpus itself — add 'CI fails if test/corpus/private/* round-trips without build error'",
"§8 gate #3 assumes Authentik exists — Stage 5.2 DoD circular if not",
"No kill-switch / feature flag for LinkedIn poster — RBR requires fast-disable independent of code deploy"
],
"principles": {
"SEBP": {"score": 4, "rationale": "Clean package/app split (§3.1); client shared between scheduler+MCP; tradeoff acknowledged."},
"SSoT": {"score": 5, "rationale": "Zod schema, sanitize config, design tokens each in one package."},
"FPT": {"score": 4, "rationale": "Cites LinkedIn docs with retrieval date (§1); stack matches verified repo state; refresh-concurrency not reasoned from first principles."},
"DiDSP": {"score": 4, "rationale": "Layered (Zod+Authentik+bearer+libsodium+audit) but idempotency + refresh-race gaps reduce from 5."},
"PbD": {"score": 4, "rationale": "Token min/encrypt/retention named; 13-month metrics retention stated. SAR path asserted, not DoD-tested."},
"OF": {"score": 5, "rationale": "Correlation IDs end-to-end, append-only audit, PII redaction, per-call LinkedIn latency capture."},
"RBR": {"score": 3, "rationale": "Approval gate, DLQ, proactive auth notify present — but expand-contract migrations unspecified, no idempotency key, no runtime kill-switch."},
"A11y": {"score": 3, "rationale": "WCAG 2.2 AA named, axe-core in CI, two-theme contrast — severity gate + SR smoke + ruleset version unspecified."},
"Testability": {"score": 3, "rationale": "Pure core extracted, injectable clock/ID/randomness, client-behind-interface — but Stage DoDs largely untestable and no determinism/record-refresh policy."}
}
}
]
}