From 64ccf0b85d80392b546f9a90fa62be186c59751e Mon Sep 17 00:00:00 2001 From: jordan Date: Mon, 2 Feb 2026 20:12:40 -0700 Subject: [PATCH] feat: add feature development E2E test and SDLC handler fixes - Add feature-dev-test.sh: full 10-step E2E test for SDLC + Claude Code workflow - Update feature-development.md cookbook with complete workflow documentation - Fix SDLC orchestrator and project management handler improvements - Update scaffold-test.sh with minor fixes Co-Authored-By: Claude Opus 4.5 --- cookbooks/VISION.md | 4 +- cookbooks/feature-development.md | 320 ++++++++++++++- cookbooks/scripts/feature-dev-test.sh | 493 ++++++++++++++++++++++++ cookbooks/scripts/scaffold-test.sh | 9 +- internal/handlers/project_management.go | 10 +- internal/handlers/sdlc_orchestrator.go | 14 +- 6 files changed, 813 insertions(+), 37 deletions(-) create mode 100755 cookbooks/scripts/feature-dev-test.sh diff --git a/cookbooks/VISION.md b/cookbooks/VISION.md index 1628244..1a6dfb6 100644 --- a/cookbooks/VISION.md +++ b/cookbooks/VISION.md @@ -269,8 +269,10 @@ POST /project/landing/build | Script | Description | |--------|-------------| | `scripts/landing-test.sh` | Landing page E2E test | -| `scripts/feature-test.sh` | Feature development E2E test | +| `scripts/feature-dev-test.sh` | Feature development E2E test (Claude + SDLC flow) | +| `scripts/scaffold-test.sh` | Scaffold validation E2E test (patterns/components) | | `scripts/composable-test.sh` | Composable monorepo E2E test | +| `scripts/sdlc-test.sh` | SDLC API endpoint validation | | `scripts/template-validation.sh` | Template validation checks | --- diff --git a/cookbooks/feature-development.md b/cookbooks/feature-development.md index 90971e6..b325ab6 100644 --- a/cookbooks/feature-development.md +++ b/cookbooks/feature-development.md @@ -4,15 +4,208 @@ ## Overview -This cookbook documents the end-to-end workflow for developing features in a composable monorepo project. It assumes you have: +This cookbook documents the end-to-end workflow for developing features in a composable monorepo project. There are two approaches: -- An rdev project with monorepo skeleton (created via `POST /project`) -- At least one API service component (`services/api`) -- A Next.js or React app component (`apps/dashboard`) +1. **SDLC-Driven** (Recommended) - Structured workflow using the SDLC system and Claude Code +2. **Manual** - Traditional development with manual planning and implementation + +The SDLC-driven approach uses skeleton commands that Claude executes inside the project pod, producing structured artifacts (spec, design, tasks) that can be approved and tracked. ``` -Feature Development Workflow -──────────────────────────── +SDLC Feature Development Workflow +────────────────────────────────── +1. Create Feature → POST /sdlc/features +2. Generate Spec → Claude runs /spec-feature +3. Approve Spec → POST /sdlc/features/{slug}/artifacts/spec/approve +4. Generate Design → Claude runs /design-feature +5. Approve Design → POST /sdlc/features/{slug}/artifacts/design/approve +6. Break Down Tasks → Claude runs /breakdown-feature +7. Approve Tasks → POST /sdlc/features/{slug}/artifacts/tasks/approve +8. Implement Tasks → Claude runs /implement-task for each +9. Review & Merge → Claude runs /review-feature, /merge-feature +``` + +--- + +## Quick Start: API-Driven Feature Development + +This section shows the minimal API calls to develop a feature using Claude Code and the SDLC system. + +### Prerequisites + +```bash +export RDEV_API_URL="https://rdev.masq-ops.orchard9.ai" +export RDEV_API_KEY="" +``` + +### 1. Create Feature + +```bash +curl -X POST "$RDEV_API_URL/projects/myapp/sdlc/features" \ + -H "X-API-Key: $RDEV_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"slug": "add-hello", "title": "Add /hello endpoint"}' +``` + +### 2. Generate Spec with Claude + +```bash +curl -X POST "$RDEV_API_URL/projects/myapp/builds" \ + -H "X-API-Key: $RDEV_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "prompt": "/spec-feature add-hello", + "auto_commit": true, + "auto_push": true, + "git_clone_url": "https://git.threesix.ai/jordan/myapp.git" + }' +# Returns: {"data": {"task_id": "abc123", ...}} +``` + +### 3. Check Build Status + +```bash +curl "$RDEV_API_URL/builds/abc123" \ + -H "X-API-Key: $RDEV_API_KEY" +# Wait for status: "completed" +``` + +### 4. Check Classifier for Next Action + +```bash +curl "$RDEV_API_URL/projects/myapp/sdlc/next?feature=add-hello" \ + -H "X-API-Key: $RDEV_API_KEY" +# Returns: {"action": "AWAIT_APPROVAL", "artifact": "spec", ...} +``` + +### 5. Approve Spec + +```bash +curl -X POST "$RDEV_API_URL/projects/myapp/sdlc/features/add-hello/artifacts/spec/approve" \ + -H "X-API-Key: $RDEV_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{}' +``` + +### 6. Continue Through Phases + +Repeat the pattern: **Generate artifact** → **Check classifier** → **Approve** → **Next phase** + +```bash +# Generate design +curl -X POST "$RDEV_API_URL/projects/myapp/builds" \ + -H "X-API-Key: $RDEV_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"prompt": "/design-feature add-hello", "auto_commit": true, "auto_push": true, "git_clone_url": "https://git.threesix.ai/jordan/myapp.git"}' + +# Approve design +curl -X POST "$RDEV_API_URL/projects/myapp/sdlc/features/add-hello/artifacts/design/approve" \ + -H "X-API-Key: $RDEV_API_KEY" -H "Content-Type: application/json" -d '{}' + +# Generate tasks +curl -X POST "$RDEV_API_URL/projects/myapp/builds" \ + -H "X-API-Key: $RDEV_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"prompt": "/breakdown-feature add-hello", "auto_commit": true, "auto_push": true, "git_clone_url": "https://git.threesix.ai/jordan/myapp.git"}' + +# Approve tasks +curl -X POST "$RDEV_API_URL/projects/myapp/sdlc/features/add-hello/artifacts/tasks/approve" \ + -H "X-API-Key: $RDEV_API_KEY" -H "Content-Type: application/json" -d '{}' + +# Implement first task +curl -X POST "$RDEV_API_URL/projects/myapp/builds" \ + -H "X-API-Key: $RDEV_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"prompt": "/implement-task add-hello T1", "auto_commit": true, "auto_push": true, "git_clone_url": "https://git.threesix.ai/jordan/myapp.git"}' +``` + +### E2E Test Script + +Run the full workflow automatically: + +```bash +./cookbooks/scripts/feature-dev-test.sh run my-test-project +./cookbooks/scripts/feature-dev-test.sh status my-test-project +./cookbooks/scripts/feature-dev-test.sh teardown my-test-project +``` + +--- + +## Skeleton Commands Reference + +These commands are installed in every project's `.claude/commands/` directory and can be invoked via the `/builds` endpoint. + +| Command | Description | Artifacts | +|---------|-------------|-----------| +| `/spec-feature ` | Create specification document | `.sdlc/features//spec.md` | +| `/design-feature ` | Create technical design | `.sdlc/features//design.md` | +| `/breakdown-feature ` | Break feature into tasks | `.sdlc/features//tasks.md` | +| `/implement-task ` | Implement a specific task | Code changes | +| `/create-qa-plan ` | Create QA test plan | `.sdlc/features//qa-plan.md` | +| `/run-qa ` | Execute QA tests | Test results | +| `/review-feature ` | Code review all changes | Review report | +| `/audit-feature ` | Audit feature for compliance | Audit report | +| `/fix-review-issues ` | Fix issues from review | Code changes | +| `/remediate-audit ` | Fix audit findings | Code changes | +| `/fix-qa-failures ` | Fix failing QA tests | Code changes | +| `/merge-feature ` | Merge feature to main | Merged branch | +| `/archive-feature ` | Archive completed feature | Archived state | +| `/next ` | Get classifier recommendation | Next action | +| `/deliver ` | Run full delivery pipeline | All artifacts | + +--- + +## SDLC API Reference + +### Feature Management + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/projects/{id}/sdlc/state` | GET | Get SDLC state for project | +| `/projects/{id}/sdlc/features` | GET | List all features | +| `/projects/{id}/sdlc/features` | POST | Create new feature | +| `/projects/{id}/sdlc/features/{slug}` | GET | Get feature details | +| `/projects/{id}/sdlc/features/{slug}` | DELETE | Delete feature | + +### Artifacts + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/projects/{id}/sdlc/features/{slug}/artifacts` | GET | List artifact status | +| `/projects/{id}/sdlc/features/{slug}/artifacts/{type}/approve` | POST | Approve artifact | + +### Tasks + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/projects/{id}/sdlc/features/{slug}/tasks` | GET | List tasks | +| `/projects/{id}/sdlc/features/{slug}/tasks` | POST | Add task | + +### Workflow Control + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/projects/{id}/sdlc/features/{slug}/block` | POST | Block feature | +| `/projects/{id}/sdlc/features/{slug}/unblock` | POST | Unblock feature | +| `/projects/{id}/sdlc/next` | GET | Get classifier recommendation | + +### Queries + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/projects/{id}/sdlc/query/blocked` | GET | List blocked features | +| `/projects/{id}/sdlc/query/ready` | GET | List ready features | +| `/projects/{id}/sdlc/query/needs-approval` | GET | List features awaiting approval | + +--- + +## Manual Development Workflow + +For cases where you want direct control without the SDLC system, you can follow the traditional development workflow below. + +``` +Manual Feature Development Workflow +──────────────────────────────────── 1. Planning → Define requirements, break into tasks 2. API Dev → Bind + Wrap patterns, OpenAPI annotations 3. Frontend Dev → Auth hooks, design system, API client @@ -21,17 +214,14 @@ Feature Development Workflow 6. Deployment → Git push, Woodpecker CI, verify ``` ---- +This approach assumes you have: -## Prerequisites - -### API Access -```bash -export RDEV_API_URL="https://rdev.masq-ops.orchard9.ai" -export RDEV_API_KEY="" -``` +- An rdev project with monorepo skeleton (created via `POST /project`) +- At least one API service component (`services/api`) +- A Next.js or React app component (`apps/dashboard`) ### Project Structure + Your composable monorepo should have: ``` @@ -728,24 +918,41 @@ import { DashboardShell, Sidebar, Header } from '@my-project/layout'; --- -## E2E Test Script +## E2E Test Scripts -Validate the complete feature development flow: +### Feature Development (Claude + SDLC) + +Test the complete feature development flow with Claude Code: ```bash -# Create test project with components -./cookbooks/scripts/feature-test.sh run test-feature +# Run full SDLC feature development flow +./cookbooks/scripts/feature-dev-test.sh run test-feature # Check status -./cookbooks/scripts/feature-test.sh status test-feature +./cookbooks/scripts/feature-dev-test.sh status test-feature # Cleanup -./cookbooks/scripts/feature-test.sh teardown test-feature +./cookbooks/scripts/feature-dev-test.sh teardown test-feature +``` + +### Scaffold Validation + +Test that project scaffolding creates correct patterns: + +```bash +# Create project and verify patterns +./cookbooks/scripts/scaffold-test.sh run test-scaffold + +# Verify specific patterns +./cookbooks/scripts/scaffold-test.sh verify-patterns test-scaffold + +# Cleanup +./cookbooks/scripts/scaffold-test.sh teardown test-scaffold ``` --- -## Troubleshooting +## Manual Troubleshooting ### Handler returns 500 instead of proper error Check that you're using `httperror.*` functions, not raw `errors.New()`. @@ -824,9 +1031,80 @@ All use CSS custom properties: `var(--background)`, `var(--accent)`, `var(--bord --- +## SDLC Troubleshooting + +### Build returns "completed" but no artifacts created + +The skeleton command may have failed silently. Check the build output: + +```bash +curl "$RDEV_API_URL/builds/" -H "X-API-Key: $RDEV_API_KEY" | jq '.data.result' +``` + +Common causes: +- Feature doesn't exist (create it first via `/sdlc/features`) +- Previous artifacts missing (spec must exist before design) +- SDLC CLI not installed in pod + +### Classifier returns AWAIT_ARTIFACT but artifact exists + +The artifact may exist in the file system but not registered with SDLC: + +```bash +# Register artifact manually +curl -X POST "$RDEV_API_URL/projects/$PROJECT/builds" \ + -H "X-API-Key: $RDEV_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"prompt": "sdlc artifact create ", "auto_commit": true, "auto_push": true, "git_clone_url": ""}' +``` + +### Task implementation doesn't commit changes + +Check that `auto_commit` and `auto_push` are set to `true`, and `git_clone_url` is provided: + +```bash +curl -X POST "$RDEV_API_URL/projects/$PROJECT/builds" \ + -H "X-API-Key: $RDEV_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{ + "prompt": "/implement-task ", + "auto_commit": true, + "auto_push": true, + "git_clone_url": "https://git.threesix.ai/jordan/.git" + }' +``` + +### Build times out + +Builds have a default timeout of 10 minutes. Complex implementations may need longer. Check the build status periodically: + +```bash +# Poll status +while true; do + status=$(curl -s "$RDEV_API_URL/builds/" -H "X-API-Key: $RDEV_API_KEY" | jq -r '.data.status // .status') + echo "Status: $status" + [[ "$status" == "completed" || "$status" == "failed" ]] && break + sleep 10 +done +``` + +### Feature stuck in wrong phase + +Check the classifier recommendation to understand what's blocking: + +```bash +curl "$RDEV_API_URL/projects/$PROJECT/sdlc/next?feature=" \ + -H "X-API-Key: $RDEV_API_KEY" | jq '.' +``` + +If needed, you can manually advance the phase by approving the pending artifact. + +--- + ## Related - [Composable App Cookbook](./composable-app.md) - Creating projects with components - [Landing Page Cookbook](./landing-page.md) - Simple single-component sites - [Composable Monorepo Guide](../.claude/guides/services/composable-monorepo.md) - [API Framework Guide](../.claude/guides/packages/api-framework.md) +- [SDLC Guide](../.claude/guides/services/sdlc.md) - Full SDLC orchestration documentation diff --git a/cookbooks/scripts/feature-dev-test.sh b/cookbooks/scripts/feature-dev-test.sh new file mode 100755 index 0000000..443614b --- /dev/null +++ b/cookbooks/scripts/feature-dev-test.sh @@ -0,0 +1,493 @@ +#!/bin/bash +set -euo pipefail + +# Feature Development E2E Test Script +# Tests the complete Claude Code + SDLC feature development workflow: +# 1. Create composable project with skeleton +# 2. Add service component +# 3. Create SDLC feature via API +# 4. Invoke Claude to generate spec (via /builds with skeleton command) +# 5. Approve spec via API +# 6. Invoke Claude to generate design +# 7. Approve design via API +# 8. Invoke Claude to break down tasks +# 9. Approve tasks via API +# 10. Invoke Claude to implement first task +# 11. Verify code was generated +# 12. Teardown +# +# Usage: ./cookbooks/scripts/feature-dev-test.sh +# Commands: run, status, teardown +# +# This is the definitive E2E test for the SDLC + Claude Code integration. +# It exercises the full feature development workflow from project creation +# to implemented code. + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/common.sh" + +# Parse --auto-teardown flag from args +ARGS=("$@") +for i in "${!ARGS[@]}"; do + if [[ "${ARGS[$i]}" == "--auto-teardown" ]]; then + AUTO_TEARDOWN="true" + unset 'ARGS[$i]' + fi +done +ARGS=("${ARGS[@]}") # Re-index array + +COMMAND="${ARGS[0]:-}" +PROJECT_NAME="${ARGS[1]:-}" +FEATURE_SLUG="add-hello-endpoint" + +# Register cleanup trap for auto-teardown +register_cleanup_trap + +if [[ -z "$COMMAND" || -z "$PROJECT_NAME" ]]; then + echo "Feature Development E2E Test Script" + echo "" + echo "Tests the complete Claude Code + SDLC feature development workflow." + echo "" + echo "Usage: $0 [--auto-teardown]" + echo "" + echo "Commands:" + echo " run - Execute full feature development flow" + echo " status - Check project and SDLC status" + echo " teardown - Delete the project" + echo "" + echo "Examples:" + echo " $0 run feature-dev-test" + echo " $0 run test-feat --auto-teardown" + echo " $0 status feature-dev-test" + echo " $0 teardown feature-dev-test" + echo "" + echo "Environment Variables:" + echo " RDEV_API_URL - rdev API base URL" + echo " RDEV_API_KEY - rdev API key" + echo " GITEA_TOKEN - Gitea API token (optional, for repo verification)" + exit 1 +fi + +# Check response for success/error +check_response() { + local response="$1" + local step="$2" + + local error + error=$(echo "$response" | jq -r '.error // ""') + if [[ -n "$error" && "$error" != "" && "$error" != "null" ]]; then + print_error "$step failed: $error" + echo "$response" | jq '.' + return 1 + fi + return 0 +} + +# Wait for a build to complete +# Arguments: task_id step_name [max_attempts] +wait_for_build_step() { + local task_id="$1" + local step_name="$2" + local max_attempts="${3:-120}" # 10 minutes default (5s * 120) + local attempt=0 + + echo " Waiting for build to complete (task: $task_id)..." + + while [[ $attempt -lt $max_attempts ]]; do + local result + result=$(api_call GET "/builds/$task_id") + local status + status=$(echo "$result" | jq -r '.data.status // .status // "unknown"') + + case "$status" in + completed) + local success + success=$(echo "$result" | jq -r '.data.result.success // .result.success // false') + if [[ "$success" == "true" ]]; then + print_success "$step_name completed successfully" + return 0 + else + print_error "$step_name completed but failed:" + echo "$result" | jq '.data.result // .result' + return 1 + fi + ;; + failed) + print_error "$step_name failed:" + echo "$result" | jq '.' + return 1 + ;; + running) + echo " Build running... (attempt $((attempt + 1))/$max_attempts)" + ;; + pending) + echo " Build pending... (attempt $((attempt + 1))/$max_attempts)" + ;; + *) + echo " Unknown status: $status (attempt $((attempt + 1))/$max_attempts)" + ;; + esac + + sleep 5 + ((attempt++)) + done + + print_error "Timeout waiting for $step_name to complete" + return 2 +} + +# Submit a build and wait for completion +# Arguments: project_id prompt step_name +submit_build_and_wait() { + local project_id="$1" + local prompt="$2" + local step_name="$3" + + local git_owner + git_owner=$(get_git_owner) + local git_clone_url="https://git.threesix.ai/${git_owner}/${project_id}.git" + + echo " Submitting build: $prompt" + + local payload + payload=$(jq -n \ + --arg prompt "$prompt" \ + --arg git_clone_url "$git_clone_url" \ + '{ + prompt: $prompt, + auto_commit: true, + auto_push: true, + git_clone_url: $git_clone_url + }') + + local result + result=$(api_call POST "/projects/$project_id/builds" "$payload") + + if ! check_response "$result" "Submit build"; then + return 1 + fi + + local task_id + task_id=$(echo "$result" | jq -r '.data.task_id // .task_id // ""') + + if [[ -z "$task_id" ]]; then + print_error "No task_id in response" + echo "$result" | jq '.' + return 1 + fi + + wait_for_build_step "$task_id" "$step_name" +} + +# Approve an SDLC artifact +# Arguments: project_id feature_slug artifact_type +approve_artifact() { + local project_id="$1" + local feature_slug="$2" + local artifact_type="$3" + + echo " Approving $artifact_type artifact..." + + local result + result=$(api_call POST "/projects/$project_id/sdlc/features/$feature_slug/artifacts/$artifact_type/approve" "{}") + + if check_response "$result" "Approve $artifact_type"; then + print_success "$artifact_type artifact approved" + return 0 + fi + return 1 +} + +# Add a component to the project +add_component() { + local comp_type="$1" + local comp_name="$2" + + echo "Adding $comp_type component: $comp_name" + + local payload + payload=$(jq -n \ + --arg type "$comp_type" \ + --arg name "$comp_name" \ + '{type: $type, name: $name}') + + local result + result=$(api_call POST "/projects/$PROJECT_NAME/components" "$payload") + + local path + path=$(echo "$result" | jq -r '.data.path // .path // ""') + + if [[ -z "$path" ]]; then + print_error "Failed to add component" + echo "$result" | jq '.' + return 1 + fi + + print_success "Added $comp_type/$comp_name at $path" + return 0 +} + +# Verify the feature artifacts exist in Gitea +verify_artifacts() { + local project_id="$1" + local feature_slug="$2" + + local git_owner + git_owner=$(get_git_owner) + + print_header "Verifying Feature Artifacts" + + local artifacts=("spec.md" "design.md" "tasks.md") + local all_found=true + + for artifact in "${artifacts[@]}"; do + local path=".sdlc/features/$feature_slug/$artifact" + echo " Checking $path..." + + local check + check=$(curl -s "https://git.threesix.ai/api/v1/repos/$git_owner/$project_id/contents/$path" \ + -H "Authorization: token ${GITEA_TOKEN:-}" 2>/dev/null | jq -r '.name // "not found"') + + if [[ "$check" == "$artifact" ]]; then + print_success " $artifact exists" + else + print_warning " $artifact not found" + all_found=false + fi + done + + if [[ "$all_found" == "true" ]]; then + print_success "All artifacts verified" + return 0 + else + print_warning "Some artifacts missing (may still be propagating)" + return 0 # Don't fail on verification, may be timing + fi +} + +# Main run flow +run_flow() { + print_header "Feature Development E2E Test" + echo "Project: $PROJECT_NAME" + echo "Feature: $FEATURE_SLUG" + echo "" + + # ========================================================================= + # Step 1: Create project skeleton + # ========================================================================= + print_header "Step 1: Creating project skeleton" + + local create_payload + create_payload=$(jq -n \ + --arg name "$PROJECT_NAME" \ + --arg desc "Feature development E2E test" \ + '{name: $name, description: $desc}') + + local create_result + create_result=$(api_call POST "/project" "$create_payload") + echo "$create_result" | jq '.' + + local domain + domain=$(echo "$create_result" | jq -r '.data.domain // .domain // ""') + + if [[ -z "$domain" ]]; then + print_error "Failed to create project" + exit 1 + fi + + # Track project for auto-cleanup + CLEANUP_PROJECT="$PROJECT_NAME" + + print_success "Project created with domain: $domain" + + # ========================================================================= + # Step 2: Add service component + # ========================================================================= + print_header "Step 2: Adding service component" + + if ! add_component "service" "api"; then + exit 1 + fi + + # Wait for git sync + echo " Waiting for git sync..." + sleep 10 + + # ========================================================================= + # Step 3: Create SDLC feature via build (using worker pod) + # ========================================================================= + # Note: Composable projects don't have dedicated pods, so we use builds + # to execute SDLC commands on worker pods that clone the repo. + print_header "Step 3: Creating SDLC feature via build" + + # Initialize SDLC and create feature in one build + if ! submit_build_and_wait "$PROJECT_NAME" \ + "Initialize SDLC for this project and create a feature with slug '$FEATURE_SLUG' and title 'Add /hello endpoint to API service'. Use the sdlc CLI: 'sdlc init' then 'sdlc feature create $FEATURE_SLUG \"Add /hello endpoint to API service\"'" \ + "SDLC initialization"; then + print_error "SDLC initialization failed" + exit 1 + fi + + # ========================================================================= + # Step 4: Generate spec via Claude + # ========================================================================= + print_header "Step 4: Claude generates spec" + + if ! submit_build_and_wait "$PROJECT_NAME" "/spec-feature $FEATURE_SLUG" "Spec generation"; then + print_error "Spec generation failed" + exit 1 + fi + + # ========================================================================= + # Step 5: Approve spec via build + # ========================================================================= + print_header "Step 5: Approve spec" + + if ! submit_build_and_wait "$PROJECT_NAME" \ + "Approve the spec artifact for feature $FEATURE_SLUG using: sdlc artifact approve $FEATURE_SLUG spec" \ + "Spec approval"; then + print_warning "Spec approval may have failed, continuing..." + fi + + # ========================================================================= + # Step 6: Generate design via Claude + # ========================================================================= + print_header "Step 6: Claude generates design" + + if ! submit_build_and_wait "$PROJECT_NAME" "/design-feature $FEATURE_SLUG" "Design generation"; then + print_error "Design generation failed" + exit 1 + fi + + # ========================================================================= + # Step 7: Approve design via build + # ========================================================================= + print_header "Step 7: Approve design" + + if ! submit_build_and_wait "$PROJECT_NAME" \ + "Approve the design artifact for feature $FEATURE_SLUG using: sdlc artifact approve $FEATURE_SLUG design" \ + "Design approval"; then + print_warning "Design approval may have failed, continuing..." + fi + + # ========================================================================= + # Step 8: Break down into tasks via Claude + # ========================================================================= + print_header "Step 8: Claude breaks down into tasks" + + if ! submit_build_and_wait "$PROJECT_NAME" "/breakdown-feature $FEATURE_SLUG" "Task breakdown"; then + print_error "Task breakdown failed" + exit 1 + fi + + # ========================================================================= + # Step 9: Approve tasks via build + # ========================================================================= + print_header "Step 9: Approve tasks" + + if ! submit_build_and_wait "$PROJECT_NAME" \ + "Approve the tasks artifact for feature $FEATURE_SLUG using: sdlc artifact approve $FEATURE_SLUG tasks" \ + "Tasks approval"; then + print_warning "Tasks approval may have failed, continuing..." + fi + + # ========================================================================= + # Step 10: Implement first task via Claude + # ========================================================================= + print_header "Step 10: Claude implements first task" + + # First task is typically T1 + local first_task_id="T1" + echo " First task ID: $first_task_id" + + if ! submit_build_and_wait "$PROJECT_NAME" "/implement-task $FEATURE_SLUG $first_task_id" "Task implementation"; then + print_error "Task implementation failed" + exit 1 + fi + + # ========================================================================= + # Step 11: Verify artifacts exist + # ========================================================================= + verify_artifacts "$PROJECT_NAME" "$FEATURE_SLUG" + + # ========================================================================= + # Summary + # ========================================================================= + print_header "E2E Test Results" + print_success "Feature development flow completed!" + echo "" + echo " Project: $PROJECT_NAME" + echo " Feature: $FEATURE_SLUG" + echo " Domain: https://$domain" + echo " Git: https://git.threesix.ai/$(get_git_owner)/$PROJECT_NAME" + echo " CI: https://ci.threesix.ai/$(get_git_owner)/$PROJECT_NAME" + echo "" + echo "Workflow completed:" + echo " 1. Project skeleton created" + echo " 2. Service component added" + echo " 3. SDLC initialized + feature created (via build)" + echo " 4. Claude generated spec" + echo " 5. Spec approved" + echo " 6. Claude generated design" + echo " 7. Design approved" + echo " 8. Claude broke down into tasks" + echo " 9. Tasks approved" + echo " 10. Claude implemented first task" + echo "" + echo "View the feature artifacts at:" + echo " https://git.threesix.ai/$(get_git_owner)/$PROJECT_NAME/src/branch/main/.sdlc/features/$FEATURE_SLUG" +} + +# Show status +check_status() { + print_header "Project Status: $PROJECT_NAME" + + # Get project info + echo "Project:" + api_call GET "/project/$PROJECT_NAME" | jq '.data // .' + echo "" + + # Get components + echo "Components:" + api_call GET "/projects/$PROJECT_NAME/components" | jq '.data // .' + echo "" + + # Get recent builds + print_header "Recent Builds" + api_call GET "/projects/$PROJECT_NAME/builds?limit=10" | jq '.data.builds[:10] // .builds[:10] // []' + echo "" + + # Note about SDLC status + echo "Note: SDLC status is stored in the git repo at .sdlc/" + echo "View at: https://git.threesix.ai/$(get_git_owner)/$PROJECT_NAME/src/branch/main/.sdlc" +} + +# Teardown +teardown() { + print_header "Tearing down: $PROJECT_NAME" + + local result + result=$(api_call DELETE "/project/$PROJECT_NAME") + echo "$result" | jq '.' + + echo "" + print_success "Project deleted. Gitea repo preserved." +} + +# Dispatch +case "$COMMAND" in + run) + run_flow + ;; + status) + check_status + ;; + teardown) + teardown + ;; + *) + echo "Unknown command: $COMMAND" + echo "Valid commands: run, status, teardown" + exit 1 + ;; +esac diff --git a/cookbooks/scripts/scaffold-test.sh b/cookbooks/scripts/scaffold-test.sh index ead3588..6ea8f09 100755 --- a/cookbooks/scripts/scaffold-test.sh +++ b/cookbooks/scripts/scaffold-test.sh @@ -1,8 +1,8 @@ #!/bin/bash set -euo pipefail -# Feature Development E2E Test Script -# Tests the complete feature development workflow: +# Scaffold Validation E2E Test Script +# Tests the composable monorepo scaffold creation: # 1. Create composable project with skeleton # 2. Add service component # 3. Add app-nextjs component @@ -11,7 +11,10 @@ set -euo pipefail # 6. Test auth integration # 7. Verify CI pipeline # -# Usage: ./cookbooks/scripts/feature-test.sh +# NOTE: This tests scaffold creation and patterns, NOT feature development. +# For feature development E2E tests, see feature-dev-test.sh instead. +# +# Usage: ./cookbooks/scripts/scaffold-test.sh # Commands: run, status, verify-patterns, teardown SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" diff --git a/internal/handlers/project_management.go b/internal/handlers/project_management.go index 02f796f..c15f493 100644 --- a/internal/handlers/project_management.go +++ b/internal/handlers/project_management.go @@ -56,12 +56,10 @@ func (h *ProjectManagementHandler) Mount(r api.Router) { Get("/{name}", h.Status) }) - // Bulk operations on projects (admin only) - r.Route("/projects", func(r chi.Router) { - // Cleanup test projects - admin only for safety - r.With(auth.RequireScope(auth.ScopeAdmin)). - Delete("/cleanup", h.CleanupTestProjects) - }) + // Bulk operations - cleanup test projects (admin only) + // Note: Uses direct route to avoid conflict with /projects in projects.go + r.With(auth.RequireScope(auth.ScopeAdmin)). + Delete("/projects/cleanup", h.CleanupTestProjects) // Template endpoints (read-only) r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)). diff --git a/internal/handlers/sdlc_orchestrator.go b/internal/handlers/sdlc_orchestrator.go index 4d3bf34..99c0bfa 100644 --- a/internal/handlers/sdlc_orchestrator.go +++ b/internal/handlers/sdlc_orchestrator.go @@ -29,13 +29,15 @@ func NewSDLCOrchestratorHandler(orchestrator *service.SDLCOrchestratorService, l } // Mount registers orchestration routes under /projects/{id}/sdlc/. +// Note: Uses direct routes to avoid conflict with /projects/{id}/sdlc in sdlc.go func (h *SDLCOrchestratorHandler) Mount(r api.Router) { - r.Route("/projects/{id}/sdlc", func(r chi.Router) { - // All orchestration operations are write operations - r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/execute", h.Execute) - r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/resolve", h.Resolve) - r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/commit", h.Commit) - }) + // All orchestration operations are write operations + r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)). + Post("/projects/{id}/sdlc/execute", h.Execute) + r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)). + Post("/projects/{id}/sdlc/resolve", h.Resolve) + r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)). + Post("/projects/{id}/sdlc/commit", h.Commit) } // Execute runs the next classifier-recommended action.