Commit Graph

38 Commits

Author SHA1 Message Date
type-two
915f653525 [m9-B] snap-to-beat + FILM shot-list panel
Beat grid consumed from Lane C's /beats: one top-level scene.beatGrid (beats
kept in FILE seconds, effective offset re-derived from the audio clip so
dragging the audio drags the grid and head-trim leaves it put). beat and bar
join the snap selector, so keys, clip and audio blocks, both trim edges, cuts
and the playhead all became musical for free. Grid drawn faint on beats and
brighter on downbeats, suppressed below ~7px spacing. Low confidence still
snaps but says so. Tempo extrapolates past the analysed range.

FILM panel mounts inside #timeline: sequence CRUD, editable shot rows (scene
dropdown, in/out, cut|dissolve + seconds, reorder, delete), live runtime, size
picker, and Render film with per-shot/per-frame progress - gated behind
hasContent() + a confirm that says plainly every shot replaces the open scene,
and locked while running so it cannot be started twice. film.js imported
lazily so its absence cannot stop the timeline mounting.

Verified live: grid 128.02 BPM/35 beats off house128.wav, snapped positions
8-10ms from the true 0.468735s grid across the whole file, bar snap hits a
downbeat, grid survives toJSON->load; FILM lists both sequences with the
server's 11.50s runtime and 3 editable rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 00:32:39 +10:00
type-two
e05a1d2946 [m9-C] beat detection + audio trim at mux
/beats?path= returns bpm, a phase-locked beat grid, downbeats (4/4) and an
honest confidence, using only stdlib + ffmpeg: decode to PCM, log-energy onset
flux, harmonic-enhanced autocorrelation with an octave check, comb phase
search, weighted LS grid refit. 4-min track analyses in ~0.5s, cached after.

Audio in/out now honoured at mux in both scene renders and film beds via one
resolve_audio() path. Found en route: on this ffmpeg build the obvious
atrim/asetpts + amix/apad chain yields an aac stream with ZERO samples, rc=0,
no warning - trim moved to the input side; and the test helper would have
passed on that empty stream, so it now refuses to answer on a window that
decodes nothing.

Tempo bias fixed after orchestrator ground-truth check: decoding at 22050
discarded the HF transient, and since the attack's HF share grew across a file
the surviving LF onset landed progressively later - a per-beat-varying
lateness that reads as tempo error. BEAT_SR now 44100/512 (same 86fps
envelope). Second bug found by sweeping: harmonics outside the lag table
counted as zero and the parabola was fitted on a slope, so 118 BPM read
114.79.

Verified vs independent ground truth: 120.030 vs 120.003 true, 128.020 vs
128.004; worst offset from the true grid 6.0/10.1ms over the whole file (was
45/52ms). Tests now assert whole-file DRIFT, not average BPM, incl. a 200s
track and hftick.wav (16kHz tick over a 60Hz body) which pins BEAT_SR.
18 groups green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 00:13:40 +10:00
type-two
b773d29776 [m8-C] multi-shot sequencing — a film is now a first-class object
Sequences (ordered shots = scene + in/out + transition) with full validation,
CRUD, and a client-driven film render the server orchestrates: browser renders
each shot through the existing /render/* endpoints, server ffmpeg-concatenates
in order with cuts or xfade dissolves, muxes a film-level audio bed, reaps
after 48h. render.js refactored so there is one frame-post path in the repo
(captureFrames/withRenderSize/begin/end/poll); new web/film.js drives the loop.
Also fixes a pre-existing mux bug: no apad meant short audio truncated PICTURE.

Orchestrator: deploy compose/script now carry SCENEGOD_SEQUENCES + SCENEGOD_FILMS
volumes so shot lists survive a rebuild.

Verified live: demo-two-shot renders one 11.500s mp4 with a genuinely
compositing dissolve; a film built from the M7 templates (street wide ->
dissolve -> closer -> cut -> record store) renders 11.800s at 960x540.
15 test groups green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 23:21:45 +10:00
type-two
7420a4ecdb docs: note that prod asset sync is separate from deploy.sh 2026-07-25 22:52:55 +10:00
type-two
16eb96a60e [m7-B] New-from-template picker + ? shortcuts overlay (M7 complete)
Lane B: New... in the scene bar fetches the templates API and applies through
a new single apply path (applyScene = stage.applyState + load + preload, now
shared with Load), confirm-before-clobber via hasContent(), thumbs, toasts on
failure. ? overlay documents 24 real bindings read out of the code across
STAGE/TIMELINE/SCENE BAR, ignores ? while typing. Audio head-trim (left edge
eats into the source, one grouped undo). applyScene strips the template key so
saved scenes stay plain scene JSON.

Orchestrator SYNC-9 fix: help overlay dt column was auto-width + nowrap, so
long terms overflowed and the three columns overlapped visually.

Verified live: picker lists 3 (2 thumbs), no confirm when empty / confirms when
dirty, loads 7 entities + 2 cuts with bar refresh, no template key in toJSON,
overlay opens on ? and Escape closes, no hijack while typing. 5 suites green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 22:47:29 +10:00
type-two
586a3ecbd2 [m7-C] starter templates: read-only API + three working scenes
templates/ ships with the repo (two-hander-golden-hour, record-store,
empty-stage), GET /templates + /templates/{name} behind the same slug and
path-traversal guards as /scenes; presentation metadata rides in a template
key that validate_scene ignores and toJSON drops, so loading then saving
yields plain scene JSON.

Test group asserts the three list in order, round-trip, traversal/unknown
rejected, write verbs refused, and every template passes validate_scene with
every referenced asset present on disk - the check that stops a rotten
template shipping. 10 groups green.

Verified live: two-hander loads 7 entities, 8s, both performers animating,
cut fires cam1 to cam2 at 4.5s; other two load clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 22:34:53 +10:00
type-two
7fea880286 [m7-A] non-mixamo rig support + world-integrated backdrops (+4 sync7 render fixes)
Lane A: bone-alias layer so CC_Base_*/Unreal-HumanIK/Rigify/Rokoko/Biped rigs
fold onto the canonical mixamo names — lady.glb pairs 52/102 bones and
retargets (53 tracks) where she previously threw 'missing hips'; mixamo→mixamo
pairing asserted bit-identical. Backdrops gain frustum auto-fit, floor tint
sampled from the plate, and FogExp2 in the plate's colour via a keyable
param. Also caught: _buildLight ran _refreshDefaults before registration, so
scene lights never dimmed the built-in hemi (every preset was washed out).

Orchestrator SYNC-7 fixes (found only by rendering actual frames):
- editor chrome (gizmo/grid/helpers/cam proxies) was baked into output mp4s
- timeline's per-tick rest transform dragged fitted plates out of frame
- fit used the viewport aspect, output renders at its own → letterboxed plates
- plate offset put its top edge on the lens axis → void above centre-frame;
  now params.fitAnchor (default 0.75) picks the row that sits at eye level

Verified: 5 suites green; 1280x720 render shows a CC character dancing a mixamo
clip against an edge-to-edge Flow plate with a horizon and matched ground.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 22:21:37 +10:00
type-two
fe3d5404a9 plan: M7 (rig aliases, world backdrops, templates) + sequencing/beat-sync backlog 2026-07-25 21:42:50 +10:00
type-two
a785de41ae fix: surface clip/direct failures instead of swallowing them
Dropping a clip on a non-mixamo rig (lady.glb is a Character Creator export,
CC_Base_* bones) threw inside a fire-and-forget event handler — the rejected
promise vanished and the drop appeared to do nothing at all. Timeline._fail
now logs + toasts via a shared window.sgToast that tlui installs; clipdrop
and direct handlers route through it.

Repro before: two clipdrops fired, only the mixamo one landed, no error.
After: 'clip: retarget: missing hips (mixamorig skeleton?)' toast.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 21:03:21 +10:00
type-two
128e24a7cb [deploy] digalot.fyi hosting: prefix-agnostic frontend, ship-check hardening, docker+nginx kit
- web: all URLs relative (imports ./web/..., fetches assets/...) so the app
  serves at / locally and under /scenegod/ behind nginx; esc() on dock
  innerHTML interpolations (ship-check XSS class)
- server: capped_body on every POST (scenes 5MB, frames 25MB, meta/director
  256KB-1MB), render begin sanity (fps<=60, dims<=4096, frame idx<=20000)
- deploy/: Dockerfile (slim+ffmpeg), compose (project 'scenegod' — avoids
  the shared-'deploy'-project orphan trap with meshgod), nginx location
  (suite basic-auth), deploy.sh (rsync model, meshgod-style)
- live: digalot.fyi/scenegod/ -> 401 unauthed, app healthy on dealgod_default

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 02:43:26 +10:00
type-two
20af1b20c2 [m5+m6] DIRECTOR MODE + FLOW VIDEO PLATES — all lanes + sync6 seam fixes
Lane A: grammar.js (shot/angle/focal/light/mark data), presets.js (bbox shot
solver, light/mark appliers, DIRECT bar UI, NL box), stage video plates
(VideoTexture plane/corner/dome, screen kind, syncVideos), dock video cards.
Lane B: scenegod:direct → grouped-undo keys/cuts, 🗣 lip-sync from audio via
/rhubarb, Cmd+wheel zoom / wheel scroll, audio trim, video clock-follow via
nullable entityVideo seam. Lane C: POST /director (LLM proxy, server-side op
validation vs vocab+scene, mock-tested), render.js awaits stage.syncVideos.
Sync6 (orchestrator): VIDEO_EXTS in server tree/media types (video primary,
img poster as thumb), stage.entityVideo accessor, syncVideos forces
videoTex.needsUpdate (rVFC never fires in hidden tabs → black plates).

Verified: 9 server groups + timeline + presets suites green; browser SYNC:
one-click MCU framing lands camera+fov keys+cut (single undo), golden_hour
relight keys, plate scrub-follow frame-exact, finalRender mp4 shows the Flow
golden-hour street plate behind an animated character.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:51:28 +10:00
type-two
86b76f6bdc [m6] Flow video plates spec + flow_intake.py; M6 queued behind M5
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 01:25:14 +10:00
type-two
a8273127ba [m5] DIRECTOR MODE round: cinema-grammar presets spec + /director LLM lane; rhubarb live
- PLAN §5 M5: shot sizes/angles/focals/light presets/blocking marks as
  one-click deterministic presets (grammar.js data + presets.js solver,
  Lane A), keyed at the playhead via scenegod:direct events (Lane B),
  optional NL box -> POST /director -> tailnet Ollama with server-side op
  validation (Lane C; m4pro serves qwen2.5:7b)
- rhubarb 1.14.0 built from John's source zip (JING5), installed arm64 at
  ~/.local/opt/rhubarb, symlinked /opt/homebrew/bin; happy path verified:
  /rhubarb on a real spoken wav (assets/audio/test/vo.wav) returns mouthCues
- lane B unlocked to wire the lip-sync-from-audio button now

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 01:09:30 +10:00
type-two
866551f266 [sync5] viseme matcher side-suffix fix, v1 complete: README, git-hygiene rule
- stage.js detectVisemes: NONMOUTH blocklist + side-stripped matching —
  /(^|_)l($|_)/ was swallowing every ARKit left-side morph (eyeblink_l,
  browdown_l), so setMorph('L') would blink the eye; self-check hardened to
  assert group contents (L === tongue only, smile lands on E)
- CLAUDE.md: lanes stage own files explicitly, never git add -A (laneB's
  attribution finding)
- README.md: run + feature summary; lane files: session 4 accepted for all,
  v1 declared complete, optional backlog recorded

Verified: node + 9 server test groups green, /rhubarb 503 gate live,
audio lane (tone.wav block, gain) + cut markers + sun keys on screen,
zero console errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 21:33:53 +10:00
type-two
252a6f666c [laneB] log: M4-B session 4 (audio + visemes)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 21:23:57 +10:00
type-two
df4e06f047 [laneA] M4-A visemes + applyState source:none fix
- stage: detectVisemes() maps CC/ARKit/plain morph names -> canonical
  A/E/I/O/U/MBP/FV/L at character load; visemeTargets(id) + setMorph(id,name,w)
  for the timeline to drive lip sync; visemeSelfCheck() asserts the matcher
- dock: inspector 🗣 badge (greyed when a rig has no mouth morphs)
- stage: camera/light default source:{type:'none'} (PLAN §4.1); applyState
  skips only upload mesh/backdrop bytes -> rig survives save/load (SYNC4)

Verified live at :8020: selfcheck ok=[A,FV,L,MBP,U]; save/load keeps cam+light.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 21:19:07 +10:00
type-two
ba29d0c6d6 [laneB] M4-B model: audio mutators, morphs track eval, importRhubarb
- timeline.js: morphs track kind evaluated via stage.setMorph (lerp per viseme
  name), audio mutators addAudio/moveAudio/setAudioGain/removeAudio (undo-able),
  importRhubarb(id, json, t0) mapping mouthCues → morph keys (per-cue ramp),
  load() ensures scene.audio[], addKey keyed by (t,name) for morphs too.
- stagestub.js: setMorph (records) + visemeTargets seam.
- timeline_test.mjs: morph lerp, rhubarb keys, audio mutators + undo round-trip.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 21:13:35 +10:00
type-two
355ad2fe33 [laneC] log: rhubarb endpoint
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 21:10:54 +10:00
type-two
fb59601cc3 [laneC] /rhubarb viseme endpoint (ffmpeg-style 503 gating)
GET /rhubarb?path= -> rhubarb -f json -> mouthCues for Lane A's viseme lane;
503 when the binary is absent (not installed on ultra yet). Path-guarded.
test_server.py: +503 gating assert (9 groups green).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 21:10:38 +10:00
type-two
a15612eed0 [sync4] applyState camera/light hotfix, M4 verified (audio mux + graft), viseme/audio-UI round unlocked
- stage.js: applyState no longer skips camera/light entities (source
  'upload' but no bytes needed) — loaded scenes were silently losing the
  directing rig; PLAN schema adds source.type 'none' for the clean fix
- lane files: A session-3 + C M4 accepted; A→absorb hotfix + viseme lane
  (M4-A), B→audio rows + Web Audio sync + morphs track (M4-B), C complete

Verified: 6/6 entities restored on scene load, PiP 178x99 capped+visible,
lady 26/26 textured, audio-muxed render h264+aac 8.000s both streams,
graft_limb --selftest green under Blender 5.0.1, node+server suites green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 21:04:09 +10:00
type-two
04e15d3e39 [laneA] M3-polish: PiP cap+swap, spec-gloss GLB fix (lady renders textured)
- PiP: sized in _resize (~24% w, 16:9, bottom-right) — CSS aspect-ratio is
  unreliable on <canvas> and had ballooned it. Click-to-swap main<->active cam;
  _render(cam,canvas) restores aspect after a pip blit so swap never distorts.
  renderActiveCamera/pause/resume contract unchanged (M3 finalRender safe).
- room3d: SpecGlossPlugin on GLTFLoader — CC/Reallusion GLBs use
  KHR_materials_pbrSpecularGlossiness (dropped in three r160) and rendered
  flat white; map spec-gloss -> MeshStandard. lady.glb now withMap=26/26.

Verified live at :8020, zero console errors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 20:49:22 +10:00
type-two
c602ce8dea [laneC] log: M4 done
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 20:47:52 +10:00
type-two
be65355f6b [laneC] M4: audio mux, graft_limb.py, MODELBEAST proxy
- render/end muxes scene audio[] (adelay+volume+amix, aac, -shortest);
  audio paths path-guarded; render.js finalRender passes scene audio
- scripts/graft_limb.py: headless Blender limb graft (align root->target bone,
  join armatures, parent-keep-offset, join meshes; refusal exit 2 on mitten
  overlap; drops phantom importer icosphere; no finish/decimate). --selftest
  builds synthetic fixtures + verifies both paths under Blender
- /mb/submit MODELBEAST proxy (SCENEGOD_MB=1 gated, token from ~/Documents/
  backnforth/.env at request time never logged, /api/assets->/api/jobs)
- test_server.py: +audio-stream, +MB gating & two-step contract (mock MB);
  8 groups green

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 20:47:31 +10:00
type-two
65b7757fd2 [laneB] log: M2 session 3
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 20:41:59 +10:00
type-two
6200f945b0 [laneB] M2 session 3: absorb _mirror + tests, setDuration, snap-step, param capture
- stagestub.js: removeEntity now fires onChange WITH the removed entity (real
  Stage contract) so _mirror's getEntity(id)===null removal path is testable.
- timeline.js: setDuration(x) — clamps the playhead, keeps out-of-range keys
  (lossless), notifies UI via onLoad. (Reviewed orchestrator's _mirror seam.)
- tlui.js: dur field → setDuration; snap-increment select (frame/0.1/0.25/1s);
  keyable-param add via dblclick on a params lane (captures current param
  values, skips 'type'/bool descriptors); params lane only when keys exist or
  kind is camera/light (no empty lane for characters).
- timeline_test.mjs: _mirror lifecycle (add→keyframe→remove) + setDuration.

Verified in browser (throwaway harness, not committed): dock-adds mirror into
rows, param capture, setDuration clamp+bar refresh, snap-step math.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 20:41:39 +10:00
type-two
41f2c9fa8d [sync2+3] entity mirror seam, SYNC 2 demo + 1080p mp4 verified, M4/polish unlocked
- timeline.js: _mirror — Timeline subscribes stage.onChange, upserts/removes
  live stage entities into scene.entities (closes the dock-added-entity gap
  both lanes flagged; keyframing now works without a scene load)
- lane files: M2/M3 accepted for all lanes; A→PiP sizing + lady.glb material
  polish, B→absorb _mirror + test + setDuration mutator, C→M4 (audio mux,
  graft_limb, MB proxy)

SYNC 2: lady+man+street+2cams+sunset built via live dock path, all
frame-exact (probes in transcript). SYNC 3: finalRender → 240 frames
1920x1080 → ffmpeg h264 8.000s, cuts + sunset verified in extracted frames.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 20:34:32 +10:00
type-two
dfd4cebd2b [laneA] M2: PiP + cameras/lights toolbar, eventized clip drops, onChange-on-add
- dock: /assets/tree {name,path,formats,thumb}; +camera/+key/+ambient toolbar;
  clip apply dispatches scenegod:clipdrop when timeline present (else preview);
  drag-from-dock places onto the viewport (screenToFloor / entityAt)
- stage: PiP overlay from active camera; renderActiveCamera hardened as M3 target
  + pause()/resume(); backdrop rebuild-on-param; fire onChange on add+remove
  (fixes tlui empty-names + a gizmo mouseUp id-vs-entity bug); set-active-camera
- verified in the integrated app (:8020, Lane B timeline mounted), zero console
  errors. Found + logged the entity-mirror integration gap (timeline model isn't
  seeded from live stage adds) for the orchestrator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 20:14:52 +10:00
type-two
bbb3f99016 [laneB] log: M2 session 2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 20:11:03 +10:00
type-two
5d9950211c [laneB] M2: SYNC1 row/bar fixes, clipdrop, crossfade, params/cut lanes, box-select
- timeline.js: onLoad hook (rebuilds rows + refreshes scene bar after load()),
  scenegod:clipdrop handler (addClipDrop: block at playhead, out=clip duration,
  auto 0.25 fade where it abuts a neighbour), crossfade now fade-IN + fade-OUT
  (pre-roll pulls successor start earlier for continuous local time).
- tlui.js: reads tracks from the TIMELINE (authoritative) not the stage copy —
  fixes post-load addKey/clipdrop not appearing. Params lanes (per-key color),
  camera-cut lane add via dblclick, snap toggle, box-select + multi-drag +
  Delete, selection highlight, fade wedge on blocks, grow-to-content height.
- timeline_test.mjs: + onLoad, addClipDrop duration, crossfade weight sum=1.

Verified in browser (throwaway harness, not committed): SYNC #3a/#3b repro
fixed, both clip blocks render, clipdrop/add-key show immediately, cut-add and
box-select multi-drag work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 20:10:07 +10:00
type-two
24b7b4d4b7 [laneC] log: M2/M3 render pipeline done
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 20:00:42 +10:00
type-two
b627a0f2ff [laneC] M2/M3: render pipeline (ffmpeg encode + render.js), thumbnails endpoint, reaper
- /render/begin|frame|end|status|out with background ffmpeg thread + on-disk
  status fallback; boot-time reap of dirs >48h
- /assets/thumb sidecar lookup (pass-through, no resize dep)
- web/render.js: draftRecord (MediaRecorder) + finalRender (deterministic
  timeline.step -> PNG -> POST, <=4 in flight) + download helper
- test_server.py: +render pipeline (ffmpeg synthetic frames -> mp4, ffprobe
  ~1s) +thumbnail; 6 groups green

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 20:00:14 +10:00
type-two
1f8ad13ed6 [sync1] wire TimelineUI to real Stage, null-mixer guard, M2 unlock + orchestrator updates
- index.html: mount TimelineUI, setMixerAuto(false) when timeline present
- timeline.js: _evalClips guards no-blocks / null-mixer (real Stage returns
  null for non-characters; the stub never did)
- tldev.html removed; stagestub.js kept as the node-test fixture
- lane files: M1 accepted for all three, answers to open questions, M2
  priorities (A: clipdrop event, PiP; B: row/scene-bar refresh; C: straight
  into M3 render pipeline)
- SCENEGOD launch.json (informational; preview uses MESHGOD's entry)

SYNC 1 verified in-browser: character+backdrop+retargeted clip scene loads,
scrub interpolates frame-exact, scene save/load round-trips via server.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 18:36:16 +10:00
type-two
de3416869f [laneA] M1: Stage viewport, retarget port, dock+inspector
- room3d.js: pure-module port of rigroom retarget, target parameterized;
  _selftest bakes across two mismatched-rest skeletons (maxErr 6.4e-12)
- stage.js: Stage class (IBL, gizmo, entities, clip bake cache) per PLAN §4.2
- dock.js: asset dock + inspector, offline file-drop + clip retarget
- index.html/style.css: grid layout, guarded timeline import (page survives B absent)

Verified in-browser end-to-end: Mixamo char loaded+normalized, backdrop plane,
gizmo move, running clip retargeted (66 tracks) and animating live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 18:06:22 +10:00
type-two
b20354c53f [laneB] log: M1 session 1
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 17:57:52 +10:00
type-two
45debd3c10 [laneB] M1: timeline model+clock, panel UI, stub, self-check
- timeline.js: headless clock/tracks/evaluate (transform slerp, param lerp,
  clip blocks with fade-out, camera cuts, undo stack). No three/DOM import.
- tlui.js: canvas panel — ruler scrub, playhead, key add/drag/delete, clip
  blocks, camera cuts, save/load with localStorage fallback. Injects own CSS.
- stagestub.js: dev Stage per PLAN §4.2 (deleted at SYNC 1).
- timeline_test.mjs: node self-check, all assertions pass.
- tldev.html: dev-only browser harness (deleted at SYNC 1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 17:57:18 +10:00
type-two
f6292f2c84 [laneC] log: M1 done
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 17:54:31 +10:00
type-two
bb85eef040 [laneC] M1 server: statics, assets tree/file, scenes CRUD+validation, render stubs, tests
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 17:53:52 +10:00
type-two
e29edc808b plan: SCENEGOD master plan + 3-lane instructions + log scaffolding
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 17:44:07 +10:00