Reuse, duplicate and make unique: deciding what is shared

The model's whole claim is that content and its occurrences are different
things, and until now nothing in the editor could tell them apart: you could
make a drawing and time it, but not expose one drawing twice, and so never find
out whether an edit arrives in two places. That is the first proof obligation in
the lane model and it was the one the commands could not reach.

Three commands, and the distinctions between them are the point:

  reuse       another occurrence of the same drawing. A decision to share,
              made on purpose, because sharing discovered later — when an edit
              turns up somewhere you did not expect — is the bad version.
  duplicate   a copy of the drawing, appended, for when what is on screen is
              the starting point for the next one.
  make unique this occurrence gets a private copy; the others keep sharing.
              The undo of reuse, and refused when nothing else uses the
              drawing: a copy nobody asked for is a second identical symbol in
              the library for no reason a person could see.

Duplicate copies the CONTENT and not the exposure. Its new occurrence is a plain
one-frame hold, not a copy of the source occurrence's transform or corrections,
because those belong to that use of the drawing — carrying them over would make
duplicating a drawing quietly duplicate the treatment of one exposure of it.

A copy is SHALLOW by default and keeps its references to other symbols, so a
head built out of reusable eyes still uses those eyes. `:deep? true` copies
everything it places with new ids throughout. The lane model asks for both and
says why: never promise decoupling while leaving the edited object shared, and
only the deep copy can keep that promise. `bring/symbols` already did the
reachability walk and the id remapping, so the deep copy is that function
pointed at its own clip.

`node/sources` was still being read as a SET at five call sites, each with a
comment about a lane that cuts between several drawings — the keyed source that
no longer exists. An occurrence names one symbol, so they now ask `node/source`,
and `placed-frame` answers with `:symbol` rather than `:of`, which was the last
echo of the retired field name.

To let the commands use `clip/free-id` and the copy machinery, the lane's own
validation moved from `domain/sequence` to `domain/symbol`, which is where it
belonged anyway: a sequence is the one composition rule a node map carries, and
it now sits beside the parent and stencil checks rather than in the namespace
that happens to build lanes. That also breaks the cycle — sequence can require
clip and bring, and nothing below it requires sequence. Preconditions still
check only the LANE's shape: refusing an exposure edit over an unrelated defect
elsewhere in the symbol would be this command answering for a part of the
document it never touches.

The cel strip gains reuse, duplicate and make unique, the last shown only where
the selected exposure actually shares its drawing. Drawing on twos is also now
under test: exposure length is the cadence, the lane's transform has its own
clock, and it still moves on every frame — stepping it would be the cel cadence
leaking into continuous motion.

397 tests, 5,561 assertions. `test/browser/sequence.mjs` drives the three new
commands through the real editor and checks that three exposures are still one
row.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-09-30 15:30:59 -04:00
parent 3d3c1bbca0
commit 9446829774
7 changed files with 427 additions and 83 deletions

View file

@ -1,9 +1,10 @@
# The Lane Model
Revised 2026-09-30. Target design. Occurrence ownership, source playback, the
first exposure commands and a one-row cel strip are implemented; correction
layers, the other commands and the remaining views are not. See the status note
under [Proof obligations](#proof-obligations-and-implementation-order).
content and exposure commands and a one-row cel strip are implemented;
correction layers, the range and retiming commands, and the remaining views are
not. See the status note under
[Proof obligations](#proof-obligations-and-implementation-order).
This revises the Claude artifact [The Lane Model](https://claude.ai/code/artifact/cd42981d-ed08-493f-94df-b7dd6657f0e6).
Its prose and diagram source were recovered from session
@ -439,18 +440,25 @@ drawing remain distinct events. Deduplicating identical ghosts is a display opti
The source-channel prototype has been removed: an occurrence names one symbol
and carries its own playback clock, and `node/problems` rejects the old
`[:source]` channel. `arthur.domain.sequence` holds lane validation and the
first three commands — add lane, append drawing, extend hold with explicit
ripple and shot-length policy — each one history step. The timeline draws a
lane's occurrences as cel blocks on the lane's own row.
`[:source]` channel. What a lane IS lives in `arthur.domain.symbol` beside the
other rules about a node map; `arthur.domain.sequence` holds the commands over
one — add lane, append drawing, reuse drawing, duplicate drawing, make unique,
and extend hold with explicit ripple and shot-length policy. Each is one history
step, and each refuses rather than half-applying. The timeline draws a lane's
occurrences as cel blocks on the lane's own row, and offers Make unique only
where the selected exposure actually shares its drawing.
Correction layers, the remaining commands (reuse, duplicate, make unique, blank,
split, trim, slip, retime) and the exposure-sheet view are not implemented; a
refusal is the current behavior where the model demands an explicit choice
nobody has made yet. The suite stands at 392 tests and 5,525 assertions, with
Content copies are shallow by default and keep their references to other
symbols; `:deep? true` is the explicit copy that shares nothing, so the promise
of independence is only made where it is kept.
Correction layers, the range and retiming commands (blank, split, trim, move,
slip source, retime) and the exposure-sheet view are not implemented; a refusal
is the current behavior where the model demands an explicit choice nobody has
made yet. The suite stands at 397 tests and 5,561 assertions, with
`frontend/test/browser/sequence.mjs` driving the editor through create, hold,
overflow and undo. Rewrite tests that encode superseded behavior rather than
preserving behavior to keep them green.
overflow, undo, reuse, make unique and duplicate. Rewrite tests that encode
superseded behavior rather than preserving behavior to keep them green.
Build small adversarial documents and test their domain operations before
expanding the interface: