9.4 KiB
Mission Core application component architecture
Status: accepted, 2026-07-26
Decision
Mission Core adopts the same dependency discipline as the NODE.DC Design Guideline without copying its application code.
The Design Guideline remains the visual platform:
@nodedc/tokens
→ @nodedc/ui-core
→ @nodedc/ui-react
→ Mission Core application
Inside Mission Core, dependencies flow in one direction:
typed contracts and data adapters
→ reusable domain components and renderers
→ feature workspaces
→ product registry and reviewed composition roots
→ App shell
A lower layer does not import a higher layer. The application owns domain meaning and data flow; the Design Guideline owns general visual controls, surfaces, interaction states, icons, and shell geometry.
This dependency direction constrains ownership, not product composition. New
workspaces may have different information architectures when their operator
jobs differ. Their placement and form follow
docs/19_PRODUCT_SURFACE_EXTENSION_PROTOCOL.md.
Why Design Guideline code is not copied
The reusable architecture in NODEDC_DESIGN_GUIDELINE is its published package
boundary, registries, validation, and one-way imports. Its living catalog is an
application and is not a source template to paste into Mission Core.
Mission Core consumes @nodedc/tokens, @nodedc/ui-core, and
@nodedc/ui-react. A visual primitive missing from those packages is proposed
and approved in the Design Guideline first. Mission Core does not fork it.
The current file: package dependencies are suitable for the local standalone
development contour, but they are not an immutable distribution contract.
Before portable CI or deployment, the packages must be published, vendored with
content verification, or pinned to a verified donor revision.
Internal layers
src/core
Owns typed domain contracts, parsers, API adapters, state machines, and domain-specific hooks. It may depend on React for a hook or context boundary, but it does not import visual adapters, workspaces, or the App shell.
src/components
Owns reusable Mission Core domain components such as observation sources, timelines, media adapters, and laboratory presentation/evidence frames. Components may consume core contracts and canonical Design Guideline exports. They do not select routes, own product navigation, or import workspaces.
src/workspaces
Owns domain composition. A workspace connects core data to reusable components and renderers. A new domain gets its own module or feature directory; it is not appended to the central workspace hub.
workspaces/contracts.ts is the host contract shared by workspace
implementations. The central Workspaces.tsx is a composition hub for existing
generic workspaces, not a home for LAB-specific adapters or evidence logic.
src/workspaces/laboratory
Owns the laboratory catalog feature. It may compose:
- the reusable laboratory presentation components;
- immutable LAB adapters from
core/laboratory; - domain evidence renderers;
- the generic spatial workspace through the explicit
SpatialViewcontract.
Every new LAB adds a bounded result component and typed data adapter here. It
does not add LAB imports, selectors, styles, or conditional evidence code to
Workspaces.tsx or App.tsx.
src/composition
Owns reviewed integration imports and registries. Vendor/plugin contributions enter Core only through this layer and the public plugin SDK. Composition does not become a second business-logic layer.
src/productModel.ts
Is the typed application registry for roots, workspaces, labels, icons, and declared capabilities. Navigation and routing project from it rather than duplicating lists across the App shell.
It is a product registry, not a runtime ontology and not a substitute for evidence or domain contracts.
New workspaces and roots enter this registry only after their user job and placement are resolved. The registry must not accumulate placeholder pages for unapproved future ideas.
src/App.tsx
Owns shell-level orchestration: selected root/workspace, global panels, runtime providers, and passing typed controllers to a workspace. It must not absorb domain API calls, per-LAB renderers, or new visual primitives.
CSS ownership
CSS follows the same feature boundary:
- general workspace composition lives in
styles/workspaces.css; - laboratory presentation and E30 evidence layout live in
styles/laboratory.css; - spatial, observation, device, and shell rules remain in their named files;
- plugin CSS is plugin-local and scoped below its contribution root.
Application CSS may arrange domain content. Canonical control geometry and states remain in the Design Guideline packages. A feature must not add selectors to a generic CSS bucket merely because that file is already imported.
Component admission
A Mission Core component is admitted only when all of the following are true:
- Its responsibility can be stated in one sentence.
- Its inputs are typed domain data or callbacks, not hidden global state.
- It does not duplicate a Design Guideline entity.
- It has explicit loading, empty, unavailable, and error behavior where relevant.
- It has no knowledge of unrelated workspaces or vendor implementations.
- Its styles are feature-scoped.
- Its contract or behavior is covered by a unit or architecture test.
If the entity is visually generic rather than domain-specific, it belongs in the Design Guideline and requires product-owner approval before implementation.
Laboratory feature template
The code shape of a new laboratory increment is:
core/laboratory/eNNContract.ts
immutable API/schema adapter
workspaces/laboratory/ENNResult.tsx
typed values for the versioned summary + admitted evidence + result projection
domain renderer module
only when an existing renderer cannot express the evidence
LaboratoryArchiveWorkspace.tsx
registry/selection composition only
styles/laboratory.css
shared LAB layout; no per-run visual language
The complete engineering narrative remains in Ops. The product component receives a concise typed projection.
components/laboratory/LaboratoryPresentation.tsx owns the executable
missioncore.laboratory-report/v1 anatomy. LaboratorySummary requires
question, approach, principalResult, and limitation;
LaboratoryResultSummary requires metrics plus proved, notProved, and
decision. A bounded LAB result module cannot own the canonical summary/result
DOM or CSS classes. The product UI test discovers every ENNResult.tsx
automatically and rejects such a fork.
Legacy/integrated diagnostic viewers may keep a result interaction inside the evidence slot only where that viewer is already the admitted result instrument. This exception does not apply automatically to a new LAB.
Local ontology decision
Mission Core already has three semantic mechanisms:
productModel.tsfor application navigation and capability declarations;- versioned TypeScript/Pydantic/JSON contracts for executable data;
docs/domain-model/mission-core-experimental-vocabulary-v0alpha2.mdfor meanings that are not yet stable enough for Platform Ontology.
These mechanisms are sufficient before A3. A new RDF/graph store, ontology service, or parallel entity catalog would introduce duplicated identity and migration work without a current query or integration consumer.
A formal local runtime ontology is introduced only when:
- at least two independent runtime consumers require the same relationships;
- typed contract traversal is demonstrably insufficient;
- ownership, identity, lifecycle, versioning, and migration are agreed;
- the graph answers named queries used by the product or automation;
- promotion or synchronization with NODE.DC Platform Ontology is defined.
Until those conditions hold, new stable meanings extend the versioned local vocabulary and executable contracts. They do not create a second runtime model.
Automated boundary gate
test/applicationArchitecture.test.mjs enforces:
- no upward imports from core/components into workspaces or App;
- no visual adapter imports from core;
- no local vendor icon library or direct Design Guideline source imports;
- LAB code and CSS remain outside the central workspace buckets;
- central composition files cannot silently return to their previous size.
test/laboratoryProductUi.test.mjs additionally enforces the versioned LAB
report fields and shared result component across bounded LAB modules.
The line limits are ratchets, not quality targets. When a file reaches a limit, split a feature; do not raise the limit to accommodate unrelated behavior.
From apps/control-station run:
npm run test:unit
npm run typecheck
npm run build
Known bounded debt
App.tsxremains a large shell orchestrator. Its current size is frozen by a ratchet; future shell behavior must extract a controller/hook or panel module.Workspaces.tsxstill contains several established generic workspaces. New domains must be separate modules, and existing ones may be extracted when their behavior changes.LaboratoryArchiveWorkspace.tsxis now physically isolated but at its ratchet. A3 receives its own component/module instead of growing that file.- Design Guideline dependencies are mutable local
file:links until a portable package/distribution decision is implemented.
None of these debts require a new ontology or a copy of the Design Guideline application.