diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 649feed..12062d2 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -21,3 +21,6 @@ jobs: - name: Test update settings script run: ./tests/test_update_settings.sh + + - name: Test setup script + run: ./tests/test_setup_script.sh diff --git a/docs/contributing.md b/docs/contributing.md index bc3ec91..2bb60b2 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -41,27 +41,27 @@ lead-dev-os/ # The plugin ## Testing -Tests live in `tests/` and cover both the plugin structure and legacy install behavior. - -### Run all tests +Tests live in `tests/`. Each suite is a standalone bash script; run them individually: ```bash -./tests/run_all.sh +bash tests/test_plugin_structure.sh +bash tests/test_skill_content.sh +bash tests/test_content_bundle.sh +bash tests/test_update_settings.sh +bash tests/test_setup_script.sh ``` +CI (`.github/workflows/actions.yml`) runs all suites on every push. + ### Test suites | Suite | File | What it tests | |-------|------|---------------| -| Plugin structure | `tests/test_plugin_structure.sh` | plugin.json valid, all 9 skill dirs exist, configure-project skill has bundled standards | -| Skill content | `tests/test_skill_content.sh` | No placeholders, all cross-refs namespaced, no config.yml refs, valid frontmatter | +| Plugin structure | `tests/test_plugin_structure.sh` | plugin.json valid, all 9 skill dirs exist, flat layout, executable scripts | +| Skill content | `tests/test_skill_content.sh` | No placeholders, cross-refs namespaced, valid frontmatter, planning content placement, orchestrated execution and full-suite gate present | | Content bundle | `tests/test_content_bundle.sh` | All global standards present, CLAUDE.md namespaced, README updated | -| Update settings | `tests/test_update_settings.sh` | update-settings.sh creates/appends deny rules, idempotent | -| Unit | `tests/test_common_functions.sh` | `ensure_dir`, `ensure_gitkeep`, `copy_if_not_exists`, `copy_with_warning`, `print_verbose` | -| Integration: creates | `tests/test_install_creates.sh` | Fresh install produces all expected files (legacy installer) | -| Integration: overwrites | `tests/test_install_overwrites.sh` | Re-install overwrites skills/guides, preserves concepts/standards (legacy installer) | -| Integration: help | `tests/test_install_help.sh` | --help flag behavior (legacy installer) | -| Integration: stack config | `tests/test_stack_config.sh` | Stack config, profiles, plan mode (legacy installer) | +| Update settings | `tests/test_update_settings.sh` | update-settings.sh writes the archive deny rule under `permissions.deny`, migrates the legacy top-level rule, idempotent | +| Setup script | `tests/test_setup_script.sh` | configure-project's setup.sh scaffolds directories, copies standards, renders templates, handles `--stacks`/`--overwrite` | ### Conventions diff --git a/docs/implementation.md b/docs/implementation.md index 0526072..7a3b6b3 100644 --- a/docs/implementation.md +++ b/docs/implementation.md @@ -16,11 +16,14 @@ Three execution modes control how much human oversight the AI receives during im ## Mode A: Autonomous -All tasks execute sequentially with no human intervention. The agent loads context, implements each task, runs tests, and auto-commits after each one. +All task groups are pre-planned in parallel (one planner subagent per group produces `plans/group-N.md`), you approve the batch, then execution runs with no human intervention. The main conversation acts as an **orchestrator**: each group is executed by a fresh executor subagent with a clean context, and the orchestrator verifies the group's tests itself, reviews the diff, and commits — one atomic commit per group. Independent groups (no dependency between them, disjoint file operations) may execute in parallel. ``` -T1 → T2 → T3 → T4 → T5 → DONE - (auto-commit after each task) + plan G1..G4 (parallel) → approve batch +G1 ⇒ executor → verify → commit +G2 ⇒ executor → verify → commit (G2 ∥ G3 if independent) +G3 ⇒ executor → verify → commit +G4 ⇒ executor → verify → commit → DONE ``` **Best for:** Small features, well-understood domains, no unknowns in the spec. The spec and task definitions are clear enough that the agent can ship without review. @@ -29,10 +32,10 @@ T1 → T2 → T3 → T4 → T5 → DONE ## Mode L: Lead-in-the-Loop -The agent implements one task, then pauses and waits for the lead developer to review before continuing. This creates a feedback loop at every task boundary. +The agent plans and implements one task group in the main conversation (using Claude Code's native plan mode per group), then pauses and waits for the lead developer to review before continuing. This creates a feedback loop at every group boundary — and keeps the work visible, which is why L mode does not delegate to subagents. ``` -T1 → [REVIEW] → T2 → [REVIEW] → T3 → [REVIEW] → T4 +G1 → [REVIEW] → G2 → [REVIEW] → G3 → [REVIEW] → G4 ↑ ↑ ↑ lead reviews lead reviews lead reviews ``` @@ -51,10 +54,10 @@ At each review gate, the lead can: ## Mode H: Hybrid -Implementation starts in autonomous mode and switches to lead-in-the-loop at a defined checkpoint. The lead specifies which task group triggers the handoff. +Implementation starts in autonomous mode (orchestrator + executor subagents, as in Mode A) and switches to lead-in-the-loop at a defined checkpoint. The lead specifies which task group triggers the handoff. ``` -T1 → T2 → T3 ── STOP ── T4 → [REVIEW] → T5 → [REVIEW] → T6 +G1 → G2 → G3 ── STOP ── G4 → [REVIEW] → G5 → [REVIEW] → G6 |← autonomous →| |←────── lead-in-the-loop ──────→| ``` @@ -74,10 +77,15 @@ The autonomous portion handles scaffolding, boilerplate, or well-defined setup t | Prototype / spike / throwaway | **Autonomous** | | Production feature with stakeholder scrutiny | **Lead-in-the-Loop** | -When you run `/lead-dev-os:step3-implement-tasks`, the skill prompts you to select a mode (A / L / H) after loading the spec context. If you choose Hybrid, it also asks which task group number to use as the checkpoint. +When you run `/lead-dev-os:step3-implement-tasks`, the skill reads the `> Size:` estimate from `spec.md` (written by step 1) and recommends a mode — Small→A, Medium→H, Large→L — then prompts you to select (A / L / H). You always decide. If you choose Hybrid, it also asks which task group number to use as the checkpoint. --- ## After Implementation -Once all task groups are complete and the feature is shipped, run `/lead-dev-os:step4-archive-spec` to archive the completed spec. This moves it to `lead-dev-os/specs-archived/` and blocks agent access so stale specs don't get loaded in future sessions. +Once all task groups are complete, the skill closes with two gates before suggesting archive: + +- **Full-suite backstop** — the entire test suite runs once (the only full-suite run in the workflow; per-group runs stay feature-scoped for fast feedback). New failures caused by the feature get fixed; pre-existing failures get reported, not fixed. +- **Runtime verification** — the agent exercises the feature's primary user flow in the running app where feasible, because tests passing is not the same as the feature working. + +Then run `/lead-dev-os:step4-archive-spec` to archive the completed spec. This moves it to `lead-dev-os/specs-archived/` and adds a rule under `permissions.deny` in `.claude/settings.json` so stale specs don't get loaded in future sessions. diff --git a/docs/index.md b/docs/index.md index b9f8586..15c44d7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -60,8 +60,8 @@ See [Installation]({{ site.baseurl }}/installation) for full details. | Phase | Skill | What it does | |-------|---------|--------------| | Write | `/lead-dev-os:step1-write-spec` | Interactive Q&A + formalize into spec with numbered requirements (FR-###) | -| Scope | `/lead-dev-os:step2-scope-tasks` | Break into task groups with context directives | -| Implement | `/lead-dev-os:step3-implement-tasks` | Context-aware execution of task groups | +| Scope | `/lead-dev-os:step2-scope-tasks` | Break into task groups (vertical slices or layers) with context directives | +| Implement | `/lead-dev-os:step3-implement-tasks` | Orchestrated execution — executor subagents per group, full-suite backstop | | Archive | `/lead-dev-os:step4-archive-spec` | Archive completed spec and block agent access | | Utility | `/lead-dev-os:create-pr` | Open a GitHub PR for the current branch with a WHAT-focused description and emoji-prefixed title | diff --git a/docs/workflow.md b/docs/workflow.md index 119a3e9..473c2b6 100644 --- a/docs/workflow.md +++ b/docs/workflow.md @@ -33,15 +33,15 @@ Interactive Q&A session to gather requirements, then formalizes into a structure ### Step 2: Scope (`/lead-dev-os:step2-scope-tasks`) -Breaks the spec into task groups with explicit context directives. Each task group declares which files from `agents-context/` to load before executing, and reads as a complete user story — a plain-language goal and "done when" definition that a non-technical stakeholder can understand and verify. Produces `tasks.md` with atomic, implementable work items. +Breaks the spec into task groups with explicit context directives. Groups follow an explicit strategy recorded in `tasks.md`: **vertical slices** (preferred — each group a thin end-to-end increment that's demoable on its own and parallelizable during implementation) or **layers** (Database → API → Frontend, when the data model is the hard part). Each task group declares which files from `agents-context/` to load before executing, and reads as a complete user story — a plain-language goal and "done when" definition that a non-technical stakeholder can understand and verify. Produces `tasks.md` with atomic, implementable work items. ### Step 3: Implement (`/lead-dev-os:step3-implement-tasks`) -Context-aware execution of task groups. The agent loads only the context it needs for each task group, implements the code, and runs tests. See [Implementation]({{ site.baseurl }}/implementation) for the three execution modes. +Context-aware execution of task groups. In autonomous modes each group runs in a fresh executor subagent while the main conversation orchestrates, verifies, and commits; independent groups can run in parallel. Execution ends with a full-test-suite backstop and a runtime check of the feature's primary flow. See [Implementation]({{ site.baseurl }}/implementation) for the three execution modes. ### Step 4: Archive (`/lead-dev-os:step4-archive-spec`) -After implementation is complete, archive the spec to keep the workspace clean. Moves the spec folder from `lead-dev-os/specs/` to `lead-dev-os/specs-archived/` and adds a deny rule to `.claude/settings.json` so the agent won't accidentally load stale specs in future sessions. +After implementation is complete, archive the spec to keep the workspace clean. Moves the spec folder from `lead-dev-os/specs/` to `lead-dev-os/specs-archived/` and adds a rule under `permissions.deny` in `.claude/settings.json` so the agent won't accidentally load stale specs in future sessions. --- diff --git a/lead-dev-os/skills/configure-project/SKILL.md b/lead-dev-os/skills/configure-project/SKILL.md index 04c592d..193d340 100644 --- a/lead-dev-os/skills/configure-project/SKILL.md +++ b/lead-dev-os/skills/configure-project/SKILL.md @@ -20,13 +20,13 @@ Check if any lead-dev-os artifacts already exist: - `lead-dev-os/specs/` directory - `CLAUDE.md` with `## lead-dev-os Framework` section -If any exist, inform the user what was found and ask: **"Some lead-dev-os artifacts already exist. Should I skip existing files (preserve your changes) or overwrite them with fresh defaults?"** +If any exist, inform the user what was found and ask: **"Some lead-dev-os artifacts already exist. Should I skip existing files (preserve your changes) or overwrite them with fresh defaults?"** Use the `AskUserQuestion` tool when available — two options: "Skip existing (Recommended)" and "Overwrite with defaults" — falling back to a plain-text question otherwise. Remember their choice — you will pass it to the setup script. ### Phase 2: Ask About Technology Stack -Ask the user: **"What technology stacks does your project use? Select all that apply:"** +Ask the user: **"What technology stacks does your project use? Select all that apply:"** Use the `AskUserQuestion` tool when available with `multiSelect: true` (one question per category below, or grouped sensibly); fall back to a plain-text list otherwise. Present these categories: - **Languages:** Python, Ruby, TypeScript, JavaScript diff --git a/lead-dev-os/skills/configure-project/scripts/setup.sh b/lead-dev-os/skills/configure-project/scripts/setup.sh index 06b2acc..bbc50d2 100755 --- a/lead-dev-os/skills/configure-project/scripts/setup.sh +++ b/lead-dev-os/skills/configure-project/scripts/setup.sh @@ -166,7 +166,12 @@ README_DEST="agents-context/README.md" if [[ -f "$README_DEST" && "$OVERWRITE" == false ]]; then SKIPPED_FILES+=("agents-context/README.md (already exists)") else - # Use bash parameter expansion for safe replacement (handles &, \, etc.) + # Use bash parameter expansion for safe replacement (handles &, \, etc.). + # bash 5.2 enables patsub_replacement by default, which expands & in the + # replacement to the matched pattern and would mangle names like "A & B"; + # disable it. Quoting the replacement instead is not portable: bash 3.2 + # (macOS default) keeps the inner quotes literally. + shopt -u patsub_replacement 2>/dev/null || true while IFS= read -r line || [[ -n "$line" ]]; do printf '%s\n' "${line//\{Project Name\}/$PROJECT_NAME}" done < "${TEMPLATES_DIR}/readme.md" > "$README_DEST" diff --git a/lead-dev-os/skills/create-or-update-concepts/SKILL.md b/lead-dev-os/skills/create-or-update-concepts/SKILL.md index 735d897..61011c6 100644 --- a/lead-dev-os/skills/create-or-update-concepts/SKILL.md +++ b/lead-dev-os/skills/create-or-update-concepts/SKILL.md @@ -109,7 +109,7 @@ For each remaining area, do a quick peek at package/config files to identify the **2b. Dispatch focused subagents per area, in parallel.** -For each significant top-level area, dispatch a codebase-analysis subagent via the Task tool. **Launch them all in a single message so they execute in parallel** — do not dispatch serially. +For each significant top-level area, dispatch a codebase-analysis subagent via the Agent tool. **Launch them all in a single message so they execute in parallel** — do not dispatch serially. Use this prompt template (fill in the area path and project root): @@ -144,7 +144,7 @@ Also answer: what questions would an AI agent need answered before making changes here? ``` -**Subagent type:** prefer `Explore` (purpose-built for codebase exploration, read-only, bounded token usage). If `codebase-analyzer` is available it's an even better fit for deep per-area analysis. Fall back to `general-purpose` if neither is available. +**Subagent type:** use `Explore` (built into Claude Code — purpose-built for codebase exploration, read-only, bounded token usage), falling back to `general-purpose` if unavailable. If the target project defines its own analysis agent (e.g. a `codebase-analyzer` under `.claude/agents/`), that's an even better fit for deep per-area analysis — but never assume one exists. Collect the reports. If any report is thin or obviously shallow (e.g. a `src/` with dozens of files summarized in one line), re-dispatch with a narrower prompt targeting the specific gap. diff --git a/lead-dev-os/skills/step1-write-spec/SKILL.md b/lead-dev-os/skills/step1-write-spec/SKILL.md index afcbfcf..57cc111 100644 --- a/lead-dev-os/skills/step1-write-spec/SKILL.md +++ b/lead-dev-os/skills/step1-write-spec/SKILL.md @@ -10,11 +10,7 @@ Interactively gather requirements for a new feature through structured Q&A, rese ## Instructions -You are a senior product engineer conducting a requirements discovery session and writing a formal specification. Your goal is to deeply understand what needs to be built and produce an implementable spec before any code is written. - -## Planning -**Use plan mode per task group when implementing** -- This will allow to further break down the task into sub-tasks and plan them out. -**When using plan mode, always include a final plan step to return to this tasks.md and check off completed tasks** (per project CLAUDE.md rule). +You are a senior product engineer conducting a requirements discovery session and writing a formal specification. Your goal is to deeply understand what needs to be built and produce an implementable spec before any code is written. This skill writes documents only — implementation planning happens later, in `/lead-dev-os:step3-implement-tasks`. ### Phase 1: Initialize @@ -51,6 +47,8 @@ Before asking questions, silently research available project context to inform y 3. **Concept-driven reusability scan** — The concept files you read describe existing patterns, conventions, and architectural decisions — and they reference source file paths. Use these to identify reusable code without blindly searching the entire codebase. Note any relevant concepts and the source paths they reference. +4. **If the scan must go beyond what the README indexes** (sparse concepts, or the feature touches undocumented territory), dispatch a read-only `Explore` subagent (fall back to `general-purpose`) with a bounded prompt — "find existing patterns, components, or modules related to [feature domain]; report paths and one-line descriptions, under 300 words" — instead of scanning the codebase in this conversation. Requirements gathering should keep the main context for the user's answers, not file dumps. + Use this context to ask smarter, more targeted questions in the next phase. Do NOT ask the user about things already documented in these files. ### Phase 3: Clarifying Questions (Round 1) @@ -145,6 +143,8 @@ Assess the feature size based on everything gathered: - **Medium** — Multiple components, some data model changes, moderate integration - **Large** — Cross-cutting concerns, significant data model changes, many integration points, multiple user flows +Record the estimate in the spec header — the `> Size:` line in `spec.md` — with a one-line rationale. `/lead-dev-os:step3-implement-tasks` reads it to recommend an execution mode. + Tell the user: - "Requirements saved to `lead-dev-os/specs/YYYY-MM-DD-/planning/requirements.md`" - "Specification saved to `lead-dev-os/specs/YYYY-MM-DD-/spec.md`" diff --git a/lead-dev-os/skills/step1-write-spec/examples/user-profile-feature.md b/lead-dev-os/skills/step1-write-spec/examples/user-profile-feature.md index 4a1d9a0..c21cbc0 100644 --- a/lead-dev-os/skills/step1-write-spec/examples/user-profile-feature.md +++ b/lead-dev-os/skills/step1-write-spec/examples/user-profile-feature.md @@ -95,6 +95,7 @@ > Spec ID: 2026-02-25-user-profile > Status: Draft > Date: 2026-02-25 +> Size: Medium — multiple components and an avatar upload flow, but no cross-cutting concerns ## Goal diff --git a/lead-dev-os/skills/step1-write-spec/template.md b/lead-dev-os/skills/step1-write-spec/template.md index e98846f..4e9c1ff 100644 --- a/lead-dev-os/skills/step1-write-spec/template.md +++ b/lead-dev-os/skills/step1-write-spec/template.md @@ -94,6 +94,7 @@ > Spec ID: YYYY-MM-DD- > Status: Draft > Date: YYYY-MM-DD +> Size: [Small | Medium | Large] — [one-line rationale] ## Goal diff --git a/lead-dev-os/skills/step2-scope-tasks/SKILL.md b/lead-dev-os/skills/step2-scope-tasks/SKILL.md index 3af8746..967c482 100644 --- a/lead-dev-os/skills/step2-scope-tasks/SKILL.md +++ b/lead-dev-os/skills/step2-scope-tasks/SKILL.md @@ -6,18 +6,16 @@ disable-model-invocation: true # Step 2: Scope Tasks -Break a specification into ordered task groups with explicit context-awareness directives. **Run this command while in plan mode.** +Break a specification into ordered task groups with explicit context-awareness directives. +Phases 1–2 are read-only research; plan mode is optional. If invoked in plan mode, present the task-group outline (groups, dependencies, grouping strategy) as the plan, and write `tasks.md` after the user approves and plan mode exits — file writes are blocked while plan mode is active. ## Instructions -You are a senior engineer breaking down a spec into implementable task groups. Each group should be an atomic focused unit of work organized by layer (database, API, frontend, etc.) with hierarchical numbered subtasks. Every task group MUST include explicit directives to read and update context files. +You are a senior engineer breaking down a spec into implementable task groups. Each group is an atomic, focused unit of work — a vertical slice of the feature or a stack layer, per the grouping strategy chosen in Phase 2 — with hierarchical numbered subtasks. Every task group MUST include explicit directives to read and update context files. Every task group MUST also stand on its own as a **complete user story** — a non-technical stakeholder who understands the feature's goal should be able to read the group and know (a) what value it delivers and (b) what "done" looks like, without reading any code or technical acceptance criteria. -## Planning -**Use plan mode per task group when implementing** -- This will allow to further break down the task into sub-tasks and plan them out. - ### Phase 1: Load Context 1. **Find the spec folder.** Look for the most recent `lead-dev-os/specs/YYYY-MM-DD-*/` folder, or ask the user which spec to work from. @@ -30,20 +28,33 @@ Every task group MUST also stand on its own as a **complete user story** — a n 5. **Read the relevant concept and standard files** identified from the README — understand domain knowledge, established patterns, and project conventions that apply to this feature. -6. **Analyze the existing codebase:** - - Identify files and modules that will be modified - - Identify patterns to follow for consistency - - Note any shared utilities or helpers to leverage +6. **Analyze the existing codebase — via research subagents, not in this conversation.** A broad codebase scan in the main context crowds out the spec and concept files you just loaded. Dispatch 1–3 read-only research subagents (prefer the `Explore` agent type; fall back to `general-purpose`) in a single parallel batch. Split by area when the feature spans several (e.g. one for backend, one for frontend). Give each a bounded prompt of this shape: + + ``` + In the project at , research what will touch in . Report under 400 words: + - Files/modules that will need modification, with paths + - Existing patterns to follow for consistency (name the exemplar files) + - Shared utilities, helpers, or components to reuse instead of rebuilding + - Anything that will constrain the task breakdown (migrations, + feature flags, cross-cutting concerns) + Do not propose a design — just report what exists. + ``` + + Use the returned reports to ground the task groups. Do not scan the repository yourself beyond the files the reports and concept files point at. ### Phase 2: Create Task Groups -Organize tasks into **sequential groups by layer**. Each group builds on the previous one. +**Choose a grouping strategy first**, and record the choice with a one-line rationale in the Overview section of `tasks.md`: + +- **Vertical slices (preferred).** Each group is a thin end-to-end increment — data + logic + UI for one user-visible capability. Choose this whenever the feature decomposes into independently verifiable increments. Two payoffs: every group is demoable at a review gate, and slice groups with no mutual dependency and disjoint file sets can be executed in parallel by `/lead-dev-os:step3-implement-tasks`. Example slices for a profile feature: + 1. **View profile** — read path end-to-end (model, endpoint, page) + 2. **Edit profile** — write path end-to-end (validations, update endpoint, form) + 3. **Avatar upload** — upload flow end-to-end + 4. **Testing** — test review & gap analysis (always last) +- **Layers.** Groups follow the stack: Database → API → Frontend → Testing. Choose this when the data model is the hard part, the feature lives in a single layer, or slices would all contend for the same few files. Trade-off: layer groups form a strict dependency chain — they always execute sequentially, and nothing is user-demoable until the top layer lands. -Common layer ordering (adapt based on the feature): -1. **Database Layer** — Schema, models, migrations -2. **API Layer** — Endpoints, controllers, services -3. **Frontend Components** — Components, pages, styling -4. **Testing** — Test review & gap analysis (always last) +Whichever strategy you choose, keep each group's `Dependencies:` list minimal and honest — over-declared dependencies serialize execution for no reason. Each task group uses **hierarchical numbered subtasks**. The parent task (N.0) is the group's completion goal. Subtasks (N.1, N.2, ...) are the steps to achieve it. @@ -52,7 +63,7 @@ For each task group (except the final Testing group), follow a **test-first appr - The final subtask is ALWAYS ensuring those tests pass - Tests should cover only critical behaviors, not exhaustive scenarios -The **final task group is always "Test Review & Gap Analysis"**. This group reviews all tests from previous groups, identifies critical gaps, and adds up to 10 additional strategic tests. It does NOT run the entire application test suite — only feature-specific tests. +The **final task group is always "Test Review & Gap Analysis"**. This group reviews all tests from previous groups, identifies critical gaps, and adds up to 10 additional strategic tests. Its last subtask is the workflow's **single full-test-suite backstop run** — the only point where the entire suite runs, to catch regressions the feature introduced elsewhere. New failures caused by the feature get fixed; pre-existing failures get reported, not fixed. ### Phase 3: Generate Tasks Document @@ -113,7 +124,7 @@ If a relevant concept or standard file does NOT yet exist, the directive should ### Rules for Task Groups -- Organize groups under **layer sections** (### headings) with task groups as #### headings +- Organize groups under **theme sections** (### headings — a slice name or a layer name, per the chosen strategy) with task groups as #### headings - Use **hierarchical numbered subtasks** (N.0 parent, N.1, N.2, ... children) - Subtask N.1 is always **writing 2-8 focused tests** — test only critical behaviors, not exhaustive scenarios - The final subtask in each group is always **ensuring those specific tests pass** — never run the full test suite @@ -121,6 +132,7 @@ If a relevant concept or standard file does NOT yet exist, the directive should - Each task group MUST have a **1-2 sentence description** immediately after the title — describe WHAT will be delivered, not HOW - Each task group MUST include a **User Story** and a **Done when (plain language)** block immediately after the description (before "Read before starting"): - The **User Story** uses the form *"As a [persona], I want [outcome] so that [benefit]."* It must be written for a non-technical reader and framed around end-user value — even for backend layers (database, API, services), express the value the layer ultimately enables for a person, not the technical artifact + - Exception: a pure enabling group (infrastructure or plumbing with no user-visible outcome of its own) MAY instead declare `**User Story:** Enables Group N's story by [one line]` — don't fabricate a persona for plumbing. Vertical-slice groups always get a full story - **Done when (plain language)** is a short bulleted list of observable, jargon-free outcomes a non-technical stakeholder could confirm. It is the human-readable counterpart to the technical Acceptance Criteria, not a duplicate of it (no test counts, file names, or framework terms) - Each task should be **completable in one focused session** - Tasks must reference **specific files** to create or modify where possible @@ -128,7 +140,7 @@ If a relevant concept or standard file does NOT yet exist, the directive should - The **"Update after completing"** section MUST specify which concept files to update or create when new patterns are established - Groups must have explicit **dependency ordering** - Context directives reference **general guidance, not code** — concept files describe approaches, conventions, and decision rationale, never code snippets -- The **final group is always "Test Review & Gap Analysis"** — reviews previous tests, fills critical gaps (up to 10 additional tests), runs only feature-specific tests +- The **final group is always "Test Review & Gap Analysis"** — reviews previous tests, fills critical gaps (up to 10 additional tests), runs feature-specific tests, then runs the full test suite ONCE as a final backstop (fix new failures, report pre-existing ones) - Include an **Execution Order** section at the end listing the recommended implementation sequence - Include an **Overview** section at the top with total task count @@ -137,7 +149,7 @@ If a relevant concept or standard file does NOT yet exist, the directive should Display the following message to the user: ``` -The tasks list has created at `lead-dev-os/specs/YYYY-MM-DD-/tasks.md`. +The tasks list has been created at `lead-dev-os/specs/YYYY-MM-DD-/tasks.md`. Review it closely to make sure it all looks good. diff --git a/lead-dev-os/skills/step2-scope-tasks/examples/user-profile-feature.md b/lead-dev-os/skills/step2-scope-tasks/examples/user-profile-feature.md index fcb23c0..c822a96 100644 --- a/lead-dev-os/skills/step2-scope-tasks/examples/user-profile-feature.md +++ b/lead-dev-os/skills/step2-scope-tasks/examples/user-profile-feature.md @@ -6,6 +6,7 @@ ## Overview Total Tasks: 20 +Grouping strategy: Layers — the profile data model is the hard part; the UI is a thin page over it ## Context Management @@ -21,8 +22,12 @@ Update relevant concepts in `agents-context/README.md` if applicable: - Add cross-references to related concepts ## Planning -**Use plan mode per task group when implementing** - This will allow further breakdown of each task into sub-tasks and plan them out. -**When using plan mode, always include a final plan step to return to this tasks.md and check off completed tasks** (per project CLAUDE.md rule). +Every task group is planned before its code is written — see `/lead-dev-os:step3-implement-tasks`: +- **A and H modes:** plans are pre-generated as `plans/group-N.md` files by parallel planner subagents and batch-approved before execution. +- **L mode:** each group is planned in Claude Code's native plan mode at the start of the group. + +Whoever executes a group must check off its tasks in this file as each task completes, not in a batch at the end. + ## Task List ### Data & API Layer @@ -215,17 +220,20 @@ As the product owner, I want confidence that the whole profile feature works fro - Focus on: full profile edit flow (load → edit → save → reload → verify), avatar upload end-to-end - Do NOT write comprehensive coverage for all scenarios - Skip edge cases, performance tests, and accessibility tests unless business-critical - - [ ] 4.4 Run feature-specific tests only - - Run ONLY tests related to the profile feature (tests from 1.1, 2.1, 3.1, and 4.3) + - [ ] 4.4 Run feature-specific tests + - Run tests related to the profile feature (tests from 1.1, 2.1, 3.1, and 4.3) - Expected total: approximately 16-34 tests maximum - - Do NOT run the entire application test suite - Verify critical workflows pass + - [ ] 4.5 Run the full test suite once as a final backstop + - This is the ONLY full-suite run in the workflow — it catches regressions the profile feature introduced elsewhere in the app + - Fix any NEW failures caused by the profile feature + - Report pre-existing failures without fixing them (out of scope for this spec) **Acceptance Criteria:** - All feature-specific tests pass (approximately 16-34 tests total) - Critical user workflows for profile feature are covered - No more than 10 additional tests added when filling gaps -- Testing focused exclusively on profile feature requirements +- The full-suite backstop run introduces no new failures (pre-existing failures are reported, not fixed) ## Execution Order diff --git a/lead-dev-os/skills/step2-scope-tasks/template.md b/lead-dev-os/skills/step2-scope-tasks/template.md index 8fc7c13..296a652 100644 --- a/lead-dev-os/skills/step2-scope-tasks/template.md +++ b/lead-dev-os/skills/step2-scope-tasks/template.md @@ -6,6 +6,7 @@ ## Overview Total Tasks: [count] +Grouping strategy: [Vertical slices | Layers] — [one-line rationale] [brief summary of the task] @@ -23,12 +24,15 @@ Update relevant concepts in `agents-context/README.md` if applicable: - Add cross-references to related concepts ## Planning -**Use plan mode per task group when implementing** - This will allow further breakdown of each task into sub-tasks and plan them out. -**When using plan mode, always include a final plan step to return to this tasks.md and check off completed tasks** (per project CLAUDE.md rule). +Every task group is planned before its code is written — see `/lead-dev-os:step3-implement-tasks`: +- **A and H modes:** plans are pre-generated as `plans/group-N.md` files by parallel planner subagents and batch-approved before execution. +- **L mode:** each group is planned in Claude Code's native plan mode at the start of the group. + +Whoever executes a group must check off its tasks in this file as each task completes, not in a batch at the end. ## Task List -### [Layer Name — e.g., Database Layer] +### [Group Theme — e.g., "View profile" slice, or Database Layer] #### Task Group 1: [Group Name — e.g., Data Models and Migrations] @@ -52,7 +56,7 @@ As a [persona], I want [outcome in plain language] so that [benefit]. [For backe **Dependencies:** None -- [ ] 1.0 Complete [layer name] +- [ ] 1.0 Complete [group theme] - [ ] 1.1 Write 2-8 focused tests for [subject] functionality - Limit to 2-8 highly focused tests maximum - Test only critical behaviors (e.g., primary validation, key association, core method) @@ -71,7 +75,7 @@ As a [persona], I want [outcome in plain language] so that [benefit]. [For backe - [Criterion specific to this group] - [Criterion specific to this group] -### [Layer Name — e.g., API Layer] +### [Group Theme — e.g., "Edit profile" slice, or API Layer] #### Task Group 2: [Group Name — e.g., API Endpoints] @@ -94,7 +98,7 @@ As a [persona], I want [outcome in plain language] so that [benefit]. [For backe **Dependencies:** Task Group 1 -- [ ] 2.0 Complete [layer name] +- [ ] 2.0 Complete [group theme] - [ ] 2.1 Write 2-8 focused tests for [subject] - Limit to 2-8 highly focused tests maximum - Test only critical actions (e.g., primary CRUD operation, auth check, key error case) @@ -112,7 +116,7 @@ As a [persona], I want [outcome in plain language] so that [benefit]. [For backe - [Criterion specific to this group] - [Criterion specific to this group] -### [Layer Name — e.g., Frontend Components] +### [Group Theme — e.g., "Avatar upload" slice, or Frontend Components] #### Task Group 3: [Group Name — e.g., UI Design] @@ -134,7 +138,7 @@ As a [persona], I want [outcome in plain language] so that [benefit]. **Dependencies:** Task Group 2 -- [ ] 3.0 Complete [layer name] +- [ ] 3.0 Complete [group theme] - [ ] 3.1 Write 2-8 focused tests for [subject] - Limit to 2-8 highly focused tests maximum - Test only critical component behaviors (e.g., primary user interaction, key form submission, main rendering case) @@ -188,21 +192,24 @@ As a [product owner / the team], I want confidence that the whole feature works - Focus on integration points and end-to-end workflows - Do NOT write comprehensive coverage for all scenarios - Skip edge cases, performance tests, and accessibility tests unless business-critical - - [ ] 4.4 Run feature-specific tests only - - Run ONLY tests related to this spec's feature - - Do NOT run the entire application test suite + - [ ] 4.4 Run feature-specific tests + - Run tests related to this spec's feature (from N.1 subtasks and 4.3) - Verify critical workflows pass + - [ ] 4.5 Run the full test suite once as a final backstop + - This is the ONLY full-suite run in the workflow — it catches regressions this feature introduced elsewhere in the app + - Fix any NEW failures caused by this feature + - Report pre-existing failures without fixing them (out of scope for this spec) **Acceptance Criteria:** - All feature-specific tests pass - Critical user workflows for this feature are covered - No more than 10 additional tests added when filling gaps -- Testing focused exclusively on this spec's feature requirements +- The full-suite backstop run introduces no new failures (pre-existing failures are reported, not fixed) ## Execution Order Recommended implementation sequence: -1. [Layer Name] (Task Group 1) -2. [Layer Name] (Task Group 2) -3. [Layer Name] (Task Group 3) +1. [Group Theme] (Task Group 1) +2. [Group Theme] (Task Group 2) +3. [Group Theme] (Task Group 3) 4. Test Review & Gap Analysis (Task Group 4) diff --git a/lead-dev-os/skills/step3-implement-tasks/SKILL.md b/lead-dev-os/skills/step3-implement-tasks/SKILL.md index e403b71..b0b4779 100644 --- a/lead-dev-os/skills/step3-implement-tasks/SKILL.md +++ b/lead-dev-os/skills/step3-implement-tasks/SKILL.md @@ -27,17 +27,19 @@ You are a senior engineer implementing a feature from a scoped task breakdown. W ### Phase 2: Select Execution Mode -Present the three modes and ask which to use. +Read the `> Size:` line from `spec.md` (written by `/lead-dev-os:step1-write-spec`) and derive a recommendation: **Small → A**, **Medium → H**, **Large → L**. If the line is absent, make your own quick size assessment from `tasks.md` (group count, integration points) and say so. + +Present the three modes with your recommendation first, and ask which to use — the user decides. | Mode | Behavior | Best for | |------|----------|----------| -| **A — Autonomous** | Pre-plan all groups in parallel → user approves batch → execute all groups sequentially with auto-commit per group. No pauses during execution. | Small features, well-understood domains, low risk. | -| **L — Lead-in-the-Loop** | Per-group cycle: plan (native plan mode) → user approves → execute → review gate → repeat. | Complex features, new domains, high visibility. | -| **H — Hybrid** | Pre-plan all groups in parallel → user approves batch → auto-execute up to the checkpoint → at and after the checkpoint, switch to L behavior. | Boilerplate setup followed by tricky logic. | +| **A — Autonomous** | Pre-plan all groups in parallel → user approves batch → each group is executed by a fresh executor subagent; the orchestrator verifies and commits per group. Independent groups may run in parallel. No pauses during execution. | Small features, well-understood domains, low risk. | +| **L — Lead-in-the-Loop** | Per-group cycle in the main conversation: plan (native plan mode) → user approves → execute → review gate → repeat. | Complex features, new domains, high visibility. | +| **H — Hybrid** | Pre-plan all groups in parallel → user approves batch → orchestrated executor-subagent execution up to the checkpoint → at and after the checkpoint, switch to L behavior. | Boilerplate setup followed by tricky logic. | -Ask: **"Which execution mode? (A / L / H)"** +Ask **"Which execution mode?"** using the `AskUserQuestion` tool when available (options A / L / H with one-line descriptions, the recommended mode listed first and labeled "(Recommended)"); fall back to a plain-text question otherwise. -If the user picks **H**, also ask: **"Which group is the checkpoint?"** Autonomous execution runs up to but not including that group; L behavior begins at that group. +If the user picks **H**, also ask: **"Which group is the checkpoint?"** — again via `AskUserQuestion`, offering the task groups as options. Autonomous execution runs up to but not including that group; L behavior begins at that group. Store the mode (and checkpoint) for the rest of the session. @@ -97,7 +99,82 @@ Wait for explicit "go" before proceeding to Phase 4. Do not start executing on y ### Phase 4: Execute Task Groups -For each incomplete task group, in dependency order, run this sub-cycle. +There are two execution paths: + +- **Orchestrated — A mode, and H before the checkpoint.** The main conversation acts as an orchestrator: each group is executed by a fresh executor subagent, then the orchestrator verifies and commits. This keeps the main context small regardless of how many groups the feature has — group 6 gets the same quality of attention as group 1. Follow **4-O**. +- **Direct — L mode, and H at/after the checkpoint.** The main conversation executes the group itself so the user can watch and steer. Follow **4a–4g**. + +#### 4-O. Orchestrated execution (A, and H pre-checkpoint) + +Work through incomplete groups in dependency order: + +1. **Dispatch an executor subagent per group** (`subagent_type: "general-purpose"`) using the prompt template below. Each executor starts with a fresh context and reads everything it needs from disk — never assume it inherits knowledge from this conversation. + +2. **Parallel dispatch (optional).** Two or more groups may be dispatched in the same batch only when BOTH hold: (a) neither depends on the other, directly or transitively, per the `Dependencies:` headers, and (b) their plans' "File operations" lists don't overlap. When in doubt, dispatch sequentially — a serialized group is cheaper than a merge conflict. + +3. **Verify — trust but verify.** When an executor returns, run the group's verification command from `plans/group-.md` yourself. Do not take the executor's report at face value. + +4. **Review the diff** briefly for scope creep, deleted tests, or weakened assertions. + +5. **Commit.** Executors never commit — the orchestrator commits after verifying, per 4f. When groups ran in parallel, stage each group's files separately (use the plan's "File operations" list) so each group still gets its own atomic commit. + +6. **Report and continue** (A-mode gate): + - Group N complete + - Tests written / passing + - Concept files created or updated + - Next group(s) queued + Then dispatch the next group(s). In H mode, when the checkpoint group is reached, switch to the direct path (4a–4g) with L behavior. + +If an executor reports a blocker, plan-invalidating drift, or exhausted retries, stop and apply the Error Handling section — surface it to the user; don't redispatch blindly. + +**Executor prompt template:** + +``` +You are executing Task Group of /tasks.md as part of +/lead-dev-os:step3-implement-tasks. + +Read first, in this order: +- /plans/group-.md — your working plan +- /tasks.md — Group N's section (subtasks + acceptance criteria) +- agents-context/README.md, then every file Group N's "Read before + starting" header names +- Every file the plan's "File operations" section says you'll modify + +Reconcile before you code: earlier groups may have changed the code since +this plan was written. If reality has drifted (files moved, signatures +changed, patterns replaced), update plans/group-.md to match reality +first and note the drift in your final report. If the drift invalidates +the group's goal, stop and report instead of improvising. + +Then execute the group: +1. Tests first — write the group's tests before implementation; they + should fail before the code that satisfies them exists. +2. Implement to make them pass, following the conventions from the loaded + context files. Stay within Group N's scope — no scope creep. +3. Verify with the plan's verification command. Run ONLY this group's + tests, not the entire suite. +4. If a test fails: diagnose, fix the most likely cause, re-run. Retry + limit: 2 attempts. Never delete tests, weaken assertions, or skip + behavior to get green — report the failure instead. +5. Check off completed tasks in tasks.md as each one finishes, not in a + batch at the end. +6. Update context: create or update the concept files named in the + group's "Update after completing" header; keep agents-context/README.md + in sync (index entry, Load-When Cheatsheet, cross-references). + +Do NOT commit — the orchestrator commits after verifying your work. + +Final report (structured): +- Tests written / passing (counts and file paths) +- Files created / modified / deleted +- Concept files created or updated +- Plan drift found and how the plan was amended (if any) +- Blockers or open questions (if any) +``` + +#### Direct execution (L, and H at/after checkpoint) + +For each incomplete task group, in dependency order, run the 4a–4g sub-cycle in the main conversation. #### 4a. Load Context @@ -107,7 +184,7 @@ For each incomplete task group, in dependency order, run this sub-cycle. #### 4b. Plan -- **A and H modes:** read `plans/group-.md`. Treat it as the working plan. The user approved the batch in Phase 3, but they may also have edited the file — read it fresh. +- **H mode (at/after the checkpoint):** read `plans/group-.md`. Treat it as the working plan. The user approved the batch in Phase 3, but they may also have edited the file — read it fresh. If earlier groups changed the code in ways the plan didn't anticipate, update the plan file to match reality before executing, and tell the user what drifted. - **L mode:** enter Claude Code's native plan mode and produce a plan with the same structure as Phase 3 (Goal, Sub-tasks, File ops, Test approach, Verification, Risks). In the plan header, include this identifier so the agent stays aware of its workflow context after `ExitPlanMode` clears the conversation: "Running as part of `/lead-dev-os:step3-implement-tasks` for `/tasks.md`, task group N. Final step: return to tasks.md and check off completed tasks." Wait for the user to approve via `ExitPlanMode` before continuing. #### 4c. Execute @@ -155,20 +232,10 @@ Atomic-commit policy: In **L** mode you may skip the auto-commit and let the user commit manually after the review gate (4g). -#### 4g. Mode-specific Gate - -After the group is complete: +#### 4g. Review Gate -**A (Autonomous)** -1. Auto-commit per 4f. -2. Report briefly: - - Group N complete - - Tests written / passing - - Concept files created or updated - - Next group queued -3. Proceed immediately to the next group (back to 4a). +After the group is complete (this gate applies to the direct path — L mode, and H at/after the checkpoint; A mode's gate is 4-O step 6): -**L (Lead-in-the-Loop)** 1. Report: - Group N complete - Tests written / passing @@ -183,13 +250,11 @@ After the group is complete: > - ➡️ Say "continue" to proceed to the next group 3. Wait for explicit instruction. Do not advance until the user says to continue. -**H (Hybrid)** -- Before the checkpoint group: behave as A (auto-commit, brief report, proceed). -- At and after the checkpoint group: behave as L (report, review gate, wait). - #### After all groups complete Regardless of mode: +- **Run the full test suite once** (if the final task group's backstop subtask didn't already). This is the only full-suite run in the workflow. Triage failures: fix NEW failures this feature caused; report pre-existing failures without fixing them. +- **Verify at runtime.** Tests passing is not the same as the feature working — exercise the feature's primary user flow in the running app where feasible (start the app, hit the endpoint, click through the UI) and confirm the observable behavior matches the spec's acceptance criteria. If runtime verification isn't feasible, say so explicitly rather than skipping silently. - Confirm every "Acceptance Criteria" block in `tasks.md` is satisfied. - List concept files created or updated during execution. - Summarize what was built. diff --git a/lead-dev-os/skills/step4-archive-spec/SKILL.md b/lead-dev-os/skills/step4-archive-spec/SKILL.md index 6cee5c2..bc952f6 100644 --- a/lead-dev-os/skills/step4-archive-spec/SKILL.md +++ b/lead-dev-os/skills/step4-archive-spec/SKILL.md @@ -23,6 +23,8 @@ You are a project maintenance assistant. After a feature has been fully implemen 4. **If multiple specs exist**, list them sorted by date prefix (newest first) and ask: **"Which spec would you like to archive?"** +For both questions, use the `AskUserQuestion` tool when available (spec names as options); fall back to plain text otherwise. + ### Phase 2: Archive Spec 1. **Create the archive directory** `lead-dev-os/specs-archived/` if it doesn't exist. @@ -39,7 +41,7 @@ Run the bundled settings script to add a deny rule that blocks agent access to a bash /scripts/update-settings.sh "$(pwd)" ``` -This adds `"Read(/lead-dev-os/specs-archived/**)"` to the deny array in `.claude/settings.json`. The script is idempotent — it skips if the rule already exists. +This adds `"Read(/lead-dev-os/specs-archived/**)"` under `permissions.deny` in `.claude/settings.json` (the only location Claude Code reads deny rules from). The script is idempotent — it skips if the rule already exists, and migrates the rule out of a legacy top-level `deny` array if an older version of this script put it there. Report the script output to the user. diff --git a/lead-dev-os/skills/step4-archive-spec/scripts/update-settings.sh b/lead-dev-os/skills/step4-archive-spec/scripts/update-settings.sh index 29b1866..5839295 100755 --- a/lead-dev-os/skills/step4-archive-spec/scripts/update-settings.sh +++ b/lead-dev-os/skills/step4-archive-spec/scripts/update-settings.sh @@ -2,9 +2,13 @@ # # update-settings.sh — Add a deny rule for archived specs to .claude/settings.json # +# The rule is added under permissions.deny (the only location Claude Code +# reads deny rules from). Also migrates the rule out of a legacy top-level +# "deny" array if a previous version of this script put it there. +# # Usage: bash update-settings.sh [project-root] # -# Idempotent: skips if the rule already exists. +# Idempotent: skips if the rule already exists under permissions.deny. set -euo pipefail @@ -27,29 +31,41 @@ fi # Case 1: settings.json doesn't exist — create it if [ ! -f "$SETTINGS_FILE" ]; then - jq -n --arg rule "$DENY_RULE" '{"deny": [$rule]}' > "$SETTINGS_FILE" + jq -n --arg rule "$DENY_RULE" '{"permissions": {"deny": [$rule]}}' > "$SETTINGS_FILE" echo "Created ${SETTINGS_FILE} with deny rule: ${DENY_RULE}" exit 0 fi -# Case 2: settings.json exists — check for deny rule EXISTING=$(cat "$SETTINGS_FILE") -# Check if rule already exists +# Migration: a previous version of this script wrote the rule to a top-level +# "deny" array, which Claude Code ignores. Remove our rule from there (leave +# any other top-level entries untouched — they aren't ours). +MIGRATED=false if echo "$EXISTING" | jq -e --arg rule "$DENY_RULE" '.deny // [] | index($rule) != null' &>/dev/null; then - echo "Deny rule already exists in ${SETTINGS_FILE} — skipping." - exit 0 + EXISTING=$(echo "$EXISTING" | jq --arg rule "$DENY_RULE" '.deny -= [$rule] | if .deny == [] then del(.deny) else . end') + MIGRATED=true fi -# Case 3: deny array exists but rule is missing — append -if echo "$EXISTING" | jq -e '.deny' &>/dev/null; then - echo "$EXISTING" | jq --arg rule "$DENY_RULE" '.deny += [$rule]' > "${SETTINGS_FILE}.tmp" - mv "${SETTINGS_FILE}.tmp" "$SETTINGS_FILE" - echo "Added deny rule to existing array: ${DENY_RULE}" +# Check if rule already exists under permissions.deny +if echo "$EXISTING" | jq -e --arg rule "$DENY_RULE" '.permissions.deny // [] | index($rule) != null' &>/dev/null; then + if [ "$MIGRATED" = true ]; then + echo "$EXISTING" > "${SETTINGS_FILE}.tmp" + mv "${SETTINGS_FILE}.tmp" "$SETTINGS_FILE" + echo "Removed legacy top-level deny rule; rule already exists under permissions.deny in ${SETTINGS_FILE}." + else + echo "Deny rule already exists in ${SETTINGS_FILE} — skipping." + fi exit 0 fi -# Case 4: no deny array — add one -echo "$EXISTING" | jq --arg rule "$DENY_RULE" '. + {"deny": [$rule]}' > "${SETTINGS_FILE}.tmp" +# Add the rule under permissions.deny, creating the objects as needed and +# preserving every other key. +echo "$EXISTING" | jq --arg rule "$DENY_RULE" '.permissions.deny = ((.permissions.deny // []) + [$rule])' > "${SETTINGS_FILE}.tmp" mv "${SETTINGS_FILE}.tmp" "$SETTINGS_FILE" -echo "Added deny array with rule: ${DENY_RULE}" + +if [ "$MIGRATED" = true ]; then + echo "Migrated legacy top-level deny rule to permissions.deny: ${DENY_RULE}" +else + echo "Added deny rule to permissions.deny: ${DENY_RULE}" +fi diff --git a/tests/test_skill_content.sh b/tests/test_skill_content.sh index 36f6668..98b1fdb 100755 --- a/tests/test_skill_content.sh +++ b/tests/test_skill_content.sh @@ -130,25 +130,90 @@ for skill_md in "$PLUGIN_DIR"/skills/*/SKILL.md; do fi done -# --- Plan mode content present in tactical skills --- +# --- Plan mode content lives where it belongs --- echo "" echo "Plan mode content:" -TACTICAL_SKILLS=( - "step1-write-spec" - "step2-scope-tasks" - "step3-implement-tasks" -) +# step3 owns implementation planning: pre-generated plan files + native plan mode +step3_md="$PLUGIN_DIR/skills/step3-implement-tasks/SKILL.md" +if grep -q 'plans/group-' "$step3_md" 2>/dev/null && grep -qi 'plan mode' "$step3_md" 2>/dev/null; then + pass "step3-implement-tasks/SKILL.md has per-group planning (plan files + plan mode)" +else + fail "step3-implement-tasks/SKILL.md missing per-group planning instructions" +fi -for skill in "${TACTICAL_SKILLS[@]}"; do - skill_md="$PLUGIN_DIR/skills/$skill/SKILL.md" - if grep -q 'Use plan mode per task group' "$skill_md" 2>/dev/null; then - pass "$skill/SKILL.md has plan mode instructions" - else - fail "$skill/SKILL.md missing plan mode instructions" - fi -done +# step1 is spec-writing only — must NOT carry implementation plan-mode boilerplate +step1_md="$PLUGIN_DIR/skills/step1-write-spec/SKILL.md" +if grep -q 'Use plan mode per task group' "$step1_md" 2>/dev/null; then + fail "step1-write-spec/SKILL.md carries step3's plan-mode boilerplate" +else + pass "step1-write-spec/SKILL.md free of misplaced plan-mode boilerplate" +fi + +# step2 explains plan-mode-optional invocation (writes tasks.md after plan approval) +step2_md="$PLUGIN_DIR/skills/step2-scope-tasks/SKILL.md" +if grep -q 'plan mode is optional' "$step2_md" 2>/dev/null; then + pass "step2-scope-tasks/SKILL.md documents plan-mode-optional invocation" +else + fail "step2-scope-tasks/SKILL.md missing plan-mode-optional note" +fi + +# --- step3: orchestrated execution via executor subagents --- + +echo "" +echo "step3 orchestrated execution:" + +STEP3_MD="$PLUGIN_DIR/skills/step3-implement-tasks/SKILL.md" + +if grep -q 'executor subagent' "$STEP3_MD" 2>/dev/null; then + pass "step3 delegates group execution to executor subagents" +else + fail "step3 missing executor subagent delegation" +fi + +if grep -q 'the orchestrator commits after verifying' "$STEP3_MD" 2>/dev/null; then + pass "step3 executors never commit — orchestrator verifies then commits" +else + fail "step3 missing orchestrator verify-then-commit rule" +fi + +if grep -q 'Reconcile before you code' "$STEP3_MD" 2>/dev/null; then + pass "step3 executor prompt reconciles stale plans against reality" +else + fail "step3 executor prompt missing stale-plan reconcile instruction" +fi + +if grep -q 'Parallel dispatch' "$STEP3_MD" 2>/dev/null; then + pass "step3 allows parallel dispatch of independent groups" +else + fail "step3 missing parallel dispatch of independent groups" +fi + +# --- Full-suite backstop gate --- + +echo "" +echo "Full-suite backstop gate:" + +STEP2_TEMPLATE="$PLUGIN_DIR/skills/step2-scope-tasks/template.md" + +if grep -q 'Run the full test suite once as a final backstop' "$STEP2_TEMPLATE" 2>/dev/null; then + pass "step2 template final group includes full-suite backstop subtask" +else + fail "step2 template missing full-suite backstop subtask" +fi + +if grep -q 'Run the full test suite once' "$STEP3_MD" 2>/dev/null; then + pass "step3 after-all-groups includes full-suite run" +else + fail "step3 missing full-suite run after all groups" +fi + +if grep -q 'Verify at runtime' "$STEP3_MD" 2>/dev/null; then + pass "step3 includes runtime verification of the feature" +else + fail "step3 missing runtime verification step" +fi # --- Summary --- diff --git a/tests/test_update_settings.sh b/tests/test_update_settings.sh index c6b8652..070cea3 100755 --- a/tests/test_update_settings.sh +++ b/tests/test_update_settings.sh @@ -2,11 +2,12 @@ # Test: update-settings.sh behaves correctly across all cases # # Verifies: -# - Creates .claude/settings.json from scratch with deny rule -# - Adds deny array to existing settings without one -# - Appends rule to existing deny array +# - Creates .claude/settings.json from scratch with the rule under permissions.deny +# - Adds permissions.deny to existing settings without one +# - Appends rule to existing permissions.deny array # - Skips when rule already exists (idempotent) # - Preserves existing settings keys and deny rules +# - Migrates the rule out of a legacy top-level "deny" array set -euo pipefail @@ -40,9 +41,9 @@ read_settings() { cat "$TMP_DIR/.claude/settings.json" } -# Helper: check if deny rule exists in settings +# Helper: check if deny rule exists under permissions.deny has_deny_rule() { - read_settings | jq -e --arg rule "$DENY_RULE" '.deny | index($rule) != null' &>/dev/null + read_settings | jq -e --arg rule "$DENY_RULE" '.permissions.deny // [] | index($rule) != null' &>/dev/null } echo "update-settings.sh Tests" @@ -95,15 +96,21 @@ else fi if has_deny_rule; then - pass "deny rule present" + pass "deny rule present under permissions.deny" else - fail "deny rule missing" + fail "deny rule missing from permissions.deny" +fi + +if read_settings | jq -e '.permissions.deny | length == 1' &>/dev/null; then + pass "permissions.deny has exactly 1 entry" +else + fail "permissions.deny has unexpected length" fi -if read_settings | jq -e '.deny | length == 1' &>/dev/null; then - pass "deny array has exactly 1 entry" +if read_settings | jq -e 'has("deny") | not' &>/dev/null; then + pass "no top-level deny key created" else - fail "deny array has unexpected length" + fail "top-level deny key created (Claude Code ignores it)" fi if echo "$output" | grep -q "Created"; then @@ -114,27 +121,27 @@ fi teardown -# --- Case 2: .claude/settings.json exists without deny array --- +# --- Case 2: .claude/settings.json exists without permissions key --- echo "" -echo "Case 2: Existing settings without deny array:" +echo "Case 2: Existing settings without permissions key:" setup mkdir -p "$TMP_DIR/.claude" -echo '{"allow": ["Read(**)"], "timeout": 30}' > "$TMP_DIR/.claude/settings.json" +echo '{"env": {"FOO": "bar"}, "timeout": 30}' > "$TMP_DIR/.claude/settings.json" run_script >/dev/null if has_deny_rule; then - pass "deny rule added" + pass "deny rule added under permissions.deny" else fail "deny rule missing" fi -if read_settings | jq -e '.allow[0] == "Read(**)"' &>/dev/null; then - pass "preserved existing allow array" +if read_settings | jq -e '.env.FOO == "bar"' &>/dev/null; then + pass "preserved existing env key" else - fail "existing allow array lost" + fail "existing env key lost" fi if read_settings | jq -e '.timeout == 30' &>/dev/null; then @@ -145,14 +152,14 @@ fi teardown -# --- Case 3: Existing deny array without the rule --- +# --- Case 3: Existing permissions.deny array without the rule --- echo "" -echo "Case 3: Existing deny array without archive rule:" +echo "Case 3: Existing permissions.deny without archive rule:" setup mkdir -p "$TMP_DIR/.claude" -echo '{"deny": ["Write(/secrets/**)"]}' > "$TMP_DIR/.claude/settings.json" +echo '{"permissions": {"allow": ["Read(**)"], "deny": ["Write(/secrets/**)"]}}' > "$TMP_DIR/.claude/settings.json" run_script >/dev/null @@ -162,18 +169,24 @@ else fail "deny rule missing" fi -if read_settings | jq -e '.deny | length == 2' &>/dev/null; then - pass "deny array has 2 entries" +if read_settings | jq -e '.permissions.deny | length == 2' &>/dev/null; then + pass "permissions.deny has 2 entries" else - fail "deny array has unexpected length (expected 2)" + fail "permissions.deny has unexpected length (expected 2)" fi -if read_settings | jq -e '.deny[0] == "Write(/secrets/**)"' &>/dev/null; then +if read_settings | jq -e '.permissions.deny[0] == "Write(/secrets/**)"' &>/dev/null; then pass "preserved existing deny rule" else fail "existing deny rule lost" fi +if read_settings | jq -e '.permissions.allow[0] == "Read(**)"' &>/dev/null; then + pass "preserved existing permissions.allow" +else + fail "existing permissions.allow lost" +fi + teardown # --- Case 4: Rule already exists (idempotent) --- @@ -183,14 +196,14 @@ echo "Case 4: Rule already exists (idempotent):" setup mkdir -p "$TMP_DIR/.claude" -jq -n --arg rule "$DENY_RULE" '{"deny": [$rule]}' > "$TMP_DIR/.claude/settings.json" +jq -n --arg rule "$DENY_RULE" '{"permissions": {"deny": [$rule]}}' > "$TMP_DIR/.claude/settings.json" output=$(run_script) -if read_settings | jq -e '.deny | length == 1' &>/dev/null; then - pass "deny array still has exactly 1 entry (no duplicate)" +if read_settings | jq -e '.permissions.deny | length == 1' &>/dev/null; then + pass "permissions.deny still has exactly 1 entry (no duplicate)" else - fail "deny array has unexpected length (duplicate added?)" + fail "permissions.deny has unexpected length (duplicate added?)" fi if echo "$output" | grep -q "already exists"; then @@ -208,22 +221,87 @@ echo "Case 5: Idempotent with other deny rules present:" setup mkdir -p "$TMP_DIR/.claude" -jq -n --arg rule "$DENY_RULE" '{"deny": ["Write(/secrets/**)", $rule, "Bash(rm -rf *)"]}' > "$TMP_DIR/.claude/settings.json" +jq -n --arg rule "$DENY_RULE" '{"permissions": {"deny": ["Write(/secrets/**)", $rule, "Bash(rm -rf *)"]}}' > "$TMP_DIR/.claude/settings.json" run_script >/dev/null -if read_settings | jq -e '.deny | length == 3' &>/dev/null; then - pass "deny array unchanged (3 entries)" +if read_settings | jq -e '.permissions.deny | length == 3' &>/dev/null; then + pass "permissions.deny unchanged (3 entries)" +else + fail "permissions.deny modified unexpectedly" +fi + +teardown + +# --- Case 6: Legacy migration — rule in top-level deny array --- + +echo "" +echo "Case 6: Migrates rule from legacy top-level deny:" + +setup +mkdir -p "$TMP_DIR/.claude" +jq -n --arg rule "$DENY_RULE" '{"deny": [$rule]}' > "$TMP_DIR/.claude/settings.json" + +output=$(run_script) + +if has_deny_rule; then + pass "rule now under permissions.deny" +else + fail "rule missing from permissions.deny after migration" +fi + +if read_settings | jq -e 'has("deny") | not' &>/dev/null; then + pass "empty legacy top-level deny removed" +else + fail "legacy top-level deny still present" +fi + +teardown + +setup +mkdir -p "$TMP_DIR/.claude" +jq -n --arg rule "$DENY_RULE" '{"deny": ["Write(/secrets/**)", $rule]}' > "$TMP_DIR/.claude/settings.json" + +run_script >/dev/null + +if has_deny_rule; then + pass "rule migrated to permissions.deny (mixed legacy array)" +else + fail "rule missing from permissions.deny (mixed legacy array)" +fi + +if read_settings | jq -e '.deny == ["Write(/secrets/**)"]' &>/dev/null; then + pass "foreign top-level deny entry left untouched" +else + fail "foreign top-level deny entry modified" +fi + +teardown + +setup +mkdir -p "$TMP_DIR/.claude" +jq -n --arg rule "$DENY_RULE" '{"deny": [$rule], "permissions": {"deny": [$rule]}}' > "$TMP_DIR/.claude/settings.json" + +run_script >/dev/null + +if read_settings | jq -e '.permissions.deny | length == 1' &>/dev/null; then + pass "no duplicate when rule exists in both locations" +else + fail "duplicate created when rule exists in both locations" +fi + +if read_settings | jq -e 'has("deny") | not' &>/dev/null; then + pass "legacy top-level copy removed" else - fail "deny array modified unexpectedly" + fail "legacy top-level copy still present" fi teardown -# --- Case 6: Valid JSON output --- +# --- Case 7: Valid JSON output --- echo "" -echo "Case 6: Output is always valid JSON:" +echo "Case 7: Output is always valid JSON:" setup @@ -239,7 +317,7 @@ teardown setup mkdir -p "$TMP_DIR/.claude" -echo '{"allow":["Read(**)"],"deny":["Write(/secrets/**)"],"timeout":30}' > "$TMP_DIR/.claude/settings.json" +echo '{"permissions":{"allow":["Read(**)"],"deny":["Write(/secrets/**)"]},"timeout":30}' > "$TMP_DIR/.claude/settings.json" run_script >/dev/null