Skip to content

Flow State

These pages describe the current implementation. They may lag behind code changes.

The runtime’s source of truth for an active flow is a single JSON object — FlowRun — persisted at:

{stateRoot}/{projectNamespace}/{flowId}/flow.json

The state root is {workspaceRoot}/.a-society/state.

SessionStore.saveFlowRun writes it; SessionStore.loadFlowRun reads and validates it. All mutations go through SessionStore.updateFlowRun, which serializes concurrent writes using a per-flow async lock so no two coroutines corrupt each other’s state.

loadFlowRun also hydrates defaults for older nullable fields that still belong to the current state version, normalizes consent state, defaults missing feedback context to standard, and re-derives recordName / recordSummary from record metadata. recordName and recordSummary are stripped before writing flow.json.


FieldDescription
flowIdUnique timestamp-based ID (e.g. 20260527T143000123Z-a1b2c3). Set at flow creation, never changes.
projectNamespaceThe project subfolder name (e.g. my-project). Together with flowId forms the FlowRef used everywhere.
workspaceRootAbsolute path to the workspace root. Verified on load — a mismatch throws rather than silently proceeding.
recordFolderPathAbsolute path to the flow's record folder under .a-society/state/{projectNamespace}/{flowId}/record/. Contains workflow.yaml, record.yaml, and flow artifacts.
stateVersionSchema version string. Current value is 11 via CURRENT_FLOW_STATE_VERSION. Load fails if this doesn't match — flows cannot be reopened after a breaking schema change.

'running' | 'awaiting_improvement_choice' | 'awaiting_feedback_consent' | 'completed'

Controls what the scheduler does next:

  • running — the scheduler picks up runnable nodes
  • awaiting_improvement_choice — forward pass closed; waiting for the human to choose graph-based, parallel, or none
  • awaiting_feedback_consent — meta-analysis complete; waiting for human decision on feedback
  • completed — flow is done; no further orchestration

string[] — node IDs claimed by an active role turn. A node enters this list when the scheduler claims it (claimRunnableWorkForParallelRun) and leaves when the turn completes or the node is suspended.

Only one node per role instance can be in runningNodes at a time — the scheduler enforces this to prevent the same role’s session from being used concurrently.

Record<string, { role: string; reason: AwaitingHumanReason }> — nodes paused waiting for human input. The reason field is one of:

ReasonCause
prompt-humanAgent emitted a prompt-human signal
autonomous-abortTurn aborted or errored without producing a handoff
consentA consent prompt is in-flight (transitional — rarely persisted)
consent-deniedA tool call was denied and the turn was stopped
role-configurationRuntime is waiting for model, Skill, or MCP server selection for this role instance
handoff-approvalA human-colab node emitted a forward handoff and the runtime is waiting for operator approval

Record<string, { text: string; receivedAt: string }> — human replies queued by the UI before the scheduler has consumed them. Keyed by node ID. When the scheduler picks up a node from this map it delivers the text as a user message and removes the entry.

This map is only for plain text replies. Nodes awaiting consent, role configuration, or handoff approval use their own protocol paths.

Record<string, HandoffTarget[]> — forward handoffs staged by human-colab nodes and held until the operator approves or declines them. Keyed by the emitting node ID.

If approved, the runtime commits the already-emitted handoff without re-running the role. If declined, the staged handoff is discarded and the node is parked as prompt-human so the operator can give corrective guidance.

string[] — node IDs whose first-entry workflow contract has already been delivered. The runtime injects node work, node-specific required_readings, and the human-collaboration directive only on first entry. If the node is re-entered (e.g. after a backward handoff), those first-entry contract fields are not re-injected.


string[] — edge keys ("${from}=>${to}") for forward handoffs that have been realized. An entry here means the source node successfully handed off to the target. A backward handoff removes the entry so the edge can be traversed again with corrected work.

Record<string, string[]> — maps edge keys to artifact paths queued along that edge. Both forward and backward handoffs populate this. The orchestrator reads this when building the node-entry message so the receiving node gets the artifact in context.

Record<string, string[]> — the full cumulative history of artifacts ever sent along each edge, deduplicated. Used solely to reject artifact reuse — an agent cannot pass the same artifact path twice across the flow’s lifetime.

string[] — node IDs currently suspended after emitting a backward handoff. They are waiting for the predecessor to send a revised forward handoff back. A node in this list is not runnable and not in runningNodes.


{
mode: 'no-access' | 'partial-access' | 'full-access';
bash: {
allowedCommands: Record<string, { command: string; grantedAt: string }>;
};
mcp: {
allowedTools: Record<string, { toolName: string; grantedAt: string }>;
};
}

Persisted with the flow so consent grants survive server restarts. allowedCommands is keyed by exact command string. allowedTools is keyed by namespaced MCP tool name (for example, mcp__server__tool). On load, normalizeConsentState sanitizes the object — unrecognized modes fall back to no-access, and malformed grants are dropped.


Present only after the forward pass closes. See type ImprovementPhaseState:

FieldDescription
statusawaiting_choicerunningawaiting_feedback_consentcompleted (or skipped)
modegraph-based, parallel, or none — set when the human makes the improvement choice
completedRolesRole instance IDs whose meta-analysis session has finished
runningRolesRole instance IDs with a session in progress
awaitingHumanRolesRoles blocked on human input during improvement
pendingHumanInputsQueued human replies for improvement roles
findingsProducedMaps role instance ID → findings file path (repo-relative)
improvementWorkflowPathRepo-relative path to the runtime-generated improvement.yaml
feedbackArtifactPathRuntime-assigned path for the upstream feedback artifact
feedbackConsentpending, granted, or denied
singleRoletrue when the workflow has only one unique base role — affects UI presentation

FeedbackContext tells the optional feedback session what kind of flow produced the findings, which changes the focus instructions injected into that session:

{
kind: 'standard' | 'initialization' | 'update';
initializationMode?: 'takeover' | 'greenfield';
updateFromVersion?: string;
updateToVersion?: string;
}

Standard flows focus on reusable framework gaps and workflow friction. Initialization flows focus on scaffolding, inferred project facts, and missing human input. Update flows focus on whether the changelog and framework delta were clear enough to apply to an existing a-docs/ layer.