Files
msd-core/docs/reference/host-integration-interface.md
Tom Boucher c0fd2e3f4c feat(#3673): add dispatch.maxConcurrency axis and dispatch-capacity query (#4162)
* test(#3673): add failing tests for dispatch.maxConcurrency axis and dispatch-capacity CLI route

Extends tests/host-integration.test.cjs with negotiateHostCapabilities
maxConcurrency negotiation coverage (test matrix rows 1-15, including a
fast-check property test) and a new #3673 dispatch-capacity CLI route
describe block spawning the real gsd-tools.cjs (rows 16-25). Extends
tests/host-integration-validator-parity.test.cjs with an all-19-descriptor
maxConcurrency presence/validity sweep (row 26) and adds a hostile-input
validator test to host-integration.test.cjs (row 27).

The dispatch.maxConcurrency field does not exist yet, so these tests fail.

* feat(#3673): add dispatch.maxConcurrency axis, negotiation, validator parity, and the dispatch-capacity query

Adds a numeric dispatch.maxConcurrency sub-field to the Host-Integration
Interface (ADR-1239 Phase 1), following the existing dispatch.isolation
sub-field pattern: DispatchCapability interface, SAFE_DEFAULTS/PROFILE_BASELINES
floors, and a negotiateHostCapabilities branch that passes through a positive
safe integer and fails closed to 1 otherwise (no engine-side reduction, per
the design doc's explicit rejection of a min(host,engine) rule).

capability-validator.cjs gains parity validation for the new field (optional,
positive safe integer or the "undocumented" sentinel — mirroring isolation's
"added after existing descriptors" treatment).

gsd-tools.cjs gains a new `query dispatch-capacity` route, a pure-read sibling
of `dispatch-isolation` with no side effects: live env
(GSD_DISPATCH_MAX_CONCURRENCY) > descriptor > fallback-to-1 precedence.

All 19 capabilities/*/capability.json descriptors now declare
dispatch.maxConcurrency: claude carries the one cited value (20, per
code.claude.com/docs/en/sub-agents); the other 18 carry "undocumented"
(not yet researched for this axis).

* docs(#3673): document dispatch.maxConcurrency and add its citation row to the capability matrix

Updates docs/reference/host-integration-interface.md's dispatch struct entry
(also backfilling the previously-undocumented isolation/backgroundDispatch
sub-fields found stale in the same table) and adds fail-closed/live-transport
precedence prose for the new maxConcurrency field.

Adds a dispatch.maxConcurrency row (with citation) to all 19 host sections in
docs/reference/host-integration-capability-matrix.md — required for
tests/host-integration-descriptors.test.cjs's kimi-code matrix-parity check,
which asserts every declared dispatch sub-axis is documented there.

Updates docs/how-to/add-or-update-a-host-integration.md's dispatch checklist
and example descriptor block to mention maxConcurrency (and, likewise
backfilling a stale gap, isolation/backgroundDispatch).

* fix(#3673): extract shared maxConcurrency validator, drop dead reserved-name check

Exports isPositiveSafeInteger from src/host-integration.cts as the single
source of truth for the dispatch.maxConcurrency positive-safe-integer
contract; negotiateHostCapabilities and gsd-tools.cjs's routeDispatchCapacity
now both call it instead of independently reimplementing the same predicate.

Also removes the __proto__/constructor/prototype reserved-name branch from
capability-validator.cjs's maxConcurrency check — copy-pasted from the
string-enum fields above it, but unreachable for a numeric field (the
generic positive-safe-integer branch already rejects any string) and absent
from maxDepth, the field the code's own comment claims to mirror.

---------

Co-authored-by: sim <sim@local>
2026-09-01 21:39:12 -04:00

5.3 KiB

Reference: the Host-Integration Interface

This is the normative reference for the GSD Host-Integration Interface (ADR-1239) — the versioned, negotiated contract over which any host embeds GSD's orchestration loop. The published surface is the SDK entry (src/host-integration-sdk.cts); this document specifies every symbol on it.

The governing principle: every axis value a host declares must come from that host's authoritative documentation. Where docs are silent, the host declares the undocumented sentinel and the engine degrades fail-closed — it never assumes a capability.


Protocol version

PROTOCOL_VERSION (a positive integer) is the interface version. It governs the negotiated capability set; see the versioning policy for what a bump means.

The nine negotiated axes

HOST_INTEGRATION_AXES is the closed vocabulary. Each axis takes a documented value (or the undocumented sentinel):

Axis Values
embeddingMode imperative | declarative
commandSurface slash-file | slash-programmatic | slash-toml | palette | prose-only
dispatch struct: { namedDispatch, nested, maxDepth, background, subagentToolkit, backgroundDispatch, isolation, maxConcurrency }
modelMode active | passive
hookBus host | engine | none
stateIO filesystem | sandboxed-storage | session-log-append
transport mcp | native-extension
runtime node | bun | sandboxed-web | python | go | rust | electron | other
effortSurface argv | none

dispatch.isolation (harness-worktree | orchestrator-worktree | none, added #2584) and dispatch.maxConcurrency (a positive integer, added #3673) are two dispatch sub-fields with their own per-field fail-closed floors — unlike the other five dispatch fields, they are not gated on namedDispatch. dispatch.maxConcurrency degrades to 1 (strictly sequential) on anything other than a positive safe integer (missing, the undocumented sentinel, zero, negative, fractional, an unsafe integer, or a non-numeric type); there is no negotiated host/engine reduction the way maxDepth has one — the resolved value passes through unchanged. Its live transport (GSD_DISPATCH_MAX_CONCURRENCY) is read only at the CLI query layer (gsd-tools query dispatch-capacity), never inside this pure negotiation module, and takes precedence over the descriptor value, which in turn takes precedence over the 1 fallback.

Classification + negotiation

  • profileOf(axes) → 'programmatic-cli' | 'declarative-cli' | 'ide' | null.
  • negotiateHostCapabilities(host, engine) → { protocolVersion, effective, points, warnings } — the in-process negotiation. Pure; never throws.
  • handleHandshakeRequest(request) / buildHandshakeRequest(descriptor) — the serialized (out-of-process) form of the same negotiation, JSON-safe across a wire boundary. The two are consistent: a serialized request yields the same effective axes as the in-process call.
  • degradationFor(point, axes) → { level, fallback, unknown? } for one of the six interface points (command | dispatch | model | hooks | state | artifact).
  • hookEventSurfaceFor(hookEvents) → the host-fireable hook events for a dialect ('claude' | 'gemini' | 'opencode-subset'), or null if unknown.
  • shouldFlattenDispatch(dispatch) → true when the orchestrator must run inline (fail-closed).

The engine adapters

All five satisfy a common { kind, runtime, install, uninstall } shape and are constructed fail-closed (they throw if a required host primitive is absent):

Adapter Factory Selected by
Embedding (declarative) createDeclarativeAdapter({runtime}) embeddingMode: 'declarative'
Embedding (imperative) createImperativeAdapter({runtime}) embeddingMode: 'imperative' (also exposes the composed registry)
Model createModelAdapter({modelMode}, {sendRequest?}) modelMode ('active' needs a host sendRequest)
Hook bus createHookBus({bus}, {hostEmit?}) hookBus ('host' needs a host hostEmit)
State IO createStateIO({io}, {backend?}) stateIO ('sandboxed-storage'/'session-log-append' need a host backend)

The declarative + imperative adapters delegate install/uninstall in-process to the same installRuntimeArtifacts engine function bin/install.js uses, so adapter output is byte-identical to a first-party install (gated by the differential attribution check, tests/emitted-attribution.test.cjs, ADR-2719).

Profiles

PROFILE_BASELINES fixes the three reference profiles:

Profile Baseline
programmatic-cli imperative, slash-file, host bus, filesystem, mcp, node
declarative-cli declarative, slash-file, host bus, filesystem, mcp, node
ide imperative, palette, active model, engine bus, sandboxed-storage, sandboxed-web

See also