Skip to content

feat: Implement the module model: rename, mitosis, scoped buses, panels, Studio face - #255

Draft
Yona-Appletree wants to merge 18 commits into
mainfrom
claude/yona-implement-modules-roadmap-4cdecb
Draft

feat: Implement the module model: rename, mitosis, scoped buses, panels, Studio face#255
Yona-Appletree wants to merge 18 commits into
mainfrom
claude/yona-implement-modules-roadmap-4cdecb

Conversation

@Yona-Appletree

@Yona-Appletree Yona-Appletree commented Aug 1, 2026

Copy link
Copy Markdown
Member

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Path: ~/.photomancer/planning/lp2025/2026-08-01-1003-modules-impl-roadmap/plan.md

Implements the ratified modules/buses/panels design (docs/design/modules.md, panel.md, PR #230). This PR carries the P1–P6 stretch, ending at gate G3 (behavior review).

Complete:

  • P1 — project→module rename: kinds, types, 159 tree-path literals, corpora, schemas, WIRE_PROTO 4→5
  • P2 — mitosis: project.json container manifest (streaming probe, hard refuse per D-A), module.json root, format 3 (v2 snapshotted), split schemas + conformance routing, Studio manifest re-point
  • P3 — ModuleDef capability: bindings, exports, per-channel meta, provenance (Q7 settled)
  • P4 — structural scope on RuntimeNodeEntry, scope query API, dead Bus deleted
  • P5 — scoped resolver keys + writer-shadowing, suppression rule deleted, sink no-demand by construction
  • P6 — real ModuleNode (root included), R7 publish/exports, engine-reported primary-visual role

ADRs: 2026-08-01-project-module-mitosis-container-format-gate, 2026-08-01-scoped-bus-engine-architecture (supersede the #218 branch ADRs).

At gate G3 (behavior) — remaining phases P7–P13 continue after the gate. Do not merge PR #248 or the #218 branch — harvest sources only.

Resume notes: read plan.md + phase files in the planning dir; discovery in ../2026-07-31-1002-modules-buses-panels/notes.md.

🤖 Generated with Claude Code

Yona-Appletree and others added 18 commits August 1, 2026 13:08
… wire bump)

Rust surface of the P1 rename: ProjectDef -> ModuleDef, NodeKind::Project
-> NodeKind::Module, NodeDef::Project -> NodeDef::Module, PROJECT_VARIANT
-> MODULE_VARIANT ("Project" -> "Module"), ModuleDef::KIND "project" ->
"module", format probe renamed to read_module_format_json. nodes/project/
moved to nodes/module/ (SHAPE_ID regen lands in a later commit).

WIRE_PROTO_VERSION 4 -> 5: NodeKind serializes its variant name in
inventory frames and TreePath segments change from .project to .module.

The word "project" survives for container concepts (project.json, Studio
open-a-project flows, PROJECT_FORMAT_VERSION) per docs/design/modules.md.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The tree-path kind segment comes from kind_name(), so the P1 KIND change
silently re-addresses every node path. This commit is ONLY the path
string sweep (/demo.project/orbit.shader -> /demo.module/orbit.shader)
so review can eyeball path strings in isolation.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rust test fixtures plus the artifact corpora (15 examples/, projects/test,
fw-browser smoke-project) and docs/design/source-artifacts.md. The
schemas/history/v1 snapshot stays frozen (it documents the v1 format) and
the 2026-07-27 defect entry keeps its historical quote.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
just schema-gen: project.schema.json now pins kind Module, ModuleDef
shape id moves (module_path! is part of the hash), NodeArtifact $ref and
the shape dump filename follow. Stragglers: schema generator variant
name, Studio node-kind icon lookup + NodeKindIcon::Module, debug-UI kind
labels, package_manifest default kind, node_kind_slug/label, justfile
format-bump path, schemas/README, modules.md section-2 done note.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y the sweep

format!-embedded \"kind\": \"Project\" fixtures (engine test helper,
server/browser/opfs/history tests), UiPackageCard/UiExampleCard story
kind fields, embedded example kind, node-authoring error copy. Pane
title chips keep "Project" (container concept).

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… relocation (P2)

project.json becomes the non-node container manifest (format/uid/name),
read by a streaming JsonSyntaxSource probe and written by a hand-rolled
deterministic writer — deliberately NOT a Slotted type, so no second
shape/codec surface links into firmware. The root module node moves to
/module.json; ModuleDef drops format/uid/name.

PROJECT_FORMAT_VERSION 2 -> 3 (bump-and-refuse; v2 schemas snapshotted
to schemas/history/v2). The load gate now reads the container: missing
or malformed project.json is a HARD refuse per settled D-A, and a
pre-mitosis kind-tagged root fails with a diagnosable manifest error.

Corpora (15 examples, projects/test, smoke-project) split into the two
files; starter project emits 9 files; registry/engine/shared fixtures
migrated; new gate tests incl. missing-manifest and pre-mitosis roots.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…split

lpa-server, lpa-link fake device, lpa-fs-opfs, lpc-wire fixtures move to
container manifest + module.json (delegated sweep); studio-core library,
demo project, e2e harnesses, device-session and node-CRUD fixtures follow.
Blank Created packages now also write a minimal module.json so they stay
loadable — gated to blank creates so device pulls stay byte-faithful and
adoption parity hashes cannot diverge. fw-browser uid patcher re-anchored
on the manifest form (was silently broken by the split), smoke.html
fetches module.json, package_manifest rewritten on ProjectManifest.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…P2 tests + ADR

Settings popup renders format/uid/name read-only from the container
manifest (UiProjectManifest on ProjectEditorView, sourced from the active
library package); gallery rename now patches the manifest name as well as
the slug. New tests: device-path wrong-format + missing-manifest refusal
on the emulated firmware (rides the booted scene_render_emu), vendored
module folder loads under the host project's gate (Q10 pinned), and a
corpus byte-identity rewrite check for both split files. modules.md
section-6/Q10 notes, schemas README, and the mitosis ADR.

ADR: docs/adr/2026-08-01-project-module-mitosis-container-format-gate.md

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…provenance (P3)

ModuleDef gains the authored surface the ratified model requires:
bindings (R7's contention pick becomes authorable), exports (local slot
-> inner-scope channel, bus: ref form; runtime interpretation lands with
the module runtime), per-channel meta overrides (Q1 curation escape
hatch, the spike's display vocabulary minus aliasing), and provenance.
ProvenanceDef (author/version/license/created, settled Q7) is a
capability of any node def; the container manifest carries the same four
keys. Schemas regenerated; byte-stable round-trip pinned by test.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…egistry (P4)

Scope identity becomes a queryable engine fact (modules.md R1/R2):
ScopeRef {Module, Sink} stored on every runtime node entry, assigned by
ensure_runtime_spine on BOTH load and apply so an edited project can
never wear different scopes than a reloaded one; present on Pending/
Failed entries and untouched by payload reattach. Query API on the tree:
scope_of, scope_introduced_by, scopes, scope_channels (R3/R4 listing
grouping), scope_persist_path — sink scopes key by the authored playlist
entry (…/entries[k]) so panel state will follow the slot, not the
content, and stay stable under sibling reorder and reattach.

The dead runtime Bus/ChannelEntry value registry is deleted (its only
construction was an empty throwaway for DestroyCtx); Destroy/MemPressure
contexts slim to node+frame. No resolution behavior change — pinned by
the load-vs-apply differential and the untouched engine suite.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ion rule deleted (P5)

QueryKey::Bus becomes {scope, channel}: bus demand carries the READING
node's scope into the cache and cycle-detection identity, so same-named
channels in different scopes can never collide or fake a cycle. The
resolver stays scope-dumb — the host answers 'which providers win for a
read from this scope' via NodeTree::providers_for_bus_read, a pure
outward walk (R5) that never descends into a scope, which is what makes
sink-scope no-demand hold by construction (R2) instead of via the
spike's probe-side filter. Provider re-reads key off the provider's own
scope; unscoped public reads resolve from the root scope.

The playlist ownership-suppression rule is deleted: entry children now
default-publish visual.out like every producer — into their entry's sink
scope, invisible to enclosing reads by construction (tests flipped to
pin the new shape, incl. probe-with-values never ticking a sink-scope
producer). Three correctness obligations pinned: adversarial two-scope
cache soundness, depth-2 writer-shadowing/inheritance (E5 minus the P6
mirror), and the load-vs-apply wiring differential.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed (P6)

Every module node — root included — now wears a live ModuleNode runtime
(harvested from the spike's ProjectNode mirror): produce resolves the
module's own introduced scope's visual.out through the scoped resolver
key and forwards render/sample dispatch to the producing node; a scope
with no visual writer renders cleared (a module without a visual is a
legitimate shape). Never feature-gated — the runtime links into every
firmware image (C6 headroom after: 243,952 B).

register_node_bindings' module early-return is gone: modules register
authored bindings (the R7 contention pick), authored exports (inner
channel republished under the export name — module-owned bus reads
resolve from the INTRODUCED scope via the host reading-scope rule), and
the automatic output→visual.out fallback publish for non-root modules
(the drop-in rule). The apply-path root reattach installs the same
runtime, so an applied project equals a reloaded one.

Primary visual becomes an engine-reported role: WireBusChannel gains
primary_visual, decided once in the probe (root-scope listing of the
vocabulary channel); Studio's channel.name == "visual.out" string
tests are gone. E5 depth-2 is pinned: a consumer beside an embedded
module resolves the sibling's publish, never root's.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Supersedes the #218 branch ADRs (2026-07-28-scoped-buses,
2026-07-28-effects-are-projects) with pointers.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ommit branch switch

The dx build rewrites lp-app/lpa-studio-web/assets/tailwind.css in the
CI working tree; the dirty tracked file aborted 'git checkout -B' in the
auto-commit step, so genuine story drift could never self-commit.

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…::Project refs

Main's new feature-gate tables (lpc-model/src/feature.rs,
lpc-engine/src/features.rs) referenced the pre-rename kind; renamed to
NodeKind::Module (module stays ungated — None feature mapping).

Plan: lp2025/2026-08-01-1003-modules-impl-roadmap
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

CI refreshed the story baselines on this branch

The validate-stories job detected drift and committed the fresh set: 20 changed
in 851127a.

Review every PNG in the PR's Files changed view (swipe / onion-skin).
Your local branch is now behind — git pull before pushing again.

Story Before After
studio__project__project-pane__change-list-empty__lg.png studio__project__project-pane__change-list-empty__lg.png@f7e4767365 studio__project__project-pane__change-list-empty__lg.png@851127af34
studio__project__project-pane__change-list-empty__md.png studio__project__project-pane__change-list-empty__md.png@f7e4767365 studio__project__project-pane__change-list-empty__md.png@851127af34
studio__project__project-pane__change-list-empty__sm.png studio__project__project-pane__change-list-empty__sm.png@f7e4767365 studio__project__project-pane__change-list-empty__sm.png@851127af34
studio__project__project-pane__change-list-overflow__lg.png studio__project__project-pane__change-list-overflow__lg.png@f7e4767365 studio__project__project-pane__change-list-overflow__lg.png@851127af34
studio__project__project-pane__change-list-overflow__md.png studio__project__project-pane__change-list-overflow__md.png@f7e4767365 studio__project__project-pane__change-list-overflow__md.png@851127af34
studio__project__project-pane__change-list-overflow__sm.png studio__project__project-pane__change-list-overflow__sm.png@f7e4767365 studio__project__project-pane__change-list-overflow__sm.png@851127af34
studio__project__project-pane__change-list__lg.png studio__project__project-pane__change-list__lg.png@f7e4767365 studio__project__project-pane__change-list__lg.png@851127af34
studio__project__project-pane__change-list__md.png studio__project__project-pane__change-list__md.png@f7e4767365 studio__project__project-pane__change-list__md.png@851127af34
12 more file(s)
  • studio__project__project-pane__change-list__sm.png (changed)
  • studio__project__project-pane__detail-popup__lg.png (changed)
  • studio__project__project-pane__detail-popup__md.png (changed)
  • studio__project__project-pane__detail-popup__sm.png (changed)
  • studio__project__project-pane__in-progress__sm.png (changed)
  • studio__project__project-pane__live-only__sm.png (changed)
  • studio__project__project-pane__staged-node-removal__lg.png (changed)
  • studio__project__project-pane__staged-node-removal__md.png (changed)
  • studio__project__project-pane__staged-node-removal__sm.png (changed)
  • studio__project__project-pane__unchanged__sm.png (changed)
  • studio__project__project-pane__uncommitted__sm.png (changed)
  • studio__project__project-workspace__sidebar-dirty-tree__sm.png (changed)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant