The generic .hidden { display:none } is defined earlier in the stylesheet than
.dev-panel { display:flex }; equal specificity meant source order won and the
panel stayed visible with the toggle off. A two-class .dev-panel.hidden rule
wins, so all dev controls/data truly hide when Dev Mode is disabled.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a single "Dev Mode" switch at the bottom of the control panel (off by
default, persisted in localStorage). When on, a panel appears below it with all
dev controls and data; when off the default view is just the experience + the
three control fieldsets.
Dev panel:
- Pool picker: a <select> of the current altitude's pool members — choose any
clip to override the random landing pick; a "Re-roll random" button re-runs
the server-canonical pick. Rebuilds on each landing.
- Active clip metadata (id, title, source, license, base_file).
- Annotations & affect: keys with salience/window/tier counts (read-only;
editing stays in /author.html).
- RenderPlan JSON — relocated here from the always-visible panel.
- Cache & playback: preload status (N/total), memory-vs-network for the active
clip, live resolution/duration/loop position.
Frontend only — no API changes; everything is read from data the renderer
already has. Factored pickRandomMember() out of landScale() so the initial
landing, ring advances, and the re-roll button share one pick.
Design: docs/superpowers/specs/2026-06-25-simulator-dev-mode-design.md
269 passed, 2 skipped; node --check + server smoke clean. Frontend behavior
to be verified by-eye by the operator (no headless browser in repo yet).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each altitude change reset the single <video> element's src and re-fetched
the scale's footage over the network, compounded by the dev no-store policy
applying to /media too — so every scale change paid a full fetch+decode.
- simulator/app.py: split the cache middleware — app shell/JS/CSS stay
no-store (by-eye dev iteration), but /media is now cacheable
(public, max-age, immutable) since the clips are static.
- simulator/static/app.js: preload every ring clip (19 base + 5 transition,
~350 MB) into in-memory blob object URLs at startup — non-blocking, the
first scale plays immediately while the rest stream in, ordered current
scale outward. mediaUrl() serves the cached blob when ready, else falls
back to the network. A small progress chip self-removes when done.
- tests: assert app assets stay no-store and /media is cacheable.
269 passed, 2 skipped. Frontend verified by-eye by the operator (no headless
browser in repo yet).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The scale ring's ⊖ out / in ⊕ buttons are replaced by a circular **Altitude**
knob (the endless rotary encoder, drawn as a dial). cosmos (highest) sits at the
top; turning clockwise descends cosmos → orbit → coast → reef → abyss and past
the deepest **wraps back to the top** — the same endless ring the server already
models (advance + wrap unchanged).
- Built dynamically from `ring.scales` (labels + ticks per scale, a needle that
points at the current scale, an ALTITUDE caption).
- Drag to turn: the rotation accumulates and commits whole detents on release, so
a big spin becomes one fast blended pass (reuses the proven wheel/detent path);
scroll the knob to step; tap a label to jump the shortest way around.
- Pure frontend — no engine/API change. JS syntax-checked; live server probed
(assets serve, dial present, abyss→cosmos wrap intact). 23 sim-API tests green.
By-eye review deferred to the operator (no Chrome on this box).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Content-pipeline Increment 2, part 2 (the tooling). Implements design §11.5
(docs/superpowers/specs/2026-06-24-content-pipeline-design.md):
- tools/pipeline/track.py — the stage-5 geometry pass. Classical path: a
hand-seeded normalized box propagated by OpenCV Lucas-Kanade optical flow
(CSRT used instead when a contrib build provides it), sampled to a SPARSE
loop-normalized keyframed track + an appear/disappear window. Optional ML
detect+track path lazy-imports ultralytics (clear ImportError if absent;
base install needs only cv2). Pure helpers (normalize/denormalize, loop_t,
infer_window, sample_track, track_to_annotation) are unit-tested; the cv2
propagation is an opt-in integration test on real abyss_wow footage.
Semantics are never produced here — geometry only.
- Author mode — /author.html + author.js/.css reuse the preview stage: pick a
pool clip, scrub, drag a seed box, Run tracker (or Add as static box), author
the LEFT detail tiers (general -> scientific+fact) + salience, shift-click to
place affect anchors with RIGHT emotion tiers, and Save to manifest. Backend:
POST /api/author/track (runs track_seed on the clip's base) + POST
/api/author/clip (idempotent upsert via tools.pipeline.manifest — keeps media
+ provenance, replaces only authored content, reloads in place). The tracker
propagates box geometry; all strings/scientific names/facts are hand-typed.
- Repointed the pipeline integration test off the retired forest base to the
cosmos pool primary. USER_GUIDE simulator section brought current (pools,
coast, 3-knob Mood, real-time dream, progressive tiers, author mode).
267 passed / 2 skipped (+ track pure + opt-in real-footage + author endpoint
tests). Author UI by-eye review deferred to the operator (no Chrome on this box).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Content-pipeline Increment 2, part 1 (the experience). Implements design §11
(docs/superpowers/specs/2026-06-24-content-pipeline-design.md):
- Rotating pool (§11.1): each ring scale references a POOL of vetted clips
(docs/content-candidate-pool.md); the player picks a random member on landing.
player.ring.Scale gains `pool` + pure `pick_clip_id(scale, r)` (injected
randomness); the pick happens at the API boundary (random.random()), a delta=0
advance is the initial/re-roll pick. clips.py parses/exposes the pool; the
client lands on move.target_clip_id. Back-compat: a scale with only clip_id is
a pool of one.
- Rename ring scale forest -> coast (new land<->sea scale, orbit..reef); ring
order cosmos -> orbit -> coast -> reef -> abyss -> wrap. Cleanup: dropped the
leftover local media (cosmos_traverse, orbit_westcoast, old forest/reef, the
Increment-1 orbit/abyss single bases); new coast-edge transition placeholders.
- Time-windowed tracked labels (§11.2): annotations gain appear/disappear
(loop-normalized, wraps across the seam); a label shows only while on screen
and its box tracks the subject. abyss + reef pools authored as test material.
- Progressive LEFT detail tiers (§11.3): per-object `salience` (first shows at
Left 5-salience) + tiered strings (general -> specific -> scientific -> +fact)
escalating with the Left knob; replaces flat min_level gating. Strings may be a
tier list or a static string (back-compat). No engine change (client reads
overlay.level).
- Progressive RIGHT emotion tiers (§11.4): affect vocabulary escalates basic ->
compound with the Right knob (dream.strength); appearance still gated by
min(left,right). No engine change.
New simulator/build_pool_manifest.py holds the hand-authored content and emits
the 19-clip pool manifest (the reproducible baseline). setup_scales_media.py
marked superseded. 251 passed / 4 skipped (+ ring pool + API pool/window tests).
By-eye visual review deferred to the operator (no Chrome on this box).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Records the 5-scale rotating clip pools the operator selected + vetted from the
candidate gallery (19 clips: cosmos 4, orbit 3, coast 4, reef 5, abyss 3), each
strict-PD/CC-BY-class, word-free, human-free, with source URL + trim window so
they are re-sourceable (media stays gitignored). Captures: the rotating-pool model
(random clip per scale on ring landing), the forest→coast rename, the cosmos
#3-Orion vs #6-Traverse processing mix-up to fix, and the rejected/leftover clips
to clean up. Feeds the next session (Increment 2 expanded: pool wiring + tracked
progressive labels + progressive emotions).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- run.py: add resolve_ffmpeg() mirroring setup_scales_media.py; change
process_clip ff param to None and resolve at call time
- test_pipeline_integration.py: gate on resolve_ffmpeg() not PATH ffmpeg;
verify proxy dims with cv2 instead of ffprobe
- ffmpeg_ops.py: add fps= to trim segments in crossfade_loop_args so xfade
receives CFR input (xfade rejects 1/0 rate from raw trim output)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bite-sized TDD plan for Increment 1 (real footage, no ML): the deterministic
tools/pipeline/ package (frame → crossfade-loop → master+proxy transcode →
manifest assembly, pure ffmpeg arg builders unit-tested + an opt-in integration
test on the real forest POC base), the backward-compatible keyframed annotation
track schema with client-side interpolation, and the 5-scale ring (orbit + reef).
9 tasks. Implements spec §3 (stages 1–4,6), §4, §5, §6, §7, and §8 Increment 1.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Brainstormed + scoped the content pipeline that turns strict-PD source clips into
shippable, seamless-looping, labelled neutral bases for the scale ring.
Decisions settled this session:
- Scale set: lean 5 (cosmos · orbit · forest/land · reef · abyss; microscopic dropped)
- Loop: crossfade tail→head seam
- Labels: motion-tracked, hybrid (ML proposes where it can, hand-seed + classical
tracker elsewhere); label semantics always hand-authored
- Format: master + 1080p H.264 proxy
Anchor fact: the Right dream is real-time (no per-clip ML bake, session 0013), so
the pipeline is transcode + authoring — the one offline ML step is the chosen
motion-track pass. Six stages + an optional keyframed annotation-track schema
(backward compatible) + a 2-increment rollout (real footage no-ML first, then the
hybrid track pass + simulator author mode). i2v ring transitions + Pi/pano deferred.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The engine already models Dark/Light as the two poles of one signed axis
(tone = (light - dark)/4, equal = neutral), so the simulator now exposes a single
bipolar Mood dial (-4 dark .. 0 neutral .. +4 light) that maps onto dark/light in
controls(). Removes the Calibration section entirely: mood_gain and overlay_gain
were always-locked 1.0 dev knobs (full dial = full effect), so they bake in as the
DEFAULT_CALIBRATION constants and the sim sends no calibration. Experience panel is
now just Left, Right, Mood.
Engine/tests unchanged (DEFAULT_CALIBRATION already carries the 1.0 constants).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the pre-baked SD restyle variants (too fluid — the painting re-rolled
every keyframe and the flow-warp melted frames) with a deterministic, real-time
dream that holds STILL across the loop; only the footage's own motion moves.
Design: docs/superpowers/specs/2026-06-22-right-axis-deterministic-dream-design.md
Engine (player/alteration.py): Restyle(variant) -> Dream(strength, intensity);
Calibration.right_variant_map -> dream_gain. player/state.py: a Right change is a
LIVE_UPDATE now, not a crossfade (only a clip swap crossfades). Plan dict
restyle -> dream. Tests migrated; 233 green. (No new Python behavior for the
client-render swap below.)
Renderer (simulator front-end): the Right dream is a WebGL2 Kuwahara shader on a
<canvas> over the live video — edge-preserving, so motion/structure stay crisp;
the dream is the same footage, just stylized (no blur). Ramped by the knob; max
goes "trippy" (vivid saturation + ~45deg hue drift + posterize, concentrated near
max via t=intensity^2). Phase-1 luminous-haze (CSS+bloom) was built then retired
by eye (blur read as out-of-focus video). Mood grade rides as a CSS filter on the
canvas; bloom layer removed.
Dev ergonomics that fell out of the iteration:
- /dev/version + a 1s client poll = live-reload (open tab never runs stale
renderer code; reloads on my edits AND server restarts).
- no-cache on the dev server; an on-screen error banner + crash-proof render so a
silent throw can't masquerade as "nothing is changing".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Left-brain HUD look-and-feel pass (judged by eye in the sim):
- corner-bracket targeting reticles (capped arms) replace plain boxes
- translucent label chips, legible over any frame
- two sensor channels: cyan detections (with deterministic confidence
scores) vs amber measurements (center crosshair)
- bbox-sized "ANALYSIS . L{n} . {k} OBJ" status tag, escalating with Left
Affect channel (emotions in the HUD), per
docs/superpowers/specs/2026-06-22-affect-channel-hud-design.md:
- surfaces only when BOTH knobs up; strength = min(left, right)
- stable per-scene palette, more words appear with combined strength
- soft glowing violet words on their own opacity layer (the dream
leaking into the machine's read); math in player/alteration.py
- forest/cosmos/abyss palettes in the manifest; 5 new unit tests
Simulator no-cache middleware so by-eye iteration never serves a stale
app.js / api response (fixes the latent "plan has affect but clip.affect
empty" stale-/api/clips bug).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ran the baker for the forest scale: real flow-stabilized painterly Right variants
1-4 now back the dreamlike axis on real Yosemite footage (was: 1 real + 3 ffmpeg
placeholders). By-eye finding this surfaced: sd-turbo's painterly transform is
sharply non-linear in img2img strength (barely registers <0.5, ramps hard through
~0.5-0.65), so the initial linear 0.28-0.58 ramp left levels 1-3 ~identical to
raw. Re-baked with a clustered 0.46/0.52/0.58/0.64 curve -> a real perceptual
ramp: gentle dream haze -> clearly impressionist -> strongest, each notch visibly
dreamier, temporally calm (flow-stabilized).
- manifest: forest right_variants 1-4 all model 'sd-turbo+farneback-flow'
- setup_sample_media.py: now only stages the neutral base; the baker owns the
Right variants, so re-running it never clobbers a real bake
- docs: USER_GUIDE setup steps + ROADMAP v2v vertical-slice-done note
Suite 228 passed / 2 skipped.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Right strength curve (level 1-4 -> increasing img2img keyframe strength) is pure
and unit-tested; the restyle engine is a verbatim port of the session-0008 POC
flow_restyle.py (SD img2img keyframes + Farneback optical-flow tweens = calm,
no boiling). torch/diffusers imported lazily so the module + curve test stay light.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>