Skip to content

The normative spec

This is the contributor-facing normative specification — Draft v2

It is written for people implementing or extending GAIA, in RFC 2119 language, and it describes the design contract rather than a user-facing feature tour. If you want to know what the shipped system does, start with How GAIA works.

Three points below are known-stale relative to the shipped system:

  • Run release. The spec has the agent session release its own run via a gaia run release command. Release is now server-owned: the agent ends a run by writing the ticket's next state, and the control plane retires the run.
  • Conductor identity. The spec derives it as hash(hostname + checkout path). That is superseded by a required, explicitly composed machine_id in the engine config — there is no derived fallback.
  • The gaia-issue skill. The single global skill described here is superseded by the @gaia/* skill set, which a project loads through its WORKFLOW.md. See Skills and the workflow contract.

Everything else stands as the normative contract.

Status: Draft v2

Purpose: Define GAIA as a Drupal-backed service that dispatches active tickets to human or AI state handlers, using repository-owned workflow contracts and dropsh for write-back.

Normative Language

The key words MUST, MUST NOT, REQUIRED, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL are to be interpreted as described in RFC 2119.

Implementation-defined means the behavior is part of the implementation contract, but this specification does not prescribe one universal policy. Implementations MUST document the selected behavior.

1. Problem Statement

GAIA is a ticket-driven orchestration system. Drupal is the execution control-plane for tickets, ticket state, assignments, conductor registration, claim leases, audit, dashboard, and import. A separate commercial and work-evidence context owns Delivery Items, LP, Work Evidence, and Billable Units and links one-way to GAIA Projects or Tickets. The state machine (states, transitions, state types, handlers) is code-defined per Ticket type. Repository-owned WORKFLOW.md files are project instruction text: they provide repository-specific working context for sessions, but they do not define states, transitions, handlers, skills, policy, or dispatch behavior. A global gaia-issue skill defines GAIA's general issue-processing behavior.

GAIA solves these operational problems:

  • It turns ticket execution into a repeatable service workflow instead of manual agent sessions.
  • It lets the current Ticket state decide which human or AI handler should work next.
  • It keeps project-specific execution rules close to the code via WORKFLOW.md.
  • It keeps ticket state, assignments, audit, and billing in Drupal.
  • It records a footprint for each Run so human time and AI runtime can roll up to customer-facing ticket cost.

Important boundary:

  • GAIA follows a hub-and-spoke (kubernetes) model: Drupal is the control-plane hub (apiserver+etcd); the conductor (gaia conductor start, one per project checkout) is the node-agent (kubelet); the gaia CLI is the client (kubectl). There is no central daemon/supervisor and no TCP — the conductor and the gaia CLI never talk directly; both are independent Drupal clients coordinating through shared gaia_run state. The conductor is built ports-and-adapters over three config-pluggable ports speaking domain types (not JSON:API): GaiaRemote (control-plane; Drupal adapter uses dropsh only as a library — backend is swappable), GaiaExecutor (session spawning; herdr adapter), GaiaWorkspace (filesystem; git-worktree adapter). The agent session owns its run lifecycle (model B): when done it transitions the ticket and then releases its own run via gaia run release (succeeded/failed) — the conductor only claims and dispatches, it does not probe or reconcile run state. A generous claim lease (TTL) is the crash backstop: an agent that crashes before releasing is freed when the lease expires (re-claimable, with a crash-cooldown). The Run lifecycle and the Ticket business are both written by the agent via the gaia CLI. Conductor identity = hash(hostname+checkout-path); a local registry (~/.gaia/conductors.json, DDEV-style) holds the machine's set; the host (herdr/systemd) holds the process; the hub holds liveness. dropsh is a library, not a separate API surface (see 03-control-plane.md, 04-conductor.md, 11-gaiad.md).
  • GAIA's Conductor starts agent sessions directly; there is no HERMES/orchestrator role in the domain model.
  • GAIA's Conductor MUST NOT write ticket content state.
  • Ticket writes are performed through dropsh-backed write-back operations. Normal human decisions happen inside a human_assist session; direct UI writes are reserved for administrative recovery.
  • A successful Run can end at a handoff state, for example HumanInput or CustomerReview, not necessarily Done.

2. Goals and Non-Goals

2.1 Goals

  • Maintain Drupal as the source of truth for ticket state, project metadata, conductor registry, audit, and billing.
  • Load project-specific instruction text from WORKFLOW.md in the project repository.
  • Combine global gaia-issue behavior with project-specific WORKFLOW.md instructions when building handler prompts.
  • Dispatch dispatchable Tickets to compatible Conductors by project, the conductor's optional state narrowing (empty = all claimable states), assignee priority, and bounded concurrency.
  • Support human/AI handoff by changing the Ticket's current assignee/handler.
  • Maintain deterministic dispatch, retry, reconciliation, and stall detection.
  • Reuse isolated per-ticket workspaces across Runs.
  • Normalize external issues into canonical GAIA Tickets and preserve deterministic source identity.
  • Use dropsh as the write-back mechanism to Drupal JSON:API.
  • Record Run footprints as machine telemetry and keep them separate from employee Work Evidence and customer-facing Delivery Points.
  • Provide operator-visible observability through dashboard snapshots and structured logs.

2.2 Non-Goals

  • Replacing Drupal as the control-plane.
  • Making Drupal or WORKFLOW.md the primary source of workflow behavior.
  • Creating a general-purpose distributed workflow engine.
  • Requiring one specific coding agent implementation.
  • Requiring every workflow state to be AI-dispatched.
  • Modeling fixed roles such as HERMES/APOLLO/HEPHAISTOS/ATHENE/APHRODITE as domain objects.
  • Creating a separate Task entity for internal sequencing.
  • Treating GitLab, Jira, Harvest, or any imported system as the operative GAIA ticket store after import.
  • Requiring two-way synchronization with external issue trackers in the initial implementation.
  • Prescribing one UI implementation beyond required platform capabilities.

3. Source-of-Truth Model

GAIA has distinct sources of truth:

AreaSource of truth
Ticket state, assignments, execution auditDrupal GAIA execution context
Delivery Items, LP, Work Evidence, Billable Units, billing statusSeparate commercial and work-evidence context; physical deployment deferred
Rates, prices, invoices, paymentsExternal billing system (Harvest in phase one)
State machine (states, transitions, claimability) per Ticket workflowGAIA code (state_machine workflows)
Project-specific work instructionsRepository WORKFLOW.md as raw text
Global GAIA issue behaviorgaia-issue skill

Drupal MAY store instruction source metadata and hashes for audit/debugging, but Drupal MUST NOT parse WORKFLOW.md into operative workflow configuration.

4. System Overview

4.1 Main Components

  1. Drupal Control-Plane

    • Stores Customer, Project, Ticket, Conductor registration, Run/claim lease, Comment, and optional label/tag data in the GAIA execution model.
    • Hosts or integrates with a separate commercial and work-evidence context containing Engagement, Delivery Item, Work Evidence, Billable Unit, and one-way Domain Link records. GAIA entities do not reference those records.
    • Models Ticket lifecycle state with Drupal's state_machine module.
    • Stores Project-level instruction source metadata for WORKFLOW.md.
    • Provides dashboards for monitoring: board, node status, run monitor, pending human-input runs. Human work itself happens in spawned human_assist sessions, not in the dashboard.
    • Provides read APIs for the Conductor.
    • Receives write-back through dropsh.
  2. Project Instructions Context

    • Reads WORKFLOW.md from the configured project repository/ref as text.
    • Makes that text available to handler sessions as project context.
    • (Future) Records path/ref/hash per Run for reproducibility — deferred; workflow_instructions_hash and related run fields are not yet implemented (see §Future).
    • MUST NOT parse WORKFLOW.md into state, transition, handler, skill, policy, hook, retry, concurrency, or dispatch configuration.
  3. gaia-issue Skill

    • Defines the global GAIA contract for ticket handling.
    • Defines how agent sessions should reason about ticket work, state updates, comments, sub-tickets, footprints, and handoff.
    • Is combined with WORKFLOW.md instruction text during prompt construction.
  4. Conductor

    • Runs as one or more registered external Node/TypeScript supervisors.
    • Owns polling, reconciliation, dispatch, local process claims, retry, and stall detection.
    • Starts or attaches handler Runs through Spawn Plugins after Drupal grants an atomic claim lease.
    • Registers heartbeat/capabilities in Drupal.
    • Creates scoped session context for dropsh.
    • MUST NOT write ticket content state.
  5. Execution Layer / Spawn Plugins

    • Creates or reuses per-ticket workspaces.
    • Runs workspace lifecycle hooks.
    • Starts, attaches, stops, and observes sessions through the selected Spawn Plugin.
    • Streams agent events back to the Conductor.
  6. Spawn Plugin

    • Normalizes concrete runtime mechanisms such as herdr, stdio, tmux, SSH, containers, Claude Code, Codex, or test fakes behind a common interface.
    • Provides dropsh as CLI/MCP write-back tooling.
    • Reports usage, rate limits, turn outcomes, liveness events, and blocked/input-required state.
  7. dropsh Write-Back

    • Uses the dropsh CLI against Drupal JSON:API.
    • Executes GAIA semantic operations such as ticket_update, ticket_create, and footprint_log.
    • Performs schema-backed validation where available.
  8. Import Layer

    • Reads external issues and time records from systems such as GitLab, Jira, and Harvest.
    • Maps external records into canonical GAIA Tickets and TimeEntries.
    • Provides on-demand single-record import for external issue URLs so operators do not need to wait for a batch import run.
    • Stores source metadata in ticket.origin and ticket.links.
    • MUST NOT create long-lived foreign-system issue entities as the operative model.
  9. Observability

    • Emits structured logs.
    • Provides runtime snapshots for running, retrying, token totals, duration totals, and rate-limit information.

4.2 Abstraction Layers

GAIA SHOULD be implemented in these layers:

  1. Policy layer: code-defined workflow policy + gaia-issue + repository WORKFLOW.md text.
  2. Control-plane layer: Drupal execution entities, dashboard, read APIs, validation, and audit; commercial/work-evidence capabilities remain a separate bounded context.
  3. Coordination layer: Conductor polling, claiming, dispatch, retry, reconciliation.
  4. Execution layer: workspace lifecycle, hooks, remote/local execution.
  5. Agent/runtime layer: Spawn Plugins, model-specific process handling, and event normalization.
  6. Write-back layer: dropsh commands mapped to GAIA operations.
  7. Observability layer: logs, dashboard snapshots, run history.

5. Core Domain Model

5.1 Customer

Customer is the existing organizational aggregate that groups Projects. It is not the customer source of truth for the target commercial model; Harvest owns that relationship in phase one, and the separate context does not link to this entity.

Required logical fields:

  • id
  • name
  • billing_profile (legacy compatibility only; not authoritative for the new context)

5.2 Project

Project stores execution context for tickets.

Required logical fields:

  • id
  • name
  • repos
  • coder_config
  • members

WORKFLOW.md is plain instruction text read from the project repository at session-build time. It is not stored as a Drupal field and does not carry workflow configuration.

5.3 Ticket

Ticket is the customer-facing deliverable and dispatch unit. It carries no Engagement, commercial role, Delivery Point, Work Evidence, or Billable Unit relationship. The separate commercial context may link to a Ticket by stable identity; the Ticket never links back.

Required logical fields:

  • id
  • identifier
  • workflow: selects the code-defined state_machine workflow directly (e.g. gaia_feature, gaia_bug, gaia_chore)
  • title
  • description (text_long, format gaia_rich — Markdown stored, rendered to HTML on display)
  • state
  • assignee_user_id: optional human assignee
  • priority
  • parent_id
  • blocked_by
  • labels (entity_reference → gaia_term, handler gaia_labels)
  • origin (string — source URL)
  • links
  • created_at
  • updated_at

The Ticket state MUST be implemented as a Drupal state_machine state field in GAIA's Drupal implementation. The workflow is resolved at runtime via the workflow field. Direct string storage is only the logical representation used in API payloads, imports, and documentation.

The Ticket is the unit the Conductor reads, claims, dispatches, and reconciles. There is no separate Task entity in the normative GAIA model.

5.4 Run / Claim Lease

Run records represent one concrete execution attempt and distributed Ticket claim leases.

Fields:

  • id
  • ticket_id
  • conductor_id (lease holder; replaces the former claim_owner)
  • state: claimed, running, released, succeeded, or failed (state_machine workflow gaia_run)
  • claim_expires_at
  • state_at_start (frozen snapshot of the Ticket state at claim time — plain string, not an active state_machine field)
  • started_at
  • worktree_path (populated by the Conductor at runtime)
  • log

Run records MUST NOT be used to write Ticket content state. They are a coordination and audit surface only.

5.5 Conductor Registration

Conductor registration represents a running or recently running external Node/TypeScript supervisor.

Fields:

  • id
  • machine_id
  • label
  • owner_user_id
  • project_id: the project this conductor registration belongs to (one registration per project)
  • states: optional narrowing of the workflow states this conductor handles (only states with claimable: true are eligible). Empty/absent = serve every claimable state in the project; a non-empty list restricts to those states, e.g. todo, coding, review
  • status: online, draining, or offline
  • current_load
  • workspace_root (base directory of the Node; per-ticket git worktrees are created under it)
  • endpoint_url
  • max_parallel
  • last_seen
  • lease_expires_at

When a Conductor starts, it MUST register or refresh its registration in Drupal before fetching work. A local developer Conductor uses the same registration model as server or worker-host Conductors.

5.6 Commercial sizing and internal cost allocation

Engagement, Delivery Item, Work Evidence, Billable Unit, Domain Link, Delivery Point, employee-capacity, and cost semantics are defined exclusively in the repository's 07-billing.md. Domain Links target only GAIA Projects or Tickets and are owned by the separate context; Runs are never link targets. Run footprints remain machine telemetry, not employee time or customer quantity.

5.7 Comment and Labels

Comments are implemented as a gaia_comment entity referencing the Ticket via ticket_id (reverse relation — the Ticket has no embedded comment field).

Comment fields: ticket_id, author, body (text_long, format gaia_rich), type (free string, default comment). type accepts any value; the conventional ones — comment, qualification, spec, plan, debug_diagnose, summary, acceptance, scenario, test — are conventions readers match on, not an enum storage enforces.

Handler outputs (spec, plan, diagnosis, summary) land as typed comments in the Ticket timeline.

Labels are implemented as gaia_term entity references (handler gaia_labels) on the Ticket.

6. Normalization Rules

  • Workspace key MUST be derived from ticket.identifier by replacing every character not matching [A-Za-z0-9._-] with _.
  • State comparison MUST use lowercase normalized names.
  • Labels SHOULD be normalized to lowercase.
  • blocked_by MUST represent incoming blockers.
  • priority MUST be numeric or null.
  • Origin metadata MUST be stored on Ticket, not as foreign system entities.
  • Imported records MUST use deterministic identifiers so repeated imports update the same GAIA Ticket.

7. Project Instructions Contract

7.1 File Discovery

Future / not yet implemented

The project.workflow_file_path, project.workflow_ref, and per-run path/ref/hash fields described here are deferred. See the Future section. Currently, WORKFLOW.md is provided as static project context; configurable path/ref resolution and Conductor-driven file loading are planned for a later phase.

Planned file path resolution (deferred):

  1. project.workflow_file_path if configured.
  2. Default: WORKFLOW.md at the project repository root.

The Conductor or execution layer will load the file from project.workflow_ref or the run-specific repository ref when session context is constructed.

7.2 File Format

WORKFLOW.md is plain Markdown project instruction text.

Rules:

  • The entire file is treated as text for prompt/session context.
  • YAML front matter, if present, has no GAIA-defined workflow semantics and MUST NOT be parsed into operative configuration.
  • The text SHOULD contain project-specific work rules, test commands, branch conventions, review expectations, and handoff guidance.

7.3 Division of Authority: Code vs WORKFLOW.md

The state machine and handler selection are code-defined. WORKFLOW.md only informs the session as repository-specific instruction text.

ConcernCode (GAIA, per Ticket workflow)WORKFLOW.md (repository)
States, transitions, claimable flagMUSTMUST NOT
Project work rules (tests, branches, conventions)defaultsSHOULD as text
concurrency/polling/retry/environment/hooks/agentMUSTMUST NOT

GAIA ships one state_machine workflow per Ticket workflow ID (gaia_bug, gaia_feature, gaia_chore, extensible). The workflow applied to a Ticket is selected by the ticket.workflow field. Workflow states carry label, claimable (optional, default false), and transitions — there is no type or handler annotation on states.

7.4 No Structured Workflow Schema

GAIA defines no structured WORKFLOW.md schema. states, transitions, handlers, skills, concurrency, polling, retry, environment, hooks, and agent MUST NOT be loaded from WORKFLOW.md. If those words appear in the file, they are user-facing instruction text only.

7.5 Required Dispatch Inputs

For dispatch, GAIA MUST resolve:

  • from the code-defined ticket workflow: states with their claimable flag and allowed transitions,
  • the conductor's optional registered states list (only states with claimable: true are eligible; empty/absent = all claimable states),
  • enough agent/environment configuration to launch a run.

7.6 Drupal State Machine Runtime

GAIA's Drupal implementation MUST use the contributed state_machine module for Ticket lifecycle state and transition application.

Runtime rules:

  • Workflows are code-defined and selected via ticket.workflow. WORKFLOW.md MUST NOT alter states or transitions.
  • Ticket transitions MUST be applied through the state_machine API, not by blindly overwriting a raw state value.
  • Claimability is a per-state flag (claimable: true) in the workflow definition — there is no separate type taxonomy or handler annotation on states.
  • Direct state assignment MAY be used only for controlled import, installation, or administrator repair paths, and MUST still validate that the target state exists in the Ticket workflow.
  • Server-side write-back MUST combine state_machine transition validation with GAIA guards for assignee consistency, blockers, permissions, and DAG cycles.

7.7 Session Construction

The session for a dispatched state MUST combine:

  1. global gaia-issue instructions,
  2. the code-defined handler/skill for the current Ticket type and state,
  3. project-specific WORKFLOW.md instruction text,
  4. normalized Ticket data,
  5. Project context,
  6. attempt/retry context,
  7. available dropsh-backed GAIA operations.

The referenced skill MUST be resolvable inside the agent session (globally installed or workspace-local); an unresolvable skill MUST fail the run with an operator-visible error.

7.8 Instruction Reload

WORKFLOW.md instruction text changes SHOULD be picked up without service restart.

Reload behavior:

  • Updated text applies to future prompt rendering and agent launches.
  • In-flight runs are not required to restart.
  • Missing or unreadable instruction files MUST NOT crash the service.
  • Operator-visible errors MUST be emitted.

8. Default Workflow (code-defined)

These defaults ship as code-defined state_machine workflows per Ticket workflow ID. A workflow MAY omit states (e.g. gaia_bug has Diagnose instead of Spec; gaia_chore skips both); the tables below show the gaia_feature superset.

Default states (gaia_feature):

StateClaimableNotes
backlognoinactive, planned
todoyesactivated, ready to start
specyesneeds requirements clarification
codingyesimplementation
reviewyesreview/QA
donenoterminal
cancellednoterminal

Default transitions (gaia_feature):

  • backlog -> todo (activate)
  • todo -> spec, coding, cancelled
  • spec -> coding, cancelled
  • coding -> review, cancelled
  • review -> coding, done

Note

HumanInput and CustomerReview states are Future — not yet implemented in the shipped workflows. See §Future.

Example WORKFLOW.md instruction text for these defaults:

markdown
# Project Work Rules

Tests: ddev phpunit ...
Branch target: reference-implementation
Human input: summarize the decision and available transitions.

9. Ticket, Run, and DAG Semantics

GAIA uses one customer-visible decomposition DAG:

  • Ticket DAG: ticket.blocked_by for decomposition via Sub-Tickets.

Rules:

  • Decomposition MUST use Sub-Tickets.
  • Internal sequencing SHOULD be represented by Ticket state transitions, not by Tasks.
  • fix -> review -> merge SHOULD be modeled as states or Sub-Tickets depending on whether the work is customer-visible/decomposable.
  • Parent integration work MAY be modeled as a normal Ticket state or a Sub-Ticket blocked by child ticket completion.
  • A Ticket is dispatchable only when its blockers are terminal.

10. Conductor Specification

SUPERSEDED by 04-conductor.md (final hub-and-spoke model)

This section's "Node/TypeScript supervisor", in-process polling/retry/stall-detection, and "Spawn Plugin attach/streamEvents" language predates the final design. Authoritative now: the conductor is a stateless node-agent (gaia conductor start, one per checkout, identity hash(hostname+path)); liveness is a hub-reconcile (fetchActiveRunsGaiaExecutor.getState probe → renewRun/releaseRun), not in-process retry; execution is the GaiaExecutor port (no attach/streamEvents). Read §10/§11 as historical; defer to 04-conductor.md/11-gaiad.md.

10.1 Responsibilities

The Conductor MUST:

  • register itself and heartbeat its Conductor registration,
  • poll dispatchable Tickets of its project compatible with its optional state narrowing (empty = all claimable states) and the assignee priority rule,
  • reconcile running Runs before dispatch,
  • validate code-defined workflow availability before dispatch,
  • resolve the current Ticket state to a handler,
  • enforce concurrency limits,
  • acquire a Drupal-backed atomic Ticket claim lease before launching,
  • renew and release Ticket claim leases,
  • start or attach sessions through the selected Spawn Plugin,
  • monitor liveness,
  • handle retry and backoff,
  • expose runtime snapshots.

The Conductor MUST NOT:

  • decide implementation content,
  • write ticket content state,
  • create Sub-Tickets directly,
  • mark work done.

10.2 Poll Tick

Each tick SHOULD run:

  1. Reconcile running Runs.
  2. Load WORKFLOW.md instruction text if needed for session context.
  3. Fetch candidate dispatchable Tickets.
  4. Resolve each Ticket's current state to a handler.
  5. Sort candidates.
  6. Atomically claim eligible Tickets in Drupal.
  7. Dispatch claimed Tickets while slots are available.
  8. Emit observability updates.

10.3 Eligibility

A Ticket candidate is dispatch-eligible only if:

  • it has required identifiers,
  • its current state has claimable: true in the workflow definition (Gate 1),
  • when the Conductor's states list is non-empty, its current state is present in that list (Gate 2 — a conditional gate; an empty/absent list applies no state filter),
  • its project matches the registered Conductor's project_id,
  • the assignee priority rule passes: an assigned Ticket is reserved for the assignee's conductors; unassigned Tickets are open to any project conductor handling the state,
  • it has no open blockers,
  • it is not running locally,
  • it is not claimed by a non-expired Drupal claim lease,
  • global concurrency slot is available,
  • per-state concurrency slot is available.

Routing is project + state + assignee. WORKFLOW.md MUST NOT choose a Conductor.

Concurrency limits (concurrency.global, concurrency.per_state, concurrency.per_handler) are enforced per Conductor. Under multiple registered Conductors the cluster-wide concurrency is the sum across Conductors. A cluster-wide cap is OPTIONAL and implementation-defined; if required, it MUST be enforced Drupal-side by counting active claim leases at claim time.

10.4 Sorting

Candidates SHOULD be sorted by:

  1. priority ascending, null last,
  2. created date oldest first,
  3. identifier lexicographic.

10.5 Claims

Local Conductor process state MUST NOT be the only distributed claim authority. The distributed claim authority is Drupal claim lease state, represented by gaia_run.

Run states (workflow gaia_run):

  • claimed
  • running
  • released
  • succeeded
  • failed

The Conductor MUST check local claimed/running state and MUST successfully acquire a Drupal claim lease before every worker launch. If a claim request returns claim_conflict, the Conductor MUST NOT start an agent for that Ticket.

10.6 Reconciliation

The Conductor MUST periodically reconcile local Runs with Drupal Ticket and Run state.

Rules:

  • Terminal Ticket state: stop the Run and clean the workspace according to workspace policy.
  • Active state equal to the claimed state: continue or refresh local snapshot.
  • Active state that no longer matches this Conductor, or inactive state: stop the local Run and keep workspace unless cleanup policy says otherwise.
  • Lost/expired lease: stop local Run without workspace cleanup.
  • Tracker read failure: keep local Run alive and retry reconciliation on the next tick.

10.7 Retry and Backoff

Retry policy:

  • Clean completion MAY use a short continuation delay when the Ticket is still active and AI-dispatchable.
  • Abnormal exit SHOULD use exponential backoff capped by retry.max_backoff_ms.
  • Retry MUST re-check Ticket eligibility and claim state before relaunch.

11. Execution Layer and Spawn Plugins

SUPERSEDED by 04-conductor.mdGaiaExecutor port

"Spawn Plugin" = GaiaExecutor (config-plugin). The final interface is sessionRefFor/startRun (detached)/getState (probe)/stopRun; attachRun and streamEvents are dropped (model: conductor probes, never streams). Hosting/detach is the executor's job (herdr). Defer to 04/11.

The Execution Layer MUST:

  • derive workspace key from Ticket identifier,
  • create or reuse per-ticket workspace,
  • run configured lifecycle hooks,
  • start or attach sessions through the selected Spawn Plugin,
  • stream events to the Conductor,
  • keep agent execution scoped to the ticket workspace.

Spawn Plugin interface:

text
capabilities() -> SpawnPluginCapabilities
startRun(input) -> SpawnedSession
attachRun(session_ref) -> SpawnedSession
stopRun(session_ref, reason) -> void
getState(session_ref) -> SpawnState
streamEvents(session_ref) -> AsyncIterable<SpawnEvent>

Run states are claimed, running, released, succeeded, and failed. Ticket claimability is controlled by the claimable flag — there is no separate state-type taxonomy. Agents MUST NOT prompt for human input inline; they MUST leave a comment with the question and transition the ticket so that it is no longer claimable by the AI conductor. The human-assist path then picks it up. (Future: a waiting_for_input run state may be introduced to let the conductor keep renewing the lease while a human responds in-session.)

The first implementation SHOULD include a deterministic fake plugin for tests and a real herdr plugin for terminal-backed local sessions. Additional plugins MAY support stdio, tmux, SSH, containers, or direct agent service APIs.

12. dropsh Write-Back Specification

SUPERSEDED by 03-control-plane.md/04-conductor.md

dropsh is no longer a separate API surface — it is a library inside the DrupalGaiaRemote adapter. Business write-back (ticket_update/ticket_create/footprint_log) is the GaiaRemote business profile, invoked by the agent via the gaia CLI. The op shapes below remain accurate as the semantic contract.

GAIA uses dropsh as the concrete write-back tool for Drupal JSON:API.

The write-back layer MUST expose semantic GAIA operations to agents:

  • ticket_update
  • ticket_create
  • footprint_log

12.1 ticket_update

Purpose:

  • request or apply a ticket state transition,
  • update assignee/agent fields when allowed,
  • attach comments,
  • attach links.

Validation:

  • transition MUST be allowed by the Drupal state_machine field for the current Ticket state,
  • transition MUST match the code-defined workflow for the Ticket type,
  • assignee/handler consistency MUST be checked,
  • write scope MUST match the session token.

12.2 ticket_create

Purpose:

  • create Sub-Tickets,
  • set parent ticket,
  • set blockers,
  • set initial state/handler/assignee.

Validation:

  • parent scope MUST match the session token,
  • blocked-by graph MUST remain acyclic,
  • initial state MUST exist in the code-defined workflow for the Ticket type.

12.3 footprint_log

Purpose:

  • record machine footprint on the active Run.

Validation:

  • an active Run context MUST exist,
  • footprint values MUST be non-negative,
  • write scope MUST match the session token.

13. Security and Auth

  • Conductor credentials MUST be scoped to read dispatchable Tickets and write only Conductor registration and Run claim/lease state.
  • Agent session tokens MUST be scoped to the claimed Ticket and allowed Sub-Tickets.
  • Raw Drupal credentials MUST NOT be exposed in handler prompts.
  • Server-side access checks MUST protect all write-back operations.
  • Atomic Ticket claim leases MUST prevent double dispatch across distributed Conductors.

14. Import and External Systems

Imported issue-like records MUST become Ticket records.

The implementation MUST NOT keep GitLabIssue, JiraIssue, HarvestTimeEntry, or similar foreign-system entities as the operative model for scheduling, board state, commercial sizing, or agent execution. Such systems MAY be represented as source references, adapter state, or logs, while GAIA behavior operates on canonical Tickets and the billing entities defined in 07-billing.md.

Each imported Ticket MUST have a deterministic import identity stored in ticket.origin.

The initial imported state, type, human assignee, and handler fields MUST be resolved through project import rules and the code-defined Ticket type workflow. They MUST NOT assume GitLab's state model matches GAIA's workflow.

Imported labels MAY be stored in ticket.labels or taxonomy terms. Labels SHOULD be normalized to lowercase.

Imported calendar or time records MAY contribute source evidence to proposed Work Evidence. They do not become customer quantities automatically.

15. Billing

The repository's 07-billing.md is the normative billing specification. In summary:

  • Delivery Points size conventional software effort without generative AI execution.
  • Work Evidence allocates employee hours for internal cost only.
  • Delivery Items own LP and link one-way to GAIA Tickets.
  • Billable Units snapshot accepted Delivery Item LP for export.
  • Runs provide machine telemetry and AI-cost evidence, not customer time.
  • GAIA execution entities never reference commercial or Work Evidence entities.
  • Harvest owns rates, prices, invoices, and the billed state in phase one.

16. Restart and Recovery

GAIA SHOULD recover from restart using Drupal state and workspace state.

On Conductor startup:

  1. Register/heartbeat the Conductor.
  2. Load Project instruction metadata as needed for future sessions.
  3. Reconcile gaia_run leases owned by the Conductor.
  4. Rebuild runtime scheduling state from active Drupal Tickets and gaia_run leases.
  5. Clean up terminal Ticket workspaces according to cleanup policy.

Exact in-memory scheduler state is not required to be durable.

17. Conformance Checklist

An implementation conforms to this specification if it:

  • normalizes imported external issues into canonical Tickets when external import is enabled,
  • stores Ticket lifecycle state with Drupal state_machine,
  • makes WORKFLOW.md instruction text available to sessions,
  • maps active Ticket states to handlers from code-defined state metadata,
  • registers one or more Conductors,
  • supports Ticket-level Conductor selection in GAIA/Drupal,
  • dispatches dispatchable Tickets through registered compatible Conductors by project, states, and assignee priority,
  • supports Drupal-backed claim leases plus local claimed/running/retry state,
  • starts or attaches sessions through Conductor Spawn Plugins,
  • scopes agent write-back through dropsh,
  • validates transitions server-side,
  • supports human handoff through human_assist Conductor runs,
  • records Run footprint data without treating it as employee time or customer quantity,
  • keeps Engagement, Delivery Item, Domain Link, Work Evidence, and Billable Unit semantics consistent with 07-billing.md, including the prohibition on reverse GAIA links and Run targets,
  • keeps foreign issue systems as import sources only.

Future (Not Yet Implemented)

The following concepts are explicitly deferred and not part of the current implementation:

  • human_assist / HumanInput / CustomerReview — states and assist-sessions for human handoff; human_assist Run mode.
  • Additional Run runtime metrics — provider/model detail, token buckets, turns, timestamps, and outcome needed for reliable AI-cost reporting.
  • session_id on Run — agent/adapter session reference.
  • agent_profile / agent_profile_snapshot — agent profile override and effective snapshot.
  • assignee_type — explicit AI vs. human assignee distinction.
  • Conductor targetingselected_conductor_id, conductor_pool, conductor_tags.
  • Conductor heartbeat / workflow cache — cached WORKFLOW.md with reload detection; workflow_instructions_hash/workflow_instructions_loaded_at as Drupal fields.
  • Retry / backoff — Conductor-driven retry with exponential backoff; retrying run state.
  • waiting_for_input run state — conductor mirrors a blocked session.

These companion design documents live in the GAIA repository next to this specification; they are not published on this site.

  • 01-domain-model.md - entities, fields, and diagrams.
  • 02-state-machine.md - workflow state and handler contract.
  • 03-control-plane.md - Drupal APIs and write-back.
  • 04-conductor.md - supervisor loop and claims.
  • 05-execution.md - workspace and adapter layer.
  • 06-roles.md - state-handler prompt contract.
  • 07-billing.md - Delivery Points, Work Evidence, Billable Units, and Harvest boundary.
  • 08-data-flows.md - example flows.
  • 09-build-order.md - sub-project order.
  • 10-vs-symphony.md - comparison to Symphony.