Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added docs/media-evidence/pr02-now-playing-paused.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/media-evidence/pr02-now-playing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
93 changes: 93 additions & 0 deletions docs/media-evidence/pr02-visual.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Media PR 02 visual and live evidence

Date: 2026-07-25

Machine: Windows 11, 2560×1440 primary display, Spotify Premium

Artifact: `node cli/bin/weaver.js dev examples/now-playing`

The widget was allowed to settle on the real desktop layer. Other windows were
minimized with `Shell.Application.MinimizeAll()`, and the widget's physical
screen region was captured with `System.Drawing.Graphics.CopyFromScreen`.
Every accepted PNG below was reopened with the image-viewing tool at original
resolution.

The first correctly framed capture exposed a blocking defect: Spotify's valid
300×300 PNG decoded to 360,000 RGBA bytes, exceeding the widget profile's fixed
256 KiB image slot, and the art area was empty. The runtime log reported
`ImageTooLarge`. The host now aspect-fit normalizes oversized decoded artwork
to at most 256×256 before cache publication. The repaired cache object was
92,684 bytes, decoded to 256×256, and registered without an error. No failed
capture is accepted as evidence.

## Settled frame

Capture: `docs/media-evidence/pr02-now-playing.png`

| Element | Present | Positioned | Styled | Correct data | Result |
|---|---:|---:|---:|---:|---|
| Rounded translucent shell | yes | yes | yes | n/a | PASS |
| Purple activity dot and `NOW PLAYING` label | yes | yes | yes | yes | PASS |
| Playback status | yes | top-right | muted uppercase | `PAUSED` | PASS |
| Album art | yes | 72×72 left slot | cover crop, rounded mask | live Spotify art | PASS |
| Title | yes | right of art | white, largest text | `at thirst sight by Assia` | PASS |
| Artist | yes | below title | muted | `MIKE` | PASS |
| Source application | yes | below artist | dim muted | `Spotify.exe` | PASS |
| Segmented progress | yes | bottom row | purple filled / slate empty | live nonzero position | PASS |

Overall: **PASS**. The capture is nonempty, unobscured, fully framed, and the
host-provided image is visibly rendered.

## Track-change frame

Capture: `docs/media-evidence/pr02-now-playing-track-change.png`

A real `WM_APPCOMMAND/APPCOMMAND_MEDIA_NEXTTRACK` was delivered to Spotify.
The next changed media provider frame increased `mediaPipeFrames` from 2 to 3
and carried a newly published cache object after 1,798 ms wall time (including
Spotify's own transition latency). The cache object was 117,930 bytes and
decoded to 256×256.

| Element | Present | Positioned | Styled | Correct data | Result |
|---|---:|---:|---:|---:|---|
| Replacement album art | yes | same 72×72 slot | cover crop, rounded mask | visibly different live art | PASS |
| Replacement title | yes | unchanged title slot | white | `C.O.T.D` | PASS |
| Replacement artist | yes | unchanged artist slot | muted | `Coaltar Of The Deepers` | PASS |
| Playback status | yes | top-right | muted uppercase | `PLAYING` | PASS |
| Progress | yes | bottom row | purple/slate segments | reset near track start | PASS |

Overall: **PASS**. Art and metadata changed together on the next emitted media
frame; no intermediate frame pointed at a partial or missing cache file.

## Pause frame

Capture: `docs/media-evidence/pr02-now-playing-paused.png`

A real `WM_APPCOMMAND/APPCOMMAND_MEDIA_PLAY_PAUSE` was delivered to Spotify and
the provider was allowed two seconds to poll.

| Element | Present | Positioned | Styled | Correct data | Result |
|---|---:|---:|---:|---:|---|
| Current album art | yes | unchanged 72×72 slot | cover crop, rounded mask | retained `C.O.T.D` art | PASS |
| Current metadata | yes | unchanged metadata column | unchanged styling | retained live track | PASS |
| Playback status | yes | top-right | muted uppercase | changed to `PAUSED` | PASS |
| Progress | yes | bottom row | purple/slate segments | retained paused position | PASS |

Overall: **PASS**.

## Static-image idle A/B

The identical `examples/styling-images` source ran from ReleaseFast host and
runtime builds on the stack parent and layer-02 head. Each process settled for
at least 40 seconds before a 60-second `TotalProcessorTime` sample. No source
change occurred during either window.

| Head | Settle | Sample | CPU start→end | CPU delta | Private start→end |
|---|---:|---:|---:|---:|---:|
| `media/01-status-sourceapp@2feb700` | 41 s | 60,014.772 ms | 125.000→125.000 ms | 0.000 ms | 42.719→42.684 MiB |
| `media/02-album-art@8f66767` | 42 s | 60,013.804 ms | 109.375→109.375 ms | 0.000 ms | 42.758→41.570 MiB |

Claim: **no observed static-image idle CPU regression**. Both heads advanced
by zero Windows CPU accounting quanta over matched 60-second windows. The
layer-02 end-of-window private working allocation was 1.114 MiB lower in this
sample; this is recorded as an observation, not a generalized memory claim.
39 changes: 29 additions & 10 deletions docs/media-run-status.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ Last updated: 2026-07-25

| Layer | Branch | Parent | State |
|---|---|---|---|
| 01/05 | `media/01-status-sourceapp` | `master` (`b1199b5`) | DRAFT PR #32 (`ce7e60b`) |
| 02/05 | `media/02-album-art` | layer 01 | NOT STARTED |
| 03/05 | `media/03-transport` | layer 02 | NOT STARTED |
| 04/05 | `media/04-macos-adapter` | layer 03 | SPIKE-GATED / NOT STARTED |
| 05/05 | `media/05-noro-gate` | layer 04 if proven, otherwise layer 03 | NOT STARTED |
| 01/05 | `media/01-status-sourceapp` | `master` (`b1199b5`) | DRAFT PR #32 (`2feb700`) |
| 02/05 | `media/02-album-art` | layer 01 | DRAFT PR #33 (`2eecd49` plus adversarial repair pending push) |
| 03/05 | `media/03-transport` | layer 02 | DRAFT PR #34; restack pending |
| 04/05 | `media/04-macos-adapter` | layer 03 | DRAFT PR #36; restack pending |
| 05/05 | `media/05-noro-gate` | layer 04 | DRAFT PR #35; restack pending |

The Native SDK submodule remains at `3f6a68b606e110087b5992cbe75f700051f1b7f3`.
This run will not change the submodule pointer.
Expand All @@ -33,6 +33,26 @@ This run will not change the submodule pointer.
provider poll; live title, artist, source ID, and timeline rendered.
- Layer 01 visual gate: PASS. Viewed captures and per-element checklist are in
`docs/media-evidence/pr01-visual.md`.
- Layer 02 recovered-work verification: host `zig build test` and runtime
`zig build test -Dweb-layer=exclude -Dtrace=off`: PASS after the unattended
session was resumed.
- Layer 02 SDK/example gates: `npm test` PASS 62/62, `npm run typecheck` PASS,
`weaver check examples/now-playing` PASS.
- Layer 02 production builds: host `zig build` PASS; runtime
`zig build -Dweb-layer=exclude -Dtrace=off` PASS.
- Layer 02 live/visual gate: PASS after repairing the observed 300×300
`ImageTooLarge` failure with bounded host-side normalization. Viewed settled,
track-change, and pause captures plus per-element results are in
`docs/media-evidence/pr02-visual.md`.
- Layer 02 static-image ReleaseFast A/B: PASS. Parent and layer 02 each used
0.000 ms process CPU over matched ~60.014 s windows after 41–42 s settles.
Private memory ended at 42.684 MiB parent / 41.570 MiB layer 02. Raw values
and the bounded claim are recorded with the visual evidence.
- Adversarial finding F9 repaired at its owning layer: transient thumbnail
refresh, normalization, size, and cache-publication failures retain the
prior art path and cache pin. Only a successful replacement or a confirmed
no-art state changes the snapshot. The new retention/no-art test and
`host: zig build test` pass.

## Blockers and unverified gates

Expand All @@ -43,11 +63,10 @@ This run will not change the submodule pointer.

## Current work

Layer 01 is committed, pushed, and open as draft PR
`https://github.com/SunkenInTime/weaver/pull/32`.
The 15-finding adversarial remediation is in progress bottom-up. Layer 02's F9
repair is committed and published after its complete per-layer gate passed.

## Next executable task

Create `media/02-album-art` from layer 01 and trace SMTC thumbnail ownership,
host cache lifecycle, runtime path containment, and dynamic image
re-registration before editing.
Restack layer 03, then fix F1/F2/F3/F4/F10/F11/F12 at the
transport-owning layer.
27 changes: 16 additions & 11 deletions examples/now-playing/widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const segmentCount = 24;

export default widget({
name: "Now Playing",
size: [360, 142],
size: [440, 160],
anchor: { corner: "bottom-left", offset: [24, 24] },
layer: "desktop",
subscribe: ["media"],
Expand All @@ -13,8 +13,8 @@ export default widget({
const progress = media.durationMs > 0 ? Math.max(0, Math.min(1, media.positionMs / media.durationMs)) : 0;
const filled = Math.round(progress * segmentCount);
return (
<column class="w-[360px] h-[142px] p-4 gap-3 bg-[#10131c]/90 rounded-2xl">
<row class="w-[328px] justify-between items-center">
<column class="w-[440px] h-[160px] p-4 gap-3 bg-[#10131c]/90 rounded-2xl">
<row class="w-[408px] justify-between items-center">
<row class="gap-2 items-center">
<canvas
class="w-[14px] h-[14px]"
Expand All @@ -29,16 +29,21 @@ export default widget({
</row>
<text class="text-xs text-[#94a3b8]">{media.status.toUpperCase()}</text>
</row>
<column class="w-[328px] gap-1">
<text class="text-xl text-[#f8fafc] font-medium truncate">{media.title || "Nothing playing"}</text>
<text class="text-sm text-[#94a3b8] truncate">{media.artist || media.album || (media.title ? "Unknown artist" : "Open a media app to begin")}</text>
<text class="text-xs text-[#64748b] truncate">{media.sourceApp || "Unknown source"}</text>
</column>
<row class="w-[328px] h-[4px] gap-1">
<row class="w-[408px] h-[72px] gap-3 items-center">
{media.artPath
? <image src={media.artPath} fit="cover" class="w-[72px] h-[72px] rounded-xl" />
: <panel class="w-[72px] h-[72px] bg-[#273244] rounded-xl" />}
<column class="w-[324px] gap-1">
<text class="text-xl text-[#f8fafc] font-medium truncate">{media.title || "Nothing playing"}</text>
<text class="text-sm text-[#94a3b8] truncate">{media.artist || media.album || (media.title ? "Unknown artist" : "Open a media app to begin")}</text>
<text class="text-xs text-[#64748b] truncate">{media.sourceApp || "Unknown source"}</text>
</column>
</row>
<row class="w-[408px] h-[4px] gap-1">
{Array.from({ length: segmentCount }, (_, index) => (
index < filled
? <panel class="w-[9px] h-[4px] bg-[#8b5cf6] rounded-full" />
: <panel class="w-[9px] h-[4px] bg-[#273244] rounded-full" />
? <panel class="w-[13px] h-[4px] bg-[#8b5cf6] rounded-full" />
: <panel class="w-[13px] h-[4px] bg-[#273244] rounded-full" />
))}
</row>
</column>
Expand Down
2 changes: 2 additions & 0 deletions host/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ pub fn build(b: *std.Build) void {
exe.root_module.linkSystemLibrary("psapi", .{});
exe.root_module.linkSystemLibrary("ntdll", .{});
exe.root_module.linkSystemLibrary("ole32", .{});
exe.root_module.linkSystemLibrary("windowscodecs", .{});
exe.root_module.linkSystemLibrary("windowsapp", .{});
b.installArtifact(exe);

Expand All @@ -107,6 +108,7 @@ pub fn build(b: *std.Build) void {
tests.root_module.linkSystemLibrary("psapi", .{});
tests.root_module.linkSystemLibrary("ntdll", .{});
tests.root_module.linkSystemLibrary("ole32", .{});
tests.root_module.linkSystemLibrary("windowscodecs", .{});
tests.root_module.linkSystemLibrary("windowsapp", .{});
const test_step = b.step("test", "Run weaverd unit tests");
test_step.dependOn(&b.addRunArtifact(tests).step);
Expand Down
Loading
Loading