# Task Board Setup

> Generator: deploys a file-based Kanban into any repo via multi-agent analysis, an optional spec + system-design layer (task-spec skill, per-task spec/design docs, domain-architect fan-out), and an optional gated CLAUDE.md-optimization pass. `upgrade` retrofits the spec layer onto an already-deployed board. Triggers: init task board, scaffold kanban, task tracker, upgrade task board, канбан-доска, спек-слой.

- Skill: `gabrielmoreira/task-board-setup` (Agent Skill)
- Install (CLI): `npx skillmds@latest add gabrielmoreira/task-board-setup`
- Raw SKILL.md: https://api.skillmd.com/api/skills/gabrielmoreira/task-board-setup/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: gabrielmoreira (https://skillmd.com/u/gabrielmoreira)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/gabrielmoreira/task-board-setup

---

<!-- brewcode-meta: version=6.1.4 content_version=5.6.0 generated_by=brewtools:task-board-setup -->

[DICT: TT=task-tracker agent (generated), TB=task-board skill (generated), BRD=board.md, FEAT=.claude/features, EXCL=source-path exclusions, REL=release style (vX.Y.Z tag | commit SHA | no tag), DOM=domain id segment, FM=frontmatter, TS=task-spec skill (generated), SPEC_MODE=spec+design layer opt-in, PS=status phase, PU=upgrade phase, PR=uninstall/purge phase]

# task-board-setup

Generator. Run from the MAIN conversation in (or pointed at) a TARGET repo. Deploys a self-contained, file-based Kanban into that repo:

| Emits | Path | Mirrors etalon |
|-------|------|----------------|
| Curator agent | `.claude/agents/task-tracker.md` | brewpage `task-tracker.md` |
| Dashboard skill | `.claude/skills/task-board/SKILL.md` | yasna `task-board` SKILL |
| Paths-scoped rule | `.claude/rules/tasks.md` | brewpage `tasks.md` |
| Board + control files | `.claude/features/{board,PROGRESS,TRACKER,TASK_TEMPLATE,INDEX}.md` + `{backlog,todo,progress,closed,specs}/` | brewpage `.claude/features/**` |
| Spec skill (SPEC_MODE only) | `.claude/skills/task-spec/SKILL.md` | `references/08-task-spec-skill.md` |
| Spec template (SPEC_MODE only) | `.claude/features/specs/SPEC_TEMPLATE.md` | `references/09-spec-templates.md` |
| Design template (SPEC_MODE only) | `.claude/features/specs/DESIGN_TEMPLATE.md` | `references/09-spec-templates.md` |

> **SPEC_MODE** (confirmed in P1) gates the three rows above AND every spec-related addition inside the other emitted artifacts. `SPEC_MODE=off` -> nothing spec-related is emitted and every artifact is byte-identical to the pre-spec-layer generator.

> **`PROGRESS.md` is UNGATED** -- the session-progress artifact and every site that references it belong to BOTH modes' baseline. `SPEC_MODE` never removes them; byte-identity above means identical to the pre-spec-layer generator *plus* those sites. Each reference's own header enumerates its ungated sites (`02`, `03`, `04`, `05`) -- read it there, !=count from here.

This skill ORCHESTRATES. It does not hand-do the bulk analysis or the doc sweep -- it spawns subagents (Task) for those passes and integrates their output. All emitted artifacts are PARAMETRIZED from Step 1 findings; templates live in `references/`.

> **Spawn from MAIN only.** This skill is inline (no `context`), so its Task spawns are first-level. Do not nest.

> **Read reference templates** with the `Read` tool using `${CLAUDE_SKILL_DIR}/references/<file>` to load them into context.

> **Fence rule -- GLOBAL, every emit on every path (P2, P3, P3.5, P4a-b, and `PU`'s U3/U4 drift-ADD).** When writing any generated file, unescape its inner code fences (`\`\`\`` -> ```` ``` ````) so the emitted file has valid fences. Stated once here; the reference templates !=repeat it.

## Prompt contract

Position 1 of `$ARGUMENTS` is a **free-form prompt** -- the verb, the target path and the optional
directive (P0 below) may all follow it in any order, exactly as P0 already parses them. Nobody types
keys: a plain sentence resolves the verb.

| Mode | EN keywords | RU keywords | Mutates? |
|------|-------------|-------------|----------|
| `status` | *(empty)*, status, check, show, what's deployed | статус, проверь, покажи, что стоит | no |
| `install` | install, setup, deploy, scaffold, init, create board | установи, разверни, создай доску, настрой | yes |
| `upgrade` | upgrade, retrofit, add spec layer, update | обнови, добавь спек-слой, апгрейд | yes |
| `enable` | enable, turn on, resume, unpause | включи, возобнови, сними паузу | yes |
| `disable` | disable, turn off, pause, mute | выключи, поставь на паузу, приглуши | yes |
| `uninstall` | uninstall, remove, unwire | удали, убери, деинсталлируй | yes |
| `purge` | purge, wipe, delete everything, nuke | вычисти, снеси, удали всё | yes, destructive |

The verb-detection rule already in P0 (a standalone canonical token wins outright; a word merely
containing one inside a sentence does not) IS this contract's steps 1-2 -- no reordering needed, P0
already scores correctly. Empty / no verb -> the documented default: `status` on a deployed board,
else `install` into the resolved `TARGET` (P0's "No verb given" rule). A destructive tie (`uninstall`
vs `purge`) still goes to `AskUserQuestion`; that already happens in P0's two-verb-conflict rule.
Prose that names no canonical verb is DIR (the free-text directive), never guessed as a verb or path.

Immediately after P0 finalizes `MODE` and `TARGET` -- before dispatching into `PS`/`PU`/`PE`/`PR`/P1
-- print this block once:

```
PLAN — brewtools:task-board-setup
INPUT:  <arguments verbatim, or "(empty)">
MODE:   <resolved> -- <explicit | matched keyword: X | default>
SCOPE:  <TARGET>; SPEC_MODE <on|off|n/a>; touching <files this run will write, or "read-only">
DO:     <2-5 imperative bullets>
RESULT: <artifacts the user ends up holding -- board.md + control files, or the status report>
```

Labels are literal ASCII; values follow the conversation language.

Every skill this generator emits (`task-board`, and `task-spec` when `SPEC_MODE=on`) carries this
same contract baked into its own template -- prompt-first hint, its own keyword table, its own PLAN
block. P5's prompt-contract gate below verifies both.

## Delegation

A big task handed to one agent = an agent gone for an hour: you cannot observe it, cannot correct it, and it usually drifts off-target. One subagent = ONE bounded unit -- ONE doc group, ~<=5 files, ~<=10 steps. Bigger MUST be split into N tasks, all spawned in ONE message. Applies to both spawn points: P1 analysis and P4c doc sweep.

Every spawn prompt MUST carry:

| Field | Content |
|-------|---------|
| GOAL | the overall task and why it exists -- the point beyond the file edit |
| ROLE | what this agent owns; what it must NOT touch |
| SCOPE | exact paths/commands in bounds + explicit out-of-bounds |
| CONTEXT | what is already done, by whom, what runs in parallel -- trimmed to what THIS agent needs |
| CONSUMER | who or what uses the result next, and the shape it must fit |
| DONE | acceptance criteria + the exact report shape you want back |

A bare one-line task is never enough. Shape (P4c sweep agent):
```
Task(subagent_type="general-purpose", prompt="
GOAL: deploying a file-based Kanban into TARGET; the board skeleton exists and this pass
  fills it from the repo's pre-existing task docs. Sibling agents handle other doc groups.
ROLE: you own <these DOCS>. Do NOT create tasks that no document supports, do NOT edit
  source dirs, do NOT touch CLAUDE.md.
SCOPE: in -- write ONLY under TARGET/.claude/features/**; read the listed DOCS.
  Out -- EXCLUSIONS (<list from P1>), TARGET/CLAUDE.md, .claude/agents, .claude/skills.
CONTEXT: P1 already confirmed DOMAINS=<...>, REL_STYLE=<...>, LANG=<...> with the user, and
  P4a-b already wrote the board skeleton, TASK_TEMPLATE.md (id convention) and board.md
  (row format) -- read them, do not reinvent either. Sibling agents sweep the other doc
  groups into the same tree right now, so touch only the DOCS listed for you.
CONSUMER: P5 verification counts what landed under closed/ + backlog/, and the installed
  task-tracker agent reads those files from then on -- an id or status dir that deviates from
  TASK_TEMPLATE.md makes the task invisible to it.
DONE: files written under closed/ + backlog/, and a manifest: docs migrated by status,
  docs trashed, board rows authored. A no-op sweep must say so explicitly.
")
```

---

## P0: Resolve verb + target repo + parse directive

`$ARGUMENTS` carries THREE optional, order-independent things: (a) a MODE verb, (b) a target repo PATH, (c) a free-text DIRECTIVE that tunes the optional CLAUDE.md-optimization phase (e.g. "also dedupe rules", "skip module split", "report only"). Disambiguate:
- A standalone token (case-insensitive) from the canonical set `status | install | upgrade | enable | disable | uninstall | purge` sets `MODE` and is CONSUMED -- it never reaches `DIR`. A word merely containing one of them inside a sentence (e.g. "upgrade the rules wording") is NOT the verb; only a standalone token is. Two conflicting verbs -> `AskUserQuestion`.
- A token that resolves to an existing directory (abs, or relative to cwd) = the PATH. Empty / unresolvable-as-dir = cwd.
- Everything else (the remaining free text) = `DIR`, passed verbatim to P5.5. If no path-like token is present, the whole non-verb argument is `DIR` and `TARGET`=cwd.
- If ambiguous (e.g. a bare word that is both a plausible relative dir and a directive verb), prefer PATH only if it resolves to an existing dir; else treat as DIR.

> `init`, `on`, `off`, `setup`, `remove`, `reset`, `create`, `update` and `cleanup` are NOT verbs any more. Recognize `init`/`setup`/`create` in free text as a synonym of `install`, `update` as a synonym of `upgrade`, `on`/`off` as synonyms of `enable`/`disable`, and `remove`/`reset`/`cleanup` as a synonym of `uninstall`/`purge` (ask which), then always echo the canonical verb back. Never print a removed alias as a command.

**No verb given** -- resolve `MODE` from the board itself, after `TARGET` is known: a deployed board (`TARGET/.claude/features/board.md` exists) -> `status`; nothing deployed -> `install` into that `TARGET`. A bare path on a fresh repo therefore still installs, and a bare invocation on a repo that already has a board reports instead of touching anything.

**EXECUTE** using Bash tool. Set `ARG` to the path-like token (or `.`):
```bash
ARG="{{ARGUMENTS_PATH_OR_DOT}}"   # the path-like token, or . for cwd
TARGET="$(cd "$ARG" 2>/dev/null && pwd)"
test -n "$TARGET" && test -d "$TARGET" && echo "TARGET=$TARGET" && echo "OK" || echo "FAIL: target not a dir"
```
> **STOP if FAIL** -- ask the user for a valid repo path.

> **Shell state does NOT survive between Bash tool calls.** Every call is a fresh shell: a variable another block assigned is EMPTY here. So EVERY later block that consumes `TARGET` MUST open by re-establishing it literally -- `TARGET="<absolute path resolved in P0>"`, with the actual resolved path written in, !=the variable name, !=a re-derivation. Same for anything derived from it (`F`, `T`). This applies to all blocks below without exception.

> **Gate blocks assert before they test.** A block whose SILENCE (or whose sole `OK` line) is read as PASS MUST first prove it ran, by opening with this exact statement:

```bash
test -n "$TARGET" && test -d "$TARGET" || { echo "MISS TARGET unresolved -- re-resolve per P0"; exit 1; }
```

> Without it an empty `TARGET` makes the test run against a nonexistent path, the error gets eaten by `2>/dev/null` / `|| true`, and the gate reports PASS having checked nothing. "No output == PASS" is true ONLY when the gate actually ran.

> `{{ARGUMENTS_PATH_OR_DOT}}` is resolved inline in P0 (the parsed path-like token, or `.`), not a template-emit placeholder -- it is absent from the Placeholder map by design.

Record `DIR` = the remaining free text (may be empty) and `MODE` (`status|install|upgrade|enable|disable|uninstall|purge`, or unset); hold both.

**Branch on board presence.** An existing `TARGET/.claude/features/board.md` means the board is already deployed. `install` refuses it; `upgrade`, `enable`, `disable`, `uninstall` and `purge` EXPECT it.

**EXECUTE** using Bash tool:
```bash
TARGET="<absolute path resolved in P0>"
test -n "$TARGET" && test -d "$TARGET" || { echo "MISS TARGET unresolved -- re-resolve per P0"; exit 1; }
test -f "$TARGET/.claude/features/board.md" && echo "EXISTS" || echo "FRESH"
```

Resolve an unset `MODE` here: `EXISTS` -> `status`, `FRESH` -> `install`. Then dispatch:

| `MODE` | board.md | Do |
|--------|----------|-----|
| `status` | either | go to **PS**. Read-only -- never writes, never asks |
| `install` | `FRESH` | continue to the MAJOR-4 guard, then P1 (fresh deploy) |
| `install` | `EXISTS` | STOP. "Board already deployed. To retrofit the spec + design layer onto it, re-run as `/brewtools:task-board-setup upgrade <path>`. To operate the existing board, use `/task-board`." Do not overwrite |
| `upgrade` | `EXISTS` | go to **PU** -- control transfers to `references/10-upgrade.md`. Skip P1-P5.5 entirely |
| `upgrade` | `FRESH` | STOP. "Nothing to upgrade: no `.claude/features/board.md` in TARGET. Run `/brewtools:task-board-setup install <path>` to deploy a fresh board" |
| `enable` | `EXISTS` | go to **PE** with `WANT=enable` |
| `disable` | `EXISTS` | go to **PE** with `WANT=disable` |
| `enable` / `disable` | `FRESH` | run **PS** instead and report that nothing is deployed. There is no machinery to toggle |
| `uninstall` | `EXISTS` | go to **PR** with `KEEP_DATA=true` |
| `purge` | `EXISTS` | go to **PR** with `KEEP_DATA=false` |
| `uninstall` / `purge` | `FRESH` | run **PS** instead and report that nothing is deployed. Do not delete anything on a guess |

> Print the `## Prompt contract` PLAN block once here -- `MODE` and `TARGET` are both resolved --
> before continuing into the dispatched phase.

**MAJOR 4 -- idempotency guard.** `install` path ONLY (`MODE=install` and board.md `FRESH`); `upgrade`, `uninstall` and `purge` skip it, since pre-existing artifacts are exactly what they operate on. A `FRESH` board.md does not prove a clean slate: a prior run may have left other artifacts. After the board.md check, **EXECUTE** using Bash tool:
```bash
TARGET="<absolute path resolved in P0>"
test -n "$TARGET" && test -d "$TARGET" || { echo "MISS TARGET unresolved -- re-resolve per P0"; exit 1; }
PARTIAL=""
for p in .claude/agents/task-tracker.md .claude/skills/task-board/SKILL.md .claude/rules/tasks.md \
  .claude/skills/task-spec/SKILL.md; do
  # A parked `.disabled` twin still occupies the slot -- an install over it would orphan it.
  test -f "$TARGET/$p" -o -f "$TARGET/$p.disabled" && PARTIAL="$PARTIAL $p"
done
test -z "$PARTIAL" && echo "CLEAN" || echo "PARTIAL:$PARTIAL"
```
> If `PARTIAL:` is non-empty (and board.md was `FRESH`), STOP and report the partial deployment. Do NOT blindly overwrite -- ask the user whether to clean those artifacts and redo, or abort. `upgrade` is NOT the fix here: with no board.md there is nothing to upgrade.

---

## PS: Status  (read-only inventory of the TARGET)

Runs for `MODE=status` -- the default on an already-deployed board -- as the fallback when `enable`/`disable`/`uninstall`/`purge` find nothing, and as the proof block after **PE** and **PR**. **Writes nothing, spawns nothing, asks nothing.**

**EXECUTE** using Bash tool:
```bash
TARGET="<absolute path resolved in P0>"
test -n "$TARGET" && test -d "$TARGET" || { echo "MISS TARGET unresolved -- re-resolve per P0"; exit 1; }
C="$TARGET/.claude"; F="$C/features"
# A `.disabled` twin is a PARKED artifact (see PE), not a missing one -- never report it as MISS.
for p in agents/task-tracker.md skills/task-board/SKILL.md rules/tasks.md skills/task-spec/SKILL.md \
  features/board.md features/PROGRESS.md features/TRACKER.md features/TASK_TEMPLATE.md features/INDEX.md \
  features/specs/SPEC_TEMPLATE.md features/specs/DESIGN_TEMPLATE.md; do
  if test -f "$C/$p"; then echo "  ok   $p"
  elif test -f "$C/$p.disabled"; then echo "  off  $p (parked as $(basename "$p").disabled)"
  else echo "  MISS $p"; fi
done
for d in backlog todo progress closed specs; do
  n=$(ls -1 "$F/$d"/*.md 2>/dev/null | wc -l | tr -d ' ')
  test -d "$F/$d" && echo "  ok   features/$d/ ($n md)" || echo "  MISS features/$d/"
done
echo "TARGET=$TARGET"
```

Report, in this shape:
```
task-board-setup — status
target:     <TARGET>
deployed:   yes|no|partial            (board.md present / absent / some artifacts only)
machinery:  enabled|DISABLED|mixed    (every artifact live / every one parked as .disabled / some of each)
spec layer: on|off|parked             (.claude/skills/task-spec/SKILL.md present / absent / .disabled)
tasks:      backlog=N todo=N progress=N closed=N specs=N
next:       install | upgrade | enable | nothing to do
```
`partial` -> name the missing artifacts and say a fresh `install` refuses to overwrite; the user must clean them first. `deployed: yes` + no spec layer -> `next: upgrade`. `machinery: DISABLED` -> `next: enable`, and say the tasks are all still there. `machinery: mixed` -> list which side each artifact is on and recommend re-running the verb that was interrupted.

---

## PU: Upgrade mode  (retrofit onto a deployed board)

Runs ONLY when `MODE=upgrade` and `board.md` EXISTS. Replaces P1-P5.5 -- do not run the fresh-init phases.

Load the upgrade procedure:

Read file: `${CLAUDE_SKILL_DIR}/references/10-upgrade.md`

Hand it:

| Input | Value |
|-------|-------|
| `TARGET` | resolved in P0 |
| `DIR` | remaining free text from P0 (may be empty) |
| recovered FINDINGS | `DOMAINS`, `EXCLUSIONS`, `LANG` re-read from the DEPLOYED artifacts (`.claude/rules/tasks.md`, `.claude/features/TRACKER.md`, `.claude/agents/task-tracker.md`), NOT re-derived from scratch. Anything unrecoverable is ASKED of the user per `10-upgrade.md` U2 -- !=re-analysed, !=guessed. P1 does NOT run on this path, and Agent C (row below) is the only analysis spawn in upgrade mode |
| Agent C output | `DOMAIN_AGENTS`, `ARCHITECT_AGENT`, `AGENT_GAPS` -- always run fresh (the target's agent roster is the whole point) |
| `SPEC_MODE` | forced `on` |

Rules that bind the whole phase:

- **Additive only.** New files (`task-spec` skill, `SPEC_TEMPLATE.md`, `DESIGN_TEMPLATE.md`) are written outright. No existing task file, board row, agent, skill or rule is rewritten wholesale.
- **Every edit of an existing file is gated:** show the exact diff, then **AskUserQuestion** per file. Declined = no edit, continue cleanly.
- **The metadata restamp (`10-upgrade.md` U5b) is UNGATED and always runs**, including when every content row is already SKIP. It rewrites `version` / `generated_by` / `last_updated` in the frontmatter of the nine stamped artifacts and nothing else -- that is the ONLY thing that clears the `stale` verdict `/brewcode:setup-status` reads off `board.md`. An `upgrade` that reports success without moving the stamp sends the user round the same loop next session.
- **Never renumber, never delete.** Existing task ids, scope ids and closed tasks are untouchable. `board.md` rows are never REORDERED and existing cell content is never CHANGED -- the one allowed row edit is APPENDING the new `spec` cell holding `--` to each existing Progress/Todo row, per `10-upgrade.md` U4 (header + separator cells patch with it; a 6-column header over 5-cell rows is corruption, not caution). `spec:` FM backfill is opt-in and !=run by default -- the default writes nothing to task files. When the user accepts it, the value is `pending` or `none` per the needs-spec heuristic -- never `full`.

> `PU` is a thin handoff: `10-upgrade.md` owns detect, verify and report. Do NOT reuse P5 here.

---

## PE: Enable / Disable  (park or restore the machinery, keep every task)

Runs for `MODE=enable` / `MODE=disable` on a deployed board. Replaces P1-P5.5. Writes no content, deletes nothing, spawns nothing.

Claude Code discovers a project agent only as `.claude/agents/<name>.md`, a project skill only as `<dir>/SKILL.md`, and auto-loads a rule only as `.claude/rules/*.md`. Withholding that one filename is therefore the whole switch:

| Artifact | `disable` | `enable` |
|----------|-----------|----------|
| `.claude/agents/task-tracker.md` | -> `task-tracker.md.disabled` | back |
| `.claude/skills/task-board/SKILL.md` | -> `SKILL.md.disabled` | back |
| `.claude/skills/task-spec/SKILL.md` (when the spec layer is deployed) | -> `SKILL.md.disabled` | back |
| `.claude/rules/tasks.md` | -> `tasks.md.disabled` | back |
| `.claude/features/**` (board, control files, every task and spec) | **untouched** | untouched |

`disable` leaves the board fully readable as plain markdown and every generated file byte-identical -- only the extension Claude Code keys on is withheld. Nothing is regenerated on `enable`: no re-analysis, no subagents, no confirmation of FINDINGS. This is the reversible pause; `uninstall` is the removal.

Skill directories are parked at their `SKILL.md`, never by renaming the directory -- `references/` beside it must keep resolving for anyone reading the files by hand.

**EXECUTE** using Bash tool (substitute `WANT`):
```bash
TARGET="<absolute path resolved in P0>"
test -n "$TARGET" && test -d "$TARGET" || { echo "MISS TARGET unresolved -- re-resolve per P0"; exit 1; }
C="$TARGET/.claude"
WANT=WANT   # enable | disable
MOVED=0; NOOP=0; MISSING=0
for p in agents/task-tracker.md skills/task-board/SKILL.md skills/task-spec/SKILL.md rules/tasks.md; do
  live="$C/$p"; parked="$C/$p.disabled"
  if [ "$WANT" = "disable" ]; then from="$live"; to="$parked"; else from="$parked"; to="$live"; fi
  if [ -f "$from" ]; then
    mv "$from" "$to" && echo "  MOVED $p -> $(basename "$to")" && MOVED=$((MOVED + 1))
  elif [ -f "$to" ]; then
    echo "  NOOP  $p already $WANT""d"; NOOP=$((NOOP + 1))
  else
    echo "  ABSENT $p (not deployed)"; MISSING=$((MISSING + 1))
  fi
done
echo "WANT=$WANT MOVED=$MOVED NOOP=$NOOP ABSENT=$MISSING"
test "$MOVED" -gt 0 -o "$NOOP" -gt 0 && echo "OK" || echo "FAIL nothing to toggle"
```
> **STOP if FAIL** -- none of the four artifacts is present in either state; the deployment is broken, report it and offer `install` after a `purge`.

`ABSENT skills/task-spec/SKILL.md` alone is EXPECTED on a board installed with `SPEC_MODE=off` -- it is not an error. `MOVED=0` with `NOOP>0` means the board was already in the requested state: say so, change nothing else.

Then run the `PS` block again and print its report -- it is the proof, not the `OK` line. Close by naming the reversal verb and stating that `.claude/features/**` was not touched, so every task survived.

---

## PR: Uninstall / Purge  (remove what this skill deployed)

Runs for `MODE=uninstall` (`KEEP_DATA=true`) and `MODE=purge` (`KEEP_DATA=false`). Replaces P1-P5.5.

| Removed | `uninstall` | `purge` |
|---------|-------------|---------|
| `.claude/agents/task-tracker.md` | yes | yes |
| `.claude/skills/task-board/` | yes | yes |
| `.claude/skills/task-spec/` | yes | yes |
| `.claude/rules/tasks.md` | yes | yes |
| any `.disabled` twin of the four above (parked by `disable`) | yes | yes |
| `.claude/features/**` (board, control files, every task and spec) | **KEPT** | yes |

The split is deliberate: the generated agent/skills/rule are MACHINERY, `.claude/features/**` is the user's DATA -- every task they ever wrote. `uninstall` unwires the machinery and leaves the data readable; only `purge` deletes the tasks.

**Confirm before deleting.** Print the exact file list from `PS` and `AskUserQuestion` once. For `purge` the question MUST state the task counts being destroyed (`closed=N` included) and offer `uninstall` (keep the data) as an alternative option. A declined confirmation ends the run cleanly -- delete nothing.

**EXECUTE** using Bash tool (substitute `KEEP_DATA`):
```bash
TARGET="<absolute path resolved in P0>"
test -n "$TARGET" && test -d "$TARGET" || { echo "MISS TARGET unresolved -- re-resolve per P0"; exit 1; }
C="$TARGET/.claude"
KEEP_DATA=KEEP_DATA   # true for uninstall, false for purge
# The `.disabled` twins go too -- removing a DISABLED board would otherwise leave the parked files behind.
rm -f  "$C/agents/task-tracker.md" "$C/agents/task-tracker.md.disabled" \
       "$C/rules/tasks.md" "$C/rules/tasks.md.disabled"
rm -rf "$C/skills/task-board" "$C/skills/task-spec"
test "$KEEP_DATA" = "false" && rm -rf "$C/features"
test ! -e "$C/agents/task-tracker.md" && test ! -e "$C/agents/task-tracker.md.disabled" \
  && test ! -e "$C/skills/task-board" && echo "OK removed" || echo "FAIL still present"
```

Then run the `PS` block again and print its report -- it is the proof, not the `OK` line.

> **CLAUDE.md is never reverted.** If P5.5 optimized the target's `CLAUDE.md` on the way in, that edit stays: it is the user's prose by now, and this skill has no record of the original. Say so explicitly in the final report and point at git history for a revert.

---

## P1: Multi-agent repo analysis  (Step 1)

Load the analysis contract and confirmation template:

Read file: `${CLAUDE_SKILL_DIR}/references/01-analysis.md`

Follow it to spawn analysis subagents IN PARALLEL (one message, multiple Task calls). Spawn the agents prescribed there (default: `Plan` for domains + release style, `Explore` for source-path exclusions + doc inventory, **Agent C** for the domain-agent inventory). Each returns a structured block. Integrate into a single FINDINGS object:

```
DOMAINS   = [ ... ]   # per-repo first-kebab id segments, derived from the repo
EXCLUSIONS= [ ... ]   # source dirs TT must NEVER write (e.g. src/, backend/, e2e-tests/, docs/)
REL_STYLE = vtag | sha | none   # detected from git tags / CI / CLAUDE.md release flow
LANG      = English | <repo doc language>
DOCS      = [ ... ]   # existing backlog/feature/task docs found, for the Step-4 sweep
DOMAIN_AGENTS  = [ ... ]  # Agent C: TARGET .claude/agents/** -> agent | domains covered | specialty
ARCHITECT_AGENT= <name>   # Agent C: best architecture-capable project agent, else `Plan`
AGENT_GAPS     = [ ... ]  # Agent C: DOMAINS with no owning agent -> they fall back to `Plan`
```

Present FINDINGS to the user with **AskUserQuestion** per the contract in `01-analysis.md` (confirm/override DOMAINS and EXCLUSIONS especially). Do not generate until the user confirms.

> In the SAME confirmation, also ask whether to run the optional **CLAUDE.md optimization** phase (P5.5) after the board is deployed. Default: offer it; if the user passed a `DIR` directive in `$ARGUMENTS`, default the answer to YES. Record `OPTIN`.

> In the SAME confirmation, also confirm **`SPEC_MODE`** (`on` | `off`) -- per the contract in `references/01-analysis.md`, which owns the exact question wording. `on` = non-trivial tasks additionally get `specs/<ID>-spec.md` + `specs/<ID>-design.md`, and the `task-spec` skill is emitted. Show `AGENT_GAPS` in the question so the user decides with the fallback cost visible. Record `SPEC_MODE`.

> `SPEC_MODE=off` is the compatibility contract: every emitted artifact is byte-identical to the pre-spec-layer generator. It is not a "reduced" mode, it is the old mode.

> **Empty DOMAINS edge:** if analysis yields no domains, do NOT proceed with an empty `{{DOMAINS}}` (it would produce broken ids like `T--SLUG`); ask the user to name at least one domain via AskUserQuestion, or fall back to a single `CORE` domain.

---

## Placeholder map

The reference templates carry these placeholders. Derive each from the confirmed FINDINGS before substituting. `{{RELEASE_STYLE}}` is the INPUT enum (`vtag|sha|none`) only -- it is NOT a literal token in any template; it picks the close-marker wording below.

> **Order is fixed, substitution is TWO-PASS.** Pass 1: expand the gated placeholders (inventory below). Pass 2: substitute the base placeholders in the table below over the WHOLE result. A gated expansion may itself contain a base token (`02`'s `{{SPEC_TRIGGERS}}` expansion contains `{{FIRST_DOMAIN}}`); the reverse never happens. Reversing the passes emits a literal `{{FIRST_DOMAIN}}`.

> **Two brace spellings, on purpose.** This skill's own tokens are DOUBLE-brace (`{{DOMAINS}}`, `{{TODAY}}`, `{{SPEC_*}}` ...). The four metadata tokens are SINGLE-brace -- `{PLUGIN_VERSION}`, `{CONTENT_VERSION}`, `{GENERATED_BY}`, `{LAST_UPDATED}` -- the repo-wide spelling fixed by `brewcode/skills/setup-status/references/artifact-metadata.md`. Substitute both sets in pass 2; a leftover `{PLUGIN_VERSION}` in an emitted file is as broken as a leftover `{{DOMAINS}}`.

| Placeholder | Owner refs | Derivation |
|-------------|-----------|------------|
| `{{DOMAINS}}` | 01,02,04,05,08,10 | confirmed domain id-segment list, comma-separated (e.g. `HTML, KV, SITE`) |
| `{{FIRST_DOMAIN}}` | 02,04,05,08,09,10 | `DOMAINS[0]` |
| `{{EXCLUSIONS}}` | 02,08,10 | confirmed source-dir exclusion list |
| `{{REPO_NAME}}` | 05,08,09,10 | basename of `TARGET` |
| `{{LANG}}` | 02,03,04,05,08,09,10 | confirmed doc language |
| `{{TODAY}}` | 05,08,09,10 | today's date, ISO (`YYYY-MM-DD`) |
| `{PLUGIN_VERSION}` | 02,03,04,05,08,10 | brewtools plugin version, `X.Y.Z`. Resolved by the bash block below -- NEVER hardcoded, never guessed |
| `{CONTENT_VERSION}` | 02,03,04,05,08,10 | this SKILL.md's own `content_version`, read from its line-1 `brewcode-meta:` marker (below the frontmatter) -- self-located, same as `{PLUGIN_VERSION}`, never a copy of it |
| `{GENERATED_BY}` | 02,03,04,05,08,10 | the literal `brewtools:task-board-setup` |
| `{LAST_UPDATED}` | 02,03,04,05,08,10 | same value as `{{TODAY}}`, quoted in YAML frontmatter. Metadata spelling of the date; `{{TODAY}}` stays the prose/card spelling |
| `{{CLOSE_MARKER}}` | 02,10 | derived from `RELEASE_STYLE`: `vtag` -> `"vX.Y.Z tag + commit SHA"`; `sha` -> `"commit SHA"`; `none` -> `"date / no tag / superseded / cancelled"`. Exact per-ref wording maps live in `02` and `03` |
| `{{CLOSE_MARKER_SHORT}}` | 03,04,05,10 | same enum, short form: `vtag` -> `"vX.Y.Z tag"`; `sha` -> `"commit SHA"`; `none` -> `"no tag"`. `04` and `05` reuse `03`'s map |
| `{{DOMAIN_AGENTS}}` | 08,10 | a COMPLETE markdown table from Agent C's inventory of TARGET `.claude/agents/**` -- header row + `\|---\|` separator + one row per agent, columns exactly `agent \| domains covered \| specialty`. Consumers paste it bare, so a bodiless expansion renders as literal pipe text. Exception: no agents found -> the non-table literal line `(none found -- fall back to the built-in Plan agent and say so in Evidence)` |
| `{{ARCHITECT_AGENT}}` | 08,10 | `ARCHITECT_AGENT` from Agent C: the best architecture-capable project agent name; none -> the literal `Plan` |
| `{{RELEASE_STYLE}}` | 02 (header) | INPUT enum `vtag\|sha\|none`. Gate variable ONLY -- NOT a literal token in any emitted body; it picks the close-marker wording above |
| `{{SPEC_MODE}}` | 03,04,09 (headers) | `on` \| `off`, as confirmed in P1. Gate variable ONLY -- like `{{RELEASE_STYLE}}` it is NOT a literal token in any template and is never substituted into an emitted body; it selects which gated blocks expand |

### Resolving `{PLUGIN_VERSION}` / `{CONTENT_VERSION}` / `{GENERATED_BY}` / `{LAST_UPDATED}`

Run ONCE, before P2, and hold the four values for every emitted file. **EXECUTE** using Bash tool:
```bash
SD="${CLAUDE_SKILL_DIR}"
if [ -n "$SD" ] && [ -f "$SD/../../.claude-plugin/plugin.json" ]; then BT_ROOT=$(cd "$SD/../.." && pwd); else BT_ROOT=$(ls -d ~/.claude/plugins/cache/claude-brewcode/brewtools/*/ 2>/dev/null | sort -V | tail -1 | sed 's:/*$::'); fi
[ -n "$BT_ROOT" ] || { echo "ERROR: cannot locate brewtools plugin root -- install/update brewtools first."; exit 1; }
PV=$(jq -r '.version // empty' "$BT_ROOT/.claude-plugin/plugin.json" 2>/dev/null || true)
PV=${PV:-$(basename "$BT_ROOT")}
# content_version -- this SKILL.md's own header marker, self-located the same way PV is.
SKILL_MD="$BT_ROOT/skills/task-board-setup/SKILL.md"
CV=$(grep -m1 'brewcode-meta:' "$SKILL_MD" | sed -n 's/.*content_version=\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/p')
[ -n "$CV" ] || { echo "ERROR: cannot read content_version from $SKILL_MD -- reinstall brewtools."; exit 1; }
echo "PLUGIN_VERSION=$PV"
echo "CONTENT_VERSION=$CV"
echo "GENERATED_BY=brewtools:task-board-setup"
echo "LAST_UPDATED=$(date +%F)"
```
> **Why the bare form.** `CLAUDE_SKILL_DIR` is a TEXT SUBSTITUTION on the skill prompt, not an env var: CC 2.1.226 rewrites only the EXACT dollar-brace literal `{CLAUDE_SKILL_DIR}` (`replace(/\$\{CLAUDE_SKILL_DIR\}/g, dirname(skillPath))` and a string-pattern `replaceAll`). A brace-modifier form such as `:-fallback` inside the braces is therefore NOT matched, reaches the shell verbatim, and its fallback ALWAYS wins. `CLAUDE_PLUGIN_ROOT` is a real env var but is exported only to hook processes and MCP servers -- never to a skill's Bash tool -- so it is ALWAYS empty here. The skill dir is correct in a cache install AND in a `--plugin-dir` dev run; the cache glob below it is a last-resort fallback only, and it would name the INSTALLED plugin.
> If `PLUGIN_VERSION` comes back empty or non-`X.Y.Z`, STOP and report -- do not emit a file with a guessed or literal-placeholder version.

These three feed the four-key metadata frontmatter (`doc_type: llm`, `version`, `generated_by`, `last_updated`) on every emitted artifact: the `task-tracker` agent (02), the `task-board` (03) and `task-spec` (08) skills, the `tasks.md` rule (04), and the five `.claude/features/**` control files (05). `doc_type` is the literal `llm` -- no placeholder. Per-task CARD frontmatter (`id/title/status/priority/owner/created/updated/tags/links/spec`) is domain data and never carries these keys.

### Gated placeholders -- the convention

The spec layer adds gated blocks inside otherwise-unchanged templates, following the `{{CMD_DECOMPOSED_NOTE}}` convention already used in `references/02-task-tracker-agent.md`. Every gated placeholder has exactly ONE of TWO kinds -- `line` or `inline` -- declared in the header of its owning reference file, alongside its expansion. That header is the source of truth for the EXPANSION TEXT and the whitespace handling; the inventory below is the complete name / kind / gate index.

| Rule | Detail |
|------|--------|
| Every gate is per-token | A gated placeholder carries its OWN gate CONDITION -- `SPEC_MODE=on`, `SPEC_MODE=off`, or `CMD_DECOMPOSED=true`. "Emitted" always means THAT condition is true. There are TWO gate variables, and one of them has an `off` arm: !=assume the condition is `SPEC_MODE=on`, !=key any removal off `SPEC_MODE` alone |
| Kind `line` | the token is the only reason its line exists. Condition TRUE -> replace the whole LINE with the expansion. Condition FALSE -> REMOVE the whole LINE. No blank line left behind, no orphan heading, no trailing separator |
| `_ON` / `_OFF` arms | an `_ON`/`_OFF` pair is ONE gate written as two adjacent `line` placeholders. `_ON` emits when the gate is `on`; `_OFF` emits when the gate is `off`; the other arm's line is REMOVED. EXACTLY one arm of the pair survives every run, in BOTH modes -- !=both, !=neither, !=a third kind. The `_OFF` arm is how a `line` placeholder rewrites a baseline line back to its byte-identical original |
| Kind `inline` | the token sits inside a line that exists in BOTH modes. Condition TRUE -> replace the TOKEN with the expansion. Condition FALSE -> delete the TOKEN only; the line stays |
| inline whitespace | declared per site by its own reference file. BOTH forms are legal, do NOT unify them: some sites carry a single space BEFORE the token, deleted together with it (`02`); others carry no leading space and the expansion supplies its own (`03`, `05`). Follow the reference header, never a global rule |
| `SPEC_MODE=off` result | the emitted artifact is byte-identical to the pre-spec-layer output. This holds only if every token was resolved against its OWN condition -- an `_OFF` arm dropped as if it were an `on` token breaks byte-identity |
| Verification | after substitution, `grep -nE '\{\{\|\{(PLUGIN_VERSION\|CONTENT_VERSION\|GENERATED_BY\|LAST_UPDATED)\}'` the written file -- any surviving `{{...}}` OR single-brace metadata token is an unresolved placeholder and a defect. P5 executes this over every emitted path |

#### Gated placeholder inventory (complete)

| Placeholder | Kind | Emitted when | Owner ref |
|-------------|------|--------------|-----------|
| `{{CMD_DECOMPOSED_NOTE}}` | line | `CMD_DECOMPOSED=true` | 02 |
| `{{CMD_DECOMPOSED_INVARIANT}}` | line | `CMD_DECOMPOSED=true` | 02 |
| `{{SPEC_TRIGGERS}}` | inline | `SPEC_MODE=on` | 02 |
| `{{SPEC_BRD_COL}}` | inline | `SPEC_MODE=on` | 02 |
| `{{SPEC_TRIAGE_BLOCK}}` | line | `SPEC_MODE=on` | 02 |
| `{{SPEC_CHECKLIST}}` | line | `SPEC_MODE=on` | 02 |
| `{{SPEC_BRD_FEATURES_ON}}` | line | `SPEC_MODE=on` | 02 |
| `{{SPEC_BRD_FEATURES_OFF}}` | line | `SPEC_MODE=off` | 02 |
| `{{SPEC_DESC_TRIGGERS}}` | inline | `SPEC_MODE=on` | 03 |
| `{{SPEC_ADD_ROW_COL}}` | inline | `SPEC_MODE=on` | 03 |
| `{{SPEC_INVARIANTS}}` | line | `SPEC_MODE=on` | 03 |
| `{{SPEC_ADD_STEP}}` | line | `SPEC_MODE=on` | 03 |
| `{{SPEC_MOVE_STEPS}}` | line | `SPEC_MODE=on` | 03 |
| `{{SPEC_VIEW_FLOW}}` | line | `SPEC_MODE=on` | 03 |
| `{{SPEC_FM_FIELD}}` | inline | `SPEC_MODE=on` | 04 |
| `{{SPEC_RULES}}` | line | `SPEC_MODE=on` | 04 |
| `{{SPEC_COL_H}}` | inline | `SPEC_MODE=on` | 05 |
| `{{SPEC_COL_S}}` | inline | `SPEC_MODE=on` | 05 |
| `{{SPEC_LC_CLOSE}}` | inline | `SPEC_MODE=on` | 05 |
| `{{SPEC_FEATURE_TABLE_HEAD_ON}}` | line | `SPEC_MODE=on` | 05 |
| `{{SPEC_FEATURE_TABLE_HEAD_OFF}}` | line | `SPEC_MODE=off` | 05 |
| `{{SPEC_FM_LINE}}` | line | `SPEC_MODE=on` | 05 |
| `{{SPEC_SCOPE_BLOCK}}` | line | `SPEC_MODE=on` | 05 |
| `{{SPEC_BOARD_COL_NOTE}}` | line | `SPEC_MODE=on` | 05 |
| `{{SPEC_TRACKER_SECTION}}` | line | `SPEC_MODE=on` | 05 |
| `{{SPEC_INDEX_ROWS}}` | line | `SPEC_MODE=on` | 05 |

Two `_ON`/`_OFF` pairs exist: `{{SPEC_BRD_FEATURES_*}}` (02, board section-6 line) and `{{SPEC_FEATURE_TABLE_HEAD_*}}` (05, `board.md` `## Feature specs` header + separator).

> `{{SPEC_INVARIANTS}}` lives in `03` ONLY -- the `02` copy was cut. Names are FILE-SCOPED: resolve every gated token against its owner's header, never across files.
> Refs 01, 06, 07, 08, 09, 10 declare NO gated placeholders. `08` and `09` are gated at WHOLE-FILE granularity (emitted only when `SPEC_MODE=on`); the file is the gate, not a token. A `{{TOKEN}}` in `10` is prose, not a placeholder.

> Gated surfaces by reference: `02` spec triage + checklist + description triggers + board cols/section-6; `03` SPECS view + add/move steps + invariant + description triggers + add-row col; `04` spec rules 13-22 + `spec:` FM field; `05` TRACKER section 10 + `spec:` FM line + `## Scope` block + board `spec` column + Feature-specs header + INDEX rows + lifecycle close gate.

---

## P2: Generate `task-tracker` agent  (Step 2)

Load the agent template:

Read file: `${CLAUDE_SKILL_DIR}/references/02-task-tracker-agent.md`

Substitute every placeholder per the Placeholder map above; each reference file's header also lists the placeholders it uses. `Write` the result to `TARGET/.claude/agents/task-tracker.md`. The template mirrors the brewpage etalon: prime directive (BRD canonical), layout, lifecycle state machine, invariants, id convention, BRD format, grooming loop, procedures, finishing checklist.

> RELEASE_STYLE shapes the closing-marker wording: `vtag` -> `vX.Y.Z tag + commit SHA`; `sha` -> bare commit SHA; `none` -> date / `no tag` / `superseded` / `cancelled`.

---

## P3: Generate `task-board` skill  (Step 3)

Load the skill template:

Read file: `${CLAUDE_SKILL_DIR}/references/03-task-board-skill.md`

Substitute placeholders, then `Write` to `TARGET/.claude/skills/task-board/SKILL.md`. The template mirrors the yasna etalon: on-demand dashboard with flows view / add / move / backlog / groom, delegating non-trivial / bulk passes to the `task-tracker` agent.

---

## P3.5: Generate `task-spec` skill  (SPEC_MODE only)

Run ONLY if `SPEC_MODE=on`. If `off`, skip silently -- write nothing, mention nothing.

Load the skill template:

Read file: `${CLAUDE_SKILL_DIR}/references/08-task-spec-skill.md`

Substitute placeholders (`{{DOMAIN_AGENTS}}`, `{{ARCHITECT_AGENT}}`, `{{DOMAINS}}`, `{{LANG}}`, `{{REPO_NAME}}`, plus whatever the reference header declares), then `Write` to `TARGET/.claude/skills/task-spec/SKILL.md`.

The emitted skill is the spec + design authoring flow: resolve id -> read task + existing specs -> parallel domain research -> **parallel domain-architect design fan-out** -> synthesize design -> synthesize spec -> AskUserQuestion on open questions -> parallel domain-expert review -> coverage gate -> write docs + update task FM and the board row.

> `disable-model-invocation` MUST NOT be set on the emitted skill: plain-pro

…(truncated)
