# Budi v8.4.1 — Release smoke test plan

**Status**: Hard release gate for [#673](https://github.com/siropkin/budi/issues/673) under epic [#667](https://github.com/siropkin/budi/issues/667).
**Predecessor**: [v8.4.0 smoke test plan](./v8.4.0-smoke-test.md). 8.4.1 is a single-vertical patch that fixes the Copilot Chat live-tailer regression discovered immediately after 8.4.0 ([#647](https://github.com/siropkin/budi/issues/647) post-mortem). The plan below is the v8.4.0 plan extended with three new automated steps (20–22) that close the parser-pipeline gap that let the regression ship green.

## How this gate is run

Every step below MUST be exercised on **macOS, Linux, and Windows** before R2 ([#673](https://github.com/siropkin/budi/issues/673)) tags `v8.4.1`. Steps split into two categories:

- **Automated** — covered by `scripts/e2e/test_655_release_smoke.sh`. Build release binaries (`cargo build --release`) and run the script on each platform's CI runner; PASS exit is required. Steps 13–15 (multi-provider endpoint contract), 18 (path-watcher resilience), 19 (daemon `/health` advertises `api_version`), and the new **20–22** (real-shape parser pipeline, streaming-truncation resilience, doctor AMBER signal) are wired into the script.
- **Manual** — host-extension UI verification. The Cursor / VS Code status bar, `budi doctor` output on a clean machine, and the click-through dashboard surface cannot be exercised from a shell. The release driver runs each manual step on a real install and pastes a PASS/AMBER/FAIL note into the per-platform record below.

Acceptance from #673: **all steps green on each platform**. Any step amber requires a release-block decision in #673 before tagging proceeds.

## What changed vs. v8.4.0

The 8.4.0 R2.2 smoke gate covered the multi-provider statusline wire shape thoroughly (steps 13–15) and the path-watcher resilience contract (step 18), but it seeded `messages` directly via sqlite3 rather than dropping a real-shape session file under the daemon's watched root and letting the parser ingest it. A parser regression of the kind documented in [#668](https://github.com/siropkin/budi/issues/668) — the `github.copilot-chat` ≥0.47.0 mutation-log shape silently dropping every active row — is, by construction, invisible to a smoke gate that doesn't run the parser.

8.4.1 closes that gap with three additions, all in the same `test_655_release_smoke.sh` script (kept under the original name so the per-platform PASS rows in §Per-platform PASS record stay aligned with the script that produces them):

- **Step 20** — drop the [#669](https://github.com/siropkin/budi/issues/669) `vscode_chat_0_47_0.jsonl` real-extension fixture under `workspaceStorage/<hash>/chatSessions/<uuid>.jsonl` and assert the expected per-request rows materialize via parser → tailer → DB. FAILs against the 8.4.0 broken parser, PASSes against the post-#R1.1 reducer.
- **Step 21** — append a kind:2 request stub (no `completionTokens` yet) to the live session file and assert the row count is unchanged (no premature emit). Then append the kind:1 `completionTokens` patch and assert exactly one new row materializes. Pins the no-emit-until-completion contract from [#668](https://github.com/siropkin/budi/issues/668) against the live tailer.
- **Step 22** — verify `budi doctor --format json` reports `pass` for the `tailer rows / Copilot Chat` check after step 20 (rows did flow), then simulate the 8.4.0 broken-parser state (bytes consumed, zero rows emitted) and assert the same check flips to `warn` (AMBER) with the parser-regression hint. The simulated state is byte-equivalent to running the v3 parser against a v4 mutation-log fixture, so this is the gate that would have caught 8.4.0 before the tag.

The test daemon stays on port 17865 (unchanged from 8.4.0) so the script can run alongside a developer's real daemon on the default port 7878. Step 22's `budi doctor` invocation reads daemon_port from `BudiConfig::default()` (= 7878) and therefore reports its `daemon health` check as FAIL (no test daemon answering on 7878 because we point `BUDI_DAEMON_BIN` at a missing path so doctor doesn't auto-start one), but the JSON payload is emitted before the FAIL-induced `exit 2`, so the `tailer rows / Copilot Chat` check is still observable. That check reads `tail_offsets` and `messages` from the analytics DB directly, so it doesn't depend on the daemon being up — exactly what we want for a parser-pipeline assertion.

## Pre-flight (per platform, once)

1. `git fetch && git checkout v8.4.1-rc<n>` (or the candidate ref R2 will tag).
2. `cargo build --release` — produces `target/release/budi` and `target/release/budi-daemon`.
3. Install the matching `siropkin/budi-cursor` 1.4.x extension build into both Cursor and VS Code on the test machine. The 1.4.x line is what advertises `copilot_chat` to the host-scoped statusline endpoint and what consumes the new `contributing_providers` tooltip field.
4. `bash scripts/e2e/test_655_release_smoke.sh` — green is the entry gate for the manual section. If this script fails, **stop and fix**; do not proceed to manual steps until automated coverage is clean.

## Manual section

### Cursor host (regression — must remain identical to 8.3.x / 8.4.0)

This block is the back-compat gate. Anything that diverges from main is a release block — the Cursor surface ships the byte-identical 8.1 single-provider statusline shape (ADR-0088 §4) and clicking through still routes to the provider-scoped Cursor dashboard (ADR-0088 §7, unchanged for provider-scoped surfaces).

1. Fresh `budi init` on a Cursor-only machine.
2. Send one prompt in Cursor chat.
3. Status bar shows `budi · $X 1d · $Y 7d · $Z 30d` within one poll cycle. Cost can lag the Usage API by ~10 min per ADR-0090 — wait a cycle if needed.
4. `budi doctor` lists `cursor` as the detected provider.
5. Click status bar → `app.getbudi.dev/dashboard/sessions` opens, scoped to `provider=cursor`.

### VS Code host (8.4.1 regression — must work on extension ≥0.47.0)

The 8.4.0 cycle shipped a parser that worked on synthetic fixtures but emitted **zero rows** from active `github.copilot-chat` ≥0.47.0 sessions. The 8.4.1 manual gate must be exercised on a machine where the installed extension is ≥0.47.0 (the v1.109+ VS Code chat session storage format). The post-#R1.1 reducer (ADR-0092 §2.3 v4) replays the kind:0 + kind:1/kind:2 mutation log and emits per-request rows synchronously with the response stream — a non-zero `$1d` should be visible in one poll cycle of the first prompt.

6. Fresh `budi init` on a VS Code-only machine with `github.copilot-chat ≥0.47.0` installed.
7. Send one prompt in Copilot Chat.
8. Status bar shows non-zero `$1d` within one poll cycle. **This is the line that 8.4.0 broke** — verify on a real install before tag.
9. `budi doctor` lists `copilot_chat` as the detected provider with non-zero session count and reports `MIN_API_VERSION = 4` (the ADR-0092 §2.6 v4 baseline introduced in R1.1 #668).
10. `budi doctor` reports `pass` for the `tailer rows / Copilot Chat` check (R1.3 #670 signal: bytes flowing AND rows landing).
11. Click status bar → cloud dashboard opens, provider-scoped to `copilot_chat` (ADR-0088 §7 — provider-scoped surfaces stay provider-only even when the host extension is multi-provider).

### VS Code + Cursor on the same host

This is the host-scoped surface ADR-0088 §7 was amended to allow (#648). The host extension running in each editor scopes its statusline to its own host's providers; the cloud dashboard never blends them.

12. Both editors active. Status bar in Cursor scopes to `cursor`. Status bar in VS Code scopes to `copilot_chat`. Tooltip in each editor names only that host's provider(s). The cloud dashboard still shows separate per-provider views, never blended (ADR-0088 §7 unchanged for provider-scoped surfaces).

## Automated section

Run `bash scripts/e2e/test_655_release_smoke.sh` after `cargo build --release`. The script exits non-zero on the first FAIL and prints the offending payload. The steps it pins:

### Multi-provider endpoint contract (ADR-0088 §7, statusline-contract.md) — unchanged from 8.4.0

13. `?provider=cursor,copilot_chat` aggregates 1d/7d/30d and emits `contributing_providers`. The script seeds two assistant rows ($5 cursor + $7 copilot_chat) and asserts the comma-list response sums to $12 with `contributing_providers: ["cursor","copilot_chat"]`. `provider_scope` is omitted on multi-provider requests (single-provider-only field).
14. `?provider=cursor` returns the byte-identical 8.1 single-provider shape: `cost_1d == 7.0`, `provider_scope == "cursor"`. Backwards-compat regression gate.
15. `?provider=unknown_provider` returns 200 with zeros, not an error; `contributing_providers` carries the unknown name through unchanged.

### GitHub Billing API reconciliation (manual — same as 8.4.0)

These two steps are **manual** because they require a configured GitHub PAT under an individual-licensed account and an org-managed test account. Fixtures cannot be checked in (real PAT material). The release driver runs them out-of-band and pastes the PASS notes into the per-platform record.

16. With a configured PAT under an individual-plan fixture: dollar totals in `budi stats --provider copilot_chat` reconcile to GitHub's user billing dashboard within rounding (per ADR-0092 §3.5: `(date, model)` bucket truth-up, `cost_confidence` flips to `exact`, `pricing_source` becomes `billing_api:copilot_chat`).
17. With an org-managed test fixture: the reconciliation call short-circuits cleanly after the two-tick empty-response heuristic from ADR-0092 §3.4. `budi doctor` reports the org-managed line. Local-tail dollars continue to flow with `cost_confidence = estimated` and `pricing_source = manifest:vNNN`.

### Daemon stability — unchanged from 8.4.0

18. Daemon stays up over an extended run with all path watchers attached even when target directories don't yet exist. The script seeds **no** Copilot Chat dirs at boot, materializes the `globalStorage/github.copilot-chat/chatSessions/` tree mid-run, and asserts the watcher attaches on the next backstop tick **without a daemon restart** (the #385 contract).
19. Old daemon + new extension: the daemon's `/health` exposes a numeric `api_version` so the host extension can compare its compiled `MIN_API_VERSION` and surface a remediation banner instead of rendering `$0`.

### Real-shape Copilot Chat parser pipeline (8.4.1 R1.5 #672) — new

20. Real-shape parser pipeline. The script copies the R1.2 ([#669](https://github.com/siropkin/budi/issues/669)) `vscode_chat_0_47_0.jsonl` fixture into a freshly-materialized `workspaceStorage/<hash>/chatSessions/<session-id>.jsonl` path under the test `$HOME`, waits one tailer backstop cycle, and asserts the row count and `SUM(output_tokens)` match the per-request tuples in `vscode_chat_0_47_0.expected.json`. **FAILs against the 8.4.0 broken parser; PASSes against the post-#R1.1 reducer.** This is the gate that would have caught the 8.4.0 ship.
21. Streaming-truncation resilience. The script appends a kind:2 stub for a brand-new request (no `completionTokens` inline, no kind:1 patch) to the live session file and asserts the row count is unchanged (no premature emit). Then it appends the kind:1 `completionTokens` patch and asserts exactly one new row materializes. Pins the "wait for the completion token to arrive" contract from #R1.1 against the live tailer (the unit-test sibling lives in `crates/budi-core/src/providers/copilot_chat.rs`).
22. Doctor AMBER signal. The script runs `budi doctor --format json` and asserts the `tailer rows / Copilot Chat` check is `pass` after step 20 (rows landed). It then simulates the 8.4.0 broken-parser state (bytes consumed, zero rows emitted) by clearing copilot_chat rows from `messages` while leaving `tail_offsets` intact, runs doctor again, and asserts the same check flips to `warn` (AMBER) with the parser-regression hint (`ADR-0092 §2.6 / MIN_API_VERSION` in the detail). The simulated state is byte-equivalent to running the v3 parser against a v4 mutation-log fixture, so this is the gate that would have caught 8.4.0 before tag (the R1.3 [#670](https://github.com/siropkin/budi/issues/670) signal in action).

## Per-platform PASS record

Fill these in during R2 execution. Any non-PASS line blocks the v8.4.1 tag until #673 carries an explicit defer or fix decision.

### macOS

| Step | Status | Notes |
|------|--------|-------|
| 1    |        |       |
| 2    |        |       |
| 3    |        |       |
| 4    |        |       |
| 5    |        |       |
| 6    |        |       |
| 7    |        |       |
| 8    |        | 8.4.0 regression — exercise on a real ≥0.47.0 install |
| 9    |        | MIN_API_VERSION must read 4 |
| 10   |        | tailer rows / Copilot Chat = pass on a clean install |
| 11   |        |       |
| 12   |        |       |
| 13   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 14   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 15   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 16   |        | individual-plan PAT fixture |
| 17   |        | org-managed PAT fixture |
| 18   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 19   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 20   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 21   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 22   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |

### Linux

| Step | Status | Notes |
|------|--------|-------|
| 1    |        |       |
| 2    |        |       |
| 3    |        |       |
| 4    |        |       |
| 5    |        |       |
| 6    |        |       |
| 7    |        |       |
| 8    |        | 8.4.0 regression — exercise on a real ≥0.47.0 install |
| 9    |        | MIN_API_VERSION must read 4 |
| 10   |        | tailer rows / Copilot Chat = pass on a clean install |
| 11   |        |       |
| 12   |        |       |
| 13   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 14   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 15   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 16   |        | individual-plan PAT fixture |
| 17   |        | org-managed PAT fixture |
| 18   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 19   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 20   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 21   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 22   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |

### Windows

| Step | Status | Notes |
|------|--------|-------|
| 1    |        |       |
| 2    |        |       |
| 3    |        |       |
| 4    |        |       |
| 5    |        |       |
| 6    |        |       |
| 7    |        |       |
| 8    |        | 8.4.0 regression — exercise on a real ≥0.47.0 install |
| 9    |        | MIN_API_VERSION must read 4 |
| 10   |        | tailer rows / Copilot Chat = pass on a clean install |
| 11   |        |       |
| 12   |        |       |
| 13   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 14   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 15   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 16   |        | individual-plan PAT fixture |
| 17   |        | org-managed PAT fixture |
| 18   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 19   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 20   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 21   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |
| 22   |        | covered by `scripts/e2e/test_655_release_smoke.sh` |

## References

- Plan ticket: [#673](https://github.com/siropkin/budi/issues/673) (R2 — release readiness, smoke run, tag, publish, promote)
- Smoke-gate ticket: [#672](https://github.com/siropkin/budi/issues/672) (R1.5 — this gate's automation)
- Parent epic: [#667](https://github.com/siropkin/budi/issues/667) (8.4.1 patch)
- Predecessor: [v8.4.0 smoke test plan](./v8.4.0-smoke-test.md), epic [#647](https://github.com/siropkin/budi/issues/647)
- Statusline contract: [`docs/statusline-contract.md`](../statusline-contract.md)
- ADRs: [ADR-0088 §7](../adr/0088-8x-local-developer-first-product-contract.md), [ADR-0092 §2.3 v4 + §2.6](../adr/0092-copilot-chat-data-contract.md)
- R1 implementation tickets: #668 (mutation-log reducer), #669 (real-extension fixture), #670 (doctor AMBER), #671 (`auto` resolver), #672 (this smoke gate)
