pi-crew 0.9.50 → 0.9.51

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,121 @@
3
3
  > **Note:** `atomic-write-v2.ts` / `AtomicWriter` mentioned in historical entries below was consolidated into `atomic-write.ts` as of v0.9.42. This changelog is preserved as historical record — the migration was completed (the v2 class was never adopted; v1 won on simplicity + symlink-safety + link+unlink atomicity). See `docs/migration/atomic-write-v2-migration.md` for the decision rationale.
4
4
 
5
5
 
6
+ ## [0.9.51] — Complete the UI animation audit + Windows coalescing fix (2026-07-26)
7
+
8
+ Closes out the remaining deferred findings from `reports/ui-animation-audit-2026-07-24.md`
9
+ (R1, R4, C4, C3, C9; C6 analyzed-then-closed-with-reasoning) and fixes the
10
+ underlying Windows event-log coalescing bug that a v0.9.49 test had been
11
+ papering over with a `process.platform === "win32"` skip. All 15 audit
12
+ findings are now resolved (11 applied in v0.9.50 + these + C6 closed).
13
+
14
+ ### Remaining audit findings applied
15
+
16
+ - **R1 — `render-scheduler.ts` fallbackLoop idle-stop** (`c46dc0e`): the
17
+ self-perpetuating `fallbackLoop` (`schedule()`→`lastEventAt`) never let the
18
+ UI idle, re-rendering ~every `fallbackMs` forever on a hung run. Now calls
19
+ a new `armDebouncedRender()` that does NOT touch `lastEventAt`; an
20
+ `idleFallbackRenders` counter caps consecutive idle catch-up renders
21
+ (default 8) then stops. `lastEventAt` is advanced only by real external
22
+ events, which reset the count + re-arm a stopped loop. **+3 tests** — one
23
+ asserts the scheduler STOPS rendering when idle, closing audit Root Gap #2
24
+ ("no test asserts the scheduler ever stops rendering when idle").
25
+ - **R4 — `render-scheduler.ts` flush re-entrancy backoff** (`c46dc0e`): the
26
+ cap-hit path (`iterations >= 5`) now calls
27
+ `armDebouncedRender(debounceMs * 2^exp)` with exponential backoff
28
+ (`CAP_BACKOFF_MAX_EXP=5`) instead of re-arming `schedule()`.
29
+ `consecutiveCapHits` resets on a clean flush. Stops the latent sustained
30
+ ~75ms render loop. +1 test.
31
+ - **C4 — `widget/index.ts` signature cache (invalidate-on-write)**
32
+ (`c46dc0e`): the prior blind-TTL cache (reverted in v0.9.50) caused stale
33
+ reads that skipped genuine re-renders. Now `cachedBuildSignature` is
34
+ invalidated in the widget's `RenderScheduler` `onInvalidate` callback (fires
35
+ on `run:state`/`worker:lifecycle`/`ui:invalidate`), plus in `invalidate()`
36
+ and on dispose. A 100ms TTL remains as a safety net only.
37
+ `notificationCount` is appended post-cache so notification changes still
38
+ re-render. +1 test asserting a genuine state change triggers re-render.
39
+ - **C9 — dead loaders removal** (`c46dc0e`): `CrewBorderedLoader` +
40
+ `CountdownTimer` were dead production code (referenced only in
41
+ `test/unit/loaders.test.ts`, never instantiated in `src/` or re-exported
42
+ from the public index). Removed both classes (`loaders.ts` → 6-line
43
+ documenting comment) + deleted the test file. **Preserved** the 2
44
+ `DynamicCrewBorder` tests (a LIVE class used by `mascot.ts`,
45
+ `settings-overlay.ts`, `run-dashboard.ts`) → moved to new
46
+ `test/unit/dynamic-border.test.ts`.
47
+ - **C3 — shared `RenderScheduler` fan-out dedup** (`3a50094`): the 3 overlays
48
+ (widget + sidebar + dashboard) each instantiated an identical
49
+ `RenderScheduler` (same run-event-bus channels, debounce 75ms, fallback
50
+ 750ms) → 9 subscriptions + 9 live timers + 3× per-event `schedule()` CPU
51
+ during active runs. Now ONE shared scheduler (module singleton in
52
+ `src/ui/shared-overlay-scheduler.ts`) fans out `render` + `onInvalidate` to
53
+ registered overlays, with per-callback try/catch isolation + ref-counted
54
+ disposal. Dedups 9→3 subscriptions, 9→3 timers, 3→1 `schedule()`/event,
55
+ and coalesces 3 independent debounce flushes → 1 batch per event burst.
56
+ +5 tests (fan-out render/invalidate, dispose ref-count, error isolation ×2).
57
+ - **C6 — mascot visibility gate: CLOSED, not wired** (`c46dc0e` gate +
58
+ `01ee10f` decision): the `setVisible()` gate was implemented + tested, but
59
+ after reading pi source (`earendil-works/pi@cee5ff75`) the wiring was
60
+ deliberately NOT done. `ctx.ui.custom()` does expose
61
+ `onHandle→OverlayHandle.isFocused()` (a usable "not obscured" proxy), so
62
+ wiring is feasible — BUT pi composites ALL non-hidden overlays
63
+ (`tui.ts:1044` `visibleEntries` → `component.render()` at `:1054`), so the
64
+ host STILL calls `mascot.render()` when obscured: C6 does not save the
65
+ render() work it appears to target, only cheap + coalesced + redundant
66
+ `requestRender()` pokes. Net benefit near zero. The correct fix is a
67
+ pi-core compositing optimization (skip `render()` for occluded overlays) —
68
+ upstream, not pi-crew. See `docs/decisions/2026-07-26-c6-mascot-visibility-not-wired.md`.
69
+
70
+ ### Windows event-log coalescing — attempted, REVERTED (remains unfixed)
71
+
72
+ An adaptive burst coalesce window (`NOTIFY_COALESCE_BURST_MS = 3000ms`) was
73
+ attempted to fix the Windows coalescing bug (the v0.9.49 `Rule 3` test had a
74
+ `process.platform === "win32"` skip hiding it). **Reverted** — verified on
75
+ Windows CI (run `30235357951`) that the 3000ms burst window is STILL shorter
76
+ than Windows's 3-5s completion spread, so coalescing still did not occur; AND
77
+ the 3000ms coalesce timer (not cancelled on `session_shutdown`) fired after
78
+ test temp-dir deletion → `ENOENT mkdir` unhandledRejection that failed the
79
+ whole `subagent-tools-integration` file on Windows. Restored the v0.9.50
80
+ behavior (800ms constant coalesce). The `win32` test skip is **retained** —
81
+ the underlying Windows coalescing bug remains unfixed (tracked from v0.9.49;
82
+ widening further would delay ALL notifications by 5s+ on every platform).
83
+ subagent-tools-integration 14/14 on Linux/macOS; both Rule 3 assertions
84
+ skipped on Windows.
85
+
86
+ ### distill-software: Transfer vs Audit mode (`418b9f2`, bonus)
87
+
88
+ From real distillation runs (audit-only distillation produced 0 visible
89
+ features for the target; artifact-level gates were ALL-GREEN while code had
90
+ runtime bugs needing multiple review rounds):
91
+ - **Phase 0 #7**: Transfer (default for "distill X to Y") vs Audit mode.
92
+ Transfer enumerates source CAPABILITIES (features not files) and applies
93
+ the gaps; deliverable = visible/behavioral new features. Audit enumerates
94
+ conventions; deliverable = invisible refactors. Decomposition follows mode.
95
+ - **Phase 1**: CAPABILITY DEPTH check before SKIP on PRESENCE=yes — surface
96
+ match can mask a capability gap.
97
+ - **Phase 3 APPLY-LOG**: 5 post-APPLY integration checks — WIRED, VISIBLE
98
+ DELTA, RUNTIME, API VERIFIED, RESOURCE LIFECYCLE.
99
+
100
+ ### Repo hygiene
101
+
102
+ - **`.claude/worktrees/*` orphan gitlinks removed** (`0e6cba5`): 7 gitlinks
103
+ (mode 160000, no `.gitmodules`) were stranded in the index from a prior
104
+ `.claude`-based tooling experiment, producing non-blocking `git exit code
105
+ 128` warnings. Removed from index + added `.claude/` to `.gitignore`
106
+ (`a0b2b1e`).
107
+
108
+ ### Verification
109
+
110
+ - `npm run test:critical` → 97/97 pass; render-scheduler 9/9, crew-widget
111
+ 6/6, mascot 7/7, dynamic-border 2/2, shared-overlay-scheduler 5/5,
112
+ subagent-tools-integration 14/14.
113
+ - `npm run typecheck` → exit 0; `npm run lint` → clean;
114
+ `npx biome format .` → clean.
115
+ - `npm run build:bundle` → 2690.6 KB, md5 `29e7a4d04ab8ad0542a3af3ef3d42426`.
116
+ - **real-test-pi-crew** all 8 tiers green (Tier 6 pty definitive — pi renders
117
+ full TUI, no crash; Tier 7 smoke team `team_20260727032433` fast-fix 3/3,
118
+ no hang).
119
+
120
+
6
121
  ## [0.9.50] — UI animation audit + distillation toolkit v2 (2026-07-25)
7
122
 
8
123
  Eliminates a class of timer-leak / flicker bugs surfaced by the UI animation
@@ -7150,7 +7150,7 @@
7150
7150
  "format": "esm"
7151
7151
  },
7152
7152
  "src/ui/render-scheduler.ts": {
7153
- "bytes": 7545,
7153
+ "bytes": 10286,
7154
7154
  "imports": [
7155
7155
  {
7156
7156
  "path": "src/utils/internal-error.ts",
@@ -7160,6 +7160,27 @@
7160
7160
  ],
7161
7161
  "format": "esm"
7162
7162
  },
7163
+ "src/ui/shared-overlay-scheduler.ts": {
7164
+ "bytes": 3382,
7165
+ "imports": [
7166
+ {
7167
+ "path": "src/utils/internal-error.ts",
7168
+ "kind": "import-statement",
7169
+ "original": "../utils/internal-error.ts"
7170
+ },
7171
+ {
7172
+ "path": "src/ui/render-scheduler.ts",
7173
+ "kind": "import-statement",
7174
+ "original": "./render-scheduler.ts"
7175
+ },
7176
+ {
7177
+ "path": "src/ui/run-event-bus.ts",
7178
+ "kind": "import-statement",
7179
+ "original": "./run-event-bus.ts"
7180
+ }
7181
+ ],
7182
+ "format": "esm"
7183
+ },
7163
7184
  "src/runtime/stale-reconciler.ts": {
7164
7185
  "bytes": 28257,
7165
7186
  "imports": [
@@ -7381,7 +7402,7 @@
7381
7402
  "format": "esm"
7382
7403
  },
7383
7404
  "src/ui/widget/index.ts": {
7384
- "bytes": 13630,
7405
+ "bytes": 15361,
7385
7406
  "imports": [
7386
7407
  {
7387
7408
  "path": "src/config/defaults.ts",
@@ -7399,14 +7420,9 @@
7399
7420
  "original": "../pi-ui-compat.ts"
7400
7421
  },
7401
7422
  {
7402
- "path": "src/ui/render-scheduler.ts",
7403
- "kind": "import-statement",
7404
- "original": "../render-scheduler.ts"
7405
- },
7406
- {
7407
- "path": "src/ui/run-event-bus.ts",
7423
+ "path": "src/ui/shared-overlay-scheduler.ts",
7408
7424
  "kind": "import-statement",
7409
- "original": "../run-event-bus.ts"
7425
+ "original": "../shared-overlay-scheduler.ts"
7410
7426
  },
7411
7427
  {
7412
7428
  "path": "src/ui/spinner.ts",
@@ -10724,7 +10740,7 @@
10724
10740
  "format": "esm"
10725
10741
  },
10726
10742
  "src/ui/run-dashboard.ts": {
10727
- "bytes": 39936,
10743
+ "bytes": 39738,
10728
10744
  "imports": [
10729
10745
  {
10730
10746
  "path": "node:fs",
@@ -10822,14 +10838,9 @@
10822
10838
  "original": "./overlays/help-overlay.ts"
10823
10839
  },
10824
10840
  {
10825
- "path": "src/ui/render-scheduler.ts",
10826
- "kind": "import-statement",
10827
- "original": "./render-scheduler.ts"
10828
- },
10829
- {
10830
- "path": "src/ui/run-event-bus.ts",
10841
+ "path": "src/ui/shared-overlay-scheduler.ts",
10831
10842
  "kind": "import-statement",
10832
- "original": "./run-event-bus.ts"
10843
+ "original": "./shared-overlay-scheduler.ts"
10833
10844
  },
10834
10845
  {
10835
10846
  "path": "src/ui/spinner.ts",
@@ -10970,7 +10981,7 @@
10970
10981
  "format": "esm"
10971
10982
  },
10972
10983
  "src/ui/mascot.ts": {
10973
- "bytes": 14829,
10984
+ "bytes": 15362,
10974
10985
  "imports": [
10975
10986
  {
10976
10987
  "path": "src/utils/visual.ts",
@@ -11672,7 +11683,7 @@
11672
11683
  "format": "esm"
11673
11684
  },
11674
11685
  "src/ui/live-run-sidebar.ts": {
11675
- "bytes": 11862,
11686
+ "bytes": 11692,
11676
11687
  "imports": [
11677
11688
  {
11678
11689
  "path": "node:fs",
@@ -11715,14 +11726,9 @@
11715
11726
  "original": "../utils/visual.ts"
11716
11727
  },
11717
11728
  {
11718
- "path": "src/ui/render-scheduler.ts",
11719
- "kind": "import-statement",
11720
- "original": "./render-scheduler.ts"
11721
- },
11722
- {
11723
- "path": "src/ui/run-event-bus.ts",
11729
+ "path": "src/ui/shared-overlay-scheduler.ts",
11724
11730
  "kind": "import-statement",
11725
- "original": "./run-event-bus.ts"
11731
+ "original": "./shared-overlay-scheduler.ts"
11726
11732
  },
11727
11733
  {
11728
11734
  "path": "src/ui/spinner.ts",
@@ -13126,7 +13132,7 @@
13126
13132
  "imports": [],
13127
13133
  "exports": [],
13128
13134
  "inputs": {},
13129
- "bytes": 6010444
13135
+ "bytes": 6020579
13130
13136
  },
13131
13137
  "dist/index.mjs": {
13132
13138
  "imports": [
@@ -15722,7 +15728,10 @@
15722
15728
  "bytesInOutput": 21485
15723
15729
  },
15724
15730
  "src/ui/render-scheduler.ts": {
15725
- "bytesInOutput": 6482
15731
+ "bytesInOutput": 7918
15732
+ },
15733
+ "src/ui/shared-overlay-scheduler.ts": {
15734
+ "bytesInOutput": 1681
15726
15735
  },
15727
15736
  "src/runtime/stale-reconciler.ts": {
15728
15737
  "bytesInOutput": 15684
@@ -15743,7 +15752,7 @@
15743
15752
  "bytesInOutput": 6263
15744
15753
  },
15745
15754
  "src/ui/widget/index.ts": {
15746
- "bytesInOutput": 9552
15755
+ "bytesInOutput": 10057
15747
15756
  },
15748
15757
  "src/ui/tool-progress-formatter.ts": {
15749
15758
  "bytesInOutput": 2682
@@ -16106,7 +16115,7 @@
16106
16115
  "bytesInOutput": 4154
16107
16116
  },
16108
16117
  "src/ui/run-dashboard.ts": {
16109
- "bytesInOutput": 30943
16118
+ "bytesInOutput": 30630
16110
16119
  },
16111
16120
  "src/ui/overlays/confirm-overlay.ts": {
16112
16121
  "bytesInOutput": 2237
@@ -16124,7 +16133,7 @@
16124
16133
  "bytesInOutput": 1913
16125
16134
  },
16126
16135
  "src/ui/mascot.ts": {
16127
- "bytesInOutput": 15930
16136
+ "bytesInOutput": 16274
16128
16137
  },
16129
16138
  "src/ui/settings-overlay.ts": {
16130
16139
  "bytesInOutput": 30796
@@ -16142,7 +16151,7 @@
16142
16151
  "bytesInOutput": 4563
16143
16152
  },
16144
16153
  "src/ui/live-run-sidebar.ts": {
16145
- "bytesInOutput": 9918
16154
+ "bytesInOutput": 9640
16146
16155
  },
16147
16156
  "src/extension/registration/ui.ts": {
16148
16157
  "bytesInOutput": 4277
@@ -16340,7 +16349,7 @@
16340
16349
  "bytesInOutput": 81
16341
16350
  }
16342
16351
  },
16343
- "bytes": 2751387
16352
+ "bytes": 2754801
16344
16353
  }
16345
16354
  }
16346
16355
  }