diff --git a/docs/designs/PLAN_ROLLOUT_V0.md b/docs/designs/PLAN_ROLLOUT_V0.md index c561fc728..40f998682 100644 --- a/docs/designs/PLAN_ROLLOUT_V0.md +++ b/docs/designs/PLAN_ROLLOUT_V0.md @@ -1,188 +1,81 @@ # PLAN_ROLLOUT_V0 — decomposition-as-artifact **Status:** SHIPPED (v0.1.0) -**Skill:** `/plan-rollout` (`plan-rollout/SKILL.md.tmpl`) +**Skill:** `plan-rollout/SKILL.md.tmpl` **Schema:** `docs/SYSTEM_MD.md` -## The problem +## Problem -LLM-assisted coding compresses implementation by 10-100x. It does not -compress *review*. A reviewer still reads code at human speed. A -2,000-line diff across 15 files becomes unreviewable — scope creep -hides, reviewers skim, bugs ship. +LLMs compress implementation 10-100x. They do not compress *review*. A +2,000-line diff is unreviewable — scope creeps, reviewers skim, bugs +ship. No existing skill asks of an actual diff: "one PR, or several?" -gstack's other planning skills scope or review the *plan*. None ask of -the actual diff: "is this one PR, or several?" +## Design -## The design +`/plan-rollout` reads the working diff + optional `SYSTEM.md`, writes +one artifact: `decomposition.md` (per-slice file lists, reader-time +estimates, dependency edges, reconciliation flags). Never splits +branches, never implements, never ships. -`/plan-rollout` reads the working diff (committed + staged + unstaged + -untracked) plus `SYSTEM.md` if present, then writes one artifact: -`decomposition.md`. The artifact contains per-slice file lists, -reader-time estimates, dependency edges, and (when `SYSTEM.md` is -present) contract-graph reconciliation flags. It does not split -branches, does not implement, does not push. +Verdict-first: if the diff is one PR, the artifact says so in one line +and stops. False slicing is worse than no slicing. -The skill is verdict-first. If the diff is one PR's worth, the artifact -says so in one line and stops. False slicing is worse than no slicing. - -`SYSTEM.md` is optional. It declares the semantic contract graph — -role-level relationships between components, including `breaks-if` and -`rollout-edge` annotations. Distinct from the package/import graph, -which is discovered at runtime. When present, it sharpens slice -ordering and surfaces coordinated-deploy edges. When absent, the skill -falls back to path heuristics plus light-touch import discovery. +`SYSTEM.md` is optional input. Path heuristics + import discovery +when absent. ## What v0 ships -| File | Purpose | -|------|---------| -| `plan-rollout/SKILL.md.tmpl` | The skill template (290 lines). | -| `plan-rollout/SKILL.md` | Generated from the template. | -| `docs/SYSTEM_MD.md` | The optional schema spec (215 lines). | -| `docs/designs/PLAN_ROLLOUT_V0.md` | This doc — design + dogfood. | -| `AGENTS.md`, `docs/skills.md` | One-line registry entries. | +| File | Lines | Purpose | +|------|------:|---------| +| `plan-rollout/SKILL.md.tmpl` | 296 | Skill template | +| `plan-rollout/SKILL.md` | 1011 | Generated from template | +| `docs/SYSTEM_MD.md` | 129 | Schema spec (optional input) | +| `docs/designs/PLAN_ROLLOUT_V0.md` | (this file) | Design + dogfood | +| `AGENTS.md`, `docs/skills.md` | +2 | Registry | -Out of v0 scope (deferred): - -- `rollout.md` (rollout/rollback strategy + inverse-rollback auto-gen) -- A spill-check skill for in-progress diffs against the declared slice -- Integrations into `/ship` and `/review` -- A `SYSTEM.md` scaffolder - -The boundary is intentional. If the primitive doesn't fit gstack, -`git rm -r plan-rollout/ docs/SYSTEM_MD.md docs/designs/PLAN_ROLLOUT_V0.md` -is the entire revert. +Out of v0 (deferred): `rollout.md`, spill-check skill, `/ship` and +`/review` integrations, `SYSTEM.md` scaffolder. Reverting v0 is +`git rm -r plan-rollout/ docs/SYSTEM_MD.md docs/designs/PLAN_ROLLOUT_V0.md`. ## Dogfood: PR #1241 -**Target:** [garrytan/gstack#1241](https://github.com/garrytan/gstack/pull/1241) -— `fix(ask-user): keep question payloads compact`. 41 files, +661 / -282. -**Operator:** manual walkthrough against the documented Step-by-Step flow. +Target: [garrytan/gstack#1241](https://github.com/garrytan/gstack/pull/1241) +— 41 files, +661/-282. Manual walkthrough. -41 files in one PR is the canonical "one PR or three?" surface — the -most informative test of the verdict logic, even though (spoiler) the -right answer turns out to be one PR. - -### File breakdown (skill Step 2) - -Bucketed by top-level dir (path-heuristic fallback, no `SYSTEM.md`): +Bucketing (no SYSTEM.md): | Bucket | Files | Lines | |--------|------:|------:| -| `*/SKILL.md` (36 skill regenerations) | 36 | +576 / -252 | -| `scripts/resolvers/preamble/` (the actual fix) | 1 | +16 / -7 | -| `test/fixtures/golden/` (3 golden regenerations) | 3 | +54 / -27 | -| `test/` (2 test files) | 2 | +31 / -3 | +| `*/SKILL.md` regenerations | 36 | +576/-252 | +| `scripts/resolvers/preamble/` (the fix) | 1 | +16/-7 | +| `test/fixtures/golden/` regenerations | 3 | +54/-27 | +| `test/` | 2 | +31/-3 | -### Reader-time estimate (skill Step 4) +Reader time: `ceil(943/80) + ceil(41/5) = 21 min`. Under 30-min cap. -`ceil(943 / 80) + ceil(41 / 5) = 12 + 9 = 21 min`. Under the 30-min cap. +### Verdict -Honest only if you treat the 36 SKILL.md regenerations as skim-time, not -read-time. A reviewer who actually reads each one would burn far longer. -The skill's heuristic does not currently model "mechanical regeneration" -— see Finding 1. +**One PR.** 39 of 41 files are deterministic regenerations of one +source change in `scripts/resolvers/preamble/generate-ask-user-format.ts`. +Not independently shippable — splitting them would leave dependent +fragments. -### Verdict the skill should emit +### Findings (v1.1 backlog) -> **This is one PR. No decomposition needed.** -> -> 39 of 41 files are deterministic regenerations of one source change -> (`scripts/resolvers/preamble/generate-ask-user-format.ts`). They are -> not independently shippable — splitting them off would leave Slice 2 -> with no standalone value and Slice 1 with broken downstream goldens -> until Slice 2 lands. Boil it. -> -> **Reader guide for the single PR:** -> 1. Read `scripts/resolvers/preamble/generate-ask-user-format.ts` -> (+16 / -7). This is the entire substantive change. -> 2. Spot-check 2-3 of the regenerated `*/SKILL.md` files to verify -> the new format is applied as intended. -> 3. Read `test/resolver-ask-user-format.test.ts` (+22 / -3) and -> `test/gen-skill-docs.test.ts` (+9 / -0). These pin the new -> behavior. -> 4. Goldens (`test/fixtures/golden/*-ship-SKILL.md`) are diff-only; -> skim if curious. - -### Findings (v1.1 todos) - -**Finding 1 — Path heuristics can't detect "regenerated output."** - -Without `SYSTEM.md`, the skill buckets by top-level directory. With this -PR it would produce roughly: - -- Slice 0: `scripts/resolvers/preamble/...` (the fix) -- Slice 1: `*/SKILL.md` (the regenerations) -- Slice 2: `test/fixtures/golden/*` (also regenerations) -- Slice 3: `test/*.test.ts` (the tests) - -The verdict logic *should* catch this because Slice 1 and Slice 2 have -no import edges back to anything except the build script — they can't -ship before Slice 0. The topological-order check would collapse them. -But the file count is high enough that a naive operator might still -ship "Source fix" + "Regenerated outputs" as two PRs. The skill needs -a deterministic-regeneration detector: if a slice's only diff is -mechanical output of another slice's source change, merge them. v1 -doesn't have this. - -**Finding 2 — Reader-time is wrong for regeneration-heavy diffs.** - -21 minutes is the right number for "skim 36 generated files + read the -source." It would be the wrong number if a reviewer read each -`SKILL.md`. The heuristic treats lines uniformly. A `regen-multiplier` -flag on slices that match build-output patterns -(`*/SKILL.md` adjacent to a build script, `test/fixtures/golden/*`) -would adjust this — v1.1. - -**Finding 3 — `SYSTEM.md` wouldn't have helped here.** - -A gstack-shaped `SYSTEM.md` with `scripts/resolvers/` marked as a -component and `*/SKILL.md` files marked as `leaf-util` wouldn't have -changed the verdict. The actual signal needed is "this file is a build -output of that file," closer to a Makefile-style dependency than a -contract graph. `SYSTEM.md` is not the right primitive for catching -this. Don't promise it is. - -**Finding 4 — The skill needs an `--explain` mode.** - -A reviewer looking at the verdict "this is one PR" deserves to see why: -how many files were classified as regeneration, which source change -they depend on, what would happen if you tried to slice anyway. v1's -verdict is a one-liner. v1.1 should print the rejected slicing -alternatives and why they were rejected. - -### What v0 got right - -- **Verdict-first design.** The skill is structured to ALWAYS output a - verdict line. Even when the right answer is "one PR," the artifact - produces value: a written record of "we looked, and slicing was - considered and rejected because of these signals." -- **Honest about reader-time.** 21 min is the right number under - reasonable skim assumptions. Calibrated for typical PR reading, not - pathological "read every regenerated file." -- **No silent slicing.** The Step 7 self-check ("If the entire diff - fits comfortably in one slice ... say so plainly") catches the case - where naive bucket-counting would propose 3 slices but the verdict - logic should collapse them. - -## v1.1 roadmap - -1. **Deterministic-regeneration detector.** Heuristic match on filename - patterns (`SKILL.md` adjacent to `SKILL.md.tmpl`, - `test/fixtures/golden/*`) plus an optional `regeneration-of:` field - in `SYSTEM.md`. -2. **Regen-multiplier on reader-time.** Scale regenerated-output line - counts by ~0.1 (skim-reading speed). +1. **Regeneration detector.** Path heuristics don't know `*/SKILL.md` + is mechanical output of `*/SKILL.md.tmpl`. Without it, a naive + operator might still ship "source + regenerations" as two PRs. Fix: + filename-pattern match + optional `regeneration-of:` in SYSTEM.md. +2. **Regen-multiplier on reader-time.** Lines in regenerated files + should count ~0.1x (skim speed). Today they count 1x. 3. **`--explain` mode.** When the verdict is "one PR," print the - rejected slicing alternatives and the signals that rejected them. + rejected slicing alternatives and why each was rejected. 4. **Calibration loop.** Log predicted vs actual reader-time on the - first ~10 real invocations to ground v2 heuristics in data. + first ~10 real invocations. Ground v2 heuristics in data. -## What v0 proves +### Limit surfaced -The skill is honest about the cases where it can produce useful output -(real multi-component diffs with clear seams) and the cases where it -can't (regeneration-heavy diffs, single-source-fan-out patterns). One -PR's worth of analysis on a real diff caught four real limits worth -fixing in v1.1. That's the bar a skill should clear before shipping. +`SYSTEM.md` is **not** the right primitive for build-output coupling. +The signal needed is "file X is generated from file Y" — closer to a +Makefile dependency than a contract graph. Don't promise SYSTEM.md +solves regeneration cases.