Adapter manifest reference

Each adapter under adapters/<name>/ carries a manifest.json that maps the framework's runtime-neutral role specs into a specific runner's vocabulary. Agent files are rendered, not written: gateline render reads role specs and manifests, then writes the runner's agent files. The renderer lives in packages/framework/src/render.ts, and CI enforces staleness with node packages/framework/src/main.ts render --check, which needs nothing installed. An adapter may narrow a role (fewer tools, tighter permissions) but never widen it. The role spec is the ceiling.

Honesty note: model IDs are ILLUSTRATIVE. The registry's model IDs are marked ILLUSTRATIVE in registry/models.yaml: each adopter must pin their organization's approved models and re-verify pricing. The manifest model spellings quoted below are configuration examples, not guaranteed live bindings. The opencode adapter specifically marks its Kimi binding as TEMPORARY (adapters/opencode/manifest.json): its comment says to revisit the binding once Kimi K3's open weights land on 2026-07-27, a date now past, so the binding is pending that revisit. The OpenRouter pricing entries back the token-based metering fallback; opencode reports real cost directly today, so they are not the live path (registry/models.yaml).

Three shipped adapters

AdapterOutput dirFilename templateTools style
claude-code .claude/agents/ {role}.md comma — capabilities rendered as a comma-separated tool list
copilot-cli .github/agents/ {role}.agent.md flow-list — capabilities rendered as a YAML flow list
opencode .opencode/agents/ {role}.md permission-map — capabilities rendered as allow/deny permissions

Manifest schema — key by key (union across adapters)

All three shipped manifests carry identical top-level non‑_comment key sets; only the headless block differs, where opencode adds session_field and resume_args. The renderer's loader requires adapter, output_dir, filename and tools_style as strings and roles as an array, and reads tool_map and model_map at use; model_overrides and extra_frontmatter are optional. The renderer never reads model_vendors or headless; the orchestrator does.

KeyPurpose
adapterAdapter id string, e.g. "claude-code"
output_dirRender target directory, created if missing
filenamePer-role filename template; {role} is substituted with the role name
roles[]Roles rendered for this runner. All three adapters render eight roles: analyst, architect, implementer, reviewer, verifier, ops, integrator, historian. The orchestrator role is not rendered — it is executed by the engine/human.
tools_styleHow capabilities render as tools/permissions: comma | flow-list | permission-map
tool_mapAbstract capability (read, search, write-artifacts, edit-code, shell) → runner's tool names
model_mapCapability profile (frontier-reasoning | balanced | fast-cheap) → runner model spelling
model_overridesPer-role model spelling overrides. Implements P5 pins on multi-vendor runners: the copilot-cli maps reviewer → gpt-5.4 and verifier → gemini-3-flash (against a Claude implementer); opencode maps reviewer → kimi-k2.6 and verifier → minimax-m3 (three distinct labs, three-way decorrelation). Single-vendor adapters (claude-code) leave this empty.
model_vendorsRunner spelling → vendor name, for the orchestrator's dispatch-time avoid_vendor_of check. For opencode, vendor is the underlying model lab (not the OpenRouter routing layer they are all fetched through).
extra_frontmatterExtra rendered frontmatter keys, values JSON-serialised. Example: copilot-cli sets disable-model-invocation: true and user-invocable: true; opencode sets mode: all.
headless.command[]v1 dispatch invocation template; {prompt} and {role} are substituted
headless.dispatch_promptPrompt template — {role} and {body} are substituted; defaults to {body} when absent
headless.session_fieldOptional. The field in the runner's output that names its own session for the dispatch; opencode reads sessionID. The seam records it on the ledger entry.
headless.resume_argsOptional. Extra arguments appended on a retry of the same role, task and round, with {session} substituted, so the retry continues the harness session; opencode passes ["--session", "{session}"].
headless.usage_reportMetering parse spec. Three formats: json-stdout (field paths + error/result fields, used by claude-code), static-estimate (falls back to registry estimates, used by copilot-cli), ndjson-sum (line_filter + dotted field paths over streaming JSON, used by opencode).

How gateline render consumes manifests

The renderer is part of @gateline/framework, a Node package with no runtime dependencies: it imports node: builtins and its own modules, nothing else. Manifests are JSON, and role frontmatter is read by a hand-rolled flat reader. That is what lets it run before the environment probe has fixed anything in a host repo.

renderAll lists the directories under adapters/ that contain a manifest.json, sorted by name for stable output, then for each listed role:

  1. Parses the role spec's frontmatter (requiring dispatch, capabilities, and capability_profile).
  2. Resolves the model: checks model_overrides[role] first, then falls back to model_map[capability_profile].
  3. Merges capabilities into the runner's tool names via tool_map, in order and deduplicated.
  4. Emits the rendered file: frontmatter (--- / name: / description: / tools‑or‑permission / model: / any extra frontmatter / ---) first, then the header comment ("RENDERED from roles/<role>.md by gateline render - DO NOT EDIT. Edit the role spec, then run: gateline render"), then the body, then overlay splices (overlays/_all.md and overlays/<role>.md are spliced in as project policy layers).

Mode --check (used by CI: .github/workflows/render-check.yml) reads existing rendered files and exits 1 if any are stale. A stale-render CI failure means the manifest or role spec was edited without re-rendering. A host repository gets the same check as .github/workflows/gateline-render-check.yml from gateline init.

In a host repository, gateline render resolves the core root from <prefix>/framework-lock.json; the default prefix is .gateline.

Permission model: narrow, never widen

An adapter may narrow a role (fewer tools, tighter permissions) but never widen it. The role spec is the ceiling. This principle is rendered as a property in the opencode adapter: the permission-map tools style generates a deny-by-default map (permission:, then "*": deny, then one <key>: allow per mapped tool) where only the role's declared capabilities are explicitly allowed. The function renderPermissionMap in render.ts implements this.

P5 decorrelation — per-adapter gradient

AdapterP5 decorrelation status
claude-code Single-vendor harness — P5 decorrelation is only partially honored. The interim fallback is a lineage split: the reviewer reviews the implementer's work using a different model within the same vendor family (fable reviews sonnet). Satisfiable via a second adapter.
copilot-cli P5 decorrelation is fully honored via model_overrides: reviewer → gpt-5.4 (OpenAI) and verifier → gemini-3-flash (Google), against a Claude (Anthropic) implementer.
opencode Three-way lab decorrelation: implementer uses DeepSeek, reviewer uses Moonshot (Kimi), verifier uses MiniMax — distinct companies with independent training pipelines and RLHF. All three routed through OpenRouter; vendor is the underlying lab, not the routing layer.

Dispatch-time enforcement: with more than one adapter configured, the orchestrator's dispatch seam refuses to bind Reviewer or Verifier to the Implementer's vendor (VendorPinError), so the avoid_vendor_of registry pins are enforced, not conventional. With a single adapter (the default gateline up with one --adapter) the pin is unsatisfiable and stays advisory: the router dispatches on the implementer's vendor and logs a P5 advisory once per role. Later --adapter flags are how those pins are satisfied.

Renderer constraints

  • No runtime dependencies: node: builtins only. A dependency that forces an install step in render-check.yml is the bug, not the workflow
  • Node 24 or later
  • JSON manifests, not YAML
  • Overlay splicing: overlays/_all.md is spliced into every agent; overlays/<role>.md into that one role's agent. The framework repository itself has no overlays/; they exist in host repositories, where gateline init writes the stubs and validate requires them present and non-empty
  • Stubs that are only HTML comments splice nothing
  • A rendering that encounters an unparseable manifest or missing frontmatter raises FrameworkError and exits 1 (no guesswork)