Appearance
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 releasecommand. 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 composedmachine_idin the engine config — there is no derived fallback. - The
gaia-issueskill. The single global skill described here is superseded by the@gaia/*skill set, which a project loads through itsWORKFLOW.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); thegaiaCLI is the client (kubectl). There is no central daemon/supervisor and no TCP — the conductor and thegaiaCLI never talk directly; both are independent Drupal clients coordinating through sharedgaia_runstate. The conductor is built ports-and-adapters over three config-pluggable ports speaking domain types (not JSON:API):GaiaRemote(control-plane; Drupal adapter usesdropshonly 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 viagaia 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 thegaiaCLI. 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.dropshis a library, not a separate API surface (see03-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 ahuman_assistsession; direct UI writes are reserved for administrative recovery. - A successful Run can end at a handoff state, for example
HumanInputorCustomerReview, not necessarilyDone.
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.mdin the project repository. - Combine global
gaia-issuebehavior with project-specificWORKFLOW.mdinstructions 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
dropshas 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.mdthe 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:
| Area | Source of truth |
|---|---|
| Ticket state, assignments, execution audit | Drupal GAIA execution context |
| Delivery Items, LP, Work Evidence, Billable Units, billing status | Separate commercial and work-evidence context; physical deployment deferred |
| Rates, prices, invoices, payments | External billing system (Harvest in phase one) |
| State machine (states, transitions, claimability) per Ticket workflow | GAIA code (state_machine workflows) |
| Project-specific work instructions | Repository WORKFLOW.md as raw text |
| Global GAIA issue behavior | gaia-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
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_machinemodule. - 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_assistsessions, not in the dashboard. - Provides read APIs for the Conductor.
- Receives write-back through
dropsh.
Project Instructions Context- Reads
WORKFLOW.mdfrom 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_hashand related run fields are not yet implemented (see §Future). - MUST NOT parse
WORKFLOW.mdinto state, transition, handler, skill, policy, hook, retry, concurrency, or dispatch configuration.
- Reads
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.mdinstruction text during prompt construction.
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.
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.
Spawn Plugin- Normalizes concrete runtime mechanisms such as
herdr, stdio, tmux, SSH, containers, Claude Code, Codex, or test fakes behind a common interface. - Provides
dropshas CLI/MCP write-back tooling. - Reports usage, rate limits, turn outcomes, liveness events, and blocked/input-required state.
- Normalizes concrete runtime mechanisms such as
dropsh Write-Back- Uses the
dropshCLI against Drupal JSON:API. - Executes GAIA semantic operations such as
ticket_update,ticket_create, andfootprint_log. - Performs schema-backed validation where available.
- Uses the
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.originandticket.links. - MUST NOT create long-lived foreign-system issue entities as the operative model.
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:
- Policy layer: code-defined workflow policy +
gaia-issue+ repositoryWORKFLOW.mdtext. - Control-plane layer: Drupal execution entities, dashboard, read APIs, validation, and audit; commercial/work-evidence capabilities remain a separate bounded context.
- Coordination layer: Conductor polling, claiming, dispatch, retry, reconciliation.
- Execution layer: workspace lifecycle, hooks, remote/local execution.
- Agent/runtime layer: Spawn Plugins, model-specific process handling, and event normalization.
- Write-back layer:
dropshcommands mapped to GAIA operations. - 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:
idnamebilling_profile(legacy compatibility only; not authoritative for the new context)
5.2 Project
Project stores execution context for tickets.
Required logical fields:
idnamereposcoder_configmembers
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:
ididentifierworkflow: selects the code-definedstate_machineworkflow directly (e.g.gaia_feature,gaia_bug,gaia_chore)titledescription(text_long, formatgaia_rich— Markdown stored, rendered to HTML on display)stateassignee_user_id: optional human assigneepriorityparent_idblocked_bylabels(entity_reference → gaia_term, handlergaia_labels)origin(string — source URL)linkscreated_atupdated_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:
idticket_idconductor_id(lease holder; replaces the formerclaim_owner)state:claimed,running,released,succeeded, orfailed(state_machine workflowgaia_run)claim_expires_atstate_at_start(frozen snapshot of the Ticket state at claim time — plain string, not an active state_machine field)started_atworktree_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:
idmachine_idlabelowner_user_idproject_id: the project this conductor registration belongs to (one registration per project)states: optional narrowing of the workflow states this conductor handles (only states withclaimable: trueare eligible). Empty/absent = serve every claimable state in the project; a non-empty list restricts to those states, e.g.todo,coding,reviewstatus:online,draining, orofflinecurrent_loadworkspace_root(base directory of the Node; per-ticket git worktrees are created under it)endpoint_urlmax_parallellast_seenlease_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.identifierby replacing every character not matching[A-Za-z0-9._-]with_. - State comparison MUST use lowercase normalized names.
- Labels SHOULD be normalized to lowercase.
blocked_byMUST represent incoming blockers.priorityMUST 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):
project.workflow_file_pathif configured.- Default:
WORKFLOW.mdat 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.
| Concern | Code (GAIA, per Ticket workflow) | WORKFLOW.md (repository) |
|---|---|---|
States, transitions, claimable flag | MUST | MUST NOT |
| Project work rules (tests, branches, conventions) | defaults | SHOULD as text |
concurrency/polling/retry/environment/hooks/agent | MUST | MUST 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
claimableflag and allowed transitions, - the conductor's optional registered
stateslist (only states withclaimable: trueare 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.mdMUST NOT alter states or transitions. - Ticket transitions MUST be applied through the
state_machineAPI, not by blindly overwriting a raw state value. - Claimability is a per-state flag (
claimable: true) in the workflow definition — there is no separatetypetaxonomy orhandlerannotation 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_machinetransition validation with GAIA guards for assignee consistency, blockers, permissions, and DAG cycles.
7.7 Session Construction
The session for a dispatched state MUST combine:
- global
gaia-issueinstructions, - the code-defined handler/skill for the current Ticket type and state,
- project-specific
WORKFLOW.mdinstruction text, - normalized Ticket data,
- Project context,
- attempt/retry context,
- 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):
| State | Claimable | Notes |
|---|---|---|
backlog | no | inactive, planned |
todo | yes | activated, ready to start |
spec | yes | needs requirements clarification |
coding | yes | implementation |
review | yes | review/QA |
done | no | terminal |
cancelled | no | terminal |
Default transitions (gaia_feature):
backlog->todo(activate)todo->spec,coding,cancelledspec->coding,cancelledcoding->review,cancelledreview->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_byfor decomposition via Sub-Tickets.
Rules:
- Decomposition MUST use Sub-Tickets.
- Internal sequencing SHOULD be represented by Ticket state transitions, not by Tasks.
fix -> review -> mergeSHOULD 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 (fetchActiveRuns → GaiaExecutor.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:
- Reconcile running Runs.
- Load
WORKFLOW.mdinstruction text if needed for session context. - Fetch candidate dispatchable Tickets.
- Resolve each Ticket's current state to a handler.
- Sort candidates.
- Atomically claim eligible Tickets in Drupal.
- Dispatch claimed Tickets while slots are available.
- Emit observability updates.
10.3 Eligibility
A Ticket candidate is dispatch-eligible only if:
- it has required identifiers,
- its current state has
claimable: truein the workflow definition (Gate 1), - when the Conductor's
stateslist 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:
- priority ascending, null last,
- created date oldest first,
- 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):
claimedrunningreleasedsucceededfailed
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.md → GaiaExecutor 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_updateticket_createfootprint_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_machinefield 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:
- Register/heartbeat the Conductor.
- Load Project instruction metadata as needed for future sessions.
- Reconcile
gaia_runleases owned by the Conductor. - Rebuild runtime scheduling state from active Drupal Tickets and
gaia_runleases. - 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.mdinstruction 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_assistConductor 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_assistRun mode.- Additional Run runtime metrics — provider/model detail, token buckets, turns, timestamps, and outcome needed for reliable AI-cost reporting.
session_idon 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 targeting —
selected_conductor_id,conductor_pool,conductor_tags. - Conductor heartbeat / workflow cache — cached
WORKFLOW.mdwith reload detection;workflow_instructions_hash/workflow_instructions_loaded_atas Drupal fields. - Retry / backoff — Conductor-driven retry with exponential backoff;
retryingrun state. waiting_for_inputrun state — conductor mirrors a blocked session.
18. Related Documents
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.