gRPC API

The Foundry service is defined in proto/foundry.proto.

Service: Foundry

Emit(EmitRequest) → EmitResponse

Fire an event into the system. The engine spawns processing as a background task and returns the event ID immediately. Use Trace to check for completion, Status to see in-flight workflows, or Watch for real-time event streaming.

Request:

FieldTypeDescription
event_typestringEvent type name
projectstringTarget project
throttleThrottle enumTHROTTLE_FULL, THROTTLE_AUDIT_ONLY, THROTTLE_DRY_RUN
payload_jsonstringOptional JSON payload

Response:

FieldTypeDescription
event_idstringDeterministic ID of the created event
workflow_idstringID of the triggered workflow (if any)

Status(StatusRequest) → StatusResponse

Query active workflow states.

Request:

FieldTypeDescription
workflow_idstringSpecific workflow (empty for all active)

Response:

FieldTypeDescription
workflowsrepeated WorkflowStatusActive workflow states

Watch(WatchRequest) → stream WatchResponse

Server-side streaming of live events as they are processed by the engine. Optionally filtered by project name.

Request:

FieldTypeDescription
projectstringProject name to filter by; empty string for all projects

Response (stream):

FieldTypeDescription
event_idstringEvent identifier
event_typestringEvent type name
projectstringTarget project
payload_jsonstringEvent payload as JSON

RegistryAdd(RegistryAddRequest) → RegistryAddResponse

Add a project to the daemon's in-memory registry and persist the change to registry.json. The daemon is the single source of truth for registry state.

Request:

FieldTypeDescription
namestringUnique project name
pathstringAbsolute path on the local filesystem
stackstringTechnology stack: rust, python, typescript, elixir, cpp
agentstringAI agent name
repostringGitHub repo slug (owner/repo)
branchstringDefault branch (empty → main)
iterateboolEnable iterate action
maintainboolEnable maintain action
pushboolEnable push action
auditboolEnable audit action
releaseboolEnable release action
install_commandstringShell command for local install (mutually exclusive with install_brew)
install_brewstringHomebrew formula for local install (mutually exclusive with install_command)
notesstringHuman-readable notes (empty → none)
timeout_secsuint64Per-project timeout (0 → use default 3600 s)

Response:

FieldTypeDescription
projectProjectThe newly created project entry

Errors: ALREADY_EXISTS if the name is already in the registry; INVALID_ARGUMENT for an unknown stack or conflicting install fields; INTERNAL with the stable message failed to persist registry state when saving fails.

CLI: foundry registry add ... routes through this RPC by default and therefore requires a reachable daemon. Pass --offline to bypass the daemon and mutate the registry file directly. Without --offline, an unreachable daemon returns an error and leaves the client-side registry file unchanged. The online path mutates daemon-owned state only and does not create FOUNDRY_REGISTRY_PATH. If daemon persistence fails, the RPC returns INTERNAL with a stable failed to persist registry state message and leaves both the daemon's in-memory registry and its on-disk registry bytes unchanged.

RegistryList(RegistryListRequest) → RegistryListResponse

List the daemon-owned registry inventory from the in-memory state held by foundryd. The online CLI path renders this response directly and does not read FOUNDRY_REGISTRY_PATH.

Request:

This message has no fields.

Response:

FieldTypeDescription
projectsrepeated ProjectEvery project currently loaded in the daemon-owned registry

Each Project carries the full registry data required by online clients: name, path, stack, agent, repo, branch, skip, action flags, install config, notes, timeout, installs-skill state, and audit exceptions.

Errors: None at the RPC layer; the daemon answers from already-loaded registry state.

CLI: foundry registry list routes through this RPC by default and therefore requires a reachable daemon. Pass --offline to read the registry file directly. Without --offline, an unreachable daemon returns an error and leaves the client-side registry file unchanged. The online path renders the RPC response directly and does not create FOUNDRY_REGISTRY_PATH; if that path is absent, the online path leaves it absent. foundry registry init is not part of this RPC surface and remains an offline-only recovery command.

RegistryShow(RegistryShowRequest) → RegistryShowResponse

Retrieve one exact-name project from the daemon-owned registry state. The match is exact and does not perform prefix or substring lookup.

Request:

FieldTypeDescription
namestringExact project name to retrieve

Response:

FieldTypeDescription
projectProjectThe full daemon-owned project record for that exact name

Errors: NOT_FOUND if no exact-name project exists.

CLI: foundry registry show <name> routes through this RPC by default and therefore requires a reachable daemon. Pass --offline to read the registry file directly. Without --offline, an unreachable daemon returns an error and leaves the client-side registry file unchanged. The online path renders the RPC response directly and does not create FOUNDRY_REGISTRY_PATH; if that path is absent, the online path leaves it absent.

RegistryRemove(RegistryRemoveRequest) → RegistryRemoveResponse

Remove a project from the registry by name.

Request:

FieldTypeDescription
namestringProject name to remove

Errors: NOT_FOUND if no project with that name exists; INTERNAL with the stable message failed to persist registry state when saving fails.

CLI: foundry registry remove <name> routes through this RPC by default and therefore requires a reachable daemon. Pass --offline to bypass the daemon and mutate the registry file directly. Without --offline, an unreachable daemon returns an error and leaves the client-side registry file unchanged. The online path mutates daemon-owned state only and does not create FOUNDRY_REGISTRY_PATH. If daemon persistence fails, the RPC returns INTERNAL with a stable failed to persist registry state message and leaves both the daemon's in-memory registry and its on-disk registry bytes unchanged.

RegistryEdit(RegistryEditRequest) → RegistryEditResponse

Apply partial edits to an existing project. Only fields that are non-empty / non-zero are applied. Use clear_* booleans to explicitly clear optional fields (e.g. clear_skip = true to un-skip a project).

Request:

FieldTypeDescription
namestringProject to edit (required)
pathstringNew path (empty → no change)
stackstringNew stack (empty → no change)
agentstringNew agent (empty → no change)
repostringNew repo slug (empty → no change)
branchstringNew branch (empty → no change)
skipstringNew skip reason; non-empty sets it; empty → no change unless clear_skip
clear_skipboolRemove the skip flag
iterateboolSet iterate to true (use clear_iterate to set false)
clear_iterateboolSet iterate to false
maintainboolSet maintain to true
clear_maintainboolSet maintain to false
pushboolSet push to true
clear_pushboolSet push to false
auditboolSet audit to true
clear_auditboolSet audit to false
releaseboolSet release to true
clear_releaseboolSet release to false
install_commandstringSet a shell-command install
install_brewstringSet a Homebrew formula install
clear_installboolRemove the install config
notesstringSet notes (empty string + clear_notes = false → no change)
clear_notesboolRemove notes
timeout_secsuint64Set timeout (0 → no change unless clear_timeout)
clear_timeoutboolRevert timeout to the daemon default

Response:

FieldTypeDescription
projectProjectThe updated project entry

Errors: NOT_FOUND; INVALID_ARGUMENT for conflicting install fields or unknown stack; INTERNAL with the stable message failed to persist registry state when saving fails.

CLI: foundry registry edit <name> ... routes through this RPC by default and therefore requires a reachable daemon. Pass --offline to bypass the daemon and mutate the registry file directly. Without --offline, an unreachable daemon returns an error and leaves the client-side registry file unchanged. The online path mutates daemon-owned state only and does not create FOUNDRY_REGISTRY_PATH. If daemon persistence fails, the RPC returns INTERNAL with a stable failed to persist registry state message and leaves both the daemon's in-memory registry and its on-disk registry bytes unchanged.

SentinelList(SentinelListRequest) → SentinelListResponse

List every daemon-owned sentinel from the in-memory scheduler control-plane state held by foundryd.

Request:

This message has no fields.

Response:

FieldTypeDescription
sentinelsrepeated SentinelEvery daemon-owned sentinel entry in scheduler evaluation order

Each Sentinel carries the exact scheduler contract the CLI needs to render: name, cron, emit_event_type, emit_project, emit_throttle, emit_payload_json, and enabled.

Errors: None at the RPC layer; the daemon answers from already-loaded sentinel state.

CLI: foundry sentinel list routes through this RPC by default and therefore requires a reachable daemon. Pass --offline to read the sentinel file directly. Without --offline, an unreachable daemon returns an error and leaves the client-side sentinel file unchanged. The online path renders the RPC response directly and does not create FOUNDRY_SENTINELS_PATH; if that path is absent, the online path leaves it absent.

SentinelShow(SentinelShowRequest) → SentinelShowResponse

Retrieve one exact-name sentinel from the daemon-owned scheduler control-plane state. The name match is exact and does not perform prefix or substring lookup.

Request:

FieldTypeDescription
namestringExact sentinel name to retrieve

Response:

FieldTypeDescription
sentinelSentinelThe full daemon-owned sentinel record for that name

Errors: NOT_FOUND if no exact-name sentinel exists.

CLI: foundry sentinel show <name> routes through this RPC by default and therefore requires a reachable daemon. Pass --offline to read the sentinel file directly. Without --offline, an unreachable daemon returns an error and leaves the client-side sentinel file unchanged. The online path renders the RPC response directly and does not create FOUNDRY_SENTINELS_PATH; if that path is absent, the online path leaves it absent.

SentinelEnable(SentinelEnableRequest) → SentinelEnableResponse

Mark one daemon-owned sentinel as enabled, persist the updated sentinel store, and wake the in-process scheduler so the next firing is recomputed immediately.

Request:

FieldTypeDescription
namestringExact sentinel name to enable

Response:

FieldTypeDescription
sentinelSentinelThe committed daemon-owned sentinel record after enable

Errors: NOT_FOUND if no exact-name sentinel exists; INTERNAL if the daemon cannot persist the sentinel store.

CLI: foundry sentinel enable <name> routes through this RPC by default and therefore requires a reachable daemon. Pass --offline to bypass the daemon and mutate the sentinel file directly. Without --offline, an unreachable daemon returns an error and leaves the client-side sentinel file unchanged. The online path mutates daemon-owned state only and does not create FOUNDRY_SENTINELS_PATH. If daemon persistence fails, the RPC returns INTERNAL, leaves the daemon-owned in-memory sentinel store unchanged, does not wake the scheduler, and leaves the on-disk sentinel bytes unchanged.

SentinelDisable(SentinelDisableRequest) → SentinelDisableResponse

Mark one daemon-owned sentinel as disabled, persist the updated sentinel store, and wake the in-process scheduler so any pending firing is cancelled immediately.

Request:

FieldTypeDescription
namestringExact sentinel name to disable

Response:

FieldTypeDescription
sentinelSentinelThe committed daemon-owned sentinel record after disable

Errors: NOT_FOUND if no exact-name sentinel exists; INTERNAL if the daemon cannot persist the sentinel store.

CLI: foundry sentinel disable <name> routes through this RPC by default and therefore requires a reachable daemon. Pass --offline to bypass the daemon and mutate the sentinel file directly. Without --offline, an unreachable daemon returns an error and leaves the client-side sentinel file unchanged. The online path mutates daemon-owned state only and does not create FOUNDRY_SENTINELS_PATH. If daemon persistence fails, the RPC returns INTERNAL, leaves the daemon-owned in-memory sentinel store unchanged, does not wake the scheduler, and leaves the on-disk sentinel bytes unchanged.

AddCampaign(AddCampaignRequest) → AddCampaignResponse

Add one campaign definition to the daemon-owned campaign store. The daemon parses the JSON definition, validates the referenced project and context paths against daemon-owned registry state, acquires the exclusive campaign-store lock, and persists the new definition atomically.

Request:

FieldTypeDescription
definition_jsonstringFull campaign definition JSON exactly as accepted by foundry campaign add

Response:

FieldTypeDescription
campaignCampaignDetailFull durable definition that was persisted

Errors: INVALID_ARGUMENT when the JSON is invalid or the definition is structurally invalid; FAILED_PRECONDITION when the definition references an unknown registered project or invalid context artifact; ALREADY_EXISTS when a campaign with the same name already exists; INTERNAL when the store cannot be saved. On INTERNAL, the daemon leaves the on-disk campaign store unchanged.

CLI: foundry campaign add <definition.json> routes through this RPC by default. The online CLI renders the returned CampaignDetail directly and does not re-read FOUNDRY_CAMPAIGNS_PATH. Pass --offline only when you intentionally need direct-file recovery while the daemon is stopped.

ListCampaigns(ListCampaignsRequest) → ListCampaignsResponse

List the durable campaign inventory from the daemon's configured campaign store. This is a read-only query: it loads the store at request time, returns records in deterministic campaign-name order, and exposes summary/status fields only.

Request:

FieldTypeDescription
projectstringOptional exact project-name filter; empty string returns all campaigns

Response:

FieldTypeDescription
campaignsrepeated CampaignDurable inventory records sorted by campaign name

Errors: FAILED_PRECONDITION when the campaign store is malformed; INTERNAL when the campaign store is unreadable. Missing or empty stores return an empty list.

PauseCampaign(PauseCampaignRequest) → PauseCampaignResponse

Pause a campaign. The operation is idempotent on the status field — pausing an already-paused campaign is not an error. The daemon holds an exclusive lock on the campaign store for the duration of the write, so a concurrent advance formation cannot interleave with a pause.

Any pending_run_result that was recorded before the pause is explicitly preserved: the operation never clears or overwrites it. The result remains available for the next manual advance after a subsequent resume.

Request:

FieldTypeDescription
namestringExact campaign name to pause

Response:

FieldTypeDescription
campaignCampaignDetailFull campaign detail reflecting the state after the pause is applied

Errors: NOT_FOUND when no campaign with the given name exists; FAILED_PRECONDITION when the campaign store is malformed; INTERNAL when the campaign store is unreadable or when persistence fails. On save failure, the daemon leaves the persisted campaign store unchanged.

CLI: foundry campaign pause <name> routes through this RPC when the daemon is reachable. Pass --offline to bypass the daemon and mutate the store file directly (useful when foundryd is not running). Without --offline, an unreachable daemon is an error and the client-side campaigns path is left untouched.

ResumeCampaign(ResumeCampaignRequest) → ResumeCampaignResponse

Resume a paused or escalated campaign, optionally extending its cycle budget. The daemon holds an exclusive lock on the campaign store for the duration of the write.

pending_run_result is explicitly preserved: the operation never clears or overwrites it. The result remains available for the next manual advance after resume.

Accepted statuses: paused and escalated. Budget-only escalations (where the engine stopped because the cycle limit was reached) may be resumed with this RPC without recording an owner-decision entry. Use DecideCampaign when the escalation contains a human judgment question that requires a policy record.

Exhausted budget guard: when add_cycles == 0 and cycles_completed >= max_cycles, the RPC returns FAILED_PRECONDITION. Pass a positive add_cycles to explicitly authorize more work; the engine never silently reactivates an exhausted campaign.

Request:

FieldTypeDescription
namestringExact campaign name to resume
add_cyclesuint64Additional cycles to add to max_cycles before resuming (0 = no extension)

Response:

FieldTypeDescription
campaignCampaignDetailFull campaign detail reflecting the state after the resume is applied

Errors: NOT_FOUND when no campaign with the given name exists; FAILED_PRECONDITION when the campaign is not paused or escalated, lacks authorized_by, the budget is exhausted and add_cycles == 0, add_cycles would overflow max_cycles, or the campaign store is malformed; INTERNAL when the campaign store is unreadable or persistence fails. On save failure, the daemon leaves the persisted campaign store unchanged.

CLI: foundry campaign resume <name> routes through this RPC when the daemon is reachable. Pass --offline to bypass the daemon and mutate the store file directly (useful when foundryd is not running). Without --offline, an unreachable daemon is an error and the client-side campaigns path is left untouched. The rendered output is built from the ResumeCampaignResponse.campaign detail — the CLI never re-reads the store file on the online path.

DecideCampaign(DecideCampaignRequest) → DecideCampaignResponse

Record an owner decision on an escalated campaign. The daemon holds the exclusive campaign-store lock for the full mutation, appends one durable owner decision record, and returns the campaign to active so the next advance can proceed with that policy in context.

The persisted owner decision carries the decision text, the campaign's current authorized_by identity, and the daemon timestamp. Existing counters and any stored pending_run_result are preserved.

Request:

FieldTypeDescription
namestringExact campaign name to update
decisionstringNon-empty owner decision text to record

Response:

FieldTypeDescription
campaignCampaignDetailFull campaign detail reflecting the state after the decision is applied

Errors: NOT_FOUND when no campaign with the given name exists; INVALID_ARGUMENT when decision is empty after trimming; FAILED_PRECONDITION when the campaign is not escalated, lacks authorized_by, or the campaign store is malformed; INTERNAL when the campaign store is unreadable or persistence fails. On save failure, the daemon leaves the persisted campaign store unchanged.

CLI: foundry campaign decide <name> --decision "<text>" routes through this RPC by default and therefore requires a reachable daemon. Pass --offline to bypass the daemon and mutate the store file directly. Without --offline, an unreachable daemon returns an error and leaves the client-side campaigns path unchanged.

CompleteCampaign(CompleteCampaignRequest) → CompleteCampaignResponse

Mark an authorized campaign complete from outside the formation loop. The request requires a non-empty reason and an existing authorized_by owner. The daemon stores the reason as an append-only owner record, clears any pending run result, changes the status to completed, and emits the normal CampaignCompleted event for terminal observers. Calling it on an already completed campaign is idempotent.

Request:

FieldTypeDescription
namestringExact campaign name to complete
reasonstringEvidence-backed owner reason for external completion

Response:

FieldTypeDescription
campaignCampaignDetailFull campaign detail reflecting the state after completion is applied

Errors: INVALID_ARGUMENT for a blank reason, NOT_FOUND for an unknown campaign, and FAILED_PRECONDITION when the campaign has no authorizing owner or the campaign store is malformed; INTERNAL when the campaign store is unreadable or persistence fails. On save failure, the daemon leaves the persisted campaign store unchanged and does not emit CampaignCompleted.

CLI: foundry campaign complete <name> --reason "<text>" routes through this RPC by default. The online CLI renders the returned CampaignDetail directly, never re-reads FOUNDRY_CAMPAIGNS_PATH, and leaves the client-side path untouched if the daemon is unreachable. Pass --offline only for direct-file recovery while the daemon is stopped.

CancelCampaign(CancelCampaignRequest) → CancelCampaignResponse

Stop a campaign permanently before its done evidence was met. Distinct from CompleteCampaign because completed is an evidence claim and cancellation records the opposite. The daemon changes the status to cancelled, clears any pending run result, stores the reason as an append-only owner record when the campaign has an owner, and emits CampaignCancelled for terminal observers.

Unlike CompleteCampaign, this does not require authorized_by — an unauthorized campaign has no other reachable terminal state.

With terminate_now, the daemon aborts the in-flight workflow before touching the store. Because a whole campaign runs inside one task, aborting it kills the running agent process and releases the campaign store lock that task was holding. The abort is awaited, so the subsequent store acquisition is uncontended. discard_work then selects how the orphaned worktree is disposed of by the DisposeCampaignWork block.

Request:

FieldTypeDescription
namestringExact campaign name to cancel
reasonstringOwner reason for abandoning the mission
terminate_nowboolAbort the in-flight workflow, killing the running agent process
discard_workboolThrow the terminated cycle's uncommitted work away

Response:

FieldTypeDescription
campaignCampaignDetailFull campaign detail reflecting the state after cancellation
event_idstringThe emitted CampaignCancelled event; empty when the call was a no-op

Errors: INVALID_ARGUMENT for a blank reason, NOT_FOUND for an unknown campaign, FAILED_PRECONDITION when the campaign is already completed or the store is malformed, and INTERNAL when the store is unreadable or persistence fails. Cancelling an already-cancelled campaign is idempotent: it returns the current detail with an empty event_id and emits no second event.

CLI: foundry campaign cancel <name> --reason "<text>" [--now] [--discard-work] routes through this RPC by default. --discard-work requires --now. --offline uses a graceful-only direct-file path that emits no event; --offline --now is refused rather than downgraded.

AdvanceCampaign(AdvanceCampaignRequest) → AdvanceCampaignResponse

Dispatch one manual campaign-advance workflow for an active or staged campaign. The daemon validates the current status under the exclusive lock, releases the lock, emits CampaignAdvanceRequested, and returns immediately.

Request:

FieldTypeDescription
namestringExact campaign name to advance

Response:

FieldTypeDescription
campaignCampaignDetailCurrent campaign detail at dispatch time
event_idstringRoot event ID of the dispatched CampaignAdvanceRequested workflow

Errors: NOT_FOUND when the name is absent; FAILED_PRECONDITION when the campaign is paused, escalated, or completed; INTERNAL when the store is unreadable. Rejected advances do not mutate the store.

CLI: foundry campaign advance <name> routes through this RPC by default. The online CLI prints the returned root event_id, watches the daemon-owned workflow stream, and renders the daemon trace for that event. It does not re-read FOUNDRY_CAMPAIGNS_PATH.

GetCampaign(GetCampaignRequest) → GetCampaignResponse

Retrieve the complete durable definition of one campaign by exact name. Unlike ListCampaigns, this RPC returns the full detail record including intent_refs, context_paths, done_evidence (with the Gate/Review type distinction preserved), escalation rules, and all runtime status fields.

Request:

FieldTypeDescription
namestringExact campaign name to look up

Response:

FieldTypeDescription
campaignCampaignDetailFull durable definition of the campaign

Errors: NOT_FOUND when no campaign with the given name exists in the store (even when other campaigns are present); FAILED_PRECONDITION when the campaign store is malformed; INTERNAL when the campaign store is unreadable.

CLI: foundry campaign show <name> routes through this RPC by default and renders the returned CampaignDetail directly, without re-reading FOUNDRY_CAMPAIGNS_PATH.

History(HistoryRequest) → HistoryResponse

List durable trace history from the daemon-owned trace store.

Request:

FieldTypeDescription
datestringExact day in YYYY-MM-DD format, or empty to request recent history
projectstringExact project filter, or empty for all projects
recent_daysuint32Number of recent days to return when date is empty; online CLI uses 7

Response:

FieldTypeDescription
daysrepeated HistoryDayMatching days, newest day first

Each HistoryDay carries a date plus repeated HistoryTrace. HistoryTrace contains event_id, event_type, project, success, total_duration_ms, and trace_id.

Completed traces are persisted to disk under ~/.foundry/traces/YYYY-MM-DD/ and survive daemon restarts. The daemon reads that store directly when serving history, preserving durable retention semantics even after in-memory cache entries expire.

CLI: foundry history routes through this RPC by default, renders the daemon response directly, and does not read or create a client-side FOUNDRY_TRACES_DIR unless --offline is explicit.

Trace(TraceRequest) → TraceResponse

Retrieve the trace of a completed event chain. Returns all events produced during processing and a record of each block execution.

Request:

FieldTypeDescription
event_idstringRoot event ID to look up

Response:

FieldTypeDescription
foundboolWhether a trace was found for the given event ID
eventsrepeated TraceEventAll events in the chain
block_executionsrepeated TraceBlockExecutionRecord of each block execution

Completed traces are persisted to disk under ~/.foundry/traces/YYYY-MM-DD/ and survive daemon restarts. The Trace RPC checks the in-memory store first (for recently completed chains) and falls back to disk for older traces.

CLI: foundry trace <event-id> routes through this RPC by default. When no trace is found, the CLI prints No trace found for <event-id> (expired or unknown).

Span(SpanRequest) → SpanResponse

Retrieve every event and block execution that belongs to one span.

Request:

FieldTypeDescription
span_idstringExact span ID to look up

Response:

FieldTypeDescription
foundboolWhether a span was found for the given span ID
eventsrepeated TraceEventEvents whose span_id matches the requested span
block_executionsrepeated TraceBlockExecutionBlocks whose own span or parent span matches
trace_idstringOwning trace ID, including for block-only spans
total_duration_msuint64Sum of returned block durations

Span is an in-memory lookup keyed by the daemon's span index. It is intended for live drill-down and status filtering rather than durable offline browsing. If the span is unknown, the response is found = false with empty collections and an empty trace_id.

Messages

WorkflowStatus

FieldTypeDescription
workflow_idstringWorkflow identifier
workflow_typestringWorkflow type name
projectstringTarget project
statestringpending, running, completed, failed
started_atstringISO 8601 timestamp
completed_atstringISO 8601 timestamp (empty if running)
task_blocksrepeated TaskBlockStatusPer-block status

TaskBlockStatus

FieldTypeDescription
namestringBlock name
statestringpending, running, completed, skipped, failed
started_atstringISO 8601 timestamp
completed_atstringISO 8601 timestamp
throttledboolTrue if emission was suppressed by throttle

TraceEvent

FieldTypeDescription
event_idstringDeterministic event identifier
event_typestringEvent type name
projectstringTarget project
occurred_atstringISO 8601 timestamp
throttleThrottle enumThrottle level for this event

Campaign

Summary-only wire form returned by ListCampaigns. Intentionally omits intent_refs, context_paths, done_evidence, and escalation — use GetCampaign to retrieve those fields.

FieldTypeDescription
namestringCampaign name
projectstringRegistered project name
missionstringCampaign mission statement
statusstringDurable status: staged, active, paused, escalated, or completed
cycles_completeduint64Number of dispatched task cycles
cycles_landeduint64Number of task results whose work reached trunk
max_cyclesuint64Configured campaign cycle budget
authorized_bystringOwner authorization identity, or empty when absent
agent_providerstringPreferred agent provider, or empty when absent
last_run_event_idstringMost recent campaign-run event ID, or empty when absent

CampaignDetail

Full durable definition of a campaign, as returned by GetCampaign. Carries all fields in Campaign plus the definition-time fields that the summary form omits.

FieldTypeDescription
namestringCampaign name
projectstringRegistered project name
missionstringCampaign mission statement
statusstringDurable status: staged, active, paused, escalated, or completed
cycles_completeduint64Number of dispatched task cycles
cycles_landeduint64Number of task results whose work reached trunk
max_cyclesuint64Configured campaign cycle budget
authorized_bystringOwner authorization identity, or empty when absent
agent_providerstringPreferred agent provider, or empty when absent
last_run_event_idstringMost recent campaign-run event ID, or empty when absent
intent_refsrepeated stringIntent reference labels that anchor the mission
context_pathsrepeated stringPaths to context documents for the campaign
done_evidencerepeated DoneEvidenceCompletion criteria with Gate/Review type distinction
escalationrepeated stringHuman-readable escalation instructions
owner_decisionsrepeated OwnerDecisionAppend-only owner policy decisions recorded after escalations

OwnerDecision

One recorded owner decision attached to a campaign after an escalation. These records are append-only and are threaded back into future campaign formation prompts as binding context.

FieldTypeDescription
decisionstringOwner-authored decision text
authorized_bystringOwner identity copied from the campaign at record time
decided_atstringRFC 3339 timestamp recorded by the daemon

DoneEvidence

One completion-evidence item. The kind field distinguishes the two variants.

Gate variant (kind = "gate"):

FieldTypeDescription
kindstring"gate"
commandstringShell command to evaluate
requiredboolWhether this gate is required for completion
statementstringEmpty for Gate items
artifactsrepeated stringRepository-relative paths that must exist before the gate can pass

Review variant (kind = "review"):

FieldTypeDescription
kindstring"review"
commandstringEmpty for Review items
requiredboolfalse for Review items
statementstringHuman-readable completion statement
artifactsrepeated stringEmpty for Review items

TraceBlockExecution

FieldTypeDescription
block_namestringName of the block that executed
trigger_event_idstringEvent ID that triggered this block
successboolWhether the block succeeded
summarystringHuman-readable summary of the result
emitted_event_idsrepeated stringIDs of events emitted by this block
duration_msuint64Wall-clock milliseconds for this block execution (including retries)
raw_outputstringCombined stdout+stderr from any shell command run by this block
exit_codeint32Exit code from any shell command run by this block
trigger_payload_jsonstringJSON payload of the event that triggered this block
emitted_payload_jsonsrepeated stringJSON payloads of events emitted by this block
audit_artifactsrepeated stringPaths to audit artefact files produced by this block

Throttle (enum)

ValueNumberDescription
THROTTLE_FULL0All blocks emit
THROTTLE_AUDIT_ONLY1Observers emit, mutators suppress
THROTTLE_DRY_RUN2Read-only, no side effects