@zhuxixi/pi-agent-board 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +22 -13
  3. package/VERIFY.md +2 -1
  4. package/docs/RELEASE.md +148 -0
  5. package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +25 -0
  6. package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +393 -0
  7. package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +51 -0
  8. package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +58 -0
  9. package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +698 -0
  10. package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +605 -0
  11. package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +1183 -0
  12. package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +66 -0
  13. package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +649 -0
  14. package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +618 -0
  15. package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +555 -0
  16. package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +259 -0
  17. package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +43 -0
  18. package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +95 -0
  19. package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +63 -0
  20. package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +57 -0
  21. package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +98 -0
  22. package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +190 -0
  23. package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +394 -0
  24. package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +86 -0
  25. package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +75 -0
  26. package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +226 -0
  27. package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +129 -0
  28. package/package.json +1 -1
  29. package/runner/job-runner.mjs +56 -22
  30. package/runner/pty-runner-legacy.mjs +1 -1
  31. package/runner/pty-runner.mjs +34 -21
  32. package/runner/state-coordinator.mjs +1 -1
  33. package/runner/state-runner.mjs +2 -2
  34. package/scripts/budget-audit.mjs +135 -0
  35. package/scripts/release.mjs +446 -0
  36. package/src/core/auto-state.mjs +12 -10
  37. package/src/core/coordinator-protocol.mjs +3 -1
  38. package/src/core/derive.mjs +3 -0
  39. package/src/core/evidence.mjs +4 -1
  40. package/src/core/host-probe.mjs +12 -1
  41. package/src/core/host-protocol.mjs +43 -5
  42. package/src/core/launch-options.mjs +37 -0
  43. package/src/core/pty-attach-reconnect.mjs +12 -1
  44. package/src/core/rows.mjs +4 -0
  45. package/src/core/state-commands.mjs +62 -8
  46. package/src/core/stop-latch.mjs +41 -0
  47. package/src/core/store.mjs +8 -1
  48. package/src/core/terminal-attach-client.mjs +11 -5
  49. package/src/core/terminal-query-sequences.mjs +158 -0
  50. package/src/core/test-knobs.mjs +28 -0
  51. package/src/core/types.mjs +5 -2
  52. package/src/core/warm-host-sweeper.mjs +13 -0
  53. package/src/runtime/service.mjs +149 -28
  54. package/src/ui/dashboard.ts +66 -9
  55. package/src/ui/pty-attach.ts +128 -3
package/CHANGELOG.md CHANGED
@@ -5,6 +5,46 @@ conventional commits by `scripts/release_helper.mjs`. Entries are
5
5
  forward-only: they begin with the first release after this file landed —
6
6
  for earlier history, see the git log and the pull-request list.
7
7
 
8
+ ## [0.10.0] - 2026-10-01
9
+
10
+ ### Features
11
+
12
+ - **ui**: x then y confirms session deletion (issue #150) (#152)
13
+ - 8th semantic state holding + manual-fence lift on interactive input (issue #145) (#151)
14
+
15
+ ### Fixes
16
+
17
+ - **ci**: read the dispatch recovery tag from inputs, not the ref name (issue #154) (#156)
18
+ - job-runner boot-window stop loss + dead-run reconcile convergence (issue #153) (#155)
19
+ - **attach**: answer the color-scheme query client-side for attached sessions (issue #148) (#149)
20
+
21
+ ### Changes
22
+
23
+ - probe knob guard uses the production-default floor, not a jitter-prone absolute bound (issue #95) (#159)
24
+ - flake postmortems, budget audit gate, ladder knobs (issue #95 F3-F4) (#157)
25
+
26
+ [0.10.0]: https://github.com/zhuxixi/pi-agent-board/compare/v0.9.0...v0.10.0
27
+
28
+ ## [0.9.0] - 2026-09-25
29
+
30
+ ### Features
31
+
32
+ - **attach**: forward terminal queries to the real terminal — automatic theme works in attach sessions (issue #128) (#138)
33
+ - **attach**: unconditional Ctrl+\ detach key, terminal-independent (issue #126) (#137)
34
+
35
+ ### Fixes
36
+
37
+ - **runner**: probes leave attachedClients at hello (issue #130) (#142)
38
+ - browse-mode Tab completes and drills down directories in cwd picker (issue #127) (#141)
39
+
40
+ ### Changes
41
+
42
+ - **release**: orchestrate releases and publish from CI (#144)
43
+ - control-reconcile flaky assertions — windowed recorder + invariant realignment (issue #140) (#143)
44
+ - **attach**: scope A5 mid-stream seq assertion to post-snapshot window (issue #132) (#139)
45
+
46
+ [0.9.0]: https://github.com/zhuxixi/pi-agent-board/compare/v0.8.0...v0.9.0
47
+
8
48
  ## [0.8.0] - 2026-09-23
9
49
 
10
50
  ### Features
package/README.md CHANGED
@@ -93,7 +93,7 @@ From the board:
93
93
  - In Peek, press `r` to reply without attaching.
94
94
  - Press `v` for a read-only transcript, or `e` for evidence and diagnostics.
95
95
  - Press `Enter`, `Right`, or `>` to attach to the real Pi session.
96
- - In PTY attach mode, press `Ctrl+Left` to return to the board at any time (even mid-draft). `Left` detaches only while the child Pi reports an empty editor; otherwise (draft, or unknown editor state) it is forwarded to the child. `Ctrl+]` is not a detach key — it is passed through to the child Pi editor. When the host is disconnected, `Left` always exits.
96
+ - In PTY attach mode, press `Ctrl+Left` to return to the board at any time (even mid-draft), or `Ctrl+\` — the unconditional escape: it detaches regardless of editor state on any terminal that forwards the raw 0x1c byte (raw, kitty, and modifyOtherKeys keyboard modes all work). `Left` detaches only while the child Pi reports an empty editor; otherwise (draft, or unknown editor state) it is forwarded to the child. `Ctrl+]` is not a detach key — it is passed through to the child Pi editor. When the host is disconnected, `Left` always exits. `Ctrl+\` caveats: on keyboard layouts where `\` sits on AltGr (e.g. German) the chord may never produce the byte — use `Ctrl+Left` there; in legacy keyboard modes `Ctrl+4` emits the same byte and also detaches; and the key is captured from the child while attached, so the child's own `Ctrl+\` binding (e.g. the vim terminal-mode prefix) is unavailable. Automatic themes also resolve correctly in attach sessions: the child's background probe (OSC 11) and color-scheme change notifications are forwarded to your real terminal, replies flow back to the child when your terminal answers, and attach re-probes the background once when it settles so the theme is right from the first frame. Set `AGENT_BOARD_FORWARD_TERMINAL_QUERIES=0` to disable this (default on); keyboard-protocol negotiation (kitty/DA) is not forwarded — it happens once at child spawn, before attach exists.
97
97
 
98
98
  ## Dashboard Workflow
99
99
 
@@ -114,7 +114,8 @@ Launch preferences are persisted and reused for later sessions. **Start & attach
114
114
  Session actions are deliberately confirmation-aware:
115
115
 
116
116
  - `d` confirms moving an inactive session to **Done**. Manual completion is the default.
117
- - Press `Ctrl+X` twice quickly to archive/delete the selected row. Archiving removes the row from the board but preserves its underlying Pi session file.
117
+ - `h` toggles the on-hold state (manual verdict — automated writers are fenced).
118
+ - Press `x`, then `y` to confirm deleting the selected row. `Ctrl+X` twice quickly still deletes without confirmation. Archiving removes the row from the board but preserves its underlying Pi session file.
118
119
  - `X` archives inactive rows in the selected state; live work is skipped.
119
120
  - `m` enters multi-select mode. Use `Space` to toggle rows, `a` to select all visible rows, `u` to clear the selection, `d` to mark inactive rows Done, or `Ctrl+X` to delete selected Done rows.
120
121
 
@@ -139,7 +140,9 @@ Shortcuts are scoped to the view where they are available:
139
140
  | `Ctrl+T` | Pin or unpin the selected session. |
140
141
  | `Ctrl+S` | Stop the selected active session. |
141
142
  | `d` | Confirm marking the selected inactive session Done. |
142
- | `Ctrl+X` twice quickly | Archive/delete the selected row. |
143
+ | `h` | Hold or unhold the selected inactive session. |
144
+ | `x`, then `y` | Archive/delete the selected row (confirmation prompt). |
145
+ | `Ctrl+X` twice quickly | Archive/delete the selected row without confirmation. |
143
146
  | `X` | Delete inactive rows in the selected state. |
144
147
  | `m` | Enter multi-select mode. |
145
148
  | `!` | Open node-pty diagnostics and repair hints. |
@@ -156,6 +159,8 @@ Peek shows the selected session's summary, blocker or question, latest output, a
156
159
  | `a`, `Right`, or `>` | Attach to the session. |
157
160
  | `v` | Open the read-only transcript. |
158
161
  | `e` | Open Evidence / Diagnostics. |
162
+ | `d` | Confirm marking the inactive session Done. |
163
+ | `h` | Hold or unhold the session. |
159
164
  | `Up` / `Down` | Move to the previous or next session. |
160
165
  | `Esc` | Return to the main list. |
161
166
 
@@ -174,6 +179,7 @@ The `v` view is a read-only projection of the durable Pi session JSONL. It does
174
179
  | `Enter` or `a` | Attach to the session. |
175
180
  | `e` | Open Evidence / Diagnostics. |
176
181
  | `d` | Confirm marking the inactive session Done. |
182
+ | `h` | Hold or unhold the session. |
177
183
  | `Left` / `Esc` / `<` | Return to the main list. |
178
184
 
179
185
  ### Evidence / Diagnostics
@@ -192,7 +198,9 @@ The `e` view shows durable session evidence, including changed files, commands a
192
198
 
193
199
  ### PTY attach
194
200
 
195
- PTY attach opens the real interactive Pi session. `Ctrl+Left` detaches and returns to the board regardless of editor state. `Left` detaches only while the child Pi reports an empty editor; while you are editing text — or when the child's editor state is unknown (e.g. the child extension is missing) — `Left` is forwarded to the Pi editor. A disconnected host can always be exited with `Left`. `Ctrl+]` is not a detach key — it is passed through to the child Pi editor. While attached, `PageUp`, `PageDown`, `Home`, `End`, and the mouse wheel scroll local scrollback. Mouse drag or double-click selects and copies text, clicks open detected links, and middle-click paste is available on systems with the required X11 tooling.
201
+ PTY attach opens the real interactive Pi session. `Ctrl+Left` detaches and returns to the board regardless of editor state, and `Ctrl+\` is the unconditional escape — a single raw ASCII byte (0x1c) that terminals forward unchanged in raw mode (kitty CSI-u and modifyOtherKeys encodings included), honored no matter what the child editor reports. `Left` detaches only while the child Pi reports an empty editor; while you are editing text — or when the child's editor state is unknown (e.g. the child extension is missing) — `Left` is forwarded to the Pi editor. A disconnected host can always be exited with `Left`. `Ctrl+]` is not a detach key — it is passed through to the child Pi editor. `Ctrl+\` caveats: on keyboard layouts where `\` sits on AltGr (e.g. German) the chord may never produce the byte — use `Ctrl+Left` there; in legacy keyboard modes `Ctrl+4` emits the same byte and also detaches; and the key is captured from the child while attached, so the child's own `Ctrl+\` binding (e.g. the vim terminal-mode prefix) is unavailable. While attached, `PageUp`, `PageDown`, `Home`, `End`, and the mouse wheel scroll local scrollback. Mouse drag or double-click selects and copies text, clicks open detected links, and middle-click paste is available on systems with the required X11 tooling.
202
+
203
+ Automatic light/dark themes work in attach sessions too. The child Pi's background-color probe (OSC 11) and its color-scheme change notification switch are forwarded to your real terminal; replies and notifications flow back to the child when your terminal answers — a terminal that never answers keeps the dark fallback. Attach also re-probes the background once when it settles, so the theme is correct from the first frame without re-selecting it in `/settings`. All of this is on by default; set `AGENT_BOARD_FORWARD_TERMINAL_QUERIES=0` to disable it. Not covered: keyboard-protocol negotiation (kitty flags / DA1) is not forwarded — the child negotiates once at spawn, before any attach client exists, and forwarding it would retune the shared real-terminal keyboard stack — so modifier-key encoding can still differ from a directly-run Pi (the modifyOtherKeys fallback applies).
196
204
 
197
205
  The attach surface can forward terminal clipboard and image/file passthrough sequences. These behaviors can be disabled individually in [Configuration](#configuration). Cold hosts may briefly show a loading/reconnect surface while their PTY becomes ready.
198
206
 
@@ -206,6 +214,7 @@ Agent Board separates a session's semantic task state from whether a worker proc
206
214
  | **Running** | The session is actively processing. |
207
215
  | **Needs answer** | The session is waiting for user input or an answer to a question. |
208
216
  | **Needs instructions** | The run ended without being marked complete and needs the next directive. |
217
+ | **On hold** | The user parked this session intentionally (manual verdict, `h` to toggle). Automated writers can't push it to Done; queued follow-ups do not wake it. |
209
218
  | **Done** | The user marked the inactive session complete; this is the default completion path. |
210
219
  | **Failed** | The worker or host ended with an error. |
211
220
  | **Stopped** | The user stopped the active work. |
@@ -216,6 +225,7 @@ Press `/` to enter filter mode. Filter tokens are case-insensitive and can be co
216
225
 
217
226
  ```text
218
227
  s:running
228
+ s:hold
219
229
  review:ready
220
230
  diag:stalled
221
231
  evidence:error
@@ -383,19 +393,17 @@ npm run verify
383
393
 
384
394
  ## Publishing
385
395
 
386
- Before publishing a release, verify the package, generate the changelog, then bump, and publish — the changelog must be generated **before** `npm version`, because `npm version` commits and tags the bump, which would empty the generation range:
396
+ Releases run through `scripts/release.mjs` and are published by CI when a GitHub Release is created. The flow checks the parts that are easy to get wrong — the changelog range, tag ancestry against `main`, and drift from a late merge — instead of assuming them. See [docs/RELEASE.md](docs/RELEASE.md) for the full process, the one-time npm trusted-publisher setup, and failure recovery.
387
397
 
388
398
  ```bash
389
- npm run verify
390
- npm run changelog -- --dry-run # preview the next section (version = current + patch)
391
- npm run changelog -- patch # inserts the section into CHANGELOG.md
392
- node scripts/release_helper.mjs verify # must exit 0: no functional PR missing from the top section
393
- git add CHANGELOG.md && git commit -m "docs(changelog): <version>"
394
- npm version patch # bumps package.json, commits, and tags vX.Y.Z
395
- npm publish
399
+ node scripts/release.mjs precheck # gate: clean main, nothing in flight
400
+ node scripts/release.mjs prepare minor # changelog + verify + bump + tag + release branch
401
+ node scripts/release.mjs open-pr 0.9.0 # push the branch and tag, open the release PR
402
+ # merge the PR with a merge commit, then:
403
+ node scripts/release.mjs finish 0.9.0 # GitHub Release → CI publishes to npm
396
404
  ```
397
405
 
398
- Use `minor`/`major` in both the changelog and `npm version` steps when appropriate. The changelog is generated from conventional commits since the last `vX.Y.Z` tag (`scripts/release_helper.mjs`, ported from the jfox release flow); review the preview before committing. The `verify` step guards against PRs merged after the changelog was generated — rerun it after any late merge and, after removing the stale top section, re-run `npm run changelog -- <bump>` if it reports missing PRs (apply refuses when the section already exists — remove the stale section first). Release notes for the GitHub Release are the top CHANGELOG section. After publishing, users install the scoped package with:
406
+ After publishing, users install the scoped package with:
399
407
 
400
408
  ```bash
401
409
  pi install npm:@zhuxixi/pi-agent-board
@@ -406,6 +414,7 @@ The Pi package gallery uses the `pi.video` and `pi.image` URLs from `package.jso
406
414
  ## Further Reading
407
415
 
408
416
  - [Manual verification](VERIFY.md) — static checks, Pi loading, provider authentication, persistence, and dashboard flows.
417
+ - [Release process](docs/RELEASE.md) — release commands, trusted publishing setup, and failure recovery.
409
418
  - [Product requirements](PRD.md) — original product scope and design context.
410
419
  - [Progress log](PROGRESS.md) — implementation checkpoints and known environment notes.
411
420
  - [Exploration notes](docs/EXPLORATION.md) — Pi API and integration research.
package/VERIFY.md CHANGED
@@ -82,7 +82,8 @@ Inside pi:
82
82
  4. **enter** on an empty input = attach to the selected full session (confirms first if it's still running).
83
83
  You're now in the real Pi session; run `/agent-board` again to return.
84
84
  5. Other keys: **/** filter (`s:running`, or free text), **Ctrl+R** rename, **Ctrl+T** pin, **Ctrl+S** stop,
85
- **Ctrl+X** delete (archives the row, keeps the session file), **?** help, **Esc** clears input / quits when empty.
85
+ **x** then **y** delete (confirm prompt; archives the row, keeps the session file), **Ctrl+X** ×2 quick delete (no confirm),
86
+ **?** help, **Esc** clears input / quits when empty.
86
87
 
87
88
  ## 5. Inspect the durable store on disk
88
89
 
@@ -0,0 +1,148 @@
1
+ # Release process
2
+
3
+ Releases are driven by `scripts/release.mjs` and published by CI when a GitHub
4
+ Release is created. Four commands and one merge:
5
+
6
+ ```
7
+ precheck → prepare <bump> → open-pr <version> → merge (merge commit!)
8
+ → finish <version> → .github/workflows/publish.yml publishes to npm
9
+ ```
10
+
11
+ `main` is protected, so a release lands as a pull request — but the npm publish
12
+ is not tied to that PR: it is tied to the GitHub Release, which is created only
13
+ after the PR is merged and the tag is confirmed to be on `main`.
14
+
15
+ ## One-time setup: the npm trusted publisher
16
+
17
+ `publish.yml` publishes with **trusted publishing** (OIDC), so no `NPM_TOKEN`
18
+ secret is stored in the repository. npm has to be told which workflow may
19
+ publish the package — this is a one-time, per-package web step:
20
+
21
+ 1. Open <https://www.npmjs.com/package/@zhuxixi/pi-agent-board> and sign in as
22
+ the package owner.
23
+ 2. Go to **Settings** → **Trusted Publisher** → **GitHub Actions**.
24
+ 3. Fill in: organization `zhuxixi`, repository `pi-agent-board`, workflow
25
+ filename `publish.yml`, environment left empty.
26
+ 4. Save.
27
+
28
+ Until this is configured, the CI publish step fails with an authentication
29
+ error. Publishing locally (the pre-2026-09 flow, see "Local publish fallback"
30
+ below) still works, because the npm account keeps its normal credentials.
31
+
32
+ ## Commands
33
+
34
+ ### `node scripts/release.mjs precheck`
35
+
36
+ Read-only gate. Run it before anything else; it fails closed when the tree is
37
+ dirty, `main` is not in sync with `origin/main`, a `release/*` PR is still open,
38
+ the last tag is not an ancestor of `origin/main` (an orphan tag from a past
39
+ squash merge), or there is nothing functional to release.
40
+
41
+ ### `node scripts/release.mjs prepare <patch|minor|major|X.Y.Z> [--dry-run]`
42
+
43
+ Runs the gate, then:
44
+
45
+ 1. renders the new CHANGELOG section from the conventional commits since the
46
+ last `v*` tag,
47
+ 2. commits it as `docs(changelog): add <version> section`,
48
+ 3. runs `npm version <bump>` — one commit plus the annotated `v<version>` tag,
49
+ 4. switches to `release/v<version>`.
50
+
51
+ Nothing leaves the machine. `--dry-run` prints the section and the plan without
52
+ writing anything; review the preview before running it for real. Pick the bump
53
+ the same way as before: features are a `minor`, fixes alone are a `patch`.
54
+
55
+ ### `node scripts/release.mjs open-pr <version> [--dry-run]`
56
+
57
+ Pushes the release branch and the tag, then opens the PR with the CHANGELOG
58
+ section as its body. The tag is pushed separately because tags are not covered
59
+ by the branch protection rule.
60
+
61
+ ### Merge the PR with a merge commit
62
+
63
+ **Do not squash the release PR.** `npm version` created the tag on a commit that
64
+ only exists on the release branch; a merge commit brings that commit into
65
+ `main`, a squash does not, and the tag becomes unreachable from `main`
66
+ (`git describe` then misreads the changelog range of the next release). Use the
67
+ **Merge pull request** button, or `gh pr merge --merge`.
68
+
69
+ ### `node scripts/release.mjs finish <version> [--dry-run] [--sync-main]`
70
+
71
+ Run it after the merge. It fetches, verifies that `v<version>` is an ancestor of
72
+ `origin/main`, re-checks the CHANGELOG against any functional PR merged after the
73
+ bump, and then creates the GitHub Release with the CHANGELOG section as its
74
+ notes. That release is the publish trigger. `--sync-main` also resets a clean
75
+ local `main` to `origin/main`, dropping the local copy of the two release
76
+ commits (they are content-equal to what the merge brought in).
77
+
78
+ To publish without the orchestration, the underlying commands are
79
+ `gh release create v<version> --title v<version> --notes-file <notes>` where the
80
+ notes are `node scripts/release.mjs notes <version>`.
81
+
82
+ ## What CI does
83
+
84
+ `.github/workflows/publish.yml` runs on `release: published`, checks out the
85
+ tag, and:
86
+
87
+ 1. asserts npm is at least 11.5.1 (trusted publishing needs it; Node 24 ships
88
+ a newer one),
89
+ 2. asserts the tag equals `package.json`'s version,
90
+ 3. runs `npm run typecheck` and `npm test`,
91
+ 4. skips the publish when that version is already on npm, and otherwise
92
+ 5. runs `npm publish --provenance --access public`.
93
+
94
+ Provenance means npm records which repository, workflow, and commit built the
95
+ tarball, and shows it on the package page.
96
+
97
+ ## Recovering from a broken release
98
+
99
+ **Orphan tag** (`finish` refuses with "not an ancestor of origin/main"). The
100
+ release PR was squash-merged. Re-point the tag at the content-equivalent commit
101
+ on `main`, then retry:
102
+
103
+ ```bash
104
+ git fetch origin
105
+ git tag -f v0.9.0 <sha on origin/main with the same content>
106
+ git push origin v0.9.0 --force
107
+ node scripts/release.mjs finish 0.9.0 --sync-main
108
+ ```
109
+
110
+ **CHANGELOG drift** (`finish` refuses with missing PR numbers, or
111
+ `node scripts/release_helper.mjs verify` exits 1). A functional PR merged after
112
+ the changelog section was generated. Add the missing entries to the top section
113
+ in a `docs(changelog)` PR, merge it, then retry — the release notes are read
114
+ again at that point, so the fix is picked up.
115
+
116
+ **The publish failed** (npm outage, trusted publisher not configured yet).
117
+ Re-run the same tag without recreating the release: Actions → **Publish to
118
+ npm** → **Run workflow** → enter the tag.
119
+
120
+ **`npm view <package>` returns 404 right after a successful publish.** That is
121
+ the registry's view index lagging, not a failed publish. Compare endpoints: the
122
+ tarball, `/<package>/latest`, and `/-/package/<package>/dist-tags` answer
123
+ immediately while the root document 404s for a few minutes. Wait; re-publishing
124
+ the same version is rejected with a 403 and is never the fix.
125
+
126
+ **Local publish fallback.** When the trusted publisher is not configured and a
127
+ release cannot wait:
128
+
129
+ ```bash
130
+ npm run verify
131
+ npm publish --registry=https://registry.npmjs.org --access public
132
+ ```
133
+
134
+ The registry must be passed explicitly: this machine defaults to
135
+ `registry.npmmirror.com`. This path skips provenance and the CI checks, so
136
+ prefer fixing the CI path.
137
+
138
+ ## CHANGELOG conventions
139
+
140
+ `scripts/release_helper.mjs` renders the section from conventional commits in
141
+ the range `<last v* tag>..HEAD`. `feat`/`fix`/`perf` become Features / Fixes /
142
+ Performance, everything else (including `test` and `docs`) lands under Changes,
143
+ merges and `npm version` squash subjects are skipped, and `docs(changelog):`
144
+ subjects are ignored so changelog fixes do not feed back into the verify check.
145
+
146
+ Every entry carries the PR number parsed from the trailing `(#N)`. `verify`
147
+ fails when a functional commit's PR is missing from the top section — that is
148
+ what keeps a late merge from shipping without release notes.
@@ -0,0 +1,25 @@
1
+ # Plan: A5 mid-stream 断言窗口修正(issue #132)
2
+
3
+ Spec: `docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md`(commit 5b6ec00)
4
+ Base: main 1e832a1
5
+
6
+ ## Task 1: 断言修正 + stress 验证(A1, A2, A3, A4)
7
+
8
+ **Files:** `test/terminal-snapshot.integration.test.mjs`(唯一,A5 mid-stream test case 体内,不动 helper/其它用例)
9
+
10
+ **Changes:**
11
+ 1. **D1(A1)**: `:241` 附近——`seqsB` 构造改为窗口限定:`messagesB.filter((m) => m.type === "output" && m.seq >= endB.nextSeq).map((m) => m.seq)`;断言注释补一句为什么(订阅前 legacy 行 seq ≤ snapshotSeq < nextSeq,被过滤——引用 runner/pty-runner.mjs 广播分支行号)
12
+ 2. **D2(A2)**: `:227` 的 clientA waitFor 之后,加 `await waitFor(() => messagesB.filter((m) => m.type === "output" && m.seq >= endB.nextSeq).length >= ticks)`——显式等 socket B 追平再断言(沿用文件既有 waitFor 默认——15s,:29;A5-burst 的显式 30s 参数不在本 case)
13
+ 3. 断言本体不变(严格连续形态保留——窗口限定后就是纯协议流)
14
+ 4. **A3 stress 验证**:在 worktree 里跑 3 轮并行全量 `node --test test/*.test.mjs`(背靠背,高争用)——记录每轮 A5 mid-stream 结果;**若任何一轮 A5 mid-stream 仍失败且错误为 "strictly contiguous"** → 立即回炉报告,不许带病交付;账本 flake(subscribe_terminal / stop-finalize / A5-burst)照常隔离复跑不算回炉条件
15
+ 5. **A4**:全量其余断言绿
16
+
17
+ **Verify:** `node --test test/terminal-snapshot.integration.test.mjs`(定向)+ 3 轮并行全量 + `npm run typecheck`
18
+
19
+ **Commit:** `test(attach): scope A5 mid-stream seq assertion to post-snapshot window, wait for socket B catch-up explicitly (issue #132)`
20
+
21
+ ## Review 焦点预设
22
+
23
+ - 窗口下限为什么是 `endB.nextSeq` 而非 `beginB.snapshotSeq + 1`(二者相等,用 nextSeq 更贴近协议游标语义)
24
+ - D2 等待的 30s 天花板来源(文件 waitFor 默认),与 A5-burst 的显式 30s 参数区分
25
+ - stress 3 轮的统计学诚实度(概率验证非根除证明,报告如实记录)