This document describes the shared framework that NashTwin's first-party model plugins use today. It is the internal contract behind vertical plugins such as SaaS, Restaurant, Defense Platform OEM, Pharma Commercial Pipeline, and the Personal Trainer / Gym Model.
Use this document when you need to add or review a first-party model plugin that participates in the digital twin, the dashboard plugin catalog, and the shared Nash Optimization runtime.
For the public third-party manifest contract, see the Plugin Contract. For general submission guidance, see the Plugin SDK.
Scope
This contract covers the common first-party framework for model plugins:
- plugin folder structure
- manifest requirements for first-party model plugins
- shared entity service framework
- shared HTTP route framework
- shared optimization descriptor and runtime framework
- dashboard integration points
- required test and verification surfaces
This is not a promise that every model plugin is identical. Individual industries can add more entities, tighter reasoning, or more domain-specific runtime logic. The goal is to keep those differences inside a stable shared shell.
What a First-Party Model Plugin Is
A first-party model plugin is a feature package under apps/web/src/features/<plugin-name>/ that does four things through shared framework code:
- Contributes graph-backed model entities.
- Exposes those entities through the generic model-plugin CRUD routes.
- Registers optimization scenarios through the shared scenario registry.
- Resolves runtime-backed recommendations through the shared optimization runtime resolver and reasoning engine.
The framework is intentionally opinionated:
- runtime ownership is plugin-local
- registration is centralized
- entity CRUD uses shared model-plugin helpers
- optimization explanations are built through the shared reasoning path
Standard File Set
Every first-party model plugin should ship the same baseline files.
apps/web/src/features/<plugin-name>/
manifest.json
manifest.test.ts
server/
recommendations.ts
runtime.ts
service.ts
apps/web/src/components/plugins/
<plugin-name>-panel.tsx
<plugin-name>-panel.test.tsx
apps/web/src/app/api/twin/<plugin-name>/entities/
route.ts
[entityId]/route.ts
Responsibility of each file
manifest.json: first-party plugin identity, graph contributions, and optimization scenario declarations.manifest.test.ts: manifest validation and schema-contribution checks.server/service.ts: shared model-plugin entity definitions, derived artifact sync, and CRUD exports.server/recommendations.ts: descriptor-backed fallback scenarios for discovery and non-runtime execution paths.server/runtime.ts: plugin-local runtime snapshot loading and live scenario resolution.- panel wrapper: thin wrapper around the shared
ModelEntityPanel. - collection route: shared GET and POST handlers.
- delete route: shared DELETE handler.
Manifest Requirements
A first-party model plugin still uses the standard plugin manifest shape, but it almost always includes both:
graphExtensionoptimizationExtension
Expected manifest characteristics
capabilitiesincludesgame-theory-strategy.eventstypically includetwin.updated,simulation.requested,simulation.completed,recommendation.generated, andoutcome.recorded.graphExtension.contributionsdefine both observation-layer entities and derived-overlay entities.optimizationExtension.scenariosdefine stable scenario ids, labels, descriptions, and default game metadata.- scenario labels should include the industry classifier, for example
(Restaurant)or(Personal Trainer / Gym).
Stable scenario ids matter
Scenario ids are part of the shared optimization registry and must remain unique across all first-party manifests. The scenario registry throws on collisions, so model plugins should treat scenario ids as stable API.
Shared Entity Framework
The entity-management side of a model plugin is built on the shared model-plugin service framework.
Core pieces:
createModelPluginServiceModelEntityDefinitionModelPanelConfigcreateModelEntityCollectionRouteHandlerscreateModelEntityDeleteRouteHandler
What server/service.ts does
Each plugin's server/service.ts defines:
- plugin constants
- schema ids
ModelEntityDefinition[]- a
ModelPanelConfig - a
createModelPluginService(...)call - exported CRUD helpers
Typical exported functions:
create<PluginName>Entitydelete<PluginName>Entitylist<PluginName>Entities
Entity definitions
Each ModelEntityDefinition describes:
- the form fields shown in the shared panel
- how input is mapped into graph node properties
- optional entity-to-entity relations
- audit category and sensitivity
- graph schema id
- display text for list rendering
The shared panel and shared HTTP layer consume these definitions directly, so the plugin only needs to supply domain-specific metadata.
Derived artifacts
Most first-party model plugins also create derived overlay nodes inside syncDerivedArtifacts.
That function usually:
- clears prior derived artifacts
- groups relevant observation nodes by schema id
- resolves one or more runtime scenario top moves with
resolveRuntimeScenarioTopMove - writes derived nodes summarizing the current best move
This keeps the digital twin aligned with the same optimization path the dashboard uses.
Shared HTTP Routes
First-party model plugins do not build bespoke route handlers for normal entity CRUD.
Instead:
- collection routes call
createModelEntityCollectionRouteHandlers - delete routes call
createModelEntityDeleteRouteHandler
That means the plugin-specific route files are intentionally thin wrappers. They mainly bind:
pluginIdpluginName- entity definitions
- the CRUD functions exported by
server/service.ts
This keeps route behavior consistent across all first-party model plugins.
Shared Optimization Framework
First-party model plugins integrate with the existing Nash Optimization path rather than special-case simulation routes.
Core shared pieces:
apps/web/src/features/nash-optimization/server/scenarios.tsapps/web/src/features/nash-optimization/server/optimize.tsapps/web/src/features/shared/optimization/first-party-scenario-registry.tsapps/web/src/features/shared/optimization/server/runtime-registry.tsapps/web/src/features/shared/optimization/server/runtime-resolver.tsapps/web/src/features/shared/optimization/server/reasoning-engine.ts
Descriptor layer
server/recommendations.ts exports a function like:
export function getMyModelScenarioDescriptor(
scenarioId: string,
): FirstPartyScenarioDescriptor | null
The descriptor layer exists so the platform can:
- discover scenario metadata
- provide fallback moves when runtime data is unavailable
- keep scenario labels and solver families stable
Each descriptor defines:
algorithmVersionlabelsolverFamilynodesEvaluatedgameExampletopMoves- optional
modelArtifactScenarioId
Plugin-Local Runtime Layer
server/runtime.ts owns live execution for that plugin's scenarios.
This is one of the main post-refactor rules: runtime logic is plugin-local, not centralized in a legacy stub executor.
What runtime files usually contain
- schema-id constants
- a runtime snapshot interface
- a
load...RuntimeSnapshot(...)function - one or more game-matrix builders
- one or more runtime move builders
resolve<PluginName>RuntimeOptimizationScenario(...)
That resolver returns a RuntimeOptimizationScenarioResolution | null.
Runtime behavior expectations
- return
nullfor scenarios not owned by the plugin - load graph-backed tenant state through the shared runtime DB shape
- build top moves using shared helpers such as
createRuntimeMove - use the scenario's declared game family to choose the correct solver branch
- return model artifacts only when appropriate
- slice moves using
preferredMoveCount
Reasoning and Explanation Contract
The runtime layer is not finished when it only returns titles.
Every first-party model plugin runtime should provide enough reasoning for the shared explanation builder to produce coherent output.
At minimum, top moves should carry:
reasoning.evidencereasoning.policyNotesreasoning.solverNotesriskSummary
The shared reasoning engine then enriches those moves into explanation-ready output for the optimize response.
Practical rule
If a new model plugin returns a top move but cannot explain:
- what graph evidence drove it
- what governance or policy limits apply
- what solver path was used
- what the main execution risk is
then the plugin is not complete.
Central Registration Points
A first-party model plugin is not active until it is registered in the shared seams.
Required registration points:
apps/web/src/features/plugin-registry/first-party.tsAdds the manifest to the first-party manifest list.apps/web/src/features/shared/optimization/first-party-scenario-registry.tsAdds the scenario-descriptor switch case.apps/web/src/features/shared/optimization/server/runtime-registry.tsAdds the plugin-local runtime resolver.apps/web/src/app/(dashboard)/dashboard/plugins/page.tsxLoads entities and surfaces the panel card in the dashboard.
Without these registrations, the plugin may exist on disk but will not appear in discovery or execution.
Dashboard Integration
The dashboard plugin page is the shared surfacing layer for first-party model plugins.
Each plugin adds:
- panel import
- service import for
list...Entities - plugin id constant
- plugin install lookup
- entity loading inside the
Promise.all - one
CollapsibleSectionCard
The panel itself should stay thin and rely on the shared ModelEntityPanel.
Testing Contract
Every first-party model plugin should extend the shared verification surfaces.
Minimum coverage:
manifest.test.ts- panel smoke test
- scenario bank coverage in
apps/web/src/features/nash-optimization/server/scenarios.test.ts - runtime-backed optimize coverage in
apps/web/src/features/nash-optimization/server/optimize.test.ts
What optimize coverage should verify
- the plugin's scenario resolves through the shared optimize path
- the result carries the expected
pluginId - the expected solver family is used
- explanation source is
plugin-runtime - reasoning content is non-empty
Why this matters
These tests prove that the plugin is flowing through the shared model-plugin contract instead of bypassing it with custom logic.
Minimal Workflow for Adding a New First-Party Model Plugin
- Create the feature folder and manifest.
- Add
server/service.tsusing the shared model-plugin service helpers. - Add
server/recommendations.tswith stable descriptor-backed scenarios. - Add
server/runtime.tswith plugin-local runtime snapshot loading and move generation. - Add the panel wrapper and panel test.
- Add the twin entity routes.
- Register the manifest, descriptor, runtime resolver, and dashboard card.
- Extend
scenarios.test.ts. - Extend
optimize.test.tswith runtime-backed and reasoning-aware assertions. - Run
pnpm testandturbo run build.
Reference Plugins
Good current references in the repo:
saas-model: strong post-refactor runtime and explanation patternrestaurant-model: compact end-to-end model-plugin service shapedefense-platform-oem-model: clear runtime move branching by solver familypharma-commercial-pipeline-model: good example of a modern Batch E model pluginpersonal-trainer-gym-model: a recent compact plugin using the full shared contract
Summary
The shared first-party model-plugin framework keeps domain differences local while standardizing everything that should be consistent:
- manifest shape
- entity CRUD
- twin routes
- dashboard surfacing
- scenario discovery
- runtime resolution
- explanation output
- test coverage
When a new first-party model plugin follows this contract, it behaves like part of one coherent platform instead of a one-off vertical integration.