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>
This commit is contained in:
122
.claude/hooks/gate-plan-exit.sh
Executable file
122
.claude/hooks/gate-plan-exit.sh
Executable file
@@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env bash
|
||||
# BMAD Plan Review Gate — PreToolUse hook for ExitPlanMode.
|
||||
#
|
||||
# Enforces CLAUDE.md "Multi-Agent Plan Review Gate": blocks ExitPlanMode
|
||||
# until /tmp/bmad-panel-verdicts-<session_id>.json contains verdicts from
|
||||
# all 7 BMAD panel agents (analyst, architect, pm, sm, ux-designer, dev, tea).
|
||||
#
|
||||
# Uses /usr/bin/node (always present; no jq dependency which is missing in
|
||||
# this box's non-interactive PATH). Fail-closed via permissionDecision=deny
|
||||
# in hookSpecificOutput JSON on stdout.
|
||||
#
|
||||
# The Claude session is responsible for writing verdicts via Agent calls —
|
||||
# this hook only validates the file exists and is well-formed.
|
||||
|
||||
set -eu
|
||||
|
||||
export HOOK_PAYLOAD
|
||||
HOOK_PAYLOAD="$(cat)"
|
||||
|
||||
/usr/bin/node -e '
|
||||
const raw = process.env.HOOK_PAYLOAD || "";
|
||||
let payload;
|
||||
try { payload = JSON.parse(raw); } catch (e) {
|
||||
// Malformed payload — allow through; not our job to second-guess.
|
||||
process.exit(0);
|
||||
}
|
||||
if ((payload.tool_name || "") !== "ExitPlanMode") process.exit(0);
|
||||
|
||||
const fs = require("fs");
|
||||
const sessionId = payload.session_id || "unknown";
|
||||
const verdictFile = "/tmp/bmad-panel-verdicts-" + sessionId + ".json";
|
||||
|
||||
function deny(reason) {
|
||||
process.stdout.write(JSON.stringify({
|
||||
hookSpecificOutput: {
|
||||
hookEventName: "PreToolUse",
|
||||
permissionDecision: "deny",
|
||||
permissionDecisionReason: reason
|
||||
}
|
||||
}));
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (!fs.existsSync(verdictFile)) {
|
||||
deny("BMAD plan review gate: verdicts file missing at " + verdictFile +
|
||||
". Invoke the /bmad-plan skill (or launch the 7 panel agents manually) " +
|
||||
"and write verdicts to this path before ExitPlanMode. See CLAUDE.md > Plan Review Gate.");
|
||||
}
|
||||
|
||||
let doc;
|
||||
try { doc = JSON.parse(fs.readFileSync(verdictFile, "utf8")); } catch (e) {
|
||||
deny("BMAD plan review gate: " + verdictFile + " is not valid JSON (" + e.message + ").");
|
||||
}
|
||||
|
||||
const verdicts = Array.isArray(doc && doc.verdicts) ? doc.verdicts : null;
|
||||
if (!verdicts || verdicts.length < 7) {
|
||||
const n = verdicts ? verdicts.length : 0;
|
||||
deny("BMAD plan review gate: " + verdictFile + " has only " + n +
|
||||
"/7 panel verdicts. All 7 (analyst, architect, pm, sm, ux-designer, dev, tea) are required.");
|
||||
}
|
||||
|
||||
const missing = verdicts.filter(function (v) { return !v || !v.agent || !v.verdict; }).length;
|
||||
if (missing > 0) {
|
||||
deny("BMAD plan review gate: " + missing + " verdict entries missing .agent or .verdict fields.");
|
||||
}
|
||||
|
||||
const rejects = verdicts.filter(function (v) {
|
||||
return String(v.verdict).toUpperCase() === "REJECT";
|
||||
}).length;
|
||||
if (rejects > 0) {
|
||||
deny("BMAD plan review gate: " + rejects +
|
||||
" panel agent(s) returned REJECT. Revise the plan or escalate before ExitPlanMode.");
|
||||
}
|
||||
|
||||
// Foundational Principles gate (two-tier).
|
||||
// Tier-1 principles MUST be scored on every verdict.
|
||||
// Tier-2 principles are scored only when declared in doc.applicable_tier2.
|
||||
// See .clinerules/12-foundational-principles.md for doctrine and rubric.
|
||||
var TIER1 = ["SEBP", "SSoT", "FPT", "DiDSP", "PbD", "OF"];
|
||||
var TIER2_ALLOWED = ["RBR", "A11y", "Testability"];
|
||||
var MIN_SCORE = 3;
|
||||
|
||||
var declaredTier2 = Array.isArray(doc.applicable_tier2) ? doc.applicable_tier2.slice() : [];
|
||||
var invalidTier2 = declaredTier2.filter(function (k) { return TIER2_ALLOWED.indexOf(k) === -1; });
|
||||
if (invalidTier2.length > 0) {
|
||||
deny("BMAD plan review gate: applicable_tier2 contains unknown principle(s): " +
|
||||
invalidTier2.join(", ") + ". Allowed values: " + TIER2_ALLOWED.join(", ") +
|
||||
". See .clinerules/12-foundational-principles.md > Tier-2 Principles.");
|
||||
}
|
||||
var requiredKeys = TIER1.concat(declaredTier2);
|
||||
|
||||
var principleViolations = [];
|
||||
verdicts.forEach(function (v) {
|
||||
var p = v && v.principles;
|
||||
if (!p || typeof p !== "object") {
|
||||
principleViolations.push((v && v.agent ? v.agent : "?") + ": missing principles object");
|
||||
return;
|
||||
}
|
||||
requiredKeys.forEach(function (key) {
|
||||
var entry = p[key];
|
||||
if (!entry || typeof entry.score !== "number") {
|
||||
principleViolations.push(v.agent + "." + key + ": missing or non-numeric score");
|
||||
} else if (entry.score < MIN_SCORE) {
|
||||
principleViolations.push(v.agent + "." + key + ": score " + entry.score + " < " + MIN_SCORE);
|
||||
} else if (typeof entry.rationale !== "string" || !entry.rationale.trim()) {
|
||||
principleViolations.push(v.agent + "." + key + ": missing rationale");
|
||||
}
|
||||
});
|
||||
});
|
||||
if (principleViolations.length > 0) {
|
||||
var tier2Note = declaredTier2.length > 0
|
||||
? " plus declared tier-2 {" + declaredTier2.join(",") + "}"
|
||||
: " (no tier-2 declared)";
|
||||
deny("BMAD plan review gate: foundational principle scoring failed:\n - " +
|
||||
principleViolations.join("\n - ") +
|
||||
"\n\nEvery verdict must include principles for tier-1 {" + TIER1.join(",") + "}" + tier2Note +
|
||||
" with .score >= " + MIN_SCORE + " and a non-empty .rationale. " +
|
||||
"See .clinerules/12-foundational-principles.md.");
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
'
|
||||
192
.claude/skills/bmad-plan/SKILL.md
Normal file
192
.claude/skills/bmad-plan/SKILL.md
Normal file
@@ -0,0 +1,192 @@
|
||||
---
|
||||
name: bmad-plan
|
||||
description: |
|
||||
BMAD 7-agent plan review workflow. Use when drafting any implementation
|
||||
plan that the CLAUDE.md "Multi-Agent Plan Review Gate" directive applies
|
||||
to. Ensures the gate is satisfied before ExitPlanMode is attempted —
|
||||
the PreToolUse hook at .claude/hooks/gate-plan-exit.sh will deny
|
||||
ExitPlanMode otherwise.
|
||||
---
|
||||
|
||||
# BMAD Plan + 7-Agent Panel Review
|
||||
|
||||
## When to invoke
|
||||
|
||||
Every plan-mode session for a non-trivial implementation. CLAUDE.md mandates this for anything beyond the smallest of fixes. Skip only for genuine one-liners (typo, comment tweak) — and even then, think twice.
|
||||
|
||||
## Workflow (non-negotiable)
|
||||
|
||||
### Phase 1 — Draft
|
||||
|
||||
1. Write the plan to the plan file supplied by plan mode (e.g. `/home/devuser/.claude/plans/<slug>.md`).
|
||||
2. The plan must follow CLAUDE.md format: `Context`, stages with DoD, critical files, verification, authorization gates, risks.
|
||||
3. **Declare tier-2 applicability** at the top of the plan as an `Applicability:` line, e.g.:
|
||||
|
||||
```
|
||||
Applicability: tier-2 = [RBR, A11y] # plan touches a migration and adds new UI
|
||||
```
|
||||
|
||||
Allowed values: `RBR` (Reversibility / Blast Radius), `A11y` (Accessibility), `Testability`. Empty list (`tier-2 = []`) is valid when none apply. The Analyst (Mary) is responsible for the call — see `.clinerules/12-foundational-principles.md > Tier-2 Principles` for when to declare each.
|
||||
|
||||
### Phase 1.5 — Harden the per-agent dispatch prompt via prompt-master
|
||||
|
||||
The 7 BMAD panel charters (`_bmad/bmm/agents/{analyst,architect,pm,sm,ux-designer,dev,tea}.md`) are the SSoT for each panelist's role and stay untouched. What this skill DOES build per-plan is the **dispatch prompt** — the message the session sends to each panelist along with the plan to review, the cross-cutting principle rubric (tier-1 + declared tier-2), and the verdict shape.
|
||||
|
||||
Per [`.clinerules/13-agent-spawning-protocol.md`](../../../.clinerules/13-agent-spawning-protocol.md), this dispatch prompt MUST be hardened before Phase 2:
|
||||
|
||||
1. Draft the dispatch template (one shape, parameterized per panelist).
|
||||
2. Invoke the `prompt-master` skill with the draft + plan summary + principle rubric. One invocation covers all 7 panelists since they share dispatch structure.
|
||||
3. Apply prompt-master's hardening to the dispatch (output-format lock, severity anchors, structural caps on verdict length, link-format lock, verification discipline).
|
||||
4. **Add the standard graph-tool leverage block** (per [`memory-bank/agent-prompts/README.md > Graph-tool leverage`](../../../memory-bank/agent-prompts/README.md)) to the dispatch — instruct each panelist to consult CRG (`crg_*` MCP tools) and Graphify (`docs/architecture/knowledge-graph/GRAPH_REPORT.md`) before speculative file reads. CRG benchmarks at 8.2× token reduction; Dev (Amelia) and TEA (Murat) gain the most leverage from this.
|
||||
5. Use the hardened dispatch in Phase 2 — substituting only the panelist-specific focus from the Phase 2 table.
|
||||
|
||||
This is the pattern that catches: link-format drift, unanchored severity calibration, soft length caps ignored, asymmetric specificity across panelists, and unverifiable aggregate claims. Skipping this step is the documented failure mode of the 2026-04-16 9-agent baseline audit.
|
||||
|
||||
### Phase 2 — Launch the 7 panel agents in parallel
|
||||
|
||||
**MUST launch each agent separately in parallel** per memory `feedback_separate_agents` — never consolidate them into one prompt.
|
||||
|
||||
Panel members (charters at `_bmad/bmm/agents/<name>.md`):
|
||||
|
||||
| Agent | Focus |
|
||||
| --- | --- |
|
||||
| Mary (analyst) | Requirements completeness, stakeholder alignment |
|
||||
| Winston (architect) | Architectural soundness, scalability, technology selection |
|
||||
| John (pm) | Business value, prioritization, risk identification |
|
||||
| Bob (sm) | Story clarity, actionability, sprint-readiness |
|
||||
| Sally (ux-designer) | User impact, UX considerations, accessibility |
|
||||
| Amelia (dev) | Implementation feasibility, code patterns, existing code reuse |
|
||||
| Murat (tea) | Test strategy, quality gates, CI/CD impact |
|
||||
|
||||
Each agent MUST return a structured verdict. Tier-1 principles are scored on every plan; tier-2 principles are scored only when declared in the plan's `Applicability` line (see Phase 1 step 3).
|
||||
|
||||
```
|
||||
VERDICT: APPROVE | REVISE | REJECT
|
||||
CONFIDENCE: low | medium | high
|
||||
STRENGTHS: - …
|
||||
GAPS: - …
|
||||
PRINCIPLES (tier-1, always):
|
||||
SEBP: { score: 1-5, rationale: "…" }
|
||||
SSoT: { score: 1-5, rationale: "…" }
|
||||
FPT: { score: 1-5, rationale: "…" }
|
||||
DiDSP: { score: 1-5, rationale: "…" }
|
||||
PbD: { score: 1-5, rationale: "…" }
|
||||
OF: { score: 1-5, rationale: "…" }
|
||||
PRINCIPLES (tier-2, only if declared):
|
||||
RBR: { score: 1-5, rationale: "…" } # if applicable
|
||||
A11y: { score: 1-5, rationale: "…" } # if applicable
|
||||
Testability: { score: 1-5, rationale: "…" } # if applicable
|
||||
ONE-LINE SUMMARY: …
|
||||
```
|
||||
|
||||
### Foundational Principle Scoring (gate-enforced)
|
||||
|
||||
All 7 agents MUST score the plan against the **six tier-1 principles**. They MUST also score every **tier-2 principle declared in `applicable_tier2`** for this plan. The hook denies `ExitPlanMode` if any required score is below 3, missing, or has no rationale.
|
||||
|
||||
**Tier-1 (always scored):**
|
||||
|
||||
| Principle | Definition | Score 1 (violation) → 5 (exemplary) |
|
||||
|-----------|------------|-------------------------------------|
|
||||
| **SEBP** — Software Engineering Best Practices | SOLID, DRY, separation of concerns, clear boundaries | 1 = duplicated logic / leaky abstractions; 5 = clean composition, no smell |
|
||||
| **SSoT** — Single Source of Truth | One authoritative source per concept (constants, types, schemas, config) | 1 = duplicated constants/types/config; 5 = every fact has exactly one home |
|
||||
| **FPT** — First Principle Thinking | Reasoning grounded in this codebase + verified docs, not training-data assumptions | 1 = "Next.js probably does X" without checking; 5 = cites file paths, runs probes |
|
||||
| **DiDSP** — Defense in Depth Security | Layered security: input validation, authz, rate limits, audit, least privilege | 1 = unvalidated input or missing authz on sensitive op; 5 = multiple independent layers + audit |
|
||||
| **PbD** — Privacy by Design | GDPR Art. 25: minimisation, purpose limitation, retention, lawful basis, user rights | 1 = collects PII without purpose / no erasure path; 5 = active minimisation + lifecycle wired |
|
||||
| **OF** — Observability First | Structured logs, metrics, audit on every meaningful boundary | 1 = silent failure paths; 5 = correlation IDs, audit on regulated ops, dashboards referenced |
|
||||
|
||||
**Tier-2 (scored only when declared):**
|
||||
|
||||
| Principle | Declare when… | Score 1 → 5 |
|
||||
|-----------|---------------|-------------|
|
||||
| **RBR** — Reversibility / Blast Radius | Plan touches migrations, deletes, third-party calls, shared infra, CI/CD, prod data | 1 = destructive, no rollback; 5 = expand-then-contract + flags + recovery procedure |
|
||||
| **A11y** — Accessibility (WCAG 2.2 AA) | Plan introduces or significantly changes user-facing UI | 1 = keyboard-trap / colour-only state; 5 = keyboard parity, contrast, screen-reader tested |
|
||||
| **Testability** | Plan introduces or significantly changes business logic / domain rules / boundaries | 1 = logic embedded in framework callbacks, hidden globals; 5 = pure cores, injectable seams |
|
||||
|
||||
A score of 3 means "adequate, no blocking concerns." Below 3 blocks the plan. Score 1–2 must be paired with a `REVISE` or `REJECT` verdict and a concrete gap entry.
|
||||
|
||||
Full doctrine, definitions, and acceptance signals: [`.clinerules/12-foundational-principles.md`](../../../.clinerules/12-foundational-principles.md).
|
||||
|
||||
### Phase 3 — Write verdicts file
|
||||
|
||||
After all 7 agents return, write:
|
||||
|
||||
```
|
||||
/tmp/bmad-panel-verdicts-${CLAUDE_SESSION_ID}.json
|
||||
```
|
||||
|
||||
Shape (enforced by the hook):
|
||||
|
||||
```json
|
||||
{
|
||||
"plan_file": "<path to plan file>",
|
||||
"created_at": "<ISO 8601>",
|
||||
"applicable_tier2": ["RBR", "A11y"],
|
||||
"verdicts": [
|
||||
{
|
||||
"agent": "analyst",
|
||||
"verdict": "APPROVE | REVISE | REJECT",
|
||||
"confidence": "low | medium | high",
|
||||
"summary": "one-line summary from the agent",
|
||||
"gaps": ["…", "…"],
|
||||
"principles": {
|
||||
"SEBP": { "score": 4, "rationale": "…" },
|
||||
"SSoT": { "score": 5, "rationale": "…" },
|
||||
"FPT": { "score": 4, "rationale": "…" },
|
||||
"DiDSP": { "score": 3, "rationale": "…" },
|
||||
"PbD": { "score": 4, "rationale": "…" },
|
||||
"OF": { "score": 4, "rationale": "…" },
|
||||
"RBR": { "score": 3, "rationale": "…" },
|
||||
"A11y": { "score": 4, "rationale": "…" }
|
||||
}
|
||||
},
|
||||
{ "agent": "architect", ... },
|
||||
{ "agent": "pm", ... },
|
||||
{ "agent": "sm", ... },
|
||||
{ "agent": "ux-designer", ... },
|
||||
{ "agent": "dev", ... },
|
||||
{ "agent": "tea", ... }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Notes on the shape:
|
||||
|
||||
- `applicable_tier2` mirrors the `Applicability:` line from the plan (Phase 1 step 3). Allowed values: `RBR`, `A11y`, `Testability`. An empty array `[]` is valid and means no tier-2 principles apply.
|
||||
- Each verdict's `principles` object MUST cover all six tier-1 keys plus every key in `applicable_tier2`. Tier-2 keys not declared SHOULD be omitted (the hook ignores them).
|
||||
|
||||
Requirements enforced by `gate-plan-exit.sh`:
|
||||
|
||||
- `verdicts` is an array of ≥7 entries.
|
||||
- Every entry has non-empty `agent` and `verdict`.
|
||||
- Zero entries with `verdict: "REJECT"` (any REJECT blocks exit).
|
||||
- `applicable_tier2` (when present) contains only `RBR`, `A11y`, or `Testability`.
|
||||
- Every verdict has `principles.{SEBP,SSoT,FPT,DiDSP,PbD,OF}` with numeric `.score >= 3` and non-empty `.rationale`.
|
||||
- Every verdict has the same fields for every key in `applicable_tier2`.
|
||||
|
||||
### Phase 4 — Consolidate
|
||||
|
||||
1. Read all 7 verdicts. Merge gap/concern items into an action list.
|
||||
2. Revise the plan file to address every REVISE item. Cite which agent raised each concern (traceability).
|
||||
3. If unanimous APPROVE: proceed to Phase 5.
|
||||
4. If any REVISE remains after plan revision: re-run the panel or escalate to the user.
|
||||
5. If any REJECT: revise or escalate; do not bypass.
|
||||
|
||||
### Phase 5 — Exit
|
||||
|
||||
1. Call `ExitPlanMode`. The hook reads the verdicts file, confirms ≥7 entries and no REJECTs, and allows the call.
|
||||
2. After user approves, copy the plan to `docs/plans/{YYYY-MM-DD}-{topic}.md` per CLAUDE.md filing rule (plans in `.claude/plans/` are ephemeral).
|
||||
|
||||
## Failure modes
|
||||
|
||||
- **Hook denies with "verdicts file missing"**: you haven't written `/tmp/bmad-panel-verdicts-${CLAUDE_SESSION_ID}.json` yet. Run the panel (Phase 2) and write the file (Phase 3).
|
||||
- **Hook denies with "only N/7 verdicts"**: an agent result was dropped. Re-launch only the missing agents and update the file.
|
||||
- **Hook denies with "N panel agent(s) returned REJECT"**: revise the plan or escalate — do not remove REJECTs from the file without addressing their substance.
|
||||
- **Hook denies with "foundational principle scoring failed"**: one or more verdicts is missing the `principles` object, has a required `.score < 3`, or has an empty `.rationale` for a tier-1 key or a declared tier-2 key. Re-engage the agent(s) whose verdicts triggered the failure with the rubric in *Foundational Principle Scoring* — never raise a score without substantive justification.
|
||||
- **Hook denies with "applicable_tier2 contains unknown principle(s)"**: the `applicable_tier2` array contains a value other than `RBR`, `A11y`, or `Testability`. Fix the typo or remove the entry — do not extend the allowed set without updating both this skill and `.clinerules/12-foundational-principles.md`.
|
||||
|
||||
## Anti-patterns (do not do)
|
||||
|
||||
- Do not skip the panel and call ExitPlanMode directly. The hook will deny.
|
||||
- Do not batch agents into a single prompt. Per memory `feedback_separate_agents`, always separate + parallel.
|
||||
- Do not write fabricated verdicts to the file to bypass the gate. The gate exists to protect plan quality; bypassing it defeats the purpose and will be caught by the next review.
|
||||
- Do not use this skill for trivial one-line fixes — see CLAUDE.md scope rules.
|
||||
Reference in New Issue
Block a user