`process.rs`: Durable Memory for Multi-Step DAL Work
A tool-using runtime can execute a sequence of actions, but execution alone is not continuity. If the host stops after step three, the next invocation needs to know what the objective was, which tools already ran, whether the last result was trustworthy, and whether a human answer is still required. In dist_agent_lang, src/stdlib/process.rs provides that durable boundary.
The module is deliberately narrower than a scheduler or commitment ledger. Its opening comment describes a process as “the memory of a single run”: an objective, completed tool steps, and the reason the run stopped. That distinction matters. A process records execution state; a Human Computer cycle or task queue can decide when to revisit it.
The record is a compact execution journal
ProcessRecord is the central serialized structure. It contains:
- a schema version and stable process id;
- the objective text and current state;
- suspension reason, question, and optional principal answer;
- the number of steps used;
- recorded tool steps and bounded conversation turns;
- optional cycle and objective metadata;
- creation and update timestamps in Unix milliseconds.
The metadata bridge is intentionally optional. cycle_id, cycle_phase, objective_id, objective_class, and objective_due_bucket let a Human Computer layer associate a run with a broader operating cycle without making the runtime process itself a scheduler. This keeps DAL policy-agnostic while still giving an application enough information to resume work coherently.
A new process starts in suspended state with the reason opened. Opening is persisted immediately, and an empty objective is rejected. Process ids are also constrained to letters, digits, dots, underscores, and hyphens, with a maximum length of 80 characters and no ... These checks are small but important: the id becomes a filename under the process root.
Steps are recorded with idempotency, not just chronology
A ProcessStep stores the tool name, canonical arguments, success flag, a bounded result digest, artifact paths, and an idempotency key. The key is a SHA-256 digest over the tool name, a newline, and the canonical argument string.
That design solves a practical crash-recovery problem. A wake can die after a tool has completed but before the surrounding loop has fully advanced. When the same tool signature is appended again, append_step_at detects an existing successful step with the same key and returns skipped: true. The journal therefore protects against repeating an already-successful operation when the execution cursor is replayed.
This is not a claim that every external tool is magically idempotent. The runtime can recognize a repeated canonical operation; tool-specific side effects still need their own safe semantics. The journal makes the boundary explicit rather than hiding retries behind an opaque loop.
Each step also records a digest instead of retaining an unbounded tool response. Digests are clipped to 2,000 characters, artifact paths are capped at 32, and artifact paths must be relative, jail-safe, and free of backslashes or parent-directory traversal. The record remains useful for inspection without becoming an uncontrolled copy of tool output.
Suspension is a first-class state
Suspension is not synonymous with failure. The module accepts five reasons:
ask_user— the process needs a principal answer;step_budget— the run reached its tool-step budget;wall_clock— the run reached a time boundary;guard— a runtime guard stopped it;unverified— the result cannot yet be treated as verified.
An ask_user suspension must include a question. On resume, the process returns needs_answer until an answer is supplied. Once supplied, the question and answer are added as turns, the answer is saved as principal_answer, and the state moves back to running.
The other suspension reasons can resume without new human input. This gives a host a clean distinction between “continue the objective” and “ask the owner before proceeding.” next_action() exposes the same decision as one of terminal, continue, or needs_answer.
Crash recovery is intentionally conservative
resume_at treats a record in running state as a crash cursor and returns continue. The comment explains the assumption: the wake may have died before it could suspend or finish, so the missing tool row is the step that may run again. Successful prior steps remain protected by their idempotency keys.
Completed and failed records are terminal. Unknown states are rejected rather than guessed. This is a good persistence rule: recovery should be predictable, and a future state should not silently be interpreted as permission to execute.
Filesystem behavior and operational controls
By default, journals live under .dal/processes in the current working directory. DAL_PROCESS_ROOT can override that location. Persistence is enabled by default and can be disabled with DAL_AGENT_RUNTIME_PERSIST=0 or false; when disabled, attempts to use the default durable API return an explicit error.
The journal is written as JSON through a temporary-file-and-rename path in the module, allowing the runtime to expose a durable record without requiring a database. SCHEMA_VERSION is currently 1, which gives future readers a way to distinguish changes in the serialized contract.
The tool loop flushes the journal when it receives a process id through run_multi_step_tool_loop_in_process. At the HTTP layer, POST /task can opt into this behavior with process_id or durable: true. In other words, durability is an explicit execution choice at the host boundary, not an accidental side effect of every short reply.
What process.rs does not do
It does not schedule future work, choose priorities, publish artifacts, or decide whether a side effect needs approval. It does not turn a process into a commitment ledger. Those responsibilities belong to the host application, such as DAL Human Computer, and to its policy and review surfaces.
That separation is the module’s strength. The runtime supplies a small, inspectable journal with bounded data, replay-aware steps, and explicit suspension semantics. A higher-level coordinator can add wake cycles, queues, review gates, and artifact indexes without changing the meaning of a process record.
A useful contract for agent infrastructure
The most important idea in process.rs is not the JSON schema by itself. It is the contract between execution and resumption:
- name the objective;
- persist progress after each tool step;
- identify repeated work canonically;
- preserve why execution stopped;
- ask for human input explicitly when needed;
- resume only from a known state;
- expose enough metadata for the host to route the next action.
That contract turns a multi-step agent run from an ephemeral model interaction into an inspectable runtime object. It does not promise that all work is safe or complete. Instead, it makes progress, interruption, retry, and uncertainty visible—exactly the properties a durable agent system needs before it can operate predictably across wakes.
Source note
This article is grounded in the current implementation at ../dist_agent_lang/src/stdlib/process.rs and the DAL Human Computer terminology contract at docs/development/TERMINOLOGY_SPEC.md. The module is the authority for the implementation details described here; application-level scheduling and approval behavior should be read from their current runtime contracts.