diff --git a/.story_kit/README.md b/.story_kit/README.md index b438fda..8f494e5 100644 --- a/.story_kit/README.md +++ b/.story_kit/README.md @@ -56,7 +56,8 @@ project_root/ │ ├── 2_current/ # Work in progress │ ├── 3_qa/ # QA review │ ├── 4_merge/ # Ready to merge to master - │ └── 5_archived/ # Completed work + │ ├── 5_done/ # Merged and completed (auto-swept to 6_archived after 4 hours) + │ └── 6_archived/ # Long-term archive ├── worktrees/ # Agent worktrees (managed by the server) ├── specs/ # Minimal guardrails (context + stack) │ ├── 00_CONTEXT.md # High-level goals, domain definition, and glossary @@ -77,7 +78,9 @@ All work items (stories, bugs, spikes) live in the same `work/` pipeline. Items Items move through stages by moving the file between directories: -`1_upcoming` → `2_current` → `3_qa` → `4_merge` → `5_archived` +`1_upcoming` → `2_current` → `3_qa` → `4_merge` → `5_done` → `6_archived` + +Items in `5_done` are auto-swept to `6_archived` after 4 hours by the server. ### Filesystem Watcher @@ -119,10 +122,10 @@ When the user asks for a feature, follow this 4-step loop strictly: ### Step 3: Verification (Close) * **Action:** For each Acceptance Criterion in the story, write a failing test (red), mark the criterion as tested, make the test pass (green), and refactor if needed. Keep only one failing test at a time. * **Action:** Run compilation and make sure it succeeds without errors. Consult `specs/tech/STACK.md` and run all required linters listed there (treat warnings as errors). Run tests and make sure they all pass before proceeding. Ask questions here if needed. -* **Action:** Do not accept stories yourself. Ask the user if they accept the story. If they agree, move the story file to `work/5_archived/`. -* **Move to Archived:** After acceptance, move the story from `work/2_current/` (or `work/4_merge/`) to `work/5_archived/`. +* **Action:** Do not accept stories yourself. Ask the user if they accept the story. If they agree, move the story file to `work/5_done/`. +* **Move to Done:** After acceptance, move the story from `work/2_current/` (or `work/4_merge/`) to `work/5_done/`. * **Action:** When the user accepts: - 1. Move the story file to `work/5_archived/` + 1. Move the story file to `work/5_done/` 2. Commit both changes to the feature branch 3. Perform the squash merge: `git merge --squash feature/story-name` 4. Commit to master with a comprehensive commit message @@ -137,7 +140,7 @@ When the user asks for a feature, follow this 4-step loop strictly: * The only files that should exist after story completion: * Updated code in `src/` * Updated guardrails in `specs/` (if needed) - * Archived work item in `work/5_archived/` + * Archived work item in `work/5_done/` (server auto-sweeps to `work/6_archived/` after 4 hours) --- @@ -163,7 +166,7 @@ Not everything needs to be a full story. Simple bugs can skip the story process: 3. **Write a Failing Test:** Before fixing the bug, write a test that reproduces it (red). This proves the bug exists and prevents regression. 4. **Fix the Bug:** Make minimal code changes to make the test pass (green). 5. **User Testing:** Let the user verify the fix in the worktree before merging. Do not proceed until they confirm. -6. **Archive & Merge:** Move the bug file to `work/5_archived/`, squash merge to master, delete the worktree and branch. +6. **Archive & Merge:** Move the bug file to `work/5_done/`, squash merge to master, delete the worktree and branch. 7. **No Guardrail Update Needed:** Unless the bug reveals a missing constraint ### Bug vs Story vs Spike @@ -192,7 +195,7 @@ Not everything needs a story or bug fix. Spikes are time-boxed investigations to * **Recommendation:** Next step (Story, Bug, or No Action) 2. **Execute Research:** Stay within the timebox. No production code changes. 3. **Escalate if Needed:** If implementation is required, open a Story or Bug and follow that workflow. -4. **Archive:** Move the spike file to `work/5_archived/`. +4. **Archive:** Move the spike file to `work/5_done/`. ### Spike Output * Decision and evidence, not production code @@ -218,7 +221,7 @@ If a user hands you this document and says "Apply this process to my project": 1. **Check for MCP Tools:** Look for `.mcp.json` in the project root. If it exists, you have programmatic access to workflow tools and agent spawning capabilities. 2. **Analyze the Request:** Ask for the high-level goal ("What are we building?") and the tech preferences ("Rust or Python?"). 3. **Git Check:** Check if the directory is a git repository (`git status`). If not, run `git init`. -4. **Scaffold:** Run commands to create the `work/` and `specs/` folders with the 5-stage pipeline (`work/1_upcoming/` through `work/5_archived/`). +4. **Scaffold:** Run commands to create the `work/` and `specs/` folders with the 6-stage pipeline (`work/1_upcoming/` through `work/6_archived/`). 5. **Draft Context:** Write `specs/00_CONTEXT.md` based on the user's answer. 6. **Draft Stack:** Write `specs/tech/STACK.md` based on best practices for that language. 7. **Wait:** Ask the user for "Story #1". diff --git a/server/src/http/agents.rs b/server/src/http/agents.rs index 8c8da35..661d453 100644 --- a/server/src/http/agents.rs +++ b/server/src/http/agents.rs @@ -67,12 +67,10 @@ struct WorktreeListEntry { /// response so the agents panel is not cluttered with old completed items on /// frontend startup. pub fn story_is_archived(project_root: &path::Path, story_id: &str) -> bool { - project_root - .join(".story_kit") - .join("work") - .join("5_archived") - .join(format!("{story_id}.md")) - .exists() + let work = project_root.join(".story_kit").join("work"); + let filename = format!("{story_id}.md"); + work.join("5_done").join(&filename).exists() + || work.join("6_archived").join(&filename).exists() } pub struct AgentsApi { @@ -298,29 +296,39 @@ mod tests { use crate::agents::AgentStatus; use tempfile::TempDir; - fn make_archived_dir(tmp: &TempDir) -> path::PathBuf { + fn make_work_dirs(tmp: &TempDir) -> path::PathBuf { let root = tmp.path().to_path_buf(); - let archived = root - .join(".story_kit") - .join("work") - .join("5_archived"); - std::fs::create_dir_all(&archived).unwrap(); + for stage in &["5_done", "6_archived"] { + std::fs::create_dir_all(root.join(".story_kit").join("work").join(stage)).unwrap(); + } root } #[test] fn story_is_archived_false_when_file_absent() { let tmp = TempDir::new().unwrap(); - let root = make_archived_dir(&tmp); + let root = make_work_dirs(&tmp); assert!(!story_is_archived(&root, "79_story_foo")); } #[test] - fn story_is_archived_true_when_file_present() { + fn story_is_archived_true_when_file_in_5_done() { let tmp = TempDir::new().unwrap(); - let root = make_archived_dir(&tmp); + let root = make_work_dirs(&tmp); std::fs::write( - root.join(".story_kit/work/5_archived/79_story_foo.md"), + root.join(".story_kit/work/5_done/79_story_foo.md"), + "---\nname: test\n---\n", + ) + .unwrap(); + assert!(story_is_archived(&root, "79_story_foo")); + } + + #[test] + fn story_is_archived_true_when_file_in_6_archived() { + let tmp = TempDir::new().unwrap(); + let root = make_work_dirs(&tmp); + std::fs::write( + root.join(".story_kit/work/6_archived/79_story_foo.md"), "---\nname: test\n---\n", ) .unwrap(); @@ -330,11 +338,11 @@ mod tests { #[tokio::test] async fn list_agents_excludes_archived_stories() { let tmp = TempDir::new().unwrap(); - let root = make_archived_dir(&tmp); + let root = make_work_dirs(&tmp); - // Place an archived story file + // Place an archived story file in 6_archived std::fs::write( - root.join(".story_kit/work/5_archived/79_story_archived.md"), + root.join(".story_kit/work/6_archived/79_story_archived.md"), "---\nname: archived story\n---\n", ) .unwrap();