Commit Graph

350 Commits

Author SHA1 Message Date
BenStullsBets ee7c65a0f2 feat(credits): restore the CC BY-SA declaration as "License & reuse" (Option D1)
s0031 removed "About this work" to avoid duplicating it on the separate About
page, but that section WAS the D1 compliance — it declares the altered work is a
derivative offered under CC BY-SA 4.0, which is what satisfies the ShareAlike
obligation for the BY-SA source clips. Without it the live site credited the
BY-SA authors but never declared the derivative share-alike.

Restore the declaration on the credits page, under the heading "License & reuse"
instead of "About this work" so it isn't confused with the separate About page
(which covers the project's intent). Tests assert the declaration is present and
that the heading is NOT "About this work".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:23:57 -07:00
BenStullsBets 64e8f658f6 claim human-experience-filter-art session 0032 (placeholder) + sessions.json entry 2026-06-30 09:21:27 -07:00
BenStullsBets 6cf8fffc51 feat(a11y): one-time photosensitivity warning gate
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:18:04 -07:00
BenStullsBets 1f98b7bd8c feat(a11y): flash.js pure WCAG 2.3.1 flash-clamp helper + tests
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:16:02 -07:00
BenStullsBets 6e2e65202e feat(a11y): AA contrast bumps, global focus-visible, visually-hidden util
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:15:41 -07:00
BenStullsBets fe77677d4c feat(a11y): move language picker below Audio, globe inline-left
Adds the Reduce-motion toggle markup + i18n label in the same Output
fieldset (wired in a later task). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:14:47 -07:00
BenStullsBets 760fdc7550 add sessions/0031/SESSION-0031.0-TRANSCRIPT-2026-06-30T09-06--2026-06-30T09-11.md + replace placeholder/variant SESSION-0031.0-TRANSCRIPT-2026-06-30T09-06--INPROGRESS.md 2026-06-30 09:13:37 -07:00
BenStullsBets a895e7139b plan: accessibility pass + About page implementation plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:11:43 -07:00
BenStullsBets 8277417583 chore(credits): drop the "About this work" section — it moves to the About page
The CC BY-SA work-license declaration ("About this work") was on the credits
page, but the About page (built on the parallel feat/accessibility-pass) will
own that content; keeping it in both places would duplicate it. Remove the
section from credits.html and make the footage note self-contained (it still
states the footage is altered — the per-clip "indicate modifications" cue stays
with the attributions). Tests updated to assert the declaration is intentionally
no longer on the credits page. The .declaration CSS rule is left in place for the
About page to reuse.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:10:59 -07:00
BenStullsBets e62382c5e5 claim human-experience-filter-art session 0031 (placeholder) + sessions.json entry 2026-06-30 09:08:16 -07:00
BenStullsBets d10ffca4e8 fix(audio): route scaleAudioUrl through mediaBase — was hardcoded /media (404 on static)
The audio layer built the soundtrack url as a literal /media/audio/<f>, which 404s
on the static R2 build (audio err 4, silent). Route it through mediaBase() like the
video path: dev → /media/ (unchanged), static → https://static.benstull.art/. Verified
live: aud loads 200 from R2 and plays.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:06:41 -07:00
BenStullsBets 041fcdeb7d fix(sim): keep Feel (affect) words from overlapping Think (label) chips
Denser left labels (now >=3/clip) plus the always-4 right affect words made the
two HUD layers collide. renderAffect now nudges each Feel word vertically off the
Think chip plates (and off already-placed words + the ANALYSIS status tag),
staying on-screen, keeping the least-overlapping spot if nothing is fully clear.

Feelings are scene-level (soft position) so we move THEM, never a chip off its
subject. Obstacles are computed from the manifest, not the DOM, and a tracked
label's path is sampled across the loop — so the clearance holds for the whole
loop even though the per-frame track re-render keeps moving the chips (no per-frame
re-layout, no jitter). New diagnostic seam window.__hefNoDeconflict (sibling of
__hefState) A/B-toggles it.

Verified across all 6 scales at max knobs: authored overlaps drop to 0 with none
introduced (cosmos 3->0), and a tracked abyss clip stays at 0 overlaps sampled
over loop time as its chip drifts. No affect-position test exists to break;
16/16 node unit + frontend pytest green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 09:06:36 -07:00
BenStullsBets a68d0bc4df feat(deploy): repeatable Cloudflare deploy script + make targets
deploy/cloudflare/deploy.sh (build → optional parallel R2 media sync → Pages
deploy); 'make deploy' (frontend-only) / 'make deploy-media'. One-time setup
stays documented in README. Guards against unauth + mis-keyed R2 uploads.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 08:56:30 -07:00
BenStullsBets 7da944af0c feat(load): progressive boot + hold-last-frame until media loaded
Boot becomes ready on the first clip and streams the rest in the background
(was: blocked on the full ~2.3 GB before 'Run simulation' appeared). During nav,
the visible morph and landing base only swap to cached blobs — otherwise the
current frame holds (settleGen guards a stale settle) — so rapid altitude-clicking
never blanks the stage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 08:53:40 -07:00
BenStullsBets 31956bfdfc add sessions/0030/SESSION-0030.0-TRANSCRIPT-2026-06-30T08-10--2026-06-30T08-50.md + replace placeholder/variant SESSION-0030.0-TRANSCRIPT-2026-06-30T08-10--INPROGRESS.md 2026-06-30 08:53:05 -07:00
BenStullsBets 5a08d1dd35 claim human-experience-filter-art session 0030 (placeholder) + sessions.json entry 2026-06-30 08:50:53 -07:00
BenStullsBets 768fee5ccd design: accessibility pass + About page (non-kiosk public web)
Reduced-motion freeze-to-stills, photosensitivity warning gate + flash
audit, keyboard/SR-operable Altitude dial, low-vision contrast, and a new
about.html (English-first). Targets WCAG 2.1 AA plus a motion/seizure layer
for the unattended public URL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 08:49:02 -07:00
BenStullsBets 432fbd7703 add sessions/0029/SESSION-0029.0-TRANSCRIPT-2026-06-30T08-09--2026-06-30T08-41.md + replace placeholder/variant SESSION-0029.0-TRANSCRIPT-2026-06-30T08-09--INPROGRESS.md 2026-06-30 08:43:06 -07:00
BenStullsBets 1c7f2b7785 feat(credits): public CC-BY/BY-SA attribution colophon + CC BY-SA work license
CC BY / CC BY-SA footage needs its author + license shown to the viewer, and the
installation alters every frame (a derivative). Per-clip license/source already
shipped in clips.json but only surfaced in the dev panel and internal review
pages — the public experience showed nothing, so the static benstull.art site
was not attribution-compliant.

Option A — add a manifest-driven credits/colophon (credits.html + credits.js):
the page fetches the same clips the experience uses (baked clips.json in static,
the live API otherwise) and renders one attribution block per clip (title,
license, source), HTML-escaped, sorted, never silently dropping a clip. An
"ⓘ Credits & licenses" link in the header (localised en/es/fr/ja) makes it
reachable from the work — what CC's "reasonable to the medium" calls for. Audio
is all PD/CC0, credited as a courtesy block.

Option D1 — the colophon declares the altered work itself is offered under
CC BY-SA 4.0 and states the footage is modified, satisfying the ShareAlike +
"indicate changes" obligations for the BY-SA sources. Non-commercial.

Ships in the static build (added to PUBLIC_ASSETS). Verified: 41/41 clips
credited incl. all 7 CC-BY/BY-SA clips, 0 missing a license. Tests: node units
for creditEntries/creditsListHtml + escaping + page structure/declaration;
pytest build-output assertion; Playwright e2e (page lists attributions, header
links through).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 08:39:26 -07:00
BenStullsBets 4e4a5c256d Merge remote-tracking branch 'origin/main' into worktree-fix+audio-and-tracking-labels 2026-06-30 08:36:40 -07:00
BenStullsBets 8cfceffb76 feat(content): enrich left (Think) labels to >=3/clip + motion-track abyss creatures
Every clip now carries at least 3 factual LEFT annotations to match the 4 RIGHT
(Feel) affect words — fixes the "many clips missing Think labels" asymmetry
(right read "everywhere", left felt sparse). Adds one researched factual label to
each of the 23 thin clips (orbit_bluemarble had only 1; 22 had 2), with full
es/fr/ja parity via the catalogs, and converts the 4 still-static abyss creature
subjects (octopus, bigfin squid, dandelion siphonophore, sea pig) from fixed
boxes to 3-keyframe motion-tracked labels (tracked clips 7 -> 11).

Boxes/tracks are a first pass sampled by eye from base footage (t=0.05/0.5/0.95);
they want operator by-eye review in author mode to fine-tune positions. Remaining
static clips are landscapes (correctly static) plus 2 diffuse fish schools and 2
surface-animal scenes, available for author-mode tracking if wanted.

Data-only change (build_pool_manifest.py LABELS + manifest + i18n catalogs); no
app/engine code touched. Verified: 43/43 manifest+catalog+i18n pytest, 16/16 node
unit, live render shows 3 chips/clip in all 4 languages with no JS errors. (The 3
failing pytest are pre-existing cv2/ffmpeg-absent env limits, unrelated.)

Note: the reported "audio not playing" was a false alarm — exhaustive repro
(Chromium+WebKit, strict autoplay, both servers, RMS-verified non-silent files)
found the audio engine correct; the operator's Safari output was muted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 08:36:05 -07:00
BenStullsBets 3a45e338f3 feat(static): "Run simulation" start button over the black stage
The experience boots silent and waits for a gesture (browser autoplay blocks
an un-gestured audio play()). Until now that gesture was an unlabelled Video
toggle — not obvious to a first-time viewer. Add a "Run simulation" button,
revealed over the black stage once media has preloaded; clicking it turns
Video on, snaps the audio dial to a gentle 2/10, dismisses the prompt, and
starts the soundtrack in that one gesture. Turning Video on directly still
begins the experience too (couples audio in, dismisses the button) via a
shared beginExperience(). Localised en/es/fr/ja.

E2E coverage updated (Playwright + pytest-playwright): button shown after
load, button starts video+audio+dismisses, and direct-Video-on path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 08:28:49 -07:00
BenStullsBets 8cc472c93c docs(static): correct Cloudflare runbook to match actual deploy
Real bucket name, wrangler 'cors set --file' (+ wrangler-format CORS file),
per-file r2 object put upload (no bulk sync exists), CLI custom-domain bind via
zone-id, Pages domain is dashboard-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 08:25:08 -07:00
BenStullsBets 5f8b8f14e6 claim human-experience-filter-art session 0029 (placeholder) + sessions.json entry 2026-06-30 08:12:39 -07:00
BenStullsBets bcae35b63e test(static): E2E — static boot with no API + dream shader survives cross-origin CORS
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 07:48:22 -07:00
BenStullsBets a93c0d57c8 chore(static): Cloudflare deploy artifacts — R2 CORS policy + deploy runbook
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 07:45:20 -07:00
BenStullsBets 77cb04bb8c feat(static): build script — dist/<app>/ + manifest-only media tree, baked API JSON, _redirects
Run via 'python -m tools.build_static' (avoids tools/http.py shadowing stdlib http).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 07:43:49 -07:00
BenStullsBets 271586625c feat(static): config-driven media base, baked-JSON boot, client pick/alteration, crossOrigin
Relative asset paths (work at dev-root and under the /human-experience-simulator
deploy prefix), config.js gate, devLiveReload no-ops in static mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 07:43:49 -07:00
BenStullsBets be13248bdd feat(static): client-side alteration engine (port of player.alteration/audio)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 07:25:24 -07:00
BenStullsBets 7ef3e0ccc6 plan: Cloudflare static publish — implementation plan
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 07:23:57 -07:00
BenStullsBets 59c1c3a895 add sessions/0028/SESSION-0028.0-TRANSCRIPT-2026-06-30T06-53--2026-06-30T07-21.md + replace placeholder/variant SESSION-0028.0-TRANSCRIPT-2026-06-30T06-53--INPROGRESS.md 2026-06-30 07:23:18 -07:00
BenStullsBets 7a51d8f6d4 design: publish to benstull.art via Cloudflare Pages + R2 (fully static)
Solution design for a no-origin public target at
benstull.art/human-experience-simulator (apex redirects): static frontend on
Pages, ~2GB served media on R2 behind static.benstull.art, dynamic logic baked
to JSON / moved client-side. Records the deliberate flotilla/PPE process
exception for a static art target.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 07:18:00 -07:00
benstull 582183db2f Merge pull request 'fix(sim): kill the reverse-landing frame jump (D3)' (#30) from fix/d3-reverse-landing-jump into main 2026-06-30 14:17:56 +00:00
BenStullsBets c13ce36787 fix(sim): kill the reverse-landing frame jump (D3)
Ascending the altitude dial and landing on the higher-altitude clip jumped ~3
seconds. A baked morph `transitions/{h}__{l}.mp4` spans src@0 (frac 0) ->
dst@~LOOP_TAIL_S (frac 1) and is scrubbed bidirectionally, but the steady loop
element always seeked LOOP_TAIL_S (3.0s) on landing. Descending lands on dst at
its tail (matches the seek — the s0026 loop-from-tail fix); ascending lands on
src at the morph's HEAD (src@0), so seeking the tail was a 3s jump.

Make the loop landing frame direction-aware: pure `HEFScrub.loopLandFrame(dir,
loopTailS)` returns 0 when ascending, loopTailS otherwise. `loadLoop` takes a
`landFrame`, stores it as `dataset.loopStart`, and the timeupdate/ended wrap
handlers honor it (the base clips are already seamless crossfade-loops, so a
reverse landing wraps [0,dur] cleanly). The settle path lands explicitly on the
direction's frame so a fast ascent never jumps even if the heading clip wasn't
preloaded in time. App-side only — no 154-morph LFS re-bake.

Verify: node unit (loopLandFrame) + a new reverse-landing e2e asserting the loop
anchors at the head (loopStart "0"), and the existing forward "loops from tail"
e2e still green (12/12 altitude-lock).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 07:16:47 -07:00
BenStullsBets 21830069ad claim human-experience-filter-art session 0028 (placeholder) + sessions.json entry 2026-06-30 06:56:42 -07:00
BenStullsBets 50366c056e add sessions/0027/SESSION-0027.0-TRANSCRIPT-2026-06-29T22-04--2026-06-30T06-47.md + replace placeholder/variant SESSION-0027.0-TRANSCRIPT-2026-06-29T22-04--INPROGRESS.md 2026-06-30 06:49:13 -07:00
BenStullsBets 934f60cbd2 Merge feat/left-brain-annotations: factual labels + localization for 28 pool clips 2026-06-30 06:44:37 -07:00
BenStullsBets 7995ba4069 feat(content): left-brain factual labels for the 28 pool clips that lacked them
Every rotating-pool clip shipped with right-brain `affect` (auto-generated per
scale by _affect_for_clip) but only 13 carried LEFT-brain factual `LABELS`. The
other 28 rendered with no detected.*/measure.* labels at any Left knob level.

Authors static tiered labels (general → specific → scientific → +fact) for all
28 — 5 cosmos, 4 orbit, 8 sky, 5 coast, 2 reef, 4 abyss — in build_pool_manifest.py
following the existing static_label pattern, plus their es/fr/ja translations in
tools/i18n/catalogs (binomials/measurements kept verbatim; units localized per
each catalog's convention). Rebuilds the manifest and merges all four languages.

Result: 41/41 clips now have left+right annotations with full en/es/fr/ja parity.

Labels are static (footage not eyeballed); the abyss/reef creature clips could
later be upgraded to tracked labels via author mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 06:44:36 -07:00
BenStullsBets 7bd6c9a57b Merge fix/boot-silent-no-autostart: boot silent, no un-gestured auto-start 2026-06-30 06:32:12 -07:00
BenStullsBets e5fe00e948 fix(sim): boot silent (video off, audio 0) — no un-gestured auto-start
The post-load autoStart() flipped Video on and lifted audio to 3/10 with no
user gesture. Browser autoplay policy blocks an audio play() made outside a
gesture (muted video survives, sound does not), so auto-start showed video but
stayed silent. Boot SILENT instead and wait for the operator to turn Video on —
that click IS the gesture, and its handler couples audio to 3/10 and starts the
soundtrack in-gesture, so sound reliably comes up with video.

Updates the pytest e2e + Playwright e2e specs to assert boot-silent + the
video-on-couples-audio wiring instead of the old auto-start wiring.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 06:31:38 -07:00
BenStullsBets 020219f9a6 Merge remote-tracking branch 'origin/main' 2026-06-30 06:30:55 -07:00
BenStullsBets 534c54fe12 claim human-experience-filter-art session 0027 (placeholder) + sessions.json entry 2026-06-29 22:14:22 -07:00
BenStullsBets 5beee55119 Merge fix/video-loop-ended-safety-net: base loop never freezes
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 21:52:55 -07:00
BenStullsBets ec660c4880 fix(sim): add ended safety net so the base loop never freezes
Root cause: the steady-state loop (#vid-loop) restarts only via a narrow
timeupdate window (last 0.08s -> seek to LOOP_TAIL_S), with native loop off.
Headless Chromium reliably lands a final timeupdate in that window, but under
real-browser decode/GPU load a timeupdate can skip past it; the clip then fires
'ended' and freezes on its last frame ('videos aren't looping'). There was no
recovery path. Add an 'ended' listener that seeks back to the tail and resumes,
guaranteeing the loop regardless of the timeupdate race. No change to normal
playback. Proven by a Playwright regression test that forces the ended state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 21:52:50 -07:00
BenStullsBets a2d8179507 Merge feat/localization-language-dropdown: language dropdown (en/es/fr/ja)
Localizes simulator UI controls + Left/Right annotations with a live
language dropdown. English default, session-only. 41 clips × 4 langs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 21:26:26 -07:00
BenStullsBets 7090d3a836 test(i18n): e2e for language dropdown chrome + reset on reload
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 18:55:36 -07:00
BenStullsBets 8df7859c5a feat(i18n): author es/fr/ja annotation translations + merge into manifest
41 clips, 753 string-variants per language, structure-validated (key-sets
and tier-counts identical to en). Escalation semantics preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 18:54:37 -07:00
BenStullsBets dcdc3d62f7 feat(i18n): manifest translation extract/merge tool + en catalog
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 18:49:52 -07:00
BenStullsBets 1977e679ee feat(i18n): live language switching for chrome + annotations
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 18:48:48 -07:00
BenStullsBets 49b77e0700 feat(i18n): tag controls with data-i18n + add language dropdown
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 18:47:03 -07:00