Request lifecycle
Every authenticated tool call follows the same skeleton: verify the session, load the definition, validate the claim the agent is making, advance the state, reseal it, and sign the trace. That uniformity is possible because the whole system crosses one boundary — sixteen MCP tools with schema-validated parameters, no side channel, no shared files as API. This page walks that skeleton using next_activity — the most eventful call — as the example.
The handlers live in src/tools/workflow-tools.ts; the shared machinery each stage uses is mapped in server anatomy.
get_activity skip stages five and six unless they record fetch or delivery events.1–2. Entry, and proving the state is trustworthy
Parameters are validated by the Zod schema attached at registration, so a handler never sees a malformed call. The handler runs inside withAuditLog, which times it and records a trace event whatever the outcome.
loadSessionForTool then turns the six-character session_index into trusted state: it resolves the planning folder (how the index maps to a folder), recomputes the HMAC of session.json, and compares it with the stored .session-token digest using a timing-safe comparison. A mismatch raises SessionStoreError(SEAL_MISMATCH) and the call goes no further — the server refuses to act on state it cannot vouch for. The file is finally parsed against the session schema, so downstream code works with typed state, never raw JSON.
One more gate applies after loading: if state.activeCheckpoint is set, the session is paused — navigation and content reads alike are refused until the checkpoint is resolved (the checkpoint model).
3. Loading the definition
The workflow named in the session is loaded fresh from the workflow directory: the manifest is parsed and validated, each activity file is validated and gets its step ids populated, and the artifactPrefix is derived from the activity filename's numeric prefix. Rule and checkpoint fragment refs are materialized at load; borrowed cross-workflow activities retain their source-workflow id for technique and fragment scoping. Nothing is cached across calls — the definition on disk is always the definition in force, which is what lets workflow hotfixes land mid-session (with a version-drift warning rather than a failure).
The get_activity delivery path
Read-only tools skip stages five and six unless they record fetch or delivery events. get_activity is the richest read path: after the session gate and definition load, the handler composes the inherited technique bundle, materializes checkpoint fragment refs in the activity YAML, derives an eager step-technique budget from the caller's context_tokens, and inlines ungated step-bound techniques that fit under a step_techniques map. Each inlined entry is identical to a step-bound get_technique fetch — including binding-seam provenance annotations — and is recorded as a technique_bundled history event. When the activity contains agent-interpreted constructs, an enforcement_notes block names who executes them. Reference delivery may collapse already-delivered bundle entries and inlined techniques to { delivery: "unchanged", content_hash } markers before the response is returned.
4. Fidelity checks
With trusted state and a validated definition, the server checks the claim the call is making. For next_activity that means five validators from utils/validation.ts:
| Validator | Checks | On failure |
|---|---|---|
validateActivityTransition | The requested activity is a declared transition from the current one (the terminal sentinel is always valid). | Blocks |
validateWorkflowVersion | The definition's version still matches the version captured at start_session. | Warns |
validateStepManifest | The reported step_manifest covers the activity's declared steps — nothing missing, nothing invented, order preserved, no empty outputs. | Warns |
validateTechniqueFetches | Every manifested technique step has a matching technique_fetched or technique_bundled history event — the step was actually loaded, not summarized from memory. | Warns |
validateTransitionCondition, validateActivityManifest | The stated transition condition exists in the definition, and the completed-activity trail is consistent with the workflow graph. | Warns |
The split is deliberate: only checks that are cheap and certain block the call; everything judgment-shaped becomes a warning in the response and the trace — see the advisory layers on the fidelity page.
5. Advancing the state
advanceSession applies the mutation as a controlled update: the monotonic seq counter increments, the timestamp refreshes, and the handler's mutator appends history events — activity_exited for the activity being left, activity_entered for the new one, and workflow_completed when the transition is terminal. A child session reaching its terminal state also notifies its parent, flipping the child's entry in the parent's triggeredWorkflows to completed (best-effort — a moved parent is tolerated).
6. Persisting and resealing
The updated state is written atomically — to a temporary file first, then renamed over session.json, with a copy-based fallback when the rename crosses filesystems. The HMAC is recomputed over the canonical JSON bytes and written to .session-token, and both files are kept at mode 0600. There is no code path that writes state without resealing it.
7. Signing the trace and responding
When tracing is enabled, the events accumulated since the last next_activity are packaged into a trace-token payload, HMAC-signed, and returned in _meta.trace_token — a tamper-evident receipt for that leg of the run, reconstructable later via get_trace.
The response body itself stays minimal: next_activity returns just the new activity's id and name, because the full definition is the worker's to fetch via get_activity. Alongside it, _meta.validation carries the outcome of stage four — { status, warnings } — so deviations are visible to the orchestrator on the very call that detected them. The warning taxonomy, error surfaces, and delivery markers an agent may encounter are catalogued in Protocol.
Next
The state this lifecycle protects is described field-by-field in the session store; the checks that guard the definitions themselves are in the quality system.