Skip to content

Contract Pattern

Z-M-Huang edited this page Apr 28, 2026 · 4 revisions

Contract Pattern

Every extension category has a typed, versioned contract. This page is the meta-shape — the fields every contract specifies, the rules they follow, and how they interact with core.

Per-category contracts (Providers, Tools, Hooks, UI, Loggers, State Machines, Commands, Session Stores, Context Providers) conform to this pattern. Each category's page documents its category-specific shape on top.

Meta-contract guidance. This page describes the shared meta-shape that every category contract conforms to. It is not itself a per-category contract and carries no independent contractVersion. Breaking changes to the meta-shape are recorded on the affected category contracts' changelogs; each per-category contract owns its own contractVersion.

Pre-release: contracts remain at 1.0.0. Iterations live in the existing changelog entry, not new SemVer entries. The first 2.0.0 bump occurs only after the first public release.


The meta-shape

classDiagram
    class ExtensionContract~T~ {
        kind : CategoryKind
        contractVersion : SemVer
        requiredCoreVersion : SemVerRange
        lifecycle : LifecycleFns
        configSchema : JSONSchema
        loadedCardinality : Cardinality
        activeCardinality : Cardinality
        stateSlot : StateSlotShape | none
        discoveryRules : DiscoveryRules
        reloadBehavior : ReloadBehavior
    }
    class LifecycleFns {
        init
        activate
        deactivate
        dispose
    }
    ExtensionContract~T~ --> LifecycleFns
Loading

Every field is normative (ten fields; the per-extension severity field was removed in Q-3). An extension whose contract omits a field, or mis-specifies its type, fails validation.


Fields

kind

The category. One of: Provider, Tool, Hook, UI, Logger, StateMachine, Command, SessionStore, ContextProvider. Fixed at load; an extension cannot change category.

contractVersion

Semver string. The version of the category contract this extension speaks. Core refuses to load extensions whose contractVersion is incompatible with the core version it bundles. See Versioning and Compatibility.

requiredCoreVersion

Semver range. The core versions this extension is known to work with. Core refuses to load extensions whose range excludes the current core version.

lifecycle

The four lifecycle entry points: init, activate, deactivate, dispose. Semantics in Extension Lifecycle.

An extension may implement all four or a subset; unimplemented ones default to no-op. dispose is always idempotent.

configSchema

A JSON-Schema-shaped document describing the extension's own configuration block. Validated at load by the Validation Pipeline. Core uses it to:

  • Reject malformed config before the extension sees it.
  • Route config into host.config.readOwn().
  • Produce diagnostics pointing at exact field paths.

Config schemas live in the contract, not in implementation — so the wiki can describe what a conforming extension accepts.

loadedCardinality

How many extensions of this kind may load simultaneously in a session.

  • unlimited for most categories.
  • n for categories with a numeric cap.
  • one for categories where only one can exist loaded at all (rare).

activeCardinality

How many of the loaded extensions may be active at once.

  • unlimited — everything loaded can be active (Providers, Tools, Hooks, Loggers, Commands, Context Providers, UI — subscriber, interactor, and region roles coexist; see Q-9 in Cardinality and Activation).
  • one — only one active at a time (Session Store).
  • one-attached — only one attached at a time (State Machines; sole use of this value).

See Cardinality and Activation for the precise definitions of active vs attached.

stateSlot

Optional per-extension state persisted by the active Session Store alongside the session manifest — the manifest records only slot metadata (see Session Manifest § Shape); the payload itself lives in a separate persistence surface owned by the store. Contract-declared shape; core serializes and restores it. Extensions read/write via host.session.stateSlot(extId). See Extension State.

An extension without a state slot is entirely stateless across resumes. An extension with a state slot declares serialization shape and a version for drift handling.

discoveryRules

How the extension declares itself to discovery. Category folder, naming expectations, ordering-manifest participation. Implementation details belong to code; this field is about the convention an extension conforms to.

reloadBehavior

Whether the extension can be hot-reloaded mid-session. See Extension Reloading. Options: in-turn (can reload at any stage boundary), between-turns (only outside a turn), never (requires a session restart).


What every contract page looks like

Every per-category contract page in contracts/ should include, in order:

  1. Intent — what the category plugs in and why it is an extension.
  2. Contract shape — the fields on top of the meta-shape.
  3. contractVersion (current) and a Changelog section.
  4. Lifecycle — anything category-specific beyond what Extension Lifecycle documents.
  5. Config schema — the shape of the extension's config.
  6. Cardinality — loaded and active, with rationale.
  7. State slot — shape and drift behavior, or "none".
  8. Discovery path — where extensions of this kind are found.
  9. Ordering rules — for ordered categories (Hooks).
  10. Reload behavior — in-turn / between-turns / never, with rationale.
  11. Interaction with core — which core surfaces the extension reaches (Host API services, registries, etc.).
  12. Security notes — what must be true for a conforming extension.

Reference pages in reference-extensions/ must not redefine any of these fields. They link to the contract page.

Failure-handling note. Per Q-3 there is no per-extension severity field. Any extension that fails validation is disabled (omitted from the loaded set); the session continues. The TUI startup surface aggregates failures into a navigable counter (N warnings, M errors). See Validation Pipeline.


Invariants

  • One category per extension. No multi-role extensions.
  • One contract version per extension load. An extension cannot speak v1 for one handler and v2 for another.
  • Contract is normative; reference is illustrative. When text diverges, the contract wins.
  • Config is validated; runtime fields are not. Runtime shape is a code concern, not a wiki concern.
  • No extension reaches another extension's state slot. Only host.session.stateSlot(extId) and only for its own extId.

Where the pattern is applied

Per-category contract pages:

# Category Page
1 Providers Providers
2 Tools Tools
3 Hooks Hooks
4 UI UI
5 Loggers Loggers
6 State Machines State Machines
7 Commands Commands
8 Session Stores Session Store
9 Context Providers Context Providers

Changelog

The meta-shape page itself is unversioned. This changelog records meta-shape changes whose contractVersion bumps land on the affected per-category contract pages.

Per-extension severity field removed

  • Dropped the per-extension severity field from the meta-shape. The eleven-field shape becomes ten.
  • Failure handling: any extension failing validation is disabled rather than session-fatal; the session continues. See Validation Pipeline.

Introduction

Reading

Core runtime

Contracts

Category contracts

Context

Security

Runtime behavior

Operations

Providers (bundled)

Integrations

Reference extensions

Tools

UI

Session Stores

Loggers

Providers

Hooks

Context Providers

Commands

Case studies

Flows

Maintainers

Clone this wiki locally