diff --git a/.architecture/config.yml b/.architecture/config.yml index e8020bc..ad72fce 100644 --- a/.architecture/config.yml +++ b/.architecture/config.yml @@ -180,8 +180,20 @@ review_process: # Architecture Decision Record (ADR) configuration adr: # Numbering format: sequential | date-based + # Sequential produces ADR-001, ADR-002, etc. + # Date-based produces ADR-20260210, ADR-20260211, etc. numbering_format: sequential + # Sequential format: zero-padding pattern + # Count of zeros determines digit width: "000" = 3 digits, "0000" = 4 digits + # Only used when numbering_format: sequential + # sequential_format: "000" + + # Date format: strftime format string passed to date command + # Only used when numbering_format: date-based + # Examples: "%Y%m%d" = 20260210, "%Y-%m-%d" = 2026-02-10 + # date_format: "%Y%m%d" + # Require alternatives section require_alternatives: true diff --git a/.architecture/templates/config.yml b/.architecture/templates/config.yml index c0e157a..d1ead8a 100644 --- a/.architecture/templates/config.yml +++ b/.architecture/templates/config.yml @@ -348,8 +348,20 @@ review_process: # Architecture Decision Record (ADR) configuration adr: # Numbering format: sequential | date-based + # Sequential produces ADR-001, ADR-002, etc. + # Date-based produces ADR-20260210, ADR-20260211, etc. numbering_format: sequential + # Sequential format: zero-padding pattern + # Count of zeros determines digit width: "000" = 3 digits, "0000" = 4 digits + # Only used when numbering_format: sequential + # sequential_format: "000" + + # Date format: strftime format string passed to date command + # Only used when numbering_format: date-based + # Examples: "%Y%m%d" = 20260210, "%Y-%m-%d" = 2026-02-10 + # date_format: "%Y%m%d" + # Require alternatives section require_alternatives: true diff --git a/.claude/skills/_patterns.md b/.claude/skills/_patterns.md index 429e139..0f61dbd 100644 --- a/.claude/skills/_patterns.md +++ b/.claude/skills/_patterns.md @@ -8,11 +8,12 @@ This document contains reusable patterns referenced by AI Software Architect ski 1. [Tool Permission Pattern](#tool-permission-pattern) 2. [Progressive Disclosure Pattern](#progressive-disclosure-pattern) -3. [Input Validation & Sanitization](#input-validation--sanitization) -4. [Error Handling](#error-handling) -5. [File Loading](#file-loading) -6. [Reporting Format](#reporting-format) -7. [Skill Workflow Template](#skill-workflow-template) +3. [Script-Based Deterministic Operations](#script-based-deterministic-operations) +4. [Input Validation & Sanitization](#input-validation--sanitization) +5. [Error Handling](#error-handling) +6. [File Loading](#file-loading) +7. [Reporting Format](#reporting-format) +8. [Skill Workflow Template](#skill-workflow-template) --- @@ -85,7 +86,7 @@ allowed-tools: Read,Write,Edit,Glob,Grep,Bash |-------|-------|-----------| | `list-members` | `Read` | Only reads members.yml | | `architecture-status` | `Read,Glob,Grep` | Scans files and searches | -| `create-adr` | `Read,Write,Bash(ls:*,grep:*)` | Creates ADRs, scans for numbering | +| `create-adr` | `Read,Write,Bash(bash:*,ls:*,grep:*)` | Creates ADRs, runs numbering script | | `specialist-review` | `Read,Write,Glob,Grep` | Reviews code, writes reports | | `architecture-review` | `Read,Write,Glob,Grep,Bash(git:*)` | Full reviews + git status | | `pragmatic-guard` | `Read,Edit` | Reads/modifies config | @@ -425,6 +426,67 @@ mkdir -p .claude/skills/skill-name/assets --- +## Script-Based Deterministic Operations + +**Status**: Implemented (2026-02-10) +**Reference**: [ADR-009](../../.architecture/decisions/adrs/ADR-009-script-based-deterministic-operations.md) + +Operations that must produce identical results regardless of LLM interpretation should be implemented as shell scripts, not as inline skill instructions. Skills invoke scripts via `Bash`, scripts return structured output. + +### When to Script + +**Script when:** +- Operation involves destructive actions (`rm -rf`, file overwrites) +- Operation has an exact correct output (numbering, formatting, paths) +- Operation reads configuration and must respect it deterministically +- LLM improvisation has caused or could cause bugs +- Trust erosion from incorrect results would undermine framework credibility + +**Keep interpretive when:** +- Operation requires understanding context (project analysis, recommendations) +- Output is naturally variable (reviews, summaries, team customization) +- No single correct answer exists + +### Script Interface Convention + +Scripts follow a consistent interface pattern: + +- **Arguments**: Positional, documented in script header comments +- **Stdout**: Structured tokens or single-value output (machine-readable) +- **Stderr**: Human-readable errors and warnings +- **Exit codes**: 0 = success, non-zero = specific failure category +- **Safety**: `set -euo pipefail` in all scripts + +### Current Scripts + +| Script | Location | Used By | Purpose | +|--------|----------|---------|---------| +| `install-framework.sh` | `setup-architect/scripts/` | `setup-architect` | All installation file operations | +| `next-adr-number.sh` | `scripts/` (shared) | `create-adr` | ADR prefix generation from config | + +### Skill Invocation Pattern + +Skills invoke scripts using the `` path provided at the top of the skill prompt: + +```bash +# Skill-specific script +bash "/scripts/install-framework.sh" "$(pwd)" + +# Shared script (one directory up from skill base) +bash "/../scripts/next-adr-number.sh" "" "" +``` + +### Script Location Convention + +- **Skill-specific scripts**: `/scripts/` — used by one skill only +- **Shared scripts**: `scripts/` (at skills root) — used by multiple skills + +### Background + +ADR-009 identified the need for deterministic operations but deferred implementation because it missed the highest-stakes candidate (framework installation with destructive `rm -rf`). The trigger condition — "bash command construction causes bugs" — was satisfied when the setup-architect skill repeatedly botched the installation copy path despite a prior fix (PR #5). This pattern was implemented to address both the installation failure and ADR numbering reliability. + +--- + ## Input Validation & Sanitization ### Filename Sanitization Pattern @@ -891,6 +953,12 @@ Don't extract when: ## Version History +**v1.3** (2026-02-10) +- Added script-based deterministic operations pattern (ADR-009 implementation) +- Documented when to script vs keep interpretive +- Added script interface convention and location convention +- Updated create-adr permission scope for script invocation + **v1.2** (2025-12-04) - Added progressive disclosure pattern (ADR-008) - Documented modular skill structure (SKILL.md + /references/ + /assets/) diff --git a/.claude/skills/create-adr/SKILL.md b/.claude/skills/create-adr/SKILL.md index b426ca3..9236b68 100644 --- a/.claude/skills/create-adr/SKILL.md +++ b/.claude/skills/create-adr/SKILL.md @@ -1,7 +1,7 @@ --- name: create-adr description: Creates a NEW Architectural Decision Record (ADR) documenting a specific architectural decision. Use when the user requests "Create ADR for [topic]", "Document decision about [topic]", "Write ADR for [choice]", or when documenting technology choices, patterns, or architectural approaches. Do NOT use for reviews (use architecture-review or specialist-review), checking existing ADRs (use architecture-status), or general documentation. -allowed-tools: Read,Write,Bash(ls:*,grep:*) +allowed-tools: Read,Write,Bash(bash:*,ls:*,grep:*) --- # Create Architectural Decision Record (ADR) @@ -18,11 +18,20 @@ Ask if needed: - What are the trade-offs? ### 2. Generate ADR Number + +Use the deterministic numbering script to get the next ADR prefix. The script reads `numbering_format`, `sequential_format`, and `date_format` from `.architecture/config.yml` and returns the correct prefix. + ```bash -# Find highest ADR number -ls .architecture/decisions/adrs/ | grep -E "^ADR-[0-9]+" | sed 's/ADR-//' | sed 's/-.*//' | sort -n | tail -1 +bash "/../scripts/next-adr-number.sh" ".architecture/decisions/adrs" "topic-slug" ``` -New ADR = next sequential number (e.g., if highest is 003, create 004) + +Where `` is this skill's base directory shown at the top of the skill prompt, and `topic-slug` is the kebab-case topic from step 3. + +- The script outputs the prefix on stdout (e.g., `001` for sequential, `20260210` for date-based) +- Exit code 2 means a collision was detected (ADR with same prefix and topic already exists) — alert the user +- Exit code 1 means a config error — check stderr for details + +**Note**: Run step 3 (sanitize input) before step 2 so the topic-slug is available for collision detection. The numbering below is logical, not execution order. ### 3. Validate and Sanitize Input **Security**: Sanitize user input to prevent path traversal and injection: @@ -32,7 +41,7 @@ New ADR = next sequential number (e.g., if highest is 003, create 004) - Validate result: ensure filename contains only [a-z0-9-] ### 4. Create Filename -Format: `ADR-XXX-kebab-case-title.md` +Format: `ADR--kebab-case-title.md` where `` is the output from step 2. Examples: - `ADR-001-use-react-for-frontend.md` diff --git a/.claude/skills/scripts/next-adr-number.sh b/.claude/skills/scripts/next-adr-number.sh new file mode 100755 index 0000000..f8380a4 --- /dev/null +++ b/.claude/skills/scripts/next-adr-number.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +# next-adr-number.sh - Deterministic ADR prefix generation +# +# Reads numbering config from .architecture/config.yml and returns the +# next ADR prefix. Supports sequential (zero-padded) and date-based +# (strftime format) numbering. +# +# Usage: next-adr-number.sh [topic-slug] +# +# Arguments: +# adrs-dir Path to the ADRs directory (e.g., .architecture/decisions/adrs) +# topic-slug Optional kebab-case topic for collision detection +# +# Config options (in .architecture/config.yml under adr:): +# numbering_format: "sequential" (default) or "date-based" +# sequential_format: Zero-padding pattern, e.g., "000" for 3 digits (default: "000") +# date_format: strftime format string, e.g., "%Y%m%d" (default: "%Y%m%d") +# +# Exit codes: +# 0 Success - prefix written to stdout +# 1 Config error or bad arguments +# 2 Collision detected (existing ADR with same prefix and topic) + +set -euo pipefail + +ADRS_DIR="${1:?Usage: next-adr-number.sh [topic-slug]}" +TOPIC_SLUG="${2:-}" + +# Validate ADRs directory exists +if [ ! -d "$ADRS_DIR" ]; then + echo "ERROR: ADR directory not found: $ADRS_DIR" >&2 + exit 1 +fi + +# Find config.yml relative to adrs-dir +# Expected: .architecture/decisions/adrs -> .architecture/config.yml +CONFIG_FILE="" +candidate="$(cd "$ADRS_DIR" && pwd)" +# Walk up looking for config.yml alongside a decisions/ dir +for _ in 1 2 3 4; do + candidate="$(dirname "$candidate")" + if [ -f "$candidate/config.yml" ] && [ -d "$candidate/decisions" ]; then + CONFIG_FILE="$candidate/config.yml" + break + fi +done + +# --- Read config values --- +# Simple grep-based extraction — avoids YAML parser dependency + +get_config_value() { + local key="$1" + local default="$2" + if [ -n "$CONFIG_FILE" ] && [ -f "$CONFIG_FILE" ]; then + local value + value=$(grep -E "^\s*${key}:" "$CONFIG_FILE" 2>/dev/null | head -1 | sed 's/^[^:]*:\s*//' | sed 's/\s*#.*//' | sed 's/^["'"'"']//' | sed 's/["'"'"']$//' | tr -d '[:space:]') + if [ -n "$value" ]; then + echo "$value" + return + fi + fi + echo "$default" +} + +NUMBERING_FORMAT=$(get_config_value "numbering_format" "sequential") +SEQUENTIAL_FORMAT=$(get_config_value "sequential_format" "000") +DATE_FORMAT=$(get_config_value "date_format" "%Y%m%d") + +# --- Generate prefix --- + +generate_sequential_prefix() { + local format="$1" + + # Count zeros to determine padding width + local width=${#format} + if [ "$width" -lt 1 ]; then + width=3 + fi + + # Find highest existing ADR number + local highest=0 + if ls "$ADRS_DIR"/ADR-* 1>/dev/null 2>&1; then + highest=$(ls -1 "$ADRS_DIR" | grep -Eo "^ADR-[0-9]+" | sed 's/ADR-//' | sort -n | tail -1) + # Strip leading zeros for arithmetic + highest=$((10#${highest})) + fi + + local next=$((highest + 1)) + printf "%0${width}d" "$next" +} + +generate_date_prefix() { + local fmt="$1" + date +"$fmt" +} + +# --- Main --- + +case "$NUMBERING_FORMAT" in + sequential) + PREFIX=$(generate_sequential_prefix "$SEQUENTIAL_FORMAT") + ;; + date-based|date_based|datebased) + PREFIX=$(generate_date_prefix "$DATE_FORMAT") + ;; + *) + echo "ERROR: Unknown numbering_format: $NUMBERING_FORMAT (expected: sequential or date-based)" >&2 + exit 1 + ;; +esac + +# --- Collision detection --- + +if [ -n "$TOPIC_SLUG" ]; then + # Check for existing file with same prefix and topic + if ls "$ADRS_DIR"/ADR-"${PREFIX}"-"${TOPIC_SLUG}"* 1>/dev/null 2>&1; then + existing=$(ls -1 "$ADRS_DIR"/ADR-"${PREFIX}"-"${TOPIC_SLUG}"* 2>/dev/null | head -1) + echo "ERROR: ADR already exists: $(basename "$existing")" >&2 + echo "ERROR: This is likely a duplicate — review before creating another" >&2 + exit 2 + fi +fi + +echo "$PREFIX" diff --git a/.claude/skills/setup-architect/SKILL.md b/.claude/skills/setup-architect/SKILL.md index d72a83e..3e0115b 100644 --- a/.claude/skills/setup-architect/SKILL.md +++ b/.claude/skills/setup-architect/SKILL.md @@ -11,29 +11,22 @@ Sets up and customizes the AI Software Architect framework for a project. ## Overview This skill performs a complete framework installation: -1. Verifies prerequisites (framework cloned, project root confirmed) -2. Analyzes project (languages, frameworks, structure, patterns) -3. Installs framework files and directory structure -4. Customizes team members and principles for detected tech stack -5. Performs initial system analysis -6. Reports customizations and findings - -**Detailed procedures**: [references/installation-procedures.md](references/installation-procedures.md) +1. Analyzes project (languages, frameworks, structure, patterns) +2. Installs framework skeleton via deterministic script (templates, directories, config) +3. Creates team members based on detected tech stack +4. Creates architectural principles based on detected frameworks +5. Updates CLAUDE.md integration +6. Performs initial system analysis +7. Reports customizations and findings + **Customization guide**: [references/customization-guide.md](references/customization-guide.md) +**Troubleshooting**: [references/installation-procedures.md](references/installation-procedures.md) ## High-Level Workflow -### 1. Verify Prerequisites - -Check requirements before installation: -- `.architecture/.architecture/` directory exists (cloned framework) -- Currently in project root directory - -**If missing**: Guide user to clone framework first. +### 1. Analyze Project -### 2. Analyze Project - -Identify project characteristics: +Identify project characteristics before installation: - **Languages**: JavaScript/TypeScript, Python, Ruby, Java, Go, Rust - **Frameworks**: React, Vue, Django, Rails, Spring, etc. - **Infrastructure**: Testing setup, CI/CD, package managers @@ -41,20 +34,38 @@ Identify project characteristics: Use `Glob` and `Grep` to detect technologies, `Read` to examine configs. -### 3. Install Framework +### 2. Install Framework + +Run the installation script. The script clones the framework repo to `/tmp`, reads `.install-manifest` to determine what to copy, installs only template files and agent docs, creates empty directories for project content, and initializes config. No manual cloning needed. + +```bash +bash "/scripts/install-framework.sh" "$(pwd)" +``` + +Where `` is this skill's base directory shown at the top of the skill prompt. + +The script outputs structured status tokens. If it fails, check stderr for the specific error. See [references/installation-procedures.md § Troubleshooting](references/installation-procedures.md#troubleshooting) for recovery steps. -Execute installation steps (see [references/installation-procedures.md](references/installation-procedures.md)): -- Copy framework files to `.architecture/` -- Remove clone directory -- Create directory structure (decisions/adrs, reviews, recalibration, etc.) -- Initialize configuration from templates -- Set up agent documentation (ADR-006 progressive disclosure) +**What the script installs:** +- `templates/` — ADR, review, config, and other templates +- `agent_docs/` — reference documentation for progressive disclosure (ADR-006) +- Empty directories: `decisions/adrs/`, `reviews/`, `recalibration/`, `comparisons/` +- `config.yml` — initialized from `templates/config.yml` (only if not already present) -**Critical**: Follow safety procedures when removing `.git/` directory. +**What the script does NOT install** (created by later steps): +- `members.yml` — created in step 3 +- `principles.md` — created in step 4 +- `reviews/initial-system-analysis.md` — created in step 6 -### 4. Customize Architecture Team +### 3. Create Architecture Team -Add technology-specific members to `.architecture/members.yml`: +Create `.architecture/members.yml` from scratch based on the project analysis from step 1. This file does not exist yet — the install script does not copy one. + +Use the format from [assets/member-template.yml](assets/member-template.yml). + +**Always include core members**: Systems Architect, Domain Expert, Security, Performance, Maintainability, AI Engineer, Pragmatic Enforcer. + +**Add technology-specific members** based on detected stack: - **JavaScript/TypeScript**: JavaScript Expert, framework specialists (React/Vue/Angular) - **Python**: Python Expert, framework specialists (Django/Flask/FastAPI) - **Ruby**: Ruby Expert, Rails Architect @@ -62,22 +73,22 @@ Add technology-specific members to `.architecture/members.yml`: - **Go**: Go Expert, Microservices Architect - **Rust**: Rust Expert, Systems Programmer -Use template from [assets/member-template.yml](assets/member-template.yml). +**Customization details**: [references/customization-guide.md § Customize Team Members](references/customization-guide.md#customize-architecture-team-members) -**Keep core members**: Systems Architect, Domain Expert, Security, Performance, Maintainability, AI Engineer, Pragmatic Enforcer. +### 4. Create Architectural Principles -**Customization details**: [references/customization-guide.md § Customize Team Members](references/customization-guide.md#customize-architecture-team-members) +Create `.architecture/principles.md` from scratch based on the project analysis from step 1. This file does not exist yet — the install script does not copy one. -### 5. Customize Architectural Principles +**Always include universal principles**: simplicity, separation of concerns, testability, security by default. -Add framework-specific principles to `.architecture/principles.md`: +**Add framework-specific principles** based on detected stack: - **React**: Component composition, hooks, unidirectional data flow - **Rails**: Convention over configuration, DRY, RESTful design - **Django**: Explicit over implicit, reusable apps, use built-ins **Principle examples**: [references/customization-guide.md § Customize Principles](references/customization-guide.md#customize-architectural-principles) -### 6. Update CLAUDE.md Integration +### 5. Update CLAUDE.md Integration If `CLAUDE.md` exists in project root, append framework usage section: - Available commands @@ -86,15 +97,7 @@ If `CLAUDE.md` exists in project root, append framework usage section: **Template**: [references/customization-guide.md § Update CLAUDE.md](references/customization-guide.md#update-claudemd-integration) -### 7. Cleanup - -Remove framework development files: -- Framework documentation (README.md, USAGE*.md, INSTALL.md) -- Template `.git/` directory (with **critical safety checks**) - -**⚠️ IMPORTANT**: Follow all safeguards in [references/installation-procedures.md § Cleanup](references/installation-procedures.md#cleanup-procedures). - -### 8. Create Initial System Analysis +### 6. Create Initial System Analysis Generate comprehensive initial analysis document: - Each member analyzes system from their perspective @@ -108,7 +111,7 @@ Save to `.architecture/reviews/initial-system-analysis.md`. **Template**: [assets/initial-analysis-template.md](assets/initial-analysis-template.md) -### 9. Report to User +### 7. Report to User Provide setup summary: @@ -137,13 +140,17 @@ Next Steps: ## Error Handling -**Framework not cloned**: +**Installation script fails**: ``` -The framework must be cloned first. Please run: +The installation script exited with an error. Check the error message above. -git clone https://github.com/codenamev/ai-software-architect .architecture/.architecture +Common causes: +- Exit 1: Clone failed or bad project path +- Exit 2: File copy failed (check permissions) +- Exit 3: Manifest not found (repo may not support manifest-based installation) +- Exit 4: Installation incomplete (missing files after copy) -Then run setup again. +For recovery: see references/installation-procedures.md § Troubleshooting ``` **Already set up**: @@ -184,11 +191,11 @@ Setup → Review initial analysis → Create ADRs → Status check → Regular r - Customize based on **actual** project, not every possible option - Be specific about **why** each customization was made - Initial analysis should be thorough but focused on actionable findings -- Safety checks during cleanup are **non-negotiable** +- The installation script handles all file operations — do not manually run cp/mkdir/rm commands ## Documentation -- **Installation details**: [references/installation-procedures.md](references/installation-procedures.md) +- **Troubleshooting & recovery**: [references/installation-procedures.md](references/installation-procedures.md) - **Customization guide**: [references/customization-guide.md](references/customization-guide.md) - **Initial analysis template**: [assets/initial-analysis-template.md](assets/initial-analysis-template.md) - **Member template**: [assets/member-template.yml](assets/member-template.yml) diff --git a/.claude/skills/setup-architect/references/installation-procedures.md b/.claude/skills/setup-architect/references/installation-procedures.md index 594b5f3..25af6c2 100644 --- a/.claude/skills/setup-architect/references/installation-procedures.md +++ b/.claude/skills/setup-architect/references/installation-procedures.md @@ -1,331 +1,90 @@ -# Installation Procedures - Detailed Guide +# Installation Procedures -This document provides detailed step-by-step procedures for installing the AI Software Architect framework in a project. +This document provides troubleshooting, recovery, and reference information for the AI Software Architect framework installation. + +**Note**: All file operations are handled by `install-framework.sh`. This document covers what the script does, how to troubleshoot failures, and how to recover from errors. ## Table of Contents -1. [Prerequisites Verification](#prerequisites-verification) -2. [Framework Installation](#framework-installation) -3. [Agent Documentation Setup](#agent-documentation-setup) -4. [Cleanup Procedures](#cleanup-procedures) -5. [Troubleshooting](#troubleshooting) +1. [What the Script Handles](#what-the-script-handles) +2. [The Install Manifest](#the-install-manifest) +3. [Script Interface](#script-interface) +4. [Troubleshooting](#troubleshooting) +5. [Recovery](#recovery) +6. [Post-Installation](#post-installation) --- -## Prerequisites Verification - -Before installing, verify the environment is ready. - -### Check Framework is Cloned - -The framework must be cloned into `.architecture/.architecture/`: - -```bash -if [ ! -d ".architecture/.architecture" ]; then - echo "❌ Framework not found. Please clone first:" - echo " git clone https://github.com/codenamev/ai-software-architect .architecture/.architecture" - exit 1 -fi - -echo "✅ Framework found at .architecture/.architecture" -``` +## What the Script Handles -### Confirm Project Root +The `install-framework.sh` script at `scripts/install-framework.sh` performs all deterministic file operations: -Verify we're in the project root directory: +1. **Clone** — Shallow-clones the framework repo to `/tmp/ai-software-architect-`. Warns if no project markers found in the target directory. +2. **Read manifest** — Reads `.install-manifest` from the cloned repo to determine what to install. +3. **Copy** — Copies only files and directories listed in the manifest (templates, agent docs). +4. **Create directories** — Creates empty directories listed in the manifest (decisions/adrs, reviews, recalibration, comparisons). +5. **Initialize config** — Copies `templates/config.yml` to `config.yml` if no config exists yet. +6. **Cleanup** — Removes the temporary clone directory on exit (via trap). +7. **Verify** — Checks all required directories exist. -```bash -# Look for common project markers -if [ -f "package.json" ] || [ -f "Gemfile" ] || [ -f "requirements.txt" ] || [ -f "go.mod" ] || [ -f "Cargo.toml" ]; then - echo "✅ In project root" -else - echo "⚠️ No project markers found. Are you in the project root?" - # Continue but warn user -fi -``` +**What the script does NOT handle** (left to the skill's interpretive steps): +- `members.yml` — created by the skill based on project analysis +- `principles.md` — created by the skill based on detected tech stack +- `reviews/initial-system-analysis.md` — created by the skill's analysis step --- -## Framework Installation +## The Install Manifest -### Step 1: Copy Framework Files +The `.install-manifest` file in the repository root controls what gets installed. It uses a simple line-based format: -Copy the framework from the cloned location to `.architecture/`: - -```bash -# Copy framework files (they're in the .architecture subfolder of the cloned repo) -cp -r .architecture/.architecture/.architecture/* .architecture/ - -# Verify copy succeeded -if [ $? -eq 0 ]; then - echo "✅ Framework files copied" -else - echo "❌ Copy failed" - exit 1 -fi ``` - -### Step 2: Remove Clone Directory - -Clean up the temporary clone directory: - -```bash -# Remove the clone directory (no longer needed) -rm -rf .architecture/.architecture - -if [ ! -d ".architecture/.architecture" ]; then - echo "✅ Clone directory removed" -fi +# Comments start with # +copy: templates # Copy directory from repo's .architecture/ +copy: agent_docs # Copy directory from repo's .architecture/ +mkdir: decisions/adrs # Create empty directory +mkdir: reviews # Create empty directory +config: templates/config.yml config.yml # Copy src to dst if dst doesn't exist ``` -### Step 3: Create Directory Structure - -Create all required directories: - -```bash -# Create coding assistant directories -mkdir -p .coding-assistants/claude -mkdir -p .coding-assistants/cursor -mkdir -p .coding-assistants/codex - -# Create architecture directories -mkdir -p .architecture/decisions/adrs -mkdir -p .architecture/reviews -mkdir -p .architecture/recalibration -mkdir -p .architecture/comparisons -mkdir -p .architecture/agent_docs - -echo "✅ Directory structure created" -``` - -### Step 4: Initialize Configuration - -Copy the default configuration file: - -```bash -# Copy config template if exists -if [ -f ".architecture/templates/config.yml" ]; then - cp .architecture/templates/config.yml .architecture/config.yml - echo "✅ Configuration initialized" -else - echo "⚠️ No config template found" -fi -``` - -**Verify installation**: -```bash -# Check key directories exist -test -d .architecture/decisions/adrs && \ -test -d .architecture/reviews && \ -test -f .architecture/members.yml && \ -test -f .architecture/principles.md && \ -echo "✅ Installation verified" || echo "❌ Installation incomplete" -``` - ---- - -## Agent Documentation Setup - -Following ADR-006 (Progressive Disclosure), create agent-specific documentation. - -### Copy Existing Agent Docs (If Available) - -If the framework includes agent documentation, copy it as templates: - -```bash -if [ -d ".architecture/agent_docs" ]; then - # Backup existing files as templates - if [ -f ".architecture/agent_docs/workflows.md" ]; then - cp .architecture/agent_docs/workflows.md .architecture/agent_docs/workflows.md.template - fi - - if [ -f ".architecture/agent_docs/reference.md" ]; then - cp .architecture/agent_docs/reference.md .architecture/agent_docs/reference.md.template - fi - - if [ -f ".architecture/agent_docs/README.md" ]; then - cp .architecture/agent_docs/README.md .architecture/agent_docs/README.md.template - fi - - echo "✅ Agent docs backed up as templates" -fi -``` +**Directives:** +- `copy: ` — Copy file or directory from the cloned repo's `.architecture/` to the target `.architecture/` +- `mkdir: ` — Create empty directory in target `.architecture/` +- `config: ` — Copy src to dst only if dst doesn't already exist -### Create Agent Documentation Files - -Create three core documentation files: - -**1. workflows.md** - Procedural documentation: -- Setup procedures (Claude Skills, Direct Clone, MCP) -- Architecture review process -- ADR creation workflow -- Implementation with methodology -- Step-by-step instructions for common tasks - -**2. reference.md** - Reference documentation: -- Pragmatic mode details and intensity levels -- Recalibration process -- Advanced configuration options -- Troubleshooting guide -- Configuration examples - -**3. README.md** - Navigation guide: -- Progressive disclosure explanation -- Quick navigation table (task → section) -- How to find information -- When to read which document - -**Content Guidelines**: -- AGENTS.md: ~400 lines, always-relevant overview -- agent_docs/: Task-specific details loaded as needed -- Keep workflows procedural and actionable -- Reference should be comprehensive but organized -- README should help users navigate effectively +Paths are relative to `.architecture/`. --- -## Cleanup Procedures - -Remove framework development files that shouldn't be in user projects. - -### Remove Documentation Files - -```bash -# Remove framework documentation (users don't need these) -rm -f .architecture/README.md -rm -f .architecture/USAGE*.md -rm -f .architecture/INSTALL.md +## Script Interface -echo "✅ Framework docs removed" ``` +Usage: install-framework.sh [repo-url] -### Remove Framework Git Repository +Arguments: + project-root Absolute path to the target project root directory + repo-url Git repo URL (default: https://github.com/codenamev/ai-software-architect) -**⚠️ CRITICAL SAFEGUARDS - READ CAREFULLY** +Environment variables: + CLONE_DIR_OVERRIDE= Use pre-cloned directory instead of cloning (for testing) -Removing `.git` directory is destructive. Follow these safeguards: +Exit codes: + 0 Success + 1 Clone failed or bad project path + 2 Copy failed + 3 Manifest not found or malformed + 4 Verification failed (installation incomplete) -#### Safeguard 1: Verify Project Root - -```bash -# Check we're in project root (NOT in .architecture/) -if [ ! -f "package.json" ] && [ ! -f ".git/config" ] && [ ! -f "Gemfile" ]; then - echo "❌ ERROR: Not in project root. Stopping." - echo " Current directory: $(pwd)" - exit 1 -fi - -echo "✅ Verified in project root" -``` - -#### Safeguard 2: Verify Target Exists - -```bash -# Check .architecture/.git exists before attempting removal -if [ ! -d ".architecture/.git" ]; then - echo "✅ No .git directory to remove" - exit 0 -fi - -echo "⚠️ Found .architecture/.git - proceeding with verification" -``` - -#### Safeguard 3: Verify It's the Template Repo - -```bash -# Verify .git/config contains template repository URL -if ! grep -q "ai-software-architect" .architecture/.git/config 2>/dev/null; then - echo "❌ ERROR: .architecture/.git doesn't appear to be template repo" - echo " Found config:" - cat .architecture/.git/config 2>/dev/null || echo " (could not read config)" - echo "" - echo "⛔ STOPPING - User confirmation required" - exit 1 -fi - -echo "✅ Verified template repository" -``` - -#### Safeguard 4: Use Absolute Path - -```bash -# Get absolute path (never use relative paths with rm -rf) -ABS_PATH="$(pwd)/.architecture/.git" - -echo "Removing: $ABS_PATH" - -# Verify path is what we expect -if [[ "$ABS_PATH" != *"/.architecture/.git" ]]; then - echo "❌ ERROR: Path doesn't match expected pattern" - echo " Path: $ABS_PATH" - exit 1 -fi - -echo "✅ Path verified" -``` - -#### Safeguard 5: Execute Removal - -```bash -# Remove with absolute path (no wildcards!) -rm -rf "$ABS_PATH" - -# Verify removal -if [ ! -d ".architecture/.git" ]; then - echo "✅ Template .git removed successfully" -else - echo "⚠️ .git directory still exists" -fi -``` - -**Complete Safe Removal Script**: - -```bash -#!/bin/bash -# Safe removal of template repository .git directory - -set -e # Exit on any error - -echo "=== Safe .git Removal ===" - -# 1. Verify project root -if [ ! -f "package.json" ] && [ ! -f ".git/config" ] && [ ! -f "Gemfile" ]; then - echo "❌ Not in project root" - exit 1 -fi - -# 2. Check target exists -if [ ! -d ".architecture/.git" ]; then - echo "✅ No .git to remove" - exit 0 -fi - -# 3. Verify template repo -if ! grep -q "ai-software-architect" .architecture/.git/config 2>/dev/null; then - echo "❌ Not template repo - STOPPING" - exit 1 -fi - -# 4. Get absolute path -ABS_PATH="$(pwd)/.architecture/.git" - -# 5. Verify path pattern -if [[ "$ABS_PATH" != *"/.architecture/.git" ]]; then - echo "❌ Unexpected path - STOPPING" - exit 1 -fi - -# 6. Execute removal -echo "Removing: $ABS_PATH" -rm -rf "$ABS_PATH" - -# 7. Verify success -if [ ! -d ".architecture/.git" ]; then - echo "✅ Successfully removed" -else - echo "❌ Removal failed" - exit 1 -fi +Stdout tokens: + CLONE_OK Repository cloned successfully + CLONE_SKIPPED Skipped (using CLONE_DIR_OVERRIDE) + MANIFEST_OK Manifest found and readable + CONFIG_INIT Config initialized from template + CONFIG_EXISTS Config already existed (not overwritten) + CONFIG_NO_TEMPLATE No config template found + INSTALL_OK: Installation complete with copy/dir/config counts + VERIFY_OK Installation verified + INSTALLED: Comma-separated list of installed components ``` --- @@ -334,70 +93,63 @@ fi ### Common Issues -**Issue**: "Framework not found at .architecture/.architecture" -- **Cause**: Framework not cloned -- **Solution**: `git clone https://github.com/codenamev/ai-software-architect .architecture/.architecture` +**"Failed to clone" (exit 1)** +- **Cause**: Network error or invalid repo URL +- **Solution**: Check network connectivity. Verify the repo URL is accessible. -**Issue**: "Permission denied" errors during copy -- **Cause**: Insufficient file permissions -- **Solution**: `chmod -R u+rw .architecture/` +**"project-root must be an absolute path" (exit 1)** +- **Cause**: Relative path passed to script +- **Solution**: Use `"$(pwd)"` when invoking the script -**Issue**: "Directory already exists" during mkdir -- **Cause**: Framework already partially installed -- **Solution**: Check if framework is already set up: `ls -la .architecture/` +**"Manifest not found" (exit 3)** +- **Cause**: The cloned repo doesn't contain `.install-manifest` +- **Solution**: The repository may not support manifest-based installation. Check that you're cloning the correct repo/branch. -**Issue**: ".git removal verification failed" -- **Cause**: Safety check detected unexpected repository -- **Solution**: Manually verify `.architecture/.git/config` contains template repo URL -- **Never**: Override safety checks without understanding why they failed - -**Issue**: "No project markers found" -- **Cause**: May not be in project root -- **Solution**: Verify you're in the correct directory, proceed with caution +**"Installation incomplete" (exit 4)** +- **Cause**: Manifest-listed items weren't created successfully +- **Solution**: Check what's missing (listed in error output). Verify the cloned repo's `.architecture/` contains the expected files. ### Verification Commands -**Check installation completeness**: -```bash -# Required directories -test -d .architecture/decisions/adrs && echo "✅ ADRs directory" || echo "❌ Missing ADRs" -test -d .architecture/reviews && echo "✅ Reviews directory" || echo "❌ Missing reviews" - -# Required files -test -f .architecture/members.yml && echo "✅ Members file" || echo "❌ Missing members" -test -f .architecture/principles.md && echo "✅ Principles file" || echo "❌ Missing principles" -test -f .architecture/config.yml && echo "✅ Config file" || echo "❌ Missing config" -``` +Check installation completeness manually: -**Check for leftover framework files**: ```bash -# These should NOT exist after cleanup -test -f .architecture/README.md && echo "⚠️ Framework README still present" -test -d .architecture/.git && echo "⚠️ Template .git still present" -test -d .architecture/.architecture && echo "⚠️ Clone directory still present" +# Required directories (created by script) +test -d .architecture/decisions/adrs && echo "OK ADRs" || echo "MISSING ADRs" +test -d .architecture/reviews && echo "OK reviews" || echo "MISSING reviews" +test -d .architecture/templates && echo "OK templates" || echo "MISSING templates" +test -d .architecture/agent_docs && echo "OK agent_docs" || echo "MISSING agent_docs" + +# Created by skill (not script) — may not exist immediately after script +test -f .architecture/members.yml && echo "OK members" || echo "PENDING members" +test -f .architecture/principles.md && echo "OK principles" || echo "PENDING principles" +test -f .architecture/config.yml && echo "OK config" || echo "MISSING config" ``` -### Recovery +--- + +## Recovery **If installation fails mid-process**: -1. Remove partial installation: `rm -rf .architecture/` (if nothing important there yet) -2. Re-clone framework: `git clone https://github.com/codenamev/ai-software-architect .architecture/.architecture` -3. Start over from Step 1 +1. The script cleans up the temp clone directory automatically (exit trap) +2. Remove partial installation: `rm -rf .architecture/` (if nothing important there yet) +3. Re-run the installation script -**If you accidentally removed the wrong .git**: -- If it was your project's .git: **Restore from backup immediately** -- If you don't have a backup: Recovery may not be possible -- This is why the safeguards are critical +**If the target project already has `.architecture/`**: +- The script is idempotent — running it again copies templates over existing ones and skips config if it already exists +- Existing `members.yml`, `principles.md`, and reviews are not affected (the script doesn't create them) --- ## Post-Installation -After installation is complete: +After the script completes successfully, the skill continues with interpretive steps: -1. **Verify setup**: Run `"What's our architecture status?"` -2. **Review customizations**: Check `.architecture/members.yml` and `.architecture/principles.md` -3. **Run initial analysis**: The setup process creates an initial system analysis -4. **Create first ADR**: Document an early architectural decision +1. **Create team** — The skill creates `members.yml` based on project analysis +2. **Create principles** — The skill creates `principles.md` based on detected tech stack +3. **Update CLAUDE.md** — The skill appends framework usage section +4. **Initial analysis** — The skill creates `reviews/initial-system-analysis.md` +5. **Verify setup**: Run `"What's our architecture status?"` +6. **Create first ADR**: Document an early architectural decision For customization procedures, see [customization-guide.md](./customization-guide.md). diff --git a/.claude/skills/setup-architect/scripts/install-framework.sh b/.claude/skills/setup-architect/scripts/install-framework.sh new file mode 100755 index 0000000..b578a91 --- /dev/null +++ b/.claude/skills/setup-architect/scripts/install-framework.sh @@ -0,0 +1,197 @@ +#!/usr/bin/env bash +# install-framework.sh - Deterministic installation of AI Software Architect framework +# +# Manifest-based installation: clones repo to /tmp, copies only files listed +# in .install-manifest, creates empty directories, initialises config. +# Content generation (members.yml, principles.md, initial analysis) is handled +# by the skill's interpretive steps, not this script. +# +# Usage: install-framework.sh [repo-url] +# +# Arguments: +# project-root Absolute path to the target project root directory +# repo-url Git repo URL (default: https://github.com/codenamev/ai-software-architect) +# +# Exit codes: +# 0 Success - all steps completed +# 1 Prerequisites failed (bad arguments, clone failed) +# 2 Copy failed +# 3 Manifest error (missing or malformed) +# 4 Verification failed (installation incomplete) +# +# Stdout: structured status tokens (one per line) +# Stderr: human-readable errors and warnings + +set -euo pipefail + +PROJECT_ROOT="${1:?Usage: install-framework.sh [repo-url]}" +REPO_URL="${2:-https://github.com/codenamev/ai-software-architect}" + +# --- Validation --- + +if [[ "$PROJECT_ROOT" != /* ]]; then + echo "ERROR: project-root must be an absolute path: $PROJECT_ROOT" >&2 + exit 1 +fi + +if [ ! -d "$PROJECT_ROOT" ]; then + echo "ERROR: project root does not exist: $PROJECT_ROOT" >&2 + exit 1 +fi + +ARCH_DIR="$PROJECT_ROOT/.architecture" +CLONE_DIR="${CLONE_DIR_OVERRIDE:-/tmp/ai-software-architect-$$}" +MANIFEST_NAME=".install-manifest" + +# Clean up temp directory on exit (skip if using override) +cleanup() { + if [ -z "${CLONE_DIR_OVERRIDE:-}" ] && [ -d "$CLONE_DIR" ]; then + rm -rf "$CLONE_DIR" + fi +} +trap cleanup EXIT + +# --- Phase 1: Clone --- + +clone_repo() { + # Warn (don't fail) if no project markers found + local has_marker=false + for marker in package.json Gemfile requirements.txt go.mod Cargo.toml .git Makefile; do + if [ -e "$PROJECT_ROOT/$marker" ]; then + has_marker=true + break + fi + done + + if [ "$has_marker" = false ]; then + echo "WARNING: No project markers found in $PROJECT_ROOT" >&2 + fi + + # Skip clone if CLONE_DIR_OVERRIDE is set (for testing) + if [ -n "${CLONE_DIR_OVERRIDE:-}" ]; then + echo "CLONE_SKIPPED" + return 0 + fi + + if ! git clone --depth 1 --quiet "$REPO_URL" "$CLONE_DIR" 2>&2; then + echo "ERROR: Failed to clone $REPO_URL" >&2 + exit 1 + fi + + echo "CLONE_OK" +} + +# --- Phase 2: Read manifest and install --- + +read_manifest() { + local manifest="$CLONE_DIR/$MANIFEST_NAME" + + if [ ! -f "$manifest" ]; then + echo "ERROR: Manifest not found at $manifest" >&2 + echo "ERROR: The repository may not support manifest-based installation" >&2 + exit 3 + fi + + echo "MANIFEST_OK" +} + +install_from_manifest() { + local manifest="$CLONE_DIR/$MANIFEST_NAME" + local source_arch="$CLONE_DIR/.architecture" + local copies=0 + local dirs=0 + local configs=0 + + mkdir -p "$ARCH_DIR" + + while IFS= read -r line || [ -n "$line" ]; do + # Skip comments and blank lines + line="${line%%#*}" + line="$(echo "$line" | xargs)" # trim whitespace + [ -z "$line" ] && continue + + local directive="${line%%:*}" + local args="${line#*: }" + + case "$directive" in + copy) + local src="$source_arch/$args" + if [ ! -e "$src" ]; then + echo "WARNING: Source not found, skipping: $args" >&2 + continue + fi + if [ -d "$src" ]; then + mkdir -p "$ARCH_DIR/$args" + cp -r "$src"/* "$ARCH_DIR/$args"/ 2>/dev/null || true + else + local dest_dir + dest_dir="$(dirname "$ARCH_DIR/$args")" + mkdir -p "$dest_dir" + cp "$src" "$ARCH_DIR/$args" + fi + copies=$((copies + 1)) + ;; + mkdir) + mkdir -p "$ARCH_DIR/$args" + dirs=$((dirs + 1)) + ;; + config) + local src_path dest_path + src_path="$ARCH_DIR/$(echo "$args" | awk '{print $1}')" + dest_path="$ARCH_DIR/$(echo "$args" | awk '{print $2}')" + if [ -f "$src_path" ] && [ ! -f "$dest_path" ]; then + cp "$src_path" "$dest_path" + configs=$((configs + 1)) + echo "CONFIG_INIT" + elif [ -f "$dest_path" ]; then + echo "CONFIG_EXISTS" + else + echo "WARNING: Config source not found: $src_path" >&2 + echo "CONFIG_NO_TEMPLATE" + fi + ;; + *) + echo "WARNING: Unknown manifest directive: $directive" >&2 + ;; + esac + done < "$manifest" + + echo "INSTALL_OK:copies=$copies,dirs=$dirs,configs=$configs" +} + +# --- Phase 3: Verify --- + +verify_installation() { + local missing=() + + [ -d "$ARCH_DIR/decisions/adrs" ] || missing+=("decisions/adrs") + [ -d "$ARCH_DIR/reviews" ] || missing+=("reviews") + [ -d "$ARCH_DIR/recalibration" ] || missing+=("recalibration") + [ -d "$ARCH_DIR/templates" ] || missing+=("templates") + [ -d "$ARCH_DIR/agent_docs" ] || missing+=("agent_docs") + + if [ ${#missing[@]} -gt 0 ]; then + echo "ERROR: Installation incomplete. Missing: ${missing[*]}" >&2 + exit 4 + fi + + # Report what was installed + local installed=() + [ -f "$ARCH_DIR/config.yml" ] && installed+=("config.yml") + [ -d "$ARCH_DIR/templates" ] && installed+=("templates/") + [ -d "$ARCH_DIR/agent_docs" ] && installed+=("agent_docs/") + [ -d "$ARCH_DIR/decisions/adrs" ] && installed+=("decisions/adrs/") + [ -d "$ARCH_DIR/reviews" ] && installed+=("reviews/") + [ -d "$ARCH_DIR/recalibration" ] && installed+=("recalibration/") + [ -d "$ARCH_DIR/comparisons" ] && installed+=("comparisons/") + + echo "VERIFY_OK" + echo "INSTALLED:$(IFS=,; echo "${installed[*]}")" +} + +# --- Execute --- + +clone_repo +read_manifest +install_from_manifest +verify_installation diff --git a/.github/workflows/claude-code-tests.yml b/.github/workflows/claude-code-tests.yml index 6035c34..8281504 100644 --- a/.github/workflows/claude-code-tests.yml +++ b/.github/workflows/claude-code-tests.yml @@ -362,6 +362,272 @@ jobs: retention-days: 7 include-hidden-files: true + script-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # --- install-framework.sh tests --- + + - name: "install-framework.sh: success path (manifest-based)" + run: | + echo "=== Testing install-framework.sh manifest-based success path ===" + SCRIPT=".claude/skills/setup-architect/scripts/install-framework.sh" + PROJ="$(mktemp -d)" + echo '{}' > "$PROJ/package.json" + + # Use CLONE_DIR_OVERRIDE to point at this repo (which has the manifest) + OUTPUT=$(CLONE_DIR_OVERRIDE="$(pwd)" bash "$SCRIPT" "$PROJ") + + # Verify tokens + echo "$OUTPUT" | grep -q "CLONE_SKIPPED" && echo "✓ CLONE_SKIPPED" || { echo "✗ missing CLONE_SKIPPED"; exit 1; } + echo "$OUTPUT" | grep -q "MANIFEST_OK" && echo "✓ MANIFEST_OK" || { echo "✗ missing MANIFEST_OK"; exit 1; } + echo "$OUTPUT" | grep -q "INSTALL_OK" && echo "✓ INSTALL_OK" || { echo "✗ missing INSTALL_OK"; exit 1; } + echo "$OUTPUT" | grep -q "VERIFY_OK" && echo "✓ VERIFY_OK" || { echo "✗ missing VERIFY_OK"; exit 1; } + + # Verify manifest-listed items installed + test -d "$PROJ/.architecture/templates" && echo "✓ templates dir" || { echo "✗ templates dir"; exit 1; } + test -d "$PROJ/.architecture/agent_docs" && echo "✓ agent_docs dir" || { echo "✗ agent_docs dir"; exit 1; } + test -d "$PROJ/.architecture/decisions/adrs" && echo "✓ adrs dir" || { echo "✗ adrs dir"; exit 1; } + test -d "$PROJ/.architecture/reviews" && echo "✓ reviews dir" || { echo "✗ reviews dir"; exit 1; } + test -d "$PROJ/.architecture/recalibration" && echo "✓ recalibration dir" || { echo "✗ recalibration dir"; exit 1; } + test -d "$PROJ/.architecture/comparisons" && echo "✓ comparisons dir" || { echo "✗ comparisons dir"; exit 1; } + test -f "$PROJ/.architecture/config.yml" && echo "✓ config.yml" || { echo "✗ config.yml"; exit 1; } + + # Verify template files copied + test -f "$PROJ/.architecture/templates/adr-template.md" && echo "✓ adr template" || { echo "✗ adr template"; exit 1; } + test -f "$PROJ/.architecture/templates/review-template.md" && echo "✓ review template" || { echo "✗ review template"; exit 1; } + test -f "$PROJ/.architecture/agent_docs/reference.md" && echo "✓ agent_docs reference" || { echo "✗ agent_docs reference"; exit 1; } + + # Verify NO cruft was copied (framework's own files) + test ! -f "$PROJ/.architecture/members.yml" && echo "✓ no members.yml (skill creates)" || { echo "✗ members.yml should not be copied"; exit 1; } + test ! -f "$PROJ/.architecture/principles.md" && echo "✓ no principles.md (skill creates)" || { echo "✗ principles.md should not be copied"; exit 1; } + test ! -d "$PROJ/.architecture/decisions/adrs/ADR-001-cli-functional-requirements.md" && echo "✓ no framework ADRs" || { echo "✗ framework ADRs leaked"; exit 1; } + REVIEW_COUNT=$(find "$PROJ/.architecture/reviews" -type f 2>/dev/null | wc -l) + test "$REVIEW_COUNT" -eq 0 && echo "✓ no framework reviews" || { echo "✗ framework reviews leaked ($REVIEW_COUNT files)"; exit 1; } + test ! -d "$PROJ/.architecture/research" && echo "✓ no research dir" || { echo "✗ research dir leaked"; exit 1; } + test ! -f "$PROJ/.architecture/deferrals.md" && echo "✓ no deferrals.md" || { echo "✗ deferrals.md leaked"; exit 1; } + + rm -rf "$PROJ" + echo "=== manifest-based success path: PASSED ===" + + - name: "install-framework.sh: idempotent (run twice)" + run: | + echo "=== Testing install-framework.sh idempotency ===" + SCRIPT=".claude/skills/setup-architect/scripts/install-framework.sh" + PROJ="$(mktemp -d)" + echo '{}' > "$PROJ/package.json" + + # First run + CLONE_DIR_OVERRIDE="$(pwd)" bash "$SCRIPT" "$PROJ" > /dev/null + + # Modify config to verify it's preserved + echo "# custom config" >> "$PROJ/.architecture/config.yml" + + # Second run + OUTPUT=$(CLONE_DIR_OVERRIDE="$(pwd)" bash "$SCRIPT" "$PROJ") + + echo "$OUTPUT" | grep -q "CONFIG_EXISTS" && echo "✓ config preserved" || { echo "✗ config overwritten"; exit 1; } + grep -q "# custom config" "$PROJ/.architecture/config.yml" && echo "✓ config content intact" || { echo "✗ config content lost"; exit 1; } + + rm -rf "$PROJ" + echo "=== idempotency test: PASSED ===" + + - name: "install-framework.sh: missing manifest (exit 3)" + run: | + echo "=== Testing install-framework.sh missing manifest ===" + SCRIPT=".claude/skills/setup-architect/scripts/install-framework.sh" + PROJ="$(mktemp -d)" + FAKE_CLONE="$(mktemp -d)" + mkdir -p "$FAKE_CLONE/.architecture" + + set +e + OUTPUT=$(CLONE_DIR_OVERRIDE="$FAKE_CLONE" bash "$SCRIPT" "$PROJ" 2>&1) + EXIT_CODE=$? + set -e + + test "$EXIT_CODE" -eq 3 && echo "✓ exit code 3" || { echo "✗ expected exit 3, got $EXIT_CODE"; exit 1; } + rm -rf "$PROJ" "$FAKE_CLONE" + echo "=== missing manifest test: PASSED ===" + + - name: "install-framework.sh: relative path (exit 1)" + run: | + echo "=== Testing install-framework.sh relative path ===" + SCRIPT=".claude/skills/setup-architect/scripts/install-framework.sh" + + set +e + OUTPUT=$(bash "$SCRIPT" "relative/path" 2>&1) + EXIT_CODE=$? + set -e + + test "$EXIT_CODE" -eq 1 && echo "✓ exit code 1" || { echo "✗ expected exit 1, got $EXIT_CODE"; exit 1; } + echo "=== relative path test: PASSED ===" + + # --- next-adr-number.sh tests --- + + - name: "next-adr-number.sh: sequential default" + run: | + echo "=== Testing next-adr-number.sh sequential default ===" + SCRIPT=".claude/skills/scripts/next-adr-number.sh" + TMPDIR="$(mktemp -d)" + + # Create structure: architecture/decisions/adrs + config + mkdir -p "$TMPDIR/decisions/adrs" + cat > "$TMPDIR/config.yml" << 'YAML' + adr: + numbering_format: sequential + YAML + + # No existing ADRs — should return 001 + OUTPUT=$(bash "$SCRIPT" "$TMPDIR/decisions/adrs") + test "$OUTPUT" = "001" && echo "✓ first ADR: 001" || { echo "✗ expected 001, got '$OUTPUT'"; exit 1; } + + # Create ADR-001 and ADR-002 + touch "$TMPDIR/decisions/adrs/ADR-001-first-decision.md" + touch "$TMPDIR/decisions/adrs/ADR-002-second-decision.md" + + OUTPUT=$(bash "$SCRIPT" "$TMPDIR/decisions/adrs") + test "$OUTPUT" = "003" && echo "✓ next after 002: 003" || { echo "✗ expected 003, got '$OUTPUT'"; exit 1; } + + rm -rf "$TMPDIR" + echo "=== sequential default test: PASSED ===" + + - name: "next-adr-number.sh: sequential custom padding" + run: | + echo "=== Testing next-adr-number.sh sequential custom padding ===" + SCRIPT=".claude/skills/scripts/next-adr-number.sh" + TMPDIR="$(mktemp -d)" + + mkdir -p "$TMPDIR/decisions/adrs" + cat > "$TMPDIR/config.yml" << 'YAML' + adr: + numbering_format: sequential + sequential_format: "0000" + YAML + + OUTPUT=$(bash "$SCRIPT" "$TMPDIR/decisions/adrs") + test "$OUTPUT" = "0001" && echo "✓ 4-digit padding: 0001" || { echo "✗ expected 0001, got '$OUTPUT'"; exit 1; } + + rm -rf "$TMPDIR" + echo "=== sequential custom padding test: PASSED ===" + + - name: "next-adr-number.sh: date-based" + run: | + echo "=== Testing next-adr-number.sh date-based ===" + SCRIPT=".claude/skills/scripts/next-adr-number.sh" + TMPDIR="$(mktemp -d)" + + mkdir -p "$TMPDIR/decisions/adrs" + cat > "$TMPDIR/config.yml" << 'YAML' + adr: + numbering_format: date-based + YAML + + EXPECTED=$(date +"%Y%m%d") + OUTPUT=$(bash "$SCRIPT" "$TMPDIR/decisions/adrs") + test "$OUTPUT" = "$EXPECTED" && echo "✓ date-based: $EXPECTED" || { echo "✗ expected $EXPECTED, got '$OUTPUT'"; exit 1; } + + rm -rf "$TMPDIR" + echo "=== date-based test: PASSED ===" + + - name: "next-adr-number.sh: date-based custom format" + run: | + echo "=== Testing next-adr-number.sh date-based custom format ===" + SCRIPT=".claude/skills/scripts/next-adr-number.sh" + TMPDIR="$(mktemp -d)" + + mkdir -p "$TMPDIR/decisions/adrs" + cat > "$TMPDIR/config.yml" << 'YAML' + adr: + numbering_format: date-based + date_format: "%Y-%m-%d" + YAML + + EXPECTED=$(date +"%Y-%m-%d") + OUTPUT=$(bash "$SCRIPT" "$TMPDIR/decisions/adrs") + test "$OUTPUT" = "$EXPECTED" && echo "✓ custom date: $EXPECTED" || { echo "✗ expected $EXPECTED, got '$OUTPUT'"; exit 1; } + + rm -rf "$TMPDIR" + echo "=== date-based custom format test: PASSED ===" + + - name: "next-adr-number.sh: collision detection (exit 2)" + run: | + echo "=== Testing next-adr-number.sh collision detection ===" + SCRIPT=".claude/skills/scripts/next-adr-number.sh" + TMPDIR="$(mktemp -d)" + + mkdir -p "$TMPDIR/decisions/adrs" + cat > "$TMPDIR/config.yml" << 'YAML' + adr: + numbering_format: sequential + YAML + + # Create existing ADR with topic + touch "$TMPDIR/decisions/adrs/ADR-001-use-postgres.md" + + set +e + OUTPUT=$(bash "$SCRIPT" "$TMPDIR/decisions/adrs" "use-postgres" 2>&1) + EXIT_CODE=$? + set -e + + # Script should return next number (002) without collision since prefix differs + # But if we force same prefix: create scenario where next is 002 and topic matches + touch "$TMPDIR/decisions/adrs/ADR-002-caching-strategy.md" + set +e + OUTPUT=$(bash "$SCRIPT" "$TMPDIR/decisions/adrs" "caching-strategy" 2>/dev/null) + EXIT_CODE=$? + set -e + + # Next number would be 003, no collision with "caching-strategy" at 003 + # To get actual collision: create ADR-003-caching-strategy.md + touch "$TMPDIR/decisions/adrs/ADR-003-caching-strategy.md" + set +e + OUTPUT=$(bash "$SCRIPT" "$TMPDIR/decisions/adrs" "caching-strategy" 2>&1) + EXIT_CODE=$? + set -e + + # Hmm, the script generates prefix 004 (next after 003), not 003 + # Collision only happens if generated prefix matches existing file + # For sequential, this can't happen by definition (always increments) + # Collision detection is mainly for date-based where two ADRs on same day with same topic + echo "✓ sequential collision: N/A (always increments)" + + # Test date-based collision + cat > "$TMPDIR/config.yml" << 'YAML' + adr: + numbering_format: date-based + YAML + + TODAY=$(date +"%Y%m%d") + touch "$TMPDIR/decisions/adrs/ADR-${TODAY}-use-redis.md" + + set +e + bash "$SCRIPT" "$TMPDIR/decisions/adrs" "use-redis" 2>/dev/null + EXIT_CODE=$? + set -e + + test "$EXIT_CODE" -eq 2 && echo "✓ date collision detected: exit 2" || { echo "✗ expected exit 2, got $EXIT_CODE"; exit 1; } + + rm -rf "$TMPDIR" + echo "=== collision detection test: PASSED ===" + + - name: "next-adr-number.sh: missing config defaults" + run: | + echo "=== Testing next-adr-number.sh missing config ===" + SCRIPT=".claude/skills/scripts/next-adr-number.sh" + TMPDIR="$(mktemp -d)" + + # Create adrs dir but no config.yml + mkdir -p "$TMPDIR/decisions/adrs" + + OUTPUT=$(bash "$SCRIPT" "$TMPDIR/decisions/adrs") + test "$OUTPUT" = "001" && echo "✓ default without config: 001" || { echo "✗ expected 001, got '$OUTPUT'"; exit 1; } + + rm -rf "$TMPDIR" + echo "=== missing config defaults test: PASSED ===" + cross-compatibility-test: runs-on: ubuntu-latest needs: claude-setup-tests diff --git a/.install-manifest b/.install-manifest new file mode 100644 index 0000000..cd905af --- /dev/null +++ b/.install-manifest @@ -0,0 +1,29 @@ +# Install Manifest for AI Software Architect +# +# Used by install-framework.sh to determine what to copy into a target +# project's .architecture/ directory. Only files and directories listed +# here are copied from the cloned repo. Everything else (members.yml, +# principles.md, config.yml, initial analysis) is generated by the +# skill's interpretive steps. +# +# Format: +# copy: — copy file or directory from repo's .architecture/ +# mkdir: — create empty directory in target .architecture/ +# config: — copy src to dst only if dst doesn't exist +# +# Paths are relative to .architecture/ + +# Template files (ADR, review, config, etc.) +copy: templates + +# Agent reference documentation (progressive disclosure - ADR-006) +copy: agent_docs + +# Empty directories for project-specific content +mkdir: decisions/adrs +mkdir: reviews +mkdir: recalibration +mkdir: comparisons + +# Configuration initialised from template +config: templates/config.yml config.yml