Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ The split below is by question type, not by human-versus-agent audience.
behavior -> `docs/spec/`
- Need current Decodex/Codex app-server compatibility range or protocol support
evidence -> `docs/spec/app-server.md`
- Need Decodex operator lane-control capability support, including inspect,
pause/resume, scan, interrupt, steer, retained retry/resume, manual attention, or
unsupported/deferred controls -> `docs/spec/lane-control.md`
- Need public static-site contracts, GitHub bundle schemas, signal-entry schemas, or
release-delta schemas -> `docs/spec/`
- Need runbooks, migrations, validation steps, troubleshooting, or operational
Expand Down
31 changes: 22 additions & 9 deletions docs/reference/operator-control-plane.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,18 @@ The browser dashboard reads the complete published state from the local
published snapshots, active-lane activity updates, and local dashboard control
acknowledgements. `GET /api/operator-snapshot` is the Decodex App read API over the
same runtime database, not a browser-dashboard polling authority and not a sign that
the dev listener owns scheduling. The current browser UI keeps live updates
unscoped and exposes explicit stop controls for active lanes with a known live child
process plus account-pool selection controls; project watch, project pause/resume,
and manual retry controls are intentionally not shown. `runActivity.activeRunsComplete`
the dev listener owns scheduling.

For the lane-control rollout, active-lane UI posture is observe-only. The dashboard
renders active-lane state, protocol activity, liveness, private-evidence references,
and local acknowledgement/account controls, but it is not the supported place to author
steer, retry, task replacement, or lifecycle mutations. CLI/API is the first
operator-control surface for lane control, governed by
[`../spec/lane-control.md`](../spec/lane-control.md). Existing low-level WebSocket
control handlers, including the hard stop fallback, are not the broad lane-control
contract and must not be expanded into dashboard steer/retry/task controls in this
rollout. Project watch, project pause/resume buttons, manual retry controls, and active
lane steer controls are intentionally not shown. `runActivity.activeRunsComplete`
marks whether a payload is the complete active-run list; subscription-filtered
payloads set it to `false`, so consumers must not treat a missing run in that payload
as ended.
Expand All @@ -162,11 +170,13 @@ operator action, snapshots may also include `warning_details` entries with the
affected `project_id`, `repo_root`, reason, and next action; for example, a stale
registered project whose repo path is no longer a Git checkout can explain the bad
project instead of only surfacing `worktree_hygiene_unavailable`.
The stop control signals the recorded child process for that run, marks the local
attempt interrupted, and releases the local queue lease. `ack` is dashboard-local
acknowledgement only. The socket is not a browser connection to Codex app-server,
GitHub, or Linear, and it does not make high-frequency protocol activity durable
outside the local operator surface.
The existing hard stop fallback, where available, signals the recorded child process
for that run, marks the local attempt interrupted, and releases the local queue lease.
It is an emergency fallback, not the preferred lane-control path. Soft interruption
through CLI/API `turn/interrupt` should become the preferred active-turn control once
implemented. `ack` is dashboard-local acknowledgement only. The socket is not a browser
connection to Codex app-server, GitHub, or Linear, and it does not make high-frequency
protocol activity durable outside the local operator surface.

| Section | Meaning |
| --- | --- |
Expand Down Expand Up @@ -380,6 +390,7 @@ rate-limited, or unavailable.

These directions were discussed but are not part of the current implemented contract:

- Active-lane UI controls for steer, retry, task replacement, or lifecycle mutation.
- Conflict-domain scheduling for `ui-preview`, `docs`, `tests`, `runtime`, or similar
lane classes.
- Demo batch planning that automatically selects two or three small visible issues and
Expand All @@ -388,13 +399,15 @@ These directions were discussed but are not part of the current implemented cont
- Inferring registered projects by scanning `.codex` history or repository-local config
files.
- Treating Linear comments as the real-time runtime backend.
- Exposing raw `thread/inject_items` as an operator lane-control feature.

If any of these become implementation work, promote the chosen behavior into the
governing spec first, then update the operator runbook and this reference.

## Authority Links

- Runtime contract: [`../spec/runtime.md`](../spec/runtime.md)
- Lane-control capability contract: [`../spec/lane-control.md`](../spec/lane-control.md)
- Linear execution ledger schema: [`../spec/linear-execution-ledger.md`](../spec/linear-execution-ledger.md)
- Pilot procedure: [`../runbook/self-dogfood-pilot.md`](../runbook/self-dogfood-pilot.md)
- Workspace layout: [`./workspace-layout.md`](./workspace-layout.md)
68 changes: 68 additions & 0 deletions docs/spec/app-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,18 @@ thread token-usage updates. This summary is published through the operator statu
snapshot and dashboard only; high-frequency protocol details remain out of Linear
unless an existing lifecycle event summarizes them.

Lane-control protocol methods are an additive operator-control extension, not part of
the current normal dispatch preflight. Decodex's intended lane-control use is:

- `turn/interrupt` for soft active-turn interruption when the active turn id is known.
- `turn/steer` for broad operator-supplied steering text.
- no operator-facing `thread/inject_items` feature in this rollout.

If generated schema or live capability probing shows that `turn/interrupt` or
`turn/steer` is unavailable, the CLI/API control must report that control as
unsupported for the active lane instead of failing ordinary issue dispatch. The
lane-control contract and support matrix live in [`lane-control.md`](./lane-control.md).

## Required request flow

1. Start the child process.
Expand Down Expand Up @@ -295,6 +307,62 @@ Decodex must not inject project-owned config, model, personality, service-tier,

Within one bounded Decodex run attempt, the runtime may start multiple turns on the same thread. Thread-level settings remain stable from `thread/start`; continuation policy such as `execution.max_turns` and between-turn tracker revalidation stays in Decodex, not in the app-server protocol.

## `turn/interrupt`

Method:

- `turn/interrupt`

Decodex's intended use is soft active-turn interruption from a CLI/API operator
control. It should target the current known thread and turn, request a graceful turn
stop through app-server, and leave outcome classification to the Decodex runtime.

`turn/interrupt` must not:

- mutate tracker state directly
- imply manual attention or review handoff by itself
- clear leases without runtime classification
- replace the hard-interrupt process fallback when app-server is unreachable

When implemented, Decodex should prefer `turn/interrupt` before signaling the child
process. A hard interrupt remains only a fallback after soft interrupt is unavailable,
times out, or cannot be routed to the live app-server session.

## `turn/steer`

Method:

- `turn/steer`

Decodex's intended use is operator-supplied steer text for an active lane through the
CLI/API control surface. The bottom-layer app-server/protocol/runtime shape must not
hard-limit task content categories. It should carry the operator's instruction broadly
and leave policy constraints to Decodex audit, privacy, workflow, recovery, and
agent-skill layers.

`turn/steer` must not be treated as:

- a tracker mutation
- a hidden task replacement
- a bypass around review, validation, or terminal finalization
- a way for an agent to self-author new scope without an operator request

If a requested steer materially replaces the issue objective or acceptance contract,
Decodex must route that as explicit lifecycle/requeue work instead of silently steering
the active lane into a different task.

## `thread/inject_items`

Method:

- `thread/inject_items`

Raw item injection is deferred as an operator feature. Decodex should not expose
`thread/inject_items` through lane-control CLI/API in this rollout because raw item
insertion has broader transcript-shaping semantics than the intended operator steer
contract. Use `turn/steer` for active-lane steering once the CLI/API implementation is
available.

## `command/exec`

Method:
Expand Down
3 changes: 3 additions & 0 deletions docs/spec/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ Then keep the body explicit:
- [`app-server.md`](./app-server.md) defines the direct Codex `app-server` interaction
contract, current compatibility range, and protocol support evidence used by the
runtime.
- [`lane-control.md`](./lane-control.md) defines the CLI/API-first operator
lane-control capability matrix and the boundary between bottom-layer steer support
and higher-level policy guardrails.
- [`github-change-bundle.md`](./github-change-bundle.md) defines the normalized GitHub
input model for PR-first public signal analysis.
- [`signal-entry.md`](./signal-entry.md) defines the published signal-entry schema used
Expand Down
160 changes: 160 additions & 0 deletions docs/spec/lane-control.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# Lane-Control Specification

Purpose: Define Decodex operator lane-control capabilities and the boundary between
bottom-layer protocol support and higher-level policy guardrails.
Status: normative
Read this when: You are implementing, validating, or using CLI/API controls for active
or retained Decodex lanes.
Not this document: The full runtime state machine, the low-level app-server method
schema, dashboard layout, or tracker-tool payload schema.
Defines: The lane-control capability matrix, supported and deferred controls, audit
requirements, and policy boundary for inspect, pause/resume, scan, interrupt, steer,
retained retry/resume, and manual-attention controls.

## Scope

Lane control is the operator-facing ability to inspect and influence a Decodex-owned
lane without bypassing the runtime lease, tracker, retained-worktree, and review
contracts.

The first supported operator-control surface for this rollout is CLI/API. Active-lane
UI controls are intentionally deferred. The dashboard may show local runtime state for
observation, but it must not become the primary place where agents or operators author
steer, retry, task replacement, or lifecycle mutations before the CLI/API contract is
implemented and audited.

Bottom-layer steer support must not hard-limit task content. The app-server,
protocol, and runtime layer should expose steer broadly enough to pass operator-supplied
instructions through to a live lane. Constraints belong above that layer: project
policy, audit records, recovery rules, workflow contracts, privacy guards, and
agent-facing skills must guide responsible use.

## Capability Matrix

| Capability | Contract status | Current implementation evidence | Required behavior |
| --- | --- | --- | --- |
| Inspect lane state | Supported | `decodex status`, `decodex status --json`, `decodex diagnose --json`, `decodex evidence <ISSUE>`, operator snapshots, and dashboard views | Always inspect before mutating or steering. Inspection must not mutate tracker state, runtime DB rows, worktrees, or app-server turns. |
| Project dispatch pause | Supported for future dispatch | `decodex project disable <service-id>` and the runtime project enabled flag; dashboard control handlers can also toggle the same flag | Pause prevents new dispatch for the project. It must not kill or rewrite already active lanes. |
| Project dispatch resume | Supported for future dispatch | `decodex project enable <service-id>` and the runtime project enabled flag; dashboard control handlers can also toggle the same flag | Resume re-enables future dispatch after the operator has inspected blockers, capacity, and queue state. |
| Linear scan request | Supported | `POST /api/linear-scan` with optional `projectId` | Queue a scan for the next control-plane tick while respecting tracker backoff. This is an intake/status refresh request, not an execution command. |
| Soft interrupt | Planned CLI/API control; bottom-layer method allowed | Decodex does not currently send `turn/interrupt` from its app-server client | Prefer soft interrupt before hard interruption when the active turn id is known and the app-server capability is present. Soft interrupt requests a graceful turn stop and must leave classification to the runtime. |
| Hard interrupt fallback | Emergency fallback only | Current dashboard `interruptRun` signals the recorded process and marks the attempt `interrupted` | Use only when soft interrupt is unavailable, timed out, or impossible because the process or app-server boundary cannot be reached. Preserve retained worktree evidence and runtime classification. |
| Steer active lane | Planned CLI/API control; bottom-layer method must stay broad | Decodex does not currently send `turn/steer` from its app-server client | Pass operator-supplied steer text through the CLI/API when available. Do not narrow the protocol to a fixed set of task-content categories. Apply policy, audit, privacy, and lifecycle guardrails above the protocol. |
| Retained resume/retry | Supported through runtime lifecycle | `decodex run <ISSUE>`, retry scheduling, retained worktree recovery, and `thread/resume` for same-thread app-server continuation | Resume only when retained worktree, issue, branch, PR, and runtime evidence still prove the same lane. Treat ambiguous lineage as manual attention. |
| Manual attention | Supported terminal control path | `decodex:needs-attention`, `issue_comment(kind = "manual_attention")`, and `issue_terminal_finalize(path = "manual_attention")` | Stop automation when policy requires a human decision. Explain the blocker through structured public fields and keep private evidence local. |
| Task replacement | Deferred lifecycle work | No supported active-lane replacement command | Do not use steer or raw injection to replace the task silently. Treat replacement as explicit lifecycle work: pause/stop if needed, update or requeue the issue, or create a new issue/lane. |
| Raw thread item injection | Unsupported as an operator feature | No Decodex operator path for `thread/inject_items` | Do not expose raw `thread/inject_items` to operators in this rollout. Use `turn/steer` for operator steer once implemented. |
| Active-lane UI authoring controls | Deferred | Existing dashboard views and low-level handlers are not the CLI/API-first lane-control contract | Do not add dashboard steer, retry, or task-replacement controls in this rollout. Ship CLI/API first, then promote UI controls only after audit and policy behavior is settled. |

## Inspect-First Rule

Before any lane-control mutation, the operator or agent must inspect:

- project id and registered project enablement
- issue identifier and tracker state
- branch and retained worktree ownership
- run id, attempt number, thread id, and current turn id when available
- active lease state, process liveness, and protocol activity
- recent private evidence and any public Linear lifecycle signal
- PR lineage when the lane already crossed into review handoff

If inspection cannot prove the requested lane identity, do not steer, interrupt, retry,
or resume. Use the manual-attention path or a read-only recovery diagnosis instead of
guessing.

## Soft And Hard Interrupts

Soft interrupt is the preferred active-turn stop path. A compliant soft interrupt:

- targets the current known app-server turn
- requests `turn/interrupt` instead of signaling the process
- records an audit event with project id, issue id, run id, attempt, thread id, turn id,
operator reason, and outcome
- leaves tracker state, retry policy, and retained-worktree classification to the
Decodex runtime

Hard interrupt is a fallback, not the normal operator control. A hard interrupt may
signal the recorded child process only after Decodex proves the process identity still
matches the current run attempt. The runtime must preserve evidence, mark the attempt
with an interruption status, clear or retain ownership according to the runtime
contract, and avoid pretending the agent completed a terminal path.

## Steer

Steer is an active-lane instruction from the operator to the running Codex turn. The
bottom-layer protocol shape must not decide which task topics are acceptable. It should
carry the operator's steer text broadly, subject only to generic transport and schema
requirements.

Higher layers own guardrails:

- CLI/API must require explicit operator-supplied steer text and a target lane identity.
- Decodex must audit steer requests in local runtime evidence before or alongside
delivery to app-server.
- Privacy and public-text guards decide what, if anything, may be mirrored to Linear.
- Agent skills must tell agents to use steer only when the operator supplies it, and to
inspect the lane first.
- Workflow policy decides whether the steered lane may continue, must retry, or must
stop for human attention after the turn resolves.

Steer is not task replacement. If the operator wants a different issue, a materially
different goal, or a new acceptance contract, the correct path is lifecycle/requeue
work, not a hidden active-lane content swap.

## Retained Resume And Retry

Retained resume and retry are lifecycle controls, not prompt injection controls.
Decodex may re-enter a retained lane only when the runtime can prove that the issue,
branch, worktree, run evidence, and PR lineage still match the owned lane.

Supported retained controls include:

- normal retry scheduling after retryable failures with remaining budget
- same-thread `thread/resume` when Decodex has a valid thread id and the app-server
accepts it
- explicit one-issue automation with `decodex run <ISSUE>` when the registered
workflow and current lane state make that issue eligible
- recovery diagnosis and explicit rebind paths defined by the retained-lane specs

Unsupported retained controls include guessing a worktree from a branch name, clearing
runtime DB rows by hand, or changing tracker state to force dispatch when ownership
signals disagree.

## Manual Attention

Manual attention is the required stop when the operator or runtime cannot safely derive
the next lane action from authoritative signals. It is also the correct route when a
requested control would overwrite useful partial work, hide a blocker, or require
guessing human intent.

Agents must not simulate manual attention by editing tracker state directly. The valid
agent path is:

1. add the configured `decodex:needs-attention` label
2. call `issue_comment` with `kind = "manual_attention"` and structured public fields
3. call `issue_terminal_finalize(path = "manual_attention")`

Operators may later clear the blocker, clear the label, and requeue the lane through
the configured lifecycle.

## Audit And Privacy

Every supported control mutation should create local runtime evidence. At minimum, a
control audit record should identify the project, issue, run id, attempt, branch,
operator command source, requested capability, normalized result, and next action.

Linear public text remains sparse. Do not write steer text, raw command output, process
diagnostics, private evidence payloads, account details, or host-local paths into
Linear unless a schema-controlled public projection explicitly allows it.

## Implementation Status For This Rollout

This document specifies capabilities that the CLI/API should expose first. Current code
already supports inspect, project enable/disable, Linear scan requests, retained
resume/retry lifecycle paths, and manual-attention finalization. Current code does not
yet implement Decodex CLI/API controls that send `turn/interrupt` or `turn/steer`, and
it does not expose raw `thread/inject_items` as an operator feature.

When implementation work adds the missing CLI/API controls, update this spec,
[`app-server.md`](./app-server.md), the operator reference, and the Decodex plugin
skills in the same lane.
Loading