ADR-0012: Visualization as the Final Package Above agents¶
| Status | Accepted |
| Date | 2026-07-06 |
| Deciders | Chief Software Architect, MineProductivity |
| Governs | mineproductivity.visualization |
| Related documents | docs/architecture/12_Visualization_Design_Specification.md; docs/design/12_Visualization_Implementation_Checklist.md; docs/architecture/11_AI_Agents_Design_Specification.md §37; ADR-0011-AI-Agents.md; docs/architecture/10_Optimization_Design_Specification.md §37; docs/architecture/09_Simulation_Design_Specification.md §37; docs/architecture/08_Digital_Twin_Design_Specification.md §34; docs/architecture/07_Decision_Intelligence_Design_Specification.md §36; docs/architecture/05_KPI_Engine_Design_Specification.md §10.1, §10.6; docs/architecture/06_Analytics_Engine_Design_Specification.md (not yet merged to main - see feature/analytics-engine) |
Context¶
MineProductivity's Foundation Layer and its six consumer packages, analytics, decision, digital_twin, simulation, optimization, and agents, are complete, tested, and locked as of agents v1.3.0. Five of those packages' own documents already named what comes next, and a sixth - the very first, kpis - had already provisioned for it structurally before any of the others existed. KPI Engine's own 29-field KPIMetadata schema carries a dedicated visualization_hint field (spec 05 §10.1), and KPIResult already ships plot()/pareto() methods that "delegate to visualization metadata hooks" (spec 05 §10.6) - written years, in this platform's own narrative, before a visualization package existed to back them. Decision Intelligence's own Future Roadmap named visualization the anticipated renderer of the DecisionResult family (spec 07 §36). Digital Twin's own Future Roadmap named it the anticipated renderer of the TwinResult/TwinState/TwinSnapshot family (spec 08 §34). Simulation's named it the anticipated renderer of the SimulationResult/Experiment family, anticipating "animating a SimulationRun's state trajectory" (spec 09 §37). Optimization's named it the anticipated renderer of the OptimizationResult/ParetoResult family, anticipating "plotting a multi-objective search's Pareto front" (spec 10 §37). AI Agents' went furthest of the five: it named visualization the anticipated renderer of the AgentResult/AgentAuditTrail family, including "a live view of a WorkflowEngine's in-progress multi-agent decomposition" (spec 11 §37). This ADR formalizes and designs visualization, the package every one of those already-locked documents assumed would exist, and closes the presentation layer none of them declined to build for itself so much as deferred entirely.
The immediate question this ADR answers: given already-measured KPIs, already-characterized trends, already-explained recommendations, a represented current twin state, projected hypothetical futures, solved optimal plans, and autonomously-decided agent actions, who turns any of it into something a human being actually looks at - without becoming an eighth computation engine, a second stateful-representation layer, a second projection layer, a second search-and-solve layer, or a second autonomous-orchestration layer? This ADR decides where that presentation layer lives, why it is shaped as a model-independent contract over pluggable rendering backends rather than a shipped charting-library integration, and why this is the final package this platform's architecture will ever need to design a place for.
Decision¶
Create mineproductivity.visualization as a new, independent package sitting directly above agents and depended on by nothing:
core → ontology → events → kpis → analytics → decision → digital_twin → simulation → optimization → agents → visualization
visualization is permitted to import core, ontology, events, registry, plugins, connectors, kpis, analytics, decision, digital_twin, simulation, optimization, and agents; it introduces its own public API (Visualization and VisualizationContext; VisualizationMetadata/VisualizationCategory as a closed, eight-member registration schema; PresentationModel as the structured, backend-independent rendering output; Widget/Layout/Theme and Dashboard - a core.BaseEntity[str] subclass - as the persistent configuration domain model; DashboardBuilder/ReportBuilder as the first concrete subclasses of core.BaseBuilder[T] anywhere in this series; Report and RenderingPipeline; an interface-only ABC for pluggable extension, Renderer; ExportRequest/ExportResult; DashboardRepository as a direct type alias over core.BaseRepository[Dashboard, str]) without requiring any change to any Foundation Layer file, or to analytics/decision/digital_twin/simulation/optimization/agents, or to any of their public APIs or dependency rules. This package holds two independent Registry instances - visualization.REGISTRY for Visualization types and visualization.RENDERERS for Renderer types - each a direct specialization of registry.Registry, following the exact precedent agents established for Agent/Tool (spec 11 §22), applied here for the second time in the series rather than as a novel exception. Full design detail is in the governing specification linked above; this ADR records why the package exists and why it is shaped the way it is.
Why a Separate Package (Not Folded Into agents or kpis)¶
- Distinct responsibility, distinct question being answered.
agentsanswers "who decides what to do next, and who coordinates the many specialized reasoners that decision spans across."visualizationanswers a different question none of the ten packages below it was ever chartered to answer: "given everything already measured, characterized, recommended, represented, projected, solved, and decided, how does a human actually see any of it?" A rendered dashboard is a presentation of already-produced facts, not a new fact, and its existence has nothing to do with whether an agent ever acted autonomously to produce the evidence behind it. - Six lower specs already drew this boundary, from six directions, one of them years before the others. KPI Engine's own
visualization_hintfield andKPIResult.plot()/pareto()methods (spec 05 §10.1, §10.6) provisioned for a future renderer from the very first package in the series, without ever attempting to build one insidekpisitself. Decision (spec 07 §36), Digital Twin (spec 08 §34), Simulation (spec 09 §37), Optimization (spec 10 §37), and Agents (spec 11 §37) each independently namedvisualizationas the anticipated renderer of their own already-stable output types rather than proposing to build a rendering pipeline into any of themselves. Folding presentation logic intoagentsorkpiswould mean shipping the exact rendering commitment each package's own specification already declined to make. - A configuration-entity shape, not a process-record shape, for the one genuinely new stateful abstraction this package needs.
visualization.Dashboardsubclassescore.BaseEntity[str]and changes viadataclasses.replace, exactly likedigital_twin.Twin,simulation.SimulationRun,optimization.OptimizationRun, andagents.Taskbefore it - but, unlike all four, it carries no execution-lifecyclestatusand nowith_state()method, because a saved dashboard is a configuration a user edits, not a process that runs to completion. This is a deliberate, recorded departure from the pattern (governing spec §3.3, §10), not an oversight - the first time in the series aBaseEntitysubclass's own shape differs from its four predecessors' in this specific way. - Consistent with every other layer boundary already drawn in this platform, including the six
analytics,decision,digital_twin,simulation,optimization, andagentsjust established at the six layers below. A platform that has consistently drawn a package boundary at every "distinct responsibility, distinct rate of change, distinct statefulness/governance profile" seam would be inconsistent to stop doing so at the one seam (agents→visualization) six of its own specifications already named from six different directions, one of them (kpis) before any of the others existed.
Dependency Rationale¶
visualization is permitted to import the entire Foundation Layer plus analytics, decision, digital_twin, simulation, optimization, and agents, not merely agents directly, for the same structural reason ADR-0007 through ADR-0011 already established at their own layers: a VisualizationContext (governing spec §8) legitimately needs kpis.KPIResult/analytics.AnalyticsResult (as measurement/characterization evidence), decision.DecisionResult (directly, including any embedded Explanation, per Decision spec 07 §36's own anticipation), digital_twin.TwinSnapshot/TwinState (as current-condition evidence, per Digital Twin spec 08 §34's own anticipation), simulation.SimulationResult (as projected-outcome evidence, per Simulation spec 09 §37's own anticipation), optimization.OptimizationResult/ParetoResult (as solved-plan evidence, per Optimization spec 10 §37's own anticipation), and agents.AgentAuditEntry (as autonomous-decision evidence, per Agents spec 11 §37's own anticipation). Restricting visualization to only agents would have forced pass-through re-exports of types it does not otherwise need to expose, exactly the worse alternative ADR-0006 through ADR-0011 already rejected at their own layers.
connectors is included in the permitted-import list for platform-wide layering consistency, but - exactly as in every package above it - is not exercised: visualization renders already-computed, already-synchronized facts, never a vendor-specific wire format, and Export (governing spec §18) is deliberately not built on connectors despite the surface-level similarity of "moving structured data across a boundary" - a connector reads external data into the platform; Export writes a rendered artifact out of it, in the opposite direction (governing spec §5, §18).
Architectural Trade-offs¶
- Trade-off accepted:
Dashboardis the firstcore.BaseEntity[str]subclass in this series to carry nostatusfield and nowith_state()method, breaking from the shapeTwin/SimulationRun/OptimizationRun/Taskeach established. Accepted because a saved dashboard has no execution lifecycle to track - it is a configuration record, not a process record - and inventing astatusenum with no real transitions to populate it would be governance theater, not a genuine state machine. - Trade-off accepted:
DashboardBuilder/ReportBuilderare the first concrete subclasses ofcore.BaseBuilder[T]anywhere in this series, closing out a framework capabilitycorehas carried, unused, since the Foundation Layer (core.BuilderError's own docstring has namedcore.builder.BaseBuildersince before this package existed). Every prior package's own builder-shaped need (decision.ExplanationBuilder, spec 07 §17) was met with a plain, non-generic utility class instead. This package adopts the formal abstraction becauseDashboard/Reportconstruction has enough optional steps to justifyBaseBuilder's own stated guidance, not because every future builder-shaped need in this platform must retroactively be migrated to it. - Trade-off accepted: this package holds two independent
Registryinstances (REGISTRY,RENDERERS) rather than one, the second package in this series to do so, followingagents' precedent (spec 11 §22) rather than inventing a new exception to "one extension mechanism, platform-wide" (governing spec §3.7). Accepted because aVisualizationtype and aRenderertype are orthogonal registrable concepts - one decides what to show, the other decides how to draw it - and conflating the two would blur a boundary this design otherwise keeps deliberately sharp. - Trade-off accepted:
VisualizationandRenderership as two interface-only ABCs with zero concrete implementations, the seventh package in the series to apply this discipline (afteranalytics,decision,digital_twin,simulation,optimization, andagents). This means the package cannot, on its own, actually render anything until a plugin implements at least one of the two - a materially weaker out-of-the-box capability than shipping even a single reference HTML renderer would provide. Accepted for the same reasons ADR-0006 through ADR-0011 already accepted the equivalent trade-off at their own layers (see "Alternatives Rejected"). - Trade-off accepted: this package introduces no dedicated rendering-output cache, the third package in the series to decline one, after
optimization(spec 10 §26) andagents(spec 11 §27) - though for a distinct reason from either:PresentationModelconstruction is comparatively cheap over already-cached upstream evidence, and where a specificRenderer's own conversion step is genuinely expensive, that cost is backend-specific and best memoized inside that renderer, not generalized into a single shared cache shape across wildly different media (governing spec §24). - Trade-off accepted:
Exportshares no code withconnectors, despite both moving structured data across a system boundary, because the two move it in opposite directions - a documented non-reuse rather than a forced, superficial one (governing spec §5, §18, §32's recorded anti-pattern).
Alternatives Considered¶
- Fold rendering logic directly into
agents(e.g. arender()method onAgentResultitself). Rejected - directly contradictsagents' own already-locked scope and its own Future Roadmap's framing ofvisualizationas the separate, downstream consumer of its output (spec 11 §37). - Fold rendering logic directly into
kpis, building outKPIResult.plot()/pareto()into a full charting subsystem inside the KPI Engine itself. Rejected -kpis' ownvisualization_hintfield (spec 05 §10.1) was deliberately designed as metadata a future renderer consults, not as a charting implementationkpisitself would ship; building a real charting subsystem into the very first package in the series would also leave every later package (decisionthroughagents) with no shared rendering layer of its own to reuse. - Defer
visualizationentirely, treating rendering as an application-layer concern outside this platform's own architecture. Rejected - for the same reason ADR-0006 through ADR-0011 each rejected deferring their own packages: six already-locked specifications independently anticipated this exact package by name; deferring it indefinitely would leave every one of those promises permanently unfulfilled, and would mean any eventual application-layer rendering effort reimplements its own, likely mutually inconsistent, notion of "what does a chart, a dashboard, or an exported report look like" for each of the six evidence families. - A single, larger "agents + visualization" package, reasoning that presenting an agent's decision is a natural extension of deciding it. Rejected - no future package is expected to consume
visualization's own output types the wayvisualizationconsumes every other package's, so there is no symmetry argument for bundling it with its one immediate neighbor the way there arguably was for earlier adjacent pairs; bundling the two would also obscure the fact thatDashboard's own object-model shape (no execution lifecycle) is deliberately different fromTask's (see "Why a Separate Package," item 3). - Ship at least one concrete implementation of
Visualization/Renderernow (a reference HTML renderer, at minimum), on the reasoning that a visualization package with nothing to actually render is of limited immediate use. Rejected - see "Alternatives Rejected" below; this was the most seriously debated alternative, mirroring the equivalent debate ADR-0006 through ADR-0011 each recorded for their own interface-only capabilities. - Bundle a specific charting or document-generation library (Plotly, Matplotlib, Jinja2, WeasyPrint) as a required or optional direct dependency of this package, reasoning that a platform this focused on operational presentation should ship a working renderer out of the box. Rejected - see "Alternatives Rejected" below.
Alternatives Rejected¶
Shipping at least one concrete implementation of Visualization or Renderer is rejected for the same three compounding reasons ADR-0006 through ADR-0011 already established for their own interface-only capabilities, restated at this layer:
- Scope creep into charting-library/document-generation territory. Choosing a specific charting approach, templating engine, or document-generation strategy is exactly the kind of implementation decision this package's charter (governing spec §3.1, §3.5, §4) excludes. Shipping one "just as a starting point" would establish a precedent that quietly widens
visualization's scope every time a plausible-looking rendering feature seems worth adding. - Premature commitment to an interface's shape via its first implementation. Defining
VisualizationandRenderernow and deferring their first concrete implementations to independently-versioned future plugins keeps each contract general, rather than shaped by whichever rendering library happened to ship first. - The package's charter is explicitly to remain rendering-backend-independent, per its own brief: never perform business computation, only present it, without coupling the architecture to any specific charting or document-generation implementation. Shipping a concrete implementation backed by any one library would be the first, precedent-setting violation of that explicit non-negotiable.
Bundling a specific charting or document-generation library as a dependency of this package was the second most seriously considered alternative, and is rejected for a related but distinct reason:
- It would make
visualizationthe first package in this series whose Foundation Layer position is coupled to a third-party presentation library's own release cadence, API stability, and licensing terms - a materially different kind of dependency than the platform's existing, deliberately dependency-light optional extras, each of which is an interchangeable backend behind an already-stable interface, never a mandatory single choice baked into the architecture itself. - No single charting or document-generation library is named in this package's own brief as preferred over any other; bundling any one would be an unjustified, arbitrary architectural commitment the brief itself does not make.
- The
Visualization/Rendererinterfaces (governing spec §8, §16) already give every rendering approach - and any future one - an equally first-class path into the platform via the identical plugin mechanism every other extension point already uses (governing spec §28); no capability is lost by declining to bundle one now.
Long-Term Evolution¶
This package is expected to grow along the same axes ADR-0006 through ADR-0011 already established for analytics, decision, digital_twin, simulation, optimization, and agents, without requiring a new ADR:
- New concrete
Visualization/Rendererimplementations within existing categories, or new savedDashboards/Themes - ordinary, checklist-governed extension (governing spec §28), not an architectural change. - The first concrete implementations of
VisualizationandRenderer, whenever they arrive - expected to land as plugins (a Plotly-, Matplotlib-, D3-, or terminal-backed adapter being the most plausible first instances) against the already-stable interfaces this design defines. - A production-grade
DashboardRepositorybackend (SQL, document store) implementingcore.BaseRepository[Dashboard, str]directly - no code change to this package required (governing spec §22).
Unlike ADR-0006 through ADR-0011, this ADR names no future package expected to consume visualization's own output types - visualization is the platform's final layer, and none is anticipated above it. A new ADR would still be warranted if a future need arose to: change visualization's position in the dependency chain; permit visualization to be imported by any lower package (which would indicate a design error, not a legitimate evolution); introduce a third registry alongside REGISTRY/RENDERERS, or merge the two, reversing the deliberate two-registry trade-off recorded above; couple this package's architecture to a specific charting or document-generation library, reversing the backend-independence guarantee recorded above; or introduce a dedicated rendering-output cache, reversing the deliberate non-need recorded above, in light of a measured performance problem this ADR did not anticipate.
Future Compatibility¶
Every public type this package introduces (governing spec §9, PresentationModel; §16, RenderedOutput; §13, Report) is a plain, frozen core.BaseValueObject (or, for Dashboard itself, a frozen core.BaseEntity), serializable via the platform's existing core.serialization mechanism, with no visualization-internal state leaking into any type a hypothetical future package would need to consume - the identical compatibility guarantee ADR-0006 through ADR-0011 each already established for their own packages' result types, extended to the final layer. Unlike every prior ADR in this series, this guarantee is offered for its own sake, as a matter of platform-wide consistency, rather than to satisfy a named future consumer - this specification's own Future Roadmap (governing spec §34) names no package above visualization and proposes none. The two interface-only ABCs this package defines (governing spec §8, §16), together with the closed VisualizationCategory enum (governing spec §26), exist specifically so that whichever future rendering backend or presentation need arrives can implement against a contract that already exists, closing out, at last, every "future package promise" six already-locked specifications made on this package's behalf before it existed.
This ADR governs the existence and shape of mineproductivity.visualization as a package. See 12_Visualization_Design_Specification.md for the full object model and 12_Visualization_Implementation_Checklist.md for the locked implementation contract.