diff --git a/docs/index.md b/docs/index.md index 7954155a..20f37a4c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -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 diff --git a/docs/reference/operator-control-plane.md b/docs/reference/operator-control-plane.md index 3151e441..42ee501a 100644 --- a/docs/reference/operator-control-plane.md +++ b/docs/reference/operator-control-plane.md @@ -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. @@ -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 | | --- | --- | @@ -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 @@ -388,6 +399,7 @@ 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. @@ -395,6 +407,7 @@ 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) diff --git a/docs/spec/app-server.md b/docs/spec/app-server.md index 4a505011..2e78dccf 100644 --- a/docs/spec/app-server.md +++ b/docs/spec/app-server.md @@ -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. @@ -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: diff --git a/docs/spec/index.md b/docs/spec/index.md index e82115a7..8011550e 100644 --- a/docs/spec/index.md +++ b/docs/spec/index.md @@ -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 diff --git a/docs/spec/lane-control.md b/docs/spec/lane-control.md new file mode 100644 index 00000000..8c4ae84d --- /dev/null +++ b/docs/spec/lane-control.md @@ -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 `, 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 ` 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 ` 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 `, 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 ` 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. diff --git a/plugins/decodex/skills/automation/SKILL.md b/plugins/decodex/skills/automation/SKILL.md index a5d9bfb2..461daac7 100644 --- a/plugins/decodex/skills/automation/SKILL.md +++ b/plugins/decodex/skills/automation/SKILL.md @@ -19,6 +19,9 @@ Operate Decodex as the retained-lane control plane for automatic development. - `docs/spec/tracker-tools.md` owns issue-scoped tracker tool semantics. - `docs/spec/post-review-lifecycle.md` owns post-`In Review` repair, landing, closeout, and cleanup phases. +- `docs/spec/lane-control.md` owns CLI/API-first lane-control capabilities, including + inspect, pause/resume, scan, interrupt, steer, retained resume/retry, manual + attention, and deferred controls. - `docs/spec/workflow-file.md` owns `WORKFLOW.md` schema and field semantics. - `docs/reference/operator-control-plane.md` owns the current status/dashboard field map. @@ -105,6 +108,39 @@ terminal automation signal. - Before assuming a lane is stuck, compare lane phase, wait reason, last run activity, protocol activity, active lease state, and child-agent activity when present. +## Lane Controls + +Read `docs/spec/lane-control.md` before using or explaining operator controls. + +Rules for agents: + +- Inspect first with `decodex status`, `decodex status --json`, `decodex diagnose + --json`, `decodex evidence `, or the dashboard snapshot. Confirm project id, + issue id, branch, run id, attempt, thread/turn evidence, process liveness, tracker + state, and PR lineage before mutating anything. +- Use project dispatch pause/resume only for future intake. `decodex project disable + ` pauses new dispatch; `decodex project enable ` resumes it. + Neither command kills active lanes. +- Request Linear refresh with `POST /api/linear-scan` when a newly queued or relabeled + issue should be observed before the next 5-minute poll. +- Prefer soft interrupt through the CLI/API `turn/interrupt` control when it exists and + the active turn can be targeted. Use hard process interruption only as a fallback when + soft interrupt is unavailable, timed out, or impossible. +- Use steer only through the CLI/API lane-control surface and only when the operator + supplies the steer text. Bottom-layer steer support is broad; policy, audit, privacy, + workflow, recovery, and skills provide the guardrails. +- Treat task replacement as explicit lifecycle work, not steer. If the operator wants a + different objective or acceptance contract, pause or stop if needed, update/requeue + the issue or create a new lane, and preserve audit evidence. +- Use retained resume/retry through runtime lifecycle paths such as `decodex run + ` only after inspection proves the retained worktree, issue, branch, runtime + evidence, and PR lineage still match. +- Do not expose or use raw `thread/inject_items` as an operator feature. +- Do not mutate Linear tracker state directly to simulate lane controls. During an + owned agent run, use issue-scoped tools for progress, review handoff, manual + attention, and terminal finalization. Outside the owned lane, use documented + CLI/API controls and the labels skill. + ## Boundaries - Do not substitute manual `decodex land` for runtime-owned retained-lane landing unless diff --git a/plugins/decodex/skills/decodex/SKILL.md b/plugins/decodex/skills/decodex/SKILL.md index 9c4d8bdf..c92ebb7e 100644 --- a/plugins/decodex/skills/decodex/SKILL.md +++ b/plugins/decodex/skills/decodex/SKILL.md @@ -38,6 +38,8 @@ specs. Decodex has two supported use modes: ## Authority Split - Runtime behavior belongs to `apps/decodex/src/` and `docs/spec/`. +- Operator lane-control capabilities belong to `docs/spec/lane-control.md`, with the + low-level app-server method boundary in `docs/spec/app-server.md`. - Operator procedures belong to `docs/runbook/`. - Current repository layout belongs to `docs/reference/`. - Registered project execution policy belongs to project-local `WORKFLOW.md`. diff --git a/plugins/decodex/skills/manual-cli/SKILL.md b/plugins/decodex/skills/manual-cli/SKILL.md index e2998ed3..20c1add9 100644 --- a/plugins/decodex/skills/manual-cli/SKILL.md +++ b/plugins/decodex/skills/manual-cli/SKILL.md @@ -14,6 +14,7 @@ runtime-owned retained-lane lifecycle. - `README.md` for the current CLI shape. - `Makefile.toml` before running repo-native checks. +- `docs/spec/lane-control.md` before using CLI/API lane controls. - `docs/reference/operator-control-plane.md` when interpreting `status` or dashboard fields. - `docs/runbook/linear-archive-hygiene.md` before archiving old terminal Linear issues. @@ -59,6 +60,23 @@ cargo run -p decodex --bin decodex -- run Before starting a live run, read the `automation` skill and the registered project's `WORKFLOW.md`. Treat live `run` as orchestration, not as a status check. +CLI/API lane controls: + +- Inspect first with `decodex status`, `decodex status --json`, `decodex diagnose + --json`, or `decodex evidence `. +- Use `decodex project disable ` to pause future dispatch for a registered + project, and `decodex project enable ` to resume it. +- Use `POST /api/linear-scan` to request an intake/status refresh before the next + scheduled Linear poll. +- Use `decodex run ` only for deliberate one-issue automation or retained + retry/resume when the lane remains eligible under the registered workflow. +- When future CLI/API controls expose soft interrupt or steer, prefer soft interrupt + over hard process interruption and use steer only with explicit operator-supplied + text. +- Do not use active-lane UI controls, direct runtime DB edits, raw + `thread/inject_items`, or tracker-state mutations as substitutes for the lane-control + contract. + Manual commit and landing are separate narrow workflows: - Use `commit` before creating a Decodex-formatted local commit.