videobook-engine 2.0.1 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/README.md +99 -68
  2. package/dist/artifacts.d.ts +4 -9
  3. package/dist/artifacts.d.ts.map +1 -1
  4. package/dist/artifacts.js +78 -138
  5. package/dist/artifacts.js.map +1 -1
  6. package/dist/books.d.ts +1 -1
  7. package/dist/books.d.ts.map +1 -1
  8. package/dist/books.js +18 -10
  9. package/dist/books.js.map +1 -1
  10. package/dist/cas.d.ts +27 -1
  11. package/dist/cas.d.ts.map +1 -1
  12. package/dist/cas.js +78 -6
  13. package/dist/cas.js.map +1 -1
  14. package/dist/catalog-metadata.d.ts +56 -0
  15. package/dist/catalog-metadata.d.ts.map +1 -0
  16. package/dist/catalog-metadata.js +42 -0
  17. package/dist/catalog-metadata.js.map +1 -0
  18. package/dist/communications.js +4 -2
  19. package/dist/communications.js.map +1 -1
  20. package/dist/context.d.ts +9 -6
  21. package/dist/context.d.ts.map +1 -1
  22. package/dist/context.js +42 -51
  23. package/dist/context.js.map +1 -1
  24. package/dist/domain.d.ts +39 -2
  25. package/dist/domain.d.ts.map +1 -1
  26. package/dist/domain.js +846 -64
  27. package/dist/domain.js.map +1 -1
  28. package/dist/edit-policy.d.ts +3 -0
  29. package/dist/edit-policy.d.ts.map +1 -0
  30. package/dist/edit-policy.js +6 -0
  31. package/dist/edit-policy.js.map +1 -0
  32. package/dist/edits.d.ts +10 -0
  33. package/dist/edits.d.ts.map +1 -0
  34. package/dist/edits.js +1392 -0
  35. package/dist/edits.js.map +1 -0
  36. package/dist/engine-types.d.ts +171 -86
  37. package/dist/engine-types.d.ts.map +1 -1
  38. package/dist/engine-types.js +32 -0
  39. package/dist/engine-types.js.map +1 -1
  40. package/dist/engine.d.ts +89 -26
  41. package/dist/engine.d.ts.map +1 -1
  42. package/dist/engine.js +27 -21
  43. package/dist/engine.js.map +1 -1
  44. package/dist/files.d.ts.map +1 -1
  45. package/dist/files.js +32 -28
  46. package/dist/files.js.map +1 -1
  47. package/dist/fork.d.ts +148 -0
  48. package/dist/fork.d.ts.map +1 -0
  49. package/dist/fork.js +602 -0
  50. package/dist/fork.js.map +1 -0
  51. package/dist/generations.d.ts +9 -0
  52. package/dist/generations.d.ts.map +1 -0
  53. package/dist/generations.js +175 -0
  54. package/dist/generations.js.map +1 -0
  55. package/dist/history.d.ts +2 -5
  56. package/dist/history.d.ts.map +1 -1
  57. package/dist/history.js +183 -640
  58. package/dist/history.js.map +1 -1
  59. package/dist/index.d.ts +21 -7
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +15 -5
  62. package/dist/index.js.map +1 -1
  63. package/dist/job-queue.d.ts +2 -0
  64. package/dist/job-queue.d.ts.map +1 -1
  65. package/dist/job-queue.js +29 -2
  66. package/dist/job-queue.js.map +1 -1
  67. package/dist/media.d.ts +3 -3
  68. package/dist/media.d.ts.map +1 -1
  69. package/dist/media.js +6 -5
  70. package/dist/media.js.map +1 -1
  71. package/dist/merge-policy.d.ts +83 -0
  72. package/dist/merge-policy.d.ts.map +1 -0
  73. package/dist/merge-policy.js +203 -0
  74. package/dist/merge-policy.js.map +1 -0
  75. package/dist/metadata.d.ts.map +1 -1
  76. package/dist/metadata.js +17 -23
  77. package/dist/metadata.js.map +1 -1
  78. package/dist/migration.d.ts +9 -0
  79. package/dist/migration.d.ts.map +1 -0
  80. package/dist/migration.js +564 -0
  81. package/dist/migration.js.map +1 -0
  82. package/dist/mvp-contract-fixtures.d.ts +244 -0
  83. package/dist/mvp-contract-fixtures.d.ts.map +1 -0
  84. package/dist/mvp-contract-fixtures.js +556 -0
  85. package/dist/mvp-contract-fixtures.js.map +1 -0
  86. package/dist/mvp-contracts.d.ts +761 -0
  87. package/dist/mvp-contracts.d.ts.map +1 -0
  88. package/dist/mvp-contracts.js +59 -0
  89. package/dist/mvp-contracts.js.map +1 -0
  90. package/dist/mvp-time.d.ts +54 -0
  91. package/dist/mvp-time.d.ts.map +1 -0
  92. package/dist/mvp-time.js +148 -0
  93. package/dist/mvp-time.js.map +1 -0
  94. package/dist/notebook/types.d.ts +85 -8
  95. package/dist/notebook/types.d.ts.map +1 -1
  96. package/dist/notebook-grid.d.ts +37 -0
  97. package/dist/notebook-grid.d.ts.map +1 -0
  98. package/dist/notebook-grid.js +160 -0
  99. package/dist/notebook-grid.js.map +1 -0
  100. package/dist/notebook-media-artifact.d.ts +23 -0
  101. package/dist/notebook-media-artifact.d.ts.map +1 -0
  102. package/dist/notebook-media-artifact.js +67 -0
  103. package/dist/notebook-media-artifact.js.map +1 -0
  104. package/dist/notebook-mentions.d.ts +20 -0
  105. package/dist/notebook-mentions.d.ts.map +1 -0
  106. package/dist/notebook-mentions.js +53 -0
  107. package/dist/notebook-mentions.js.map +1 -0
  108. package/dist/order-keys.d.ts +22 -0
  109. package/dist/order-keys.d.ts.map +1 -0
  110. package/dist/order-keys.js +262 -0
  111. package/dist/order-keys.js.map +1 -0
  112. package/dist/runtime-services.js +8 -11
  113. package/dist/runtime-services.js.map +1 -1
  114. package/dist/schema.d.ts +8 -5
  115. package/dist/schema.d.ts.map +1 -1
  116. package/dist/schema.js +624 -141
  117. package/dist/schema.js.map +1 -1
  118. package/dist/search-benchmark.d.ts +4 -0
  119. package/dist/search-benchmark.d.ts.map +1 -0
  120. package/dist/search-benchmark.js +144 -0
  121. package/dist/search-benchmark.js.map +1 -0
  122. package/dist/sequences.d.ts +20 -0
  123. package/dist/sequences.d.ts.map +1 -0
  124. package/dist/sequences.js +825 -0
  125. package/dist/sequences.js.map +1 -0
  126. package/dist/similarity.js +10 -10
  127. package/dist/similarity.js.map +1 -1
  128. package/dist/status.js +8 -10
  129. package/dist/status.js.map +1 -1
  130. package/dist/storage.d.ts +3 -1
  131. package/dist/storage.d.ts.map +1 -1
  132. package/dist/storage.js +388 -7
  133. package/dist/storage.js.map +1 -1
  134. package/dist/store.d.ts +64 -3
  135. package/dist/store.d.ts.map +1 -1
  136. package/dist/store.js +354 -68
  137. package/dist/store.js.map +1 -1
  138. package/dist/streams.d.ts +10 -0
  139. package/dist/streams.d.ts.map +1 -0
  140. package/dist/streams.js +183 -0
  141. package/dist/streams.js.map +1 -0
  142. package/dist/temporal-models.d.ts +48 -0
  143. package/dist/temporal-models.d.ts.map +1 -0
  144. package/dist/temporal-models.js +278 -0
  145. package/dist/temporal-models.js.map +1 -0
  146. package/dist/temporal-search.d.ts +29 -0
  147. package/dist/temporal-search.d.ts.map +1 -0
  148. package/dist/temporal-search.js +1551 -0
  149. package/dist/temporal-search.js.map +1 -0
  150. package/dist/transcripts.d.ts +16 -0
  151. package/dist/transcripts.d.ts.map +1 -0
  152. package/dist/transcripts.js +519 -0
  153. package/dist/transcripts.js.map +1 -0
  154. package/docs/engine-layout.md +345 -110
  155. package/docs/mvp-prd.md +918 -0
  156. package/fixtures/media/vancat.mp4 +0 -0
  157. package/fixtures/media/vancat_profile.jpg +0 -0
  158. package/fixtures/v5/contract-fixtures.json +1532 -0
  159. package/package.json +42 -5
  160. package/dist/history-types.d.ts +0 -74
  161. package/dist/history-types.d.ts.map +0 -1
  162. package/dist/history-types.js +0 -2
  163. package/dist/history-types.js.map +0 -1
  164. package/dist/resolver.d.ts +0 -15
  165. package/dist/resolver.d.ts.map +0 -1
  166. package/dist/resolver.js +0 -81
  167. package/dist/resolver.js.map +0 -1
  168. package/dist/timeline.d.ts +0 -9
  169. package/dist/timeline.d.ts.map +0 -1
  170. package/dist/timeline.js +0 -236
  171. package/dist/timeline.js.map +0 -1
@@ -1,8 +1,9 @@
1
1
  # Videobook engine layout
2
2
 
3
3
  This document is the column-by-column layout of the engine-owned data model.
4
- It describes schema version 4 as implemented today, then identifies the
5
- additional structures needed for a full non-linear video editor.
4
+ It describes the original schema version 4 layout, notes where later schema
5
+ versions changed the picture (currently v18), and identifies the additional
6
+ structures needed for a full non-linear video editor.
6
7
 
7
8
  The executable source of truth remains
8
9
  [`src/schema.ts`](../src/schema.ts). If this document and the DDL disagree, the
@@ -41,7 +42,7 @@ flowchart LR
41
42
  OUTBOX --> STAGE
42
43
  STAGE --> REV[Dolt main revision]
43
44
 
44
- FILES[Artifact file APIs] --> CAS[Immutable SHA-256 objects]
45
+ FILES[Artifact file APIs] --> CAS[SHA-256 objects, forgettable]
45
46
  FILES --> MAP[Versioned artifact_files mapping]
46
47
  CAS --> WS[Disposable artifact workspace]
47
48
  MAP --> WS
@@ -52,15 +53,24 @@ flowchart LR
52
53
 
53
54
  ## Global invariants
54
55
 
55
- - One engine catalog contains exactly one `book` row and one matching
56
- `timeline` row. There is no project scope.
56
+ - One engine catalog contains exactly one `book` row and one primary
57
+ `sequences` row. There is no project scope.
57
58
  - The only supported live Dolt branch is `main`.
58
59
  - Semantic mutations are SQL transactions followed by forward-only Dolt
59
60
  commits. A restore creates a new commit; it never rewinds the live branch.
60
- - `SEMANTIC_TABLES` is the staging allowlist. Every mutation also records an
61
- `operations` row.
62
- - `runtime_%` and `sqlite_sequence` are ignored by Dolt. Runtime state can be
63
- rebuilt, expired, or invalidated without changing semantic history.
61
+ - `SEMANTIC_TABLES` is the staging allowlist. Every operation declares the
62
+ semantic tables it may write (`OperationInput.tables`, including ON DELETE
63
+ CASCADE targets); a commit probes exactly that set with row-level diffs,
64
+ stages the truly dirty tables, asserts them clean afterwards, and is
65
+ skipped when only runtime bookkeeping changed. A full-catalog sweep runs
66
+ once per open and faults on rows no operation attributed (doltStatus
67
+ itself over-reports and is never trusted for dirtiness). The commit itself
68
+ is the provenance record: the operation, its parameters, write set, and
69
+ base revision ride in a structured commit message under the configured
70
+ identity as `--author`.
71
+ - `runtime_%`, `job_runs`, and `sqlite_sequence` are ignored by Dolt. Runtime
72
+ state can be rebuilt, expired, or invalidated without changing semantic
73
+ history.
64
74
  - Engine-generated surrogate identities are stable UUIDv7 strings; the SQL
65
75
  columns are `TEXT`, so UUID form is enforced by engine APIs rather than a
66
76
  database check. Content identity is a lowercase, 64-character SHA-256.
@@ -68,8 +78,16 @@ flowchart LR
68
78
  currently modeled use integer frames.
69
79
  - JSON is stored as canonical text with recursively sorted object keys.
70
80
  - Deletes are hard deletes. Owned rows cascade; live artifact/entity
71
- references restrict deletion; immutable objects and prior Dolt revisions
72
- remain available.
81
+ references restrict deletion; prior Dolt revisions remain available.
82
+ - Objects are content-immutable but forgettable. `engine.storage.deleteObject`
83
+ forgets one object (refusing `IN_USE` references at HEAD unless forced);
84
+ `engine.storage.gc` sweeps every object nothing references at HEAD.
85
+ Forgetting is a semantic commit that sets `objects.forgotten_at`; the row
86
+ is never deleted, so it remains as the tombstone (hash + size + forgotten
87
+ timestamp) for historical references, and reads of forgotten content
88
+ surface `OBJECT_UNAVAILABLE` rather than failing or lying. Published
89
+ history is append-only, so bulk forgettable text lives behind CAS hashes
90
+ (see "Forgettable data and raw-text audit" below).
73
91
 
74
92
  ## Dolt-versioned semantic schema
75
93
 
@@ -81,16 +99,16 @@ Notation used below:
81
99
  - `→` names the referenced column.
82
100
  - Defaults and checks are shown inline.
83
101
 
84
- There are 25 allowlisted semantic tables.
102
+ There are 34 allowlisted semantic tables.
85
103
 
86
104
  ### Catalog, artifacts, and content
87
105
 
88
106
  | Table | Columns | Keys, constraints, and purpose |
89
107
  | --- | --- | --- |
90
- | `engine_schema` | `singleton INTEGER`<br>`version INTEGER`<br>`created_at INTEGER` | `singleton PK CHECK(singleton = 1)`. Records the clean-break catalog version. Version 4 rejects older catalogs rather than migrating them. |
91
- | `book` | `book_id TEXT`<br>`slug TEXT`<br>`created_at INTEGER` | `book_id PK`; `slug UQ`. Exactly one row per engine root. |
92
- | `artifacts` | `artifact_id TEXT`<br>`slug TEXT`<br>`kind TEXT`<br>`created_at INTEGER` | `artifact_id PK`; `slug UQ`; `kind CHECK IN (video, image, audio, script, character, prompt, scene, final)`. The artifact is the stable identity for source media, generated media, documents, and final renders. |
93
- | `objects` | `object_hash TEXT`<br>`size_bytes INTEGER`<br>`created_at INTEGER` | `object_hash PK`; `size_bytes CHECK >= 0`. This is versioned metadata for immutable bytes stored outside the database. |
108
+ | `engine_schema` | `singleton INTEGER`<br>`version INTEGER`<br>`created_at INTEGER` | `singleton PK CHECK(singleton = 1)`. Records the clean-break catalog version. The recorded version rejects older catalogs rather than migrating them. |
109
+ | `book` | `book_id TEXT`<br>`name TEXT`<br>`created_at INTEGER` | `book_id PK`. Exactly one row per engine root; `name` is free-text display. |
110
+ | `artifacts` | `artifact_id TEXT`<br>`label TEXT?`<br>`kind TEXT`<br>`created_at INTEGER` | `artifact_id PK`; `kind CHECK IN (video, image, audio, script, character, prompt, scene, final)`. `label` is optional, non-unique display text. The artifact id is the stable identity for source media, generated media, documents, and final renders. |
111
+ | `objects` | `object_hash TEXT`<br>`size_bytes INTEGER`<br>`created_at INTEGER`<br>`forgotten_at INTEGER?` | `object_hash PK`; `size_bytes CHECK >= 0`. Versioned metadata for bytes stored outside the database. Rows are append-only: `forgotten_at` marks a tombstone whose bytes were deleted by `deleteObject`/`gc`. |
94
112
  | `artifact_files` | `artifact_id TEXT`<br>`path TEXT`<br>`object_hash TEXT`<br>`mtime_ms INTEGER`<br>`created_at INTEGER` | `(artifact_id, path) PK`; `artifact_id FK → artifacts.artifact_id ON DELETE CASCADE`; `object_hash FK → objects.object_hash ON DELETE RESTRICT`. Maps a logical artifact path to immutable content. |
95
113
  | `book_metadata` | `key TEXT`<br>`value_json TEXT` | `key PK`. Singleton-book key/value metadata; no redundant `book_id`. |
96
114
  | `artifact_metadata` | `artifact_id TEXT`<br>`key TEXT`<br>`value_json TEXT` | `(artifact_id, key) PK`; `artifact_id FK → artifacts.artifact_id ON DELETE CASCADE`. Extensible artifact metadata that participates in revisions. |
@@ -100,36 +118,48 @@ There are 25 allowlisted semantic tables.
100
118
  | Table | Columns | Keys, constraints, and purpose |
101
119
  | --- | --- | --- |
102
120
  | `entities` | `entity_id TEXT`<br>`type TEXT`<br>`name TEXT`<br>`description TEXT?`<br>`prompt TEXT?`<br>`data_json TEXT DEFAULT '{}'`<br>`created_at INTEGER` | `entity_id PK`; `type CHECK IN (prompt, character, scene)`. Normalized reusable creative concepts. |
103
- | `notebooks` | `notebook_id TEXT`<br>`name TEXT`<br>`properties_json TEXT DEFAULT '{}'`<br>`created_at INTEGER` | `notebook_id PK`. Owns a generation/authoring graph. |
104
- | `cells` | `notebook_id TEXT`<br>`cell_id TEXT`<br>`type TEXT`<br>`title TEXT`<br>`position_x REAL`<br>`position_y REAL`<br>`entity_id TEXT?`<br>`prompt TEXT?`<br>`model TEXT?`<br>`inputs_json TEXT DEFAULT '{}'`<br>`output_artifact_id TEXT?` | `(notebook_id, cell_id) PK`; notebook `FK → notebooks ON DELETE CASCADE`; entity `FK → entities ON DELETE RESTRICT`; output `FK artifacts ON DELETE RESTRICT`; `type CHECK IN (prompt, character, scene, asset, image, video)`. |
121
+ | `notebooks` | `notebook_id TEXT`<br>`name TEXT`<br>`created_at INTEGER` | `notebook_id PK`. Owns a generation/authoring graph. Schema v18 removed the monolithic `properties_json` cell. |
122
+ | `notebook_fields` | `notebook_id TEXT`<br>`field TEXT`<br>`value_json TEXT` | `(notebook_id, field) PK`; notebook `FK → notebooks ON DELETE CASCADE`; `field` is restricted to the typed public notebook fields. Stores optional notebook-level workflow values independently. |
123
+ | `cells` | `notebook_id TEXT`<br>`cell_id TEXT`<br>`type TEXT`<br>`label TEXT?`<br>`grid_row INTEGER`<br>`grid_column INTEGER`<br>`output_entity_id TEXT?`<br>`prompt TEXT?`<br>`provider TEXT?`<br>`model TEXT?`<br>`operation TEXT?`<br>`tool TEXT?`<br>`inputs_json TEXT DEFAULT '{}'`<br>`output_artifact_id TEXT?` | `(notebook_id, cell_id) PK`; notebook `FK → notebooks ON DELETE CASCADE`; entity and output artifacts use `ON DELETE RESTRICT`. `label` is optional display text; the grid slot is the user-facing handle. |
124
+ | `notebook_cell_executions` | `notebook_id TEXT`<br>`cell_id TEXT`<br>fingerprint/status/output/provider/run/timestamp/tool/error fields<br>`stale INTEGER`<br>`fixture_baseline INTEGER` | `(notebook_id, cell_id) PK` and composite cell FK with cascade. Gives each cell's execution and staleness state its own merge boundary. |
125
+ | `notebook_generation_plans` | `notebook_id TEXT`<br>`plan_id TEXT`<br>`cell_id TEXT`<br>`status TEXT`<br>`plan_json TEXT`<br>output/error/timestamp fields | `(notebook_id, plan_id) PK`; composite cell FK with cascade. |
126
+ | `notebook_run_plans` | `notebook_id TEXT`<br>`plan_id TEXT`<br>`status TEXT`<br>plan/cost/fingerprint/output fields<br>timestamps | `(notebook_id, plan_id) PK`. Stores approval and execution plans as independently mergeable rows. |
127
+ | `notebook_transcript_edits` | `notebook_id TEXT`<br>`action_id TEXT`<br>`kind TEXT`<br>`restored INTEGER`<br>`payload_json TEXT` | `(notebook_id, action_id) PK`; notebook FK with cascade. |
128
+ | `notebook_transcript_attachments` | `notebook_id TEXT`<br>`attachment_id TEXT`<br>`payload_json TEXT` | `(notebook_id, attachment_id) PK`; notebook FK with cascade. |
105
129
  | `edges` | `notebook_id TEXT`<br>`edge_id TEXT`<br>`source_cell_id TEXT`<br>`target_cell_id TEXT`<br>`target_input TEXT` | `(notebook_id, edge_id) PK`; notebook `FK → notebooks ON DELETE CASCADE`; composite source and target FKs reference cells in the same notebook and cascade on cell deletion. |
106
130
  | `runs` | `run_id TEXT`<br>`notebook_id TEXT`<br>`status TEXT`<br>`started_at INTEGER`<br>`completed_at INTEGER`<br>`cell_order_json TEXT`<br>`outputs_json TEXT`<br>`error TEXT?` | `run_id PK`; notebook `FK → notebooks ON DELETE CASCADE`; `status CHECK IN (completed, failed, aborted)`. Terminal, versioned notebook execution records. |
131
+ | `generations` | `generation_id TEXT`<br>`notebook_id TEXT`<br>`cell_id TEXT`<br>`output_cell_id TEXT?`<br>`run_id TEXT?`<br>`status TEXT`<br>`tool TEXT`<br>`provider TEXT?`<br>`model TEXT?`<br>`prompt TEXT?`<br>`resolved_prompt TEXT?`<br>`provider_artifact_id TEXT?`<br>`output_artifact_id TEXT?`<br>`error TEXT?`<br>`created_at INTEGER`<br>`updated_at INTEGER` | `generation_id PK`; composite cell FK with cascade; `status CHECK IN (dispatched, awaiting_provider, completed, failed)`. One row per generation attempt; every transition is its own attributed semantic commit, so `dolt_history_generations` is the per-attempt timeline. |
107
132
 
108
- ### Current timeline and media editing state
133
+ ### Sequence timeline and media editing state
134
+
135
+ Sequences are the single timeline model. `sequences`, `sequence_tracks`,
136
+ `sequence_clips`, `clip_links`, `clip_transforms`, `transitions`, and
137
+ `caption_cues` hold the edit; `engine.sequences` and `engine.edits` are the
138
+ only timeline APIs. The legacy schema-v4 `timeline`, `timeline_slots`, and
139
+ `timeline_audio` tables were removed in v17 — schema-v4 imports convert
140
+ still-image slots into clips on the primary sequence instead. See
141
+ [`src/schema.ts`](../src/schema.ts) for the full sequence DDL.
109
142
 
110
143
  | Table | Columns | Keys, constraints, and purpose |
111
144
  | --- | --- | --- |
112
- | `timeline` | `book_id TEXT`<br>`render TEXT DEFAULT 'landscape'` | `book_id PK FK → book.book_id ON DELETE CASCADE`; `render CHECK IN (landscape, portrait, square)`. One sequence-level orientation row. |
113
- | `timeline_slots` | `slot_id TEXT`<br>`artifact_id TEXT`<br>`ordinal INTEGER`<br>`volume REAL?`<br>`audio_fade_in REAL?`<br>`audio_fade_out REAL?` | `slot_id PK`; artifact `FK → artifacts ON DELETE RESTRICT`; `ordinal CHECK >= 0`; volume and fades are null or `>= 0`. A single ordered visual lane; ownership by the singleton timeline is implicit. |
114
- | `timeline_audio` | `audio_id TEXT`<br>`artifact_id TEXT`<br>`ordinal INTEGER`<br>`start_frame INTEGER`<br>`duration_frames INTEGER`<br>`volume REAL?`<br>`fade_in REAL?`<br>`fade_out REAL?` | `audio_id PK`; artifact `FK → artifacts ON DELETE RESTRICT`; ordinal/start `CHECK >= 0`; duration `CHECK > 0`; volume and fades are null or `>= 0`. Ordered overlays with explicit timeline timing. |
115
145
  | `audio_waveforms` | `artifact_id TEXT`<br>`peaks_json TEXT` | `artifact_id PK FK → artifacts.artifact_id ON DELETE CASCADE`. Versioned waveform peaks used by editing UI. |
116
146
 
117
147
  The normalized current relationship is:
118
148
 
119
149
  ```mermaid
120
150
  erDiagram
121
- BOOK ||--|| TIMELINE : owns
122
- ARTIFACTS ||--o{ TIMELINE_SLOTS : supplies
123
- ARTIFACTS ||--o{ TIMELINE_AUDIO : supplies
151
+ BOOK ||--o{ SEQUENCES : owns
152
+ SEQUENCES ||--o{ SEQUENCE_TRACKS : orders
153
+ SEQUENCE_TRACKS ||--o{ SEQUENCE_CLIPS : places
154
+ ARTIFACTS ||--o{ SEQUENCE_CLIPS : supplies
124
155
  ARTIFACTS ||--o| AUDIO_WAVEFORMS : has
125
156
  ARTIFACTS ||--o{ ARTIFACT_FILES : maps
126
157
  OBJECTS ||--o{ ARTIFACT_FILES : backs
127
158
  ```
128
159
 
129
- `timeline_slots` order is defined by `(ordinal, slot_id)`. It does not
130
- currently store timeline start, source in/out, duration, track, transform,
131
- speed, opacity, effects, or transitions. `timeline_audio` does store start and
132
- duration frames, but there is no catalog-level frame-rate/timebase row yet.
160
+ Sequence timing is integer frames against each sequence's rational frame
161
+ rate; timed clip sources use rational timebase ticks. Track and clip
162
+ ordering uses fractional order keys with the row UUID as tie-breaker.
133
163
 
134
164
  ### Prompts and messages
135
165
 
@@ -138,25 +168,39 @@ duration frames, but there is no catalog-level frame-rate/timebase row yet.
138
168
  | `prompt_entries` | `prompt_id TEXT`<br>`surface TEXT`<br>`prompt TEXT`<br>`context_json TEXT DEFAULT '{}'`<br>`created_at INTEGER` | `prompt_id PK`. Semantic prompt history grouped by UI or agent surface. |
139
169
  | `messages` | `message_id TEXT`<br>`role TEXT`<br>`body_json TEXT`<br>`created_at INTEGER` | `message_id PK`. Structured semantic conversation history. |
140
170
 
141
- ### Operations, action graph, and terminal jobs
171
+ ### Provenance and terminal jobs
142
172
 
143
- | Table | Columns | Keys, constraints, and purpose |
144
- | --- | --- | --- |
145
- | `operations` | `operation_id TEXT`<br>`operation TEXT`<br>`artifact_id TEXT?`<br>`details_json TEXT DEFAULT '{}'`<br>`write_set_json TEXT DEFAULT '[]'`<br>`base_revision TEXT?`<br>`created_at INTEGER`<br>`author TEXT` | `operation_id PK`. One low-level provenance row is written with every semantic mutation. Artifact and revision references are deliberately loose so audit history survives deletion. |
146
- | `actions` | `action_id TEXT`<br>`operation TEXT`<br>`scope TEXT`<br>`actor TEXT`<br>`lane TEXT`<br>`phase TEXT`<br>`base_revision TEXT?`<br>`target_artifact_id TEXT?`<br>`target_action_id TEXT?`<br>`layout_json TEXT?`<br>`details_json TEXT DEFAULT '{}'`<br>`created_at INTEGER` | `action_id PK`; scope `CHECK IN (book, artifact, layout, external, system)`; phase `CHECK IN (requested, started, completed, failed, cancelled, conflicted)`. High-level workflow/action projection. Targets are loose audit references. |
147
- | `action_events` | `event_id TEXT`<br>`action_id TEXT`<br>`operation_id TEXT`<br>`phase TEXT`<br>`details_json TEXT DEFAULT '{}'`<br>`created_at INTEGER` | `event_id PK`; action `FK → actions ON DELETE CASCADE`; same phase check as `actions`. `operation_id` is a loose provenance link. |
148
- | `action_parents` | `action_id TEXT`<br>`parent_action_id TEXT` | `(action_id, parent_action_id) PK`; child `FK → actions ON DELETE CASCADE`; parent `FK → actions ON DELETE RESTRICT`. Forms the action DAG. |
149
- | `action_artifacts` | `action_id TEXT`<br>`artifact_id TEXT`<br>`direction TEXT` | `(action_id, artifact_id, direction) PK`; action `FK → actions ON DELETE CASCADE`; `direction CHECK IN (input, output)`. Artifact ID is deliberately loose for durable lineage. |
150
- | `action_write_set` | `action_id TEXT`<br>`resource TEXT` | `(action_id, resource) PK`; action `FK → actions ON DELETE CASCADE`. Normalized resources used for overlap/conflict reasoning. |
151
- | `job_runs` | `run_id TEXT`<br>`artifact_id TEXT?`<br>`job_type TEXT`<br>`state TEXT`<br>`payload_json TEXT`<br>`result_json TEXT?`<br>`error_json TEXT?`<br>`started_at INTEGER?`<br>`finished_at INTEGER` | `run_id PK`; `state CHECK IN (done, failed, aborted)`. Terminal job audit. Artifact references are loose so completed history survives artifact deletion. |
152
-
153
- `actions.layout_json` currently maps to the public
154
- `HistoryLayout { stage: number; column: number }`. It is workflow layout
155
- metadata, not spatial video composition.
173
+ Provenance is not a set of tables; it is the Dolt commit log itself. Every
174
+ semantic commit message is structured and machine-parseable:
175
+
176
+ ```
177
+ <operation>[ artifact:<artifactId>]
178
+
179
+ op-id: <uuidv7>
180
+ base-revision: <commit hash> (when the mutation declared one)
181
+ actor: <operation actor> (when the mutation declared one)
182
+ write-set: <canonical JSON array> (when non-empty)
183
+ details: <canonical JSON object> (when non-empty)
184
+ ```
185
+
186
+ History listings, per-artifact history, stale write-set conflict checks, and
187
+ edit restore are all derived from `dolt_log`, `dolt_diff`, and
188
+ `dolt_at_<table>(revision)` projections — there is no parallel `operations`,
189
+ `actions`, or `edit_batches` record to merge. doltlite rejects commit
190
+ messages of 65536 bytes or more, so an oversized `details` or `write-set`
191
+ payload is dropped in favor of a `details-omitted` / `write-set-omitted`
192
+ trailer that records the payload size; projections treat omitted trailers as
193
+ empty.
194
+
195
+ Terminal job audit rows live in `job_runs` (`run_id PK`; `state CHECK IN
196
+ (done, failed, aborted)`), an ignored runtime table rather than a semantic
197
+ one: job payload blobs are rebuildable bookkeeping, so recording a terminal
198
+ job never mints a commit. Artifact references are loose so completed history
199
+ survives artifact deletion.
156
200
 
157
201
  ### Committed Dolt policy table
158
202
 
159
- `dolt_ignore` is created and staged separately from the 25-table allowlist:
203
+ `dolt_ignore` is created and staged separately from the 28-table allowlist:
160
204
 
161
205
  | Column | Constraint |
162
206
  | --- | --- |
@@ -168,8 +212,140 @@ Its committed rows are:
168
212
  | Pattern | Ignored | Meaning |
169
213
  | --- | ---: | --- |
170
214
  | `runtime_%` | `1` | Never version any engine runtime table. |
215
+ | `job_runs` | `1` | Never version terminal job audit blobs. |
171
216
  | `sqlite_sequence` | `1` | Never version local AUTOINCREMENT counters. |
172
217
 
218
+ ### Merge policy per constraint class
219
+
220
+ Merges run through `mergeWithPolicy` (`src/merge-policy.ts`), which encodes
221
+ one rule per constraint class. doltlite verifies the merged working set
222
+ against UNIQUE, CHECK, and foreign-key constraints and rolls a violating
223
+ merge back atomically ("working set with constraint violations"); there is
224
+ no `dolt_verify_constraints()` in doltlite, so the merge itself plus
225
+ post-merge scans (`PRAGMA foreign_key_check`, duplicate-singleton scans)
226
+ are the constraint-verification primitives.
227
+
228
+ - **Precondition: same schema version.** Both sides must carry the same
229
+ `engine_schema.version`; a mismatch is refused with
230
+ `SCHEMA_INCOMPATIBLE` before any merge is attempted
231
+ (`assertSameSchemaVersion`). This was previously only implied by the
232
+ engine's open-time version gate.
233
+ - **Artifact identity is `artifact_id` (UUIDv7) → no name-conflict
234
+ class.** Forks mint collision-free ids, and `artifacts.label` is
235
+ non-unique display text that merges as an ordinary column. Row-level
236
+ same-row edits surface as `MERGE_CONFLICT`.
237
+ - **RESTRICT foreign keys → verification-surfaced typed violation.** A
238
+ fork that deletes a row another fork newly references is caught by
239
+ doltlite's merge-time working-set verification, which refuses and rolls
240
+ back; the policy maps that refusal to `MERGE_VIOLATION` (never a raw
241
+ `IO_ERROR`) and re-verifies referential integrity after every successful
242
+ merge (`verifyConstraintHealth`). Delete-time `IN_USE` pre-checks cover
243
+ every RESTRICT referencing table of `artifacts` (cells, streams,
244
+ transcripts, clips, pinned search results) so single-branch deletes also
245
+ fail with typed errors.
246
+ - **`transcripts.state='current'` / `sequences.is_primary` → derived
247
+ singletons.** A merge of forks that each crowned a different row yields
248
+ duplicates. Reads resolve a deterministic winner and the post-merge
249
+ reconcile (`reconcileSingletonFlags`) rewrites losers: transcripts keep
250
+ the latest `created_at` (ties: lowest `transcript_id`), sequences keep
251
+ the earliest `created_at` — the original primary (ties: lowest
252
+ `sequence_id`). The reconcile is Dolt-committed so the working set is
253
+ clean for the next merge.
254
+ - **Grid slots and order keys** need no resolution: `(grid_row,
255
+ grid_column)` collisions and identical fractional order keys are both
256
+ resolved at read time by the stable row-UUID tie-break (see the order
257
+ rules above). Writers self-repair: `moveTrack` runs `reconcileOrderKeys`
258
+ over a duplicate-key sibling group before computing a between-key, so the
259
+ position between two merge-minted duplicates stays reachable.
260
+ - **`objects.forgotten_at` → forget wins, earliest stamp.** The same
261
+ takedown applied independently on fork and upstream produces a same-row
262
+ different-value cell (wall clocks differ). The projection merge resolves
263
+ it instead of conflicting: when both sides agree on everything except
264
+ `forgotten_at`, a set value beats NULL (deleted bytes stay deleted on
265
+ both lineages) and two set values keep the earlier timestamp
266
+ (`resolveObjectsRow` in `src/fork.ts`).
267
+
268
+ ve-wsu: doltlite currently corrupts secondary UNIQUE indexes on
269
+ `dolt_checkout` once a working set has three or more tables, corrupts full
270
+ engine catalogs on checkout and `dolt_clone` (the cloned file's schema does
271
+ not even parse: `invalid rootpage` on a secondary autoindex), misfires its
272
+ "uncommitted changes" merge guard on the full 28-table catalog (every
273
+ table reports a phantom `modified` status with zero row diffs), and — when
274
+ the guard is bypassed by committing the phantom dirt — dies in schema
275
+ loading on true merges (`schema conflict on table 'sqlite_autoindex_*'`).
276
+ The dedicated merge-back flow therefore runs this policy around a
277
+ projection-level three-way merge instead of `dolt_merge`; see "Forks and
278
+ merge-back integration" below. `mergeWithPolicy` remains the drop-in merge
279
+ mechanism once the upstream bugs are fixed, exercised against the real
280
+ semantic DDL in `tests/merge-policy.test.ts`.
281
+
282
+ ### Forks and merge-back integration
283
+
284
+ A fork of a public book is, from the engine's point of view:
285
+
286
+ 1. **A platform fork.** Creating the hosted copy of a catalog and giving
287
+ it a URL is the hosting layer's job; it is out of engine scope.
288
+ 2. **A clone of the catalog into a local engine root** (`bootstrapFork` in
289
+ [`src/fork.ts`](../src/fork.ts)). Because `dolt_clone` corrupts full
290
+ catalogs (ve-wsu), bootstrap takes a byte snapshot of a healthy upstream
291
+ `videobook.db` (captured while the upstream engine is closed) and opens
292
+ it as a normal engine — no `initialBookSlug`, the singleton book row
293
+ comes along with the snapshot. A URL bootstrap path attempts
294
+ `dolt_clone` and health-validates the result, surfacing a typed
295
+ `FEATURE_UNAVAILABLE` while the upstream bug stands; it starts working
296
+ unchanged once doltlite is fixed.
297
+ 3. **A public-read object store keyed by SHA-256.** `ContentStore` stays
298
+ the abstraction; the existing `ensureLocal` lazy fetch in
299
+ [`src/cas.ts`](../src/cas.ts) downloads any object the fork lacks on
300
+ first touch, so upstream objects of a public book are readable to
301
+ forkers without a bulk copy. Whether the fork's store proxies to
302
+ upstream's is a hosting concern — an engine root only ever sees one
303
+ `ContentStore`.
304
+
305
+ The fork is then a full citizen: it commits on its own `main` and backs up
306
+ to its own catalog remote and object store.
307
+
308
+ **The live-engine never-pulls rule stands.** An open engine never fetches,
309
+ pulls, or merges, and `main` remains the only supported live branch.
310
+ Integration is a dedicated flow, `mergeBack` in
311
+ [`src/fork.ts`](../src/fork.ts), separate from any live engine:
312
+
313
+ 1. Copy a healthy upstream `videobook.db` (engine closed) into a throwaway
314
+ temp workspace. The flow never requires or mutates the user's open
315
+ catalog — the source file is only read.
316
+ 2. Register/fetch the fork remote (`dolt_remote`, `dolt_fetch`) and
317
+ resolve heads and the merge base (`dolt_merge_base`; commit hashes via
318
+ `dolt_log`/`dolt_branches` — `doltHashOf` returns content hashes, not
319
+ commit hashes; the fetched remote-tracking ref gets a local branch
320
+ pointer, a ref-only write that is safe under ve-wsu).
321
+ 3. Run the merge policy: same-schema precondition, then a
322
+ projection-level three-way row merge over `dolt_at_<table>`
323
+ snapshots of base/ours/theirs (row
324
+ semantics mirror Dolt: one-sided changes win, identical changes
325
+ resolve, incompatible changes abort with `MERGE_CONFLICT`), deterministic
326
+ singleton-flag reconcile, and post-merge constraint verification
327
+ (`MERGE_VIOLATION`). The working-set rewrite uses the restore idiom —
328
+ delete all semantic tables in reverse, reinsert parent-before-child.
329
+ 4. Upload the fork's new objects (rows in `dolt_at_objects(theirs)` not at
330
+ ours, tombstones excluded) from the fork's object store to upstream's
331
+ BEFORE the catalog ref moves — the same objects-before-push ordering as
332
+ `engine.storage.backup`.
333
+ 5. Land one forward integration commit on `main` and `dolt_push` it.
334
+
335
+ ve-wsu makes a true two-parent `dolt_merge` commit impossible on full
336
+ catalogs today, so the integration commit is single-parent and records the
337
+ integrated fork head in a `merged-revision` commit-message trailer (plus
338
+ `base-revision`, per the structured-message convention). Re-running the
339
+ flow is a no-op when the fork's net changes are already on `main`. The
340
+ projection merge in `mergeRefs` is the single swap point: when doltlite is
341
+ fixed, `mergeWithPolicy` + `dolt_merge` replace it and the commit becomes
342
+ a true merge commit.
343
+
344
+ When a plain `engine.storage.backup()` push is rejected because upstream
345
+ moved, the backup surfaces `DIVERGED` with guidance into this flow —
346
+ someone with a healthy upstream catalog runs `mergeBack`; the fork never
347
+ pulls to catch up.
348
+
173
349
  ### Semantic indexes
174
350
 
175
351
  Primary keys and unique declarations create their own backing indexes. The
@@ -186,15 +362,9 @@ schema additionally defines every index below.
186
362
  | `edges_source` | `edges(notebook_id, source_cell_id)` |
187
363
  | `edges_target` | `edges(notebook_id, target_cell_id)` |
188
364
  | `runs_notebook_completed` | `runs(notebook_id, completed_at, run_id)` |
189
- | `timeline_slots_order` | `timeline_slots(ordinal, slot_id)` |
190
- | `timeline_slots_artifact` | `timeline_slots(artifact_id)` |
191
- | `timeline_audio_order` | `timeline_audio(ordinal, audio_id)` |
192
- | `timeline_audio_artifact` | `timeline_audio(artifact_id)` |
193
365
  | `prompt_entries_lookup` | `prompt_entries(surface, created_at, prompt_id)` |
194
366
  | `messages_created` | `messages(created_at, message_id)` |
195
- | `operations_created` | `operations(created_at, operation_id)` |
196
- | `operations_artifact_created` | `operations(artifact_id, created_at, operation_id)` |
197
- | `action_events_action_created` | `action_events(action_id, created_at, event_id)` |
367
+ | `generations_cell` | `generations(notebook_id, cell_id, created_at)` |
198
368
 
199
369
  ## Local-only runtime schema
200
370
 
@@ -252,57 +422,106 @@ to recover safely:
252
422
 
253
423
  1. Start `BEGIN IMMEDIATE`.
254
424
  2. Change the requested semantic rows.
255
- 3. Insert the mutation's `operations` row.
256
- 4. Insert `runtime_commit_outbox(operation_id, tables_json, message, created_at)`.
257
- 5. Commit the SQL transaction.
258
- 6. Stage only changed names from `SEMANTIC_TABLES`.
259
- 7. Create a Dolt commit on `main`.
260
- 8. Delete the runtime outbox row in a runtime transaction.
261
-
262
- On reopen, the engine drains any surviving outbox record. Runtime staging is
263
- checked before writes and after commits. Historical reads use
425
+ 3. Insert `runtime_commit_outbox(operation_id, tables_json, message, created_at)`;
426
+ `tables_json` carries the operation's declared write set alongside the
427
+ allow-empty flag, so the declaration commits atomically with the mutation
428
+ and recovery stages the same tables.
429
+ 4. Commit the SQL transaction.
430
+ 5. Probe each declared table with a row-level diff (`dolt_diff_<table>`,
431
+ which also sees staged-but-uncommitted rows during recovery) and stage
432
+ the dirty ones one `dolt_add` at a time doltlite's multi-argument
433
+ `dolt_add` over-stages, and its `doltStatus` phantom `modified` entries
434
+ are why status is never consulted for dirtiness.
435
+ 6. Create a Dolt commit on `main` with the configured identity as `--author`;
436
+ when nothing semantic changed, clear the outbox row instead of minting an
437
+ empty commit.
438
+ 7. Assert the committed tables are no longer dirty, then delete the runtime
439
+ outbox row.
440
+
441
+ On reopen, the engine drains any surviving outbox record (legacy rows
442
+ without a declared table list fall back to probing the full allowlist) and
443
+ then sweeps the whole catalog: any semantic table with unattributed
444
+ uncommitted rows faults the open with `STORAGE_ERROR`. Staged tables are
445
+ asserted against the allowlist before every commit. Opening a catalog never creates a
446
+ commit: terminal-job reconciliation writes the ignored `job_runs` table
447
+ through a runtime transaction. Historical reads use
264
448
  `dolt_at_<semantic_table>(revision)`; history and conflict projections use
265
- Dolt log, status, and diff APIs. Remote catalog support is push-backup only.
449
+ Dolt log, status, and diff APIs. A whole-book restore reloads **every** table
450
+ in `SEMANTIC_TABLES` from its `dolt_at_*` projection at the target revision —
451
+ deleting in reverse and reinserting in forward, parent-before-child order,
452
+ with columns taken from `PRAGMA table_info` — so the restored state is
453
+ exactly the recorded state and no hand-maintained table list can drift.
454
+ Remote catalog support is push-backup only.
455
+
456
+ ## Forgettable data and raw-text audit
457
+
458
+ Published history is append-only: once a catalog has been pushed anywhere
459
+ someone could fork it, its commits are permanent. Forgetting therefore means
460
+ deleting content-addressed objects, never rewriting history. History squash
461
+ tooling would only be legal before the first push (before anyone can fork);
462
+ the engine deliberately does not implement it.
463
+
464
+ ### Object deletion and GC
465
+
466
+ - `engine.storage.deleteObject(hash, { force?, remote? })` forgets one
467
+ object. It refuses with `IN_USE` (listing the HEAD references) when a HEAD
468
+ row still names the hash, unless `force` is given — the takedown path.
469
+ - `engine.storage.gc({ dryRun?, remote?, doltGc? })` sweeps every object
470
+ nothing references at HEAD, plus stray local files that never got an
471
+ `objects` row. A hash is referenced at HEAD when a HEAD row names it in a
472
+ first-class `object_hash`/`payload_hash` column (`artifact_files`,
473
+ `artifact_streams`, `pinned_search_results`, `sequence_clips`,
474
+ `transcripts`) or embeds it as an `objectHash` in a `cell_references`
475
+ snapshot. Historical revisions are not consulted.
476
+ - Both record forgetting as a semantic commit (`delete_object` /
477
+ `gc_objects`) that sets `objects.forgotten_at`. The row is never deleted:
478
+ it stays as the tombstone (hash + size + forgotten timestamp) for every
479
+ historical row that named the object, and backup never tries to publish a
480
+ forgotten object again. Re-importing the same bytes resurrects the row
481
+ (`forgotten_at` is cleared when the object is re-linked).
482
+ - Restoring an old revision whose object was forgotten relinks the
483
+ tombstone: the forward restore commit stands, and reads of the missing
484
+ bytes surface `OBJECT_UNAVAILABLE` through the existing error path instead
485
+ of crashing. Deleting a referenced object with `force` behaves the same
486
+ way at HEAD.
487
+ - With `remote: true`, deletion also unpublishes via
488
+ `ContentStore.delete(key)` and clears the `runtime_object_publications`
489
+ marker so a later re-import is published again.
490
+ - doltlite exposes `dolt_gc()` as a SQL function (verified: it returns a
491
+ `"N chunks removed, M chunks kept"` summary). `gc({ doltGc: true })` runs
492
+ it after collecting to physically reclaim chunks left behind by dropped
493
+ table data in the versioned catalog.
494
+ - Run `deleteObject` and `gc` only while no imports are in flight; CAS puts
495
+ happen outside the serialized write chain, so a concurrent import could
496
+ race the sweep.
497
+
498
+ ### Raw-text audit
499
+
500
+ | Table / column | Decision | Rationale |
501
+ | --- | --- | --- |
502
+ | `transcript_segments.text`, `transcript_words.text` | **Moved behind CAS** (schema v16) | Full transcripts are the bulk-text case. Segment/word text lives in one CAS object named by `transcripts.payload_hash`; the versioned rows keep only structure (IDs, ordinals, ticks, speaker, confidence, kind), so `transcripts.selectionRange` and caption-cue word references keep working after the payload is forgotten. `transcripts.delete` removes the rows; the payload then becomes GC-collectable. |
503
+ | `caption_cues.text` | Accepted permanent | Short editorial cue text needed to render without CAS access; a deliberate editorial snapshot that can diverge from the transcript. |
504
+ | `prompt_entries.prompt`, `cells.prompt`, `entities.prompt`/`description` | Accepted permanent | Small user-authored creative working data; part of the semantic record users expect to persist, like commit messages. |
505
+ | `messages.body_json` | Accepted permanent | Small per-row authored conversation record. |
506
+ | `pinned_search_results.query_json`/`signals_json`/`representative_json`, `cell_references.snapshot_json` | Accepted permanent | Small selection snapshots; may embed short excerpts. Snapshot `objectHash` values are honored as loose GC roots. |
507
+ | `runs.cell_order_json`/`outputs_json`/`error`, `book_metadata`/`artifact_metadata.value_json` | Accepted permanent | Structural or small key/value data. |
508
+ | `runtime_segment_text.text`, `runtime_text_similarity_chunks.chunk_text` | Out of scope | `runtime_*` tables are never versioned or pushed, so they never enter the public record; rebuild or drop them locally at will. |
266
509
 
267
510
  ## Video-editing structures exposed today
268
511
 
269
512
  The SQL schema is only one part of the editing model. These structures and
270
513
  conventions are also important.
271
514
 
272
- ### Timeline contract
273
-
274
- ```ts
275
- type TimelineRender = "landscape" | "portrait" | "square";
276
-
277
- interface Timeline {
278
- bookId: string;
279
- render: TimelineRender;
280
- slots: TimelineSlot[];
281
- audio: TimelineAudio[];
282
- }
283
-
284
- interface TimelineSlot {
285
- id: string; // UUIDv7
286
- artifactId: string;
287
- volume?: number;
288
- audioFadeIn?: number;
289
- audioFadeOut?: number;
290
- }
291
-
292
- interface TimelineAudio {
293
- id: string; // UUIDv7
294
- artifactId: string;
295
- startFrame: number;
296
- durationFrames: number;
297
- volume?: number;
298
- fadeIn?: number;
299
- fadeOut?: number;
300
- }
301
- ```
515
+ ### Sequence contract
302
516
 
303
- `engine.timeline.set()` replaces/synchronizes the normalized rows while
304
- preserving caller-supplied stable IDs. `getAtRevision()` projects the three
305
- timeline tables at a Dolt revision.
517
+ The timeline is a `Sequence` projection: rational frame rate and pixel
518
+ aspect, an audio sample rate and channel layout, ordered video/audio/caption
519
+ tracks, clips with source ranges and transforms, transitions, and caption
520
+ cues. `engine.sequences` reads and mutates sequence structure (tracks,
521
+ names, primary sequence); `engine.edits` applies transactional clip,
522
+ transition, and caption operations. `getAtRevision()` projects the sequence
523
+ tables at a Dolt revision. The full contract types live in
524
+ [`src/mvp-contracts.ts`](../src/mvp-contracts.ts).
306
525
 
307
526
  ### Artifact manifest and content
308
527
 
@@ -318,7 +537,7 @@ interface ArtifactManifestFile {
318
537
 
319
538
  interface ArtifactManifest {
320
539
  artifactId: string;
321
- slug: string;
540
+ label?: string;
322
541
  path: string; // disposable workspace path
323
542
  fileCount: number;
324
543
  files: ArtifactManifestFile[];
@@ -360,12 +579,10 @@ status, and error. Entities provide structured prompts, characters, and scenes.
360
579
 
361
580
  ### Provenance and concurrency
362
581
 
363
- - `Revision` projects Dolt commit hash, operation, artifact, details, and file
364
- changes.
365
- - `HistoryAction` projects the action DAG, lifecycle events, input/output
366
- artifact lineage, workflow layout, and write set.
367
- - `base_revision` plus normalized write sets detect overlapping semantic
368
- actions.
582
+ - `Revision` projects the Dolt commit hash, operation, artifact, and
583
+ parameters parsed from the structured commit message.
584
+ - `base_revision` plus the write sets carried in commit messages detect
585
+ overlapping semantic changes.
369
586
  - Runtime jobs, resource leases, artifact views, owner IDs, expiry times, and
370
587
  monotonic fences coordinate processors without polluting Dolt history.
371
588
 
@@ -381,10 +598,15 @@ status, and error. Entities provide structured prompts, characters, and scenes.
381
598
 
382
599
  ## Full NLE structures not yet implemented
383
600
 
384
- Schema v4 can assemble one ordered visual lane plus timed audio overlays. It is
385
- not yet a complete non-linear editor model. The following is a concrete
386
- candidate layout for a future clean-break schema. Every name in this section
387
- is proposed and does **not** exist in schema v4.
601
+ The core sequence model proposed below has since landed (`sequences`,
602
+ `sequence_tracks`, `sequence_clips`, `clip_links`, `clip_transforms`,
603
+ `transitions`, `caption_cues`) and is now the only timeline model; the
604
+ legacy v4 lane described here was removed in v17. The remaining proposals —
605
+ effects, keyframes, audio buses, markers, derivatives — are still open.
606
+ Schema v4 assembled one ordered visual lane plus timed audio overlays. The
607
+ following is a concrete candidate layout for the remaining pieces. Every
608
+ name in this section is proposed and, unless noted above, does **not** exist
609
+ in the catalog.
388
610
 
389
611
  ### Core sequence model
390
612
 
@@ -460,12 +682,21 @@ must reproduce a render.
460
682
  - Preserve loose references only in audit/lineage records that must outlive the
461
683
  target.
462
684
  - Keep order as an integer plus stable ID tie-breaker, or use a documented
463
- fractional ordering key if frequent insertion warrants it.
685
+ fractional ordering key if frequent insertion warrants it. The engine uses
686
+ fractional/lexicographic order keys (base-62, `"a0"`-style midpoint keys,
687
+ see `src/order-keys.ts`) for engine-maintained orderings — sequence tracks
688
+ and, before the legacy triple was removed in v17, timeline slots and
689
+ timeline audio — with the row UUID as tie-breaker, so inserting or moving
690
+ one row never renumbers its neighbors. Notebook cells
691
+ keep their explicit integer `(grid_row, grid_column)` slot as the ordering
692
+ key with the cell UUID as tie-breaker; slot uniqueness is no longer
693
+ enforced by the schema and collisions left by a merge are repaired on the
694
+ next write instead.
464
695
  - Put user intent and accepted edit decisions in Dolt. Put playhead position,
465
696
  selections, UI panels, decode caches, thumbnails, temporary renders, active
466
697
  jobs, locks, and presence in ignored runtime tables.
467
698
  - Pin renders and derived analysis to source object hashes and a Dolt revision,
468
- not mutable artifact slugs or workspace paths.
699
+ not mutable artifact labels or workspace paths.
469
700
  - Treat color space, transfer function, matrix, range, alpha mode, rotation,
470
701
  sample rate, channel layout, and variable-frame-rate timing as first-class
471
702
  media facts.
@@ -478,12 +709,16 @@ must reproduce a render.
478
709
  | Concern | Implementation |
479
710
  | --- | --- |
480
711
  | DDL, table allowlists, indexes | [`src/schema.ts`](../src/schema.ts) |
712
+ | Fractional order keys and minimal rekeying | [`src/order-keys.ts`](../src/order-keys.ts) |
481
713
  | SQL transactions, Dolt staging, commits, outbox recovery | [`src/store.ts`](../src/store.ts) |
714
+ | Merge policy per constraint class | [`src/merge-policy.ts`](../src/merge-policy.ts) |
715
+ | Fork bootstrap and merge-back integration | [`src/fork.ts`](../src/fork.ts) |
482
716
  | Engine paths and row projections | [`src/context.ts`](../src/context.ts) |
483
- | Immutable object layout and remote keys | [`src/cas.ts`](../src/cas.ts) |
717
+ | Content-addressed object layout, remote keys, deletion | [`src/cas.ts`](../src/cas.ts) |
718
+ | Object publication, deletion, GC, and catalog backup | [`src/storage.ts`](../src/storage.ts) |
484
719
  | Artifact mappings and workspace materialization | [`src/files.ts`](../src/files.ts) |
485
- | Current timeline API and row synchronization | [`src/timeline.ts`](../src/timeline.ts) |
486
- | Public timeline, manifest, job, status, and similarity types | [`src/engine-types.ts`](../src/engine-types.ts) |
720
+ | Sequence reads/structure and transactional edits | [`src/sequences.ts`](../src/sequences.ts), [`src/edits.ts`](../src/edits.ts) |
721
+ | Public manifest, job, status, and similarity types | [`src/engine-types.ts`](../src/engine-types.ts) |
487
722
  | Entity/notebook graph types | [`src/notebook/types.ts`](../src/notebook/types.ts) |
488
723
  | Revision and action projections/restores | [`src/history.ts`](../src/history.ts), [`src/history-types.ts`](../src/history-types.ts) |
489
724
  | Media naming and discovery conventions | [`src/media.ts`](../src/media.ts) |