Skip to main content

supercode_harness/
support.rs

1//! Canonical implementation inventory for external coding harnesses.
2//!
3//! This registry describes wiring that exists in the compiled core. It does
4//! not claim that a harness has passed a real executable smoke test; the
5//! support audit joins this inventory with behavioral probe receipts and
6//! tracker state before it calls anything verified.
7
8use std::collections::BTreeMap;
9
10use serde::{Deserialize, Serialize};
11
12use crate::{
13    AcpRuntimeBackend, ClaudeCodeRuntimeBackend, CodexRuntimeBackend, HarnessId,
14    OpenCodeRuntimeBackend, PiRuntimeBackend, RuntimeBackend, RuntimeCapabilities,
15    RuntimeConnectLaunch, RuntimeLaunch,
16};
17
18/// Schema emitted by [`harness_support_registry`].
19pub const SUPPORT_REGISTRY_SCHEMA: &str = "supercode.support-registry.v1";
20
21/// How a primitive is wired into the compiled core.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
23#[serde(rename_all = "snake_case")]
24pub enum ImplementationKind {
25    /// A harness-specific implementation is registered.
26    BuiltIn,
27    /// A protocol-generic implementation is usable with a known launch.
28    GenericProtocol,
29    /// No implementation is present.
30    Absent,
31}
32
33/// Persisted-session and translation implementation facts.
34#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
35pub struct NativeSupport {
36    /// Whether the catalog can discover this harness's sessions.
37    pub discover: ImplementationKind,
38    /// Whether the core can load this harness's native persisted format.
39    pub load: ImplementationKind,
40    /// Whether the generic follower can open this harness's native storage.
41    pub follow: ImplementationKind,
42    /// Whether the canonical session can import this native format.
43    pub import: ImplementationKind,
44    /// Whether the canonical session can export this native format.
45    pub export: ImplementationKind,
46}
47
48/// Live runtime wiring known without launching the real executable.
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
50pub struct RuntimeSupport {
51    /// Harness-specific or protocol-generic adapter registration.
52    pub implementation: ImplementationKind,
53    /// Protocol spoken by the adapter.
54    pub protocol: String,
55    /// Command used when callers do not provide an override.
56    pub default_launch: Option<RuntimeLaunch>,
57    /// Connect-mode launch for gateway harnesses: where a running endpoint's
58    /// address and credential live in the harness's own config file. `None`
59    /// for spawn-only harnesses; declaring one is an explicit registry
60    /// decision, never inferred.
61    #[serde(default, skip_serializing_if = "Option::is_none")]
62    pub connect_launch: Option<RuntimeConnectLaunch>,
63    /// Static adapter capabilities. Optional protocol features are only true
64    /// for known agents that advertise them; the adapter validates them again
65    /// during the live handshake.
66    pub capabilities: RuntimeCapabilities,
67}
68
69/// One compiled harness implementation descriptor.
70
71/// The twelve Domain 11 concepts, in plan order
72/// (`docs/plans/orchestration-domain-11-2026-09-02.md`).
73pub const ORCHESTRATION_CONCEPTS: &[&str] = &[
74    "scheduled_job",
75    "run",
76    "conversation",
77    "pending_request",
78    "profile",
79    "skills",
80    "memory",
81    "delivery_target",
82    "channel",
83    "routing",
84    "inbound_trigger",
85    "gateway_health",
86];
87
88/// One orchestration concept's tiers for one harness (ORCH-4).
89#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
90pub struct ConceptSupport {
91    /// One of [`ORCHESTRATION_CONCEPTS`].
92    pub concept: String,
93    /// Read tier: supercode lists/inspects the concept from the harness's own files or CLI.
94    pub observed: ImplementationKind,
95    /// Write tier: supercode mutates the concept through the harness's own verb.
96    pub controlled: ImplementationKind,
97    /// `harness.v1.<noun>.<verb>` methods backing the non-`Absent` tiers.
98    #[serde(default, skip_serializing_if = "Vec::is_empty")]
99    pub methods: Vec<String>,
100}
101
102/// Per-concept observed / controlled tiers for one harness (additive to the
103/// v1 registry schema, like `connect_launch`).
104#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
105pub struct OrchestrationSupport {
106    /// Exactly [`ORCHESTRATION_CONCEPTS`], in order.
107    pub concepts: Vec<ConceptSupport>,
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
111pub struct HarnessSupportDescriptor {
112    /// Stable harness identifier.
113    pub id: HarnessId,
114    /// Human-readable name.
115    pub display_name: String,
116    /// Native persistence/translation implementation.
117    pub native: NativeSupport,
118    /// Live runtime implementation.
119    pub runtime: RuntimeSupport,
120    /// ORCH-4: orchestration concept tiers (defaults to all-`Absent`).
121    #[serde(default)]
122    pub orchestration: OrchestrationSupport,
123}
124
125/// Machine-readable compiled support inventory.
126#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
127pub struct SupportRegistryReport {
128    /// Report schema.
129    pub schema: String,
130    /// Harness descriptors, in stable product order.
131    pub harnesses: Vec<HarnessSupportDescriptor>,
132}
133
134/// Whether a harness can enter its own sandbox by replacing its process image.
135///
136/// WebAssembly has no process replacement, so a harness asked to sandbox itself
137/// there aborts before its handshake. Everything the loader starts on that
138/// target is already confined to the browser it runs in.
139pub(crate) fn self_sandbox_supported() -> bool {
140    !cfg!(target_family = "wasm")
141}
142
143/// Grok refuses its `workspace` sandbox when its home is a symlink ("symlinked GROK_HOME is
144/// not allowed under sandbox write-deny"), so a symlinked `~/.grok` is named by the path it
145/// resolves to: the same directory, which the sandbox can then protect. An explicit
146/// `GROK_HOME` is left to the caller.
147pub(crate) fn grok_home_env() -> BTreeMap<String, String> {
148    let mut env = BTreeMap::new();
149    if std::env::var_os("GROK_HOME").is_none() {
150        if let Some(home) = supercode_interchange::user_home()
151            .map(std::path::PathBuf::into_os_string)
152            .map(std::path::PathBuf::from)
153        {
154            let grok = home.join(".grok");
155            let linked = std::fs::symlink_metadata(&grok)
156                .map(|metadata| metadata.file_type().is_symlink())
157                .unwrap_or(false);
158            if let (true, Ok(resolved)) = (linked, grok.canonicalize()) {
159                env.insert("GROK_HOME".into(), resolved.to_string_lossy().into_owned());
160            }
161        }
162    }
163    env
164}
165
166/// A headless Grok runtime's environment: no agent dashboard, and [`grok_home_env`].
167pub(crate) fn grok_env() -> BTreeMap<String, String> {
168    let mut env = grok_home_env();
169    env.insert("GROK_AGENT_DASHBOARD".into(), "0".into());
170    env
171}
172
173/// Builds Grok's stdio launch, asking it to sandbox itself where it can.
174fn grok_arguments() -> Vec<String> {
175    let mut arguments: Vec<String> = Vec::new();
176    if self_sandbox_supported() {
177        arguments.push("--sandbox".into());
178        arguments.push("workspace".into());
179    }
180    arguments.push("agent".into());
181    arguments.push("--no-leader".into());
182    arguments.push("stdio".into());
183    arguments
184}
185
186fn built_in_native() -> NativeSupport {
187    NativeSupport {
188        discover: ImplementationKind::BuiltIn,
189        load: ImplementationKind::BuiltIn,
190        follow: ImplementationKind::BuiltIn,
191        import: ImplementationKind::BuiltIn,
192        export: ImplementationKind::BuiltIn,
193    }
194}
195
196fn built_in_runtime(
197    backend: &dyn RuntimeBackend,
198    protocol: &str,
199    launch: RuntimeLaunch,
200) -> RuntimeSupport {
201    RuntimeSupport {
202        implementation: ImplementationKind::BuiltIn,
203        protocol: protocol.into(),
204        default_launch: Some(launch),
205        connect_launch: None,
206        capabilities: backend.capabilities(),
207    }
208}
209
210/// Return the single compiled inventory used by product surfaces and audits.
211
212/// Derive a harness's orchestration tiers from what the registry already
213/// proves: a harness whose sessions load natively is `observed` for the
214/// conversation concept (`sessions.discover`/`load`), and a runtime door that
215/// answers protocol requests is `controlled` for pending requests
216/// (`runtimes.respond`). Every other cell is `Absent` until its ORCH item
217/// lands and adds its method here.
218pub fn orchestration_support(descriptor: &HarnessSupportDescriptor) -> OrchestrationSupport {
219    let concepts = ORCHESTRATION_CONCEPTS
220        .iter()
221        .map(|concept| {
222            let (observed, controlled, methods): (
223                ImplementationKind,
224                ImplementationKind,
225                Vec<&str>,
226            ) = match *concept {
227                // ORCH-18: the two harnesses that publish a client-callable
228                // cron verb are also CONTROLLED — supercode runs `hermes cron
229                // …` / `openclaw cron …` on the caller's behalf and re-reads
230                // the row (`crate::jobs_control`). supercode still schedules
231                // nothing itself; the tier means "the harness's own verb is
232                // reachable through one uniform door".
233                "scheduled_job"
234                    if crate::jobs_control::supports_job_control(descriptor.id.as_str()) =>
235                {
236                    (
237                        ImplementationKind::BuiltIn,
238                        ImplementationKind::BuiltIn,
239                        vec![
240                            "harness.v1.jobs.list",
241                            "harness.v1.jobs.get",
242                            "harness.v1.jobs.create",
243                            "harness.v1.jobs.update",
244                            "harness.v1.jobs.pause",
245                            "harness.v1.jobs.resume",
246                            "harness.v1.jobs.run",
247                            "harness.v1.jobs.delete",
248                        ],
249                    )
250                }
251                // ORCH-7: the three harnesses that HAVE scheduled jobs are read
252                // uniformly from their own stores. Claude Code stays read-only
253                // on purpose: its jobs are session-scoped runtime state created
254                // by the model inside a session (`CronCreate`), so there is no
255                // harness verb for a client to call.
256                "scheduled_job" if crate::jobs::supports_jobs(descriptor.id.as_str()) => (
257                    ImplementationKind::BuiltIn,
258                    ImplementationKind::Absent,
259                    vec!["harness.v1.jobs.list", "harness.v1.jobs.get"],
260                ),
261                // ORCH-8: a harness is `observed` for runs when it KEEPS a
262                // fire store the loader in `crate::runs` opens. Claude Code
263                // has scheduled jobs but no run store — its fires are turns —
264                // so it is deliberately absent here while `scheduled_job`
265                // above is built-in for it.
266                "run" if crate::runs::supports_runs(descriptor.id.as_str()) => (
267                    ImplementationKind::BuiltIn,
268                    ImplementationKind::Absent,
269                    vec!["harness.v1.runs.list", "harness.v1.runs.get"],
270                ),
271                // ORCH-19: a harness is CONTROLLED for conversations when it
272                // publishes at least one lifecycle DOOR supercode can drive —
273                // Codex's `archive`/`delete` CLI verbs, OpenCode's HTTP session
274                // API, Hermes's and OpenClaw's `/new`/`/reset` slash commands
275                // typed into a live driven session, Hermes's `sessions delete`,
276                // and supercode's own store. The advertised methods come from
277                // `sessions_control`'s own door table, so a method can never be
278                // listed here without a door behind it. Claude Code stays
279                // read-only on purpose: it publishes no lifecycle verb at all
280                // (its sessions expire on a retention window it owns).
281                "conversation"
282                    if descriptor.native.load == ImplementationKind::BuiltIn
283                        && !crate::sessions_control::controlled_methods(descriptor.id.as_str())
284                            .is_empty() =>
285                {
286                    let mut methods =
287                        vec!["harness.v1.sessions.discover", "harness.v1.sessions.load"];
288                    methods.extend(crate::sessions_control::controlled_methods(
289                        descriptor.id.as_str(),
290                    ));
291                    (
292                        ImplementationKind::BuiltIn,
293                        ImplementationKind::BuiltIn,
294                        methods,
295                    )
296                }
297                // ORC-7: the orchestrator's conversations are its BINDINGS,
298                // discovered from every profile folder's `bindings` table
299                // (`supercode_interchange::catalog::discover_orchestrator`). They are
300                // observed, not loadable: the transcript belongs to the
301                // worker harness the binding addresses and is read through
302                // that harness's own `sessions.load`.
303                // ORC-13 makes them CONTROLLED: `/new` and `/reset` are the
304                // two chat commands the daemon's reducer applies to a binding
305                // (`docs/ORCHESTRATOR-IR.md` §4.5), and the operator door now
306                // reaches that reducer from outside a chat — the daemon's own
307                // socket, or its package's CLI when the daemon is down. The
308                // methods come from `sessions_control`'s door table, so a
309                // method can never be advertised without a door behind it.
310                "conversation" if descriptor.id.as_str() == HarnessId::ORCHESTRATOR => {
311                    let mut methods = vec!["harness.v1.sessions.discover"];
312                    methods.extend(crate::sessions_control::controlled_methods(
313                        descriptor.id.as_str(),
314                    ));
315                    (
316                        ImplementationKind::BuiltIn,
317                        ImplementationKind::BuiltIn,
318                        methods,
319                    )
320                }
321                "conversation" if descriptor.native.load == ImplementationKind::BuiltIn => (
322                    ImplementationKind::BuiltIn,
323                    ImplementationKind::Absent,
324                    vec!["harness.v1.sessions.discover", "harness.v1.sessions.load"],
325                ),
326                // ORCH-9: a harness is `observed` for pending requests when
327                // its runtime door can carry one — the same flag that makes
328                // it `controlled`, because at the pinned versions the LIVE
329                // request IS the only uniform source (no harness stores
330                // approvals; see `crate::approvals`).
331                // ORCH-20 adds `harness.v1.approvals.resolve` to the
332                // controlled tier: one uniform decision, translated onto the
333                // request's own options and sent through `runtimes.respond`.
334                "pending_request" if descriptor.runtime.capabilities.respond_to_requests => (
335                    ImplementationKind::BuiltIn,
336                    ImplementationKind::BuiltIn,
337                    vec![
338                        "harness.v1.approvals.list",
339                        "harness.v1.approvals.resolve",
340                        "harness.v1.runtimes.respond",
341                    ],
342                ),
343                // ORCH-21: the two harnesses that publish a client-callable
344                // profile lifecycle verb are also CONTROLLED — supercode runs
345                // `hermes profile create|delete` / `openclaw agents
346                // add|delete` on the caller's behalf and re-reads the row
347                // (`crate::profiles_control`). supercode still owns no config
348                // plane; the tier means "the harness's own verb is reachable
349                // through one uniform door".
350                "profile"
351                    if crate::profiles_control::supports_profile_control(
352                        descriptor.id.as_str(),
353                    ) =>
354                {
355                    (
356                        ImplementationKind::BuiltIn,
357                        ImplementationKind::BuiltIn,
358                        vec![
359                            "harness.v1.profiles.list",
360                            "harness.v1.profiles.get",
361                            "harness.v1.profiles.create",
362                            "harness.v1.profiles.delete",
363                        ],
364                    )
365                }
366                // ORCH-10: the profile noun is read for the four harnesses
367                // that have one — supercode's presets, Codex's
368                // `[profiles.<name>]` tables, Hermes's profile homes, and
369                // OpenClaw's agent homes. Every other harness refuses. Codex
370                // and supercode stay read-only on purpose: a Codex profile is
371                // a table a human authors in `config.toml` and a supercode
372                // preset is compiled-in code, so neither publishes a verb a
373                // client could call.
374                "profile"
375                    if crate::profiles::PROFILE_HARNESSES.contains(&descriptor.id.as_str()) =>
376                {
377                    (
378                        ImplementationKind::BuiltIn,
379                        ImplementationKind::Absent,
380                        vec!["harness.v1.profiles.list", "harness.v1.profiles.get"],
381                    )
382                }
383                // ORCH-11: a harness is `observed` for skills when the loader
384                // in `crate::skills` opens its documented skill roots.
385                // ORCH-22 makes those same harnesses `controlled`: each one
386                // publishes a skills door supercode drives — `hermes skills
387                // install|uninstall`, `openclaw skills install`, and for the
388                // core four the loader's own directory, which IS their only
389                // skills door. supercode resolves no registry and unpacks no
390                // archive; a verb a harness lacks (OpenClaw has no `skills
391                // remove` at the pin) refuses with UnsupportedAction.
392                "skills"
393                    if crate::skills_control::supports_skill_control(descriptor.id.as_str()) =>
394                {
395                    (
396                        ImplementationKind::BuiltIn,
397                        ImplementationKind::BuiltIn,
398                        vec![
399                            "harness.v1.skills.list",
400                            "harness.v1.skills.install",
401                            "harness.v1.skills.remove",
402                        ],
403                    )
404                }
405                // ORCH-13: a delivery target is a FIELD on a job or a run,
406                // not a noun with verbs of its own, so it is observed exactly
407                // where those rows are — `deliver` on every job harness, and
408                // the delivery record on the two that keep a fire store.
409                // Nothing is controlled: supercode never sends.
410                "delivery_target" if crate::jobs::supports_jobs(descriptor.id.as_str()) => {
411                    let mut methods = vec!["harness.v1.jobs.list", "harness.v1.jobs.get"];
412                    if crate::runs::supports_runs(descriptor.id.as_str()) {
413                        methods.push("harness.v1.runs.list");
414                        methods.push("harness.v1.runs.get");
415                    }
416                    (
417                        ImplementationKind::BuiltIn,
418                        ImplementationKind::Absent,
419                        methods,
420                    )
421                }
422                // ORCH-14: the channel noun is read for the two gateway
423                // harnesses that HAVE install-scoped channels — Hermes's
424                // `platforms:` blocks and OpenClaw's `channels.<name>`
425                // entries. Claude Code's channels are MCP servers that
426                // declare the capability over the protocol, not in a config
427                // file, so it is refused rather than guessed at.
428                // ORCH-17: gateway state/endpoint on the inventory row, derived from the
429                // UNI-7 running-instance probe and the harness's own config.
430                "gateway_health"
431                    if matches!(
432                        descriptor.id.as_str(),
433                        // ORC-7: the orchestrator's gateway state is its
434                        // daemon lease (`<home>/orchestrator.lock` plus a
435                        // liveness check on the pid it names), reported on
436                        // the same `harnesses.list` row as the other two.
437                        HarnessId::HERMES | HarnessId::OPENCLAW | HarnessId::ORCHESTRATOR
438                    ) =>
439                {
440                    (
441                        ImplementationKind::BuiltIn,
442                        ImplementationKind::Absent,
443                        vec!["harness.v1.harnesses.list"],
444                    )
445                }
446                // ORCH-16: inbound webhook routes / hook mappings from the same configs.
447                "inbound_trigger"
448                    if crate::triggers::TRIGGER_HARNESSES.contains(&descriptor.id.as_str()) =>
449                {
450                    (
451                        ImplementationKind::BuiltIn,
452                        ImplementationKind::Absent,
453                        vec!["harness.v1.triggers.list"],
454                    )
455                }
456                // ORCH-15: routing entries read from the same gateway configs.
457                "routing" if crate::routes::ROUTE_HARNESSES.contains(&descriptor.id.as_str()) => (
458                    ImplementationKind::BuiltIn,
459                    ImplementationKind::Absent,
460                    vec!["harness.v1.routes.list"],
461                ),
462                "channel"
463                    if crate::channels::CHANNEL_HARNESSES.contains(&descriptor.id.as_str()) =>
464                {
465                    (
466                        ImplementationKind::BuiltIn,
467                        ImplementationKind::Absent,
468                        vec!["harness.v1.channels.list", "harness.v1.channels.status"],
469                    )
470                }
471                // ORCH-12: a harness is `observed` for memory when
472                // `crate::memory` opens its own persistent memory documents —
473                // Claude Code's per-project auto-memory directory, Hermes's
474                // `memories/MEMORY.md`/`USER.md` per profile home, and
475                // OpenClaw memory-core's workspace files. Nothing is
476                // controlled: forget/reset stay the harness's own verb.
477                "memory" if crate::memory::supports_memory(descriptor.id.as_str()) => (
478                    ImplementationKind::BuiltIn,
479                    ImplementationKind::Absent,
480                    vec!["harness.v1.memory.show", "harness.v1.memory.search"],
481                ),
482                _ => (
483                    ImplementationKind::Absent,
484                    ImplementationKind::Absent,
485                    vec![],
486                ),
487            };
488            ConceptSupport {
489                concept: (*concept).to_string(),
490                observed,
491                controlled,
492                methods: methods.into_iter().map(str::to_string).collect(),
493            }
494        })
495        .collect();
496    OrchestrationSupport { concepts }
497}
498
499pub fn harness_support_registry() -> SupportRegistryReport {
500    let claude = ClaudeCodeRuntimeBackend::new();
501    let codex = CodexRuntimeBackend::new();
502    let opencode = OpenCodeRuntimeBackend::new();
503    let pi = PiRuntimeBackend::new();
504    let grok_launch = RuntimeLaunch {
505        program: "grok".into(),
506        arguments: grok_arguments(),
507        env: grok_env(),
508    };
509    let grok = AcpRuntimeBackend::new(HarnessId::from(HarnessId::GROK), grok_launch.clone())
510        .with_resume_support(true);
511    let gemini_launch = RuntimeLaunch {
512        program: "gemini".into(),
513        // PARITY-24 drift 2026-08-31: gemini-cli 0.29.x renamed the ACP
514        // flag; `--acp` is rejected with "Unknown argument". Verified live:
515        // `--experimental-acp` completes the v1 initialize handshake.
516        arguments: vec!["--experimental-acp".into()],
517        env: BTreeMap::new(),
518    };
519    let gemini = AcpRuntimeBackend::new(HarnessId::from(HarnessId::GEMINI), gemini_launch.clone())
520        .with_resume_support(true);
521    let goose_launch = RuntimeLaunch {
522        program: "goose".into(),
523        arguments: vec!["acp".into()],
524        env: BTreeMap::new(),
525    };
526    let goose = AcpRuntimeBackend::new(HarnessId::from(HarnessId::GOOSE), goose_launch.clone())
527        .with_resume_support(true);
528    let hermes_launch = RuntimeLaunch {
529        program: "hermes-acp".into(),
530        arguments: Vec::new(),
531        env: BTreeMap::new(),
532    };
533    let hermes = AcpRuntimeBackend::new(HarnessId::from(HarnessId::HERMES), hermes_launch.clone())
534        .with_resume_support(true);
535    let openclaw_launch = RuntimeLaunch {
536        // `openclaw acp` is a stdio ACP bridge that CONNECTS to a running
537        // Gateway (never spawns one); with no flags it resolves the gateway
538        // target from OpenClaw's own config. The explicit-endpoint variant is
539        // the connect_launch below.
540        program: "openclaw".into(),
541        arguments: vec!["acp".into()],
542        env: BTreeMap::new(),
543    };
544    let openclaw = AcpRuntimeBackend::new(
545        HarnessId::from(HarnessId::OPENCLAW),
546        openclaw_launch.clone(),
547    )
548    .with_resume_support(true);
549    let supercode_launch = RuntimeLaunch {
550        program: "supercode".into(),
551        arguments: vec!["acp".into()],
552        env: BTreeMap::new(),
553    };
554    let supercode = AcpRuntimeBackend::new(
555        HarnessId::from(HarnessId::SUPERCODE),
556        supercode_launch.clone(),
557    )
558    .with_resume_support(true);
559
560    let mut report = SupportRegistryReport {
561        schema: SUPPORT_REGISTRY_SCHEMA.into(),
562        harnesses: vec![
563            HarnessSupportDescriptor {
564                id: HarnessId::from(HarnessId::CLAUDE_CODE),
565                display_name: "Claude Code".into(),
566                orchestration: OrchestrationSupport::default(),
567                native: built_in_native(),
568                // the backend's own prefix: a published launch that omitted the
569                // stream-json flags started the interactive TUI on a pipe when
570                // handed back through `RuntimeStart.launch`
571                runtime: built_in_runtime(&claude, "claude-stream-json", claude.launch().clone()),
572            },
573            HarnessSupportDescriptor {
574                id: HarnessId::from(HarnessId::CODEX),
575                display_name: "Codex".into(),
576                orchestration: OrchestrationSupport::default(),
577                native: built_in_native(),
578                runtime: built_in_runtime(
579                    &codex,
580                    "codex-app-server-jsonl",
581                    RuntimeLaunch {
582                        program: "codex".into(),
583                        arguments: vec!["app-server".into()],
584                        env: BTreeMap::new(),
585                    },
586                ),
587            },
588            HarnessSupportDescriptor {
589                id: HarnessId::from(HarnessId::OPENCODE),
590                display_name: "OpenCode".into(),
591                orchestration: OrchestrationSupport::default(),
592                native: built_in_native(),
593                runtime: built_in_runtime(
594                    &opencode,
595                    "opencode-http-sse",
596                    RuntimeLaunch {
597                        program: "opencode".into(),
598                        arguments: vec!["serve".into()],
599                        env: BTreeMap::new(),
600                    },
601                ),
602            },
603            HarnessSupportDescriptor {
604                id: HarnessId::from(HarnessId::PI),
605                display_name: "Pi".into(),
606                orchestration: OrchestrationSupport::default(),
607                native: built_in_native(),
608                runtime: built_in_runtime(
609                    &pi,
610                    "pi-rpc-jsonl",
611                    RuntimeLaunch {
612                        program: "pi".into(),
613                        arguments: vec!["--mode".into(), "rpc".into()],
614                        env: BTreeMap::new(),
615                    },
616                ),
617            },
618            HarnessSupportDescriptor {
619                id: HarnessId::from(HarnessId::GROK),
620                display_name: "Grok".into(),
621                orchestration: OrchestrationSupport::default(),
622                native: built_in_native(),
623                runtime: RuntimeSupport {
624                    implementation: ImplementationKind::GenericProtocol,
625                    protocol: "acp-v1-jsonrpc".into(),
626                    default_launch: Some(grok_launch),
627                    connect_launch: None,
628                    capabilities: grok.capabilities(),
629                },
630            },
631            HarnessSupportDescriptor {
632                id: HarnessId::from(HarnessId::GEMINI),
633                display_name: "Gemini CLI".into(),
634                orchestration: OrchestrationSupport::default(),
635                native: built_in_native(),
636                runtime: RuntimeSupport {
637                    implementation: ImplementationKind::GenericProtocol,
638                    protocol: "acp-v1-jsonrpc".into(),
639                    default_launch: Some(gemini_launch),
640                    connect_launch: None,
641                    capabilities: gemini.capabilities(),
642                },
643            },
644            HarnessSupportDescriptor {
645                id: HarnessId::from(HarnessId::GOOSE),
646                display_name: "Goose".into(),
647                orchestration: OrchestrationSupport::default(),
648                native: built_in_native(),
649                runtime: RuntimeSupport {
650                    implementation: ImplementationKind::GenericProtocol,
651                    protocol: "acp-v1-jsonrpc".into(),
652                    default_launch: Some(goose_launch),
653                    connect_launch: None,
654                    capabilities: goose.capabilities(),
655                },
656            },
657            HarnessSupportDescriptor {
658                id: HarnessId::from(HarnessId::HERMES),
659                display_name: "Hermes Agent".into(),
660                orchestration: OrchestrationSupport::default(),
661                native: NativeSupport {
662                    // UNI-15 read-only tier: discovery + load over the
663                    // state.db SQLite store. Follow/import stay Absent.
664                    // EXPORT (UNI-18) goes through Hermes's own door:
665                    // `hermes sessions import --from codex` (0.21.0), which
666                    // writes the store with Hermes's own writer — supercode
667                    // never writes a live Hermes store itself. The tier
668                    // stays driven (matrix membership is UNI-17's flip).
669                    discover: ImplementationKind::BuiltIn,
670                    load: ImplementationKind::BuiltIn,
671                    follow: ImplementationKind::Absent,
672                    import: ImplementationKind::Absent,
673                    export: ImplementationKind::GenericProtocol,
674                },
675                runtime: RuntimeSupport {
676                    implementation: ImplementationKind::GenericProtocol,
677                    protocol: "acp-v1-jsonrpc".into(),
678                    default_launch: Some(hermes_launch),
679                    connect_launch: None,
680                    capabilities: hermes.capabilities(),
681                },
682            },
683            HarnessSupportDescriptor {
684                id: HarnessId::from(HarnessId::OPENCLAW),
685                display_name: "OpenClaw".into(),
686                orchestration: OrchestrationSupport::default(),
687                native: NativeSupport {
688                    // UNI-16 read-only tier: discovery over
689                    // `agents/<id>/sessions/*.jsonl` and the pi-v3-dialect
690                    // loader (`from_openclaw_str`). Import (translate IN),
691                    // export (write OUT), and follow stay Absent — the write
692                    // path is a permanent skip, and the TIER stays `driven`:
693                    // matrix membership remains UNI-17's priced flip.
694                    discover: ImplementationKind::BuiltIn,
695                    load: ImplementationKind::BuiltIn,
696                    follow: ImplementationKind::Absent,
697                    import: ImplementationKind::Absent,
698                    export: ImplementationKind::Absent,
699                },
700                runtime: RuntimeSupport {
701                    implementation: ImplementationKind::GenericProtocol,
702                    protocol: "acp-v1-jsonrpc".into(),
703                    default_launch: Some(openclaw_launch),
704                    // Blind-walk finding 2026-08-31: `gateway.url` is NOT a
705                    // key openclaw's config schema accepts (the gateway
706                    // rejects the whole file as invalid config). The real
707                    // shape: an optional full URL at `gateway.remote.url`, a
708                    // bare `gateway.port` number, or nothing at all — the
709                    // documented out-of-the-box endpoint is ws://127.0.0.1:18789.
710                    connect_launch: Some(RuntimeConnectLaunch {
711                        config_path: "~/.openclaw/openclaw.json".into(),
712                        address_pointer: "/gateway/remote/url".into(),
713                        port_pointer: Some("/gateway/port".into()),
714                        default_address: Some("ws://127.0.0.1:18789".into()),
715                        auth_pointer: Some("/gateway/auth/token".into()),
716                        protocol: "acp-v1-jsonrpc".into(),
717                    }),
718                    capabilities: openclaw.capabilities(),
719                },
720            },
721            // ORC-7: the orchestrator is a harness id so the EXISTING
722            // orchestration readers list its state — its folder is a
723            // Hermes-shaped home (`docs/ORCHESTRATOR-IR.md` §6) and each
724            // reader is pointed at it with no new reader code. It has no
725            // native session tier of its own: it keeps no transcripts, only
726            // BINDINGS that address a WORKER harness's session, which is read
727            // through that harness's own door. It has no runtime either — the
728            // daemon is a Node process the operator verbs start and stop, not
729            // an adapter supercode connects a turn to.
730            HarnessSupportDescriptor {
731                id: HarnessId::from(HarnessId::ORCHESTRATOR),
732                display_name: "Orchestrator".into(),
733                orchestration: OrchestrationSupport::default(),
734                native: NativeSupport {
735                    discover: ImplementationKind::Absent,
736                    load: ImplementationKind::Absent,
737                    follow: ImplementationKind::Absent,
738                    import: ImplementationKind::Absent,
739                    export: ImplementationKind::Absent,
740                },
741                runtime: RuntimeSupport {
742                    implementation: ImplementationKind::Absent,
743                    protocol: "none".into(),
744                    default_launch: None,
745                    connect_launch: None,
746                    capabilities: RuntimeCapabilities {
747                        start_session: false,
748                        resume_session: false,
749                        attach_existing_process: false,
750                        send_input: false,
751                        stream_events: false,
752                        interrupt: false,
753                        steer: false,
754                        respond_to_requests: false,
755                    },
756                },
757            },
758            HarnessSupportDescriptor {
759                id: HarnessId::from(HarnessId::SUPERCODE),
760                display_name: "Volter Harness".into(),
761                orchestration: OrchestrationSupport::default(),
762                native: built_in_native(),
763                runtime: RuntimeSupport {
764                    implementation: ImplementationKind::GenericProtocol,
765                    protocol: "acp-v1-jsonrpc".into(),
766                    default_launch: Some(supercode_launch),
767                    connect_launch: None,
768                    capabilities: supercode.capabilities(),
769                },
770            },
771        ],
772    };
773    for descriptor in &mut report.harnesses {
774        descriptor.orchestration = orchestration_support(descriptor);
775    }
776    report
777}
778
779/// Look up one harness in the compiled registry.
780pub fn harness_support(id: &str) -> Option<HarnessSupportDescriptor> {
781    harness_support_registry()
782        .harnesses
783        .into_iter()
784        .find(|harness| harness.id.as_str() == id)
785}