blue/.blue/docs/spikes/2026-01-26T1500Z-formalize-sdlc-workflow-and-release-process.wip.md
Eric Garcia 0fea499957 feat: lifecycle suffixes for all document states + resolve all clippy warnings
Every document filename now mirrors its lifecycle state with a status
suffix (e.g., .draft.md, .wip.md, .accepted.md). No more bare .md for
tracked document types. Also renamed all from_str methods to parse to
avoid FromStr trait confusion, introduced StagingDeploymentParams struct,
and fixed all 19 clippy warnings across the codebase.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 12:19:46 -05:00

13 KiB

Spike: Formalize SDLC Workflow and Release Process

Status In Progress
Date 2026-01-26
Time Box 2 hours

Question

What gaps exist between our current implicit workflow practices and a formalized SDLC? Specifically: (1) Are docs committed upon RFC approval? (2) Is worktree usage enforced for all implementation? (3) Is worktree cleanup enforced post-merge? (4) Is squash merge enforced? (5) Is direct-to-main merge properly blocked? (6) Is the release process with semver fully formalized?


Investigation

Current Enforcement Audit

Area Enforcement Location
Doc commit on acceptance MISSING server.rs:2684-2783
Worktree required for implementation SOFT (warning only) server.rs:2864-2955
Worktree cleanup after merge SOFT (suggestion only) pr.rs:341-441
Squash merge SOFT (default, overridable) pr.rs:344-413
Direct-to-main blocking HARD (error, no bypass) pr.rs:98-107
Release process + semver PARTIAL (blocks in-progress, manual commands) release.rs:28-98
Specialized agent roles AD HOC (alignment-expert exists, doc-writer informal) .claude/agents/

Gap Analysis

1. Doc Commit on Approval — MISSING

When blue_rfc_update_status transitions an RFC to accepted, the handler updates the markdown file's status field via update_markdown_status() but does not git-commit the change. The document sits modified but uncommitted.

What should happen: On acceptance, the RFC document (and any companion plan file) should be committed to the current branch with a message like docs: accept RFC NNNN - Title.

2. Worktree Enforcement — SOFT

blue_rfc_update_status emits a warning when transitioning to in-progress without a worktree, but blue_rfc_task_complete has no worktree check at all — tasks can be marked complete from any branch. There's no enforcement that implementation happens in isolation.

What should happen: blue_rfc_task_complete should verify the current working directory is inside a worktree for that RFC, or at minimum warn. blue_worktree_create should remain the gate for transitioning to in-progress (which it already enforces via plan file requirement).

3. Worktree Cleanup After Merge — SOFT

blue_pr_merge returns "next_steps": ["Run blue_worktree_cleanup..."] but takes no action. Cleanup is entirely manual.

What should happen: After a successful merge, blue_pr_merge should either auto-invoke cleanup or emit a stronger signal (e.g., a reminder that surfaces in blue_next/blue_status).

4. Squash Merge — SOFT

squash defaults to true but accepts squash=false. No validation rejects non-squash merges.

What should happen: Remove the squash parameter entirely. All PR merges should be squash-only. The MergeStrategy::Merge path should only be available to blue_release_create when merging develop into main.

5. Direct-to-Main Blocking — HARD (Already correct)

blue_pr_create rejects base="main" or base="master" with an error. No bypass exists. This is the only fully enforced constraint.

No change needed. This is correct as-is.

6. Release Process — PARTIAL

blue_release_create blocks if RFCs are in-progress (good), analyzes implemented RFCs for version bump type (good), generates changelog entries (good), but:

  • Returns shell commands instead of executing them
  • Version is hardcoded to "0.1.0" instead of reading from Cargo.toml
  • Does not create the release PR, tag, or push
  • Does not update version files

What should happen: The release handler should:

  1. Read current version from Cargo.toml
  2. Calculate next version from implemented RFCs
  3. Allow version override
  4. Create a release branch from develop
  5. Update Cargo.toml version (all workspace members)
  6. Generate/update CHANGELOG.md
  7. Commit version bump
  8. Create PR targeting main (the ONE exception to the base-branch rule)
  9. After merge: tag vX.Y.Z, push tag
  10. Mark included RFCs as released

7. Specialized Agent Roles — AD HOC

The project already uses .claude/agents/alignment-expert.md for dialogue participants. The technical-writer built-in subagent type has been used ad hoc for writing spike documentation, producing well-structured output that follows Blue's document formats. But there's no formalization of which agent types to use for which SDLC activities.

Current state:

  • alignment-expert — custom agent in .claude/agents/, used by dialogue orchestration
  • technical-writer — built-in subagent, used informally for spike writeups
  • No custom agent for Blue-specific doc writing (spikes, RFCs, ADRs)
  • No mapping of SDLC phase → agent type

What should happen: Create a doc-writer custom agent in .claude/agents/ that knows Blue's document formats (spike, RFC, ADR), voice rules (2 sentences before action, no hedging, evidence over opinion), and writing conventions (tables for comparisons, code blocks for examples, direct conclusions). Map each SDLC activity to a recommended agent:

SDLC Activity Agent Rationale
Spike investigation writeup doc-writer Structured findings, consistent format
RFC drafting doc-writer Knows RFC template, voice rules
Alignment dialogues alignment-expert Bounded output, marker format
Code implementation default (opus) Full tool access needed
Code review / analysis Explore subagent Read-only, thorough search

Proposed SDLC Workflow (Formalized)

Branch Model

main ──────────────────────────────── (releases only, tagged)
  │
  └── develop ─────────────────────── (integration branch, all PRs target here)
        │
        ├── feature-branch-1 ──────── (worktree, squash-merged to develop)
        ├── feature-branch-2 ──────── (worktree, squash-merged to develop)
        └── release/vX.Y.Z ────────── (release prep, merged to main + tagged)

Lifecycle

RFC Draft
  │
  ├─ Dialogue/Review
  │
  ▼
RFC Accepted ──────── git commit docs (auto)
  │
  ├─ Plan file created
  │
  ▼
Worktree Created ──── branch from develop, isolated workspace
  │
  ├─ Implementation (tasks completed in worktree only)
  │
  ▼
RFC Implemented ───── ≥70% plan progress gate
  │
  ├─ PR created (squash-only, base=develop)
  ├─ Test plan verified
  ├─ Approvals checked
  │
  ▼
PR Merged (squash) ── worktree cleaned up (auto or prompted)
  │
  ├─ ... repeat for other RFCs ...
  │
  ▼
Release ───────────── version bump, changelog, PR to main, tag

Semver Rules

RFC Title Keywords Bump Example
"breaking", "remove", "deprecate", "redesign" Major 1.0.0 → 2.0.0
"add", "implement", "feature", "new", "support" Minor 1.0.0 → 1.1.0
"fix", "patch", "docs", "refactor", "test" Patch 1.0.0 → 1.0.1

Pre-1.0: Breaking changes bump minor, features bump patch.

Release Checklist

  1. No RFCs in in-progress status
  2. All worktrees cleaned up
  3. Version calculated from implemented RFCs since last release
  4. Cargo.toml version updated across workspace
  5. CHANGELOG.md generated from RFC titles
  6. Release PR: developmain (only valid main-targeting PR)
  7. After merge: git tag vX.Y.Z && git push origin vX.Y.Z
  8. Implemented RFCs marked as released

Findings Summary

Already Working

  • Direct-to-main blocking (HARD gate)
  • Worktree creation requires plan file
  • PR merge defaults to squash
  • Release blocks on in-progress work
  • Branch naming convention (RFC 0007)

Needs Hardening

  1. Doc commit on acceptance — Add git add/commit in blue_rfc_update_status
  2. Squash-only merge — Remove squash parameter from blue_pr_merge, always squash for feature PRs
  3. Worktree cleanup — Auto-cleanup after merge or surface as blocking reminder in blue_status
  4. Release execution — Read real version, create release branch, update files, create PR, tag

Needs New Implementation

  1. Release branch flowrelease/vX.Y.Z branch that's the one exception to the main-targeting PR rule
  2. Version file updates — Automated Cargo.toml workspace version bump
  3. CHANGELOG generation — Append to CHANGELOG.md from implemented RFC list
  4. RFC released status — New terminal status after release ships
  5. doc-writer custom agent.claude/agents/doc-writer.md with Blue format/voice knowledge
  6. Agent-to-phase mapping — Formalize which agent handles which SDLC activity

Recommendation

Create an RFC to implement these changes. The work naturally splits into two phases:

Phase 1 — Workflow Hardening (enforce what we already have):

  • Auto-commit docs on acceptance
  • Remove squash override (always squash feature PRs)
  • Auto-cleanup or remind after merge
  • Worktree presence check in task completion

Phase 2 — Release Formalization:

  • Read version from Cargo.toml
  • Release branch creation
  • Version bump + changelog generation
  • Tag creation and push
  • RFC released status lifecycle

Phase 3 — Agent Formalization:

  • Create doc-writer custom agent (Blue format/voice knowledge, sonnet model)
  • Define agent-to-phase mapping (doc-writer for spikes/RFCs, alignment-expert for dialogues, Explore for review)
  • Install at both project (.claude/agents/) and user (~/.claude/agents/) level for cross-repo use

Cross-Spike Note: Fixing "Edit Before RFC" via Plugin Architecture

The core bug — Claude jumping into code edits before an RFC is approved and a worktree is active — has a mechanical fix when combined with the plugin architecture spike and the thin-plugin/fat-binary spike.

The Problem

MCP instructions tell Claude about voice and ADRs, but say nothing about workflow discipline. The worktree check is a warning, not a gate. Claude's default behavior is to be helpful, so it edits files directly on develop without an RFC or worktree. No amount of conversational guidance fixes this because Claude doesn't reliably follow soft suggestions across sessions.

The Fix: PreToolUse Hooks (Mechanical Gate)

The plugin architecture spike documents that hooks can intercept PreToolUse events. A PreToolUse hook on Write, Edit, and Bash (for file-writing commands) can call the compiled binary to check:

  1. Is the current working directory inside a Blue worktree?
  2. Does the worktree correspond to an RFC in accepted or in-progress status?
  3. Are the files being modified within the worktree's directory tree?

If any check fails, the hook blocks the tool call before it executes. Claude cannot bypass this — hooks run before the tool, not after.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "blue guard --tool=$TOOL_NAME --path=$INPUT_FILE_PATH"
          }
        ]
      }
    ]
  }
}

Why This Fits Thin Plugin / Fat Binary

Per the thin-plugin/fat-binary spike, the hook file is a one-liner that calls the compiled binary. The blue guard command contains all the logic in compiled Rust:

  • Static (user sees): "command": "blue guard --tool=$TOOL_NAME" — meaningless without knowing what guard checks
  • Runtime (compiled): Worktree detection, RFC status validation, path-in-worktree verification, allowlist for non-code files (docs, spikes, ADRs that don't require worktrees)

Allowlist: What Can Be Edited Without a Worktree

Not all edits require an RFC. The guard command needs an allowlist:

Path Pattern Requires Worktree? Rationale
.blue/docs/spikes/** No Spikes are investigation, not implementation
.blue/docs/adrs/** No ADRs are philosophical, not code
.blue/docs/rfcs/** No RFC drafts are pre-implementation
.blue/docs/dialogues/** No Dialogues are discussion artifacts
.claude/agents/** No Agent definitions are config
crates/**, src/**, tests/** Yes Code requires RFC + worktree
Cargo.toml, Cargo.lock Yes Dependency changes are implementation
Any other file Yes (default) Safe default

Combined Enforcement Stack

With all three spikes folded into one RFC, the enforcement becomes layered:

  1. MCP instructions (fat binary) — Tell Claude the rules: "Do not edit code without an approved RFC and active worktree"
  2. PreToolUse hook (plugin) — Mechanically block edits that violate the rules before they execute
  3. Status transition gates (MCP tools) — Prevent RFC status from advancing without prerequisites (plan, worktree, 70% progress)
  4. blue_next / blue_status (MCP tools) — Surface violations as the top priority action

Layer 1 is aspirational (Claude may ignore it). Layer 2 is mechanical (Claude cannot bypass it). Layers 3-4 are structural (the workflow itself prevents skipping steps). Together they close the gap.