nw.schema#
Schema for an nw project — narrative-workflow SSOT data shapes.
A project is a folder with a project.json at its root. The shape is
deliberately compatible with the layout muvid established for music-video
projects, so the_bells_v* fixtures load directly into nw without
migration. nw generalizes muvid’s IR by:
making
RenderStrategyopen (a string), so apps can register their own strategies (composite_lipsync, slideshow, panel, …) without touching nw,adding
ProjectSummaryas a typed read view returned byProject.read_summary(),promoting setters that muvid expressed via
python -cglue (set_title,set_global_style,set_character_anchor).
Pydantic is used (instead of frozen dataclasses) for two reasons:
lacing already uses Pydantic — sharing the conventions keeps the ecosystem coherent.
nw will eventually round-trip schemas through HTTP/MCP; Pydantic gives JSON-Schema export and validation for free.
Classes
|
Pointer to a character folder under |
|
One entry of a project's decision log, flattened for display. |
|
Pointer to an environment folder under |
|
The top-level project SSOT, persisted as |
|
Typed read view of a project — what |
|
A "where we left off" snapshot, returned by |
|
A non-overlapping span of the project's master timeline. |
|
A timeline-locked visual unit. |
|
Metadata for the master audio file. |
- class nw.schema.CharacterRef(**data)[source]#
Bases:
BaseModelPointer to a character folder under
characters/<name>/.The stable-attribute fields mirror
nw.bodies.CharacterRefBodyV1field-for-field, and that is load-bearing rather than cosmetic:nw.Project.read_spec()builds aCharacterReffrom the graph body andnw.Project.write_spec()writes the body back from theCharacterRef. Any field present on the body but missing here is silently erased by the nextupdate_spec— which is what used to happen toreference_image_urls. Add a field to one, add it to both.
- class nw.schema.DecisionEntry(**data)[source]#
Bases:
BaseModelOne entry of a project’s decision log, flattened for display.
- class nw.schema.EnvironmentRef(**data)[source]#
Bases:
BaseModelPointer to an environment folder under
environments/<name>/.Mirrors
nw.bodies.EnvironmentRefBodyV1field-for-field, for the same load-bearing reason asCharacterRef— see that docstring.reference_image_urls(the lookbook the FE curates for a location) was erased by everyupdate_specuntil this mirror was completed.
- class nw.schema.ProjectSpec(**data)[source]#
Bases:
BaseModelThe top-level project SSOT, persisted as
project.json.Field names and order are chosen to round-trip identically with muvid’s ProjectSpec for
schema_version=1, so the_bells_v* fixtures (and any other muvid-shaped project) load and re-save without churn.
- class nw.schema.ProjectSummary(**data)[source]#
Bases:
BaseModelTyped read view of a project — what
muvid statusprinted, but typed.Returned by
Project.read_summary(). Holds the small facts the user most often wants: title, root, song path, counts of characters / shots / sections / output, plus a coarse “stages_done” list naming the lifecycle stages that have been reached.
- class nw.schema.ResumptionBrief(**data)[source]#
Bases:
BaseModelA “where we left off” snapshot, returned by
nw.Project.resumption_brief().Pure data: no fal calls, no LLM, no network. reelee renders it as prose and injects it as the first tool-result of a session.
**The field names are chosen to be honest about what nw can currently measure**, because a confidently wrong number is worse than no number:
downstream_of_last_authored_changeis not “stale”. It isnw.descendants_of— pure provenance reachability, comparing no content and no timestamp — so this set includes everything already regenerated since the change. It is an upper bound on what needs attention, and it is named for what it measures.nw.stale_afteris the narrower answer and it now cuts off early (nw#24), so switching this field to it would return a smaller and correct set. That is deliberately not done here: the field would then be named for the wrong measurement, and which of the two a resumption brief should show is nw#7’s call, not nw#24’s. Callers who want the exact set can callnw.stale_afterwithlast_authored_change_id.The walk starts at the last authored change — the most recent annotation the user wrote (a shot, a section, a character or environment ref), never one a Transform derived. Walking from “the newest annotation” instead would be inverted: the newest node in a provenance graph is by construction a leaf, so its descendant set is empty in exactly the case the field exists for.
total_spend_usdsums every recorded render decision across every store scope. Nothing records per-branch outcomes yet, so a render that failed after being billed is counted here exactly like one that succeeded. Also an upper bound.
caveatscarries those qualifications as data — so a consumer renders them next to the numbers instead of rediscovering them.
- class nw.schema.SectionSpec(**data)[source]#
Bases:
BaseModelA non-overlapping span of the project’s master timeline.
labelis free-form (“intro”, “verse”, “chorus”, “scene-1”, “act-2”, …) so different apps (music-video, explainer, podcast-clip) can use their own taxonomy.
- class nw.schema.ShotSpec(**data)[source]#
Bases:
BaseModelA timeline-locked visual unit.
[start_s, end_s)is half-open.render_strategyis an open string rather than a closed Literal, so apps can register their own strategies vianw.renderers.register_strategy()(Phase 1b.3) without modifying the schema.