|
| 1 | +--- |
| 2 | +id: SPEC-STATUS-AUTO-001 |
| 3 | +version: "1.0.0" |
| 4 | +status: implemented |
| 5 | +created: "2026-04-27" |
| 6 | +updated: "2026-04-27" |
| 7 | +author: GOOS |
| 8 | +priority: P1 |
| 9 | +labels: [spec-status, automation, hooks, cli, sync-workflow] |
| 10 | +--- |
| 11 | + |
| 12 | +# SPEC-STATUS-AUTO-001: SPEC Status Auto-Update System |
| 13 | + |
| 14 | +## Problem Statement |
| 15 | + |
| 16 | +SPEC documents in `.moai/specs/` track lifecycle status (`draft` → `planned` → `implemented` → `completed`), but status updates are purely manual. When a SPEC's implementation is merged to main, the SPEC frontmatter is often left at `draft` or `planned` indefinitely. 15 SPECs were found in this exact state during audit on 2026-04-27. |
| 17 | + |
| 18 | +Root causes: |
| 19 | +1. No code exists to modify SPEC frontmatter status |
| 20 | +2. `/moai sync` Step 2.4 documents status updates but has no implementation |
| 21 | +3. SPEC metadata formats are fragmented (6 variants: YAML, Markdown list, table, Korean fields) |
| 22 | +4. No trigger mechanism to detect when a SPEC should transition |
| 23 | + |
| 24 | +## Requirements (EARS Format) |
| 25 | + |
| 26 | +### REQ-1: SPEC Status Updater Library (Priority: P0) |
| 27 | + |
| 28 | +**WHEN** `spec.UpdateStatus(specID, newStatus)` is called, **THE SYSTEM SHALL** locate `.moai/specs/<specID>/spec.md`, detect its metadata format (YAML frontmatter, Markdown list, or table), update the status field to `newStatus`, and preserve all other content unchanged. |
| 29 | + |
| 30 | +**Acceptance Criteria:** |
| 31 | +- AC-1.1: Supports all 6 format variants: |
| 32 | + - YAML `status:` (Format A, B) |
| 33 | + - Markdown `- **Status**: X` (Format D) |
| 34 | + - Markdown table `| 상태 | X |` or `| Status | X |` (Format E) |
| 35 | + - Adds YAML frontmatter block if none exists (Format F) |
| 36 | +- AC-1.2: Validated status values: `draft`, `planned`, `in-progress`, `implemented`, `completed`, `superseded` |
| 37 | +- AC-1.3: Returns error if spec.md file not found or status value invalid |
| 38 | +- AC-1.4: Does not modify any content outside the status field |
| 39 | +- AC-1.5: Unit test coverage >= 90% for all format parsers |
| 40 | + |
| 41 | +### REQ-2: CLI Command (Priority: P0) |
| 42 | + |
| 43 | +**WHEN** `moai spec status <SPEC-ID> <status>` is invoked, **THE SYSTEM SHALL** call the updater library and report success or failure. |
| 44 | + |
| 45 | +**Acceptance Criteria:** |
| 46 | +- AC-2.1: Command registered under `moai spec` parent command |
| 47 | +- AC-2.2: Validates SPEC-ID exists in `.moai/specs/` before attempting update |
| 48 | +- AC-2.3: Prints human-readable confirmation: `SPEC-XXX status updated: draft → completed` |
| 49 | +- AC-2.4: Supports `--dry-run` flag to preview change without writing |
| 50 | +- AC-2.5: Supports `--list` flag to show all SPECs and their current status |
| 51 | + |
| 52 | +### REQ-3: Commit-Based Auto-Detection (L1) (Priority: P1) |
| 53 | + |
| 54 | +**WHEN** a git commit message contains a pattern matching `SPEC-[A-Z0-9]+-[0-9]+`, **THE SYSTEM SHALL** extract the SPEC-ID(s) and automatically update their status to `implemented`. |
| 55 | + |
| 56 | +**Acceptance Criteria:** |
| 57 | +- AC-3.1: Implemented as `internal/hook/spec_status.go` hook handler |
| 58 | +- AC-3.2: Triggered by `PostToolUse` event when tool is `Bash` and command matches `git commit` |
| 59 | +- AC-3.3: Parses commit message from `git log -1 --format=%s` |
| 60 | +- AC-3.4: Extracts all unique SPEC-XXX patterns from the message |
| 61 | +- AC-3.5: For each SPEC-ID found, calls `spec.UpdateStatus(specID, "implemented")` |
| 62 | +- AC-3.6: Logs updated SPECs to stderr (non-blocking — hook exit code always 0) |
| 63 | +- AC-3.7: Gracefully skips if `.moai/specs/` directory does not exist |
| 64 | + |
| 65 | +### REQ-4: Sync Workflow Integration (L2) (Priority: P1) |
| 66 | + |
| 67 | +**WHEN** `/moai sync` completes documentation synchronization, **THE SYSTEM SHALL** update the synced SPEC status to `completed`. |
| 68 | + |
| 69 | +**Acceptance Criteria:** |
| 70 | +- AC-4.1: sync.md Phase 2.4 invokes `moai spec status <SPEC-ID> completed` |
| 71 | +- AC-4.2: Status update occurs after documentation sync succeeds |
| 72 | +- AC-4.3: Failure to update status does not block the sync workflow (warning only) |
| 73 | +- AC-4.4: Logs the status transition in sync output |
| 74 | + |
| 75 | +### REQ-5: Batch Status Command (Priority: P2) |
| 76 | + |
| 77 | +**WHEN** `moai spec status --sync-git` is invoked, **THE SYSTEM SHALL** cross-reference all SPECs in `.moai/specs/` against git log on main and update statuses where implementation commits exist. |
| 78 | + |
| 79 | +**Acceptance Criteria:** |
| 80 | +- AC-5.1: Scans `git log main --oneline --no-merges` for SPEC-XXX patterns |
| 81 | +- AC-5.2: For each SPEC found in commits but not marked `completed`/`implemented`, updates status |
| 82 | +- AC-5.3: Reports a summary: `Updated N SPECs, skipped M (already completed), K not found` |
| 83 | +- AC-5.4: Requires `--confirm` flag (or `--yes` for non-interactive) before writing changes |
| 84 | + |
| 85 | +## Technical Approach |
| 86 | + |
| 87 | +### Package Structure |
| 88 | + |
| 89 | +``` |
| 90 | +internal/spec/ |
| 91 | + status.go # UpdateStatus(), ParseStatus(), Format detection |
| 92 | + status_test.go # Unit tests for all 6 format parsers |
| 93 | +
|
| 94 | +internal/hook/ |
| 95 | + spec_status.go # L1 hook handler (PostToolUse) |
| 96 | +
|
| 97 | +internal/cli/ |
| 98 | + spec.go # `moai spec` parent command |
| 99 | + spec_status.go # `moai spec status` subcommand |
| 100 | +``` |
| 101 | + |
| 102 | +### Format Detection Algorithm |
| 103 | + |
| 104 | +``` |
| 105 | +1. Read first 30 lines of spec.md |
| 106 | +2. If contains "---" delimiter → YAML frontmatter |
| 107 | + a. Parse YAML, look for "status:" key |
| 108 | + b. If no "status:" key → add it |
| 109 | +3. Else if contains "| 상태 |" or "| Status |" → Table format |
| 110 | + a. Replace status value in table row |
| 111 | +4. Else if contains "- **Status**:" → Markdown list format |
| 112 | + a. Replace status value in list item |
| 113 | +5. Else → Prepend YAML frontmatter block with status field |
| 114 | +``` |
| 115 | + |
| 116 | +### Hook Registration |
| 117 | + |
| 118 | +In `settings.json` hooks: |
| 119 | +```json |
| 120 | +{ |
| 121 | + "PostToolUse": [{ |
| 122 | + "matcher": "Bash", |
| 123 | + "hooks": [{ |
| 124 | + "command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/moai/handle-spec-status.sh\"", |
| 125 | + "timeout": 5 |
| 126 | + }] |
| 127 | + }] |
| 128 | +} |
| 129 | +``` |
| 130 | + |
| 131 | +Hook wrapper script reads stdin JSON, checks if bash command was `git commit`, then calls `moai spec status --auto-commit`. |
| 132 | + |
| 133 | +## Scope |
| 134 | + |
| 135 | +### In Scope |
| 136 | +- SPEC status updater library with multi-format support |
| 137 | +- CLI command `moai spec status` |
| 138 | +- L1 PostToolUse hook for commit-based detection |
| 139 | +- L2 sync workflow integration |
| 140 | +- Batch sync-git command |
| 141 | + |
| 142 | +### Out of Scope |
| 143 | +- L3 PR merge detection via GitHub Actions (future SPEC) |
| 144 | +- SPEC lifecycle state machine enforcement (validation only) |
| 145 | +- Web UI for SPEC status management |
| 146 | +- Automatic archiving of completed SPECs |
| 147 | + |
| 148 | +## Dependencies |
| 149 | + |
| 150 | +- Existing `internal/cli/` command structure (cobra) |
| 151 | +- Existing `internal/hook/` hook handler pattern |
| 152 | +- `.moai/specs/` directory structure (convention) |
| 153 | + |
| 154 | +## Risks |
| 155 | + |
| 156 | +| Risk | Mitigation | |
| 157 | +|------|------------| |
| 158 | +| YAML frontmatter parsing fragile | Use simple line-based regex, not full YAML parser | |
| 159 | +| Hook timeout on large commit messages | 5s timeout, process only first line | |
| 160 | +| Race condition on concurrent edits | SPEC files are single-writer (one session at a time) | |
| 161 | +| False positive SPEC pattern in commit body | Only match conventional commit title (first line) | |
0 commit comments