Skip to content

Epic: Meta-harness for role and task backend routing #519

Description

@dcellison

Context

Kai is moving toward multiple execution harnesses: Claude Code, Codex CLI, OpenCode-style ACP backends, and possibly other one-shot or role-specific reasoners. The useful design is a harness router: a small policy layer that maps a normalized task shape to a known execution route.

The risky design is a meta-agent that asks a model which other model or tool to call on every turn. Do not build that. The router should be boring infrastructure with explicit inputs, capability checks, policy, adapters, and logs.

Design note: harness-router-architecture.md.

Goal

Design and implement a deterministic harness router that can select a harness, adapter, model policy, permission profile, and fallback route for each task while preserving shared memory, scheduling, tool access, operator preferences, and behavior rules.

Why this matters

Different harnesses are good at different work, and not every installation has every harness available.

  • Claude Code is the incumbent adapter and remains useful where available.
  • Codex CLI is a practical coding and one-shot harness with model-aware routing value.
  • OpenCode-style ACP backends may provide a cleaner protocol-shaped integration.
  • Scheduled jobs, PR review, spec review, fact extraction, implementation, and lightweight chat do not all need the same route.

A router lets Kai use backend diversity deliberately instead of making one backend carry every workload.

This should not ship before backend-neutral memory context and scoped retrieval are solid. Routing the right task to the right harness with the wrong memory context is still wrong.

Canonical routing contract

Use one route contract throughout the design:

role + task_profile + capability_registry + routing_policy -> route(harness, adapter, model, permission_profile, fallbacks)

Inputs:

  • role: outer intent bucket.
  • task_profile: normalized workload inside the role.
  • capability_registry: install-local description of harnesses, models, permissions, modes, auth state, and verified capabilities.
  • routing_policy: operator and installation preferences.

Output:

  • selected harness
  • adapter
  • model or symbolic model class
  • permission profile
  • ordered fallback routes

Roles and task profiles are separate. Role assignment chooses intent. Harness routing chooses the concrete execution route.

Initial roles and task profiles

Role Task profiles
conversation interactive_conversation
memory fact_extraction, memory_summarization
review pr_review
spec_review spec_review
jobs scheduled_status
implementation implementation, repo_exploration, small_shell_task
research research

The first implementation does not need to route every profile. It should define the vocabulary up front, then wire one narrow profile first, likely fact_extraction.

Adapter contract

The core abstraction is an adapter contract, not a router prompt.

Each harness adapter should normalize invocation, environment setup, timeout handling, permissions, model resolution, logging, and result parsing.

Conceptual operation:

result = harness.run(
    prompt=prompt,
    cwd=workspace,
    files=files,
    timeout=timeout,
    permissions=permissions,
    output_mode=output_mode,
)

The exact signature can change, but the boundary must stay strict. The router chooses a route. The adapter owns harness-specific details.

Capability registry

Harness availability is install-local. Kai should support installations where any given harness is missing, unconfigured, unauthenticated, disabled by policy, or available only for some roles.

Use explicit availability states:

  • missing
  • unconfigured
  • unauthenticated
  • disabled
  • blocked_by_policy
  • available

Useful capability fields include:

  • interactive support
  • one-shot support
  • structured output support
  • streaming support
  • tool permission controls
  • workspace editing support
  • read-only mode
  • protocol type, such as CLI, ACP, or HTTP
  • timeout support
  • model override support
  • permission policy support
  • available models or symbolic model classes
  • cost and context classes
  • auth state
  • verified modes

Capability checks happen at the route level, not only the harness level. A harness may be generally available but still ineligible for a specific model, permission profile, or task profile.

Model policy

The router selects harness and model policy together. Do not pick a model first and then ask which harness can run it.

Task-profile model classes should be symbolic where portability matters:

  • small-fast
  • structured-cheap
  • coding-strong
  • reasoning-strong
  • long-context-strong

Adapters resolve symbolic classes into harness-specific model IDs. Exact model IDs can be supported as local overrides, but provider-specific strings should not spread through generic routing policy.

Fact extraction should prefer the smallest reliable structured-output route. PR and spec review should prefer strong reasoning and citation discipline. Implementation should prefer tool-competent models with safe permission handling.

Policy model

The first policy table should be small:

  • Conversation uses the configured primary backend.
  • Review uses a dedicated review backend if configured, otherwise the primary backend.
  • Spec review uses the review backend with a spec-review prompt profile.
  • Memory uses the configured one-shot reasoner.
  • Jobs use a dedicated jobs backend if configured, otherwise the primary backend in one-shot mode.
  • Implementation uses the active conversational backend unless explicitly delegated.

Policy should be configuration, not hidden code. If the strategic direction is to prefer non-Claude-Code routes where available, that belongs in routing policy with explicit fallback rules.

Reason strings should be closed enum-like values so logs remain queryable:

  • preferred_route_available
  • configured_route_available
  • fallback_route_selected
  • acp_not_verified
  • missing
  • unconfigured
  • unauthenticated
  • disabled
  • blocked_by_policy
  • model_unavailable
  • permission_profile_unsupported
  • lower_priority_by_policy
  • operator_confirmation_required

Fallback policy

Fallbacks must be explicit and conservative.

  • If a configured review harness is unavailable, fail closed unless policy allows a specific fallback.
  • If a jobs harness is unavailable, use the primary backend only for low-risk read-only jobs.
  • If a memory extraction harness is unavailable, skip extraction and log the reason rather than route to an unapproved harness.
  • If an implementation harness is unavailable, ask before silently using another write-capable route.
  • If a model is available but the harness cannot enforce the required permission profile, reject the route.

Read-only tasks may have broader fallback options. Write-capable tasks should not silently change harnesses.

Logging and debuggability

Every routed task should log the routing decision.

Minimum fields:

  • role
  • task profile
  • selected harness
  • selected adapter
  • selected model or symbolic model class
  • reason for selection
  • workspace
  • timeout
  • permission profile
  • read-only or write-capable flag
  • fallback behavior
  • harness availability state
  • model availability state
  • considered and rejected routes with reasons

Bad routing will happen. The log must make it clear whether policy was wrong, an adapter failed, a harness was unavailable, or a model route was ineligible.

Operator controls

The operator should be able to inspect and override routing policy without reading adapter code:

  • List available harnesses and their state.
  • Show task profiles and preferred routes.
  • Show disabled routes and reasons.
  • Override the model for a task profile.
  • Disable a harness globally.
  • Override a route for a single task when policy allows it.
  • Explain why a route was selected.

Harness-specific notes

OpenCode is the main ACP-protocol test case. Its ACP path should be empirically verified before it becomes a primary route. Kai must represent partial capability states such as "available for conversation, not eligible for memory extraction."

Codex CLI is a strong one-shot and implementation harness, but its contract is CLI-shaped. The Codex adapter owns binary path, login preconditions, working directory, sandbox and approval flags, output mode, timeout behavior, and model selection.

Claude Code remains a supported adapter and compatibility fallback, but it should not define the generic abstraction. The strategic goal is to demote Claude Code from foundation to adapter, not remove it.

Dependency order

  1. Complete Extract backend-neutral per-turn memory context assembly #516 so memory context assembly is backend-neutral.
  2. Complete Codex semantic memory parity enough that Codex is a real fallback, not a memory-blind shell.
  3. Complete scoped memory retrieval (Epic: Scoped global/project memory #517) so routed tasks receive the right context.
  4. Define the role and task-profile vocabulary.
  5. Define backend and harness capability contracts.
  6. Define model policy and symbolic model classes.
  7. Implement the capability registry and route eligibility checks.
  8. Implement one explicit route, likely fact_extraction.
  9. Add fallbacks, logging, and operator inspection.
  10. Extend routing to review, jobs, implementation, and research only after the narrow route is trustworthy.

Scope

  • Define the router's responsibilities and boundaries.
  • Define canonical roles and task profiles.
  • Define install-local capability metadata for harnesses, models, modes, auth state, permissions, cost, and context classes.
  • Add routing policy configuration with explicit defaults and fallbacks.
  • Preserve one shared memory/context assembly path rather than duplicating prompt construction per backend.
  • Ensure routed jobs include correct chat ID, workspace, project scope, memory context, and operator preferences.
  • Add route eligibility checks before execution.
  • Add operator-visible controls for current routing policy.
  • Add structured logs explaining route selection and rejected routes.
  • Add tests for fallback behavior when a harness, model, or permission profile is unavailable.

Out of scope

Candidate child issues

  • Define backend and harness capability schema.
  • Define role and task-profile taxonomy.
  • Add backend-neutral task envelope for routed work.
  • Add routing policy config and validation.
  • Add symbolic model classes and adapter-level resolution.
  • Add capability probes for configured harnesses.
  • Add operator command to inspect active routing policy.
  • Add explicit backend or model override for a single task or role.
  • Add fallback behavior when the preferred route is unavailable.
  • Add logs explaining route selection and rejected routes.
  • Add scheduled-job backend routing.
  • Add PR/spec review backend routing.
  • Add one-shot reasoner routing for memory work.
  • Add integration tests for mixed-backend workflows.

Acceptance criteria

  • Kai can select a route based on explicit role and task profile.
  • Routing decisions are deterministic and logged with enough detail to debug them.
  • Every routed task receives the same backend-neutral memory/context contract.
  • The router chooses harness, adapter, model, permission profile, and fallbacks as one route.
  • Harness and model availability are represented explicitly.
  • Backend failure falls back predictably or reports a clear error.
  • Write-capable tasks do not silently switch harnesses.
  • The operator can inspect current routing rules.
  • The operator can override routing when policy allows it.
  • Scheduled jobs and interactive sessions preserve correct chat, workspace, project, and memory context.
  • The system does not fork behavior rules across backends.

Metadata

Metadata

Assignees

No one assigned

    Labels

    architectureDesign decisions and architectural directionenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions