Gateway Corporate

Developer Docs

Docs/Model Plugin Contract

Model Plugin Contract

Shared framework contract for first-party model plugins covering entities, routes, runtimes, registry wiring, and optimization integration.

Updated 2026-04-137 min readdocs/model-plugin-contract.md

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:

  1. Contributes graph-backed model entities.
  2. Exposes those entities through the generic model-plugin CRUD routes.
  3. Registers optimization scenarios through the shared scenario registry.
  4. 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:

  • graphExtension
  • optimizationExtension

Expected manifest characteristics

  • capabilities includes game-theory-strategy.
  • events typically include twin.updated, simulation.requested, simulation.completed, recommendation.generated, and outcome.recorded.
  • graphExtension.contributions define both observation-layer entities and derived-overlay entities.
  • optimizationExtension.scenarios define 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:

  • createModelPluginService
  • ModelEntityDefinition
  • ModelPanelConfig
  • createModelEntityCollectionRouteHandlers
  • createModelEntityDeleteRouteHandler

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>Entity
  • delete<PluginName>Entity
  • list<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:

  1. clears prior derived artifacts
  2. groups relevant observation nodes by schema id
  3. resolves one or more runtime scenario top moves with resolveRuntimeScenarioTopMove
  4. 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:

  • pluginId
  • pluginName
  • 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.ts
  • apps/web/src/features/nash-optimization/server/optimize.ts
  • apps/web/src/features/shared/optimization/first-party-scenario-registry.ts
  • apps/web/src/features/shared/optimization/server/runtime-registry.ts
  • apps/web/src/features/shared/optimization/server/runtime-resolver.ts
  • apps/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:

  • algorithmVersion
  • label
  • solverFamily
  • nodesEvaluated
  • gameExample
  • topMoves
  • 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 null for 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.evidence
  • reasoning.policyNotes
  • reasoning.solverNotes
  • riskSummary

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.ts Adds the manifest to the first-party manifest list.
  • apps/web/src/features/shared/optimization/first-party-scenario-registry.ts Adds the scenario-descriptor switch case.
  • apps/web/src/features/shared/optimization/server/runtime-registry.ts Adds the plugin-local runtime resolver.
  • apps/web/src/app/(dashboard)/dashboard/plugins/page.tsx Loads 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

  1. Create the feature folder and manifest.
  2. Add server/service.ts using the shared model-plugin service helpers.
  3. Add server/recommendations.ts with stable descriptor-backed scenarios.
  4. Add server/runtime.ts with plugin-local runtime snapshot loading and move generation.
  5. Add the panel wrapper and panel test.
  6. Add the twin entity routes.
  7. Register the manifest, descriptor, runtime resolver, and dashboard card.
  8. Extend scenarios.test.ts.
  9. Extend optimize.test.ts with runtime-backed and reasoning-aware assertions.
  10. Run pnpm test and turbo run build.

Reference Plugins

Good current references in the repo:

  • saas-model: strong post-refactor runtime and explanation pattern
  • restaurant-model: compact end-to-end model-plugin service shape
  • defense-platform-oem-model: clear runtime move branching by solver family
  • pharma-commercial-pipeline-model: good example of a modern Batch E model plugin
  • personal-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.