WebCodex uses the word session for two independent systems. They share casual vocabulary only. They must not be merged, cross-wired, or inferred from each other.
The default durable event tail retains up to 2,000 events per Session, while
one model-facing summary remains capped at 200 events (50 by default). These are
independent bounds: longer forensic/recovery retention does not enlarge one model
response, and the ledger remains a bounded tail rather than an archive. If
retention_truncated is true, event-derived summary counts describe the retained
ledger rather than claiming lifetime-complete history.
Executable constraints that agents must obey live in
AGENTS.md; this document is the Workflow Sessions domain
source linked from §6. Standing architecture summary:
architecture-decisions.md §1.
| Formal name | Casual aliases (avoid in design) | Implementation home |
|---|---|---|
| Workflow Session | coding session, tool ledger session, wc_sess_* session |
tool_runtime::sessions |
| Action Audit Session | HTTP action session, audit session, operator action trail | Internal module action_audit_sessions (SQLite table still named action_sessions for compatibility) |
When writing code, docs, or reviews, prefer the formal names above. If a statement is true for only one kind, name that kind explicitly.
Adapters may derive a bounded, domain-separated ClientWindow from host-owned window metadata such as _meta["openai/session"]. The raw host value is never persisted or exposed. A principal-scoped wc_peer_* id may be derived from the hashed Window for lightweight collaboration routing, but neither identity is a Workflow Session selector, Project authority, credential, model-context-retention proof, implicit recorder, model-turn id, or liveness proof. Stateless MCP still never treats caller-supplied Mcp-Session-Id as hidden continuity.
Window correlation supports ActionAudit, agent-loop observations, bounded Peer awareness/messaging, and other explicitly designed non-authority features, while ordinary coding continuity remains the canonical Workflow Session lifecycle. work_on_project(session_id=...) resumes only that exact authorized Session; omission creates a fresh Session. Credentials, Project ids, windows, connections, Peer ids, and prior requests never select a Workflow Session implicitly.
The durable Agent/Conversation/Wake domain is also not a session type. A Server-minted Agent may participate in Conversations and own asynchronous Agent Tasks while concrete execution uses zero or more independent Workflow Sessions. Agent Task / Agent TaskAttempt remain a distinct work-ownership domain; similar names do not imply shared lifecycle, window binding, storage, or authority. See ../architecture/durable-agent-runtime.md.
Window Peer state is a small communication plane, not a third Session domain. Discovery requires the same authenticated principal, exact Project, and meaningful Window activity within a 10-minute recency window. Discovery produces a retained-state-deduplicated peer_awareness hint; the word "recent" is literal and does not imply online/presence state.
After discovery, a wc_peer_* route is principal-scoped but Project-independent. post_peer_message therefore does not become invalid merely because either collaborator later works in another Project/worktree; bounded Peer retention still applies. The route conveys only the explicit bounded message and safe message metadata. It grants no visibility into the peer's current Project, Workflow Session, files, branch, activity, assignment, or handoff. Session business tools retain their own target/project authorization, including the existing exact-project equality fence for recorder-to-target Workflow Session collaboration.
Peer delivery is ambient on model-facing ToolResults. Ordinary messages are persisted and marked after one projection attempt; ACK-required messages are eligible again whenever the current request omits their id. Persistent first_projected_at_ms, last_projected_at_ms, projection_count, and first_ack_observed_at_ms are analysis/observability facts only. They must not be described as delivery, reading, acceptance, current memory, or work completion.
Bounded coding-task continuity and evidence for MCP, GPT Actions, and runtime tools. It records what happened in a task so review, validation, handoff, and finish can reason about the same unit of work.
- Coding task start / finish lifecycle
- Tool-call evidence (bounded, redacted)
- Checkpoint-related task continuity
- Session-local message board
- Validation evidence and closeout summaries
- Handoff / finish tooling (
session_handoff_summary,finish_coding_task, …)
Workflow Session lifecycle is independent from the durable wc_goal_* Goal domain. A Session may be explicitly correlated to a Goal, but that correlation grants no Session/Project authority and does not make the Session the Goal's lifecycle owner. In particular, finish_coding_task does not transition a Goal to completed; any Goal transition is a separate explicit Goal-domain mutation.
| Aspect | Contract |
|---|---|
| ID form | wc_sess_* (SESSION_ID_PREFIX) |
| Business field | session_id on tools that take a workflow session as input |
| Coding resume field | session_id on canonical external work_on_project; the internal startup primitive's resume_session_id is not a wire/API field |
| Recorder field | recording_session_id on generic wrappers, including the stateless MCP 2026 tool-argument projection (metadata only; stripped before concrete tool dispatch) |
Stateless MCP 2026 never derives a Workflow Session or recorder identity from transport/window continuity. ChatGPT may supply _meta["openai/session"] as a hashed ClientWindow, but that identity is intentionally not a Workflow Session selector or trusted provenance source. Its tools/list schema therefore projects recording_session_id as explicit wrapper metadata for runtime tools. A call may carry recording_session_id=W while the concrete tool body carries business session_id=C; the MCP adapter removes the recorder field before concrete parsing and the kernel independently authorizes W before it can record evidence or supply trusted collaboration provenance. This does not revive legacy mcp-session-id, grant target authority, or infer a recorder from credentials, project identity, connection state, or ClientWindow.
Window activity correlation is observational and does not weaken that targeting
rule. Stateless MCP may persist the hashed ClientWindow on ActionAudit events
and attach a normalized Window↔Workflow Session relation only after the existing
Session/Project authority path has already established the fact: an authorized
outer recording_session_id yields a recording relation, while a successful
canonical work_on_project create/resume yields a work_on_project relation from
its typed projection. These relations are intentionally many-to-many and confer
no lease, ownership, authority, or implicit recorder selection.
Agent-loop timing uses the same observational boundary without adding a Session
identity. Adjacent meaningful MCP calls are eligible to pair only when the hashed
ClientWindow and canonical authenticated principal correlation both match.
Project remains an event dimension and current-visibility boundary, not the
continuity identity. A matching Window does not prove a matching model turn:
WebCodex receives no reliable turn/generation/response id and never infers one
from elapsed time. Missing or malformed host Window metadata therefore leaves
loop continuity unavailable rather than falling back to Workflow Session,
credential, Project, connection, trace, or MCP Session identity.
For a later successful meaningful Project tool call with no explicit recorder, WebCodex may diagnose a recorder continuity gap when the same hashed Window, canonical principal, and exact Project have a recent explicit Session affinity. The candidate must still be Active, match the exact Project, and pass the current caller through the ordinary Session authority check. The diagnostic may suggest that exact Session to the model, but the missing call is not backfilled into the Session ledger and the candidate never becomes a sticky recorder. Status and discovery calls are not meaningful gap evidence. This preserves the original Session event order, revisions, ACK state, validation evidence, and permission facts while making an otherwise silent recorder discontinuity observable.
One real kernel tool request also receives one trusted runtime-generated logical invocation correlation id. The outer recorder event pair and any inner concrete business-execution event pair inherit that id while retaining independent pair-level call_id values. A small recorder/business role discriminator lets Session-local semantic projections deterministically prefer authoritative business execution facts when both pairs land in the same Workflow Session. Raw ledger facts remain intact. Correlation never grants authority and is not a permission identity, retry token, idempotency key, execution identity, lifecycle key, or model-supplied input. If recorder Session W and business Session C differ, each Session keeps its own one-invocation semantic evidence; correlation is never used for cross-Session global deduplication. Current-v2 ledger events without the additive correlation fields remain uncorrelated and are projected conservatively per event; restore never invents an id or rewrites persisted history.
Stateless MCP 2026 also projects optional ack_session_message_ids wrapper metadata, bounded to eight opaque wc_msg_* ids. For Session messages, an ACK is request-scoped evidence that the current model context still retains an unresolved ACK-required message in the exact authorized recording Workflow Session. For Window Peer messages, the same wrapper may acknowledge an ACK-required message addressed to the current principal-bound ClientWindow even when no Workflow Session recorder exists. The adapter removes ACK metadata before concrete tool parsing; ACK never grants authority, resolves a message, accepts work, or gates the concrete tool effect. Any Session or Peer message kind/priority may request ACK. Accepted ids suppress that body only in the current response; later omission makes an unresolved Session message or retained Peer ACK message eligible for bounded re-projection. Historical ACK state is never used to infer current model-context retention.
An ACK-required Session message may persist first_ack_observed_at; an ACK-required Peer message persists the analogous window-message timestamp. For Session messages only the first accepted ACK advances message-observation revision; repeated echoes do not create revision churn. These fields mean only that the Server once observed an explicit ACK echo. They are not delivery/read receipts and do not by themselves change business status. resolve_session_message remains the durable processed-state transition for Session messages.
Window Peer transport is bounded retained communication, not a durable task queue. Retention pruning may eventually remove old Peer messages or discovery edges, so ACK-required Peer re-projection lasts only while the message remains retained; durable work ownership and completion continue to use explicit Workflow Session or Agent Task primitives.
Workflow Session targeting is explicit in 0.4. Canonical external work_on_project creates a fresh Workflow Session when session_id is omitted and continues only the exact existing wc_sess_* when it is supplied. The retired start_coding_task wire/API name is not a second continuation path. Ordinary project tools do not infer a Workflow Session from caller identity, window identity, project identity, or prior calls. To record a call in a Workflow Session, pass an explicitly authorized recording_session_id; when a tool has its own Session business input, that explicit id is authorized independently.
Project scope is fail-closed. An explicit project-scoped business Session or recorder must match the canonical resolved request project before business execution or Session mutation. There is no cross-project warning/escape mode. complete_session_message records an answer author only from an explicitly authorized recorder; without one, author Session provenance is absent rather than inferred.
The JSON ledger restores only the current version-2 top-level shape and canonical current Session rows. Pre-current ledger versions are rejected rather than migrated. Within v2, fields explicitly declared optional/default may be absent and restore conservatively. The retired context_revision members are accepted only through explicit read-only compatibility sinks and are never restored into live Session state or re-emitted; other unknown row members still fail that row closed. General ClientWindow support remains available to explicitly designed non-Workflow observations; Workflow Sessions do not use it for selection or authority.
Executable todo completion is assignment-fenced in 0.4. A worker first calls get_session_assignment for the exact coordinator session_id + message_id; one atomic store snapshot returns the open todo, every retained direct reply within the bound, and an opaque Session/todo-bound assignment_fence. Current complete_session_message requests require that exact token as expected_assignment_fence together with the independent caller completion_key. Assignment-local semantic changes stale the fence before mutation; unrelated Session traffic and ACK bookkeeping do not. A stale result has state_changed=false and includes the current assignment plus a fresh durable fence only when that exact current state remains provable. Retention loss or an oversized direct-reply set is non-completable from stale context.
The fence and completion key are different identity domains: the fence proves the semantic assignment snapshot, while the completion key correlates one accepted intent across uncertain retries. ack_session_message_ids and message-observation tokens are separate continuity/cursor domains and cannot substitute for either. Business session_id remains the independently authorized target; recording_session_id remains provenance only and never supplies business guards or execution defaults. Current persisted rows must carry the assignment-history and completion-fence tracking metadata required by the v2 format; restore never invents a missing fence, fingerprint, or tracking state, and a row that cannot prove the current shape is discarded rather than admitted for replay.
Workflow Sessions no longer maintain a model-context checkpoint revision, ACK
baseline, or ToolDefinition checkpoint classification. Finished tool calls use the
same canonical ledger append path regardless of whether their ToolResult is
model-facing. Durable consequence evidence remains in Session events, including
bounded context_result_summary, validation/effect evidence, observed/changed
paths, repository-edit sticky state, and Job evidence. events_observed remains
the cumulative Session event count, including events later removed by retention;
it is not a model-context token or cursor.
Normal continuous work has no context-revision ACK input, automatic recovery
delta, or handoff suggestion on unrelated tool results. When task context is
missing, the model explicitly calls session_handoff_summary with the exact
session_id. No identity, Project, window, transport, or recent-call state can
select a Session implicitly. The authorized business Session supplies its Project
when project is omitted; an explicit Project still passes the normal equality
and authority checks. An outer recorder never changes the recovery target.
The default result contains session_id, project, and handoff_brief. The
shared deterministic brief is hard-bounded at 8 KiB and carries root/latest task
instructions, workspace state, progress/changed paths/recent files, validation,
active/recovering Job attention, open collaboration counts, next actions, and
basis completeness. diagnostic=true explicitly adds detailed ledger and closeout
evidence. include_workspace, include_validation, include_checkpoints, and
limit continue to select bounded observations; omitted/unavailable evidence is
reported truthfully in the brief. There is no implicit handoff or ACK baseline.
Handoff assembly captures an internal Session snapshot fence before gathering
workspace/Job/Session evidence and compares it again afterwards. The fence has
exactly two independent Session mutation dimensions: events_observed for ledger
event mutations and message_observation_revision for collaboration/message
mutations. If either changes, or the Session disappears, the brief reports
basis.complete=false and session_changed_during_snapshot; the caller can
explicitly re-observe before dependent work. This detects Session evidence races
without claiming atomicity across independent Runner workspace or Job reads. No
handoff generation or replacement model-context revision is introduced or
returned.
Collaboration is independent: ack_session_message_ids still proves that the
current request retains specific ACK-required Session/Peer messages.
session_attention suppression/re-projection, message resolution, assignment
fences, completion keys, message-observation tokens, and their durable revision
keep their existing semantics. A handoff neither ACKs nor resolves a message and
grants no authority.
Stateless MCP 2026 tools also accept an explicit bounded context_request wrapper sidecar request. It is independent of collaboration ACKs and handoff recovery and is removed before concrete ToolCall parsing. A static canonical material registry authorizes every requested material before its provider is read: webcodex.workflow is public; project.instructions requires the resolved Project plus project:read; skills.catalog additionally requires the admitted Skill runtime protocol capability; plugins.catalog requires the resolved Project plus both project:read and plugin:inspect; and memory.bootstrap requires the admitted Memory protocol capability plus both project:read and memory:read. Scope or material-capability denial is nonfatal to the main ToolResult and returns a bounded unavailable material without provider content. Unknown material keys are nonfatal and remain open-ended at the MCP schema layer. Sidecar material is projected only after the main tool effect or observation has completed, never grants authority, never retroactively makes requested guidance a precondition of that effect, never records caller-read state, and never infers a Project or model-memory state from a Workflow Session, connection, credential, Mcp-Session-Id, or hidden window identity. A model that has lost Project rules or durable Memory guidance must recover project.instructions and/or memory.bootstrap on an observation call, use memory_read when detailed Memory content is needed, reason over that context, and only then issue a later mutation that must obey it. Legacy MCP and generic REST/GPT Actions/OpenAPI do not expose this sidecar request contract.
Project Memory is a separate durable knowledge plane from Workflow Session continuity. memory_search/memory_read require both project:read and memory:read; memory_set/memory_delete require both project:write and memory:manage, with mutations still passing the independent permission evaluator. Direct shared-key runtime credentials explicitly carry both Memory scopes, while Open Anonymous, ProjectCredential, Project Share, and legacy/default OAuth client scope sets do not gain them from project scopes. A Memory memory_key is logical semantic identity, memory_id identifies the current incarnation, the internal definition_hash identifies canonical model-relevant content, and model-facing revision is a generation-bound state ETag/CAS identity; delete and identical recreate therefore produce a different memory_id and revision. Session events never create or consolidate Memory automatically. ack_session_message_ids is limited to ACK-required collaboration messages and never acknowledges Memory. Memory reads/searches may leave bounded metadata-only consequences in Session history, but Memory bodies, summaries, search results, and memory.bootstrap projections are not copied into durable Session recovery. Re-registering the same runtime Project id to a different authoritative registered root resolves to a distinct internal Memory scope rather than inheriting the old root's Memory.
The Session-local message board has a separate durable monotonic message-observation revision used by observe_session_messages. The public observation token is bounded and opaque; it binds the exact Workflow Session plus durable cursor state without exposing the internal revision as the caller cursor. It is observation state only and grants no authority. Malformed, oversized, wrong-Session, and future-revision tokens fail closed.
A no-token call establishes the current baseline and returns no historical messages. Later token calls return retained messages whose latest observable state changed after that cursor, optionally with one bounded wait. Posts are observable mutations; resolve advances only on a real field/status change; a new complete_session_message observes both the todo resolution and answer creation; exact idempotent replay does not advance. This is current-state delta, not an audit/event log, so multiple changes to one retained message may collapse to its final state.
Retention correctness does not infer continuity from deque length or position. Retained messages keep their latest internal observation revisions and the Session persists a low-watermark for removed observation history, including non-FIFO completion retention holes. history_lost=true tells callers when a cursor predates state that can no longer be reconstructed. With pagination, a token advances only through the last returned change while has_more=true. Observation-token issuance fences the ledger generation containing the cursor revision, so tokens issued by the current implementation remain valid across Server restart when the Session itself restores.
Waiting uses process-local notification only as a wake signal; durable revision state remains the truth. No Session-store or persistence-writer mutex is held across the bounded await, unrelated Session mutation can only cause a spurious recheck, and timeout is a successful unchanged result rather than a tool failure.
Message observation is not a delivery receipt, not model-context retention, not a subscription/stream, and not an Agent Wake. Durable Agent/Conversation/Participant/Delivery/Wake state already exists as a separate Server-owned domain; this Workflow Session cursor neither observes nor mutates it. Presence, typing, Agent Task scheduling/worker-pool behavior, automatic worker spawning, and routing remain separate additive capabilities rather than reinterpretations of this cursor.
Every Workflow Session admitted to the in-memory store carries one canonical creation-time authority-group fingerprint. The ledger keeps only that domain-separated SHA-256 fingerprint under the historical owner_authority_fingerprint field; raw user, shared-key, project-grant, credential, or window identity material is never persisted as Session authority. Project authorization and creation-time Session authority are separate checks: access to a project does not authorize another authority group's Session, and a matching Session fingerprint does not bypass project authorization or project equality.
An existing registered-project-bound Workflow Session may persist a closed set
of strongly typed execution defaults. The wire/type name remains
SessionExecutionContext for compatibility:
SessionExecutionContext {
default_cwd: project-relative path? | remote path?,
default_shell: sh | bash?,
resource: named Runner SSH resource?
}
These fields are execution defaults, not arbitrary model context. They cannot
contain environment variables, credentials, SSH host/configuration, keys,
passwords, SSH state, shell input, connection data, or custom options.
resource is only a safe named resource configured on the Runner that owns the
Session project. It is persisted as a name, never as an SSH transport or
authentication material.
Without resource, default_cwd is validated and normalized as a
project-relative path: absolute paths, URI forms, control characters, and
parent traversal fail without changing Session state. Filesystem existence,
canonicalization, symlink, and allowed-root checks remain in the normal Runner
execution path and fail closed there without retrying from the project root.
With resource, default_cwd is instead a bounded remote path; it is not
checked against the Runner project root, and the remote shell reports an
unenterable cwd explicitly.
Inheritance is intentionally closed and per field:
| Tool | default_cwd |
default_shell |
named resource |
|---|---|---|---|
run_process |
inherited when cwd is omitted |
not applicable | unsupported; fails before process start |
run_script |
inherited when cwd is omitted |
not applicable | unsupported; fails before script start |
run_shell |
inherited when cwd is omitted |
inherited when shell is omitted |
routes this call through the named SSH resource |
run_job |
inherited when cwd is omitted |
inherited when shell is omitted |
routes this Job through the named SSH resource |
open_session_shell |
inherited when cwd is omitted |
inherited when shell is omitted |
opens the persistent shell through the named SSH resource |
Structured Cargo/Go tools (cargo_fmt, cargo_check, cargo_test, go_test)
do not inherit default_cwd or default_shell; a named resource is also
rejected for those tools rather
than silently falling back to the Runner-host project. File, Git, LSP, and
checkpoint tools do not inherit any execution default.
run_shell and run_job remain independent-process tools. When an SSH
resource is selected, run_shell, run_job, and a newly opened
open_session_shell execute through that remote resource. Remote cwd
precedence for one-shot SSH commands is:
per-call cwd
> exact active project-matched Workflow Session default_cwd
> SSH resource default_cwd
> remote login default directory
Each SSH run_shell / run_job command gets an independent remote exec
channel and requires the Runner's ssh_shell capability. Unix may reuse a
Runner-local authenticated ControlMaster transport for the same
Session/resource/generation; Windows starts one direct ssh.exe process for
each one-shot/background execution and creates no mux state. Neither transport
preserves cd, exports, aliases, functions, umask, or shell-process state between
commands. A generation change affects future preparation only: an already-spawned
command is never redirected, replayed, or blindly retried. This
one-shot/background capability is independent of named SSH persistent-shell
support.
Raw shell text has one shared model-authored ceiling of 16,000 UTF-8 bytes for
run_shell, raw run_job, and session_shell_exec. Larger shell program text
belongs in run_script; large literal data belongs in stdin, files, or
artifacts. Control may internally expand an explicit sh/bash command while
POSIX-quoting it for the existing Runner wire, so the internal raw-shell wire
envelope is separately capped at 64 KiB and revalidated by the Runner. That
transport headroom is not an additional model-facing command allowance.
A concrete OS/shell launch envelope may still be narrower (notably Windows
CreateProcess after shell-specific wrapping); such a request remains a
pre-start failure rather than weakening the authored bound or silently changing
execution semantics. Large or quote-dense program text should use run_script.
A missing Session leaves execution unchanged. A mismatched Session fails or, on an explicitly authorized cross-project escape path, executes without inheriting its context.
The cross-project escape is a low-level compatibility/debug control. It remains auditable when explicitly used, but model-facing ToolSpecs and flattened Action arguments do not advertise it. Read-only mismatches may continue with a factual warning and without inheriting Session context; write/shell boundaries that require the escape fail closed on ordinary model paths.
Execution intent (purpose=diagnostic, test, and so on) is evidence metadata, not
proof that a failed process is harmless. A started shell/process failure therefore
remains actionable by default. Model-facing execution and structured-validation tools
may instead declare a bounded result_expectation before execution: the omitted
success default requires ordinary success; failure expects a completed known
business failure; and observe accepts either completed known business result. The
legacy internal expected_failure / expected_failure_kind recorder fields remain a
separate compatibility/testing mechanism and are not exposed as the normal model
contract.
run_process additionally accepts a bounded accepted_exit_codes set for commands
whose exit status is itself a result (for example a boolean Git predicate). Matching
expectations change only ledger expectation/actionability classification. Validation
evidence keeps three facts separate: the real execution/validator outcome, whether the
pre-declared expectation was satisfied, and whether validation itself passed. A matched
negative/observation failure is neutral expected-result evidence: it does not become a
validation pass and cannot resolve an earlier real validation failure with the same
identity. ToolResult, exit code, effect evidence, authorization decision, and execution
state are never rewritten. Pre-start rejection, permission/guard denial, transport failure,
malformed result, timeout/cancellation, and unknown/lost outcomes remain fail-closed.
A Job admission is not a terminal match: structured validation Jobs inherit the
pre-declared expectation and classify it only when terminal evidence is materialized.
If the originating expectation evidence is no longer retained, terminal projection
falls back to the normal success-required behavior rather than guessing.
The canonical runtime has a separate, command-oriented PersistentShell model:
open_session_shell
session_shell_exec
session_shell_status
close_session_shell
Opening creates one real long-lived Runner-owned shell process, at most one
active shell per Workflow Session: sh/bash on Unix, or the configured
PowerShell program/profile on Windows. For an agent:<client>:<project> the Runner owns
and controls the shell. Without execution_context.resource, it runs against
the registered project host. With a named resource, the Runner opens a remote
persistent shell through that SSH resource; this requires persistent_shell +
ssh_persistent_shell, not the separate one-shot/background ssh_shell
capability, and never silently falls back to the registered Project host. Unix may reuse its OpenSSH mux;
Windows owns one direct long-lived ssh.exe channel while the remote shell remains
sh/bash. No PTY/ConPTY or terminal-control protocol is implied. Registered
Project execution is Runner-owned; the Server has no Project-local process or
persistent-shell fallback. read_only Sessions cannot open or execute a persistent
shell. The pre-0.4 inspect Session mode is retired.
Runner-project open resolution is explicit cwd/shell, then the exact Session's
default_cwd/default_shell, then the project/Runner defaults. The explicit
sh/bash override is Unix-only; Windows callers omit it and the Runner uses
the configured PowerShell program/profile, failing closed for incompatible
configuration. For an SSH persistent shell, cwd precedence is explicit open
cwd, Session default_cwd,
the named resource's default cwd, then the remote login default; the selected
Session default_shell is also inherited when shell is omitted. Profile
environment and initialization run once at open. Later commands retain the
same process's cwd, exports, unset state, umask, functions, and ordinary shell
variables. The shell record retains the execution location selected at open, so
updating Session execution defaults never redirects, moves, restarts, or
changes an already-open shell; close and reopen it to apply new defaults.
run_shell and run_job never reuse this process.
The random shell_id is bound to the exact Session, runtime project, executor,
Runner client when applicable, dialect/profile, initial cwd, and timestamps.
Every operation rechecks caller authorization and active Session/project
identity; before exec/status the Runner also rechecks its current project,
raw-shell, cwd, allowed-root, profile, and shell policy. Close remains available
for cleanup after an execution-policy change. A closed id remains terminal and
cannot address a subsequently opened shell. Explicit close is idempotent.
Session close, project disable/unregister, idle expiry, shell exit, Runner
disconnect/shutdown, and detected process/control-channel damage release the
process group and its pipes.
Commands are serialized; a concurrent command receives shell_busy. Output is
bounded independently for stdout and stderr. Completion uses transport-private
control framing with a high-entropy per-command token, never an ordinary output
marker: Unix uses inherited control descriptors; Windows uses a private control
file plus exact stdout/stderr drain boundaries. On timeout the owner performs
only the platform's safe bounded recovery attempt. If framing synchronization
cannot be proved, it terminates the owned process tree, marks the shell
poisoned/lost, returns shell_reset_required, and never writes another command
to that process.
Persistent shells are process-local and are not durable Job records. Neither a
Server nor Runner restart claims to recover or reattach one from ledger data.
There is no PTY, raw keystroke/input stream, terminal resize, WebSocket terminal
UI, or full-screen terminal application support.
The Session ledger stores bounded lifecycle and permission evidence
(shell_id, action, shell/execution state, error code, and completion flags),
never command text, stdout/stderr, the complete environment, internal shell
state, credentials, or unbounded command output.
The context is an additive serde-defaulted ledger-version-1 field, so older
ledgers load it as {} without a version bump. Startup and Session summaries
return the complete current context. Context changes record bounded structured
metadata only; no command, environment, token, or secret content is added to
the audit ledger.
work_on_project is the canonical external full-runtime start-or-continue
aggregate. The former start_coding_task tool name and advanced direct/API
schema are retired and fail closed before dispatch. No internal ToolCall
variant remains for that retired name; work_on_project calls the shared coding
workflow engine directly, with diagnostic projection controls available only to
tests rather than as a Session-selection or compatibility surface.
work_on_project also owns the optional managed-worktree bootstrap without
creating a new authority or Session concept. On a fresh client_id + path call
with mode=worktree, path is a source checkout: the selected Runner validates
the source against its filesystem policy, resolves base_ref (or the source
HEAD) to an exact commit, chooses and creates an isolated detached worktree,
registers that worktree as an ordinary runtime Project, and only then creates the
Workflow Session on that final Project. The Server never constructs a Runner-host
worktree path or interprets the Git ref. work_on_project hides Project
registration and managed-worktree bootstrap from the ordinary model workflow;
Project authority itself is not removed.
An explicit session_id in worktree mode resumes only its already-authorized
final managed Project. The Runner re-observes that registered worktree and its
source provenance instead of creating another worktree; a provided base_ref
must still resolve to the stored exact base commit. recording_session_id,
ClientWindow, ACK metadata, source-path knowledge, and managed operation ids do
not select or authorize the Project. Finishing or closing the Workflow Session
does not remove the managed worktree or unregister its Project; later review,
commit, push, PR, handoff, and investigation remain possible until a future
explicit lifecycle operation says otherwise.
work_on_project deliberately does not use Workflow Session identity, transport
identity, a client-window key, credentials, project identity, or Server lifetime
as evidence that the current model still retains static bootstrap content. The
same wc_sess_* may be explicitly resumed by multiple independent ChatGPT
conversations. Its include_workflow_guidance, include_project_instructions, and
include_extension_catalog flags are caller-explicit model-facing projection
preferences only: their defaults are true, and false is appropriate only when
the caller's current model context already retains the corresponding content.
guidance_profile is a separate request-local presentation enum: direct by
default, or code_mode only in Experimental Code Mode builds. It selects only
workflow.tool_strategy in workflow contract v14. Exact resume may choose either
without a Session transition; omission always selects direct, never a remembered
choice. It is not persisted in Session state or event arguments and changes no
admission, authority, effects, validation or Job semantics. An unavailable profile
fails parsing even with include_workflow_guidance=false; that flag still omits
the whole workflow. The independent webcodex.workflow context sidecar continues
to project the explicit default, with no Session inference.
Repository instruction files are still re-observed and Session metadata/delta
status still update when instruction bodies are suppressed. The default bounded
extension catalog contains selection metadata only: Skills are drawn from the
same canonical union as skill_list (project .agents/skills, Runner-configured
live skills.roots, and active Runner-managed Skill Store packages), while
Plugins are limited to ready committed providers whose configured cwd matches
the authoritative Project root. Skill bodies, Plugin schemas, bindings, native
paths, commands, environments, and provider identities are not projected; using
a selected Plugin still requires plugin_tool describe before call.
Startup selection is strict and ordered:
The durable projection stores only:
The canonical hash input is the principal kind/id, transport, already-hashed
stable window identity, resolved project, and already-hashed canonical
repository root. It uses fixed field order and length prefixes under
webcodex.workflow-current-binding.v1. Raw MCP session ids, hosted
conversation ids, cookies, credentials, authorization headers, and repository
paths never enter this projection, and neither binding hashes nor component
hashes are returned to the model.
The binding field is an additive, serde-defaulted field in ledger version 1, so
older ledgers load it as empty without migration and keep their existing
Session events/messages. Restore accepts only bounded, lowercase SHA-256 keys
that reference known active wc_sess_* records. Malformed, duplicate,
conflicting, missing, closed, project-mismatched-on-lookup, and excess entries
are discarded without rejecting valid Session data. Internal status exposes
only bounded counts (durable_binding_count, restored_binding_count,
discarded_binding_count) and never a binding key.
This durable binding remains Workflow Session state and is separate from ActionAudit/window observations. It does not create a second coding-task lifecycle, and window correlation never infers or mutates a Workflow Session.
This is not the same state machine as Action Audit Sessions. Lifecycle
tools and error kinds (unknown_session_id, session_closed, mode denials,
guard failures) apply only to Workflow Sessions.
continuation_feedback is a deterministic, read-only projection surfaced
by finish_coding_task, session_handoff_summary, and (as validation_delta)
validation_summary. The internal coding-startup implementation also consumes
this projection while building canonical work_on_project startup state. It is derived only from existing
persistent state — the Workflow Session ledger, validation evidence, bounded
Job metadata, and the session message board — and it is never a substitute for
a finish_coding_task verdict.
- Read-only projection contract: building it directly never executes shell,
reads project files, enqueues Agent/Runner requests, mutates the ledger,
refreshes activity, consumes or auto-resolves guidance, or calls an LLM.
Canonical coding startup still appends its legitimate new
task_instruction. Public MCP, REST, or runtime dispatch also records the enclosing tool's uniformtool_call_started/tool_call_finishedtelemetry, soevents_total,updated_at, and activity telemetry are not required to stay unchanged across a public call. Those recorder facts are separate from the projection's business semantics. - Startup describes the previous attempt: for reused, explicitly resumed,
and restored-after-restart sessions, canonical
work_on_projectstartup snapshots the pre-instruction state before appending the new instruction, socontinuation_feedback.attemptdescribes the previous attempt's bounded, redacted instruction excerpt, activity, changes, current unresolved failure identities, and validation — not the empty new attempt. When an unresolved identity is available, the first suggested action names that concrete target. A fresh session reportsstatus = not_applicable,reason_code = fresh_session. - Attempt boundary: the attempt window is segmented by the most recent
task_instructionretained in the ledger window. When that instruction has been evicted by the bounded event limit, the boundary is reported assource = unavailable,reason_code = attempt_boundary_evicted, andevent_range.complete = false— the projection never masquerades a truncated retained window assession_startwithcomplete = true. - Exploration workset:
attempt.explorationprojects only successful, structured evidence from focusedread_files,search_project_texts, and typed LSP navigation calls. The existing ledger retains only a bounded set of validated project-relative paths; it never retains search patterns or previews, file contents, symbol/hover/diagnostic bodies, arbitrary result JSON, shell commands/output, or the absolute repository root for this workset. Paths are deduplicated newest successful observation first. Enumeration tools such asproject_overview,list_project_files, andlist_project_tracked_files, Git diff lists, failed calls, error text, and shell output are not exploration evidence. The workset is segmented by the same attempt boundary; when that boundary was evicted,exploration.complete = falseas well. - Continuation reuse, not execution: automatic continuation, explicit
resume, read-only to normal mode upgrades, and ledger restoration reuse the
prior attempt's workset. Startup returns at most 3 paths in
minimaland 12 instandard(including the core embedded byfull); full continuation feedback returns at most 100 with the real total and truncation state. This is a hint for model judgment only: startup never reads, searches, or navigates those paths automatically. - Handoff is independent of the display limit:
session_handoff_summarybuilds its display list from the caller-suppliedlimit, butcontinuation_feedbackreads an independent bounded evidence snapshot (the maximum retained event window), so a small display limit cannot shrink the attempt boundary.include_validation = falsedoes not fabricate validation; it reportsvalidation_not_requestedrather thannot_run. - Validation delta comparability:
validation_deltaisavailableonly when the latest and prior runs are proven comparable — same validation kind/tool/cwd and structured scope (package, filter, features, targets, purpose), with complete evidence on both sides and a consistent parser identity. Otherwise it reports a stable reason code (no_previous_validation,validation_scope_changed,previous_evidence_incomplete,current_evidence_incomplete,parser_changed,parser_identity_unavailable,test_identity_unavailable,insufficient_scope_identity,validation_not_requested). Count deltas are signed integers (a decrease in passed tests yields a negativepassed_delta); zero-test success never resolves a prior test failure. - Async terminal validation evidence: structured validation Job metadata carries the same opaque
validation_target_idas the originating validation attempt. When an authorized validation-summary or Runtime Console Session read observes a retained terminal Job, WebCodex idempotently materializes one boundedvalidation_job_terminalevent in that exact Workflow Session before projecting validation state. Idempotence does not depend on that event remaining in the 200-event Session FIFO: the version-1 ledger also persists a serde-defaulted exact Job-id marker set bounded to the Runner authoritative terminal inventory limit (64), and a new materialization evicts only markers absent from the current terminal-candidate snapshot. The marker check, marker insertion, and event append commit under one Session-store mutation, so concurrent reconcilers append at most once and restart restoration keeps the same suppression identity. Terminal reconciliation also serializes authoritative candidate-snapshot acquisition through marker/event materialization within one runtime: a later snapshot cannot commit first, so an older snapshot never gains eviction authority over a marker established from newer inventory. Synthetic evidence uses the authoritative Jobfinished_at; reconciliation never advances Session activity to the wall-clock read time. This is recovery/materialization only: it never re-runs validation, never treats acceptance/handoff as terminal success, and never exposes raw Job output. A later terminal success for the same structured target can therefore resolve an older retained failure even after the acceptance event is gone; the materialized terminal evidence then follows normal Session persistence/retention across Server restart. - Cargo test-count postconditions: a
cargo_testcaller may require a bounded minimum count withrequire_tests/min_tests. These are request-scoped evidence assertions: they are persisted with local and Runner Job metadata so the same invocation is evaluated consistently after handoff or restart, but they are deliberately excluded from the structured validation target identity and do not become durable Workflow Session requirements. If the validator exits successfully while the requested count is below the minimum, or bounded output cannot prove the count, the immutable raw ToolCall failure remains in history while validation projects the execution separately as an evidence gap rather than a code/test correctness failure. A later sufficient validation of the same execution target therefore need not inherit the earlier assertion threshold. Omitted assertions preserve zero-test execution compatibility. - Opaque scope identity:
comparison.scope_identityis a domain-separated, opaque stable identity (validation_scope:v1:<sha256>) over the normalized structured scope. It never returns a raw command, absolute path, or test filter — command text is not re-exposed through another field. - Jobs report only proven status: the
attempt.jobsblock reports counts computed over the full bounded active Job aggregate, never the truncatedrecentlist, so a hidden recovering job is never misreported as healthy. Fields that cannot be reliably proven are not reported. - No new persistence model: continuation feedback introduces no new durable table and no second attempt state machine. Exploration adds only a serde-defaulted field to the existing version-1 event ledger, so older ledgers restore it as empty without a version bump; feedback remains a projection over that existing state.
session_handoff_summary and finish_coding_task return the same version-1
handoff_brief, built by one shared pure projection. It is the compact,
model-friendly view for a new window, a new Agent, or a human receiver;
continuation_feedback remains the more detailed evidence available from the
explicit diagnostic handoff view and closeout tools. The
brief is not Session replay, does not reconstruct chat or hidden model
context, and does not decide that implementation work is complete.
The builder consumes only the bounded Session summary, continuation feedback,
workspace, validation, Job, guidance, exploration, and suggested-action
snapshots that its caller already obtained. It performs no shell, Git, file,
search, LSP, Agent, or Runner request; does not refresh activity, consume
guidance, append a ledger event, or call an LLM; and stores no new Session
data. work_on_project intentionally does not return handoff_brief, so the
standard startup core's worst-case size does not grow.
A direct internal session_handoff_summary(...) call does not add business
events beyond those snapshots. Calls through MCP, REST, or runtime dispatch
remain subject to the uniform recorder and normally append exactly
tool_call_started and tool_call_finished. This telemetry is not guidance
consumption or a handoff-builder mutation, and session_handoff_summary must
not receive a recorder bypass.
The projection has these stable bounds and semantics:
- root and latest task instruction excerpts reuse the existing Session
credential redaction (including token, Bearer, and client-secret styles) and
are capped at 600 Unicode characters. Unix,
Windows-drive, UNC, and
file://locations; shell commands and parameters; fenced and inline code; and ordinary task prose remain available as useful handoff context. The latest excerpt is the latest retainedtask_instruction; when it equals the root, the same excerpt is returned. It is null when no such retained event exists. Itstruncatedflag is true only when credential redaction, the 600-character bound, or final byte-budget reduction changed the returned excerpt. - changed paths reuse continuation changes (12 maximum); recent files reuse
the attempt exploration workset (8 maximum, deduplicated newest-first);
unresolved failure identities are capped at 5; deterministic next actions
are capped at 5. Each bounded evidence list preserves
total/returned/truncated. Recent files are only continuity hints, not complete history. progress.stateis selected in order: a non-mutable lifecycle isclosed; a workspace conflict, blocking/recovering Job, unresolved validation failure, or open risk isblocked; workspace changes without a proven latest validation pass areneeds_validation; missing critical evidence isinsufficient_evidence; otherwise an active Session isready_to_continue. A dirty worktree alone is not a blocker, questions and todos are counts rather than blockers, and terminal-pending Jobs are nonblocking.- workspace is
available,not_requested, orunavailable.include_workspace=falsenever causes an implicit Git query. Validation ispassed,failed,not_run,not_requested, orunavailable;include_validation=falsenever masquerades asnot_run. basis.completeis false whenever a sorted fixedreason_codesentry identifies omitted or unavailable evidence, including an evicted attempt boundary. Internal error text is never a reason code.
The complete object is checked against its actual serialized JSON size and hard-capped at 8192 bytes. Stable reduction removes recent files, changed paths, failure identities, next actions, and then instruction excerpt characters while retaining lifecycle/mode, progress and validation status, attention counts, basis, and deterministic/LLM flags.
A new window can start a new Session normally and then explicitly read the old
Session with session_handoff_summary(session_id=...). Explicit
resume_session_id remains available when the caller truly intends to resume
the same active Session and continues to obey the existing identity,
lifecycle, project, guard, and binding rules.
For deliberate coordinator/worker delegation across separate windows, keep the Sessions independent and use the existing handoff plus message-board primitives; see Manual Multi-Window Collaboration.
HTTP Action call auditing and operator-facing grouping of external API requests. It answers “what HTTP/API actions happened in this audit window?” — not “what is the coding task ledger for this repo work?”.
- Group HTTP Action / REST audit events under one audit session
- Persist action audit records (endpoints, status, durations, redacted summaries)
- Snapshot non-secret caller attribution at event write time: canonical credential kind, optional stable user id, and OAuth-only client id
- Idle open-session reuse and explicit close for operator audit views
- Aggregate stats for read-only audit APIs, with explicit bounded-scan coverage
Model-visible runtime tool calls reuse the existing Action Audit event as the
durable/queryable sink for low-cardinality ergonomics telemetry. No second
telemetry table or recorder is created. The shared ToolRuntime kernel owns the
normal timer for a registered model-visible tool; the transport that already owns
the outer Action Audit row then finalizes one summary.model_ergonomics object
from the final model-facing ToolResult projection. A transport may use a bounded
fallback timer only after a runtime tool identity is established when MCP-only
validation rejects the call before kernel entry or the MCP hard dispatch timeout
prevents kernel completion. Batch items do not create generic invocation records,
and hidden/internal helpers do not start this telemetry.
The current durable generic record uses schema_version = 6. Older telemetry rows
remain naturally queryable and are not migrated or backfilled. The current
record contains:
- base generic fields:
tool_name,tool_category,success,duration_ms, nullableserialized_result_bytes, nullable structurederror_kind,failure_kind,recovery_kind, and authoritative closedexecution_state; - optional
finish_summary_onlyand boundedwork_on_projectrequest-shape facts; - optional edit measurement fields:
edit_surface,edit_outcome, andedit_conflict_kind.
Context-ACK presence/status and recovery-delta byte/event metrics are retired. Generic final-result byte size and latency remain available without retaining revision values or recovery bodies.
edit_surface is the closed label canonical | advanced. edit_outcome is a
bounded structured classification derived from authoritative edit result fields.
edit_conflict_kind is limited to the closed allowlist multiple_matches,
match_not_found, occurrence_out_of_range, overlapping_edits, and
sha256_mismatch; unknown values are omitted and arbitrary error prose is never
promoted into the durable record.
serialized_result_bytes is the UTF-8 byte length of the exact final
model-facing ToolResult JSON object (success, output, and error only when
present), serialized with the normal serde JSON representation. It is not a
character count, Rust memory size, stdout/stderr estimate, database-row size, or
HTTP/JSON-RPC/MCP framing size. MCP finalizes the count from the final
structuredContent ToolResult after MCP-only image/resource framing, so bytes
removed from the model-facing ToolResult are not charged. If a registered
model-visible tool identity has already been established but a deterministic
pre-result rejection occurs (for example invalid arguments, insufficient scope,
or MCP wrapper validation), the invocation is still counted with
serialized_result_bytes = null; canonical edit tools classify those definite
rejections as edit_outcome = rejected.
The MCP outer hard dispatch timeout is different: dispatch may already have
started and the terminal edit result is unavailable. It is recorded with
error_kind = dispatch_hard_timeout, and a canonical edit remains
edit_outcome = uncertain rather than being interpreted as a definite rejection.
No retry authority, state-change claim, or rollback claim is derived from that
measurement. There is currently no authoritative generic final-result truncation
fact, so generic result_truncated is deliberately not recorded;
tool-specific truncation fields keep their existing meanings.
Classification consumes structured ToolResult fields only. It never derives a
kind from arbitrary English error prose. The generic record stores no tool
arguments, commands/argv/scripts/stdin/stdout/stderr, ToolResult body or error
prose, paths/cwd/project ids, query/file/clipboard/Computer contents, Session
message/prompt/answer bodies, credentials, native identities, or arbitrary user
text. Existing Action Audit correlation/attribution fields remain separate
pre-existing audit data; P1a does not copy them into model_ergonomics.
edit_tool_telemetry remains the edit-specific structured tracing enrichment.
An edit invocation therefore has one generic Action Audit invocation record plus
its existing edit-specific enrichment, not two generic counts. Workflow Session
tool_call_started / tool_call_finished events remain a separate workflow
ledger and are not the persistence source for this aggregate ergonomics data.
Telemetry is observation-only and failure-isolated. Failure to serialize the bounded generic projection or to persist the Action Audit row is dropped/warned without changing the tool's success, output, error, permission, execution state, retry safety, Job lifecycle, or Computer authority.
The real MCP protocol method tools/list has a separate durable measurement;
it is not stored under summary.model_ergonomics and must not be confused with
the runtime list_tools tool. Each executed JSON-RPC request with an id
creates one metadata-only ActionAudit row:
endpoint = /mcp
action_name = toolsList
operation = mcp_tools_list
A JSON-RPC notification has no executed ActionAudit row. On success the durable summary shape is:
{
"transport": "mcp",
"tool_surface": {
"schema_version": 1,
"protocol_era": "legacy | stateless_2026",
"compact_schemas": false,
"tool_count": 0,
"serialized_tools_bytes": 0,
"serialized_result_bytes": 0,
"gateway_tool_included": false
}
}tool_count comes from the final caller-visible tools array.
serialized_tools_bytes is the exact JSON UTF-8 byte length of final
result["tools"]; serialized_result_bytes is the exact JSON UTF-8 byte length
of the final MCP result object, after stateless result metadata has been added
when applicable but excluding the JSON-RPC envelope. gateway_tool_included
reflects the actual final response rather than theoretical authorization. The
durable summary never stores the tool-schema body, tool-name array,
descriptions, arguments, or scopes. ActionAudit persistence failure remains
non-blocking and cannot change the MCP protocol result.
For dogfood analysis, read bounded Action Audit events through
/api/audit/session or query SQLite action_events.summary_json, select rows
with summary.model_ergonomics, and aggregate by tool_name. The raw bounded
records are sufficient for invocation/success counts, duration p50/p95,
serialized-result mean/high percentiles, and structured error/recovery-kind
distributions in later SQL/Python analysis; no dashboard or analytics service is
part of this contract.
| Aspect | Contract |
|---|---|
| ID form | UUID string (or client-supplied id via headers/query), not wc_sess_* |
| Request affinity | Headers x-action-session-id / x-webcodex-session-id, or query action_session_id |
| Default creation | Server may create a new UUID when no open recent session is reused |
| Durable caller attribution | principal_kind, optional principal_user_id, OAuth-only oauth_client_id; legacy rows remain NULL and are never inferred from target project or session |
| Stats exposure | /api/audit/stats aggregates credential kinds and OAuth client usage; ordinary /api/audit/session event views do not expose principal/user/client attribution fields |
| Aspect | Contract |
|---|---|
| Internal module | action_audit_sessions (crate-private; formerly the module path action_sessions) |
| HTTP handlers | audit_http under /api/audit/* |
| Persistence | SQLite tables action_sessions and action_events |
| Related types | ActionSessionRecord, ActionEventRecord, DB helpers in db/audit.rs |
- An audited HTTP request arrives; optional explicit audit session id is read from headers/query.
get_or_create_active_sessionattaches the event to an existing open session (explicit id, or recent idle-open session) or creates a new one.- Events are written to SQLite; session aggregate counters update. Caller attribution is snapshotted from the authenticated request context before the write and never reconstructed later from execution targets.
- Operator APIs list sessions, fetch one session with events, or compute stats. Stats report scanned/available event coverage and fail on database read errors instead of silently presenting a partial aggregate as complete.
- Sessions may be closed (
status = closed); idle open sessions time out for reuse purposes (ACTION_SESSION_IDLE_TIMEOUT_SECS).
This lifecycle is orthogonal to Workflow Session start/finish tools.
- Not a coding / workflow session
- Not a substitute for canonical coding-startup /
work_on_projectevidence - Not an input to
session_summary, message board, orfinish_coding_task - Not automatically correlated to any
wc_sess_*
The two systems:
- Use different ID namespaces
- Use different storage backends
- Expose different APIs (runtime tools / MCP vs
/api/audit/*) - Define different open/close and failure semantics
There is no shared session state machine, no shared store, and no
requirement that a request participate in both. A single HTTP call may
incidentally touch both only when a tool invocation both (a) records workflow
ledger evidence via session_id / recording_session_id and (b) is wrapped by
HTTP action audit middleware — those are still two separate writes.
Do not:
- Fold Action Audit Sessions into
tool_runtime::sessions - Store workflow ledger events in SQLite
action_*tables - Reuse
wc_sess_*as SQLiteaction_sessions.session_idby convention - Drive workflow guards from audit session status, or audit close from
finish_coding_task - “Simplify” by making one ID type serve both products
Merge would couple coding-task continuity to HTTP transport audit, break identity rules, and blur security/guard boundaries. Keep two implementations.
The standing optional-correlation contract is:
Until that design is implemented, code must treat the systems as unlinked.
| Forbidden | Why |
|---|---|
Infer wc_sess_* from current HTTP Action Audit Session |
Wrong namespace; audit ids are not workflow ids |
| Fall back to Action Audit Session when Workflow Session is missing | Breaks unknown_session_id and explicit-wins |
Treat /api/audit/session payload as coding-task summary |
Different evidence model and redaction rules |
Pass audit UUID as tool session_id expecting ledger semantics |
Unknown or wrong session; not a supported bridge |
The following names are part of storage, HTTP, or external API contracts. Internal Rust module renames for clarity are allowed; these surfaces are not renamed without an explicit compatibility migration:
- Table:
action_sessions - Table:
action_events - Index:
idx_action_sessions_status_last_event - Column names and migration history in
db/schema.rs/db/audit.rs
POST /api/audit/sessionsPOST /api/audit/sessionPOST /api/audit/stats- Request affinity:
x-action-session-id,x-webcodex-session-id, queryaction_session_id
- Audit session records (
session_id,status, counters, timestamps, …) - Audit event views (without principal attribution fields) and stats aggregates, including coverage plus credential-kind/OAuth-client usage summaries
- Workflow tool fields:
session_id,recording_session_id, session mode valuesnormal/read_only. A pre-0.4 persisted v2 row containingmode = "inspect"is malformed under the current enum and is discarded row-closed without reinterpreting it asnormal. - Error kinds such as
unknown_session_id
- GPT Action OpenAPI operation ids and schemas that mention workflow
session_id/recording_session_id - MCP tool input schemas for session tools
- Runtime tool names (
start_session,work_on_project,session_summary, …); retired wire names are not part of this external surface
| Layer | Current clarity practice |
|---|---|
| Docs / design | Prefer Workflow Session and Action Audit Session |
| Rust module path | tool_runtime::sessions vs action_audit_sessions |
| SQLite / HTTP / JSON | Keep existing action_sessions / session_id names for compatibility |
Renaming a crate-private module path does not change wire contracts. Renaming tables, routes, or serialized field names does.
AGENTS.md— executable Session invariantsarchitecture-decisions.md— dual-model summaryopenapi-guidelines.md—session_idvsrecording_session_idon GPT Actions../ARCHITECTURE.md— module map and Workflow Session overview