diff --git a/cli/test/example-surface-smoke.mjs b/cli/test/example-surface-smoke.mjs index 5ab9b068..e8e48ab7 100644 --- a/cli/test/example-surface-smoke.mjs +++ b/cli/test/example-surface-smoke.mjs @@ -11,6 +11,7 @@ const fixtures = [ "pomodoro", "system", "now-playing", + "noro-shell", "visualizer", "dpi-diagnostic", "m4b-parity", diff --git a/docs/media-evidence/pr05-adversarial-live.txt b/docs/media-evidence/pr05-adversarial-live.txt new file mode 100644 index 00000000..533424eb --- /dev/null +++ b/docs/media-evidence/pr05-adversarial-live.txt @@ -0,0 +1,23 @@ +2026-07-25 Windows adversarial live re-verification + +Artifact: + node cli/bin/weaver.js dev examples/noro-shell + +Artwork transition: + Before: YOU'RE MINE, YOU! with its live host-cached cover. + 250 ms: prior cover/title retained. + 750 ms: prior cover/title retained. + 1250 ms: prior cover/title retained. + 2000 ms: DEEP IN A DREAM with a visibly different live cover. + No bundled fallback, blank frame, or black image appeared in any viewed + capture. + +Exact timeout: + A temporary transport-only diagnostic widget started play() while weaverd + was suspended with the provider connection left open. + 1000 ms capture: yellow PENDING indicator. + 3300 ms capture: red REJECTED indicator. + Runtime log: + [2026-07-26T01:31:47.973Z] info: widget console: TIMEOUT_REJECTED_3003MS + weaverd was resumed immediately after capture. The diagnostic source was + not committed. diff --git a/docs/media-evidence/pr05-adversarial-recheck-0250ms.png b/docs/media-evidence/pr05-adversarial-recheck-0250ms.png new file mode 100644 index 00000000..8fc86671 Binary files /dev/null and b/docs/media-evidence/pr05-adversarial-recheck-0250ms.png differ diff --git a/docs/media-evidence/pr05-adversarial-recheck-0750ms.png b/docs/media-evidence/pr05-adversarial-recheck-0750ms.png new file mode 100644 index 00000000..8fc86671 Binary files /dev/null and b/docs/media-evidence/pr05-adversarial-recheck-0750ms.png differ diff --git a/docs/media-evidence/pr05-adversarial-recheck-1250ms.png b/docs/media-evidence/pr05-adversarial-recheck-1250ms.png new file mode 100644 index 00000000..8fc86671 Binary files /dev/null and b/docs/media-evidence/pr05-adversarial-recheck-1250ms.png differ diff --git a/docs/media-evidence/pr05-adversarial-recheck-2000ms.png b/docs/media-evidence/pr05-adversarial-recheck-2000ms.png new file mode 100644 index 00000000..41e5b0ff Binary files /dev/null and b/docs/media-evidence/pr05-adversarial-recheck-2000ms.png differ diff --git a/docs/media-evidence/pr05-adversarial-recheck-before.png b/docs/media-evidence/pr05-adversarial-recheck-before.png new file mode 100644 index 00000000..8fc86671 Binary files /dev/null and b/docs/media-evidence/pr05-adversarial-recheck-before.png differ diff --git a/docs/media-evidence/pr05-control-next.png b/docs/media-evidence/pr05-control-next.png new file mode 100644 index 00000000..6a5b1fd0 Binary files /dev/null and b/docs/media-evidence/pr05-control-next.png differ diff --git a/docs/media-evidence/pr05-control-pause.png b/docs/media-evidence/pr05-control-pause.png new file mode 100644 index 00000000..5eab03f3 Binary files /dev/null and b/docs/media-evidence/pr05-control-pause.png differ diff --git a/docs/media-evidence/pr05-control-play.png b/docs/media-evidence/pr05-control-play.png new file mode 100644 index 00000000..66ccc47a Binary files /dev/null and b/docs/media-evidence/pr05-control-play.png differ diff --git a/docs/media-evidence/pr05-control-previous-track.png b/docs/media-evidence/pr05-control-previous-track.png new file mode 100644 index 00000000..abf7cd32 Binary files /dev/null and b/docs/media-evidence/pr05-control-previous-track.png differ diff --git a/docs/media-evidence/pr05-noro-final-side-by-side.png b/docs/media-evidence/pr05-noro-final-side-by-side.png new file mode 100644 index 00000000..3bdd567c Binary files /dev/null and b/docs/media-evidence/pr05-noro-final-side-by-side.png differ diff --git a/docs/media-evidence/pr05-noro-live-side-by-side.png b/docs/media-evidence/pr05-noro-live-side-by-side.png new file mode 100644 index 00000000..5f061198 Binary files /dev/null and b/docs/media-evidence/pr05-noro-live-side-by-side.png differ diff --git a/docs/media-evidence/pr05-noro-live-widget.png b/docs/media-evidence/pr05-noro-live-widget.png new file mode 100644 index 00000000..172698f2 Binary files /dev/null and b/docs/media-evidence/pr05-noro-live-widget.png differ diff --git a/docs/media-evidence/pr05-noro-post-seek-widget.png b/docs/media-evidence/pr05-noro-post-seek-widget.png new file mode 100644 index 00000000..36f03f7d Binary files /dev/null and b/docs/media-evidence/pr05-noro-post-seek-widget.png differ diff --git a/docs/media-evidence/pr05-noro-pre-post.png b/docs/media-evidence/pr05-noro-pre-post.png new file mode 100644 index 00000000..34878870 Binary files /dev/null and b/docs/media-evidence/pr05-noro-pre-post.png differ diff --git a/docs/media-evidence/pr05-noro-pre-seek-widget.png b/docs/media-evidence/pr05-noro-pre-seek-widget.png new file mode 100644 index 00000000..6fac6ea6 Binary files /dev/null and b/docs/media-evidence/pr05-noro-pre-seek-widget.png differ diff --git a/docs/media-evidence/pr05-noro-pre-seek.png b/docs/media-evidence/pr05-noro-pre-seek.png new file mode 100644 index 00000000..36b19b52 Binary files /dev/null and b/docs/media-evidence/pr05-noro-pre-seek.png differ diff --git a/docs/media-evidence/pr05-rec-dot-paused.png b/docs/media-evidence/pr05-rec-dot-paused.png new file mode 100644 index 00000000..518d531e Binary files /dev/null and b/docs/media-evidence/pr05-rec-dot-paused.png differ diff --git a/docs/media-evidence/pr05-rec-dot-playing.png b/docs/media-evidence/pr05-rec-dot-playing.png new file mode 100644 index 00000000..03c6dc95 Binary files /dev/null and b/docs/media-evidence/pr05-rec-dot-playing.png differ diff --git a/docs/media-evidence/pr05-seek-after-75pct.png b/docs/media-evidence/pr05-seek-after-75pct.png new file mode 100644 index 00000000..758224b2 Binary files /dev/null and b/docs/media-evidence/pr05-seek-after-75pct.png differ diff --git a/docs/media-evidence/pr05-seek-before.png b/docs/media-evidence/pr05-seek-before.png new file mode 100644 index 00000000..759f5f61 Binary files /dev/null and b/docs/media-evidence/pr05-seek-before.png differ diff --git a/docs/media-evidence/pr05-session-atomic-0250ms.png b/docs/media-evidence/pr05-session-atomic-0250ms.png new file mode 100644 index 00000000..1b3cf6fc Binary files /dev/null and b/docs/media-evidence/pr05-session-atomic-0250ms.png differ diff --git a/docs/media-evidence/pr05-session-atomic-0750ms.png b/docs/media-evidence/pr05-session-atomic-0750ms.png new file mode 100644 index 00000000..1b3cf6fc Binary files /dev/null and b/docs/media-evidence/pr05-session-atomic-0750ms.png differ diff --git a/docs/media-evidence/pr05-session-atomic-1500ms.png b/docs/media-evidence/pr05-session-atomic-1500ms.png new file mode 100644 index 00000000..bc0b1e16 Binary files /dev/null and b/docs/media-evidence/pr05-session-atomic-1500ms.png differ diff --git a/docs/media-evidence/pr05-session-atomic-2500ms.png b/docs/media-evidence/pr05-session-atomic-2500ms.png new file mode 100644 index 00000000..bc0b1e16 Binary files /dev/null and b/docs/media-evidence/pr05-session-atomic-2500ms.png differ diff --git a/docs/media-evidence/pr05-session-atomic-before.png b/docs/media-evidence/pr05-session-atomic-before.png new file mode 100644 index 00000000..1b3cf6fc Binary files /dev/null and b/docs/media-evidence/pr05-session-atomic-before.png differ diff --git a/docs/media-evidence/pr05-timeout-pending-1000ms.png b/docs/media-evidence/pr05-timeout-pending-1000ms.png new file mode 100644 index 00000000..50a906b2 Binary files /dev/null and b/docs/media-evidence/pr05-timeout-pending-1000ms.png differ diff --git a/docs/media-evidence/pr05-timeout-rejected-3300ms.png b/docs/media-evidence/pr05-timeout-rejected-3300ms.png new file mode 100644 index 00000000..2ce860b2 Binary files /dev/null and b/docs/media-evidence/pr05-timeout-rejected-3300ms.png differ diff --git a/docs/media-evidence/pr05-visual.md b/docs/media-evidence/pr05-visual.md new file mode 100644 index 00000000..12086b73 --- /dev/null +++ b/docs/media-evidence/pr05-visual.md @@ -0,0 +1,155 @@ +# PR 05 visual and live evidence + +Date: 2026-07-25 +Machine: Windows 11, 2560x1440 desktop, Spotify and the installed Rainmeter +NoroPlayer skin + +## Capture method + +`node cli/bin/weaver.js dev examples/noro-shell` launched the real ReleaseFast +widget and host artifacts. After the widget settled, PowerShell minimized all +windows through `Shell.Application`, waited three seconds, and captured the +actual desktop-layer region with `System.Drawing.Graphics.CopyFromScreen`. +Windows were restored after each capture. Every PNG named below was opened and +viewed at native resolution; no black, empty, or occluded capture was accepted. + +## Viewed captures + +- `pr05-noro-pre-seek.png`: the byte-identical master noro source beside the + installed/running Rainmeter original before the seek-track change. +- `pr05-noro-pre-seek-widget.png`: exact 340x356 pre-change Weaver region. +- `pr05-noro-live-side-by-side.png`: live Weaver media v2 port beside the + installed Rainmeter original. +- `pr05-noro-live-widget.png`: exact 340x356 live Weaver region. +- `pr05-noro-final-side-by-side.png`: final paused, two-segment seek state + beside Rainmeter. +- `pr05-noro-post-seek-widget.png`: exact 340x356 final Weaver region with the + interactive seek track at the pre-change fill width. +- `pr05-noro-pre-post.png`: native-resolution pre-change and final captures + placed directly side by side. +- `pr05-control-play.png`, `pr05-control-pause.png`, + `pr05-control-next.png`, and `pr05-control-previous-track.png`: observed + transport results. +- `pr05-seek-before.png` and `pr05-seek-after-75pct.png`: temporary diagnostic + render showing the absolute seek calculation and observed result. + +Rainmeter was genuinely running from +`C:\Program Files\Rainmeter\Rainmeter.exe`, with the original NoroPlayer skin +at its configured desktop position. It was in its real `STANDBY / OPEN PLAYER` +state during the side-by-side; the capture does not claim Rainmeter was +connected to Spotify. + +## Per-element checklist + +| Element | Present | Positioned | Styled | Correct data | Result | +|---|---:|---:|---:|---:|---| +| 340x356 outer shell and 51px rounding | PASS | PASS | PASS | N/A | PASS | +| 188px artwork viewport and clipped top corners | PASS | PASS | PASS | PASS, Spotify art | PASS | +| Grid/grain artwork overlays | PASS | PASS | PASS | N/A | PASS | +| Red playing-status indicator | PASS | PASS | PASS | PASS, follows status | PASS | +| Elapsed/title/clock baseline | PASS | PASS | PASS | PASS, live provider/time | PASS | +| 312x3 seek strip | PASS | PASS | PASS | PASS, live position | PASS | +| 24px textured separator band | PASS | PASS | PASS | N/A | PASS | +| Three 100x100 transport buttons | PASS | PASS | PASS | PASS | PASS | +| Previous/play-pause/next icons | PASS | PASS | PASS | PASS, status-driven | PASS | +| Installed Rainmeter reference | PASS | PASS | PASS | PASS, real standby state | PASS | + +## Seek-track parity + +The pre-change source used a 26px white fill in a 312x3 track. For the final +capture, Spotify was paused and the interactive 24-segment track was clicked +at 2/24 duration, producing the same 26px white fill. In +`pr05-noro-pre-post.png`, the before and after tracks are visually +indistinguishable at native resolution: same x/y position, 312x3 bounds, +two-segment fill width, background, and edge treatment. Only the intentionally +live artwork, text, clock, and play state differ. + +## Live transport checklist + +| Action | Visible result | Capture | Result | +|---|---|---|---| +| Play | elapsed advanced and center icon changed to pause | `pr05-control-play.png` | PASS | +| Pause | elapsed stopped and center icon changed to play | `pr05-control-pause.png` | PASS | +| Next | art and title changed to `DANCE WITH THE MEMORY` | `pr05-control-next.png` | PASS | +| Previous | returned to `MIRROR` at `00:00` after Spotify's restart-current first press | `pr05-control-previous-track.png` | PASS | +| Seek | 75% of `04:16` is `03:12`; observed `03:12` | `pr05-seek-before.png`, `pr05-seek-after-75pct.png` | PASS (0s displayed error) | + +The temporary seek diagnostic changed only the center text to +`position/duration`; it was reverted before the final captures and commit. + +## Cross-session artwork atomicity recheck (2026-07-25) + +After the final layer-02 session-boundary repair was restacked, the real +layer-05 artifact was launched again against Spotify. Other windows were +minimized, the exact 340x358 desktop widget region was captured with +`CopyFromScreen`, a real global Next media key was delivered, and every PNG +below was opened and viewed at original resolution. + +| Capture | Artwork | Metadata | Result | +|---|---|---|---| +| `pr05-session-atomic-before.png` | pink mirrored cover | `FUCK THE SPEAKERZ` | PASS, complete old frame | +| `pr05-session-atomic-0250ms.png` | same pink cover | same old title | PASS, complete old frame retained | +| `pr05-session-atomic-0750ms.png` | same pink cover | same old title | PASS, complete old frame retained | +| `pr05-session-atomic-1500ms.png` | replacement monochrome cover | `BLIND (RAVE MIX)` | PASS, replacement published together | +| `pr05-session-atomic-2500ms.png` | replacement monochrome cover | `BLIND (RAVE MIX)` | PASS, settled replacement | + +Per-element result: shell, artwork viewport, overlays, title, clock, seek +strip, and transport controls were present, correctly positioned, and styled +in all five captures. The old title and cover remained paired while the +replacement thumbnail resolved; the replacement title and cover first +appeared together. No blank/fallback image, black frame, or old-cover/new-title +combination was visible. Overall: **PASS**. + +## Round-2 normalized-coordinate recheck (2026-07-25) + +The final shell was relaunched against the real Spotify session after replacing +the hardcoded `event.x / 312` calculation with the press event's normalized +`event.u`. The live widget was viewed before input at `03:24`; a press at 75% +of the 312 px seek strip advanced the visible position to `04:09` within +2.2 seconds. The observed window retained the same settled shell geometry and +live artwork as the accepted captures above; this handler-only change alters +no rendered node, dimension, class, or asset. Result: PASS. + +## Adversarial remediation live recheck + +The repaired layer-05 head was launched again after F3/F9. The exact 340×356 +desktop widget region was captured before and at 250, 750, 1250, and 2000 ms +after a real Next click. Every PNG was opened and viewed. + +| Element | Present | Positioned | Styled | Correct data | Result | +|---|---:|---:|---:|---:|---| +| Shell, artwork viewport, overlays | PASS | PASS | PASS | PASS | PASS | +| Prior cover during refresh | PASS | PASS | PASS | PASS at 250/750/1250 ms | PASS | +| Replacement cover | PASS | PASS | PASS | PASS at 2000 ms | PASS | +| Title transition | PASS | PASS | PASS | `YOU'RE MINE, YOU!` → `DEEP IN A DREAM` | PASS | +| Fallback/blank/black flash | N/A | N/A | N/A | Absent in every viewed frame | PASS | +| Transport buttons and seek strip | PASS | PASS | PASS | Live | PASS | + +Capture paths: + +- `pr05-adversarial-recheck-before.png` +- `pr05-adversarial-recheck-0250ms.png` +- `pr05-adversarial-recheck-0750ms.png` +- `pr05-adversarial-recheck-1250ms.png` +- `pr05-adversarial-recheck-2000ms.png` + +The exact deadline was exercised with a temporary transport-only diagnostic: +weaverd was suspended after keeping the provider connection open, then a real +command was sent. The viewed 1000 ms capture shows the yellow pending state; +the viewed 3300 ms capture shows red rejected. The runtime logged +`TIMEOUT_REJECTED_3003MS`. Captures are +`pr05-timeout-pending-1000ms.png` and +`pr05-timeout-rejected-3300ms.png`; raw notes are in +`pr05-adversarial-live.txt`. The diagnostic widget source is deliberately not +part of the PR. + +## REC-dot ruling addendum (2026-07-26) + +The original capture round recorded the red indicator as "follows status" +while the source rendered it unconditionally — an evidence overclaim caught +in Dara's eyes-on verification. Dara ruled the dot follows status like a REC +light (2026-07-26). `widget.tsx` now renders the red dot only when +`status === "playing"` (transparent placeholder preserves layout). Verified +live against Spotify and viewed: `pr05-rec-dot-playing.png` (dot present, +pause glyph) and `pr05-rec-dot-paused.png` (no dot, no placeholder artifact, +play glyph). diff --git a/docs/media-run-status.md b/docs/media-run-status.md index 43c47169..42934a20 100644 --- a/docs/media-run-status.md +++ b/docs/media-run-status.md @@ -9,8 +9,8 @@ Last updated: 2026-07-26 | 01/05 | `media/01-status-sourceapp` | `master` (`b1199b5`) | DRAFT PR #32 (`4dcf19c`) | | 02/05 | `media/02-album-art` | layer 01 | DRAFT PR #33 (`e8748fb`) | | 03/05 | `media/03-transport` | layer 02 | DRAFT PR #34 (`d3753b2`); restacked on the final layer-02 repair | -| 04/05 | `media/04-macos-adapter` | layer 03 | DRAFT PR #36; round-3 repair locally green | -| 05/05 | `media/05-noro-gate` | layer 04 | DRAFT PR #35 (`b6cc02a` before restack); Dara's REC-dot commit preserved | +| 04/05 | `media/04-macos-adapter` | layer 03 | DRAFT PR #36 (`4495be9`); round-3 repair pushed | +| 05/05 | `media/05-noro-gate` | layer 04 | DRAFT PR #35; round-3 implementation head `6f73027`, Dara's REC-dot commit preserved | The Native SDK submodule remains at `3f6a68b606e110087b5992cbe75f700051f1b7f3`. This run will not change the submodule pointer. @@ -111,7 +111,8 @@ This run will not change the submodule pointer. invalid/non-1× playback rate and 1 Hz advancement, plus a helper-owned verifier executable for delayed seek convergence, no session, callback timeout, and persistent out-of-tolerance observations. The Objective-C - helper build/test and hosted session remain CI-pending. + helper build/test and hosted session passed on the recorded round-2 heads; + the new round-3 recovery scenario is awaiting its own hosted result. - Layer 04 round-3 local gate: `npm test` PASS 63/63, `npm run typecheck` PASS, release audit PASS, all 18 examples passed `weaver check`, runtime build/test PASS (including the automation build), @@ -123,6 +124,107 @@ This run will not change the submodule pointer. runtime-detected macOS command-send failure is now process-fatal; the hosted automation test requires a new widget PID, a new randomized endpoint, resumed media frames, and a subsequently resolved transport command. +- Layer 05 adversarial Windows live art check: PASS. A real Next transition + retained the prior cover at 250/750/1250 ms and showed a visibly different + replacement cover/title at 2000 ms. All five exact-region PNGs were opened + and viewed; no bundled fallback, blank image, or black flash appeared. +- Layer 05 exact timeout live check: PASS. With the provider connection open + and weaverd deliberately suspended, the temporary transport-only diagnostic + was visibly pending at 1000 ms and rejected at 3300 ms. The runtime log + measured `TIMEOUT_REJECTED_3003MS`. weaverd was resumed and the diagnostic + was uninstalled/deleted. Evidence is in + `docs/media-evidence/pr05-adversarial-live.txt` and `pr05-visual.md`. +- Layer 05 final automated gate: `npm test` PASS 63/63, + `npm run typecheck` PASS, release audit PASS, all 18 widget examples passed + `weaver check`, runtime test/build PASS, Windows host test/build PASS, and + both macOS provider/host no-emit semantic compiles PASS. +- After the final layer-03 macOS test-harness repair and 04/05 restack, the + layer-05 head was rechecked locally: `npm test` PASS 63/63, + `npm run typecheck` PASS, all 18 widget examples passed `weaver check`, + runtime `zig build test -Dweb-layer=exclude -Dtrace=off` PASS, and host + `zig build test` PASS. +- A post-restack Greptile P1 correctly identified that macOS slot teardown + still joined an in-flight command worker on the supervision loop. Layer 04 + now transfers ownership to the stopped worker, which self-releases after + the helper's bounded timeout; final process shutdown tracks and drains all + workers within an explicit three-second fail-closed window. A deterministic + test holds a command for 300 ms and requires supervision-side teardown to + return in under 100 ms. Both macOS source files pass direct aarch64 semantic + compilation; runtime behavior remains attended-Mac unverified. +- The first layer-03 hosted-session run and its retry deterministically + exposed a time-only macOS startup crash: `subscribe: ["time"]` incorrectly + armed the host-provider drain, which reached an inert client whose clock had + not been initialized. Layer 03 now excludes runtime-native `time` from + host-backed subscription polling and initializes the inert client's clock. + The repaired hosted run passed that Clock startup gate. +- That same hosted run then exposed frame wakes bypassing the established + timer drain: all system frames arrived, but immediate one-at-a-time drains + broke the two-widget fan-out batch gate. Layer 03 now advances the reader + wake generation only for acknowledgements and acknowledgement-protocol + failures; metadata frames retain their 1 Hz/fast-audio timer drain. + Runtime tests and direct aarch64 semantic compilation pass. +- Layer 05 round-2 local gate: `npm test` PASS 63/63, + `npm run typecheck` PASS, all 18 examples passed `weaver check`, runtime and + Windows-host `zig build test` PASS, and both macOS no-emit semantic compiles + PASS. The Noro seek handler now uses normalized `event.u`; a viewed live + Spotify recheck moved the visible position from `03:24` to `04:09` within + 2.2 seconds after a 75% strip press, with unchanged shell rendering. +- The final layer-03 stalled-send test uses a real connected UDS plus an + injected retry seam in the production nonblocking send loop. It proves the + deadline, disconnect, and return to the app loop without depending on + kernel socket-buffer capacity. The final hosted run passes this test. +- The hosted fan-out gate now asserts semantic delivery: two running + per-widget endpoints, two subscribers, and bounded successful frame writes. + It does not mistake `SOCK_STREAM` read segmentation or log flush timing for + a protocol packet boundary. +- The first-frame watchdog is also enforced from the host's 1 Hz supervision + tick. Its atomic attempt state races the first valid frame against the exact + 10-second deadline; expiry kills the child, emits one canonical empty frame, + marks unavailable, and enters the existing bounded backoff. Hosted runs + `30189812372` and `30189813642` pass the full smoke. +- Greptile replies were posted for PR #35's normalized `event.u` fix and both + PR #36 worker-stall fixes; their commit references were updated after the + final restack. +- A final PR #33 re-review exposed one valid F9 edge: after an SMTC session + replacement, a failed first thumbnail refresh could pair the retained prior + cover with replacement metadata. Layer 02 now retains the cache snapshot and + pin but withholds the replacement frame until its artwork publishes or + no-art is confirmed. The focused regression test passes. A real Spotify Next + transition was captured and every exact-region PNG was viewed: the complete + old title/cover remained at 250 and 750 ms, and the replacement title/cover + appeared together at 1500 ms with no blank or mismatched frame. Evidence and + the per-element checklist are in `docs/media-evidence/pr05-visual.md`. +- The follow-up review correctly found that the initial atomic guard consumed + a transient first-refresh dirty event without retrying. While + `refresh_failed` remains active, the subscribed provider now retries on its + existing bounded 1 Hz poll; it creates no transport-only or idle timer. The + native retry truth-table regression test and every exact-head gate pass. +- The subsequent review correctly separated permanent failure from transient + retry. An oversized thumbnail resolves immediately as unavailable; other + unresolved refreshes retain the prior complete frame for three subscribed + polls, then publish refreshed metadata explicitly without art while + retaining the old cache path/pin. Later subscribed retries may still + recover and publish art. Tests prove the retry bound and the unavailable + frame state, so no permanent source can strand the prior media frame. +- Greptile's final PR #33 review is PASS on `a153e6c`. +- After that repair and the final restack, every changed layer was re-run + locally. Layer 02: `npm test` PASS 62/62; layers 03-05: PASS 63/63. Every + layer passed `npm run typecheck`, runtime and host `zig build test`, and all + 18 `weaver check` example gates. + +## Superseded round-2 GitHub Actions results + +All conclusions below were actual completed results for the named heads. They +do not project the still-running round-3 heads; a final table will replace +this one only after those runs complete. + +| PR | Head evidenced | Actions run | Jobs | +|---|---|---|---| +| #32 / 01 | `4dcf19c` | `30173553577` | PASS: gate, Intel headless, Apple-silicon headless, hosted Apple-silicon | +| #33 / 02 | `a153e6c` | `30193379848` | PASS: gate, Intel headless, Apple-silicon headless, hosted Apple-silicon | +| #34 / 03 | `ddfff61` | `30193380921` | PASS: gate, Intel headless, Apple-silicon headless, hosted Apple-silicon | +| #36 / 04 | `9c31229` | `30193380839` | PASS: gate, Intel headless, Apple-silicon headless, hosted Apple-silicon | +| #35 / 05 | `12d5040` | `30193381494` | PASS: gate, Intel headless, Apple-silicon headless, hosted Apple-silicon | ## Blockers and unverified gates @@ -146,45 +248,46 @@ This run will not change the submodule pointer. ## Current work The original 15-finding remediation remains implemented. Round 2 additionally -fixes the three partial/new P1s and four P2s through layer 04: +fixes the three partial/new P1s and four P2s through layer 05: | Finding | Owning layer | Accepted state | |---|---|---| | F1 | 03 | Fixed: both endpoints require the launched child PID; real hijack tests added. | | F2 | 03/04 | Fixed: all command, runtime, and adapter readers discard/count EOF residuals. | -| F3 | 03 | Fixed: subscription-only polling, reader wake, exact one-shot 3 s deadline. | +| F3 | 03 | Fixed: host-backed subscription-only polling (time excluded), reader wake, exact one-shot 3 s deadline. | | F4 | 03 | Fixed: nine-entry proven nack lane plus keyed four-pending ack slots and late-ack test. | | F5 | 04 | Fixed: helper/channel failures reject; only exit 2 OS decline resolves false. | -| F6 | 04 | Fixed: seek requires bounded read-back within ±2000 ms accounting for advance. | -| F7 | 04 | Fixed: blank title is a session, unknown is stopped, timestamped position advances at 1 Hz. | +| F6 | 04 | Fixed: seek polls to a two-second deadline and requires read-back within ±2000 ms at the reported rate. | +| F7 | 04 | Fixed: blank title is a session, unknown is stopped, and validated-rate position advances at 1 Hz. | | F8 | 04 | Fixed: decode/downsample/PNG/cache path; malformed art causes adapter loss. | -| F9 | 02 | Fixed: failed refresh retains the prior Windows art snapshot and pin. | -| F10 | 03 | Fixed: forced SDK mapping and alias/re-export symbol tracing with bypass tests. | -| F11 | 03/04 | Fixed: bounded endpoint writes and bounded TERM-to-KILL helper teardown. | -| F12 | 03 | Fixed: Windows media calls run on a bounded per-widget worker. | +| F9 | 02 | Fixed: transient failure retains the prior complete Windows frame and retries at the subscribed 1 Hz cadence; definite/exhausted failure publishes refreshed metadata artless without clearing the prior cache pin, so stale pairing and indefinite retention are both impossible. | +| F10 | 03 | Fixed: forced SDK mapping, binding/assignment tracing, and SDK-signature backstop with bypass tests. | +| F11 | 03/04 | Fixed: both UDS directions use bounded writes and helper teardown escalates TERM-to-KILL. | +| F12 | 03/04 | Fixed: platform calls run on bounded per-widget workers; macOS slot teardown never joins them on supervision. | | F13 | 04 | Fixed: restart reset requires a frame plus 30 s stable streaming. | | F14 | 04 | Fixed: ProcessInfo 15.4 runtime gate; exact-floor behavior remains unverified. | -| F15 | 04/05 | Static-audit and blocked-record addenda fixed; `media-v2-results.md` addendum waits for layer-05 restack. | +| F15 | 04/05 | Fixed: static audit, blocked record, spike row, results, and run status now tell the same dated story. | | Round-2 item | Owning layer | Accepted state | |---|---|---| -| macOS runtime send | 03 | Fixed: nonblocking one-second deadline; the existing send-error path unregisters the pending slot and rejects. | +| macOS runtime send | 03 | Fixed: nonblocking one-second deadline; the send-error path unregisters the pending slot and rejects. | | Windows shutdown race | 03 | Fixed: persistent manual-reset shutdown events, stopping checks before every read, and deterministic barrier tests in both readers. | | CLI binding bypasses | 03 | Fixed: binding/assignment tracing plus the resolved-signature declaration backstop and all four named bypass tests. | | fatal shared channel | 03/04 | Fixed: both hosts kill and crash-restart the slot rather than strand it. | -| first-frame watchdog | 04 | Fixed: 10-second silent-helper kill, one loss frame, bounded backoff; hosted execution pending. | -| seek convergence | 04 | Fixed: repeated observations through the two-second deadline with four verifier scenarios. | +| first-frame watchdog | 04 | Fixed: 10-second silent-helper kill, one loss frame, bounded backoff; hosted execution PASS. | +| seek convergence | 04 | Fixed: repeated observations through the two-second deadline with delayed/no-session/timeout/out-of-tolerance verifier tests. | | playback rate | 04 | Fixed: parsed/validated and used in timestamp, synthetic advancement, and seek verification. | +| Noro normalized seek | 05 | Fixed: `event.u` replaces the hardcoded width division; live Windows recheck passed. | | Round-3 item | Owning layer | Accepted state | |---|---|---| | post-frame idle work | 04 | Fixed locally: startup alone uses bounded watchdog polls; a proven stream blocks indefinitely on stdout/HUP. Hosted macOS result pending. | | runtime-detected send failure | 04 | Fixed locally: failure marks the channel fatal and wakes/exits the runtime through crash supervision. The hosted full PID/endpoint/frame/command recovery gate is pending. | -| stale result narrative | 05 | Pending layer-05 restack; the CI-pending statement will be replaced only with actual completed check results. | +| stale result narrative | 05 | Fixed: results now distinguish the completed round-2 hosted jobs from the not-yet-completed round-3 heads. | ## Next executable task -Commit/push layer 04, restack layer 05 without dropping Dara's `b6cc02a`, -then wait for and record every actual Actions result. In particular, hosted -macOS must compile/link the helper and pass the full runtime-fatal recovery -gate before this document calls that behavior remotely verified. +Commit/push the layer-05 restack, run every local head gate, then wait for and +record every actual Actions result. In particular, hosted macOS must +compile/link the helper and pass the full runtime-fatal recovery gate before +this document calls that behavior remotely verified. diff --git a/docs/media-v2-results.md b/docs/media-v2-results.md new file mode 100644 index 00000000..e4695f46 --- /dev/null +++ b/docs/media-v2-results.md @@ -0,0 +1,116 @@ +# Media v2 results + +Date: 2026-07-25 + +## 2026-07-26 round-3 addendum + +The Objective-C helper tests and hosted macOS session passed on the completed +round-2 heads recorded in `docs/media-run-status.md`; the earlier +“CI-pending” statement below is superseded. Round 3 removes a post-first-frame +100 ms adapter poll and makes a runtime-detected macOS command-send failure +process-fatal so host supervision replaces the widget and its authenticated +endpoint. The current round-3 macOS implementation is locally green; its new +hosted idle/recovery coverage has not yet completed and is not claimed here. +Real-player macOS behavior remains within the attended-only UNVERIFIED matrix. + +## 2026-07-25 round-2 addendum + +The second adversarial pass is implemented locally. The accepted state now +also includes a deadline-bounded macOS runtime send, race-free Windows reader +shutdown, binding- and signature-complete CLI capability detection, +crash-supervised recovery from a fatal shared provider channel, a 10-second +macOS first-frame watchdog, repeated seek read-back through the two-second +deadline, and validated playback-rate timeline math. The Noro seek handler now +uses `event.u`; a live Spotify recheck visibly advanced from `03:24` to `04:09` +after a 75% strip press without changing the rendered shell. + +The Objective-C helper build/tests and hosted macOS session subsequently +passed for the exact round-2 heads recorded in the run status. Those synthetic +checks do not verify real-player behavior for the watchdog, non-1× +advancement and seek, or fatal-channel restart; those remain **UNVERIFIED +(needs attended Mac)**, and the earlier spike still proves only the route. + +## 2026-07-25 adversarial-remediation addendum + +The historical macOS BLOCKED conclusion at the end of this file is +superseded. The standalone attended spike gate is **PASSED** per +`docs/media-evidence/pr04-mac-spike.md`. Layer 04 now implements the route and +passes the locally executable Windows, portable, release-audit, and macOS Zig +semantic gates. The implementation itself remains **UNVERIFIED (needs +attended Mac)**; the spike did not run Weaver's provider. Exact macOS 15.4 +floor behavior is separately **UNVERIFIED (needs attended Mac at 15.4)**. + +The accepted implementation state includes PID-bound endpoints, strict EOF +framing, idle-zero transport, exact command deadlines, structurally non-lossy +ack/nack lanes, truthful helper failure rejection, read-back-verified seek, +timestamped timeline advancement, normalized artwork, retained Windows art on +refresh failure, bounded platform waits/teardown, stable restart backoff, and +the runtime OS floor. The finding-by-finding evidence and remaining attended +matrix are in `docs/media-run-status.md`. + +## Windows acceptance result + +The Windows slice is complete through the noro gate: + +- `examples/noro-shell` observes real `media` and `time` provider frames. +- The 188px screen area uses host-cached artwork when available and retains + the bundled cover as a conditional fallback. +- Elapsed time, title, source status, and play/pause glyph are live. +- Existing previous, play/pause, and next buttons deliver real SMTC commands. +- The former static 312x3 progress stack is a pixel-matched click-to-seek + button using the press event's normalized local `event.u`. +- `skills/conjure-widget/SKILL.md` teaches `MediaData.artPath`, + `useMediaTransport`, the `media-transport` capability, and promise semantics. + +The full viewed visual checklist and capture inventory are in +`docs/media-evidence/pr05-visual.md`. + +## Live Windows result + +Spotify was used as the real player. Art and metadata were already visible on +the first settled provider frame. Pause/play visibly changed the glyph and +timeline, next changed art/title, and previous returned to the prior track. + +Seek was measured with a temporary diagnostic title. The track duration was +`04:16` (256 seconds); a click at 75% targeted 192 seconds and the next +provider frame displayed `03:12` (192 seconds). The diagnostic was reverted +before the final capture and commit. + +The installed Rainmeter original was captured beside Weaver. Rainmeter was in +its genuine standby state, while Weaver was connected to Spotify; the evidence +therefore supports shell geometry/styling parity and Weaver's live state, not a +claim that both processes rendered the same media frame. + +## Idle A/B + +This is a controlled source-only A/B on the same machine and same media-v2 +ReleaseFast runtime/host binaries. The baseline used +`examples/noro-shell/widget.tsx` byte-for-byte from `master`; the candidate +used the PR 05 source. Each process set settled for 40 seconds, then process +CPU, private bytes, and thread counts were sampled across approximately 60 +seconds. Spotify was paused and stable. + +| Metric | Master noro source | Media-v2 noro source | Difference | +|---|---:|---:|---:| +| Sample | 60.027s | 60.039s | +0.012s | +| Widget CPU | 0.000ms | 562.500ms | +562.500ms | +| Host CPU | 218.750ms | 359.375ms | +140.625ms | +| Combined CPU | 218.750ms | 921.875ms | +703.125ms | +| Combined CPU / one logical core | 0.364% | 1.535% | +1.171 percentage points | +| End private memory, widget + host | 41.81 MiB | 47.12 MiB | +5.31 MiB | +| End threads, widget + host | 8 | 17 | +9 | + +Performance claim: explicitly declined. This single matched run measures the +cost of activating the Windows media provider, dynamic artwork path, timeline +updates, and transport endpoint. It does not support a no-regression or broad +benchmark claim. It does show bounded steady-state behavior over the measured +minute: candidate private memory changed by about 0.03 MiB during the sample. + +## Historical macOS acceptance (superseded by the addendum above) + +The original Windows-only run recorded PR 04 as spike-gated and BLOCKED +because it could not produce a real macOS metadata frame plus delivered +command. That historical decision is retained for provenance only. The later +attended route evidence passes the spike, while Weaver's remediated +implementation and the macOS noro side-by-side remain unverified as stated in +the dated addendum. diff --git a/examples/noro-shell/widget.tsx b/examples/noro-shell/widget.tsx index 70d44315..8d3ef7bb 100644 --- a/examples/noro-shell/widget.tsx +++ b/examples/noro-shell/widget.tsx @@ -1,34 +1,68 @@ -import { useState, widget } from "@weaver/sdk"; +import { useMediaTransport, useProvider, widget } from "@weaver/sdk"; -// Pixel-faithful static replica of noro-player (SunkenInTime/noro-player). +// Pixel-faithful live port of noro-player (SunkenInTime/noro-player). // Every dimension and color comes from the skin's Variables.inc. -const noop = () => {}; +const progressSegments = 24; + +function elapsed(ms: number): string { + const totalSeconds = Math.max(0, Math.floor(ms / 1000)); + return `${String(Math.floor(totalSeconds / 60)).padStart(2, "0")}:${String(totalSeconds % 60).padStart(2, "0")}`; +} + +function clock(hourText: string, minuteText: string): string { + const hour = Number(hourText); + return `${String(hour % 12 || 12).padStart(2, "0")}:${minuteText} ${hour >= 12 ? "PM" : "AM"}`; +} export default widget({ name: "Noro Shell", size: [340, 356], anchor: { corner: "top-right", offset: [420, 32] }, + subscribe: ["time", "media"], + capabilities: ["media-transport"], }, () => { - const [playing, setPlaying] = useState(true); + const time = useProvider("time"); + const media = useProvider("media"); + const transport = useMediaTransport(); + const playing = media.status === "playing"; + const progress = media.durationMs > 0 ? Math.max(0, Math.min(1, media.positionMs / media.durationMs)) : 0; + const filledSegments = Math.round(progress * progressSegments); return ( - + {media.artPath + ? + : } - + {playing + ? + : } - 00:06 - LET IT GO - 03:58 AM + {elapsed(media.positionMs)} + {media.title ? media.title.toUpperCase() : "OPEN PLAYER"} + {clock(time.hh, time.mm)} - - - + @@ -38,13 +72,13 @@ export default widget({