mirror of https://github.com/garrytan/gstack.git
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 <noreply@anthropic.com>
This commit is contained in:
parent
bcfa57273b
commit
9dcc34a218
40
CHANGELOG.md
40
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 `<topic>-slab-<k>.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.**
|
||||
|
|
|
|||
|
|
@ -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] <id> → <option>` result has already surfaced for a question, proceed with that option (no prose). Because in Conductor you go straight to prose without ever calling the tool, this auto-decide-first ordering is enforced HERE, not only by the PreToolUse hook. When you render a Conductor prose brief, also capture it with `bin/gstack-question-log` (the PostToolUse capture hook never fires on a prose path, so `/plan-tune` history/learning depends on this call).
|
||||
|
||||
**Rule (non-Conductor):** 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.
|
||||
|
||||
If AskUserQuestion is unavailable (no variant in your tool list) OR a call to it fails, do NOT silently auto-decide or write the decision to the plan file as a substitute. Follow the **failure fallback** below.
|
||||
|
||||
|
|
@ -325,7 +363,11 @@ Tell three outcomes apart:
|
|||
2. **Completeness scores per choice** — explicit `Completeness: X/10` on EACH choice (10 complete, 7 happy-path, 3 shortcut); use the kind-note when options differ in kind not coverage, but never silently drop the score.
|
||||
3. **The recommendation and why** — a `Recommendation: <choice> because <reason>` line plus the `(recommended)` marker on that choice.
|
||||
|
||||
Layout: a `D<N>` title + a one-line note that AskUserQuestion failed and to reply with a letter; the issue ELI10; the Recommendation line; then ONE paragraph per choice carrying its `(recommended)` marker, its `Completeness: X/10`, and 2-4 sentences of reasoning — never a bare bullet list; a closing `Net:` line. Split chains / 5+ options: one prose block per per-option call, in sequence. Then STOP and wait — the user's typed answer is the decision. In plan mode this satisfies end-of-turn like a tool call.
|
||||
Layout: a `D<N>` title + a one-line note to reply with a letter (in Conductor this is the normal path; elsewhere it means AskUserQuestion was unavailable or errored); the issue ELI10; the Recommendation line; then ONE paragraph per choice carrying its `(recommended)` marker, its `Completeness: X/10`, and 2-4 sentences of reasoning — never a bare bullet list; a closing `Net:` line. Split chains / 5+ options: one prose block per per-option call, in sequence. Then STOP and wait — the user's typed answer is the decision. In plan mode this satisfies end-of-turn like a tool call.
|
||||
|
||||
**Continuation — mapping a typed reply back to a brief.** Each brief carries a stable label (`D<N>`, or `D<N>.k` in a split chain). The user references it (e.g. "3.2: B"). A bare letter maps to the single most-recent UNANSWERED brief; if more than one is open (a split chain), do NOT guess — ask which `D<N>.k` it answers. Never apply a bare letter ambiguously across a chain.
|
||||
|
||||
**One-way / destructive confirmations in prose.** When the decision is a one-way door (irreversible or destructive — delete, force-push, drop, overwrite), prose is a WEAKER gate than the tool, so make it stronger: require an explicit typed confirmation (the exact option letter or word), state plainly what is irreversible, and NEVER proceed on a vague, partial, or ambiguous reply — re-ask instead. Treat silence or "ok"/"sure" without the explicit choice as not-yet-confirmed.
|
||||
|
||||
### Format
|
||||
|
||||
|
|
@ -409,7 +451,7 @@ Before calling AskUserQuestion, verify:
|
|||
- [ ] (recommended) label on one option (even for neutral-posture)
|
||||
- [ ] Dual-scale effort labels on effort-bearing options (human / CC)
|
||||
- [ ] Net line closes the decision
|
||||
- [ ] You are calling the tool, not writing prose — unless the documented failure fallback applies (then: prose with the mandatory triad — issue ELI10, per-choice Completeness, Recommendation + `(recommended)` — and a "reply with a letter" instruction, then STOP)
|
||||
- [ ] You are calling the tool, not writing prose — unless `CONDUCTOR_SESSION: true` (then prose is the DEFAULT, not the tool) OR the documented failure fallback applies (then: prose with the mandatory triad — issue ELI10, per-choice Completeness, Recommendation + `(recommended)` — and a "reply with a letter" instruction, then STOP)
|
||||
- [ ] Non-ASCII characters (CJK / accents) written directly, NOT \u-escaped
|
||||
- [ ] If you had 5+ options, you split (or batched into ≤4-groups) — did NOT drop any
|
||||
- [ ] If you split, you checked dependencies between options before firing the chain
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "gstack",
|
||||
"version": "1.60.1.0",
|
||||
"version": "1.61.0.0",
|
||||
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
|
|
|||
Loading…
Reference in New Issue