Author corrections without baking them into motion

Constant, ramp, and return offsets now append ordinary channel layers to a lane or cel in explicit owner frames. The inspector exposes the commands as one undoable transaction, shows conflicts, and offers removal or retry while preserving generated bases through regeneration.

Ordered-stack compatibility is shared by validation, conflict reporting, and regeneration, including adjacent replacement coverage. Cel-sheet gaps and headers select their lane, so commands cannot fall through to another column's stale selection.

437 tests, 5,804 assertions; both browser flows; 56 Django tests; optimized frontend build.
This commit is contained in:
Your Name 2026-09-30 22:00:48 -04:00
parent 3dbbe285fc
commit 815ce449ea
14 changed files with 806 additions and 60 deletions

View file

@ -1,10 +1,11 @@
# Lane and cel handoff
Status (2026-09-30): the lane model is implemented through its commands and its
first two views. Cels are ordinary nodes with their own playback clock, the
timeline draws them as one row, the cel sheet draws frames down and lanes across,
and both views issue the same commands. Correction layers evaluate and survive
regeneration. What is missing is the commands that make a correction.
Status (2026-09-30): the lane model is implemented through its commands, its
first two views, and correction authoring. Cels are ordinary nodes with their
own playback clock; the timeline draws them as one row and the cel sheet draws
frames down and lanes across. Both views issue the same commands. Rotation and
position corrections can be authored as Constant, Ramp, or Return motion on a
lane or cel, survive regeneration, and expose conflicts for removal or retry.
The commits beginning at `3d3c1bb` are the argument for the model and are worth
reading before touching what they did — they are the design record, more than
@ -87,18 +88,18 @@ decision, not a cleanup.
## Next steps, in order
1. **The commands that make a correction** — Constant adjustment, Ramp, Return
motion over a selected range, per `lane-model.md`. The evaluator is done and
has no opinion about how a range or a motion shape is chosen, which is now a
view question. A panel also needs to offer `clip/conflicts` for resolution.
Note the one open question: a correction needs a stable `:id` from somewhere,
and cel ids come from the caller because this namespace is pure.
2. **Slip source and retime.** Both have real design questions open and the doc
The implemented correction slice and its remaining UI limits are recorded in
[Correction authoring](correction-authoring-plan.md).
1. **Slip source and retime.** Both have real design questions open and the doc
says to refuse rather than approximate: retime needs a defined warp and
interpolation behaviour, and is not moving keys whose numbers happen to fall
inside a selection.
3. **Deleting reused content.** Reference discovery exists (`node/sources`,
2. **Deleting reused content.** Reference discovery exists (`node/sources`,
`clip/places`, `clip/contains-symbol?`); the policy does not.
3. **Displayed-range correction gestures.** The first correction panel asks for
explicit owner frames. Dragging a range in a retimed/nested view still needs
a proved mapping; do not make it snap through floors or loops.
4. **Collaboration.** `lane-model.md` is explicit that one leaf per channel does
NOT solve two people editing different keys of the same channel. No conflict
policy exists for that.
@ -126,6 +127,12 @@ decision, not a cleanup.
- **Generated sampling applies to the base, not the hand correction.** Picture
rate and pose selection may choose an earlier generated frame; correction
support and values still read the node's current authored frame.
- **Correction commands live in `domain/correction.cljs`.** IDs come from the
event caller; the pure command materializes default transform channels,
appends one layer, and validates the complete document. The inspector authors
rotation and position offsets in explicit owner frames. One Apply is one undo
step. `channel/reconcile` is the shared ordered-stack compatibility rule used
by validation, conflict reporting, and regeneration.
- **Two test patterns worth copying.** `the-cursor-agrees-with-the-specification-in-any-frame-order`
holds the optimized cursor to `value-at` in forward, backward and random order
— add a case to it for any new channel shape. And `drawn` in `lane_test`
@ -165,7 +172,7 @@ decision, not a cleanup.
From `frontend/`:
npx shadow-cljs compile test && node out/node-tests.js # 429 tests, 5,767 assertions
npx shadow-cljs compile test && node out/node-tests.js # 437 tests, 5,804 assertions
npx shadow-cljs compile app # the bundle Django serves
npx shadow-cljs release app # then `compile app` again — see above