48 — Configuration across the hierarchy — define-at vs apply-at, cascade & override (envisioning)
Consolidation / envisioning doc. Governed by
00-ROADMAP.md— defer to it on conflict. Created 2026-06-29 (owner-raised, after the doc-46 workflow editor landed): "still unsure if the workspace can be applied to different dimensions of the hierarchy (Roster / Artist / Project / Track / Variant)… and the LEGO software — how would it apply in Portals, Browse, and Manage." This doc recognises both uncertainties as one theme and gives it a single model.2 decisions locked with the owner (2026-06-29):
- Envision first (this doc), then build — matching how doc-47 was done.
- Priority = the studio-wide workflow cascade — a process defined once above the project that projects inherit by default. So §5 is concrete/buildable; attributes (LEGO) and per-variant are framed here but defer their depth to doc-43 / doc-46.
▶ PRIORITY BACKEND BUILT 2026-06-29 — the studio-wide workflow cascade ships (§5): new
workspacemodule (Workspacedoc keyed by owner — the doc-44 container seed — withdefaultWorkflowTemplateId;GET /workspace,PATCH /workspace/default-workflow), andProjectService.resolveWorkflowStructurewalks project.workflow ▸ owner studio default ▸ system so an un-instantiated project inherits the studio process with zero setup.release-readinessuses it.nest build+jest75/75 + workflow e2e 5/5 (incl. a cascade test). The cascade also drivesmgmt.ctrlBoard/Matrix/Overview readiness. §8 Q1+Q4 resolved by owner: workspace/owner default (interim-store on the per-ownerWorkspacedoc, forward-compatible with doc-44). iOS surface shipped 2026-06-29 (xcodebuildgreen): Configure ▸ Workflow "Set as studio default" + STUDIO DEFAULT badge; Project Portal shows inherited-vs-custom source + Reset to studio default (DELETE /project/:id/workflow;release-readinessnow returnsworkflowSource). The studio-default loop is end-to-end. Remaining: Artist override (rung 2) · attributes on the same rails (doc-43). SeeCHANGELOG.md.Needs confirmation (remaining §8 open questions: cascade depth · override granularity).
1. The realisation: two questions, one shape
The owner asked two things that look separate but are the same problem:
- "Can a workflow apply at different levels — Roster / Artist / Project / Track / Variant?" (doc-46)
- "How does the LEGO (configurable attributes) apply across Portals / Browse / Manage?" (doc-43)
Both are: "a configurable thing, defined at one level of the hierarchy, applied (and overridable) at others, and surfaced consistently everywhere." Workflows and attributes are two instances of the same define-at / apply-at / cascade abstraction. Solve it once; both inherit it.
2. The hierarchy (what the dimensions actually are)
Workspace (owner / tenant — doc-44; no entity yet, owner is the de-facto root)
└─ ArtistSpace ← carries an optional `roster` { name, color } (a TAG, doc-26)
└─ Project ← the release container; holds the workflow INSTANCE today
└─ Track ← a recording; carries readiness map + `variantType` (doc-28)
└─ Variant ← NOT a child entity — it's `Track.variantType` + relations (doc-28)Two nuances that shape everything below:
- Roster is not a container, it's a tag + lens (
ArtistSpace.roster, doc-26). "Apply at the roster level" = "apply to every ArtistSpace whose roster = X" — a query/scope, not a parent pointer. - Variant is not a node, it's an attribute of a Track (
variantType, doc-28). "Per-variant" = a predicate on the track, not a separate place in the tree.
So the real container ladder is Workspace → ArtistSpace → Project → Track; Roster and Variant are scopes/predicates layered on top.
3. Current state (verified against the schema, 2026-06-29)
| Dimension | Workflow today | Attributes/LEGO today |
|---|---|---|
| Workspace/owner | workflow templates are owner-scoped (WorkflowTemplate.owner) — authored here, but not auto-applied | none — forbidNonWhitelisted rejects unknown fields |
| ArtistSpace | none | none (only roster tag + lifecycle statusCode) |
| Project | ✅ the only anchor — embedded Project.workflow {stages}, instantiated via POST /project/:id/workflow/instantiate; rollup in release-readiness | none |
| Track | steps can be level: 'track' inside the project workflow (roll up across tracks) — but no track-OWNED workflow | Track.readiness map (per-step status) + variantType |
| Variant | none (appliesToVariants not built) | variantType exists (doc-28) |
Takeaway: authoring is workspace-scoped (templates), but application is Project-only. There is no cascade — every project must be instantiated individually. That's exactly the gap the owner felt.
4. The model: define-at · apply-at · resolve (cascade + override)
Three orthogonal ideas — keep them separate:
Define-at — where a config is authored/stored. Workflow templates are defined at the Workspace (owner). Attribute definitions (doc-43) would be defined at the Workspace too (per-workspace catalog).
Apply-at — where a config is attached as the default. A default workflow could be attached at Workspace, ArtistSpace, or Project. Today only Project.
Resolve — what actually governs a given node, by walking most-specific-wins up the container ladder, then falling back:
resolveWorkflow(project) = project.workflow // explicit instance (most specific) ?? project.artistSpace.defaultWorkflow // studio/label default for this artist ?? workspace(owner).defaultWorkflow // the studio-wide default ← the priority ?? SYSTEM_DEFAULT // shipped "Studio Release"This is the same cascade-with-override rule already used for permissions (doc-44: most-specific scope wins → deny>allow) and lifecycle status (doc-41). One mental model across the platform. Default = inherit; override is explicit and local.
Per-variant (doc-46
appliesToVariants) is an apply-at predicate, not another ladder rung: a step (or a whole sub-process) is tagged with the variant types it applies to; resolution filters steps by the track'svariantType. Layered on top of the ladder above.
5. Priority build — the studio-wide workflow cascade
The owner's #1: "my studio works this way" — author the process once, every project inherits it.
What's needed (small, leverages everything built):
- A default-workflow pointer at the studio level. Two honest options for where (§8 Q1):
- (A) Workspace/owner default — the cleanest match (templates are already owner-scoped). Needs a home for "owner's default workflow templateId" (a
Workspacedoc — doc-44 wants one — or interim on the user /settings). One default per studio. - (B) ArtistSpace default —
ArtistSpace.defaultWorkflowTemplateId. Lets a multi-label operator run different processes per artist/label. More flexible, more to manage. - Recommendation: ship (A) workspace default first (covers "my studio works this way"), add (B) artist override as the next rung — the resolver (§4) already accommodates both.
- (A) Workspace/owner default — the cleanest match (templates are already owner-scoped). Needs a home for "owner's default workflow templateId" (a
resolveWorkflow(project)server-side (the cascade above) — andrelease-readinessuses it when a project has no explicitproject.workflow, instead of falling straight to the system default. So a brand-new project already reflects the studio default with zero per-project setup.- Instantiate semantics unchanged: applying a template on a project still writes
project.workflow(an explicit local override). New verb: "set as studio default" (and later "set as this artist's default") in Configure ▸ Workflow.
iOS surfacing (after backend):
- Configure ▸ Workflow — mark a template "Studio default" (a badge + a "Make default" action).
- Project Portal — the pipeline shows "Inherited from studio default" vs "Custom for this project", with "Customise" (instantiate → local override) and "Reset to studio default" (clear
project.workflow).
This is backend-first, then a thin iOS surface — and it reuses the doc-46 pipeline + the Configure editor already shipped.
6. Attributes (LEGO) ride the same rails — defer depth to doc-43
The configurable-attribute engine (doc-43) is the same define-at/apply-at/resolve pattern, with values instead of process:
- Define-at — a per-Workspace AttributeDefinition catalog (doc-43): name, type, and which levels it attaches to (Project-only? Track-only? any?).
- Apply-at — a typed
attributesbag on the entity (Project/Track/…); some attributes may inherit down (e.g. a Project's "Genre" defaulting onto its Tracks) using the §4 resolver. - Resolve — same most-specific-wins walk.
doc-48 does not design the attribute engine (that's doc-43) — it just asserts attributes must use this shared cascade so workflow and LEGO behave identically. The Configure "Attributes" / "Workspace" Soon bubbles are where define-at lives; §7 is where apply-at shows up.
7. Surfacing — how config + attributes appear in the three zones (doc-47)
The owner asked specifically how this shows in Portals / Browse / Manage. One rule per zone:
| Zone | How configuration/attributes surface |
|---|---|
| Browse — Portals (per node) | each Portal renders the node's resolved config: workflow pipeline (doc-46, shipped) + an Attributes section (doc-43 values), each marked inherited vs overridden here. The Portal is where you override locally. |
| Browse — grid/lenses | group-by / filter on attributes + workflow state (doc-26 lenses + doc-40) — "show projects in Mastering stage", "group by Genre". |
| Manage — x-ray (doc-42) | attributes become matrix columns + filters; workflow stage becomes a cross-catalog status axis. The eagle-view reads the resolved value at every node. This is where the "Soon" bubbles ultimately pay off. |
Define-at lives in Configure (the catalog/editors); apply-at + view live in Browse/Manage. Clean split, consistent with doc-47.
Updated by doc-54 (2026-07-04): Configure onboards progressively — config surfaces appear as they become relevant (doc-47 §7 principle 7); workspaces and portals start at the preset minimum ("starting portals with almost nothing") and grow user-friendly. The cascade above is why zero-setup already works: the preset minimum is always in force; Configure is where you change it, never a prerequisite. Architecturally present from the beginning, shipped when ready.
8. Open questions (confirm before building §5)
- Where does the studio default live? Workspace/owner doc (cleanest; needs the doc-44 Workspace container — or an interim store) vs ArtistSpace field (more flexible). Lean: Workspace default first, Artist override next.
- How deep does cascade go? Workspace → Artist → Project is clearly useful. Do we also want a Roster-scoped default (apply to all artists with roster = X — a query-based rule, heavier)? Lean: no for v1 — roster stays a lens, not a config anchor.
- Override granularity — is "explicit
project.workflowoverrides inherited" enough, or do we need partial overrides (inherit the studio process but tweak two steps on this project)? Lean: full override first (simplest); partial later. - Does the Workspace container get built now (doc-44) or do we interim-store the studio default on the owner/user until then? (Decides §5 effort.)
- Per-variant — confirm it's an apply-at predicate (
appliesToVariantson steps), filtered at resolve — not a separate workflow per variant. (doc-46.)
9. Sequencing (after confirmation)
- Confirm §8 (esp. Q1 + Q4 — where the default lives).
- Backend: studio-default pointer +
resolveWorkflow(project)cascade +release-readinessuses it for un-instantiated projects. (+ "set as studio default" verb.) - iOS: "Studio default" badge/action in Configure ▸ Workflow; "Inherited vs Custom / Reset" on the Project Portal.
- Then: Artist-level override (rung B) · per-variant predicate (doc-46) · the LEGO attribute engine on these same rails (doc-43) · Manage columns/filters from attributes (doc-42).
Nothing here changes the MVP loop (doc-19); it makes the configuration the owner already authored actually propagate, and gives attributes a home to land on.
10. Cross-references
- Workflow engine (templates, instance, pipeline):
46-WORKFLOW-ENGINE.md - Configurable attributes / LEGO engine (define-at catalog):
43-CONFIGURABLE-ATTRIBUTES.md - Three-zone model + Configure control room:
47-PRODUCT-NORTH-STAR.md - Permissions cascade (the same resolve rule) + Workspace container:
44-RBAC-TEAMS-PERMISSIONS.md - Lifecycle/status defaults (inherit-down precedent):
41-RECORDS-AUDIT-STATE-STATUS.md - Variants (per-variant predicate):
28-RECORDINGS-VARIANTS-RELEASES.md - Roster as tag + lens, navigation:
26-IDENTITY-SHARING-NAVIGATION.md - X-ray surfacing (matrix columns/filters):
42-MANAGEMENT-CTRL.md