All work

The Element Tree

Every document is a composite tree that can be arbitrarily deep. There are only two concrete runtime classes, and the difference between them is small, but together they represent everything from a whole CV down to a single date field. Each holds a reference to an ElementDefinition asset that supplies its identity.

Leaves and containers

A LeafElement has a collider sized to its text bounds, renders text, holds its own error list, subscribes to profile data, and runs the pen-fed state machine; it's the only thing the pen can point at. A ContainerElement has no collider, holds a list of children, has an OnChildrenPopulated hook for bottom-up error generation, and distributes scoped data to its children. Containers are sometimes meaningful (JobHistoryContainer) and sometimes purely structural (JobHistoryEntry), but it's the same class either way.

Where errors live

The rule is simple: if the player can point at something to flag it, the error lives on that leaf. If not, it lives on the nearest container that owns the concept and surfaces in the terminal's general-issues list instead of the circled list. A typo in a company name lives on the CompanyName leaf; an employment gap between two jobs, or all jobs being at the same company, lives on the JobHistoryContainer because there's nothing single to point at.

Scoped data distribution

Containers that hold sub-slices hand scoped data down. A JobHistoryContainer subscribes to the profile and receives the full work history; for each JobHistoryEntry child it spawns, it sets that child's scoped profile to a single job record. The entry's own leaves, CompanyName, RoleTitle, DateRange, then read from their parent's scoped slice rather than the root profile. Every element only ever sees the data relevant to it.