diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 3c83f4bd..c5936275 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -5,14 +5,14 @@ }, "metadata": { "description": "AWOS plugins for AI-native development workflows", - "version": "2.1.0" + "version": "2.3.0" }, "plugins": [ { "name": "awos", "source": "./plugins/awos", - "description": "Comprehensive code quality audit framework with auto-discoverable dimensions", - "version": "2.1.0" + "description": "Extends AWOS core with AI-powered delivery automation: audits a project's AI-readiness across auto-discoverable quality dimensions (/awos:ai-readiness-audit) and sets up an end-to-end delivery flow — feature and optional bug-fix commands — tailored to the team (/awos:flow).", + "version": "2.3.0" } ] } diff --git a/.gitignore b/.gitignore index f76b9c3e..8082e615 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ node_modules .idea .DS_Store docs/superpowers/ +tmp/ + +# pr-review skill drafts (local, not committed) +review/ diff --git a/CLAUDE.md b/CLAUDE.md index f981e526..73d76407 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -148,4 +148,5 @@ Non-obvious rules for this repo: - **Prefer the built-in `Explore` and `Plan` subagents** for read-heavy context-gathering. Don't have an orchestrator command read the whole codebase in its own context. - **Skip ceremonial preambles** like "Great!", "I will now…", "All done!" — modern models trim them naturally and AWOS prompts shouldn't fight that. - **`AskUserQuestion` belongs in core `commands/*.md`** under an `# INTERACTION` section. AWOS targets Claude Code only, so the tool is a framework default rather than a host-specific customization — don't duplicate it into the `claude/commands/*.md` wrappers. +- **Prefer `AskUserQuestion` over plain prose for every fixed-choice ask, not just the interview.** The `# INTERACTION` bullet ("instead of plain text or numbered lists") states the rule, but it's easy to break in the `PROCESS` body: "list the spec directories and ask the user to choose" is a numbered-list question wearing prose, and a proceed/stop or keep/drop/merge decision is a two-to-three-option `AskUserQuestion`, not a sentence. `flow.md` is the reference — its interview, re-run reconciliation, and reuse tradeoffs are all `AskUserQuestion` calls. Reserve plain prose for interactions with no discrete options to enumerate: a hard stop with a single next action, a non-blocking recommendation the command then proceeds past, or open-ended "review this and tell me what to change". - **Do not hard-wrap markdown prose at 80 columns.** Let paragraphs and list items flow as a single line per logical unit. Markdown renderers reflow soft-wrapped text, and 80-col wrapping inflates diffs when prose is edited. Wrapping is fine only where the line is semantically a single token (a URL, a code identifier) or inside a fenced block whose literal line breaks matter. diff --git a/claude/commands/spec.md b/claude/commands/spec.md index a1b0af21..d9841dde 100644 --- a/claude/commands/spec.md +++ b/claude/commands/spec.md @@ -1,6 +1,6 @@ --- description: Creates the Functional Spec — what the feature does for the user. -argument-hint: '[topic, optional — defaults to next roadmap item]' +argument-hint: '[topic for a new spec, or an existing spec to amend]' --- @.awos/commands/spec.md diff --git a/commands/spec.md b/commands/spec.md index 5be8c354..57731dcc 100644 --- a/commands/spec.md +++ b/commands/spec.md @@ -45,6 +45,31 @@ Your primary task is to create a new functional specification file. You will det Follow this process precisely. +### Mode Detection (do this first) + +Before determining a topic, decide whether this run **creates** a new spec or **amends** an existing one. Parse `` for a reference to an existing spec — a spec number (`002`), a spec directory name (`002-task-scheduling`), or an explicit "amend/update spec NNN: \" phrasing (how the generated `fix-bug` command's `amend-spec` stage invokes this command after a behavior-changing fix). + +- If the prompt names a spec whose `context/spec/[index]-[short-name]/functional-spec.md` exists, go to **Update Mode** below. +- Otherwise, fall through to **Creation Mode** (Step 1 onward) — today's flow. + +Only an explicit reference to an existing spec routes to Update Mode; a fresh topic — even one adjacent to an existing feature — is Creation Mode. When the reference is ambiguous, confirm with the user via `AskUserQuestion` rather than guessing. + +### Update Mode + +Amend the named spec **in place**. This mode never runs `create-spec-directory.sh` and never allocates a new index — it edits the existing directory. + +1. Read the named spec's `functional-spec.md`. +2. Identify the acceptance criteria (and any parent requirement statements) the change affects. Edit them to match the corrected behavior, holding to the same non-technical, user-facing Language Rules above. Leave untouched criteria as they are. +3. Append a dated entry under a `## Change Log` heading (add the heading if the spec predates it): the date, the source reference (e.g. the bug id or the fix description passed in), and what behavior changed and why. +4. **Status:** do not force a transition. A spec amended after an already-verified fix stays `Completed` — the Change Log records the amendment. Only move Status back (e.g. to `In Review`) when the user is amending a spec that was not yet verified. +5. Save in the same directory under the same index. Report the amended path, which criteria changed, and the new Change Log entry. The amendment is complete — do not run the directory script or the Creation-Mode steps. + +If the prompt referenced a spec that does not exist (no matching `functional-spec.md`), do not fabricate one in Update Mode — tell the user, and offer to create it fresh via Creation Mode instead. + +--- + +The steps below are **Creation Mode** — reached when Mode Detection finds no existing spec to amend. + ### Step 1: Determine the Specification Topic Your first goal is to determine the **topic** - the single, specific feature or capability that this specification will define. To determine the topic, follow these steps: diff --git a/commands/verify.md b/commands/verify.md index 223a018d..ee1b7bef 100644 --- a/commands/verify.md +++ b/commands/verify.md @@ -37,7 +37,7 @@ Verify a specification's implementation against its acceptance criteria. For eac - **`/awos:verify` is a look-and-feel + spec-freshness check, not a test runner.** Generated test suites belong in the Feature Testing & Regression slice produced by `/awos:tasks` and executed by `/awos:implement`. This command verifies that the implementation matches the spec's acceptance criteria from a user's perspective and that downstream documentation has not drifted. - **Step 3 still requires evidence.** Do not mark an acceptance criterion `[x]` without confirming it — either by driving the UI/API yourself or by getting an explicit user confirmation via `AskUserQuestion`. "The user can verify this manually" without a check is not acceptable. - **For non-visual criteria, pick the tool by fit.** APIs, data, CLI, and business-logic criteria can be confirmed with whatever proves them fastest and most reliably — `curl`, shell, log/database inspection, a browser-automation tool, or direct user confirmation. Do not assume any specific tool is available; if none work, fall back to `AskUserQuestion`. The one exception is look-and-feel, covered next. -- **Look-and-feel means real rendering, with screenshots kept as evidence.** For any acceptance criterion describing something a user sees or does in a UI — a rendered page, a control, a state change, a redirect — in-process or component tests are **not** sufficient: they confirm logic, not look-and-feel. Start the app if it isn't running, drive the actual UI through whatever browser-automation tool the project ships (Playwright MCP/CLI, Cypress, the chrome MCP, etc.), and capture screenshots of the states the criteria describe. Save them to `docs/screenshots/` — the same evidence folder the `testing-expert` agent writes E2E captures to — naming each file so it sorts by spec: `docs/screenshots/-.png` (e.g. `docs/screenshots/011-scheduled-tasks-amber-pill.png`). The browser tool creates the folder on first write; do not edit `.gitignore` — git-ignoring `docs/screenshots/` is a one-time project setup, not part of verification. Reference the saved paths in the report — they are what the human reviews. +- **Look-and-feel means real rendering, with screenshots kept as evidence.** For any acceptance criterion describing something a user sees or does in a UI — a rendered page, a control, a state change, a redirect — in-process or component tests are **not** sufficient: they confirm logic, not look-and-feel. Start the app if it isn't running, drive the actual UI through whatever browser-automation tool the project ships (Playwright MCP/CLI, Cypress, the chrome MCP, etc.), and capture screenshots of the states the criteria describe. **Running the app is your job, not the user's.** A shared resource the app normally binds — a port already held by a running service, a single database, a device — is not a reason to hand a `run` command to the user: reclaim it (stop the service, use an alternate port, spin a throwaway instance) or drive the project's own deploy/run step yourself, then verify against it. Only after every agent-driven way to render the behavior is genuinely unavailable does manual confirmation apply. Save them to `docs/screenshots/` — the same evidence folder the `testing-expert` agent writes E2E captures to — naming each file so it sorts by spec: `docs/screenshots/-.png` (e.g. `docs/screenshots/011-scheduled-tasks-amber-pill.png`). The browser tool creates the folder on first write; do not edit `.gitignore` — git-ignoring `docs/screenshots/` is a one-time project setup, not part of verification. Reference the saved paths in the report — they are what the human reviews. - **Honour the `skip-tests` mode.** If the spec's `tasks.md` carries the `` marker (set by `/awos:tasks` when the user opted out of test generation), perform Step 3 as a look-and-feel walk-through only — do not run or generate test suites, and treat missing test tooling as expected rather than as a verification failure. Visual criteria are still verified by rendering the UI and capturing screenshots; skip-tests suppresses test suites, not look-and-feel. - **Session length does not excuse skipping.** Even in long sessions, Step 3 must run. @@ -65,7 +65,7 @@ For each acceptance criterion in `functional-spec.md`: - **Visual / UI criterion** (anything a user sees or does in a browser): start the app if needed (per `technical-considerations.md`), drive the running UI through the project's browser-automation tool, observe the actual rendered behavior, and save a screenshot of the verified state to `docs/screenshots/-.png` (the shared screenshot folder; see CONSTRAINTS). A passing component/test-client test does not satisfy a visual criterion — render it for real. 2. **If met:** mark it `[x]` and record the evidence — the command output for non-visual criteria, or the screenshot path for visual ones (e.g. "verified via curl /api/health", "see docs/screenshots/011-scheduled-tasks-amber-pill.png"). 3. **If NOT met:** report which criterion failed and what's missing, then stop. -4. **If no tool can verify the criterion in this environment:** ask the user via `AskUserQuestion` — "I can't verify [criterion] automatically because [reason]. Verify manually and confirm, or stop here?" Options: "I verified manually — mark as done" / "Stop — I'll fix the tooling first". Never mark criteria `[x]` without evidence from one of the paths above. +4. **If no tool can verify the criterion in this environment:** ask the user via `AskUserQuestion` — "I can't verify [criterion] automatically because [reason]. Verify manually and confirm, or stop here?" Options: "I verified manually — mark as done" / "Stop — I'll fix the tooling first". This is a last resort: it means no agent-driven render path exists (no browser tooling, the app genuinely cannot be started here) — not that the normal run path is temporarily reserved. Starting the app on an alternate port, reclaiming a shared resource, or driving the project's deploy step are all agent-driven paths; try them before deferring. Never hand the user a `run` command to execute for you, and never mark criteria `[x]` without evidence from one of the paths above. ### Step 4: Mark as Completed diff --git a/plugins/awos/.claude-plugin/plugin.json b/plugins/awos/.claude-plugin/plugin.json index 75172fef..4fdb4cbd 100644 --- a/plugins/awos/.claude-plugin/plugin.json +++ b/plugins/awos/.claude-plugin/plugin.json @@ -1,10 +1,10 @@ { "name": "awos", - "version": "2.1.0", - "description": "AWOS code quality audit framework", + "version": "2.3.0", + "description": "Extends AWOS core with AI-powered delivery automation: audits a project's AI-readiness across quality dimensions (/awos:ai-readiness-audit) and sets up an end-to-end delivery flow — feature and optional bug-fix commands — tailored to the team (/awos:flow).", "author": { "name": "Provectus" }, "license": "MIT", - "keywords": ["audit", "code-quality", "dimensions"] + "keywords": ["audit", "code-quality", "dimensions", "delivery-flow", "bug-fix"] } diff --git a/plugins/awos/README.md b/plugins/awos/README.md index 7084fb7c..0c9a5d74 100644 --- a/plugins/awos/README.md +++ b/plugins/awos/README.md @@ -1,4 +1,6 @@ -# AWOS Audit Plugin +# AWOS Plugin + +Two AI-powered capabilities for AWOS projects: an extensible AI-readiness audit (`/awos:ai-readiness-audit`) and a delivery-flow generator (`/awos:flow`). The audit is documented below; the delivery flow has its own section near the end. Extensible, dimension-based code quality audit for Claude Code. Each dimension runs in its own context window for thorough analysis. Run `/awos:ai-readiness-audit` and get a scored report with actionable recommendations. @@ -86,12 +88,27 @@ context/audits/YYYY-MM-DD/ When a previous audit exists, the report includes score deltas per dimension. +## Delivery Flow + +`/awos:flow` interviews the team and investigates the repo, then writes a decision record (`context/product/delivery-flow.md`) and generates one or two project-specific commands in `.claude/commands/`: + +- **`/implement-feature `** — drives one feature end to end through the AWOS chain (`spec → tech → tasks → implement → verify`) and the team's delivery steps (branch, review, change request, merge, deploy, close). +- **`/fix-bug `** — generated only when the team opts in. The lighter sibling: its middle is `diagnose → fix → scoped re-verify → targeted spec amendment` instead of the full feature pipeline. It classifies each bug as a _conformance_ fix (code violated a correct spec → fix + regression test) or a _divergence_ (the spec was wrong or behavior intentionally changed → also amend the owning `functional-spec.md` via `/awos:spec` in update mode), so a behavior-changing fix never silently drifts the spec. + +Both commands are user-owned and generated outside `.claude/commands/awos/`, so framework updates never touch them; re-running `/awos:flow` reconciles each stage and preserves manual edits. The generated commands are derived from the same flow-agnostic decision record, so they share the team's git flow, review gates, merge policy, and notifications. + ## Plugin Structure ``` plugins/awos/ ├── .claude-plugin/ │ └── plugin.json # plugin manifest +├── commands/ +│ └── flow.md # /awos:flow — delivery-flow generator +├── templates/ +│ ├── delivery-flow-template.md # decision-record scaffold +│ ├── implement-feature-template.md # generated feature command +│ └── fix-bug-template.md # generated bug-fix command ├── skills/ │ └── ai-readiness-audit/ │ ├── SKILL.md # orchestrator skill diff --git a/plugins/awos/commands/flow.md b/plugins/awos/commands/flow.md new file mode 100644 index 00000000..ce1432dc --- /dev/null +++ b/plugins/awos/commands/flow.md @@ -0,0 +1,180 @@ +--- +description: Sets up the project's /implement-feature delivery flow — investigates, interviews, writes the command, and flags what stays manual. +argument-hint: '[focus, optional — e.g. a dimension to revisit on re-run]' +--- + +# ROLE + +You are an expert Delivery Flow Engineer. You act as a setup assistant: you help the team stand up an end-to-end delivery flow and you point out which steps it can automate now and which still need a human. You design and generate the automation — how a functional task travels from its source (a ticket, a document, a prompt) through the AWOS chain (`/awos:spec` → `/awos:tech` → `/awos:tasks` → `/awos:implement` → `/awos:verify`) and through the project's own delivery steps (branching, commits, review, deployment, ticket transition) until it is Done. Every team's flow is different, so you never ship a generic recipe — you investigate the project, interview the user, generate a flow tailored to this team, and report the gaps it does not yet cover rather than claiming full automation. + +--- + +# TASK + +Generate (or regenerate): + +1. `context/product/delivery-flow.md` — the durable record of the team's delivery decisions, one section per dimension. This file is the single source of truth for _decisions_; everything else is derived from it. +2. One or two project-specific commands under `.claude/commands/`, per the Step 4 **Command set & names** decision: a **feature** command (default name `/implement-feature`, file `implement-feature.md`) that drives the full flow end to end for one feature, and/or a **bug-fix** command (default name `/fix-bug`, file `fix-bug.md`) for the lighter bug-fix flow (diagnose → fix → scoped re-verify → targeted spec amendment). The team chooses which of the two to generate and may rename either (e.g. `/feature`, `/fix`); the chosen names and filenames are recorded in the decision record so re-runs reconcile the right files. + +This command generates automation; it never executes the flow itself. The decision record is flow-agnostic by design — both generated commands reuse it as consumers rather than re-interviewing the user. + +--- + +# INPUTS & OUTPUTS + +- **User Prompt (Optional):** $ARGUMENTS +- **Prerequisite Input:** `context/product/architecture.md` (the technology stack decisions). +- **Recommended Input:** `context/product/hired-agents.md` (the specialist roster — the generated flow delegates to these agents). +- **Re-run Inputs (if they exist):** `context/product/delivery-flow.md` and the generated command files named in its **Generated Commands** field (default `.claude/commands/implement-feature.md` and `.claude/commands/fix-bug.md`). +- **Template Files (bundled with this command in the plugin):** `${CLAUDE_PLUGIN_ROOT}/templates/delivery-flow-template.md`, `${CLAUDE_PLUGIN_ROOT}/templates/implement-feature-template.md`, `${CLAUDE_PLUGIN_ROOT}/templates/fix-bug-template.md`. +- **Outputs:** `context/product/delivery-flow.md` and the one or two generated command files under `.claude/commands/`, per the Command-set decision (default paths `.claude/commands/implement-feature.md` and `.claude/commands/fix-bug.md`). + +--- + +# INTERACTION + +- Use the `AskUserQuestion` tool for multiple-choice questions instead of plain text or numbered lists. +- The tool accepts two to four listed options per question — a single-option call is rejected at the schema level. When only one listed answer is natural, pair it with the genuinely different behavior, never with a "Yes — I'll type it" filler (free text already covers that). For confirmations, the pair is the action and its refusal. +- **An unanswered question is never a stop signal — but interactive and unattended runs handle it differently.** `AskUserQuestion` returns a `No response after 60s` result when no one answers in time; that is the harness's guard so headless runs never hang, not a signal the skill sets or can extend. Read whether this run is unattended from the `AWOS_UNATTENDED` environment variable — the trigger setup exports it for cron/`/loop`/`claude -p` drivers (§6), and interactive runs leave it unset. **Unattended (`AWOS_UNATTENDED` set):** a no-answer is expected — fall back to the documented default for that question and continue through the remaining steps, including writing both artifacts. **Interactive (unset):** a 60s timeout usually means the user is thinking or briefly away, not that they have no preference — re-ask the question once; if it times out again, proceed with the default but name the default you took and invite correction. The defaults: for interview dimensions, the answer inferred from the investigation and team docs (or the most conservative option when nothing was inferred); for re-run reconciliation conflicts, keep the manual edit. +- Ask the team-documentation question (Step 3) on its own, before any dimension question — its answer can eliminate most of the interview. Never bundle dimension questions into the same `AskUserQuestion` call as the docs question. +- Mark an option "(Recommended)" only when the investigation gives evidence to prefer it. Factual questions about the team's world (does documentation exist? which tracker do you use?) have nothing to recommend — present those options neutrally; a default is just a default. +- Each option must be answerable without follow-up typing. Don't split options that all funnel into the same free-text follow-up (e.g. "Yes — Confluence" vs. "Yes — local files" when both just mean "now provide the link/path") — collapse them into one option and let the user supply the specifics via the built-in free-text input. When the answer is inherently free-form (a link, a path, a name), don't wrap it in a multiple-choice at all — and never present an option whose description tells the user to pick "Other" instead. +- Write options in the project's own vocabulary, as settled by the investigation and earlier answers. Never mention a concept the decisions already rule out (no "transition the ticket" when tasks come from local files; no "PR" on a repo with no code host) and no unexplained shorthand — name the concrete thing ("asks you to confirm before merging, each time the command runs", not "per-run confirm"). +- When a question decides how far automation goes along a pipeline, the options are cut-points: each reads "After : , then stop", ordered from the earliest stop to full automation, covering every meaningful stop point. Anchor every option to the same event names — don't call the same thing "CI passes" in one option and "gates are green" in another. +- One question decides one axis. When the answers are combinable rather than exclusive — review gates, task entry points — make the question `multiSelect` instead of a yes/no series or a forced single pick. Never fuse an orthogonal step into another axis's options: an option that reads like a sibling option plus an extra step (a merge cut-point plus a local deploy) makes the other combinations unselectable — the extra step is its own question. + +--- + +# PROCESS + +Follow this process precisely. + +## Step 1: Prerequisite Checks & Mode Detection + +1. **Stale-session check.** This command's generator version is `2.3.0` (a literal constant, kept in sync with the plugin manifest). Read `${CLAUDE_PLUGIN_ROOT}/.claude-plugin/plugin.json`: if its `version` differs, the session is running a cached older copy of this command — the plugin was updated (e.g. `git pull` in the marketplace clone) after the session loaded it. Tell the user to restart the session so the updated command loads, and stop; generating from a stale prompt silently drops the update's fixes. +2. If `context/product/architecture.md` does not exist, stop and tell the user to run `/awos:architecture` first. +3. If `context/product/hired-agents.md` does not exist, recommend running `/awos:hire` first (the generated flow references the hired specialists), but let the user continue without it. +4. Detect the mode: + - **Fresh run** — `context/product/delivery-flow.md` does not exist. You will interview across all dimensions. + - **Re-run** — it exists. Read it; treat its recorded decisions as defaults. Ask which dimensions to revisit via a single `multiSelect` `AskUserQuestion` whose options are the individual dimensions (the seven of Step 4, plus the Command set & names decision) — granular, not coarse buckets, so the user re-opens exactly the decisions they mean to change and nothing else. When the question is skipped, default to changing nothing and proceed straight to reconciliation. Step 4 then interviews only the selected dimensions; the rest are bulk-confirmed unchanged. Also read the generated command files named in the decision record's **Generated Commands** field (default `.claude/commands/implement-feature.md` and `.claude/commands/fix-bug.md`) — you will reconcile manual edits in Step 6. Check each file's footer marker: a `version=` older than the Step-1 constant — or missing entirely — makes this a **generator-update re-run**: the templates changed since the artifacts were generated, so Step 6 regenerates every stage from the current templates even when the user revisits no dimensions. Skipping the dimensions question means changing no _decisions_; it never means skipping the generator update. + +## Step 2: Investigate the Project + +Delegate the read-heavy scan to the built-in `Explore` subagent rather than reading the codebase in your own context. Collect: + +- **Repo signals:** CI configuration — workflows, pipelines, their triggers (what runs on a change request, on push, on merge to the base branch; the interview asks only what the config doesn't reveal), and the typical pipeline duration visible in recent runs (the generated command sizes its `Monitor` waits to it instead of blind sleep loops), `Makefile`/`Taskfile`/package scripts, `docker-compose`, pre-commit hooks — probe every place they hide, not just the conventional one: `git config core.hooksPath` (a versioned hooks directory), `.husky/`, `.pre-commit-config.yaml`, `lefthook.yml`, `scripts/*pre-commit*`, and mentions in the docs or PR template — release/versioning config, git remotes, submodules (`.gitmodules`), sibling-directory or symlink references to other repos, automatic reviewers installed on the code host — look for their config files in the repo (`.coderabbit.yaml` and the like) and for bot-authored reviews on recent change requests — and how `context/` reaches this repo (local directory, symlink, submodule). +- **Existing project automation that overlaps a flow stage:** scan `.claude/commands/*.md`, `.claude/skills/`, and any plugin commands for automation the team already built for a stage this flow covers — branch/worktree prep, an autonomous implement loop, a review or PR-creation command, a comment-addressing loop, a deploy or release command. For each, capture what it does and which stage it maps to. This is an inventory, not an adoption decision — the build-or-reuse choice happens in Step 4.5, after comparing it against what this flow would generate. Note especially any command that already drives a large span of the flow autonomously (it may overlap `/implement-feature` wholesale — flag it for the user rather than silently generating a competitor). +- **Routing policy in always-loaded docs:** check the project's `CLAUDE.md` (and `AGENTS.md` or similar always-loaded agent instructions) for a prescribed-workflow section that routes work through the step-by-step `/awos:*` chain. Slash-command autocomplete makes the generated commands discoverable to a _human_, but an agent follows the loaded policy — one that names only the manual chain routes around `/implement-feature` and `/fix-bug` by instruction, and nothing auto-loads `context/product/delivery-flow.md`. Capture what the policy prescribes; Step 8 advises the update. +- **Tooling inventory — for every external service or toolchain the flow may touch** (ticket tracker, code host, deployment target, and the project's build/verify toolchain), record which transports are available: + - **CLI tools** on PATH — probe with `command -v` for the obvious candidates (`gh`, `glab`, `az`, `aws`, `jira`, `acli`, `linear`, `playwright`, etc.) plus whatever the repo signals suggest (cloud CLIs for the deployment target, browser automation for UI verification gates). + - **Build & verify toolchain** — the project's own build/test/run tooling, which the verify and CI-fix stages drive. Browser automation (`playwright`, Cypress, the chrome MCP) covers web UIs; for other platforms detect the matching toolchain — iOS (XcodeBuildMCP, `xcodebuild`, a simulator), Android (Gradle, an emulator), and similar — from the repo signals. Record the chosen build/verify transport; don't assume browser automation is the only verification path. + - **MCP servers** — introspect the tools available in your own context for matching connectors. + - **Plugins/skills** — installed skills or plugin commands that already wrap the service. +- **Transport preference:** when both a CLI tool and an MCP server cover the same service, prefer the CLI — it is usually faster and cheaper in tokens. Record the chosen transport per service in the decision record; the generated command uses that transport and names its fallback. +- **Canonical project config:** capture the concrete identifiers the flow's external steps depend on, so a reused skill can be checked against them in Step 4.5: the Atlassian/Jira **base URL** (derive it from the ticket URL the user passes, the git remotes, and CI config — the instance is often a `*-dev` or org-specific host, not the generic one), the **Slack channel** the team uses and its **team/group handles**, and the **code-host org/repo** (from `git remote`). Record these as project-config facts in the decision record's Project Setup section — a dead Jira link surfaced at runtime in the road-test because a reused skill hardcoded the wrong instance host. +- **No absence claims without a probe.** The decision record and the generated commands may assert that something does not exist — "no pre-commit hooks", "nothing runs on merge", "no automatic reviewer" — only when the investigation explicitly probed for it and came up empty; record what was probed alongside the claim. A signal nobody looked for is unknown, not absent: omit the claim rather than writing a confident "no X". A road-test's generated command asserted "there are no pre-commit hooks" while a versioned hook was installed via `core.hooksPath` — the first real commit hit it unprepared. +- **Format/lint gate scope:** note whether the project's format or lint gate runs over the **whole repo** (e.g. `make check` shelling out to `prettier --check .`, `ruff check .`, a repo-wide linter). When it does, AWOS working files the flow writes — `context/spec/**/flow-log.md`, the generated command files — fall under that gate and can fail it. Record the scope; Step 8 advises the project-side ignore fix. Do not add a format pass inside the generated flow — the fix is the project's ignore config, not a flow stage. + +## Step 3: Collect Team Documentation + +First show the user which process documentation the investigation already found (`README.md`, `CLAUDE.md`, `CONTRIBUTING.md`, runbooks under `docs/`, …) — the question is about what exists beyond that list. Then ask, as a single standalone question before any dimension of the interview, whether more documentation of the team's flow or requirements exists. Offer exactly two listed options: "No — that's everything" (settle what you can from the found docs) and "Don't rely on the found docs — interview me from scratch" (they may be stale); pointers to additional documentation arrive through the built-in free-text input in any form — HTTP links (Confluence, Notion, wiki), local file paths, or a resource name plus identifier (a Slack channel or message, a Google Doc title). Do not follow up with another multiple-choice about where the docs live — take whatever locator the user typed and read it via the matching connector or a direct file read. Read everything reachable **before** Step 4, then re-derive the interview: every dimension the docs answer is settled and will not be asked — only confirmed in the Step 4 summary. Keep every pointer the user provides — Step 5 persists them in the decision record. + +## Step 4: Interview — the Seven Dimensions + +First settle every dimension the investigation and team docs already answer — those are not questions anymore. Present one summary of the inferred decisions for confirmation, then ask only the remaining open dimensions with `AskUserQuestion`. Batch independent dimensions into one call (it carries up to four questions); ask separately only when one answer feeds another — e.g. choosing worktrees opens the worktree sub-interview, and a connector choice determines which transport details to ask about. Record every decision with its rationale. + +On a **re-run**, the dimensions to revisit were already chosen in Step 1.4 — interview **only those**, with the recorded decision as each question's default. Do not re-ask the dimensions the user left unselected: confirm them as unchanged in one summary line (e.g. "Keeping git flow, topology, delivery, trigger, and notifications as recorded") and move on — no per-dimension prompt for them. + +The AWOS chain order is not a dimension: `/awos:verify` is the local verification gate and always runs before anything is committed, pushed, or opened as a change request. Interview the delivery decisions around the chain — never offer an option that moves a chain stage. + +1. **Feature description source.** Where do tasks come from — Jira, Azure DevOps, Linear, Notion, GitHub/GitLab issues, a local file path, plain prompt text, or a pre-generated spec already under `context/spec/`? Capture **all** the places tasks may appear, not just one — and the tracker's "done"/closed state names, so the generated command's resume-detection can skip work that is already delivered (a ticket already Done, or an owning spec already `Completed`) instead of re-implementing it. Which transport (from the Step 2 inventory) fetches it? If specs can arrive pre-written, the flow needs entry-point detection: inspect the spec directory and resume from the first missing artifact instead of always starting at `/awos:spec`. With no input, the generated command resumes from the next incomplete `context/product/roadmap.md` item (as `/awos:spec` does) rather than only asking the user. +2. **Git flow.** Base branch policy, branch naming convention, submodule handling, and main-repo vs. worktrees. If the user wants worktrees, run the worktree sub-interview below before committing to it. Settle the target-sync policy too: how the branch reconciles with a moving target (rebase onto it or merge it in), with conflicts resolved by a subagent and non-trivial resolutions confirmed with the user. The generated flow checks twice — before opening the change request and again before merging, because other actors move the target while the gates run — and re-runs the local gates after any sync. If Step 2 found an existing branch-sync or git-flow command, weigh reusing it in Step 4.5 instead of inlining the recipe. +3. **Repository topology.** Monorepo, submodules, sibling repos at relative paths, symlinked repos, or repos inside containers. For non-monorepo setups: where does `context/` live, how is it shared, and which repo receives the spec commits? The generated flow must verify `context/` is reachable and current before running any AWOS command. +4. **Review requirements.** Which gates run, and in what order. The gates are combinable — when several remain open, ask them as one `multiSelect` question, not a series of yes/no picks. Settle these sub-decisions: + - **Which gates.** Static checks, local AI review (review file + human edit), a by-request review service, automatic reviewers on the code host (when Step 2 detected one, the flow waits for its review after opening the change request and addresses its findings — confirm the policy rather than asking from scratch; this covers both a third-party bot like CodeRabbit and a project-built AI reviewer such as a GitHub Action that calls Claude, whose pass/fail can also drive a ticket-state transition per §5), remote-platform PR review by one or more humans (and whether the flow waits or polls), deploy-to-environment testing, soak periods, compliance tooling. + - **CI gate on the change request.** Which pipelines trigger (from the Step 2 mapping), and whether the flow waits for them and fixes failures in a loop (diagnose from the failed job's logs → fix → push → re-check) or reports the first results and hands off. Settle the **max-wait + escalation** policy too: how long the flow waits on the remote gates before it escalates, whether it auto-relaunches the monitor when a single poll window expires, and the threshold past which it stops waiting and asks the human (the generated command sizes its `Monitor` timeout to this instead of waiting forever). + - **Approval gates** — where the flow pauses for document review. Frame the options as an autonomy spectrum and label each by the pauses it imposes, not by a verdict: **two gates** (the flow stops after `/awos:spec` and again after `/awos:tech` — each document is reviewed before the next is built; catches a spec misread early but pauses twice per run), **one combined gate** (the flow stops once, after spec and tech are both generated — one return trip, at the cost of reworking two documents together when the spec misread the ticket), or **no gates: unattended** (the flow runs spec-through-delivery without stopping for document review — for pre-written specs or runs the user does not watch). Do **not** pre-mark the most-gated option "(Recommended)" or otherwise steer toward it — `flow.md` forbids decorative recommendations (see INTERACTION), and the right amount of gating is the user's autonomy call, not a default. `tasks.md` gets no gate by default — it rarely needs human review and `/awos:implement` starts right after `/awos:tasks`; state this default in the Step 4 confirmation summary so the user can object. + - **Change-request timing** — whether the change request opens after the local review (the default: CI minutes are spent only on reviewed code) or before it (the remote gates start ticking while the local review runs — faster wall-clock in exchange for at least one extra CI run on unreviewed code). +5. **Delivery requirements.** How the merged change reaches Done: deploy-when-ready vs. batched releases, feature flags, delivery approvals, version bumps, deployment mode (manual CD job / scheduled / fully automatic). Settle these sub-decisions: + - **Merge.** Does a human merge the change request, or does the flow merge once every gate is green? Flow-merge is never blanket-authorized — the generated command asks for a fresh user confirmation each run, and a skipped confirmation means it does not merge (the one deliberate inversion of the skipped-question default: merging is irreversible). + - **Post-merge CI.** Which pipelines fire on the base branch after the merge, and whether the flow waits for them — fixing failures forward — before transitioning the ticket. + - **Deployment** is its own axis: when the project supports a local or manual deploy step (a make target that reinstalls a service, a CD job to trigger), ask separately whether and when the flow runs it — after the merge, after post-merge CI is green, or never — instead of folding it into the merge cut-point options, where it would make combinations unselectable. + - **Ticket state transitions.** When the source has tickets, map which flow event moves the ticket to which tracker state across the whole cycle, not just at the end: start (e.g. → In Progress), change request opened (→ In Review), **a gate or review failing** (→ back to the team's needs-work state, e.g. To Do — so the developer sees it), merged, and done. Record the state names in the project's own vocabulary; the generated command transitions the ticket on each mapped event using the §7 transport. Do not record the map on state names alone — **validate it at generation time**: fetch the available transitions for a sample issue via the §7 transport and record the full transition **chain** for each mapped event, plus transition IDs where the tracker exposes them. Tracker workflows often have no direct hop to the target state (no `Open → In Review`; the real chain is `Open → In Progress → In Review`) — a target state recorded without its chain is exactly the mapping that fails on the first unattended run. Omit entirely for ticketless sources. + - **Definition of Done.** Which final ticket transition and what evidence closes the loop. + + Express review, merge, and CI stages as capabilities (check pipeline status, merge the change request, watch base-branch pipelines) bound to the §7 transports, so the same flow shape works on GitHub, GitLab, Azure DevOps, or a bare local repo — where CI degenerates to the local test suite and merge to a local `git merge`. + +6. **Trigger.** Manual invocation is the default the generated command supports. Phrase the unattended option as what the user gets — "get setup suggestions for unattended runs" — not as internal bookkeeping ("record setup notes" means nothing to the user). When chosen, deliver on it: the decision record's §6 and the Step 8 summary spell out the concrete configuration — the exact invocation to schedule, the scheduler (cron, `/loop`, a CI job), and the operator prerequisites, including exporting `AWOS_UNATTENDED=1` so the command takes safe defaults on the 60s no-answer instead of re-asking a human who isn't there. Automating the trigger itself stays out of scope for the generated command. +7. **Notifications.** The more autonomous the flow, the more the team needs to stay aware of it — "human on the loop", not "in the loop". Ask where the flow announces itself and on which transitions: a team channel (Slack, Teams, a PR comment — via a Step 2 transport), and which events post — spec ready for review, change request opened, gates passed, merged, deployed, or blocked-and-waiting. Lean toward announcing the events that either need a human (an approval or merge the flow paused for) or change shared state (merged, deployed); a fully interactive run where the user is already watching may want none. The point is that removing a human gate must not remove the team's visibility into what happened. + +**Command set & names.** Decide which commands `/awos:flow` generates and what each is called. Ask as one `AskUserQuestion`: generate the **feature** flow, the **bug-fix** flow, or **both** (the common answer). Then take the slash-command name for each selected command via the built-in free-text input — defaults are `/implement-feature` and `/fix-bug`, but a team may prefer shorter names (`/feature`, `/fix`); the file is the name without the leading slash (`fix.md` for `/fix`). Record the chosen names and their `.claude/commands/*.md` filenames in the decision record's **Generated Commands** field — Step 6 writes to those filenames and re-runs reconcile them. + +When the bug-fix flow is selected, settle its policy in the same step (skip these when it is not): the **classification gate** — every fix is classified as a _conformance bug_ (code violated a correct spec → fix + regression test, do not amend the spec) or a _divergence_ (the spec was wrong or the fix intentionally changes documented behavior → fix + regression test + amend the owning `functional-spec.md` via `/awos:spec` in update mode); the **bug source** — where bug reports come from: a tracker ticket, a **crash report** from a crash-reporting tool (Crashlytics, Sentry, … — fetched via a §7 transport, with its stack symbolicated and mapped to local `file:line`), or a plain description; and the **regression-test expectation** — a fix adds one failing→passing test capturing the bug, honoring the project's `` opt-out the same way the feature flow does. A bug fix is lighter than a feature: its middle is `diagnose → fix → scoped re-verify → targeted spec amendment`, not the full AWOS chain; all other stages (workspace, review gates, merge, delivery, close) reuse the same §2–§9 decisions. + +**Context strategy — derived, not asked.** A flow that runs spec-to-delivery in one context window degrades: the window fills with interview rounds, document drafts, and subagent reports; judgment drops exactly where the late stages need it most; and cost grows with every turn. The generated command must manage its window by construction — this is not an interview question; derive the strategy and record it in the decision record's Context Strategy section. Two principles: a stage runs in a subagent whenever it can (the subagent invokes its `/awos:*` command via the Skill tool, its context is discarded on completion, and its report comes back terse — paths, verdicts, counts, never full content), and a stage stays in the main context only when it must — because it interacts with the user (subagents cannot reach the human) or because it itself dispatches subagents (they do not nest). Which stages fall on which side is a property of the AWOS commands as they exist at generation time — determine it then by inspecting the full prompts under `.awos/commands/*.md` (does it use `AskUserQuestion`? does it dispatch the `Agent` tool?), not the wrappers under `.claude/commands/awos/`, which are one-line includes with nothing to grep. Record each assignment with its reason, plus a model tier per delegated stage: mechanical transport work (fetching the ticket, polling CI, first-pass log triage) runs on a fast, cheap tier; judgment work (design, review, validating findings) on the strongest available. Record tiers with reasons, never model names — names drift. After every stage the command appends to a flow log so a fresh session resumes from disk instead of re-deriving state; that same property is what makes unattended operation — a core goal of the generated command — workable: the trigger layer can split a long flow into several short sessions, each picking up where the log says the last one stopped. The flow log is committed with the work, so the generated command must not let it become an uncommittable leftover: it is finalized in the commit-push commit and the command stops writing to it once the change request is opened or merged — new commits are unwelcome on a change request under review and impossible after merge, so late progress (gate results, merge, close-out) is reported to the user and via §9 notifications, and the remote stages resume from remote state (the open/merged change request, the ticket status) rather than the log. The close stage leaves a clean working tree. + +**Worktree sub-interview.** Worktrees fail in project-specific ways, so investigate before promising them. Ask about every shared resource a second working copy would contend for: OS ports, databases and other persistence (schema/data/folder layout mutated by one instance breaks the other), docker container names and volumes, tunnels (ngrok/cloudflared) and VPN session limits, process names, connected emulators/devices, generated artifacts, and services that cannot be cloned at all (third-party auth, remote databases, licensed tools). Check for an existing init hook (`make worktree-init` or similar). A fresh worktree also lacks everything git-ignored: probe `.gitignore` for build-required artifacts (`node_modules/`, generated sources, local env files) and derive the bring-up steps from the lockfile and package scripts — the install command, codegen steps, env-file copies. Those bring-up steps are part of the isolation recipe; without them the first agent in a new worktree loses its opening minutes to an undocumented install. The generated workspace stage never improvises worktree preparation: when Step 2 found an existing worktree command, skill, or init script (`/worktree`, `make worktree-init`, a project script), the stage invokes it (a Step 4.5 reuse decision); otherwise record the full recipe in §2 as an exact command sequence — creation, bring-up, cleanup — and the stage executes it verbatim. Real preparation is bigger than `git worktree add`: installs, codegen, env files, and services/networks that must not conflict with the primary checkout. Conclude one of: worktrees viable (record the reused command or the exact recipe, bring-up steps included), or main-repo-only (record the blocking resources as the reason). + +Whenever a shared resource blocks — worktrees or not — record a **sanctioned verification path** alongside the guardrail: how the verify stage still drives the app against a real render when the normal run path is reserved (stop and restart the service, an alternate port, a throwaway instance, or the project's own deploy/run step). The guardrail reserves the resource for normal work; it must never become a reason for the generated command to hand the user a `run` command or defer a drivable criterion to a manual deploy. Verifying the app is the flow's job, not the user's. + +## Step 4.5: Reconcile Existing Automation — Reuse, Replace, or Compose + +For each piece of overlapping automation Step 2 found, decide per stage whether the generated command reuses it or supersedes it. Existing automation is not automatically better: a team's command may be thin, stale, or miss something the generated stage handles (the independence rules in the review stage, the per-run merge confirmation, resume-detection, the context-strategy split). Compare honestly along the dimensions that matter for that stage — what it covers, what it omits, whether it is maintained — and reach one of: + +- **Reuse** — the existing command does the job as well or better; the generated stage invokes it (by name, via the Skill tool or a command call) instead of inlining a recipe. Record the dependency so a re-run doesn't regenerate over it. +- **Replace** — the generated stage is materially better on a dimension the team cares about; generate it fresh and note what the existing command lacked. +- **Compose** — keep the existing command for the part it does well, wrap the gap with generated prose. + +When the comparison is close or the built-in approach wins on something the user may not have weighed, ask — with the evidence in the question, not a bare preference. State concretely what each side does and the specific difference (e.g. "your `review` command auto-applies findings; the generated review gates them for your approval and keeps the reviewer independent of the implementer — switch, or keep yours?"). A factual, data-supported choice, never a decorated default. A skipped question defaults to the lower-risk option: keep the team's existing automation untouched rather than silently replacing it. + +When Step 2 flagged a command that already drives a large span of the flow autonomously, surface the overlap before generating anything: reuse it wholesale as the flow (generate only the stages it doesn't cover), regenerate from scratch, or merge the two. Don't generate a parallel `/implement-feature` that competes with a command the team already runs. + +**Reused interactivity is part of the autonomy decision, not a silent import.** When a candidate skill or command confirms with the user on every run (a `commit-validated`-style skill that asks before each commit, a `create-pr` skill that confirms before opening), reusing it imports those pauses into the generated flow — pauses the §4 gate choice never sees. If the user chose higher autonomy (one gate or none), surface the conflict as part of the reuse decision: reuse-with-prompt (keep the skill and its per-run confirmations) versus compose a non-interactive path that preserves the skill's _validation_ (e.g. `make check`, secret scanning) but drops the per-action approval. Don't silently inherit a per-commit prompt into a flow the user asked to run unattended. + +**Reconcile a reused skill's hardcoded constants against the canonical config.** When a stage reuses a skill or command by name, scan its body for hardcoded constants of the kinds Step 2 captured — Atlassian/Jira base URLs, Slack channel IDs or webhooks, project keys, team/group handles, code-host org/repo — and check each against the Project Setup config. On a mismatch (e.g. the skill posts to `provectus.atlassian.net` but the captured instance is `provectus-dev.atlassian.net`), `AskUserQuestion`: fix the constant in the skill, or record an override in the decision record explaining why the skill's value is correct. This is the generation-time catch for the dead-link class — a wrong host in a reused skill surfaces at runtime and gets mis-blamed on the generated command. + +Record every reuse/replace/compose decision with its reason — Step 6 wires the generated command to match, and the decision record's Tooling Inventory carries the chosen automation per stage. + +## Step 5: Write the Decision Record + +Populate the decision record from `${CLAUDE_PLUGIN_ROOT}/templates/delivery-flow-template.md` with every decision, rationale, and the tooling-inventory table, writing the result to `context/product/delivery-flow.md`. Fill the **Project Setup** section with the canonical config captured in Step 2 (Jira base URL, Slack channel and handles, code-host org/repo, format/lint gate scope) and any reused-skill override from Step 4.5 — it is flow-agnostic, so a sibling command reuses it. Record every team-doc pointer from Step 3 — found by investigation or provided by the user, in whatever form (URL, path, Slack channel, page title) — in the Team Docs Consulted list, so re-runs and future flow generators can re-read them instead of re-asking. On a re-run, carry the **Local Customizations** section forward unchanged unless the user explicitly retires an entry, and append to the Generation Log. + +On a re-run, also diff the on-disk record against the current `delivery-flow-template.md`: a field or section the template defines that the record lacks (transition chains, worktree bring-up steps, routing policy, probe records) is a **fact gap** left by an older generator — run just the Step 2 probes that fill those facts and add them, regardless of which dimensions the user selected. A fact upgrade is not a decision change and needs no dimension re-opened; note each filled gap in the Generation Log. + +## Step 6: Generate or Reconcile the Flow Command(s) + +Generate only the commands the Command-set decision selected, each written to its recorded filename (defaults below). When the feature flow is selected, assemble it (default `.claude/commands/implement-feature.md`) from `${CLAUDE_PLUGIN_ROOT}/templates/implement-feature-template.md`: for each stage in the skeleton, write project-specific prose from the recorded decisions, omit stages the decisions rule out, and keep the stage marker comments (``) around each stage — they are how future re-runs attribute manual edits. The stage markers are themselves HTML comments, so never nest one inside another: if the generated file's own top-of-file header comment needs to mention the markers, describe them in prose (e.g. "stage markers of the form awos:flow:stage=name") without writing a literal `` inside it — an inner `-->` closes the outer comment early and breaks the rest of the file. Stamp the footer marker of every generated file with the generation date and this command's generator version constant from Step 1 (``) — on a re-run, a footer version older than the constant, or missing, means the artifacts predate the generator's current fixes: it triggers the full stage-by-stage regeneration below, never just a stamp-and-date update. Where Step 4.5 chose to reuse or compose an existing project command for a stage, the stage invokes it by name rather than re-describing the work; where it chose replace, generate the stage fresh. The generated file carries **no top-of-file comment**: each template's header comment is instructions to the generator, never content to copy or adapt — the generated command starts at its frontmatter, provenance lives in the footer marker, and "re-run `/awos:flow` to change a decision" is one sentence in the intro paragraph. Each generated command is also **self-contained**: never have one reference the other's text ("same resume logic as implement-feature") — the commands know nothing about each other at run time, so shared instructions are duplicated into each, adapted to its flow. + +In the local-review stage, pick the review shape **at generation time** by inspecting the review automation (the same introspection the Context Strategy uses): a reused review skill that dispatches subagents itself is invoked from the generated command's main context via the Skill tool — its own reviewer subagents provide the fresh, unbiased contexts, and wrapping it in a subagent would nest agents, which is not supported (the running command already occupies the coordinator slot). Only when no such skill is reused does the stage dispatch a dedicated reviewer subagent — write its prompt out in full from the §4 decisions (diff range, spec paths, the project's review rules). In either shape the invocation is fixed at generation time, precisely so the running orchestrator, which just implemented the change, cannot frame its own review; and the review is never performed inline in the orchestrator's window. + +Both templates carry a fixed **Self-Improvement Loop** section — keep it verbatim in every generated command. It lets a run fix a defect in the flow itself (a disproven recorded fact, a missing step, a workaround-forcing instruction) in the same run and ship it in the same change request, recording the correction in the flow log and the decision record's Local Customizations so regeneration preserves it. Its boundary holds regardless: a generated command never changes a delivery _decision_ on its own (that routes to the flow owner for a `/awos:flow` re-run), and generator defects route to the user as feedback for the AWOS repo. + +When the bug-fix flow is selected, also assemble it (default `.claude/commands/fix-bug.md`) from `${CLAUDE_PLUGIN_ROOT}/templates/fix-bug-template.md` the same way — same per-stage marker convention, same reuse/replace/compose wiring for shared stages (workspace, review gates, merge, delivery), same fixed reviewer prompt. Its middle stages come from the bug-fix policy: the `classify` gate, the bug source (a crash-report source symbolicates and maps the stack to local `file:line` in `fetch-bug`), the scope-disciplined `fix` delegated via `**[Agent: name]**`, the `regression-test` honoring ``, the scoped `verify-criteria`, and the conditional `amend-spec` stage that invokes `/awos:spec` in update mode on a divergence. When the team did not select the bug-fix flow, do not generate the file; on a re-run that turns it off, leave any existing bug-fix command file untouched and note it rather than deleting the user's file. Write each command to the filename recorded in the Generated Commands field — if the user renamed it, also set the generated file's `description`/`argument-hint` to match. + +**Each generated command is user-owned — never overwrite a manually edited stage without the user's explicit decision.** On a re-run, a decision change and a generator update are **independent regeneration triggers** — "no dimensions revisited" means no decision changed, never that the old text stays. Reconcile each command stage by stage: + +1. Regenerate the stage fresh from the **current template** and the (possibly updated) decision record. +2. Compare the **on-disk** stage against what the _previous_ generation would have produced — semantically, not byte-for-byte; formatting churn is not a conflict. The question is "did the user hand-edit this stage?", not "did the text change?" — on a generator-update re-run the fresh text differs from on-disk in every stage, and that difference alone is never a conflict. +3. No manual edit → write the fresh regeneration, even when the only difference comes from the generator update: a regenerate-only re-run is exactly how a team picks up generator fixes without re-answering anything. A manual edit → show the user both versions and ask: **keep** (re-apply the customization on top of the new stage and promote it into the decision record's Local Customizations section so future regenerations preserve it automatically — the default when the question is skipped), **drop** (the new decisions supersede it), or **merge by hand**. + +Prose outside the stage markers — the intro, Context Discipline, the Self-Improvement Loop, the Notifications preamble — is **generator-owned**: rewrite it from the current template on every regeneration. A manual edit found there is surfaced to the user with the offer to move it into a stage or record it as a Local Customization — never silently kept in place, because nothing outside the markers is reconciled stage-by-stage. + +## Step 7: Write & Surface for Review + +Write the artifacts without waiting for approval — generation is reversible (re-run `/awos:flow` to revise), so the deliverable must never be gated behind a confirmation an unattended run cannot answer. The protection runs the other way: manual edits to an existing generated command survive unless the user explicitly chose otherwise in Step 6. After writing, present every file for review and apply any requested adjustments. The generated commands go under `.claude/commands/` at their recorded filenames (default `implement-feature.md` / `fix-bug.md`) — the project's own command namespace, deliberately outside `.claude/commands/awos/`, so neither the AWOS installer nor a framework update ever touches them. + +## Step 8: Final Summary + +Report: + +- **Decision Record:** path, and which dimensions changed (on re-runs). +- **Generated Command(s):** report each generated command by its recorded name. The feature command (default `/implement-feature `) — what its stages are and where customizations were preserved. When the bug-fix flow was selected, also the bug-fix command (default `/fix-bug `) — its lighter stage set (`diagnose → fix → scoped re-verify → targeted spec amendment`) and where it still stops for a human (e.g. a manual merge). +- **Interaction budget:** every human-pause the generated command will actually have on a run — not just the approval gates. Enumerate them and tag each by source: gate-controlled (the §4 spec/tech gates), reuse-imposed (each reused interactive skill's confirmations — e.g. `commit-validated` asking once per commit, `create-pr` confirming before it opens the change request, the always-on merge confirmation), and chain-imposed (interviews inside the AWOS commands themselves — e.g. `/awos:spec`'s requirements clarification). This makes "how autonomous is it really" visible at generation time: a flow with "no gates" can still pause a dozen times through reused skills and chain interviews, and the user can re-run to re-tune (raise autonomy on a reused skill via the Step 4.5 tradeoff, or accept the pauses). +- **Still manual / automation opportunities:** the stages this flow does not yet automate — where it stops for a human (an approval gate, a manual merge, a manual deploy) and any step you found no transport or decision for — with the concrete next move to close each gap. This is the assistant's job: surface what could still be automated rather than implying the flow is fully hands-off. +- **Tooling gaps:** any service with no working transport, and what to install or configure. +- **Trigger setup notes:** if the team chose unattended runs, the concrete configuration — the exact invocation to schedule, the scheduler, and the prerequisites (permission mode, `claude` on PATH, `AWOS_UNATTENDED=1` in the driver's environment, headless-capable transports) — not just a pointer at the idea. +- **Project-side setup fixes:** any project config the flow needs but cannot set itself. When Step 2 found a repo-wide format/lint gate, advise adding the AWOS working files (`context/spec/**/flow-log.md` and the generated command files) to the project's ignore (`.prettierignore`, lint excludes) so the flow's own output doesn't fail the gate. When Step 4.5 recorded a hardcoded-constant override instead of a fix, restate it here. When Step 2 found an always-loaded routing policy that prescribes the manual `/awos:*` chain without naming the generated commands, advise updating it and offer the concrete wording — a line per generated command stating when an agent should reach for it, plus a pointer to `context/product/delivery-flow.md` as the decision record behind them. These are project-side changes the user applies — the flow flags them, it does not make them. + +End by telling the user to commit the generated artifacts — `context/product/delivery-flow.md` and `.claude/commands/implement-feature.md` (and `.claude/commands/fix-bug.md` when the bug-fix flow was generated) — so the first real run starts from a clean tree instead of warning on the flow's own uncommitted output. + +Then the next step: run `/awos:spec` to continue the chain manually, or try the new command directly with a real feature: `/implement-feature `. diff --git a/plugins/awos/templates/delivery-flow-template.md b/plugins/awos/templates/delivery-flow-template.md new file mode 100644 index 00000000..881b5701 --- /dev/null +++ b/plugins/awos/templates/delivery-flow-template.md @@ -0,0 +1,125 @@ +# Delivery Flow + +Generated by `/awos:flow` on [YYYY-MM-DD]. This file is the single source of truth for the team's delivery decisions — `.claude/commands/implement-feature.md` and any future flow commands are derived from it. Re-run `/awos:flow` to change a decision; edit the generated command directly only for details no decision covers (re-runs will offer to promote such edits into §10). + +- **Status:** Draft | Approved +- **Team Docs Consulted:** [every pointer found by investigation or provided by the user — URLs, file paths, or resource identifiers such as a Slack channel or page title — or "none"] + +--- + +## Project Setup + +The canonical project config every generated flow command checks against. Flow-agnostic — both `implement-feature` and any sibling command (e.g. `fix-bug`) reuse these facts; a reused skill with a hardcoded constant that disagrees with one of these is reconciled at generation time. + +- **Atlassian/Jira base URL:** [the actual instance host — e.g. `https://acme-dev.atlassian.net` — derived from the ticket URL, git remotes, or CI config; or "n/a — no tracker"] +- **Slack channel & handles:** [the team channel and any team/group handles the flow posts to — or "n/a"] +- **Code-host org/repo:** [from `git remote` — e.g. `acme/payments-api`] +- **Format/lint gate scope:** [whether the project's format/lint gate runs over the whole repo (so AWOS working files must be added to its ignore) or is already scoped — or "none"] +- **Reused-skill overrides:** [any hardcoded constant in a reused skill the user chose to keep, with why — or "none"] + +## Generated Commands + +Which commands `/awos:flow` generated and their names — re-runs reconcile exactly these files. + +- **Feature command:** [slash name and file — e.g. `/implement-feature` → `.claude/commands/implement-feature.md` — or "not generated"] +- **Bug-fix command:** [slash name and file — e.g. `/fix-bug` → `.claude/commands/fix-bug.md`, or a rename such as `/fix` → `.claude/commands/fix.md` — or "not generated"] + +## 1. Feature Description Source + +- **Source:** [Jira | Azure DevOps | Linear | Notion | GitHub/GitLab issue | local file | prompt text | pre-generated spec] +- **Fetch transport:** [CLI command / MCP tool / plugin — from §7; name the fallback if the primary is unavailable] +- **Normalization:** [what the flow extracts: ID, title, description, acceptance hints, link] +- **Pre-generated specs:** [whether `context/spec/` directories may arrive pre-written, and the entry-point detection rule — resume from the first missing artifact] + +## 2. Git Flow + +- **Base branch:** [e.g. `origin/main`] +- **Branch naming:** [convention + example] +- **Submodules:** [none | init/update steps] +- **Target sync & conflicts:** [rebase onto target | merge target in — checked before opening the change request and again before merging (the target moves while gates run); conflicts resolved by a subagent, non-trivial resolutions confirmed with the user, local gates re-run after any sync] +- **Worktrees:** [viable — isolation recipe, including the bring-up steps a fresh worktree needs for its git-ignored prerequisites (install command, codegen steps, env-file copies — derived from `.gitignore`, the lockfile, and package scripts) | main-repo-only — blocking shared resources and why] +- **Sanctioned verification path:** [when a shared resource (a port a running service holds, a single database, a device) is reserved by the guardrail, how the verify stage still drives the app against a real render — stop/restart the service, an alternate port, a throwaway instance, or the §5 deploy/run step. The generated command uses this to self-verify rather than handing the user a `run` command; "n/a — nothing shared blocks a run" when no such resource exists] + +## 3. Repository Topology + +- **Layout:** [monorepo | submodules | sibling repos at relative paths | symlinked | containerized] +- **`context/` location & sharing:** [where the AWOS context folder lives and how this repo reaches it] +- **Spec commits go to:** [which repo/branch holds `context/spec/` changes] +- **Pre-flight check:** [how the flow verifies `context/` is reachable and current] + +## 4. Review + +[Ordered list of gates the flow runs, e.g.:] + +1. [Static checks — what runs, where] +2. [Local AI review — file convention, human-edit loop, learning-loop into CLAUDE.md] +3. [Automatic reviewer on the code host — a third-party bot (e.g. CodeRabbit) or a project-built AI reviewer (e.g. a GitHub Action calling Claude); whether the flow waits for its review after opening the change request and addresses its findings, and whether its pass/fail drives a §5 ticket-state transition] +4. [Remote PR review — platform, human reviewers, wait-or-poll policy] +5. [CI on the change request — which pipelines trigger, the typical duration from recent runs (the flow polls at matching intervals), and the policy: wait + fix failures in a loop | report first results and hand off | no CI (local suite is the gate)] +6. [Environment/soak/compliance gates — if any] + +- **Approval gates:** [after spec and after tech — most reliable | one gate after spec + tech — faster | none; tasks.md has no gate by default — `/awos:implement` starts right after `/awos:tasks`] +- **Change-request timing:** [after local review — CI runs only on reviewed code | before local review — remote gates start earlier, one extra CI run on unreviewed code] +- **Max-wait & escalation:** [how long the flow waits on remote gates before escalating, whether it auto-relaunches the monitor when a poll window expires, and the threshold past which it asks the human instead of waiting — the generated `Monitor` timeout is sized to this] + +## 5. Delivery + +- **Mode:** [deploy-when-ready | batched/sprint | feature-flagged partial rollout] +- **Merge policy:** [a human merges | the flow merges once gates are green — always behind a per-run user confirmation, never unconfirmed; transport per §7, or local `git merge` for repos without a code host] +- **Post-merge CI:** [pipelines triggered on the base branch by the merge, and the policy: wait + fix forward | wait + report | none] +- **Approvals:** [who signs off, where recorded] +- **Versioning:** [what gets bumped, where, by whom] +- **Deployment:** [manual CD job | scheduled | fully automatic | local deploy step — the command, and when the flow runs it: after the merge | after post-merge CI is green | never] +- **Ticket state transitions:** [the event→state map across the whole cycle, in the tracker's own state names — start (→ In Progress), change request opened (→ In Review), a gate/review failing (→ back to needs-work, e.g. To Do), merged, done — validated at generation time against a sample issue's available transitions: record the full transition chain per event (e.g. `Open → In Progress → In Review` when the tracker has no direct hop) plus transition IDs where the tracker exposes them; or "n/a — ticketless source"] +- **Definition of Done:** [the evidence that closes the loop — may include post-merge pipelines green; plus the final ticket transition, only when the source has tickets to transition] + +## 6. Trigger + +- **Supported:** manual — `/implement-feature ` +- **Wanted (setup notes only):** [polling via `/loop`/cron, webhook — what to set up, or "none". For unattended runs, the driver chains headless stage invocations (`claude -p "/implement-feature "`; resume-detection makes each idempotent) — record the prerequisites the operator owns: the machine, `claude` on PATH, a permission mode that allows edits, `AWOS_UNATTENDED=1` exported in the driver's environment (so the command takes safe defaults on the 60s no-answer instead of re-asking a human who isn't there), and headless-capable transports] + +## 7. Tooling Inventory + +| Service | CLI | MCP | Plugin/Skill | Chosen Transport | +| ------- | --- | --- | ------------ | ---------------- | +| [Jira] | [—] | [✓] | [—] | [MCP — no CLI] | + +**Stage automation (reuse / replace / compose):** [for each stage where the project already had an overlapping command or skill — the stage, the existing automation, the decision (reuse it / replace with generated / compose), and the reason. "none" if the project had no overlapping automation.] + +## 8. Context Strategy + +How the generated command keeps its context window small — one long window degrades judgment (worst exactly at review time) and inflates cost. + +- **Mode:** single session with subagent-isolated stages [the default — works under any permission setup; chained headless sessions exist only at the trigger layer, per §6] +- **Stages isolated in subagents:** [every stage that needs no user interaction and dispatches no subagents of its own — each invokes its command via the Skill tool and returns a terse report] +- **Stages kept in the main context:** [stages that interview the user or themselves dispatch subagents — list each with its reason, as determined at generation time] +- **Flow log:** `context/spec/{SPEC_NAME}/flow-log.md` — the flow's resumable memory; what each entry records and how it keeps the window small is defined in the generated command's Context Discipline section. Committed with the work and finalized at commit-push: the flow stops writing to it once the change request is opened or merged (new commits can't reach a merged/in-review change request), so it never becomes an uncommittable leftover; late-stage progress is reported and announced instead, and the remote stages resume from remote state +- **Model tiers:** [per delegated stage, with reasons — the fast tier for mechanical transport work (ticket fetch, CI polling, log triage), the strongest for judgment (design, review, validation); tiers, never model names] + +## 9. Notifications + +Where the flow announces itself so the team stays aware as gates are removed — "human on the loop", not "in the loop". + +- **Channel:** [team channel and transport from §7 — Slack, Teams, a PR comment — or "none (interactive runs only)"] +- **Announce on:** [the transitions that post — e.g. spec ready, change request opened, gates passed, merged, deployed, blocked-and-waiting; favor events that need a human or change shared state] + +## Bug-fix Flow + +Whether `/awos:flow` generated the lighter bug-fix command (named in **Generated Commands** above), and its policy. Flow-agnostic — the bug-fix command consumes §1–§9 above and only its middle stages differ. + +- **Generated:** [yes — see Generated Commands for the name/file | no] +- **Bug source:** [where bug reports come from — a tracker ticket, a crash report from a crash-reporting tool (e.g. Crashlytics, Sentry; transport per §7, stack symbolicated and mapped to local `file:line`), a plain description, or several of these — or "n/a (not generated)"] +- **Classification & amendment policy:** [every fix is classified conformance (code violated a correct spec → fix + regression test, no spec change) vs. divergence (spec was wrong or behavior intentionally changed → fix + regression test + amend the owning `functional-spec.md` via `/awos:spec` update mode); a bug mapping to no spec proceeds without amendment] +- **Regression-test expectation:** [a fix adds one failing→passing test capturing the bug, honoring the project's `` opt-out — or "n/a (not generated)"] + +## 10. Local Customizations + +Manual edits to the generated command that the user chose to keep during regeneration, and in-run corrections promoted by the generated commands' **Self-Improvement Loop** (a flow defect found during a run — a wrong recorded fact, a missing step, a workaround-forcing instruction — fixed in the same run and shipped in the same change request). `/awos:flow` re-applies these on every regeneration; remove an entry to retire it. + +- [stage: short description of the customization and why — or "none"] + +--- + +## Generation Log + +- [YYYY-MM-DD] — [initial generation | re-run: which dimensions changed | in-run fact correction by `` run ``: what was corrected — evidence: the disproving observation] diff --git a/plugins/awos/templates/fix-bug-template.md b/plugins/awos/templates/fix-bug-template.md new file mode 100644 index 00000000..9acb4d3f --- /dev/null +++ b/plugins/awos/templates/fix-bug-template.md @@ -0,0 +1,236 @@ +--- +description: Fixes one bug end-to-end — diagnoses the root cause, applies a scoped fix with a regression test, re-verifies the touched criteria, and amends the spec when behavior changed. +argument-hint: '[bug — report ID, link, or description]' +--- + + + +# Fix a Bug End-to-End + +Takes one bug — its report from [source per §1 of delivery-flow.md, a bug report rather than a feature ticket] — and drives it through diagnosis, a scoped fix with a regression test, re-verification of the touched acceptance criteria, and delivery until it is closed. On the way it keeps the owning spec honest: when the fix changes documented behavior, it amends that spec rather than letting it drift. + +## Notifications + +[Per §9 of delivery-flow.md: on each recorded transition (e.g. root cause found, fix pushed, change request opened, gates passed, merged, closed, blocked-and-waiting), post a short status to the team channel via its §7 transport. Omit this section entirely if §9 records "none".] + +## Arguments + +`$ARGUMENTS` — [expected bug reference shape per §1: a report ID, URL, or a free-text description]. If empty, ask the user. + +## Context Discipline + +A flow degrades in one long context window. Per §8 of delivery-flow.md: + +- Run every isolatable stage in a subagent (a subagent can invoke `/awos:*` commands via the Skill tool; its context is discarded on completion). Subagent reports must be terse — paths, verdicts, counts — never full diff, log, or review content. +- After each completed stage, append an entry to `context/spec/{SPEC_NAME}/flow-log.md` (or `context/fix-log-{BUG_ID}.md` when the bug maps to no spec): the stage name, what was produced and where (paths, branch, commit), the classification verdict once known, any decisions taken, and which stage comes next. The log is the flow's memory outside the context window — a fresh session resumes by reading this one small file. It is committed with the work (commit-push stages it alongside the code), so it must never become an uncommittable leftover: **once the change request is opened — or the change is merged — stop writing to the tracked log.** New commits are unwelcome on a change request under review or already merged, so a late append would strand a change that can never reach it. From that point, report late-stage progress (gate results, merge, close-out evidence) to the user and via §9 notifications, and resume the remote stages from remote state — the open/merged change request and the ticket status, which the resume-detection stage already inspects. The close stage leaves a clean working tree and never writes a final entry it cannot commit. +- Never launch a nested headless session (`claude -p`) from this command — permission modes, PATH, and timeouts differ per machine. Unattended chaining belongs to the trigger setup (§6), outside this command. +- Tell every dispatched subagent: tools are functional — do not test them or make exploratory calls; every call needs a purpose. Run each delegated stage on the model tier recorded in §8 — the fast tier for mechanical transport work, the strongest for judgment (diagnosis, classification, review). +- A subagent's report is a claim, not a fact. Before acting on a report that names files and lines, asserts a root cause, or reports a test outcome, spot-check it — read the named lines, run the named test. The diagnose and regression-test stages spell out their specific checks; the principle applies to every delegated report. +- Every fixed-choice interaction with the user — a per-run choice the decisions left open (e.g. main repo vs. worktree), the divergence confirmation, keep/drop on review findings, the merge confirmation — goes through `AskUserQuestion` with the recorded default marked, never a prose question. Plain prose is only for inherently free-form input (a bug description, a file path). +- An unanswered `AskUserQuestion` (the harness returns `No response after 60s` — its guard so unattended runs never hang) is handled by run mode. Read the `AWOS_UNATTENDED` environment variable: when it is set (the §6 trigger setup exports it for cron/`/loop`/`claude -p` drivers), a no-answer is expected — take the safe default and continue. When it is unset the run is interactive, and a timeout usually means the user is thinking or briefly away, not that they have no preference — re-ask the question once, then proceed naming the default you took so they can correct it. A timeout never authorizes an irreversible step: the merge confirmation and the divergence spec-amendment confirmation each treat an unanswered prompt as a no in either mode. + +This command is an orchestrator. It diagnoses and decides, but the code change goes through a delegated specialist — **do not edit code in the main context**. + +## Self-Improvement Loop + +This command is maintained through its own runs. When a run exposes a defect in the flow itself — a recorded fact disproven by reality (a "no X" claim, a dead link, a wrong state name), a missing step (an undocumented bootstrap, a transition chain), or a stage instruction that had to be worked around — fix the flow **in the same run**: + +1. Patch the affected file(s) right in this working copy: this command file, `context/product/delivery-flow.md` (correct the fact where it is recorded), or the reused skill. +2. Stage those edits in the commit-push stage alongside the code change — same branch, same change request. Never park flow fixes for a separate change request; a flow that shipped its work while still carrying a known-wrong instruction has not finished the job. A defect found after the change request is open waits: report it as pending in the close-out, and the next run applies it at the workspace stage. +3. Record the correction in the flow log — with the observation that disproved the old text — and promote it into the decision record's **Local Customizations** section, so a future `/awos:flow` regeneration preserves it instead of resurrecting the defect. +4. Two kinds of defect are not yours to fix. A delivery _decision_ (a gate, the merge policy, the autonomy level) belongs to whoever owns the team's process — report the friction and leave the change to a `/awos:flow` re-run. A defect in how `/awos:flow` generated this command cannot be fixed here — tell the user so they can report it to the AWOS repo. + + + +### Step 1: Fetch & Normalize the Bug + +[Connector-specific fetch using the chosen transport from §7 of delivery-flow.md, with its recorded fallback — reuse §1, but the source is a bug report rather than a feature ticket. Extract and keep: bug ID, title, the reported symptom, reproduction steps if given, affected area, link. For description-only sources this stage just normalizes the input. Store the bug ID as `BUG_ID`.] + +[Ticket sources: also fetch the ticket's **remote links, attachments, and linked conversations** (tracker remote links, attached screenshots, a linked chat thread) via the §7 transports and read the reachable ones — the report's real context often lives there, not in the description: a screenshot names WHICH surface renders the broken data while the description names another. List anything linked but unreachable in the normalized report instead of silently skipping it, so the diagnosis knows context is missing. Omit this paragraph for description-only sources.] + +[Crash-report source (per the Bug-fix Flow source decision — e.g. Crashlytics, Sentry): fetch the issue and its most recent events via the §7 transport for the crash tool, and use the title/subtitle as the problem statement. Map every app-frame in the stack to a real `file:line` in the local checkout (Grep/Read), ignoring system frames. **If the stack is unsymbolicated** (raw addresses, no file/line), say so explicitly and do not invent line numbers — the symbol file (dSYM/source map) for that build was likely not uploaded. Capture impact — affected versions, user count, first/last-seen — and prefer a source-typed branch name (e.g. `bug/crash-`) per §2. The diagnose stage starts from this stack context. Omit this paragraph when the bug-fix source decision does not include crash reports.] + + + + + +### Step 2: Detect the Entry Point + +Start with a cheap preflight on the fast model tier (per §8): is this bug **already fixed**? Check the status across every source §1 records (bug reports can live in more than one place) before doing any work — if the tracker ticket is in a closed/fixed state, the crash issue is already resolved, or a merged change request exists, report that and stop rather than re-fixing. Then, if a flow log for this bug exists (`context/spec/{SPEC_NAME}/flow-log.md`, or `context/fix-log-{BUG_ID}.md`), read it first — it names the last completed stage and carries the branch, commit, classification verdict, and change-request state, and is the resume signal for the middle stages that produce no scannable artifact. + + + + + +### Step 3: Prepare the Workspace + +[Per §2–§3 of delivery-flow.md: verify `context/` is reachable and current; warn on a dirty working tree (uncommitted AWOS artifacts left by `/awos:flow` are an expected cause, not a blocker); create the branch from the base branch using the team's naming convention; submodule init/update if required. For a worktree: invoke the project's own worktree command, skill, or init script when §2 records one, otherwise execute the §2 isolation recipe — bring-up steps included — verbatim. Never improvise worktree preparation in-run: real prep is bigger than `git worktree add` (installs, codegen, env files, service/network isolation), and the recorded recipe or project script is the tested path. Store the branch name as `BRANCH`.] + + + + + +### Step 4: Diagnose + +Reproduce the bug and find the root cause. Delegate the investigation to the built-in `Explore` subagent or a debugging specialist via **[Agent: name]** (per §8 model tier — judgment work) — the orchestrator does not read the whole codebase or write code itself. The subagent returns terse: the reproduction, the root-cause location (file/function), and a proposed minimal fix shape. If the bug cannot be reproduced, report that and stop rather than guessing at a fix. + +A symptom rarely has exactly one renderer. The diagnosis is not done at the first root cause: it must enumerate **every surface that renders or consumes the symptom data** — grep for sibling composers of the same output (other builders of the same title/string, widgets showing the same records, backend notification builders) — and return a verdict per surface, affected or clean. One data gap routinely hides behind several surfaces; fixing the named one and shipping leaves the others to come back as "reopened". + +The diagnosis report labels every claim **verified** (the subagent read the named `file:line`, or executed the reproduction) or **hypothesis** — and the orchestrator re-reads the named lines before accepting the fix shape, trimming it to what the code actually shows. A subagent report is a claim, not a fact: a road-test diagnosis proposed a three-file fix where re-reading the cited lines showed one changed line sufficed. + + + + + +### Step 5: Classify — Conformance vs. Divergence + +This gate decides whether the spec gets amended later, so it runs **before** any fix touches behavior. Locate the owning `context/spec/NNN-*/` for the affected behavior (read its `functional-spec.md`), then classify: + +- **Conformance bug** — the code violates a _correct_ spec. The acceptance criteria were right; the code was wrong. → Fix the code and add a regression test; **do not** amend the spec. +- **Divergence** — the spec was wrong or incomplete, or the fix intentionally changes documented behavior. → Fix, add a regression test, and **amend** the owning spec in the `amend-spec` stage. + +If the bug maps to **no** existing spec (legacy or cross-cutting behavior), do not fabricate one — record "no owning spec" and proceed without amendment. Record the verdict and the owning spec dir (or "none") in the flow log; later stages read it. + + + + + +### Step 6: Fix + +Delegate the code change to a specialist via **[Agent: name]** (chosen from §8 / the hired roster for the affected area) — the orchestrator never edits code itself. Keep the change scope-disciplined: a flat task list targeting the root cause, no vertical slicing, no opportunistic refactors beyond what the fix needs. Pass the subagent the root-cause findings from Step 4 and the classification, not a re-derivation. + + + + + +### Step 7: Regression Test + +Add one test that fails on the old code and passes on the fix, capturing the bug so it cannot silently return. Delegate it to the testing specialist via **[Agent: name]**. Honor the `` marker: if the owning spec's `tasks.md` carries it (the team opted out of generated test suites), skip adding an automated test and note that the regression is covered by the look-and-feel check in the next stage instead. + +"Fails on the old code" is demonstrated, not asserted. The orchestrator verifies the test targets a **changed** site: revert the fix hunk (e.g. stash the fixed files), run the test and watch it fail, restore the fix, watch it pass — then record the fail→pass evidence in the flow log. A subagent can return a green-but-vacuous test that asserts a path that was already correct before the fix; green-on-old-code means the test captures nothing — reject it and have the specialist retarget the changed lines. + + + + + +### Step 8: Verify the Touched Criteria + +Re-check **only** the acceptance criteria the bug touched, with `/awos:verify`'s evidence discipline — drive the UI/API for real, screenshot visual criteria to `docs/screenshots/`, and `AskUserQuestion` only when a criterion has no agent-driven render path at all. This is scoped: it does not re-run the whole acceptance set, does not flip the spec's Status, and honors `` (look-and-feel walk-through only, no test suites). Report the criteria checked and their evidence. + +Running the app to verify is the flow's job, not the user's. [If §2/§3 recorded a shared resource the app binds — a port a running service holds, a single database, a device — the workspace guardrail reserves it for normal work, but this stage still verifies against a real render: reclaim the resource (stop the service, use an alternate port, spin a throwaway instance) or drive the project's own §5 deploy/run step and verify against that, per the sanctioned verification path §2/§3 records. Do not hand the user a `run` command to execute, and do not defer a drivable criterion to a later manual deploy — the manual `AskUserQuestion` fallback is only for a criterion the agent genuinely cannot render here.] + +Scale the evidence to what changed. When the fix touched only the data or payload and the diff contains no render-path edits, the sanctioned evidence is the demonstrated failing→passing regression test plus a unit-level render of the changed data with mocks — standing up the full stack (backend, database, seeded data) to watch an unchanged render branch repeat itself is disproportionate. This tier applies only when the render path is provably untouched by the diff; a fix that edits the render path itself still drives the UI/API for real. + + + + + +### Step 9: Amend the Spec (on divergence) + +Conditional on the Step 5 verdict: + +- **Conformance** — nothing to amend; the spec was already correct. Skip to the next stage. +- **Divergence** — confirm the amendment with the user first (`AskUserQuestion`: amend the spec / leave as a pending divergence — amending changes documented behavior; an unanswered confirmation means do not amend). Then invoke `/awos:spec` in update mode for the owning spec, passing the spec directory and a description of the behavior change (e.g. `/awos:spec amend spec NNN: `). `/awos:spec`'s Mode Detection routes this to its Update Mode, which edits the affected acceptance criteria in place and appends a dated `## Change Log` entry — no new spec index is allocated, and a `Completed` Status is left untouched. Do not duplicate the amendment prose here; the amendment capability lives in core `/awos:spec`. + +In either case, if the fix revealed that `product-definition.md` or `architecture.md` also drifted, surface the same `/awos:product <…>` / `/awos:architecture <…>` suggestions `/awos:verify` Step 5 emits — as suggestions, never auto-edits. + + + + + +### Step 10: Local Review + +The review must stay independent of this conversation's authorship bias — it never happens inline in the orchestrator's own window, which just drove the fix: + +[Per §4 and the §8 context strategy, one of two shapes — decided at generation time by inspecting the review automation, never at run time: + +- The project has a review skill/command that itself dispatches subagents (most do): the orchestrator invokes it from the **main context** via the Skill tool — its own reviewer subagents provide the fresh, unbiased contexts. Do not wrap it in a subagent: agents do not nest, and this orchestrator already occupies the coordinator slot. +- Otherwise: dispatch a dedicated **reviewer subagent** with the fixed verbatim prompt written here at generation time (diff range, spec paths, the project's review rules). + +In both shapes run the §4 static checks first, and keep the invocation fixed — do not add run-time focus areas drawn from what was fixed; the author framing the review is the bias.] + +The reviewer writes findings to a review file and returns only the verdict, the finding count by severity, and the file's path. Lead the presentation to the user with that path on its own line — e.g. `Review file: ` — before the verdict and findings, and record the same path in the flow log. Collect a keep/drop decision on the findings (`AskUserQuestion`), apply only accepted ones — via a fresh agent that reads the review file and the diff, never from your summary — and re-run the static checks after fixes. + + + + + +### Step 11: Commit & Push + +Write this stage's flow-log entry **before** staging so the log rides in this commit — this is the flow-log's last committed state (see Context Discipline). Then stage all changed files, excluding `.env`, credentials, and secrets. [Commit message convention per §2, referencing `BUG_ID`; pre-commit hook failures: fix and amend.] Push `BRANCH` to the remote. + + + + + +### Step 12: Remote Gates + +From here the change request is open — **do not append to the tracked flow-log** (Context Discipline): a commit adding log lines is unwelcome on a change request under review, and impossible once it merges. Report gate progress to the user and via Notifications instead; resume relies on the remote state, not the log. + +[Per §2 sync policy: before opening the change request, fetch the target branch and verify the branches merge cleanly — a dry-run merge or rebase. On conflicts: delegate resolution to a subagent (per §8), re-run the local gates on the resolved result, and push.] + +[Per §4: open the change request via the chosen transport from §7, then wait on every remote gate concurrently — CI checks, the automatic reviewer's pass (address its findings), human review (wait-or-poll policy), environment/soak/compliance gates — and join them before merge. On CI failure, per the recorded policy: delegate diagnosis and the fix to a subagent working from the failed job's logs, push, re-check until green — or report the first results and hand off. For a repo with no code host, the local suite already served as the gate — omit this stage and any other gate §4 rules out.] + +[Per §5's ticket-state map (omit for ticketless sources): transition the ticket to the in-review state when the change request opens, and back to the needs-work state if a gate or review fails, re-advancing it when the gates go green again. Follow the recorded transition chain for each event (including intermediate hops), not just the target state name.] + +Wait with the `Monitor` tool, never foreground `sleep` loops: a poll loop that emits each gate's terminal result and exits when all are settled, its timeout sized to the typical pipeline duration recorded in §4, the poll interval 30s+ against remote APIs, and the filter covering every terminal state — failures and cancellations, not just success. Apply §4's max-wait & escalation policy when a poll window expires without the gates settling — auto-relaunch the monitor, or ask the human past the recorded threshold; never wait forever. + + + + + +### Step 13: Merge + +[Per §2: the target branch may have moved while the gates ran — re-check mergeability via the chosen transport or a fresh fetch + dry-run merge. If it no longer merges cleanly: sync per the recorded policy (resolution delegated per §8), push, and return to Step 11 — the remote gates run again on the new commit before any merge.] + +[Per §5 merge policy: a human merges — stop here and report the ready-to-merge state — or the flow merges via the chosen transport from §7, or a plain `git merge` + push for a repo without a code host.] + +Merging is irreversible. Even when the recorded policy lets the flow merge, ask the user for confirmation in this run (`AskUserQuestion`: merge / don't merge), after showing that every gate is green. A skipped or unanswered confirmation means do not merge — report the ready-to-merge state and stop. + +[Per §5 post-merge CI: pipelines triggered by the merge on the base branch — watch them via the chosen transport and, per the recorded policy, fix failures forward or report them. Omit if nothing runs on merge.] + + + + + +### Step 14: Close the Ticket + +[Per §5's definition of Done: gather the recorded evidence and report the final state to the user. When the source has tickets, transition the bug to its closed/fixed state using the chosen transport and attach the evidence; omit the transition for ticketless sources — the report to the user is the close.] + +Include the local review and the spec-amendment outcome in the reported evidence, so neither is buried in the logs. From the flow log, report: the review **verdict**, the **finding count** (by severity), the **review file path** (`context/spec/{SPEC_NAME}/review.md`, or the path a reused review command used), that a manual keep/drop gate ran over the findings, and — for a divergence fix — that the owning spec was amended (the criteria touched and the Change Log entry). The path lets the user re-open the full review without re-running. + +[Crash-report source: optionally write a short investigation note back to the crash issue via the §7 transport — root cause, branch, files touched — but never auto-close it; a crash resolves on its own once a non-crashing build ships.] + +Leave a clean working tree: do not write a closing flow-log entry (the log was finalized at commit-push and the change request is now open or merged — a new entry could never be committed into it). If any flow-created artifact is still uncommitted, surface it in the report rather than leaving it behind — an uncommitted leftover after a merged or in-review change request is a bug, not a record. + + + +--- + + diff --git a/plugins/awos/templates/implement-feature-template.md b/plugins/awos/templates/implement-feature-template.md new file mode 100644 index 00000000..8019f5d7 --- /dev/null +++ b/plugins/awos/templates/implement-feature-template.md @@ -0,0 +1,198 @@ +--- +description: Implements one feature end-to-end — fetches its requirements, runs the AWOS chain, and delivers per the team's flow. +argument-hint: '[feature — ticket ID, link, or file path]' +--- + + + +# Implement a Feature End-to-End + +Takes one feature — its requirements from [source per §1 of delivery-flow.md], wherever they come from — and drives it through spec, implementation, verification, review, and delivery until it is Done. + +## Notifications + +[Per §9 of delivery-flow.md: on each recorded transition (e.g. spec ready, change request opened, gates passed, merged, deployed, blocked-and-waiting), post a short status to the team channel via its §7 transport — so the team stays aware as the flow runs unattended. Omit this section entirely if §9 records "none".] + +## Arguments + +`$ARGUMENTS` — [expected ticket reference shape per §1: ID, URL, or file path]. If empty, resume from the next incomplete item in `context/product/roadmap.md` (the first unchecked `- [ ]`, as `/awos:spec` does); if the roadmap is missing or fully complete, ask the user. + +## Context Discipline + +A flow this long degrades in one context window — judgment is worst exactly where it matters most, at review time. Per §8 of delivery-flow.md: + +- Run every isolatable stage in a subagent (a subagent can invoke `/awos:*` commands via the Skill tool; its context is discarded on completion). Subagent reports must be terse — paths, verdicts, counts — never full document or review content. +- After each completed stage, append an entry to `context/spec/{SPEC_NAME}/flow-log.md`: the stage name, what was produced and where (paths, branch, commit), any decisions taken along the way, and which stage comes next. The log is the flow's memory outside the context window — a fresh session (after a restart, a crash, or an unattended hand-off between sessions) resumes by reading this one small file instead of re-deriving state from the whole repo. That is what keeps the window small across a long flow: nothing needs to stay in context once it is in the log. The log is committed with the work (Step 9 stages it alongside the code), so it must never become an uncommittable leftover: **once the change request is opened — or the change is merged — stop writing to the tracked log**, since a commit adding log lines is unwelcome on a change request under review and impossible once it merges, so a late append would strand a change that can never reach it. From that point report late-stage progress to the user and via §9 notifications, and resume the remote stages from remote state (the open/merged change request and the ticket status), which the resume-detection stage already inspects. The close stage leaves a clean working tree and never writes a final entry it cannot commit. +- Never launch a nested headless session (`claude -p`) from this command — permission modes, PATH, and timeouts differ per machine. Unattended chaining belongs to the trigger setup (§6), outside this command. +- Tell every dispatched subagent: tools are functional — do not test them or make exploratory calls; every call needs a purpose. Run each delegated stage on the model tier recorded in §8 — the fast tier for mechanical transport work, the strongest for judgment. +- A subagent's report is a claim, not a fact. Before acting on a report that names files and lines, asserts a root cause, or reports a test outcome, spot-check it — read the named lines, run the named test — rather than relaying it verbatim into the next stage. +- Every fixed-choice interaction with the user — a per-run choice the decisions left open (e.g. main repo vs. worktree), an approval gate verdict, keep/drop on review findings, the merge confirmation — goes through `AskUserQuestion` with the recorded default marked, never a prose question. Plain prose is only for inherently free-form input (a feature description, a file path). +- An unanswered `AskUserQuestion` (the harness returns `No response after 60s` — its guard so unattended runs never hang) is handled by run mode. Read the `AWOS_UNATTENDED` environment variable: when it is set (the §6 trigger setup exports it for cron/`/loop`/`claude -p` drivers), a no-answer is expected — take the safe default and continue. When it is unset the run is interactive, and a timeout usually means the user is thinking or briefly away, not that they have no preference — re-ask the question once, then proceed naming the default you took so they can correct it. A timeout never authorizes an irreversible step: the merge confirmation treats an unanswered prompt as a no in either mode. + +## Self-Improvement Loop + +This command is maintained through its own runs. When a run exposes a defect in the flow itself — a recorded fact disproven by reality (a "no X" claim, a dead link, a wrong state name), a missing step (an undocumented bootstrap, a transition chain), or a stage instruction that had to be worked around — fix the flow **in the same run**: + +1. Patch the affected file(s) right in this working copy: this command file, `context/product/delivery-flow.md` (correct the fact where it is recorded), or the reused skill. +2. Stage those edits in the commit-push stage alongside the code change — same branch, same change request. Never park flow fixes for a separate change request; a flow that shipped its work while still carrying a known-wrong instruction has not finished the job. A defect found after the change request is open waits: report it as pending in the close-out, and the next run applies it at the workspace stage. +3. Record the correction in the flow log — with the observation that disproved the old text — and promote it into the decision record's **Local Customizations** section, so a future `/awos:flow` regeneration preserves it instead of resurrecting the defect. +4. Two kinds of defect are not yours to fix. A delivery _decision_ (a gate, the merge policy, the autonomy level) belongs to whoever owns the team's process — report the friction and leave the change to a `/awos:flow` re-run. A defect in how `/awos:flow` generated this command cannot be fixed here — tell the user so they can report it to the AWOS repo. + + + +### Step 1: Fetch & Normalize the Ticket + +[Connector-specific fetch using the chosen transport from §7 of delivery-flow.md, with its recorded fallback. Extract and keep: ticket ID, title, description, acceptance hints, link. For local-file or prompt-text sources this stage just reads/normalizes the input.] + + + + + +### Step 2: Detect the Entry Point + +Start with a cheap preflight on the fast model tier (per §8): is this feature **already done**? Check the status across every source §1 records (tickets can live in more than one place) before doing any work — if the tracker ticket is in a Done/closed state, or the owning AWOS spec is already `Completed` (or all its `tasks.md` items are `[x]`), or a merged change request exists, report that and stop. Don't re-run the chain over work that is already delivered. Then: if `context/spec/{SPEC_NAME}/flow-log.md` exists, read it first — it names the last completed stage and carries the branch, commit, and change-request state. The log is a convenience, not ground truth: for the spec-generation stages the on-disk artifacts win when they disagree with the log (a manual or partial rerun can leave it stale) — cross-check `context/spec/` and, if they differ, resume from the first missing artifact and repair the log to match before continuing. Past spec generation there is no such artifact to scan, so the log is the only resume signal. [Per §1: if a spec directory for this feature may already exist under `context/spec/`, inspect it and resume from the first missing artifact — skip `/awos:spec` if `functional-spec.md` exists, skip `/awos:tech` if `technical-considerations.md` exists, and so on. Omit the pre-written-spec handling if specs never arrive pre-written.] + + + + + +### Step 3: Prepare the Workspace + +[Per §2–§3 of delivery-flow.md: verify `context/` is reachable and current; warn on a dirty working tree; create the branch from the base branch using the team's naming convention; submodule init/update if required. For a worktree: invoke the project's own worktree command, skill, or init script when §2 records one, otherwise execute the §2 isolation recipe — bring-up steps included — verbatim. Never improvise worktree preparation in-run: real prep is bigger than `git worktree add` (installs, codegen, env files, service/network isolation), and the recorded recipe or project script is the tested path. Store the branch name as `BRANCH` and the ticket ID as `TICKET_ID` for later stages.] Uncommitted AWOS artifacts — `context/product/delivery-flow.md` and this command file, left by `/awos:flow` — are an expected dirty-tree cause; surface them as such rather than treating them as a blocker. + + + + + +### Step 4: Generate Specs and Tasks + +Run the AWOS commands sequentially, passing the normalized ticket as context. [Per §8: which of the three stay in the main context and which run in a subagent — a command that interviews the user must stay in main; a non-interactive one runs in a subagent returning the artifact path and a one-line verdict.] + +1. `/awos:spec` — [approval gate per §4's gate decision] +2. `/awos:tech` — [approval gate per §4's gate decision] +3. `/awos:tasks` — [no gate unless §4 records one] — proceed straight to implementation; the task list stays revisable by re-running `/awos:tasks`. + +Store the spec directory name (e.g. `007-tasks-api`) as `SPEC_NAME`. + + + + + +### Step 5: Commit Specs + +[Per §3: stage `context/spec/{SPEC_NAME}/` in the repo that owns it and commit using the team's message convention, referencing `TICKET_ID`.] + + + + + +### Step 6: Implement via Subagents + +Run `/awos:implement` [per §8: in the main context if it dispatches subagents itself — a command that dispatches subagents cannot run inside one]. It delegates all coding and tracks progress — do not implement tasks in the main context. Wait for all tasks to complete. + + + + + +### Step 7: Verify + +Run `/awos:verify` [per §8: in a subagent if it is non-interactive], returning the verdict and the list of gaps. Address gaps before proceeding. + +Running the app to verify is the flow's job, not the user's. [If §2/§3 recorded a shared resource the app binds — a port a running service holds, a single database, a device — the workspace guardrail reserves it for normal work, but verification still needs a real render: reclaim the resource (stop the service, alternate port, throwaway instance) or drive the project's §5 deploy/run step and verify against it, per the sanctioned verification path §2/§3 records. Don't hand the user a `run` command or defer a drivable criterion to a later manual deploy — manual confirmation is only for a criterion the agent genuinely cannot render here.] + + + + + +### Step 8: Local Review + +The review must stay independent of this conversation's authorship bias — it never happens inline in the orchestrator's own window, which just drove the implementation: + +- [Per §4 and the §8 context strategy, one of two shapes — decided at generation time by inspecting the review automation, never at run time. The project has a review skill/command that itself dispatches subagents (most do): the orchestrator invokes it from the **main context** via the Skill tool — its own reviewer subagents provide the fresh, unbiased contexts; do not wrap it in a subagent, since agents do not nest and this orchestrator already occupies the coordinator slot. Otherwise: dispatch a dedicated **reviewer subagent** with the fixed verbatim prompt written here at generation time.] +- The invocation is fixed: pass it verbatim. Do not add run-time focus areas drawn from what you implemented or suspect — the author framing the review is the bias. +- The reviewer writes its findings to a review file and returns only the verdict, the finding count by severity, and that file's path — never the full review body. The generated reviewer subagent writes to a fixed path, `context/spec/{SPEC_NAME}/review.md`; a reused project review command may write elsewhere — capture whatever path it used. +- **Lead the review presentation to the user with that path on its own line** — e.g. `Review file: context/spec/{SPEC_NAME}/review.md` — before the verdict and the findings. A fixed opening line is surfaced reliably; a path appended after a long findings list gets dropped (the recurring failure this guards against). Record the same path in this stage's flow-log entry, so it survives outside the chat even if the line is missed. +- Collect the keep/drop decisions on the findings with `AskUserQuestion`. The agent that applies accepted findings reads the review file and the diff fresh — relay the user's decisions, not your own summary of the findings. + +[Per §4 of delivery-flow.md: static checks, then the local AI review — the reviewer subagent's verbatim prompt, derived from §4 at generation time: the diff range, the spec paths, the project's review rules; findings presented to the user, never auto-fixed; accepted findings applied before anything is pushed. If §4 includes the human-edit loop, also diff the user's edits against the original review and suggest CLAUDE.md amendments for generalizable corrections. If §4 records change-request-first timing, move this stage after Step 9 instead and run the review concurrently with the remote gates — faster wall-clock, at the cost of an extra CI run on unreviewed code.] + + + + + +### Step 9: Commit & Push + +Write this stage's flow-log entry **before** staging so the log rides in this commit — this is the flow-log's last committed state (see Context Discipline). Then stage all changed files, excluding `.env`, credentials, and secrets. [Commit message convention per the team; pre-commit hook failures: fix and amend.] Push `BRANCH` to the remote. + + + + + +### Step 10: Remote Gates + +From here the change request is open — **do not append to the tracked flow-log** (Context Discipline): a commit adding log lines is unwelcome on a change request under review, and impossible once it merges. Report gate progress to the user and via Notifications instead; resume relies on the remote state, not the log. + +[Per §2 sync policy: before opening the change request, fetch the target branch and verify the branches merge cleanly — a dry-run merge or rebase. On conflicts: delegate resolution to a subagent (per §8), re-run the local gates on the resolved result, and push.] + +[Per §4: open the change request via the chosen transport from §7. Then wait on every remote gate concurrently rather than in sequence — CI checks (e.g. `gh pr checks`, `glab ci status`, the Azure DevOps CLI), the automatic reviewer's pass (address its findings), human review (wait-or-poll policy), environment/soak/compliance gates — and join them all before merge. On CI failure, per the recorded policy: delegate diagnosis and the fix to a subagent (per §8) working from the failed job's logs, push, re-check until green — or report the first results and hand off. For a repo with no code host, the local test/lint suite already served as the gate — omit this stage, along with any other gate §4 rules out.] + +[Per §5's ticket-state map (omit for ticketless sources): transition the ticket to the in-review state when the change request opens, and back to the needs-work state if a gate or review fails — so the developer sees it — re-advancing it when the gates go green again. Follow the recorded transition chain for each event (including intermediate hops), not just the target state name.] + +Wait with the `Monitor` tool, never foreground `sleep` loops: a poll loop that emits each gate's terminal result and exits when all are settled, its timeout sized to the typical pipeline duration recorded in §4, the poll interval 30s+ against remote APIs, and the filter covering every terminal state — failures and cancellations, not just success, because a monitor that only greps the success marker stays silent through a failed run. Apply §4's max-wait & escalation policy when a poll window expires without the gates settling — auto-relaunch the monitor, or ask the human past the recorded threshold; never wait forever. + + + + + +### Step 11: Merge + +[Per §2: the target branch may have moved while the gates ran — re-check mergeability via the chosen transport or a fresh fetch + dry-run merge. If the branch no longer merges cleanly: sync per the recorded policy (resolution delegated per §8), push, and return to Step 10 — the remote gates run again on the new commit before any merge.] + +[Per §5 merge policy: a human merges — stop here and report the ready-to-merge state — or the flow merges via the chosen transport from §7: the platform's merge capability, or a plain `git merge` + push for a repo without a code host.] + +Merging is irreversible. Even when the recorded policy lets the flow merge, ask the user for confirmation in this run (`AskUserQuestion`: merge / don't merge), after showing that every gate is green. A skipped or unanswered confirmation means do not merge — report the ready-to-merge state and stop. + +[Per §5 post-merge CI: pipelines triggered by the merge on the base branch — watch them via the chosen transport and, per the recorded policy, fix failures forward or report them. Omit if nothing runs on merge.] + + + + + +### Step 12: Deliver + +[Per §5: deployment mode, batching/feature flags, approvals, version bumps. Omit what the decisions rule out; stop at the recorded hand-off point for manual or scheduled deployment.] + + + + + +### Step 13: Close the Loop + +[Per §5's definition of Done: gather the recorded evidence (change-request link, merge commit, deploy confirmation) and report the final state to the user. When the source has tickets, also transition the ticket using the chosen transport and attach the evidence; omit the transition entirely for ticketless sources — the report to the user is the close.] + +Include the local review in the reported evidence — it is a real gate but gets buried in the logs otherwise. From the local-review stage's flow-log entry, report: the review **verdict**, the **finding count** (by severity), the **review file path** (`context/spec/{SPEC_NAME}/review.md`, or the path a reused review command used), and that a manual keep/drop gate ran over the findings. The path lets the user re-open the full review without re-running. + +Leave a clean working tree: do not write a closing flow-log entry (the log was finalized at commit-push and the change request is now open or merged — a new entry could never be committed into it). If any flow-created artifact is still uncommitted, surface it in the report rather than leaving it behind — an uncommitted leftover after a merged or in-review change request is a bug, not a record. + + + +--- + + diff --git a/templates/functional-spec-template.md b/templates/functional-spec-template.md index db7cfa7d..fdf9df87 100644 --- a/templates/functional-spec-template.md +++ b/templates/functional-spec-template.md @@ -44,3 +44,11 @@ _Clearly define what is and is not included in this work to prevent ambiguity._ ### Out-of-Scope - [A list of related items that are explicitly NOT included.] + +--- + +## Change Log + +_Dated amendments made after the spec was first written — typically by `/awos:spec` in Update Mode when a bug fix changed documented behavior. Each entry records the date, the source reference (bug id or fix description), and what behavior changed and why. Leave empty until the first amendment._ + +- [YYYY-MM-DD] — [source reference] — [what behavior changed and why] diff --git a/tests/lint-prompts.test.js b/tests/lint-prompts.test.js index 0c87c638..16efbc6a 100644 --- a/tests/lint-prompts.test.js +++ b/tests/lint-prompts.test.js @@ -29,6 +29,8 @@ const dimensionsDir = path.join( 'dimensions' ); const templatesDir = path.join(repoRoot, 'templates'); +const pluginCommandsDir = path.join(repoRoot, 'plugins', 'awos', 'commands'); +const pluginTemplatesDir = path.join(repoRoot, 'plugins', 'awos', 'templates'); function readUtf8(p) { return fs.readFileSync(p, 'utf8'); @@ -208,7 +210,12 @@ test('all /awos: cross-references resolve', () => { const rootCommands = new Set( listMarkdown(commandsDir).map((f) => '/awos:' + f.replace(/\.md$/, '')) ); - // Plugin-provided commands (the audit plugin contributes /awos:ai-readiness-audit). + // Plugin-provided commands resolve too: commands under plugins/awos/commands/ + // (e.g. /awos:flow, kept out of the core installer per review) plus the + // audit skill /awos:ai-readiness-audit. + for (const f of listMarkdown(pluginCommandsDir)) { + rootCommands.add('/awos:' + f.replace(/\.md$/, '')); + } rootCommands.add('/awos:ai-readiness-audit'); for (const ref of references) { assert.ok( @@ -554,24 +561,6 @@ test('commands/tasks.md documents the skip-tests opt-out and persists it', () => ); }); -test('commands/tasks.md marks an unreviewed tasks.md and clears it on review', () => { - // tasks.md is written before review (Step 4), so it starts as a - // draft carrying a "" marker that Step 5 - // removes once the user reviews it. The marker shape is the contract - // — awos-qa greps the saved file to tell a draft from a reviewed - // plan, so a reword here would silently break that detection. Lock - // the shape, plus the removal-on-review instruction. - const body = readUtf8(path.join(commandsDir, 'tasks.md')); - assert.ok( - body.includes(''), - 'commands/tasks.md must record the literal "" marker so awos-qa can detect a draft-grade tasks.md' - ); - assert.ok( - /remove the `` marker/i.test(body), - 'commands/tasks.md Step 5 must remove the not-user-reviewed marker once the plan has been reviewed' - ); -}); - test('commands/verify.md acknowledges the skip-tests marker', () => { // The Slack thread feedback frames /awos:verify as look-and-feel + // spec-freshness rather than a test runner. The skip-tests marker @@ -604,6 +593,434 @@ test('verify.md does not hardcode a verification-tool priority order', () => { ); }); +test('verify.md and the flow templates never punt a drivable render to the user', () => { + // Road-test regression (session 928eba0a): the generated /fix-bug + // paused and told the user to run `! make run` for the live check, + // even though the agent could reclaim the port or drive the deploy + // itself. It punted because a shared-resource guardrail made + // "can't auto-verify" artificially true. The fix: running the app is + // the flow's job, and the manual AskUserQuestion fallback is only for + // a criterion with no agent-driven render path at all. Lock the + // guidance into verify.md and both flow templates so a regenerated + // command carries it. + const verify = readUtf8(path.join(commandsDir, 'verify.md')); + assert.ok( + /Running the app is your job, not the user's/i.test(verify) || + /Running the app to verify is/i.test(verify), + "commands/verify.md must state that running the app to verify is the agent's job — a reserved shared resource is not grounds to hand the user a `run` command" + ); + assert.ok( + /last resort/i.test(verify) && /alternate port/i.test(verify), + 'commands/verify.md must frame the manual AskUserQuestion fallback as a last resort and name agent-driven paths (alternate port / reclaim / deploy) to try first' + ); + + for (const tmpl of ['implement-feature-template.md', 'fix-bug-template.md']) { + const body = readUtf8(path.join(pluginTemplatesDir, tmpl)); + assert.ok( + /Running the app to verify is the flow's job, not the user's/i.test(body), + `${tmpl} verify stage must state that running the app to verify is the flow's job, not the user's — the shared-resource guardrail must not become a reason to punt` + ); + assert.ok( + /sanctioned verification path/i.test(body), + `${tmpl} verify stage must point at the §2/§3 sanctioned verification path (reclaim the resource / alternate port / drive the deploy) so a drivable criterion is never deferred to a manual run` + ); + } + + const flow = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /sanctioned verification path/i.test(flow), + 'flow.md worktree/shared-resource investigation must record a sanctioned verification path so the generated verify stage self-verifies instead of handing the user a `run` command' + ); + const dfTemplate = readUtf8( + path.join(pluginTemplatesDir, 'delivery-flow-template.md') + ); + assert.ok( + /Sanctioned verification path/i.test(dfTemplate), + 'delivery-flow-template.md §2 must carry a "Sanctioned verification path" field so the decision record captures how verify drives the app when a shared resource is reserved' + ); +}); + +test('the flow templates finalize the flow-log at commit-push and never leave it as a leftover', () => { + // Road-test regression (session 928eba0a): the generated flow left + // context/spec/006-settings-page/flow-log.md dirty — the close stage + // appended after the last commit, so the entry could never reach the + // merged PR. The flow-log is committed with the work but must stop + // being written once the change request is opened or merged, and the + // close stage must leave a clean tree. Lock the discipline into both + // templates, flow.md, and the decision record. + for (const tmpl of ['implement-feature-template.md', 'fix-bug-template.md']) { + const body = readUtf8(path.join(pluginTemplatesDir, tmpl)); + assert.ok( + /once the change request is opened — or the change is merged — stop writing to the tracked log/i.test( + body + ), + `${tmpl} must stop writing to the tracked flow-log once the change request is opened or merged — a late append strands a change that can never reach the PR` + ); + assert.ok( + /flow-log's last committed state/i.test(body), + `${tmpl} commit-push stage must finalize the flow-log in that commit (write the entry before staging)` + ); + assert.ok( + /Leave a clean working tree/i.test(body) && + /leftover after a merged or in-review change request is a bug/i.test( + body + ), + `${tmpl} close stage must guarantee a clean working tree — an uncommitted flow-created artifact after merge/review is a bug, not a record` + ); + } + + const flow = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /stops writing to it once the change request is opened or merged/i.test( + flow + ), + 'flow.md context-strategy must bake in the flow-log commit discipline so generated commands never leave an uncommittable leftover' + ); + const dfTemplate = readUtf8( + path.join(pluginTemplatesDir, 'delivery-flow-template.md') + ); + assert.ok( + /never becomes an uncommittable leftover/i.test(dfTemplate), + 'delivery-flow-template.md §8 flow-log field must record that the log is finalized at commit-push and never left as a leftover' + ); +}); + +test('the flow distinguishes interactive vs unattended AskUserQuestion timeouts', () => { + // A road-test found the 60s AskUserQuestion no-answer fallback firing + // in an interactive session, silently defaulting while the user was + // still deciding. The 60s timer is a harness guard (the skill can't + // change it), but the reaction is ours: unattended runs (driven with + // AWOS_UNATTENDED=1) take the safe default; interactive runs re-ask + // once and announce the default. An irreversible step never proceeds + // on a timeout. Lock the env-var contract into flow.md, both + // templates, and the decision record. + const flow = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /AWOS_UNATTENDED/.test(flow), + 'flow.md must key the unanswered-question handling off the AWOS_UNATTENDED env var — interactive and headless runs treat a 60s timeout differently' + ); + assert.ok( + /re-ask the question once/i.test(flow), + 'flow.md must re-ask once in an interactive run rather than silently defaulting on the 60s timeout' + ); + + for (const tmpl of ['implement-feature-template.md', 'fix-bug-template.md']) { + const body = readUtf8(path.join(pluginTemplatesDir, tmpl)); + assert.ok( + /AWOS_UNATTENDED/.test(body) && /No response after 60s/i.test(body), + `${tmpl} must carry the AWOS_UNATTENDED run-mode rule for the harness's "No response after 60s" fallback` + ); + assert.ok( + /timeout never authorizes an irreversible step/i.test(body), + `${tmpl} must keep the guard that a timeout never authorizes an irreversible step (merge / spec-amendment confirmations stay a no)` + ); + } + + const dfTemplate = readUtf8( + path.join(pluginTemplatesDir, 'delivery-flow-template.md') + ); + assert.ok( + /AWOS_UNATTENDED=1/.test(dfTemplate), + 'delivery-flow-template.md §6 must record AWOS_UNATTENDED=1 as an operator prerequisite for unattended runs' + ); +}); + +test('flow.md investigation probes before claiming absence and validates ticket transitions', () => { + // Road-test #2 regressions (HOP-3749): (1) the investigation missed a + // versioned pre-commit hook installed via core.hooksPath and the + // generated command confidently asserted "there are no pre-commit + // hooks"; (2) the decision record said "PR opened → In Review" but the + // tracker had no direct Open → In Review transition, so an unattended + // run would fail its first transition; (3) the worktree recipe skipped + // git-ignored build prerequisites, costing the fix agent an + // undocumented install + codegen. Lock the probe list, the + // no-absence-claims rule, transition-chain validation, and the + // bring-up steps into flow.md and the decision-record template. + const flow = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /core\.hooksPath/.test(flow) && /\.pre-commit-config\.yaml/.test(flow), + 'flow.md Step 2 must carry an explicit pre-commit-hook probe list (core.hooksPath, .husky/, .pre-commit-config.yaml, …) — a hook found only in the conventional place is how the road-test missed a versioned one' + ); + assert.ok( + /No absence claims without a probe/i.test(flow), + 'flow.md Step 2 must forbid the decision record and generated commands from asserting "no X" unless X was explicitly probed — an unprobed signal is unknown, not absent' + ); + assert.ok( + /validate it at generation time/i.test(flow) && + /transition.*chain/i.test(flow), + 'flow.md §5 ticket-state map must be validated at generation time against a sample issue and record full transition chains (intermediate hops), not just target state names' + ); + assert.ok( + /bring-up steps/i.test(flow) && /\.gitignore/.test(flow), + 'flow.md worktree sub-interview must probe .gitignore for build-required artifacts and put the bring-up steps (install, codegen, env files) into the isolation recipe' + ); + + const dfTemplate = readUtf8( + path.join(pluginTemplatesDir, 'delivery-flow-template.md') + ); + assert.ok( + /transition chain/i.test(dfTemplate), + 'delivery-flow-template.md §5 must record validated transition chains (with IDs where exposed), not just target states' + ); + assert.ok( + /bring-up steps/i.test(dfTemplate), + 'delivery-flow-template.md §2 Worktrees field must include the bring-up steps for git-ignored prerequisites' + ); + + for (const tmpl of ['implement-feature-template.md', 'fix-bug-template.md']) { + const body = readUtf8(path.join(pluginTemplatesDir, tmpl)); + assert.ok( + /recorded transition chain/i.test(body), + `${tmpl} remote-gates stage must follow the recorded transition chain (intermediate hops), not just the target state name` + ); + } +}); + +test('fix-bug template reads remote links, sweeps all surfaces, and verifies subagent claims', () => { + // Road-test #2 regressions (HOP-3749): the bug's real context (a + // screenshot naming the broken surface) lived in a Jira remote link; + // diagnosis stopped at the first of three affected surfaces; an + // Explore report proposed a 3-file fix where one line sufficed; and a + // subagent returned a green-but-vacuous regression test asserting an + // already-correct path. Lock the fetch-remote-links step, the + // all-surfaces sweep, verified-vs-hypothesis labels, the demonstrated + // fail-on-old-code check, and the verify-evidence proportionality + // tier into the fix-bug template. + const body = readUtf8(path.join(pluginTemplatesDir, 'fix-bug-template.md')); + assert.ok( + /remote links, attachments/i.test(body), + "fix-bug fetch stage must pull the ticket's remote links and attachments — the real repro context often lives there, not in the description" + ); + assert.ok( + /every surface that renders or consumes the symptom data/i.test(body), + 'fix-bug diagnose stage must enumerate every renderer/consumer of the symptom data (sibling composers), not stop at the first root cause' + ); + assert.ok( + /\*\*verified\*\*/.test(body) && /\*\*hypothesis\*\*/.test(body), + 'fix-bug diagnose reports must label each claim verified vs hypothesis, and the orchestrator re-reads the named lines before accepting the fix shape' + ); + assert.ok( + /demonstrated, not asserted/i.test(body) && /green-but-vacuous/i.test(body), + 'fix-bug regression-test stage must demonstrate fail-on-old-code on a changed path (revert → fail → restore → pass) and reject a test that is green pre-fix' + ); + assert.ok( + /Scale the evidence to what changed/i.test(body), + 'fix-bug verify stage must carry the proportionality tier: for a payload-only fix with an untouched render path, regression test + unit-level render with mocks is sanctioned evidence' + ); + + for (const tmpl of ['implement-feature-template.md', 'fix-bug-template.md']) { + const t = readUtf8(path.join(pluginTemplatesDir, tmpl)); + assert.ok( + /report is a claim, not a fact/i.test(t), + `${tmpl} Context Discipline must state that subagent reports are claims to spot-check, not facts to relay` + ); + } +}); + +test('flow.md generator version constant matches plugin.json and stamps the artifacts', () => { + // Road-test #2 finding: after `git pull` in the marketplace clone, an + // already-running session kept executing the stale cached command text + // with no signal. flow.md carries a literal generator-version constant + // checked against plugin.json on invocation (a mismatch means the + // session must restart), and every generated artifact's footer marker + // is stamped with that version. This test keeps the constant in sync + // with the manifest so the stale-session check stays truthful. + const flow = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + const manifest = JSON.parse( + readUtf8( + path.join(repoRoot, 'plugins', 'awos', '.claude-plugin', 'plugin.json') + ) + ); + const constant = flow.match(/generator version is `([^`]+)`/); + assert.ok( + constant, + 'flow.md Step 1 must declare a literal generator-version constant ("generator version is `X.Y.Z`") for the stale-session check' + ); + assert.strictEqual( + constant[1], + manifest.version, + `flow.md generator-version constant (${constant[1]}) must equal plugins/awos/.claude-plugin/plugin.json version (${manifest.version}) — bump them together or the stale-session check misfires` + ); + + for (const tmpl of ['implement-feature-template.md', 'fix-bug-template.md']) { + const body = readUtf8(path.join(pluginTemplatesDir, tmpl)); + assert.ok( + /awos:flow:generated date=\[YYYY-MM-DD\] version=\[/.test(body), + `${tmpl} footer marker must carry a version=[…] stamp so re-runs can tell which generator produced the on-disk artifacts` + ); + } +}); + +test('flow.md flags routing policies that route agents around the generated commands', () => { + // Road-test #2 item 8 (narrowed): the generated commands are + // auto-discovered by autocomplete, but an agent follows the loaded + // routing policy — hops' CLAUDE.md "AWOS Workflow (Required)" section + // prescribed the manual /awos:* chain, so agents routed around + // /implement-feature and /fix-bug by instruction, and nothing + // auto-loads delivery-flow.md. flow.md must check always-loaded docs + // for such a policy at investigation time and advise the wording fix + // in the Step 8 project-side setup fixes (flag, never auto-edit). + const flow = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /Routing policy in always-loaded docs/i.test(flow), + 'flow.md Step 2 must inspect CLAUDE.md/AGENTS.md-style always-loaded docs for a prescribed-workflow section that routes agents through the manual /awos:* chain' + ); + assert.ok( + /routes around/i.test(flow), + 'flow.md must state why the policy matters: an agent following a manual-chain-only policy routes around the generated commands by instruction' + ); + assert.ok( + /advise updating it and offer the concrete wording/i.test(flow), + 'flow.md Step 8 must advise the routing-policy update with concrete wording as a project-side fix the user applies — the flow flags it, it does not edit CLAUDE.md' + ); +}); + +test('generated commands carry the hops-style Self-Improvement Loop with governed boundaries', () => { + // Road-test #2 item 9, reworked to the hops team's field-tested shape + // (hops fix-bug.md "Self-Improvement Loop"): a flow defect found + // during a run (disproven fact, missing step, workaround-forcing + // instruction) is fixed in the same run, shipped in the same change + // request, recorded in the flow log, and promoted to Local + // Customizations so regeneration preserves it. Boundaries stay + // governed: delivery decisions belong to the flow owner, and + // generator defects are reported via the user (an in-command "report + // to the maintainers" is not actionable — the LLM has no channel). + for (const tmpl of ['implement-feature-template.md', 'fix-bug-template.md']) { + const body = readUtf8(path.join(pluginTemplatesDir, tmpl)); + assert.ok( + /## Self-Improvement Loop/.test(body), + `${tmpl} must carry the fixed Self-Improvement Loop section` + ); + assert.ok( + /same branch, same change request/i.test(body), + `${tmpl} loop must ship flow fixes in the same change request as the work, never a separate one` + ); + assert.ok( + /promote it into the decision record's \*\*Local Customizations\*\*/i.test( + body + ), + `${tmpl} loop must promote corrections to Local Customizations so regeneration preserves them` + ); + assert.ok( + /belongs to whoever owns the team's process/i.test(body), + `${tmpl} loop must leave delivery decisions to the flow owner — a run never changes one` + ); + assert.ok( + /tell the user so they can report it to the AWOS repo/i.test(body), + `${tmpl} loop must route generator defects through the user (actionable), not an abstract "report to maintainers"` + ); + } + + const flow = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /Self-Improvement Loop/.test(flow) && + /never changes a delivery _?decision_? on its own/i.test(flow), + 'flow.md Step 6 must instruct keeping the Self-Improvement Loop verbatim and restate that a run never changes a delivery decision' + ); + + const dfTemplate = readUtf8( + path.join(pluginTemplatesDir, 'delivery-flow-template.md') + ); + assert.ok( + /Self-Improvement Loop/.test(dfTemplate), + 'delivery-flow-template.md §10 must say Self-Improvement Loop corrections land in Local Customizations' + ); +}); + +test('generated commands are clean, self-contained, and interaction-explicit', () => { + // Road-test #3 feedback (sde-automation PR #26): the regenerated + // commands copied the template's generator-facing header comment into + // the output (two paragraphs of noise), fix-bug said "Same resume + // logic as implement-feature" (commands know nothing about each other + // at run time), and stages that ask the user lost their explicit + // AskUserQuestion mentions. Lock the fixes into the templates and + // flow.md. + for (const tmpl of ['implement-feature-template.md', 'fix-bug-template.md']) { + const body = readUtf8(path.join(pluginTemplatesDir, tmpl)); + assert.ok( + /do NOT copy it, or any\s+adaptation of it, into the generated file/.test( + body + ), + `${tmpl} header comment must declare itself generator-only — never copied or adapted into the generated command` + ); + assert.ok( + /self-contained/i.test(body), + `${tmpl} must require the generated command to be self-contained — no references to the sibling command` + ); + assert.ok( + /Every fixed-choice interaction with the user/i.test(body), + `${tmpl} Context Discipline must route every fixed-choice user interaction through AskUserQuestion` + ); + assert.ok( + /Never improvise worktree preparation/i.test(body), + `${tmpl} workspace stage must invoke the project's worktree command/script or the recorded §2 recipe — never improvise git-worktree prep in-run` + ); + assert.ok( + /agents do not nest/i.test(body), + `${tmpl} local-review stage must handle the agent hierarchy: a review skill that spawns subagents runs from the main context, never wrapped in a subagent` + ); + } + + const fixBug = readUtf8(path.join(pluginTemplatesDir, 'fix-bug-template.md')); + assert.ok( + !/Same resume logic as implement-feature/i.test(fixBug), + 'fix-bug-template.md must not defer to implement-feature for its resume logic — commands are independent at run time' + ); + assert.ok( + /awos:flow:stage=local-review/.test(fixBug), + 'fix-bug-template.md must have its own local-review stage — review folded into remote-gates loses its independent context' + ); + + const flow = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /no top-of-file comment/i.test(flow), + 'flow.md Step 6 must state the generated file carries no top-of-file comment — template headers are generator instructions' + ); + assert.ok( + /never improvises worktree preparation/i.test(flow), + 'flow.md worktree sub-interview must reuse an existing worktree command/script or record an exact recipe the stage executes verbatim' + ); + assert.ok( + /inspecting the review automation/i.test(flow), + 'flow.md Step 6 must pick the review shape at generation time by inspecting whether the reused review skill dispatches subagents' + ); +}); + +test('a generator update triggers full regeneration even when no decision changed', () => { + // Road-test regression (sde-automation PR #25): after the generator + // gained the road-test #2 fixes, a regenerate-only re-run (no + // dimensions revisited) produced ONLY a version-stamped footer and a + // log entry — none of the new template prose landed, because + // reconciliation was decision-driven: unchanged decisions read as + // "nothing to change", new sections outside stage markers had no + // reconciliation slot, and new probes hung off unselected dimensions. + // Lock the three fixes: generator update as an independent + // regeneration trigger, generator-owned prose outside markers, and + // fact-gap probes on any re-run. + const flow = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /generator-update re-run/i.test(flow), + 'flow.md Step 1 re-run detection must classify a footer version older than (or missing against) the constant as a generator-update re-run that regenerates every stage' + ); + assert.ok( + /independent regeneration triggers/i.test(flow), + 'flow.md Step 6 must treat a decision change and a generator update as independent regeneration triggers — "no dimensions revisited" never means the old text stays' + ); + assert.ok( + /never just a stamp-and-date update/i.test(flow), + 'flow.md Step 6 must forbid the no-op failure mode: an outdated footer triggers stage regeneration, not just a version stamp' + ); + assert.ok( + /generator-owned/i.test(flow) && /outside the stage markers/i.test(flow), + 'flow.md Step 6 must declare prose outside stage markers generator-owned — rewritten from the current template on every regeneration, since nothing reconciles it stage-by-stage' + ); + assert.ok( + /fact gap/i.test(flow) && + /fact upgrade is not a decision change/i.test(flow), + 'flow.md Step 5 must fill record fields the current template defines but the on-disk record lacks (transition chains, bring-up steps, routing policy) on any re-run, without a dimension being re-opened' + ); +}); + test('hire.md QA Complement Rule is search-first and not tool-hardcoded', () => { // Mirror of the verify.md anti-hardcoding rule. /awos:hire must // propose a QA agent by searching the registry, not by always @@ -675,9 +1092,589 @@ test('SDD-07 recognizes the dual-model QA coverage', () => { ); }); -// --------------------------------------------------------------------------- -// Brownfield awareness contracts -// --------------------------------------------------------------------------- +test('flow.md wires the delivery-flow generator contract end to end', () => { + // /awos:flow generates the project's /implement-feature command from two + // templates and a decision record. The four path references below are the + // joints of that contract — if any drifts, generation reads or writes the + // wrong file. The command ships as a plugin command (plugins/awos/commands/), + // not via the core installer — workshur asked to keep it out of the main flow. + const body = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + const requiredRefs = [ + // Templates ship bundled in the plugin (self-contained), not via the + // installer's .awos/templates/ — a plugin user need not re-run the + // installer to get the scaffolds. + '${CLAUDE_PLUGIN_ROOT}/templates/delivery-flow-template.md', + '${CLAUDE_PLUGIN_ROOT}/templates/implement-feature-template.md', + 'context/product/delivery-flow.md', + '.claude/commands/implement-feature.md', + // Context-strategy introspection must target the full prompts, not the + // one-line wrappers in .claude/commands/awos/ (observed live: grepping + // the wrappers returned all zeros and confused the interview). + '.awos/commands/*.md', + ]; + const missing = requiredRefs.filter((ref) => !body.includes(ref)); + assert.deepEqual( + missing, + [], + `plugins/awos/commands/flow.md must reference its templates and both generated artifacts; missing: ${missing.join(', ')}` + ); + assert.ok( + /prefer the CLI/i.test(body), + 'plugins/awos/commands/flow.md must record the CLI-over-MCP transport preference (CLI is usually faster and cheaper in tokens)' + ); + assert.ok( + body.includes('`Explore`'), + 'plugins/awos/commands/flow.md must delegate the read-heavy project scan to the built-in Explore subagent, not read the codebase in its own context' + ); + assert.ok( + /automatic reviewer/i.test(body), + 'plugins/awos/commands/flow.md must cover automatic reviewers on the code host (CodeRabbit-style bots) — both detection in Step 2 and the wait-and-address gate in the review dimension' + ); + assert.ok( + /two to four listed options/i.test(body), + 'plugins/awos/commands/flow.md must state the AskUserQuestion 2–4 option bound — a single-option question is rejected at the schema level (observed live: the Step 3 docs question crashed with InputValidationError)' + ); + assert.ok( + !/One listed option suffices/i.test(body), + 'plugins/awos/commands/flow.md must not instruct a single-option AskUserQuestion call — the tool schema requires at least two options' + ); + assert.ok( + body.includes('`multiSelect`'), + 'plugins/awos/commands/flow.md must direct combinable answers (review gates, entry points) to multiSelect questions instead of yes/no series or forced single picks' + ); + assert.ok( + /Reuse, Replace, or Compose/i.test(body) && /Step 4\.5/.test(body), + 'plugins/awos/commands/flow.md must evaluate existing project automation in Step 4.5 (reuse/replace/compose) rather than adopting or ignoring it unconditionally — discovered automation is compared, and close calls are asked with the evidence' + ); + assert.ok( + /drives a large span of the flow autonomously/i.test(body), + 'flow.md must detect an existing command that overlaps the whole flow and surface the collision instead of generating a competing /implement-feature' + ); + assert.ok( + /\*\*Notifications\.\*\*/.test(body), + 'plugins/awos/commands/flow.md must interview the Notifications dimension — the flow announces transitions so the team stays aware as gates are removed' + ); +}); + +test('flow.md re-run interviews only the dimensions the user chose', () => { + // A road-test re-run re-reviewed all seven dimensions with "(текущее)" + // defaults instead of only the ones the user wanted to change. The re-run + // path must collect a granular per-dimension selection in Step 1.3 and + // interview only those, bulk-confirming the rest unchanged. + const body = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /granular/i.test(body), + 'flow.md re-run path must collect a granular per-dimension selection (the individual dimensions), not coarse buckets that re-ask everything inside them' + ); + assert.ok( + /bulk-confirm/i.test(body), + 'flow.md re-run path must bulk-confirm the unselected dimensions as unchanged in one summary line — never re-ask a dimension the user did not choose to revisit' + ); + assert.ok( + /only those/i.test(body), + 'flow.md Step 4 must interview only the dimensions selected on a re-run, not fall back to the fresh-run all-dimensions interview' + ); +}); + +test('flow.md keeps autonomy holistic and un-steered', () => { + // The approval-gates question mis-steered the road-test user toward the + // most-gated option, and autonomy was gates-only — reused interactive + // skills and chain interviews impose pauses the gate choice never sees. + const body = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /do \*\*not\*\* pre-mark the most-gated option/i.test(body), + 'flow.md approval-gates question must not pre-mark the most-gated option "(Recommended)" — the amount of gating is the user\'s autonomy call, and flow.md forbids decorative recommendations' + ); + assert.ok( + /no gates: unattended/i.test(body), + 'flow.md approval-gates options must read as an autonomy spectrum labeled by the pauses each imposes (two gates / one combined gate / no gates: unattended)' + ); + assert.ok( + /Reused interactivity is part of the autonomy decision/i.test(body), + "flow.md Step 4.5 must treat a reused skill's per-run confirmations as part of the autonomy decision (reuse-with-prompt vs. compose a non-interactive path that keeps validation) — not a silent import" + ); + assert.ok( + /Interaction budget/i.test(body), + 'flow.md Step 8 must report an Interaction budget enumerating every human-pause — gate-controlled, reuse-imposed, and chain-imposed — so "how autonomous is it really" is visible at generation time' + ); +}); + +test('flow.md tells the user to commit the generated artifacts', () => { + // /awos:flow leaves delivery-flow.md + the generated command uncommitted; + // the first run then warns on the dirty tree. Step 8 must close the gap. + const body = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /commit the generated artifacts/i.test(body), + 'flow.md Step 8 must tell the user to commit the generated artifacts (delivery-flow.md, implement-feature.md, fix-bug.md when present) so the first run starts from a clean tree' + ); +}); + +test('flow.md captures canonical project config and reconciles reused-skill constants', () => { + // A reused skill hardcoded the wrong Jira instance host; the dead link + // surfaced at runtime and was mis-blamed on the generated command. Step 2 + // must capture the canonical config and Step 4.5 must reconcile a reused + // skill's hardcoded constants against it at generation time. + const body = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /Canonical project config/i.test(body) && /base URL/i.test(body), + 'flow.md Step 2 must capture canonical project config (Jira base URL, Slack channel/handles, code-host org/repo) as project-config facts' + ); + assert.ok( + /hardcoded constants/i.test(body) && /Project Setup/i.test(body), + 'flow.md Step 4.5 must scan a reused skill for hardcoded constants and reconcile them against the captured Project Setup config, asking on a mismatch' + ); + assert.ok( + /Format\/lint gate scope/i.test(body) && + /Do not add a format pass inside the generated flow/i.test(body), + 'flow.md Step 2 must detect a repo-wide format/lint gate and Step 8 must advise the project-side ignore fix — without adding a format pass inside the flow' + ); +}); + +test('delivery-flow-template.md carries a flow-agnostic Project Setup section', () => { + // The canonical config the reconcile step checks against lives in the + // decision record so re-runs and the sibling fix-bug flow reuse it. + const body = readUtf8( + path.join(pluginTemplatesDir, 'delivery-flow-template.md') + ); + assert.ok( + /## .*Project Setup/.test(body), + 'delivery-flow-template.md must declare a "Project Setup" section recording the canonical config (Jira base URL, Slack channel, team handles) — reconciled against reused-skill constants at generation time' + ); + assert.ok( + /base URL/i.test(body) && /code-host org\/repo/i.test(body), + 'delivery-flow-template.md Project Setup must record the Jira base URL and code-host org/repo so reused skills can be checked against them' + ); +}); + +test('flow.md and the template guard the generated header against comment-nesting', () => { + // A generated file embedded a literal awos:flow:stage marker inside its + // outer header comment; the inner --> closed the comment early + // (CodeRabbit-flagged). The generator must be told to describe markers in + // prose, never nest one HTML comment inside another. + const flowBody = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + const tplBody = readUtf8( + path.join(pluginTemplatesDir, 'implement-feature-template.md') + ); + assert.ok( + /never nest one inside another/i.test(flowBody), + "flow.md Step 6 must instruct the generator not to nest stage-marker HTML comments inside the generated file's own header comment" + ); + assert.ok( + /never nest one inside another/i.test(tplBody), + 'implement-feature-template.md header must warn the generator never to nest the stage-marker comments' + ); +}); + +test('implement-feature-template.md carries stage markers and the AWOS chain', () => { + // The generated /implement-feature command is user-owned; /awos:flow re-runs + // reconcile manual edits per stage. The HTML-comment stage markers are the + // attribution mechanism — without them, regeneration degrades to whole-file + // clobbering. The template must also route coding through the AWOS chain + // rather than implementing in the main context. + const body = readUtf8( + path.join(pluginTemplatesDir, 'implement-feature-template.md') + ); + assert.ok( + body.includes(''), + 'implement-feature-template.md must fence every stage with / markers so /awos:flow re-runs can attribute manual edits per stage' + ); + for (const cmd of [ + '/awos:spec', + '/awos:tech', + '/awos:tasks', + '/awos:implement', + '/awos:verify', + ]) { + assert.ok( + body.includes(cmd), + `implement-feature-template.md must run ${cmd} as part of the generated flow` + ); + } + assert.ok( + /do not implement tasks in the main context/i.test(body), + 'implement-feature-template.md must preserve the orchestrator-only guard — coding goes through /awos:implement subagents' + ); + for (const stage of ['local-review', 'remote-gates', 'merge']) { + assert.ok( + body.includes(``), + `implement-feature-template.md must carry the ${stage} stage — the flow reviews locally before spending CI minutes, waits on remote gates, and covers the merge step, not just PR creation` + ); + } + assert.ok( + /skipped or unanswered confirmation means do not merge/i.test(body), + 'implement-feature-template.md merge stage must keep the per-run confirmation guard as fixed prose — merging is irreversible, so a skipped confirmation is a no (inverse of the #132 skip-default)' + ); + assert.ok( + body.includes('flow-log.md'), + 'implement-feature-template.md must keep the flow-log contract — each stage appends a summary so fresh sessions resume from disk state' + ); + assert.ok( + /never launch a nested headless session/i.test(body), + 'implement-feature-template.md must forbid nested `claude -p` calls — permission modes, PATH, and timeouts vary per machine; headless chaining lives at the trigger layer' + ); + assert.ok( + /`Monitor` tool, never foreground `sleep` loops/.test(body), + 'implement-feature-template.md must wait on remote gates with the Monitor tool, not blind sleep loops — and its filter must cover failure states, not just success' + ); + assert.ok( + /merge cleanly/.test(body) && /re-check mergeability/.test(body), + 'implement-feature-template.md must check target-branch conflicts twice: before opening the change request and again before merging (the target moves while gates run)' + ); + assert.ok( + /do not add run-time focus areas/i.test(body), + 'implement-feature-template.md review stage must keep the independence rule: the reviewer prompt is fixed at generation time — an orchestrator that just implemented the change must not frame its own review' + ); + // The close stage must surface the local review (verdict + finding count + + // review file path) — the review is a real gate but otherwise buried in + // the logs. The Close-the-Loop stage is the hand-off report. + const closeStage = body.slice(body.indexOf('awos:flow:stage=close-ticket')); + assert.ok( + /verdict/i.test(closeStage) && + /finding count/i.test(closeStage) && + closeStage.includes('review.md'), + 'implement-feature-template.md close stage must report the local review evidence — verdict, finding count, and the review file path (context/spec/{SPEC_NAME}/review.md)' + ); + const stageOrder = [ + 'fetch-ticket', + 'resume-detection', + 'workspace', + 'specs', + 'commit-specs', + 'implement', + 'verify', + 'local-review', + 'commit-push', + 'remote-gates', + 'merge', + 'delivery', + 'close-ticket', + ]; + const positions = stageOrder.map((s) => + body.indexOf(``) + ); + for (let i = 0; i < stageOrder.length; i++) { + assert.ok( + positions[i] !== -1 && (i === 0 || positions[i] > positions[i - 1]), + `implement-feature-template.md stages must appear in canonical order (${stageOrder.join(' → ')}); '${stageOrder[i]}' is missing or out of place — in particular, verify and local-review precede commit-push (CI minutes are spent on reviewed code only) and merge comes after remote-gates` + ); + } +}); + +test('delivery-flow-template.md preserves customizations and the tooling inventory', () => { + // Local Customizations is where /awos:flow promotes manual edits the user + // chose to keep — losing the section silently re-clobbers them on the next + // regeneration. The tooling inventory records the chosen transport + // (CLI vs MCP) per external service. + const body = readUtf8( + path.join(pluginTemplatesDir, 'delivery-flow-template.md') + ); + assert.ok( + /## .*Local Customizations/.test(body), + 'delivery-flow-template.md must declare a "Local Customizations" section — the regeneration contract depends on it' + ); + assert.ok( + /## .*Tooling Inventory/.test(body), + 'delivery-flow-template.md must declare a "Tooling Inventory" section recording the chosen transport per service' + ); + assert.ok( + /\*\*Merge policy:\*\*/.test(body) && /\*\*Post-merge CI:\*\*/.test(body), + 'delivery-flow-template.md §5 must record the merge policy and post-merge CI fields — the generated merge/ci-monitor stages derive from them' + ); + assert.ok( + /## .*Context Strategy/.test(body), + 'delivery-flow-template.md must declare a "Context Strategy" section — subagent-isolated stages and the flow log are recorded decisions, not ad-hoc behavior' + ); + assert.ok( + /## .*Notifications/.test(body), + 'delivery-flow-template.md must declare a "Notifications" section — where the flow announces transitions so the team stays aware as gates are removed' + ); + assert.ok( + /Stage automation \(reuse \/ replace \/ compose\)/.test(body), + 'delivery-flow-template.md must record the per-stage reuse/replace/compose decision for overlapping project automation, so re-runs do not regenerate over a reused command' + ); + assert.ok( + /## .*Bug-fix Flow/.test(body), + 'delivery-flow-template.md must declare a "Bug-fix Flow" section — whether fix-bug was generated, the classification/amendment policy, and the regression-test expectation; the sibling command consumes it' + ); +}); + +test('commands/spec.md carries an Update Mode that amends in place', () => { + // spec.md was creation-only; a behavior-changing fix had no way to keep the + // spec in sync. Update Mode mirrors the Step 2A pattern in + // product/roadmap/architecture: detect an existing spec, edit it in place, + // and never allocate a new index. + const body = readUtf8(path.join(commandsDir, 'spec.md')); + assert.ok( + /Mode Detection/i.test(body) && /Update Mode/i.test(body), + 'commands/spec.md must add a Mode Detection step that routes an existing-spec reference to an Update Mode (mirroring product/roadmap/architecture)' + ); + assert.ok( + /never allocates a new index/i.test(body) && + /never runs `create-spec-directory\.sh`/i.test(body), + 'commands/spec.md Update Mode must edit in place — it must never run create-spec-directory.sh and never allocate a new index' + ); + assert.ok( + /## Change Log/.test(body), + 'commands/spec.md Update Mode must append a dated entry under a ## Change Log heading' + ); + assert.ok( + /stays `Completed`/i.test(body), + 'commands/spec.md Update Mode must not force a Status transition — a spec amended after a verified fix stays Completed' + ); +}); + +test('functional-spec-template.md declares a Change Log section', () => { + // The amendment target for spec.md Update Mode must be a well-defined, + // canonical section so the edit knows where to write. + const body = readUtf8(path.join(templatesDir, 'functional-spec-template.md')); + assert.ok( + /## Change Log/.test(body), + 'functional-spec-template.md must carry a canonical "## Change Log" section — the target for Update-Mode amendments' + ); +}); + +test('fix-bug-template.md carries the canonical bug-fix stages and the classify gate', () => { + // The generated /fix-bug command is the lighter sibling of + // /implement-feature: diagnose → fix → scoped re-verify → targeted spec + // amendment. Its classify gate is what makes spec-amendment correct, and + // its amend-spec stage must invoke core /awos:spec rather than duplicating + // amendment prose. + const body = readUtf8(path.join(pluginTemplatesDir, 'fix-bug-template.md')); + const stageOrder = [ + 'fetch-bug', + 'resume-detection', + 'workspace', + 'diagnose', + 'classify', + 'fix', + 'regression-test', + 'verify-criteria', + 'amend-spec', + 'commit-push', + 'remote-gates', + 'merge', + 'close-ticket', + ]; + const positions = stageOrder.map((s) => + body.indexOf(``) + ); + for (let i = 0; i < stageOrder.length; i++) { + assert.ok( + positions[i] !== -1 && (i === 0 || positions[i] > positions[i - 1]), + `fix-bug-template.md stages must appear in canonical order (${stageOrder.join(' → ')}); '${stageOrder[i]}' is missing or out of place` + ); + } + assert.ok( + body.includes(''), + 'fix-bug-template.md must close every stage with the marker so /awos:flow re-runs can attribute manual edits per stage' + ); + assert.ok( + /[Cc]onformance/.test(body) && /[Dd]ivergence/.test(body), + 'fix-bug-template.md classify stage must distinguish conformance bugs (do not amend the spec) from divergence (amend the spec) — the gate that makes amendment correct' + ); + assert.ok( + /amend-spec/.test(body) && /invoke[s]? `\/awos:spec`/i.test(body), + 'fix-bug-template.md amend-spec stage must invoke core /awos:spec in update mode on a divergence, not duplicate amendment prose' + ); + assert.ok( + body.includes('**[Agent:') && + /do not edit code in the main context/i.test(body), + 'fix-bug-template.md must be orchestrator-only — the fix is delegated via **[Agent: name]** and the orchestrator never edits code itself' + ); + assert.ok( + body.includes(''), + 'fix-bug-template.md regression-test/verify-criteria stages must honor the opt-out' + ); + assert.ok( + /`Monitor` tool, never foreground `sleep` loops/.test(body), + 'fix-bug-template.md remote-gates stage must wait with the Monitor tool, not blind sleep loops, with a filter covering failure states' + ); + assert.ok( + /skipped or unanswered confirmation means do not merge/i.test(body), + 'fix-bug-template.md merge stage must keep the per-run confirmation guard — a skipped confirmation is a no' + ); + const closeStage = body.slice(body.indexOf('awos:flow:stage=close-ticket')); + assert.ok( + /verdict/i.test(closeStage) && + /finding count/i.test(closeStage) && + closeStage.includes('review.md'), + 'fix-bug-template.md close stage must report the local review evidence (verdict, finding count, review file path) — the same hand-off treatment as implement-feature' + ); + for (const section of ['§2', '§4', '§5', '§9']) { + assert.ok( + body.includes(section), + `fix-bug-template.md must reuse the shared delivery-flow decisions (${section}) rather than re-deriving them` + ); + } +}); + +test('flow.md wires fix-bug generation alongside implement-feature', () => { + // /awos:flow must generate the optional second command from its own + // template, gated on the Command-set decision, with the same + // reconcile-on-rerun behavior as implement-feature. + const body = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + body.includes('${CLAUDE_PLUGIN_ROOT}/templates/fix-bug-template.md'), + 'flow.md must reference the bundled fix-bug-template.md so generation is self-contained in the plugin' + ); + assert.ok( + body.includes('.claude/commands/fix-bug.md'), + 'flow.md must keep the default bug-fix command path .claude/commands/fix-bug.md' + ); + assert.ok( + /classification gate/i.test(body), + 'flow.md bug-fix policy must settle the classification gate (conformance vs. divergence)' + ); +}); + +test('flow.md inventory covers platform build/verify toolchains', () => { + // Step 2 probed only web browser automation; mobile/native flows verify + // with a platform toolchain (Eugene uses XcodeBuildMCP build_sim). + const body = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /Build & verify toolchain/i.test(body), + "flow.md Step 2 inventory must record the project's build/verify toolchain as a transport" + ); + assert.ok( + /XcodeBuildMCP|Gradle|emulator|simulator/i.test(body), + 'flow.md must recognize non-web build/verify toolchains (iOS/Android), not just browser automation' + ); +}); + +test('generated commands resume from the roadmap and skip already-done work', () => { + // /everclear:workflow with no arg picks the next roadmap item; and a ticket + // already Done / spec already Completed must not be re-implemented. + const feat = readUtf8( + path.join(pluginTemplatesDir, 'implement-feature-template.md') + ); + const flow = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /next incomplete item in `context\/product\/roadmap\.md`/i.test(feat), + 'implement-feature-template.md must resume from the next incomplete roadmap item when invoked with no input' + ); + for (const f of ['implement-feature-template.md', 'fix-bug-template.md']) { + const body = readUtf8(path.join(pluginTemplatesDir, f)); + assert.ok( + /every source §1 records/i.test(body), + `${f} resume-detection must check status across every source §1 records, not a single place` + ); + } + assert.ok( + /already `Completed`/i.test(feat), + 'implement-feature-template.md must stop when the owning spec is already Completed (or its tasks are all done) instead of re-running the chain' + ); + assert.ok( + /"done"\/closed state names/i.test(flow), + "flow.md §1 must capture the tracker's done/closed state names so resume-detection can skip delivered work" + ); +}); + +test('flow.md and the template record a ticket-state lifecycle and CI escalation', () => { + // Eugene automates the whole status cycle off flow/CI events — including + // the failure path (review fails → back to To Do) — driven by a project + // -built CI AI reviewer. And remote-gate waits need a max-wait/escalation + // policy, not an unbounded poll. + const flow = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + const tpl = readUtf8( + path.join(pluginTemplatesDir, 'delivery-flow-template.md') + ); + assert.ok( + /Ticket state transitions/i.test(flow) && + /Ticket state transitions/i.test(tpl), + 'flow.md §5 and delivery-flow-template.md must record a ticket-state transition map (events → tracker states), not just the closing transition' + ); + assert.ok( + /back to .*needs-work|→ back to/i.test(flow), + 'the ticket-state map must cover the failure path — a failed gate/review sends the ticket back to a needs-work state' + ); + assert.ok( + /project-built AI reviewer/i.test(flow), + 'flow.md §4 must recognize a project-built CI AI reviewer (a GitHub Action calling Claude), not only third-party bots' + ); + assert.ok( + /max-wait/i.test(flow) && /max-wait/i.test(tpl), + 'flow.md §4 and delivery-flow-template.md must record a max-wait & escalation policy for remote-gate waits' + ); + for (const f of ['implement-feature-template.md', 'fix-bug-template.md']) { + const body = readUtf8(path.join(pluginTemplatesDir, f)); + assert.ok( + /max-wait & escalation policy/i.test(body), + `${f} remote-gates stage must apply the §4 max-wait & escalation policy instead of waiting forever` + ); + } +}); + +test('fix-bug-template.md supports a crash-report source', () => { + // Eugene's /everclear:fix starts from a Crashlytics issue: pull events, + // map the stack to real file:line, and refuse to invent lines when the + // build is unsymbolicated. The generated fix-bug command must support + // crash reporters as a bug source, generically. + const tpl = readUtf8(path.join(pluginTemplatesDir, 'fix-bug-template.md')); + assert.ok( + /unsymbolicated/i.test(tpl) && /do not invent line numbers/i.test(tpl), + 'fix-bug-template.md fetch-bug stage must map a crash stack to local file:line and refuse to invent line numbers on an unsymbolicated build' + ); + assert.ok( + /never auto-close/i.test(tpl), + 'fix-bug-template.md must allow writing an investigation note back to the crash issue without auto-closing it' + ); + const flow = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /bug source/i.test(flow) && /crash report/i.test(flow), + 'flow.md bug-fix policy must ask the bug source, including a crash report from a crash-reporting tool' + ); +}); + +test('flow.md interviews the command set and names', () => { + // Eugene's road-test named his commands to taste (/everclear:workflow, + // /everclear:fix); the generator must let the team pick which commands to + // build and what to call them, and record the names so re-runs reconcile + // the right files. This decision absorbs the old bug-fix opt-in. + const body = readUtf8(path.join(pluginCommandsDir, 'flow.md')); + assert.ok( + /Command set & names/i.test(body), + 'flow.md must interview a "Command set & names" decision — which commands to generate (feature, bug-fix, or both) and the slash-name for each' + ); + assert.ok( + /Generated Commands/.test(body), + "flow.md must record the chosen command names/filenames in the decision record's Generated Commands field so re-runs reconcile the right files" + ); + assert.ok( + /`\/feature`|`\/fix`/.test(body), + 'flow.md must show that the generated commands can be renamed from the defaults (e.g. /feature, /fix)' + ); +}); + +test('delivery-flow-template.md records the generated command set', () => { + // Re-runs read this field to find the exact files to reconcile — it can no + // longer assume implement-feature.md / fix-bug.md once names are renameable. + const body = readUtf8( + path.join(pluginTemplatesDir, 'delivery-flow-template.md') + ); + assert.ok( + /## .*Generated Commands/.test(body), + 'delivery-flow-template.md must declare a "Generated Commands" section recording each command\'s slash name and file' + ); +}); + +test('commands/tasks.md marks an unreviewed tasks.md and clears it on review', () => { + // tasks.md is written before review (Step 4), so it starts as a + // draft carrying a "" marker that Step 5 + // removes once the user reviews it. The marker shape is the contract + // — awos-qa greps the saved file to tell a draft from a reviewed + // plan, so a reword here would silently break that detection. Lock + // the shape, plus the removal-on-review instruction. + const body = readUtf8(path.join(commandsDir, 'tasks.md')); + assert.ok( + body.includes(''), + 'commands/tasks.md must record the literal "" marker so awos-qa can detect a draft-grade tasks.md' + ); + assert.ok( + /remove the `` marker/i.test(body), + 'commands/tasks.md Step 5 must remove the not-user-reviewed marker once the plan has been reviewed' + ); +}); test('product.md creates context/product/brownfield.md on brownfield detection', () => { // /awos:product is the entry point for brownfield detection. When it finds