Foundations: The Resolver Engine Dependency¶
Composer's foundation is not a dependency it merely sits on top of; it is the same architecture,
one level up. The Resolver Engine — the render-time resolve → measure → solve → animate → place
pipeline, the motion graph, and the schema reshape that carries layout.mode, pin, flowBox,
pinPoint, background, price, cues, and keyframes — is a standalone delivery with its own mkdocs
site (resolver-engine-mkdocs-site, same folder). That site is the source of truth for every
engine mechanism. This page says only what Composer takes from it, what Composer asks of it, and
where the two still need reconciling. Nothing here restates an engine mechanism; every construct
names the engine page that owns it.
The one discipline Composer inherits, not just consumes
The engine's headline rule — one resolver, every consumer reads it, never two mechanisms
computing the same fact — is a principle Composer's own mutation engine is built with directly.
The resolver that folds Change[] against a base (The Change Model) is one
pure function every caller reads from (editor preview, render, rebase), never each caller
deriving effective state its own way. That is the engine's motion-graph discipline
(Resolver Engine → The Motion Graph) applied to document mutation instead of layout.
What Composer builds on¶
| Engine construct | What Composer uses it for | Owned by |
|---|---|---|
layout.mode discriminated union (absolute / flow / pin / derived) |
The positioning vocabulary for every layer inside an Element and at a Scene's top level; an Element instance is positioned by exactly one of these like any other layer. pin is a mode any layer may use, not a layer type. |
Resolver Engine → Positioning |
The layer type union under schemaVersion: 2 — one text type, image, video, lottie, shape, sticker, component, product, audio, plus the three new types that carry no intrinsic size of their own (flowBox, pinPoint, background) |
An Element's own layer list is exactly this union (Elements, Scenes & Projects). The single text type is the point/box consolidation, which is designed but not yet built (status below). |
Resolver Engine → Text Foundation (point/box consolidation), Positioning, Background |
Uniform w/h sizing, the anchor grid, content / extents bounds (raster is engine-internal and never a pin target) |
The sizing rule an Element's internal layers and its own instance box resolve under; the bounds an instance's pin targets. |
Resolver Engine → Coordinate System & Sizing |
Intrinsic size per layer type (including shape's declared props.intrinsicSize), the two sizing-collapse cases, and the empty-container rule |
These surfaced from working through Element mechanics and now live in the engine. Composer's part is where the engine's "every mutation" lands for Composer: on every saved fork diff (The Change Model). | Resolver Engine → Coordinate System & Sizing → Intrinsic size |
{ value, unit: "targetFraction" } on pin, flow, and absolute (against the containing box); overflow: show \| hide as a property of every container |
Container-relative sizing of a layer against its containing Element / Scene / Overlay, and the overflow behaviour of an Element instance's box, are the engine's rules applied to one more container (Elements, Scenes & Projects). The engine builds the absolute case specifically so a future element system can size against its containing box. |
Resolver Engine → Coordinate System & Sizing → Container-relative sizing |
pin, pinType |
An Element instance can be pinned to another instance exactly the way any two layers can. | Resolver Engine → Positioning |
pinPoint |
The engine's answer to a custom pivot, so Composer never designs one. | Resolver Engine → Coordinate System & Sizing → Pivot, and the pinPoint escape hatch |
flowBox |
Group flow inside an Element, and the closest existing precedent for what an Element instance needs to be (asks table below). Whether a flowBox can also sit at a Scene's top level to flow Element instances is an open question on the schema page. |
Resolver Engine → Positioning |
background (layout.mode: "derived", of: LayerId[]) |
The spanning background BSR asked for, as an ordinary layer inside an Element. | Resolver Engine → Background |
| Content-item union (text run / price run), the typography resolver | Price as a mode of text inside an Element's text layers; no Composer-side price concept exists. | Resolver Engine → Text Foundation, Price |
The motion graph, container-local per (container, ratio) |
Why an Element instance should be a container in the same sense a Scene is — which the engine does not yet provide; see the asks table. | Resolver Engine → The Motion Graph |
Expressions — targetFraction as sugar for an expression, a manifest binding as the degenerate = manifest.<field>, expression references counted in the shared depth budget |
Composer's manifest bindings land on this, so what an exposed field resolves to is the engine's expression vocabulary rather than a Composer concept. Worth reading before designing the authoring tool's binding UI, since it bounds what a binding can express. | Resolver Engine → Expressions |
at-addressed cues, keyframe tracks |
Deferred from Composer's side; only the Change[] hook-in is Composer's (Keyframes & Cues, from Composer's side). |
Resolver Engine → Cues, Keyframe Animation |
schemaVersion: 1 \| 2 gating, the frozen legacy pipeline |
The engine ships as a permanent two-pipeline fork with no live transformer. Composer's own document versioning is a separate concept that still needs reconciling with this (asks table). | Resolver Engine → Schema-Version Gating |
The engine's worked example — Resolver Engine → Examples → The discount lockup, five layers, no
React, no bespoke component — is exactly what a JSON-authored Element looks like from the inside:
flowBox, two text layers (one carrying a price run and an inline suffix run), a derived
background, a pinned badge. It is the single best picture of what Composer's Elements are made
of.
Where BSR's four asks land¶
| BSR's original ask | Answered by | Where |
|---|---|---|
| A large background block | The engine's spanning background layer, placed inside an Element — spanning several Element instances at Scene level depends on an open question (Elements, Scenes & Projects) |
Resolver Engine → Background |
| Add a sticker | Element / Scene composition: an ordinary Element placed by anyone with composition privilege — "sticker" stops being its own concept | Permissions & Enforcement |
| Add an extra product | Element / Scene composition: addElementInstance |
The Change Model |
| Change how many lines of text render | The engine's self-computed line-breaking and the point/box text consolidation | Resolver Engine → Text Foundation |
Only the background ask maps onto an engine capability directly; the sticker and product asks are Composer's own structural-editing job, and the line-count ask is text-engine mechanics.
Engine status, as it bears on Composer¶
The engine's own status lives in the engine docs, not here. What matters for Composer's planning, read against those docs as of this brief's last revision:
- Built and trusted: Text Foundation's core reshape and its padding fix, plus the
editorModeSchemaschema declaration, onfeature/neo-1253-retail-studio-text-engine— the only pre-existing engine-side code either brief treats as a trusted foundation. - Designed, not built, inside the otherwise-built feature: the point/box text consolidation (the
single
texttype an Element's layer list is written against) and the layout-shape correction from the nested{mode, box}form to flat-spread ratio keys. - Reserved in the schema but not built:
pin. Not started: price,background,flowBox,pinPoint,targetFraction, cues, keyframes. Theneo-1253-layout-system-reworkworktree is being deleted per the engine brief's rollout mechanics and is not a source for any status claim. - Consequence for Composer: Composer's schema cannot yet be authored against real
pin/pinPoint/targetFractioncode or against the consolidatedtexttype, andsetInstanceLayout's concrete shape (The Change Model) waits onflowBoxandpinbeing schema-stable. The engine has an owner (every feature except the built Text Foundation core is Matthew's own build), but its timeline is decoupled from Composer's alpha window entirely (Home → Target deadline).
What Composer asks of the engine¶
Working through Element mechanics surfaces requirements the engine's docs don't yet carry. Several
have already been adopted there — intrinsic size, shape's declared size, the sizing-collapse
fallback, the empty-container rule, overflow as a container property — and are now simply engine
rules Composer applies (table above). What remains is a Composer requirement stated on the Composer
side, flagged here so it gets reconciled into the engine's design rather than assumed; the mechanism
and reasoning for each lives on the page that owns it.
| Requirement | Composer's position | Engine docs today |
|---|---|---|
| An Element instance is a layer in its Scene's graph and a container root for its own internal layers. | An instance is positioned in the Scene like any layer (so it can be pinned to), and its internal layers position, pin, and flow against the instance's box the way a Scene's top-level layers position against the Scene's canvas; the instance's own motion composes into its internals (Elements, Scenes & Projects). | flowBox is the existing precedent and gets part of the way: a real layer with its own layout, group-level animations its members inherit through the graph, overflow, targetFraction against its box, and nesting inside another flowBox. Four deltas remain undesigned. (a) There is no layerSchema variant for a reference to a frozen version of another document, which is what an Element instance is — and the union admits only built variants, so a Scene layer list holding one would fail validation today. (b) Membership is by a member's own flowIn reference, not by containment, so an Element's internals aren't addressed as "inside" anything. (c) The graph is rooted at scene-like containers only ((container, ratio), scenes and overlays), so a layer is never itself a root. (d) Reference scope is the whole scene, so nothing stops a reference crossing what Composer needs to be an encapsulation boundary. |
| An Element instance owns a local time origin and an instance-assigned duration, which its internal layers' start and end resolve against. | A placed instance gets a duration (defaulting to the Scene's), and a layer inside it is timed relative to the instance's own start, resolved by a single shift and optional scale into the Scene's clock (Elements, Scenes & Projects). | The engine's time model is document-global: animateScene(solvedScene, frame, fps) runs on the scene's frame, resolveScene takes the whole document because cue frames depend on every earlier scene's duration, and no layer has a start or end relative to a sub-scene container. |
targetFraction on the time axis. |
A layer's start/end inside an Element may be a fraction of the instance's resolved duration (Elements, Scenes & Projects). | The unit is spatial only. |
| Composer document versions upcast lazily through a transformer chain. | content is run through upgradeVNtoVN+1 transformers at lookup time, silently, for Composer's own { major, minor } schemaVersion (Identity, Versions & Schema Versioning). |
The engine's schemaVersion: 1 \| 2 is a permanent fork with no live transformer (Resolver Engine → Schema-Version Gating); its upgradeV1ToV2 is a manual, per-document tool (→ Coordinate System & Sizing → Migrating existing values). Whether Composer's versions are a separate axis from the engine's, or the same one, is not settled. |
| One render-and-compare implementation behind both the engine PR gate and Composer's rebase check. | The two are one mechanism, two triggers (Testing, Preview & Incident Response). | The engine brief carries its PR gate as a blocking CI gate on its own code and declares it "independent of any Composer-side rebase mechanism." Sharing the implementation is Composer's ask, not an agreed fact. |
| The editor-capability field ends up named once. | layoutMode (basic < intermediate < advanced < author) becomes editTier, because it will govern composition privilege too (Permissions & Enforcement). |
The engine has already built the same declaration's rename to editorModeSchema, leaving only its apps/web consumer sweep. So editTier is a second rename of shipped schema code, not a competing proposal — one of the two has to give. |
Risks¶
- Composer surfaces an engine need nobody has found yet. Nobody has built a real Element against the engine's design. It has happened repeatedly by working through Element mechanics rather than by building one, and the container-root and instance-clock rows above are the current, largest instances.
- Price as a mode of text may need configuration surface beyond what Composer's schema anticipates once actually authored into a real Element. Not knowable until one is.
- The engine's timeline no longer tracks Composer's. Composer's sequencing (Delivery Sequencing) treats the engine as an external delivery at two stages, not as work this team schedules.
Open questions¶
- Whether Composer's alpha needs price,
background, andflowBoxlanded for real, or can ship against whichever subset covers BSR's most pressing asks first — now that none of the three has started. The background ask needsbackground; the sticker and product asks need neither. - Every row of "What Composer asks of the engine" above is an unreconciled design point until the engine docs adopt, adapt, or reject it.