From 9dcc34a218de4e34a1af709f0e8cbfacee243f2f Mon Sep 17 00:00:00 2001 From: sohmn Date: Tue, 9 Jun 2026 15:06:10 -0700 Subject: [PATCH] chore(fanout): bump to v1.60.0.0 + CHANGELOG entry Queue-aware claim: main at 1.57.7.0, 1.58/1.59 claimed by open PRs. Co-Authored-By: Claude Fable 5 --- CHANGELOG.md | 40 ++++++++++++++++++++++++++++++++++++++++ VERSION | 2 +- fanout/SKILL.md | 48 +++++++++++++++++++++++++++++++++++++++++++++--- package.json | 2 +- 4 files changed, 87 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 351e7cabd..8a0a6013b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,45 @@ # Changelog +## [1.61.0.0] - 2026-07-10 + +**New skill: `/fanout` decomposes a finished design doc into 2-3 parallel agent tasks.** +**Per-slab prompt files plus a worktree dispatch script. v0 produces the plan and stops.** + +After `/office-hours` + eng-review + `/design-consultation` produces a design doc, the bottleneck is execution: one agent works through the whole doc serially. `/fanout docs/designs/MY_FEATURE.md` reads the doc, identifies independent slabs of work via a 4-layer heuristic (numbered phases, implementation subsections, file-reference tables, natural seams), promotes shared groundwork to a synchronous Slab 0, builds a slab matrix with verification gates, and writes a `## Parallel Execution Plan` section back to the doc. Alongside it: one editable `-slab-.prompt.md` per slab and a `worktree-dispatch.sh` with Slab 0 ready to run and Slabs 1-N commented out, uncommented manually after Slab 0 lands on main. + +### The shape of the win + +No production benchmarks yet, this is a v0 release. The structural argument: + +- Serial implementation of a 3-subsystem design doc waits on one agent finishing each subsystem in sequence. +- Parallel implementation runs three agents concurrently after a brief Slab 0 (shared groundwork) lands. Wall-clock collapses to Slab 0 time plus the slowest slab, instead of the sum. +- The slab matrix plus explicit Slab 0 promotion is the structural defense against three parallel agents tripping over the same shared type file. The skill also names the coordination tax it can't remove: every slab's /ship queues a CHANGELOG/VERSION entry, and each slab after the first rebases over the earlier ones. + +Real numbers land in v1 with the first production runs and a paid E2E eval. Until then the win is qualitative: it turns "wait for one agent" into "wait for the slowest of three." + +### What this means for builders + +If you're sitting on a finished design doc that touches 3+ subsystems, `/fanout` turns it into 2-3 worktrees you can dispatch in parallel, each with an editable prompt file. Read the appended section, tweak the prompts if you want, run the script, let Slab 0 land before uncommenting the rest. Caps at 3 slabs by default because more is usually false parallelism. Pure prompt-based skill, no new binaries, no eval cost. + +### Itemized changes + +#### Added +- `/fanout` skill at `fanout/SKILL.md.tmpl` and generated `fanout/SKILL.md`. +- Free `bun test` fixture at `test/fanout.test.ts` validates frontmatter, section structure, and the hardening guardrails below. +- `/fanout` routing line in `CLAUDE.md`, rows in `README.md`, `AGENTS.md`, `docs/skills.md`, and a `test/skill-coverage-matrix.ts` entry. + +#### Hardening (from the first PR's adversarial review, all six prior limitations closed) +- Worktree dirs and branch names carry a doc-path hash suffix, so two design docs with the same filename stem can't collide. +- CHANGELOG/VERSION are named expected-conflict files in the Merge order, the per-slab prompts, and the wall-clock report, so the coordination tax of parallel /ship is visible up front. +- Slab 0 promotion keeps contract ownership with the owning slab (Slab 0 lands skeletons only). +- An over-decomposition guard stops the skill when more than a third of slabs end up chained, and the natural-seams heuristic refuses docs whose shared contracts are marked draft. +- Table cells escape `|` and collapse newlines so odd file paths can't corrupt the slab matrix. +- Per-slab prompt files replace heredocs in the dispatch script, eliminating the EOF-breakout class and making each slab's marching orders user-editable before dispatch. + +#### For contributors +- Design doc at [`docs/designs/FANOUT.md`](docs/designs/FANOUT.md) documents the slab detection heuristic, Slab 0 promotion logic, conflict resolution rules, edge cases, and the v0.1 hardening section. +- No new infrastructure: skill is auto-discovered by `setup` via the existing top-level-directory glob. + ## [1.60.1.0] - 2026-07-09 ## **The /autoplan dual-voice eval is back on the board, catching real regressions.** diff --git a/VERSION b/VERSION index c4190e004..82b3fa4c6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.60.1.0 +1.61.0.0 diff --git a/fanout/SKILL.md b/fanout/SKILL.md index a1ba8caf5..ae066aa0c 100644 --- a/fanout/SKILL.md +++ b/fanout/SKILL.md @@ -54,6 +54,24 @@ echo "REPO_MODE: $REPO_MODE" _SESSION_KIND=$(~/.claude/skills/gstack/bin/gstack-session-kind 2>/dev/null || echo "interactive") case "$_SESSION_KIND" in spawned|headless|interactive) ;; *) _SESSION_KIND="interactive" ;; esac echo "SESSION_KIND: $_SESSION_KIND" +# Conductor host: AskUserQuestion is unreliable here (native disabled, MCP +# variant flaky), so skills render decisions as prose instead of calling the +# tool. Gated on !headless so an eval/CI run INSIDE Conductor (GSTACK_HEADLESS) +# still BLOCKs rather than rendering prose to nobody. +if [ "$_SESSION_KIND" != "headless" ] && { [ -n "${CONDUCTOR_WORKSPACE_PATH:-}" ] || [ -n "${CONDUCTOR_PORT:-}" ]; }; then + echo "CONDUCTOR_SESSION: true" +fi +_ACTIVATED=$([ -f ~/.gstack/.activated ] && echo "yes" || echo "no") +_FIRST_LOOP_SHOWN=$([ -f ~/.gstack/.first-loop-tip-shown ] && echo "yes" || echo "no") +echo "ACTIVATED: $_ACTIVATED" +echo "FIRST_LOOP_SHOWN: $_FIRST_LOOP_SHOWN" +# First-run project detection: run the detector ONLY on the first-ever skill run +# (ACTIVATED=no, interactive) so it stays off the hot path for every run after. +_FIRST_TASK="" +if [ "$_ACTIVATED" = "no" ] && [ "$_SESSION_KIND" != "headless" ]; then + _FIRST_TASK=$(~/.claude/skills/gstack/bin/gstack-first-task-detect 2>/dev/null || true) +fi +echo "FIRST_TASK: $_FIRST_TASK" _LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") echo "LAKE_INTRO: $_LAKE_SEEN" _TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) @@ -223,6 +241,24 @@ touch ~/.gstack/.proactive-prompted Skip if `PROACTIVE_PROMPTED` is `yes`. +## First-run guidance (one-time) + +If `ACTIVATED` is `no` (first skill run on this machine) AND the preamble printed a non-empty `FIRST_TASK:` value that is NOT `nongit`: show ONE short, project-specific line mapped from the token, as a heads-up, then CONTINUE with whatever the user actually asked — do NOT halt their task. Map the token: `greenfield` → "Fresh repo — shape it first with `/spec` or `/office-hours`." `code_node`/`code_python`/`code_rust`/`code_go`/`code_ruby`/`code_ios` → "There's code here — `/qa` to see it work, or `/investigate` if something's off." `branch_ahead` → "Unshipped work on this branch — `/review` then `/ship`." `dirty_default` → "Uncommitted changes — `/review` before committing." `clean_default` → "Pick one: `/spec`, `/investigate`, or `/qa`." Then substitute the token you saw for TASK_TOKEN and run (best-effort), and mark activated: +```bash +~/.claude/skills/gstack/bin/gstack-telemetry-log --event-type first_task_scaffold_shown --skill "TASK_TOKEN" --outcome shown 2>/dev/null || true +touch ~/.gstack/.activated 2>/dev/null || true +``` + +If `ACTIVATED` is `no` but `FIRST_TASK:` is empty or `nongit` (headless, non-git, or nothing actionable): show nothing, just run `touch ~/.gstack/.activated 2>/dev/null || true`. + +Else if `ACTIVATED` is `yes` AND `FIRST_LOOP_SHOWN` is `no`: say once as a heads-up (then continue): + +> Tip: gstack pays off when you complete one loop — **plan → review → ship**. A common first loop: `/office-hours` or `/spec` to shape it, `/plan-eng-review` to lock it, then `/ship`. + +Then run `touch ~/.gstack/.first-loop-tip-shown 2>/dev/null || true`. + +Skip this section if `ACTIVATED` and `FIRST_LOOP_SHOWN` are both `yes`. + If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`: Check if a CLAUDE.md file exists in the project root. If it does not exist, create it. @@ -303,7 +339,9 @@ AI orchestrator (e.g., OpenClaw). In spawned sessions: "AskUserQuestion" can resolve to two tools at runtime: the **host MCP variant** (e.g. `mcp__conductor__AskUserQuestion` — appears in your tool list when the host registers it) or the **native** Claude Code tool. -**Rule:** if any `mcp__*__AskUserQuestion` variant is in your tool list, prefer it. Hosts may disable native AUQ via `--disallowedTools AskUserQuestion` (Conductor does, by default) and route through their MCP variant; calling native there silently fails. Same questions/options shape; same decision-brief format applies. +**Conductor rule (read before the MCP rule):** if `CONDUCTOR_SESSION: true` was echoed by the preamble, do NOT call AskUserQuestion at all — neither native nor any `mcp__*__AskUserQuestion` variant. Render EVERY decision brief as the **prose form** below and STOP. This is proactive, not a reaction to a failure: Conductor disables native AUQ and its MCP variant is flaky (it returns `[Tool result missing due to internal error]`), so prose is the reliable path. **Auto-decide preferences still apply first:** if a `[plan-tune auto-decide]