diff --git a/server/src/io/fs.rs b/server/src/io/fs.rs index e1961f9..5580820 100644 --- a/server/src/io/fs.rs +++ b/server/src/io/fs.rs @@ -230,7 +230,8 @@ This folder contains the "Living Specification" for the project. It serves as th 3. If a Story changes behavior, update the spec *first*, get approval, then write code. "#; -const STORY_KIT_CONTEXT: &str = r#"# Project Context +const STORY_KIT_CONTEXT: &str = r#" +# Project Context ## High-Level Goal To build a standalone **Agentic AI Code Assistant** application as a single Rust binary that serves a Vite/React web UI and exposes a WebSocket API. The assistant will facilitate a "Story-Driven Spec Workflow" (SDSW) for software development. Unlike a passive chat interface, this assistant acts as an **Agent**, capable of using tools to read the filesystem, execute shell commands, manage git repositories, and modify code directly to implement features. @@ -265,7 +266,8 @@ To build a standalone **Agentic AI Code Assistant** application as a single Rust * **Tool Call:** A structured request from the LLM to execute a specific native function. "#; -const STORY_KIT_STACK: &str = r#"# Tech Stack & Constraints +const STORY_KIT_STACK: &str = r#" +# Tech Stack & Constraints ## Overview This project is a standalone Rust **web server binary** that serves a Vite/React frontend and exposes a **WebSocket API**. The built frontend assets are packaged with the binary (in a `frontend` directory) and served as static files. It functions as an **Agentic Code Assistant** capable of safely executing tools on the host system. diff --git a/server/src/io/onboarding.rs b/server/src/io/onboarding.rs index 8e4dc1e..f2e0622 100644 --- a/server/src/io/onboarding.rs +++ b/server/src/io/onboarding.rs @@ -1,12 +1,10 @@ use std::path::Path; -/// Unique marker found in the scaffold template for `00_CONTEXT.md`. -/// If the project's context file contains this phrase, it is still the -/// default scaffold content and needs to be replaced. -const TEMPLATE_MARKER_CONTEXT: &str = "Agentic AI Code Assistant"; - -/// Unique marker found in the scaffold template for `STACK.md`. -const TEMPLATE_MARKER_STACK: &str = "Agentic Code Assistant"; +/// Sentinel comment injected as the first line of scaffold templates. +/// Only untouched templates contain this marker — real project content +/// will never include it, so it avoids false positives when the project +/// itself is an "Agentic AI Code Assistant". +const TEMPLATE_SENTINEL: &str = ""; /// Marker found in the default `script/test` scaffold output. const TEMPLATE_MARKER_SCRIPT: &str = "No tests configured"; @@ -40,11 +38,11 @@ pub fn check_onboarding_status(project_root: &Path) -> OnboardingStatus { OnboardingStatus { needs_context: is_template_or_missing( &story_kit.join("specs").join("00_CONTEXT.md"), - TEMPLATE_MARKER_CONTEXT, + TEMPLATE_SENTINEL, ), needs_stack: is_template_or_missing( &story_kit.join("specs").join("tech").join("STACK.md"), - TEMPLATE_MARKER_STACK, + TEMPLATE_SENTINEL, ), needs_test_script: is_template_or_missing( &project_root.join("script").join("test"), @@ -102,19 +100,19 @@ mod tests { } #[test] - fn needs_onboarding_true_when_specs_contain_scaffold_markers() { + fn needs_onboarding_true_when_specs_contain_scaffold_sentinel() { let dir = TempDir::new().unwrap(); let root = setup_project(&dir); - // Write scaffold template content + // Write content that includes the scaffold sentinel fs::write( root.join(".story_kit/specs/00_CONTEXT.md"), - "# Project Context\nTo build a standalone Agentic AI Code Assistant...", + "\n# Project Context\nPlaceholder...", ) .unwrap(); fs::write( root.join(".story_kit/specs/tech/STACK.md"), - "# Tech Stack\nThis is an Agentic Code Assistant binary...", + "\n# Tech Stack\nPlaceholder...", ) .unwrap(); @@ -124,6 +122,30 @@ mod tests { assert!(status.needs_onboarding()); } + #[test] + fn needs_onboarding_false_when_content_mentions_agentic_but_no_sentinel() { + let dir = TempDir::new().unwrap(); + let root = setup_project(&dir); + + // Real project content that happens to mention "Agentic AI Code Assistant" + // but does NOT contain the scaffold sentinel — should NOT trigger onboarding. + fs::write( + root.join(".story_kit/specs/00_CONTEXT.md"), + "# Project Context\nTo build a standalone Agentic AI Code Assistant application.", + ) + .unwrap(); + fs::write( + root.join(".story_kit/specs/tech/STACK.md"), + "# Tech Stack\nThis is an Agentic Code Assistant binary.", + ) + .unwrap(); + + let status = check_onboarding_status(&root); + assert!(!status.needs_context); + assert!(!status.needs_stack); + assert!(!status.needs_onboarding()); + } + #[test] fn needs_onboarding_false_when_specs_have_custom_content() { let dir = TempDir::new().unwrap(); @@ -239,13 +261,13 @@ mod tests { let dir = TempDir::new().unwrap(); let root = setup_project(&dir); - // Context is still template + // Context still has sentinel fs::write( root.join(".story_kit/specs/00_CONTEXT.md"), - "Agentic AI Code Assistant placeholder", + "\n# Project Context\nPlaceholder...", ) .unwrap(); - // Stack is customised + // Stack is customised (no sentinel) fs::write( root.join(".story_kit/specs/tech/STACK.md"), "# My Stack\nRuby on Rails + PostgreSQL",