40 KiB
P8 Daemon (lgs-shell): evidence log
Status
Maintenance sessions 5-6 (2026-10-07 16:07-16:25 EDT, platform maintainer; build 11094443): REQ C2a->P8 #17 confirmed fixed and closed; no open REQs to P1-P6, P8, P9. Session 5 (16:07-16:12) ran two more native sessions with C2a's flags (wp.c2a,wp.p3,wp.c1a, --mode pad, /library/home, card open): every snapshot showed main shapes: [], 18 plates and the home-card slab in glassd.json, cover: 0 and nothing dropped in glassd-out.json, and P7's dump had the cover node, 8 base pieces and the card's pop. hv --look --full --grabs 3 (fresh frames, the window brought into view with a lab-only P7 override, then cleared) showed glass discs with their art, labels, dots, the segmented control, search, sidebar, Back and the card at +15 mm, with no grey slab. Every frame was looked at, then deleted on both machines. The answers are in C2a.md (#17) and C2a-review-R2.md (REQ C2a-R2->P8), both [x]. Session 6 (16:22) found no new native session was needed. Since 16:05 the only changed source is P10's lab/lab_gates.js, and nothing in the daemon -> reporter -> glassd -> scene graph chain has changed. The open REQ grep across wp/*.md found none addressed to P1-P6, P8 or P9. Device as found: unit active, CSS only, flags {}, SteamVR pages ok, not paused. Still open for others: REQ P8->P7 (off-axis order) and REQ P8->V1.
Maintenance session 4 (2026-10-07 12:55-14:25 EDT, platform maintainer; build 11094443): three REQs to P8 done. On the Frame: device/lgs_shell.py sha1 02473552…, device/lgs_vr.py 77f1e9f5…, device/lgs_layers.js (P6) ca65765f… = this tree (13:42, frame_ssh put + flock /tmp/lgs/sync.lock and an atomic rename); the unit runs them, CSS only, since 14:19.
- REQ C2a->P8 #17 (native Home a grey slab): not reproducible with today's tree; two defects that draw such a slab fixed. Six native sessions (13:05-14:04) with C2a's flags show the planned report end to end (glassd.json main
shapes: []+ 18 plates + the card slab; glassd-outcover: 0,plates18; P7's dump: the cover node and base pieces only inside C2a's 4 bands) and, in headset looks, native Home right frontal and at −20° (glass discs with their art and a bezel, labels, dots, top-row plates, the card at +15 mm). C2a's frames were single, probably stale grabs (before P10's 10:59 fresh-frame fix). Fixed: (1) P6's reporter sent explicit mosaic bands next to a cover (seen live 13:07:24 in another session: main with a 1080 cover + Home's 18 plates), now never (RP-MO,wp/P6.md), and the daemon drops bands next to a cover too (build_spec, contract §9); (2) main is hidden while the frame shows SteamVR's page (next item). Open, P7's: off axis (+20°) the base pieces beside a pop lose their content behind the cover's plates (REQ P8->P7 inwp/P7.md). - REQ C6b->P8 (page script not installed after a flag flip): the lab's
--theme off/--theme onon avr:page (alsogates/auditstock snapshots) left/tmp/lgs/vr-theme-paused, so the daemon's page theming stayed paused (no page scripts, no theming of new pages) until the next start.lgs_vr.apply_once()now ends the pause, the daemon drops a pause older than 300 s (VR_PAUSE_MAX_S), and each page-script pass reads the flags files itself. Live: install at the step's first sample; avr:gates run pauses and resumes in 2 s. - REQ C6b->P8 (main's cover over SteamVR's settings page):
page_away(): whilepage.steamis false, main draws nothing in glassd.json (quad still masked) and is left out of the spec; plates and slabs materialize again on return;status.mainAway. Live 13:45-13:46. - Tests: offline
offline_s4.py27/27 (scratchpad); selftests on the Framedm54/4,dm218/18,plates13/13 (14:12-14:19); the live rows under Evidence, session 4. - Open, for others: REQ P8->P10 (below), REQ P8->P7 (
wp/P7.md), REQ P8->V1 (as before).
Round R1 review fixes: done (session 3, 2026-10-07 08:10-09:35; review wp/P8-review-R1.md). Every major finding (M1 binding globals, M2 glassd while dormant, M3 --stay) and every minor one (m1-m8) is fixed and re-tested; two new REQ P7->P8 (Steam's page by summon key; the daemon's own freeze, SG-6) and REQ P9->P8 (hole.edges) are done and answered. See Evidence, round R1 (finding-by-finding table and live runs). M1, M3 reached; card complete.
- On the Frame:
device/lgs_shell.pysha12f3bdbf2…anddevice/shell_ext/__init__.py5409a7ab…= this tree (09:33); the unit runs CSS only (python3 -B), not dormant, theme on. - Live (08:32-09:32), all PASS:
actions38/38,dm315/15,dm412/12 (a plugin file loaded live from RAM),dm5 --stop7/7,dm6 --live3/3,grace8/8,grace --native18/18 (glassd stopped while dormant),dm117/17,dm218/18,plates13/13, newfreeze12/12 (real glassd--no-feed: nodes back 0.37 s, no false "hung" restart); idle CSS only 0.45 % of a core with 0 new sockets/min (review: 0.9 %, ≈ 81/min); concurrentstart→started+running. Offline:p8r1_offline.py40/40, session-2 harnesses 39/39 and 11/11, the reviewer'sp8rev_stay.pynow stops in both cases. - Open, for others: REQ P8->V1 (NATIVE.md, LAB.md texts), REQ P8->P10 (
glass.pyrunspython3without-B, so lab commands still writedevice/__pycache__). Not re-run on purpose: the reviewer'slgs offsweep (it would clear other agents'/tmp/lgs/flags.json); the daemon's part of it is covered bydm5 --stopanddm1. - If a later session continues: nothing is pending in P8; re-run
selftest freeze,grace --nativeanddm1after any change to the loops (each holdsnative.lock; expect waits of up to 10 min for it).
M1 reached (2026-10-07 04:10): contract docs/phase2/contracts/daemon.md written; device/shell_ext/__init__.py (action registry) loads; the daemon declares NATIVE_MODES for P1.
M3 reached (2026-10-07 06:45) and complete (07:00): every build of the card is in and every acceptance test passes on the Frame: DM-1 (14/14), DM-2 (15/15), DM-3, DM-4, DM-5, DM-6, plus the integration check with P6's reporter v3 (selftest plates, 12/12: a plate from a live element reaches glassd, is acked with data-lgs-plate-ack, fades out; motion.js prepended; nothing native left after). The daemon on the Frame runs this tree in CSS-only mode.
Session 2 (06:10-06:45, after the usage-limit cut-off) added:
- Theme-off grace: a theme off without
lgs off(lab--stock, P1's selftests) made the daemon exit after 3 s and nobody restarted it (found it dead at 06:05). Now it goes dormant up toshellThemeGraceS(default 600 s; 120 s until 06:52, raised because other agents' tests keep the theme off for minutes) and resumes in the same process; exits at once after a Steam restart, after 120 s without Steam (selftest grace8/8). - REQ P4->P8 done:
data:fonts become binaryFontFaces in SteamVR pages (live:LGS Interloaded in all 3 pages, same metrics as Steam). - glassd v3 completed:
roomDim,dimslabs (+fill, 514 ms out-ramp),dimplates andnoneslabs only with their caps;dimslabs go to the spec asdimSlabs(not popped). - Robustness: a single Steam evaluation that times out (Steam busy) no longer drops the connection (that took the native layer down 2-4 s); reconnect after 0.5 s; freeze recovery now 0.4 s. Spec log lines say why a spec is empty (
[theme off],[glassd not running]). Leaked devtools sockets closed at exit.
Complete (2026-10-07 07:00): nothing left in the card. Also verified: interactivePops switches the profile live (lab --flags), the binary font leaves with the page theme, the steam-loop lifecycle offline (11/11). For later rounds: REQ P8->V1 (NATIVE.md sentence on theme off); a real Steam-restart exit can only be tested offline (never-list); the daemon on the Frame runs this tree, CSS only, shellThemeGraceS default 600 s.
Requests
-
REQ P8->V1:
docs/NATIVE.md(yours, PLAN §2.6), section Daemon ("It stops on SIGTERM …, when the Steam theme is seen off on 3 polls in a row …"): since 2026-10-07 06:15 the daemon goes dormant on a theme off that did not come fromlgs off(lab--stock, P1's selftests) and resumes when the theme is back; it exits aftershellThemeGraceS(default 600 s), at once after a Steam restart, or after 120 s without Steam (contracts/daemon.md§1). Please point that sentence at the contract when you do the §6 NATIVE.md update. Also there: "After a freeze the daemon rebuilds everything within about 1 s" is now 0.4 s (DM-1, 06:33); line ~312 "--stay… the daemon goes dormant … instead of exiting": since R1 (08:30)--stayis bounded (dormant ≤ 2400 s, native ≤ 2400 s after start) and a Steam restart or 120 s without Steam end it too. Anddocs/LAB.mdline ~115 ("For users,lgs onstarts the transient watcher unitlgs-vr… It exits when the Steam side is turned off"): the watcher is now thelgs-shellunit (P8);lgs offstops it at once, a theme off withoutlgs offmakes it dormant (same contract §1). (Review R1 m6.) -
REQ P8->P10:
glass.pyline 78,PY = "env -u LD_LIBRARY_PATH -u LD_PRELOAD /usr/bin/python3": please add-B(orPYTHONDONTWRITEBYTECODE=1in theenv). Lab commands importlgsandlgs_shelland so writedevice/__pycache__/*.pyc(andlab/__pycache__/) on the Frame, outside/tmp/lgsand/dev/shm/lgsand surviving a reboot (hard rule 7; review R1 m1). The daemon itself no longer writes any (python3 -B,sys.dont_write_bytecodebefore its imports, checked 2026-10-07 08:31:device/__pycache__deleted, a freshlgs_shell.cpython-312.pycreappears only after lab commands); P1'sdevice/lgswrapper already setsPYTHONDONTWRITEBYTECODE=1.- P10 (2026-10-07 session 4): done, both ways:
glass.pyruns every Frame command asenv -u LD_LIBRARY_PATH -u LD_PRELOAD PYTHONDONTWRITEBYTECODE=1 /usr/bin/python3 -B, andlab/lab.pysetssys.dont_write_bytecode = Truebefore its imports (so a script that starts lab.py itself caches nothing either). The olddevice/__pycache__andlab/__pycache__were deleted on the Frame (10:53);hv,jsandgatesruns since wrote none. Copies that came back at 10:57:47 (lgs,lgs_vr,lab) come from P7'snative/spike/sg_test.py, which imports them under a plainpython3: REQ P10->P7. contracts/lab.md §10.
- P10 (2026-10-07 session 4): done, both ways:
-
REQ P8->P10:
lab/lab.pyshot(and every step that callsset_theme("off", "vr:…")without a matchingset_theme("on", …)) leaves SteamVR's pages stripped after the step:restore_on = theme == "off" and not surface.startswith("vr:")gives the theme back only for Steam surfaces. Please give avr:page's theme back at the lock exit too (lgs_vr.apply_once(), which since 2026-10-07 session 4 also ends the daemon's page-theming pause). Why: until then the daemon leaves every SteamVR page alone, so no page script (C5b's Now Playing, C6b's settings) is injected and pages that appear are not themed; since session 4 the daemon ends a pause older than 300 s by itself, but a 5-minute gap after every before-shot is still a device not left as found (REQ C6b->P8;contracts/daemon.md§6).- P10 (2026-10-07 session 5): done.
set_theme("off", "vr:…")now remembers the stripped page, and every lock exit (lab.Lock.__exit__, before the step options are undone) callslgs_vr.apply_once()for it while the Steam theme is on (if the Steam theme itself is off, the pages stay stripped and stderr says so). A step that themes the page again itself (gates --theme off,audit) clears the mark. Live (python glass.py shot vr:systemui _p10_s5_p8 --theme off, 15:55:30): stderr "(SteamVR pages themed again at the lock exit)"; your journal "15:55:29 vr: page theming paused (lab --theme off on a vr: page)" then "15:55:30 vr: page theming resumed (paused 3 s)";shell statusright after:steamvrPages.pausedfalse, keyboard / controllerbindingui / systemuiok. The capture was deleted. contracts/lab.md §4 (intro) and §10.
- P10 (2026-10-07 session 5): done.
Evidence
Steam build 11094443 (PLAN §7) for every live row. Commands run from glass-shell/ on the PC; FR = python C:/Users/blcha/.claude/skills/steam-frame-ssh/scripts/frame_ssh.py, D = ~/.local/share/glass-shell/device.
| Test | Date | Command | Result | Artifacts |
|---|---|---|---|---|
| shell_ext offline self-test | 2026-10-07 04:05 | python -c "import shell_ext; shell_ext.selftest(tmp)" in device/ (PC) and lgs_shell.py selftest actions (Frame, 04:24) |
34 / 34 PASS on both (validator, sources, wrong context, rate and global limit, dry run by flag and by the runtime action logger, flag gate, plugin error, timeout, reload, unload) | — |
| Offline daemon harness | 2026-10-07 04:20, 06:30 | scratch p8_offline.py (glassd_config, fades, spec top, native_state; session 2: dim slabs and plates, roomDim, dimSlabs in the spec, grace flag, dormant status) |
39 / 39 PASS (25 at 04:20): v3 fields held back without caps (plates fall back to cover shapes, none slabs dropped), passed with caps; unknown plate keys dropped; new ids materialize; leaving slab/plate kept at phase 0 for 350 ms then removed; a returning id cancels the fade; no fade on a hidden surface; windowless surface gets lgs-native from an acked plate; a thick plate is acked only after its ramp |
— |
| DM-3 geometry | 2026-10-07 04:24 | FR run "cd D && python3 lgs_shell.py selftest dm3" |
PASS 15 / 15: S, H0, r, Hm, unitM, mmPerCssPx, dashDist equal the SP §1.2 snippet run in the same step (r = 1.0, Hm 0.5535 m); frame menu fWidth 0.124 / fHeight 1.1275 = DumpLaserOverlays; clipCssH 491.0, mpMm 0.847 (WN §3.3.2's 0.847 at r = 1); page valve.steam.gamepadui.main, steam true; P1's bridge holds the same geom and page; daemon heartbeat fresh |
— |
| DM-4 actions | 2026-10-07 04:24 | … selftest dm4 (calls window.lgsAction in SharedJSContext) |
PASS 8 / 8: echo answered ok; unknown type → unknown-type; echo.main from src bar → bad-source; 300-char text → bad-args; second echo within 0.2 s → rate; every rejection in shell.json actions.rejected and the journal; the bridge reply holds the last reply |
— |
| DM-5 page script | 2026-10-07 04:25 | … selftest dm5 --stop (holds lab-vr.lock, native.lock for the stop part) |
PASS 6 / 6: /tmp/lgs/vr-scripts/systemui.p8echo.js injected into vr:systemui only (not controllerbindingui), listed in status, removed when its file goes, re-injected, removed on daemon stop; unit restarted CSS only |
— |
| DM-6 native=auto | 2026-10-07 04:25 | … selftest dm6 --live (holds native.lock, lab-vr.lock: stop, start(native='auto')) |
PASS 3 / 3: status.mode = css-only (native=auto: the native gate has not passed (device/defaults.json has no native: on)), native.requested auto, source builtin; a second start('auto') keeps the running unit |
— |
| Steam loop lifecycle, offline | 2026-10-07 06:58 | scratch p8_steamloop.py (the real Shell.steam_loop against a fake devtools socket) |
PASS 11 / 11: theme off → dormant, no stop, scene graph not wanted; theme back → resumed in the same loop; grace over → exit; a new SharedJSContext target with the theme off → exit at once (no grace), logged; a new target with the theme on → keeps running, restart flag cleared; two evaluation timeouts in a row keep the socket (logged once, slowEvals 1), three drop and reconnect it; Steam unreachable for STEAM_GONE_S → exit. A real Steam restart is never done on the device (never-list) |
— |
interactivePops profile flag |
2026-10-07 06:44 | python glass.py js --flags interactivePops "…bridge.get('daemon').profile…" (P10 writes /tmp/lgs/flags.json for the step) |
PASS: bridge daemon.profile = wearer inside the step (heartbeat 175 ms old); journal flags: profile default -> wearer 06:44:48 and wearer -> default 06:44:51 when the step's file was restored |
— |
| Fonts removed with the page theme (G-REMOVE, SteamVR pages) | 2026-10-07 06:55 | scratch p8_fontremove.py on the Frame under lab-vr.lock: __LGS_VR.disable() on vr:keyboard, then wait for the daemon |
PASS: before LGS Inter loaded; after disable() no LGS Inter face, no #lgs-theme, no lgs-on; the daemon re-applied the theme and the face 0.5 s later |
— |
| DM-1 NAT regressions | 2026-10-07 06:33 | FR run "cd D && python3 lgs_shell.py selftest dm1" (holds native.lock, lab-vr.lock; fakeglassd, --test-report, --stay) |
PASS 14 / 14: native with fakeglassd, lgs-native on main, scene graph built; freeze (SIGSTOP 15 s): lgs-native off at 2.6 s, nodes off at 11.8 s (watchdog 12 s), both back 0.4 s after SIGCONT (NAT: about 1 s); teardown order plugins → scene graph → lgs-native + reporter → glassd → exit, nothing left (no lgs-native, no nodes, no glassd files); backoff 2, 4, 8, 16 s then gives up after 5 crashes in 120 s (css-only (glassd gave up: 5 exits in 120 s)), everything native gone; back to CSS only. Earlier runs 06:21 and 06:27 failed only "both back within 3 s" (3.9 s; 2.5 s): an evaluation that timed out right after the freeze dropped the Steam socket and the native layer for 2 s. Fixed (timeouts tolerated, 0.5 s reconnect) |
— |
Reporter v3 + plates + acks (selftest plates) |
2026-10-07 06:44 | FR run "cd D && python3 lgs_shell.py selftest plates" (holds native.lock, lab.lock, lab-vr.lock; fakeglassd, the real reporter, a #lgs-p8-fix element with data-lgs-plate="liquid" in the main window, removed after) |
PASS 12 / 12: reporter v 3 reporting, with fragments 00-base, 41-home, 99-legacy, profile default, geom S 0.369 (ours, via the bridge), ackMode; __LGS_MOTION in vr:systemui (motion.js prepended); the plate reaches glassd.json with materialize (240 × 240 texture px), fakeglassd lists it, data-lgs-plate-ack set 0.82 s after the element appeared (250 ms ramp + 350 ms + polling), status.steam.plateAcks lists it; element removed → phase 0 → gone; back to CSS only with no reporter, no __LGS_MOTION, no __LGS_SG, no glassd.json |
— |
P7 REQ item 3 (CSS-only lgs_sg.js for overrides) |
2026-10-07 04:27 | daemon running CSS only, /tmp/lgs/flags.json with tabBarDepth (P7's flagged rule), then the flag removed |
PASS: lgs_sg.js installed in vr:systemui while the override was active, overrides() sent (answered unsupported by the tree's Phase 1 lgs_sg.js copy at that time), destroy() when the flag dropped |
— |
| REQ P4->P8 fonts in SteamVR pages | 2026-10-07 06:16 | daemon restarted with the new lgs_vr_core.js (native.lock + lab-vr.lock); probe in every SteamVR page (document.fonts, fonts.check, canvas width of a 24 px test string) and the same canvas probe in the Steam main window |
PASS: systemui, controllerbindingui, keyboard: LGS Inter 100 900 loaded, fontErrors [], check true, body "LGS Inter", "Motiva Sans", …, width 296.8 = Steam main window 296.8 (sans-serif 308.9); no data: font left in the page CSS (272 KB → 114 KB). Offline: node harness over the real theme (wOF2 bytes, descriptors, re-apply idempotent, disable() deletes the face) |
— |
Theme-off grace (selftest grace) |
2026-10-07 06:13 | FR run "cd D && python3 lgs_shell.py selftest grace" (holds lab.lock, lab-vr.lock; lgs.op('off') then lgs.op('on'), as lab --stock does) |
PASS 8 / 8: dormant within 3 s with the same pid, still running 8 s after the theme went off, no scene graph while dormant, resumed in the same process when the theme came back (not dormant), lgsAction present, bridge daemon heartbeat fresh, SteamVR pages ok. The journal also shows another agent's --stock step at 06:12:14-21 survived the same way |
— |
| DM-2 v3 spec | 2026-10-07 06:20 | FR run "cd D && python3 lgs_shell.py selftest dm2 --keep-dump" (holds native.lock, lab-vr.lock; fakeglassd v3 with --dump, --test-report) |
PASS 15 / 15: fakeglassd caps = all 12 v3 caps; glassd.json carries plates (liquid, thick + fill, dim) in order with materialize, slab tint (CSS string) + hole fill, none slab with hole: true + colour-list tint, surface masks + coverDz, top-level world masks, unitM = live geometry, reduceMotion, roomDim 0.3, a dim slab with its fill, the keyboard surface (coverDz −0.027, scaleFrom: overlay); glassd-out lists the 3 plates and 2 holes; scene graph built (12 items); a leaving plate and slab are written with phase 0 (0.44 s after the report file changed; the test report is re-read once a second) and removed 0.34 s later (350 ms ramp); back to CSS only. First run failed 2 checks (dump dir not created; removal timed from the report write): fixed in the test, and the systemui loop now wakes at a fade's end |
/tmp/lgs/p8-dump/first-fakeglassd-main.png (procedural texture, no camera): looked at (disc, thick card with its fill, feathered dim plate in the backdrop; tinted slab and the dim cell in the atlas), then deleted on both machines |
Evidence, round R1 (review fixes, 2026-10-07 08:10-09:20)
Steam build 11094443. Deployed with frame_ssh put + flock /tmp/lgs/sync.lock install (only P8's two files; sha1 of the final lgs_shell.py 2f3bdbf2… = tree, shell_ext/__init__.py 5409a7ab… = tree; checked 09:33). All live runs python3 -B from D; locks as each selftest states (order native → lab → lab-vr). Offline harnesses in the scratchpad folder p8r1/ (p8r1_offline.py, and the session-2 p8_offline.py, p8_steamloop.py, rerun on the new code).
Finding by finding
| Finding | Fix (device/lgs_shell.py unless noted) |
Evidence | Result |
|---|---|---|---|
M1 lgsLayers global left after lgs off (G-REMOVE) |
lgsLayers binding added only when native_possible(); teardown deletes both binding globals (DELETE_BINDINGS_JS: only native-code functions, a test's JS stand-in is left); a CSS-only unit deletes a leftover lgsLayers when it connects |
selftest dm5 --stop 08:36 (daemon stop → {lgsLayers: undefined, lgsAction: undefined}), dm1 09:02 (after stop: both undefined), dm1/dm2/plates "back to CSS": lgsLayers undefined, lgsAction function; grace --native 08:53: native unit has lgsLayers; journal 08:20:31 steam: removed a leftover binding global lgsLayers (first new CSS unit), 08:29:27 another agent's native session ending: binding globals deleted [lgsLayers,lgsAction]; offline: CSS unit adds lgsAction only, native adds both, leftover deleted; node check of DELETE_BINDINGS_JS (native function deleted, JS function kept) |
fixed |
| M2 glassd runs through dormancy | supervise_glassd: dormant → kill_glassd("dormant"), glassd.json / glassd-out.json removed; glassd_loop does not count it as a crash nor back off, and starts glassd again only when not dormant; no glassd.json written while dormant |
selftest grace --native 08:53 18 / 18: native unit (fakeglassd, no --stay), theme off → dormant (mode dormant), glassd pid 197747 stopped in the same second (journal 08:53:35 glassd: stopping it (dormant)), no glassd files, lgs-native off, still stopped 3 s later; theme back 08:53:40 → new glassd 197858, native at 08:53:42, restarts 0 → 0, lgs-native back. Offline: terminated, not counted, not restarted while dormant, restarted after; a real exit still counts |
fixed |
M3 --stay outlives a Steam restart |
Steam-restart exit and STEAM_GONE_S exit apply with --stay; --stay grace = STAY_MAX_S 2400 s; after 2400 s a --stay unit drops native and follows the normal rules; start(native='on') restarts a unit past its limit |
the reviewer's own p8rev_stay.py on the new code: STAY B: stops ['steam theme off'] restarted True dormant False task done True, STAY E: stops ['steam gone'] task done True; p8r1_offline.py: B, E, limit (native off, --stay limit reason, normal grace after) |
fixed (offline: a Steam restart is never done on the device) |
| m1 bytecode on the Frame | unit runs python3 -B -u with -E PYTHONDONTWRITEBYTECODE=1; sys.dont_write_bytecode = True before the module's imports |
ExecStart /usr/bin/python3 -B -u …, /proc/<pid>/environ has PYTHONDONTWRITEBYTECODE=1 (08:30); device/__pycache__ and device/shell_ext/__pycache__ deleted 08:31; shell_ext/__pycache__ still absent at 09:19 after many daemon restarts; device/__pycache__/lgs_shell…pyc came back at 09:14:39 from a lab command (glass.py runs python3 without -B): REQ P8->P10 |
fixed for the daemon; lab part requested |
| m2 malformed report field fatal | every number through _num / _nums (finite, ranged), lists and objects shape-checked in glassd_config, build_spec, native_state, track_layers, main_resized, caps(), glassd_out_loop; the supervisor call wrapped in bug() |
p8r1_offline.py: r: "12px", x: "1e", dz: [1], w: NaN, h: Infinity, dz: 5, strings / ints / None as layers, texW: "wide", a list name, a dict overlayKey, layers: 5, a bad backdrop / uv / coverAt / pop time: no exception, only the bad item dropped, no NaN in glassd.json (9 checks) |
fixed |
| m3 frame-menu geometry stale; DM-3 not atomic | GEOM_JS returns a signature of the frame menu node (key, uv, mpp, visibility, scale, origin, curvature); a change (or any geometry change) schedules dumps at +1, +3, +6 s (the dump lags the node); safety dump every 15 s; status.geomDumpAt. DM-3: our dump → wait for a daemon dump after it → our second dump; compared only when our two dumps agree; repeated when the panel changed or the daemon was restarted |
selftest dm3 08:32 12 / 12 (runtime off: bridge not checked) and 09:18 15 / 15 (with bridge, incl. frameMenu.fWidth on the bridge): the first try saw the panel change 0.5557 → 0.6269 m and was repeated; the daemon's dump 1.4 s after ours matched (fWidth 0.6269, fHeight 1.1757). An 08:31 run failed 10 checks because another agent's native session restarted the daemon mid-run (status read during the restart): DM-3 now repeats a run in which the pid changed |
fixed |
| m4 idle work (CSS only) | SteamVR page sockets kept open per target (vr_page_eval), /json/list over one keep-alive aiohttp session, no /json/version probe, page scripts re-read only on a file change, the 10 Hz native_loop / glassd_out_loop at 1 Hz while CSS only; DumpLaserOverlays every 15 s instead of 5 s (plus after changes) |
scratch idle.py on the Frame (09:16, a quiet 60 s window, CSS-only unit pid 209778): 0.45 % of a core (27 ticks / 60 s; review: 0.9 %), 0 new sockets opened by the daemon (distinct socket inodes among its fds, sampled 4×/s; review: ≈ 81 TIME_WAIT / min); an earlier window (09:11, before the 1 Hz loops) 0.67 %, 0 new sockets |
fixed |
m5 start() not serialized |
start() holds /tmp/lgs/shell-start.lock (60 s); systemd's "already loaded" → running (…, started concurrently) |
scratch race.sh 09:18 under all three locks: stop, then two start --css at once → s1: running (css only), s2: started (css only), unit active |
fixed |
| m6 stale text | contract line 7, §1 (--stay, start lock, dormant glassd, mode), §4 (dump policy), §5 (bindings, RAM plugins), §7 (hole.edges, type checks), §10, §11 (all selftests incl. plates, grace --native), §12; docstring (lifecycle, selftest list); REQ P8->V1 extended to LAB.md ~115 and NATIVE.md's --stay line |
contracts/daemon.md, lgs_shell.py 1-50 |
fixed (V1's files by REQ) |
m7 status starting while dormant |
mode() = dormant while dormant (both modes; bridge daemon.mode too, native false) |
selftest grace 08:42 (CSS unit) and grace --native 08:53: mode dormant; offline (loop and both modes) |
fixed |
| m8 weak tests | DM-1 "nodes back" needs expired === false; DM-2 phase-0 wait 6 s; dm3 holds lab-vr.lock, dm4 lab.lock, dm5 --stop native → lab → lab-vr (was lab-vr then native: wrong order), dm6 --live native → lab → lab-vr; DM-4 loads a plugin file live from the RAM folder /tmp/lgs/shell-ext-test/ (kinds echo / read only; anything else refused) |
dm4 08:33 12 / 12: p8.probe loaded by the running daemon, called ({probe: 42, plugin: "test:p8probe"} on the bridge), a kind: nav file refused (a test plugin may only have kinds ('echo', 'read')), unloaded when the files went. DM-1 still reports "nodes back 0.0 s" with the stricter check: lgs_sg.js rebuilds the last spec on the first ping after the freeze, before our first probe (≈ 0.1 s) returns. DM-2's phase 0 came 0.44 s after the report change |
fixed |
| REQ P7->P8 (1) Steam's page by summon key | GEOM_JS and SP12_SNIPPET: the page whose m_sSummonOverlayKey is valve.steam.gamepadui.main in frame.pages (fallback frames[0].activePage only without one); r, Hm from its size |
live probe 09:22: frames[0].pages 1 = system.settings (lh 0), 2 = controller binding (lh 0), 4 = Steam (lh 1.5, wh 0.5535): /page:3/ matched nothing; DM-3 09:32 15 / 15 on the new daemon |
done (wp/P7.md answered) |
| REQ P7->P8 (2) a freeze of the daemon itself (SG-6) | stall_loop (event-loop gap > 1.5 s): glassd's silence then is not held against it (no "hung?" restart); the systemui loop pings first when its own gap shows the stall; timeouts across a stall keep the sockets; lgs-native retries at once |
new selftest freeze 09:25 12 / 12 (real glassd --no-feed, no camera; SIGSTOP 15 s): nodes back 0.37 s, lgs-native 0.7 s, glassd pid 214092 kept, no restart; journal 09:25:42 daemon: our event loop stalled 14.5 s …, systemui: heartbeat sent first …. P7 had measured 2.36-2.38 s with the earlier daemon. Offline 3 checks |
done (wp/P7.md answered) |
REQ P9->P8 hole.edges |
clean_edges (≤ 4 edges, each a colour or ≤ 8 colours, bad samples and empty edges dropped), only with cap holeEdges |
dm2 09:04: glassd.json slab p8-play hole {fill, edges: {top: "rgb(80 52 37)", left: ["rgb(70 50 40)", [0.3, 0.2, 0.15]]}} (an 80-char sample and right: [] dropped); offline 6 checks |
done (wp/P9.md answered) |
Live runs (R1)
| Test | Date | Command | Result |
|---|---|---|---|
| shell_ext offline | 09:19 | FR run "cd D && python3 -B lgs_shell.py selftest actions" (and PC) |
PASS 38 / 38 (34 + 4 RAM-folder checks) |
| DM-3 | 08:32, 09:18 | … selftest dm3 (holds lab-vr.lock) |
PASS 12 / 12, then 15 / 15 with the bridge |
| DM-4 | 08:33 | … selftest dm4 (holds lab.lock) |
PASS 12 / 12 |
| DM-5 | 08:34-08:36 | … selftest dm5 --stop (native → lab → lab-vr) |
PASS 7 / 7 (incl. no binding global after stop) |
| DM-6 | 08:36-08:41 | … selftest dm6 --live (native → lab → lab-vr) |
PASS 3 / 3 |
| grace | 08:41-08:42 | … selftest grace (lab → lab-vr) |
PASS 8 / 8 (mode dormant) |
| grace, native | 08:42 (waited for native.lock to 08:53) |
… selftest grace --native |
PASS 18 / 18 |
| DM-1 | 08:54-09:02 | … selftest dm1 |
PASS 17 / 17: lgs-native off 3.6 s, nodes off 11.8 s, both back 0.4 s; teardown order; no binding global; backoff [2, 4, 8, 16]; gives up; back to CSS |
| DM-2 | 09:02-09:04 | … selftest dm2 --keep-dump |
PASS 18 / 18 (fakeglassd lists all 13 caps incl. holeEdges); its procedural dumps (no camera) were not needed and were deleted at 09:11 |
| plates | 09:04-09:10 | … selftest plates |
PASS 13 / 13 (fragments 00-base … 99-legacy, 7 now; ack 0.92 s) |
| idle cost | 09:16 | scratch idle.py 60 |
0.45 % of a core, 0 new sockets / min |
| start race | 09:18 | scratch race.sh |
one started, one running, no failure |
| freeze (new, REQ P7->P8) | 09:25 | … selftest freeze (native → lab-vr) |
PASS 12 / 12 |
| DM-1, grace native, DM-3 again (after the stall and page changes) | 09:25-09:32 | … selftest dm1, grace --native, dm3 |
PASS 17 / 17, 18 / 18, 15 / 15 |
Not re-run: the reviewer's lgs off / on --css sweep (p8rev_remove.py). lgs off clears /tmp/lgs/flags.json, which held other agents' development flags (wp.c1a, wp.c2a, wp.p3) all session, and on --css would not restore them. The daemon's half of lgs off is stop(), which dm5 --stop and dm1 check directly (both binding globals gone); the rest of the sweep (pages, fonts, files) did not change in R1.
Evidence, session 4 (maintenance, 2026-10-07 12:55-14:25 EDT)
Steam build 11094443. Native sessions are python glass.py native-session --flags wp.c2a,wp.p3,wp.c1a --mode pad --step "js '<navigate, hold 34-52 s, log plates / acks / lgs-native / glass mode each second>'", driven from the scratchpad (p8/run2.py, p8/run3.py: the looks start only once glassd.json shows the wanted state, so a step that waited for lab.lock cannot shift them onto someone else's route); during the hold glassd.json, glassd-out.json and shell.json were copied every 2.5 s (no room imagery), P7's dump read with glass.py js … --in vr:systemui, and glass.py hv NAME --look --full --grabs 3. For the looks the window was lifted (and brought 350 mm closer) with a lab-only __LGS_SG.overrides({rules: [{id: "p8-lift", target: "window", addMm: [0, 600, 0]}]}) (or [0, 560, 350]), cleared after each look and by the daemon's teardown in any case. Every frame was looked at, then deleted on both machines at once (the tool deletes the Frame copy; the local look folders and crops were removed by hand within the minute).
| Test | Time | Command / what | Result |
|---|---|---|---|
| #17 report chain | 13:05-13:07 | session A: hold on /library/home 40 s, poll |
Reporter (__LGS_LAYERS.debug('main')): windowless, shapes: [], 18 plates (shell-back, shell-search, home-top-seg, home-top-wn, 13 discs, home-card), 4 bands, layer home-card dz 0.0407; plates acked 2 s after the route. glassd.json main: material: window, shapes: [], 18 plates, slab home-card; glassd-out.json main: cover: 0, counts.plates 18, slab cell. P7's dump: cover (glassd backdrop uv [0, 0, 1, 0.496]) + 8 base pieces, all inside the 4 bands, minus the card's hole + the card's pop and slab. The chain is as planned |
| #17 look, frontal | 13:16, 13:54, 13:58 | sessions B, D, E: hv … --look (window lifted; D, E also closer) |
Native Home right: glass discs with art and a thin bezel, labels and page dots, top-row plates (segmented track, What's New, Back circle), the open card at +15 mm. No window slab. The search field's plate reads faint (see P7 REQ) |
| #17 look, off axis | 13:58 (E), 14:02 (F) | hv … --offaxis 20 and -20 |
−20°: right. +20°: in the base pieces split off by the card's hole the art goes behind the plates on part of the piece (the disc beside the card bare glass); full-width pieces right. Panel-flag trials via /tmp/lgs/sg-flags.json (removed after, checked absent 14:04): base/slab/pop sort-depth-bias −0.3/−0.45/−0.6 helps partly, cover no-depth-write not at all. Filed REQ P8->P7 (wp/P7.md). AllGames (window route) at ±20° and 0: posters right; dotted vertical seams at the tab row's pop edges (in the P7 REQ) |
| #17 cause seen in another session | 13:07:24 | the poll during another agent's native session | main with shapes: [{0, 0, 1920, 1080, r 81}] + Home's 18 plates + legacy tabs / tab-arrow pops (Home's module leaving at its step's end, main window-full): with the explicit bands this hid everything outside the bands behind the window glass. Fixed in P6's reporter (RP-MO) and guarded here (build_spec) |
| C6b #2 main away | 13:45-13:46 | session C: FrameStore switched to system.settings for 13 s from vr:systemui (navigation only, back to Steam's page by its summon key) |
status.mainAway true and main empty in glassd.json 13:45:51-13:45:59 (page system.settings, steam: false); spec without main; the settings page's look: SteamVR's panel, no dark rectangle; back 13:46:01, Home's 18 plates + the card slab again by 13:46:04, lgs-native on main 13:46:03 |
| C6b #1 cause | 13:11 | glass.py js "<poll __LGS_VRX.settings 24 s>" --in vr:systemui --flags wp.c6b with shell.json polled |
steamvrPages.paused: true, vrScripts {}, the flag seen in flags but no script for 24 s: the pause file left by a lab vr: theme off/on |
| C6b #1 fixed | 14:06 | same step on the new daemon; then glass.py gates vr:systemui --only aud --json |
__LGS_VRX.settings present at the first sample (journal: installed 14:06:13, removed 14:06:14 when the flag went); gates' stock snapshot: journal page theming paused 14:06:32, resumed (paused 2 s) 14:06:34, no pause file left, AUD PASS |
| Offline | 13:21 | python -B offline_s4.py (scratchpad p8/, the real Shell on the PC) |
27/27: page shown / away / back (main empty but kept, no fades, out of the spec, bar unchanged, re-materialize, unknown page shown), mosaic with a cover dropped (shapes and the shape-less window fallback), windowless bands kept, stale pause ended, fresh pause honoured, apply_once() ends a pause (with and without SteamVR), a flag in flags.json installs a script while the Steam loop's copy is stale, status.mainAway |
| Selftests | 14:12-14:19 | cd D && python3 -B lgs_shell.py selftest dm5, dm2, plates |
PASS 4/4, 18/18, 13/13 |
Decisions taken under PLAN §1.17's rule (safe default, flag, note)
-
R1: glassd is stopped as soon as the daemon goes dormant (no warm window). The safe default for "nothing reads the camera with nothing to draw"; the cost is about 2 s more to native after a lab
--stockstep (glassd restarts; 08:53:40 → 08:53:42). -
R1:
--stayis bounded by 2400 s (STAY_MAX_S, the PC side ofnative-sessiongives up after 2400 s), as dormancy grace and as native lifetime; after it the unit is an ordinary CSS-only unit. A constant, not a flag: it guards against an agent that died, which cannot be relied on to set a flag. -
R1: RAM test plugins (
/tmp/lgs/shell-ext-test/) are always scanned, but may only declare kindsecho/read, so a file in/tmpcan never navigate, launch or change the UI. -
R1:
start()lock/tmp/lgs/shell-start.lock(RAM, 60 s); on a timeout it goes on and treats "already loaded" as running. -
native=Noneandnative='auto'never restart a running unit (a plainlgs onby any agent must not end another agent'snative-session);autois resolved only when a new unit starts, in the order flags.json, defaults.json, built-in CSS only. -
The source check for actions uses two things: the binding call's execution context must be SharedJSContext's default context (learned with a nonce probe, so Runtime.enable is not needed on Steam's busiest context), and the claimed
srcmust be in the action'ssources. -
nav/launchactions are logged and not run whileactionsDryRunis on or P1's runtime action logger (rt.test.actions.enabled()) is on; they are recorded there asdaemon.<type>. If the logger state cannot be read, the action is not run. -
Built-in
sgwindowaction (window dim / recede with a TTL ≤ 60 s, default 10 s) is the CSS-only path for P7'swindowstate; it expires by itself so a forgotten request cannot leave the window dimmed. -
Theme off without
lgs off: dormant (native layer, reporter, scene graph off; SteamVR page theming and the action binding kept) forshellThemeGraceS(default 600 s, flag,0= Phase 1's exit after 3 s), then exit; exit at once when the theme is off on a new SharedJSContext target (Steam restarted) and after 120 s without Steam. A bounded wait keeps the transient rule (nothing autostarts: the theme only comes back through someone'slgs on/lgs.op('on')), and lab--stocksteps no longer kill the daemon for everyone. -
A Steam evaluation that times out is tolerated twice in a row (connection kept, logged once); the third drops the socket as before. Steam's JS thread is often busy for seconds with other agents' audits, and dropping the socket took the native layer down for 2-4 s.
-
dimslabs (glassd G7 room-dim cells) are passed to glassd with theirfillonly when glassd listsdimSlab, and go to the scene-graph spec as surfacedimSlabs(never popped: there is no element to crop). P7 places them when room dim is built (C7'sroomDimflag is off).dimplates need thedimcap;roomDim(report top level) theroomDimcap. -
lgs_shell.py start --assume-caps …(lab only, a CLI option rather than a flag so no shared file is touched) makes the writer act as if glassd announced those caps, so the glassd.json side of DM-2 can be checked before P9's v3 binary is installed.
New strings
None.