Decisions
- Pending: Notification channel — terminal only, or also messaging (iMessage/WhatsApp)?
- Pending: Stale threshold — how many days without activity before flagging?
- Pending: Gate enforcement — strict (blocks transition) or advisory (warns but allows)?
- Pending: Auto-close — fully automatic on user confirmation, or require explicit archive command?
- Pending: Pitch state — include in state machine or handle separately in FR-050?
User Tasks
Summary
Replace the current convention-based feature lifecycle with a code-enforced Python state machine that validates transitions, executes side effects, and auto-manages features through their lifecycle.
Problem / Motivation
Opus currently manages the feature lifecycle through rules and conventions — Claude follows instructions about moving files between folders, updating dashboards, and checking gates. This approach has several weaknesses:
- No enforcement: Nothing prevents invalid transitions (e.g., moving to in-progress with unresolved decisions)
- No automation: Dashboard updates, file moves, and git commits are all manual
- No visibility: No timestamps on state transitions, no stale detection, no activity tracking
- No nudging: Features waiting for user input can sit indefinitely without reminders
- Inconsistency: Different sessions may handle transitions differently depending on context loaded
Nexie solves this with a formal state machine that makes the lifecycle programmatic, validated, and automated.
Proposed Solution
Build a Python state machine module that:
-
Defines valid states and transitions as a formal graph:
pitch -> idea -> new -> planned -> in-progress -> done | v blockedPitch state: When the system (via FR-050 monitoring) proactively identifies a pain point, the workflow enters the “pitch” state. The system sends a structured 3-line message: Problem → Proposal → “Should I create an FR?“. User approves or rejects.
Fast-track logic: When the user initiates a feature request directly (via /new-feature or messaging), the pitch state is skipped and the workflow goes straight to “design” (spec creation by feature request specialist).
-
Validates every transition — checks gates before allowing state changes:
new -> planned: All open questions must be answeredplanned -> in-progress: No unresolved decisions, prerequisites metin-progress -> done: All phases complete, tests pass
-
Executes side effects on transition:
- Move FR file to the correct directory
- Update feature-dashboard.md
- Update related dashboards (review, tasks)
- Add history entry to the FR
- Commit changes to git with structured message
-
Tracks timing per state:
created_at,entered_new_at,entered_planned_at,started_at,completed_at- Duration in each state for retrospective analysis
-
Generates notifications when action is needed:
- Unresolved decisions blocking progress
- Stale features (no activity for X days)
- Phase completion (ready for next phase)
-
Provides query interface:
- “What’s blocked?” — features waiting on decisions or prerequisites
- “What’s stale?” — features without recent activity
- “What just completed?” — recent state transitions
Auto-close/archive flow (when user confirms “FR-040 is done”):
- Moves file from
in-progress/todone/ - Updates
feature-dashboard.md(status → done, increment done count) - Updates
user-review-dashboard.mdif applicable - Adds completion entry to FR history log with timestamp
- Commits all changes to git:
feat: close FR-040 — [title]
Open Questions
1. Notification Channel
Question: How should the state machine deliver notifications and nudges?
| Option | Description |
|---|---|
| A) Terminal only | Print notifications in Claude Code session. Simple, no external dependencies |
| B) Terminal + vault inbox | Write notifications to vault/90_inbox/ as notes. Visible in Obsidian |
| C) Terminal + messaging | Send via iMessage/WhatsApp (requires FR-023). Full push notification experience |
Recommendation: Option A for Phase 1-2, Option B for Phase 3. Messaging can come later via FR-023.
Decision:
2. Stale Threshold
Question: How many days without activity before a feature is flagged as stale?
| Option | Description |
|---|---|
| A) 3 days | Aggressive — catches issues fast but may be noisy |
| **B) 7 days | Balanced — one week without progress is worth flagging |
| C) 14 days | Relaxed — only flags truly abandoned work |
Recommendation: Option B — 7 days is a natural review cycle. Make it configurable per-FR for flexibility.
Decision:
3. Gate Enforcement
Question: Should gates strictly block transitions or just warn?
| Option | Description |
|---|---|
| A) Strict — block invalid transitions | Prevents mistakes but may frustrate when exceptions are legitimate |
| B) Advisory — warn but allow override | Flags issues, user decides. Respects user autonomy |
| C) Configurable per-gate | Some gates are strict (security), others advisory |
Recommendation: Option B to start — Opus is user-driven, not system-driven. Strict gates can be added for critical paths later.
Decision:
Phase Overview
| Phase | Description | Status |
|---|---|---|
| Phase 1 | State definitions + transition rules | — |
| Phase 2 | Side effects + auto-transitions | — |
| Phase 3 | Nudging + stale detection + notifications | — |
Phase 1: State Definitions + Transition Rules —
Goal: Define the state graph, transition validation, and gate checking as a Python module.
| File / Feature | Details | Owner | Status |
|---|---|---|---|
src/opus/workflow/states.py | State enum: idea, new, planned, in_progress, blocked, done. Transition graph definition | opus | — |
src/opus/workflow/machine.py | State machine class: current state, transition(target), validate(target), history | opus | — |
src/opus/workflow/gates.py | Gate definitions: what must be true for each transition. Parse FR to check gates | opus | — |
| FR parsing | Read FR frontmatter and content to extract status, decisions, phases, prerequisites | opus | — |
| Unit tests | Valid transitions succeed, invalid transitions rejected, gates enforced correctly | opus | — |
Phase 2: Side Effects + Auto-Transitions —
Goal: Execute automated actions on state transitions and detect when transitions should happen automatically.
| File / Feature | Details | Owner | Status |
|---|---|---|---|
src/opus/workflow/effects.py | Side effect registry: file moves, dashboard updates, history entries, git commits | opus | — |
| File moves | Auto-move FR files between lifecycle directories on transition | opus | — |
| Dashboard updates | Auto-update feature-dashboard.md counts and status | opus | — |
| History logging | Auto-add timestamped entries to FR history table | opus | — |
| Git commits | Auto-commit with structured message: feat: transition FR-XXX to [state] | opus | — |
| Auto-transition detection | When all phase tasks are done, auto-advance to next phase or completion | opus | — |
| Timing tracking | Record timestamps for each state entry, calculate duration per state | opus | — |
Phase 3: Nudging + Stale Detection + Notifications —
Goal: Proactively detect features needing attention and notify the user.
| File / Feature | Details | Owner | Status |
|---|---|---|---|
src/opus/workflow/monitor.py | Periodic scan of all FRs: check for stale, blocked, needing-input states | opus | — |
| Stale detection | Flag features with no git commits or file changes for X days | opus | — |
| Decision nudging | Detect FRs with unresolved decisions that are blocking progress | opus | — |
| Notification delivery | Format and deliver notifications (terminal print, vault inbox note) | opus | — |
| Query interface | CLI/skill queries: “what’s blocked?”, “what’s stale?”, “what completed this week?“ | opus | — |
| Integration with briefing | Feed stale/blocked items into daily briefing (FR-037) | opus | — |
Prerequisites / Gap Analysis
Requirements
| Requirement | Description |
|---|---|
| REQ-1 | Python project scaffold (FR-009) — state machine is Python code |
Current State
| Component | Status | Details |
|---|---|---|
| Python scaffold | — | FR-009 not started |
| Feature lifecycle rules | done | Defined in .claude/rules/ and vault conventions |
| Feature dashboard | done | vault/00_system/dashboards/feature-dashboard.md exists |
| File directory structure | done | ideas/, new/, planned/, in-progress/, done/ directories exist |
Gap (What’s missing?)
| Gap | Effort | Blocker? |
|---|---|---|
| Python scaffold (FR-009) | Med | Yes — code needs a runtime |
| FR frontmatter parser | Low | No — can be built as part of Phase 1 |
| Git automation helpers | Low | No — subprocess calls to git |
Test
Manual tests
| Test | Expected | Actual | Last |
|---|---|---|---|
| Valid transition (new → planned) with gates met | Transition succeeds, side effects execute | pending | - |
| Invalid transition (new → in-progress) skipping planned | Transition rejected with reason | pending | - |
| Gate check: unresolved decisions block planned → in-progress | Transition blocked, decisions listed | pending | - |
| Auto-close: user confirms done | File moved, dashboard updated, git committed | pending | - |
AI-verified tests
| Scenario | Expected behavior | Verification method |
|---|---|---|
| … | … | … |
E2E tests
| Scenario | Assertion |
|---|---|
| … | … |
Integration tests
| Component | Coverage |
|---|---|
| … | … |
Unit tests
| Component | Tests | Coverage |
|---|---|---|
| … | … | … |
History
| Date | Event | Details |
|---|---|---|
| 2026-03-04 | Created | Inspired by Nexie’s code-enforced workflow state machine |
| 2026-03-04 | Updated | Added pitch state and fast-track logic (inspired by Nexie) |
References
- FR-009 (Python Project Scaffold) — code infrastructure prerequisite
- FR-028 (Feature Workflow Automation) — related but broader; state machine is the engine
- FR-027 (Feature Overview Sync Check) — dashboard sync becomes a side effect of transitions
- FR-037 (Daily Briefings) — stale/blocked items feed into briefings
- FR-045 (Complexity Routing) — routing triggers state transitions
- FR-046 (Job Registry) — state transitions can create jobs
- FR-050 (Proactive Monitoring) — monitoring uses state machine data
- Nexie (Sven Hennig) — original inspiration for code-enforced lifecycle