|
| 1 | +# FastStream 0.7 migration — PR1 (defensive pin + planning scaffold) Implementation Plan |
| 2 | + |
| 3 | +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. |
| 4 | +
|
| 5 | +**Goal:** Tighten the FastStream pin to `>=0.6,<0.7`, adopt `--cov-fail-under=100`, and scaffold the `planning/` workflow directory mirroring `faststream-outbox`. Lands the PR2 design doc in the same commit so PR2 has a home. |
| 6 | + |
| 7 | +**Architecture:** Pure config + docs change. No runtime code touched. Single commit on `chore/pin-faststream-pre-0.7`. The coverage gate is conditional: if `just test` is not at 100% today, the gate addition gets reverted and deferred to PR2 (recorded as decision in commit body). |
| 8 | + |
| 9 | +**Tech Stack:** `pyproject.toml`, `uv`, `pytest-cov`, markdown. |
| 10 | + |
| 11 | +**Related spec:** `planning/specs/2026-06-03-faststream-0.7-migration-design.md`. |
| 12 | + |
| 13 | +--- |
| 14 | + |
| 15 | +## Task 1: Create the working branch |
| 16 | + |
| 17 | +**Files:** None (git operation). |
| 18 | + |
| 19 | +- [ ] **Step 1: Confirm clean tree** |
| 20 | + |
| 21 | +Run: `git status` |
| 22 | +Expected: `nothing to commit, working tree clean` (a fresh `planning/` dir from the brainstorming session is acceptable; we'll stage it later). |
| 23 | + |
| 24 | +- [ ] **Step 2: Create branch** |
| 25 | + |
| 26 | +Run: `git switch -c chore/pin-faststream-pre-0.7` |
| 27 | +Expected: `Switched to a new branch 'chore/pin-faststream-pre-0.7'` |
| 28 | + |
| 29 | +## Task 2: Establish coverage baseline (precondition for gate adoption) |
| 30 | + |
| 31 | +**Files:** None (read-only test run). |
| 32 | + |
| 33 | +The spec's R8 flags that adopting `--cov-fail-under=100` in PR1 requires the suite to already hit 100%. This task discovers the baseline. |
| 34 | + |
| 35 | +- [ ] **Step 1: Run full suite via Justfile** |
| 36 | + |
| 37 | +Run: `just test` |
| 38 | +Expected: All tests pass. Final coverage line at bottom of output shows total `%` covered. |
| 39 | + |
| 40 | +- [ ] **Step 2: Record baseline** |
| 41 | + |
| 42 | +Note the total coverage percentage from the report. There are three branches: |
| 43 | + |
| 44 | +- **If `100%`:** Continue to Task 3 — gate adoption is safe. |
| 45 | +- **If `<100%` AND missing lines are trivially testable:** Continue to Task 3 but write the backfill tests in Task 3a (insert before Task 4) before adding the gate. |
| 46 | +- **If `<100%` AND gaps are non-trivial:** Skip the gate adoption entirely; remove "adopt `--cov-fail-under=100`" from this PR's scope; record decision in commit body; gate moves to PR2 where the orphaned-branch removal naturally lifts coverage. |
| 47 | + |
| 48 | +## Task 3: Tighten the FastStream pin |
| 49 | + |
| 50 | +**Files:** |
| 51 | +- Modify: `pyproject.toml` (line 12) |
| 52 | + |
| 53 | +- [ ] **Step 1: Read current dependencies block** |
| 54 | + |
| 55 | +Run: `sed -n '9,14p' pyproject.toml` |
| 56 | +Expected output: |
| 57 | +``` |
| 58 | +requires-python = ">=3.13,<4" |
| 59 | +license = "MIT" |
| 60 | +dependencies = [ |
| 61 | + "faststream~=0.6", |
| 62 | + "redis>=5.0", |
| 63 | +] |
| 64 | +``` |
| 65 | + |
| 66 | +- [ ] **Step 2: Edit the pin** |
| 67 | + |
| 68 | +Change `"faststream~=0.6"` to `"faststream>=0.6,<0.7"` on line 12. |
| 69 | + |
| 70 | +- [ ] **Step 3: Verify edit** |
| 71 | + |
| 72 | +Run: `grep -n "faststream" pyproject.toml | grep -v module` |
| 73 | +Expected: `12: "faststream>=0.6,<0.7",` |
| 74 | + |
| 75 | +## Task 4: Adopt `--cov-fail-under=100` (conditional on Task 2) |
| 76 | + |
| 77 | +**Files:** |
| 78 | +- Modify: `pyproject.toml` (line 67) |
| 79 | + |
| 80 | +**Skip this task if Task 2 step 2 selected the third branch (`<100%`, non-trivial gaps).** |
| 81 | + |
| 82 | +- [ ] **Step 1: Read current pytest config** |
| 83 | + |
| 84 | +Run: `sed -n '66,70p' pyproject.toml` |
| 85 | +Expected output: |
| 86 | +``` |
| 87 | +[tool.pytest.ini_options] |
| 88 | +addopts = "--cov=. --cov-report term-missing" |
| 89 | +asyncio_mode = "auto" |
| 90 | +asyncio_default_fixture_loop_scope = "function" |
| 91 | +``` |
| 92 | + |
| 93 | +- [ ] **Step 2: Append the coverage gate to addopts** |
| 94 | + |
| 95 | +Change `addopts = "--cov=. --cov-report term-missing"` to `addopts = "--cov=. --cov-report term-missing --cov-fail-under=100"`. |
| 96 | + |
| 97 | +- [ ] **Step 3: Verify edit** |
| 98 | + |
| 99 | +Run: `grep -n "addopts" pyproject.toml` |
| 100 | +Expected: `addopts = "--cov=. --cov-report term-missing --cov-fail-under=100"` |
| 101 | + |
| 102 | +## Task 5: Regenerate lockfile |
| 103 | + |
| 104 | +**Files:** |
| 105 | +- Touched (gitignored, not committed): `uv.lock` |
| 106 | + |
| 107 | +`uv.lock` is gitignored in this repo, so the file is local-only. The regeneration confirms the resolver is happy with the tightened pin. |
| 108 | + |
| 109 | +- [ ] **Step 1: Regenerate lock** |
| 110 | + |
| 111 | +Run: `uv lock` |
| 112 | +Expected: `Resolved N packages in <time>` with no error. The `faststream` line in `uv.lock` should resolve to a `0.6.x` version (verify by `grep '^name = "faststream"' -A 2 uv.lock`). |
| 113 | + |
| 114 | +- [ ] **Step 2: Sync environment** |
| 115 | + |
| 116 | +Run: `uv sync --all-extras --all-groups --frozen` |
| 117 | +Expected: Sync succeeds against the frozen lock. |
| 118 | + |
| 119 | +## Task 6: Verify the gate (re-run suite) |
| 120 | + |
| 121 | +**Files:** None. |
| 122 | + |
| 123 | +- [ ] **Step 1: Re-run full suite under the new gate** |
| 124 | + |
| 125 | +Run: `just test` |
| 126 | +Expected: All tests pass; if Task 4 ran, the final line is `Required test coverage of 100% reached`. If Task 4 was skipped, this is just a re-confirmation that the pin change didn't break anything. |
| 127 | + |
| 128 | +## Task 7: Scaffold `planning/` (likely already present from spec-writing) |
| 129 | + |
| 130 | +**Files:** |
| 131 | +- Create (if missing): `planning/specs/.gitkeep` |
| 132 | +- Create (if missing): `planning/plans/.gitkeep` |
| 133 | + |
| 134 | +- [ ] **Step 1: Check current state** |
| 135 | + |
| 136 | +Run: `ls -la planning/specs planning/plans 2>&1` |
| 137 | +Expected: Both directories already exist with `.gitkeep` files and a design doc in `planning/specs/`. (Created at spec-writing time.) |
| 138 | + |
| 139 | +- [ ] **Step 2: Touch any missing `.gitkeep` files** |
| 140 | + |
| 141 | +If either `.gitkeep` is missing: |
| 142 | +Run: `touch planning/specs/.gitkeep planning/plans/.gitkeep` |
| 143 | + |
| 144 | +## Task 8: Confirm design + plan docs are on disk |
| 145 | + |
| 146 | +**Files:** |
| 147 | +- Verify: `planning/specs/2026-06-03-faststream-0.7-migration-design.md` |
| 148 | +- Verify: `planning/plans/2026-06-03-faststream-0.7-migration-pr1-plan.md` (this file) |
| 149 | +- Verify: `planning/plans/2026-06-03-faststream-0.7-migration-pr2-plan.md` |
| 150 | + |
| 151 | +- [ ] **Step 1: List planning files** |
| 152 | + |
| 153 | +Run: `ls planning/specs planning/plans` |
| 154 | +Expected output: |
| 155 | +``` |
| 156 | +planning/plans: |
| 157 | +.gitkeep |
| 158 | +2026-06-03-faststream-0.7-migration-pr1-plan.md |
| 159 | +2026-06-03-faststream-0.7-migration-pr2-plan.md |
| 160 | +
|
| 161 | +planning/specs: |
| 162 | +.gitkeep |
| 163 | +2026-06-03-faststream-0.7-migration-design.md |
| 164 | +``` |
| 165 | + |
| 166 | +## Task 9: Add `## Workflow` section to CLAUDE.md |
| 167 | + |
| 168 | +**Files:** |
| 169 | +- Modify: `CLAUDE.md` |
| 170 | + |
| 171 | +- [ ] **Step 1: Read current CLAUDE.md to confirm anchor** |
| 172 | + |
| 173 | +Run: `grep -n "^## " CLAUDE.md` |
| 174 | +Expected output: |
| 175 | +``` |
| 176 | +5:## Commands |
| 177 | +11:## Tests |
| 178 | +``` |
| 179 | + |
| 180 | +- [ ] **Step 2: Insert `## Workflow` between `## Commands` and `## Tests`** |
| 181 | + |
| 182 | +After the `## Commands` block (which ends with the `just install` line on line 9) and before the `## Tests` heading on line 11, insert this block (mind the leading blank line): |
| 183 | + |
| 184 | +```markdown |
| 185 | + |
| 186 | +## Workflow |
| 187 | + |
| 188 | +Per-feature workflow: brainstorming → spec in |
| 189 | +`planning/specs/YYYY-MM-DD-<slug>-design.md` → writing-plans → |
| 190 | +plan in `planning/plans/YYYY-MM-DD-<slug>-plan.md` → |
| 191 | +executing-plans / subagent-driven-development → |
| 192 | +requesting-code-review → finishing-a-development-branch. |
| 193 | + |
| 194 | +Topic slugs are kebab-case descriptions (e.g. `faststream-0.7-migration`), |
| 195 | +not story IDs. |
| 196 | +``` |
| 197 | + |
| 198 | +- [ ] **Step 3: Verify section order** |
| 199 | + |
| 200 | +Run: `grep -n "^## " CLAUDE.md` |
| 201 | +Expected output: |
| 202 | +``` |
| 203 | +5:## Commands |
| 204 | +11:## Workflow |
| 205 | +22:## Tests |
| 206 | +``` |
| 207 | +(Line numbers approximate; the important thing is `## Workflow` appears between `## Commands` and `## Tests`.) |
| 208 | + |
| 209 | +## Task 10: Verify gitignore does not eat new plan files |
| 210 | + |
| 211 | +**Files:** None (read-only check). |
| 212 | + |
| 213 | +- [ ] **Step 1: Sanity check the gitignore rule** |
| 214 | + |
| 215 | +Run: `git check-ignore -v planning/plans/2026-06-03-faststream-0.7-migration-pr1-plan.md` |
| 216 | +Expected: exit code `1` (no output) — the file is NOT ignored. |
| 217 | + |
| 218 | +If exit code is `0` and output names `plan.md` as the rule: the gitignore rule is matching too aggressively (would surprise me but verify). In that case, edit `.gitignore` to anchor the rule: change the bare `plan.md` line to `/plan.md` so it only matches the root. |
| 219 | + |
| 220 | +## Task 11: Run lint |
| 221 | + |
| 222 | +**Files:** None. |
| 223 | + |
| 224 | +- [ ] **Step 1: Run lint** |
| 225 | + |
| 226 | +Run: `just lint` |
| 227 | +Expected: All four steps (`eof-fixer`, `ruff format`, `ruff check --fix`, `ty check`) green. Note: `eof-fixer` may auto-add newlines to `.gitkeep` or CLAUDE.md — re-stage afterward. |
| 228 | + |
| 229 | +## Task 12: Stage and commit |
| 230 | + |
| 231 | +**Files:** All modified/created files this PR. |
| 232 | + |
| 233 | +- [ ] **Step 1: Stage changes** |
| 234 | + |
| 235 | +Run: |
| 236 | +``` |
| 237 | +git add pyproject.toml \ |
| 238 | + CLAUDE.md \ |
| 239 | + planning/ |
| 240 | +``` |
| 241 | + |
| 242 | +- [ ] **Step 2: Confirm staged files** |
| 243 | + |
| 244 | +Run: `git status --short` |
| 245 | +Expected output (order may vary): |
| 246 | +``` |
| 247 | +M CLAUDE.md |
| 248 | +M pyproject.toml |
| 249 | +A planning/plans/.gitkeep |
| 250 | +A planning/plans/2026-06-03-faststream-0.7-migration-pr1-plan.md |
| 251 | +A planning/plans/2026-06-03-faststream-0.7-migration-pr2-plan.md |
| 252 | +A planning/specs/.gitkeep |
| 253 | +A planning/specs/2026-06-03-faststream-0.7-migration-design.md |
| 254 | +``` |
| 255 | + |
| 256 | +If `uv.lock` shows up in `git status` despite being gitignored, do **not** stage it — confirm `.gitignore` still lists `uv.lock`. |
| 257 | + |
| 258 | +- [ ] **Step 3: Verify no per-call kwarg accidentally landed** |
| 259 | + |
| 260 | +Run: `git diff --cached pyproject.toml` |
| 261 | +Expected: only the two single-line edits (pin + addopts). |
| 262 | + |
| 263 | +- [ ] **Step 4: Commit** |
| 264 | + |
| 265 | +```bash |
| 266 | +git commit -m "$(cat <<'EOF' |
| 267 | +chore: pin faststream <0.7 and adopt planning/ workflow |
| 268 | +
|
| 269 | +- pyproject.toml: faststream~=0.6 -> >=0.6,<0.7. Explicit upper bound |
| 270 | + makes intent visible and lets the 0.7 migration land in a feature |
| 271 | + branch without users pulling unfinished work. |
| 272 | +- pyproject.toml: addopts adds --cov-fail-under=100 (matches sister |
| 273 | + faststream-outbox project). Hardens the coverage gate before the |
| 274 | + 0.7 migration (PR2) removes branches. |
| 275 | +- planning/: scaffold planning/specs/ + planning/plans/ with .gitkeep |
| 276 | + files; mirrors the sister faststream-outbox layout. Includes the |
| 277 | + faststream-0.7 migration design and the two plan files so PR2 has |
| 278 | + a home. |
| 279 | +- CLAUDE.md: new ## Workflow section pointing at the planning/ layout. |
| 280 | +
|
| 281 | +Companion PR migrates to faststream 0.7 and drops 0.6 support |
| 282 | +(>=0.7,<0.8). |
| 283 | +EOF |
| 284 | +)" |
| 285 | +``` |
| 286 | + |
| 287 | +(If Task 4 was skipped, drop the `--cov-fail-under=100` bullet from the body.) |
| 288 | + |
| 289 | +- [ ] **Step 5: Verify commit** |
| 290 | + |
| 291 | +Run: `git log -1 --stat` |
| 292 | +Expected: single commit with the message above and the staged files. |
| 293 | + |
| 294 | +## Task 13: Push and open PR |
| 295 | + |
| 296 | +**Files:** None (git remote operation). |
| 297 | + |
| 298 | +- [ ] **Step 1: Push branch** |
| 299 | + |
| 300 | +Run: `git push -u origin chore/pin-faststream-pre-0.7` |
| 301 | +Expected: `* [new branch]` line and a hint URL for opening the PR. |
| 302 | + |
| 303 | +- [ ] **Step 2: Open PR via gh** |
| 304 | + |
| 305 | +```bash |
| 306 | +gh pr create --title "chore: pin faststream <0.7 and adopt planning/ workflow" --body "$(cat <<'EOF' |
| 307 | +## Summary |
| 308 | +
|
| 309 | +- Tightens the FastStream pin from `~=0.6` to `>=0.6,<0.7`. The companion PR migrates to 0.7 and drops 0.6 support. |
| 310 | +- Adopts `--cov-fail-under=100` (matches sister `faststream-outbox` project). |
| 311 | +- Scaffolds `planning/specs/` and `planning/plans/` and lands the 0.7 migration design + two-PR plan files. |
| 312 | +
|
| 313 | +## Test plan |
| 314 | +
|
| 315 | +- [ ] `just lint` passes |
| 316 | +- [ ] `just test` passes at 100% coverage |
| 317 | +- [ ] `planning/specs/` and `planning/plans/` are tracked by git |
| 318 | +- [ ] `CLAUDE.md` shows the new `## Workflow` section between `## Commands` and `## Tests` |
| 319 | +EOF |
| 320 | +)" |
| 321 | +``` |
| 322 | + |
| 323 | +(If Task 4 was skipped: drop the second bullet of the Summary block.) |
| 324 | + |
| 325 | +- [ ] **Step 3: Note PR URL** |
| 326 | + |
| 327 | +The `gh pr create` command outputs the PR URL. Record it for the user. |
| 328 | + |
| 329 | +--- |
| 330 | + |
| 331 | +## Verification (acceptance gate) |
| 332 | + |
| 333 | +The PR is done iff: |
| 334 | + |
| 335 | +- [ ] `git diff main..chore/pin-faststream-pre-0.7 -- pyproject.toml` shows only the pin edit and (if applicable) the `--cov-fail-under=100` addition. |
| 336 | +- [ ] `git grep -n "faststream~=0.6"` returns nothing on the branch. |
| 337 | +- [ ] `planning/specs/2026-06-03-faststream-0.7-migration-design.md` exists and is tracked. |
| 338 | +- [ ] `planning/plans/2026-06-03-faststream-0.7-migration-pr1-plan.md` (this file) exists and is tracked. |
| 339 | +- [ ] `planning/plans/2026-06-03-faststream-0.7-migration-pr2-plan.md` exists and is tracked. |
| 340 | +- [ ] `planning/specs/.gitkeep` and `planning/plans/.gitkeep` exist and are tracked. |
| 341 | +- [ ] `CLAUDE.md` contains the `## Workflow` section between `## Commands` and `## Tests`. |
| 342 | +- [ ] `just lint` clean on the branch tip. |
| 343 | +- [ ] `just test` green (at 100% if Task 4 ran). |
| 344 | +- [ ] PR is open with the prescribed title and body. |
0 commit comments