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>
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 fromCargo.toml - Does not create the release PR, tag, or push
- Does not update version files
What should happen: The release handler should:
- Read current version from
Cargo.toml - Calculate next version from implemented RFCs
- Allow version override
- Create a release branch from develop
- Update
Cargo.tomlversion (all workspace members) - Generate/update
CHANGELOG.md - Commit version bump
- Create PR targeting main (the ONE exception to the base-branch rule)
- After merge: tag
vX.Y.Z, push tag - 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 orchestrationtechnical-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
- No RFCs in
in-progressstatus - All worktrees cleaned up
- Version calculated from implemented RFCs since last release
Cargo.tomlversion updated across workspaceCHANGELOG.mdgenerated from RFC titles- Release PR:
develop→main(only valid main-targeting PR) - After merge:
git tag vX.Y.Z && git push origin vX.Y.Z - 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
- Doc commit on acceptance — Add git add/commit in
blue_rfc_update_status - Squash-only merge — Remove
squashparameter fromblue_pr_merge, always squash for feature PRs - Worktree cleanup — Auto-cleanup after merge or surface as blocking reminder in
blue_status - Release execution — Read real version, create release branch, update files, create PR, tag
Needs New Implementation
- Release branch flow —
release/vX.Y.Zbranch that's the one exception to the main-targeting PR rule - Version file updates — Automated
Cargo.tomlworkspace version bump - CHANGELOG generation — Append to
CHANGELOG.mdfrom implemented RFC list - RFC
releasedstatus — New terminal status after release ships doc-writercustom agent —.claude/agents/doc-writer.mdwith Blue format/voice knowledge- 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
releasedstatus lifecycle
Phase 3 — Agent Formalization:
- Create
doc-writercustom 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:
- Is the current working directory inside a Blue worktree?
- Does the worktree correspond to an RFC in
acceptedorin-progressstatus? - 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 whatguardchecks - 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:
- MCP instructions (fat binary) — Tell Claude the rules: "Do not edit code without an approved RFC and active worktree"
- PreToolUse hook (plugin) — Mechanically block edits that violate the rules before they execute
- Status transition gates (MCP tools) — Prevent RFC status from advancing without prerequisites (plan, worktree, 70% progress)
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.