From 7b7d6f55a2aa999f4c99578935f4bac7c880863a Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Wed, 27 May 2026 17:18:05 +0800 Subject: [PATCH] {"schema":"decodex/commit/1","summary":"Start scheduler from Decodex App","authority":"manual"} --- README.md | 16 ++++++----- apps/decodex-app/README.md | 27 ++++++++++--------- .../DecodexApp/DecodexServerBridge.swift | 1 - apps/decodex/src/cli.rs | 10 +++---- apps/decodex/src/orchestrator.rs | 1 + apps/decodex/src/orchestrator/entrypoints.rs | 2 +- docs/reference/operator-control-plane.md | 12 ++++----- docs/runbook/self-dogfood-pilot.md | 24 ++++++++++------- plugins/decodex/skills/automation/SKILL.md | 14 +++++----- plugins/decodex/skills/manual-cli/SKILL.md | 9 ++++--- 10 files changed, 61 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 79121a3d..150c7aa6 100644 --- a/README.md +++ b/README.md @@ -104,11 +104,13 @@ cargo run -p decodex --bin decodex -- status cargo run -p decodex --bin decodex -- diagnose --json cargo run -p decodex --bin decodex -- maintenance prune --dry-run cargo run -p decodex --bin decodex -- run --dry-run -cargo run -p decodex --bin decodex -- serve --interval 60s --listen-address 127.0.0.1:8912 +cargo run -p decodex --bin decodex -- serve --listen-address 127.0.0.1:8912 ``` Project-scoped commands accept `--config ` after the subcommand when the operator wants to override registry-based project resolution for that command. +`decodex serve` owns the default scheduler cadence of 15 seconds; pass +`--interval ` only when deliberately overriding that poll interval. ### Install from Source @@ -224,11 +226,13 @@ node dev/operator-dashboard-mock.mjs --listen-address 127.0.0.1:57399 --use-code ``` Use hidden `decodex serve --dev --listen-address 127.0.0.1:8912` only when -developing Decodex App's bundled server path or the local account/app snapshot APIs -against real runtime state. Dev mode deliberately does not register projects, poll -Linear, dispatch work, or accept `--config` or `--interval`. For real automation, -use ordinary `decodex serve --interval ...`; for dashboard-only UI work, prefer the -mock server above. +developing local account/app snapshot APIs against real runtime state while explicitly +avoiding scheduler activity. Dev mode deliberately does not register projects, poll +Linear, dispatch work, or accept `--config` or `--interval`. Decodex App's normal +fallback server is ordinary `decodex serve --listen-address 127.0.0.1:8912`; the CLI +owns the default scheduler interval, currently 15 seconds. App launch connects to an +existing live default listener instead of starting a duplicate server. For +dashboard-only UI work, prefer the mock server above. The dashboard semantics and local-vs-external state boundary live in `docs/reference/operator-control-plane.md`. diff --git a/apps/decodex-app/README.md b/apps/decodex-app/README.md index 769cac55..5b1e0d95 100644 --- a/apps/decodex-app/README.md +++ b/apps/decodex-app/README.md @@ -13,11 +13,11 @@ The first Decodex App release manages the shared Codex account pool through the bundled Rust app helper so account UI stays on the same CLI-owned files even when a long-running local `decodex serve` is older than the app bundle. On launch the app also connects to an existing `decodex serve` on the default local endpoint when one is -available; otherwise it starts the bundled Decodex binary in its hidden dev endpoint -mode as `decodex serve --dev --listen-address 127.0.0.1:8912` for operator snapshot -and WebUI routes. App-started servers do not poll registered projects or dispatch -Linear work. The helper owns account -operations and interactive login flows that need streamed command output: +available; otherwise it starts the bundled Decodex binary as a normal scheduler with +`decodex serve --listen-address 127.0.0.1:8912`. The CLI owns the default scheduler +interval, currently 15 seconds. App-started servers load the enabled project registry +and own the same operator listener as a manually started `decodex serve`. The helper +owns account operations and interactive login flows that need streamed command output: - list accounts without printing token material - pin future Decodex runs to one account @@ -26,9 +26,10 @@ operations and interactive login flows that need streamed command output: - run isolated Codex device login, then import the resulting auth file - remove a stored account from the local pool -The app does not schedule Decodex runs, own project registration, or replace -`decodex serve`. It is a native UI over the shared Rust account-management service, -not a wrapper around the `decodex` CLI binary. +The app does not schedule Decodex runs itself, own project registration, or replace the +Rust control plane. It is a native UI over the shared Rust account-management service +and uses the bundled `decodex` server only when no compatible local server is already +running. The app and operator dashboard share account-pool state through the Rust account API: stored accounts come from `~/.codex/decodex/accounts.jsonl`, run routing and account @@ -72,11 +73,11 @@ DECODEX_APP_HELPER="$(pwd)/target/debug/decodex-app-helper" \ swift run --package-path apps/decodex-app DecodexApp ``` -The app-started server path is the main reason to use `decodex serve --dev` -manually: it lets you test the same local account APIs, app snapshot API, and -dashboard routes without starting the scheduler. Do not use `--dev` to validate -project registration, Linear polling, queue intake, or retained-lane execution; use -ordinary `decodex serve --interval ...` for those paths. +Use hidden `decodex serve --dev` only when manually testing local account APIs, the app +snapshot API, or dashboard routes while deliberately avoiding scheduler activity. The +normal app fallback is ordinary `decodex serve --listen-address 127.0.0.1:8912`. Do +not use `--dev` to validate project registration, Linear polling, queue intake, or +retained-lane execution; use ordinary `decodex serve` for those paths. The staging script follows the local Rsnap-style signing path: it writes `target/decodex-app/Decodex App.app`, signs the bundle with an Apple Development diff --git a/apps/decodex-app/Sources/DecodexApp/DecodexServerBridge.swift b/apps/decodex-app/Sources/DecodexApp/DecodexServerBridge.swift index 00c9d110..db02419e 100644 --- a/apps/decodex-app/Sources/DecodexApp/DecodexServerBridge.swift +++ b/apps/decodex-app/Sources/DecodexApp/DecodexServerBridge.swift @@ -223,7 +223,6 @@ actor DecodexServerBridge { process.executableURL = try decodexExecutableURL() process.arguments = [ "serve", - "--dev", "--listen-address", defaultListenAddress, ] process.standardOutput = nullDevice diff --git a/apps/decodex/src/cli.rs b/apps/decodex/src/cli.rs index d47f72e2..88cb5ac8 100644 --- a/apps/decodex/src/cli.rs +++ b/apps/decodex/src/cli.rs @@ -314,13 +314,13 @@ impl RunCommand { struct ServeCommand { #[command(flatten)] project_config: ProjectConfigArgs, - /// Poll interval between control-plane ticks, for example `60s` or `5m`. + /// Override the scheduler poll interval, for example `15s` or `5m`. #[arg(long, value_name = "INTERVAL", value_parser = parse_duration_arg)] interval: Option, /// Operator UI listen address. #[arg(long, value_name = "ADDR", default_value = "127.0.0.1:8912")] listen_address: String, - /// Start the Decodex App/dev endpoint without polling or dispatching projects. + /// Start the local dev endpoint without polling or dispatching projects. #[arg(long, hide = true)] dev: bool, } @@ -334,11 +334,7 @@ impl ServeCommand { orchestrator::run_control_plane(ServeRequest { config_path: self.project_config.as_path(), - poll_interval: if self.dev { - None - } else { - Some(self.interval.unwrap_or_else(|| Duration::from_secs(60))) - }, + poll_interval: if self.dev { None } else { self.interval }, listen_address: &self.listen_address, dev: self.dev, }) diff --git a/apps/decodex/src/orchestrator.rs b/apps/decodex/src/orchestrator.rs index 9642990a..baee2af4 100644 --- a/apps/decodex/src/orchestrator.rs +++ b/apps/decodex/src/orchestrator.rs @@ -87,6 +87,7 @@ const OPERATOR_STATE_HEADER_TERMINATOR: &[u8] = b"\r\n\r\n"; const OPERATOR_DASHBOARD_WS_HEARTBEAT_INTERVAL: Duration = Duration::from_secs(20); const OPERATOR_RUN_ACTIVITY_STREAM_INTERVAL: Duration = Duration::from_secs(1); const OPERATOR_DEV_SNAPSHOT_STREAM_INTERVAL: Duration = Duration::from_secs(1); +const DEFAULT_CONTROL_PLANE_POLL_INTERVAL: Duration = Duration::from_secs(15); const PULL_REQUEST_REVIEW_STATE_QUERY: &str = r#" query($owner: String!, $name: String!, $number: Int!, $reviewThreadsAfter: String) { repository(owner: $owner, name: $name) { diff --git a/apps/decodex/src/orchestrator/entrypoints.rs b/apps/decodex/src/orchestrator/entrypoints.rs index a35c0331..bd1a4774 100644 --- a/apps/decodex/src/orchestrator/entrypoints.rs +++ b/apps/decodex/src/orchestrator/entrypoints.rs @@ -149,7 +149,7 @@ pub(crate) fn run_control_plane(request: ServeRequest<'_>) -> Result<()> { let poll_interval = request .poll_interval - .unwrap_or_else(|| Duration::from_secs(60)); + .unwrap_or(DEFAULT_CONTROL_PLANE_POLL_INTERVAL); if poll_interval.is_zero() { eyre::bail!("serve interval must be greater than zero."); diff --git a/docs/reference/operator-control-plane.md b/docs/reference/operator-control-plane.md index 8645057b..4e2a7129 100644 --- a/docs/reference/operator-control-plane.md +++ b/docs/reference/operator-control-plane.md @@ -36,15 +36,13 @@ Decodex currently runs as a local, single-machine control plane: Decodex App is a native shell over the same local runtime and account-pool state. On launch it connects to an existing default local listener when one is reachable; if not, it starts the bundled `decodex` binary as -`decodex serve --dev --listen-address 127.0.0.1:8912`. Dev mode serves the dashboard, -account APIs, and `GET /api/operator-snapshot` for the app, but it does not register -projects, poll Linear, dispatch work, or accept `--config` or `--interval`. Use -ordinary `decodex serve --interval ...` for the automation loop. +`decodex serve --listen-address 127.0.0.1:8912`. The app fallback is a normal +control-plane server: it loads the enabled project registry, uses the CLI-owned default +15-second poll interval, and serves the dashboard, account APIs, and +`GET /api/operator-snapshot` from the single local listener. -Use `--dev` only for local development and app-owned startup: +Use `--dev` only for isolated local development: -- Decodex App may start the bundled server with `--dev` when no compatible default - listener is already running. - Developers may use `--dev` to exercise real account APIs, `GET /api/operator-snapshot`, and dashboard routes against local runtime state without starting automation. - Do not use `--dev` for operator automation, queue intake, retained-lane recovery, diff --git a/docs/runbook/self-dogfood-pilot.md b/docs/runbook/self-dogfood-pilot.md index 0cec1a5a..8756e382 100644 --- a/docs/runbook/self-dogfood-pilot.md +++ b/docs/runbook/self-dogfood-pilot.md @@ -289,7 +289,7 @@ is still unmerged, use the normal reviewed lane checkout instead. After `probe`, `project add`, `run --dry-run`, and `run` all behave as expected, use `serve` for the long-running pilot loop: ```sh -cargo run -p decodex --bin decodex -- serve --interval 60s +cargo run -p decodex --bin decodex -- serve ``` ### Installed-binary observer loop @@ -419,16 +419,16 @@ wants to observe the self-bootstrap loop without reading source code. ```sh decodex project add ~/.codex/decodex/projects/decodex decodex project list - decodex serve --interval 60s --listen-address 127.0.0.1:8912 + decodex serve --listen-address 127.0.0.1:8912 ``` `serve` owns one operator UI and schedules all enabled registered projects from the local runtime database. Passing `--config` refreshes that project registration before the scheduler starts. - Do not use `decodex serve --dev` for this step. Dev mode is only for Decodex App - and local account/app snapshot API development; it does not register projects, - poll Linear, dispatch work, or accept `--config` or `--interval`. + Do not use `decodex serve --dev` for this step. Dev mode is only for local + account/app snapshot API development while avoiding scheduler activity; it does not + register projects, poll Linear, dispatch work, or accept `--config` or `--interval`. Pass `decodex serve --config ` when you want `serve` to refresh one project registration before it starts. Omit it when the registry already contains @@ -618,12 +618,18 @@ Decodex is intentionally Unix-only, and the control plane relies on Unix file-de `decodex serve` owns the local operator console. Use `--listen-address` when you need a non-default bind address: ```sh -decodex serve --interval 60s --listen-address 127.0.0.1:8912 +decodex serve --listen-address 127.0.0.1:8912 ``` -Use hidden `decodex serve --dev` only for Decodex App or local account/app snapshot API -development. It is not a scheduler and must not be used for this runbook's automation, -queue intake, project registration, or retained-lane recovery steps. +Omit `--interval` to use the CLI default 15-second scheduler cadence. Pass +`--interval ` only when this runbook step deliberately needs a non-default +poll interval. + +Use hidden `decodex serve --dev` only for local account/app snapshot API development +while deliberately avoiding scheduler activity. Decodex App's fallback server uses +ordinary `decodex serve` and leaves scheduler cadence to the CLI default. Dev mode is +not a scheduler and must not be used for this runbook's automation, queue intake, +project registration, or retained-lane recovery steps. The listener serves the operator console from the canonical `GET /` and `GET /dashboard` routes, the same JSON operator snapshot used by `cargo run -p decodex --bin decodex -- status --json` through the `/dashboard/control` WebSocket, and the minimal `GET /livez` liveness probe on the same listener. The single console keeps `Projects`, `Running Lanes`, `Intake Queue`, `Review & Landing`, `Recovery Worktrees`, and `Run Ledger` visible together. Intake candidates that are already claimed by a running lane are shown as active queue echoes, capacity-bound candidates are shown as waiting rather than blocked, running lane worktrees stay with their owning lane, and retained/recovery worktrees remain folded until diagnostics are needed: diff --git a/plugins/decodex/skills/automation/SKILL.md b/plugins/decodex/skills/automation/SKILL.md index 81981446..6ceb97d1 100644 --- a/plugins/decodex/skills/automation/SKILL.md +++ b/plugins/decodex/skills/automation/SKILL.md @@ -32,7 +32,7 @@ decodex project add "$HOME/.codex/decodex/projects/" decodex status decodex run --dry-run decodex run -decodex serve --interval 60s +decodex serve ``` From the Decodex repo while developing the runtime: @@ -43,7 +43,7 @@ cargo run -p decodex --bin decodex -- project add "$HOME/.codex/decodex/projects cargo run -p decodex --bin decodex -- status cargo run -p decodex --bin decodex -- run --dry-run cargo run -p decodex --bin decodex -- run -cargo run -p decodex --bin decodex -- serve --interval 60s +cargo run -p decodex --bin decodex -- serve ``` Use `decodex serve --config ` or @@ -52,11 +52,11 @@ wants to register that project and start the scheduler in one command. Use `decodex run ` or `cargo run -p decodex --bin decodex -- run ` only for a deliberate one-issue automation pass; it still uses the same retained-lane eligibility and lifecycle rules. -Do not use hidden `serve --dev` for automation. That mode belongs to Decodex App and -local development: it serves local dashboard/account/app snapshot APIs, but it does -not register projects, poll Linear, or dispatch lanes, and it rejects `--config` and -`--interval`. Use it only when testing the app-owned endpoint without starting the -scheduler. +Do not use hidden `serve --dev` for automation. That mode is for isolated local +development: it serves local dashboard/account/app snapshot APIs, but it does not +register projects, poll Linear, or dispatch lanes, and it rejects `--config` and +`--interval`. Decodex App's fallback server uses ordinary `serve` when no compatible +local listener is already running. ## Intake and Ownership diff --git a/plugins/decodex/skills/manual-cli/SKILL.md b/plugins/decodex/skills/manual-cli/SKILL.md index da485a71..b9d2c06a 100644 --- a/plugins/decodex/skills/manual-cli/SKILL.md +++ b/plugins/decodex/skills/manual-cli/SKILL.md @@ -80,10 +80,11 @@ Manual commit and landing are separate narrow workflows: - Use `run --dry-run` before live automation to validate project loading, issue discovery, eligibility, and worktree planning without tracker mutation. - Use `probe stdio://` before relying on the Codex app-server boundary. -- Treat hidden `serve --dev` as Decodex App and local-development infrastructure - only. It serves dashboard, account, and app snapshot APIs, but it does not register - projects, poll Linear, dispatch work, or accept `--config` or `--interval`. Use it - only when testing the app-owned endpoint without starting the scheduler. +- Treat hidden `serve --dev` as isolated local-development infrastructure only. It + serves dashboard, account, and app snapshot APIs, but it does not register projects, + poll Linear, dispatch work, or accept `--config` or `--interval`. Decodex App's + fallback server uses ordinary `serve` when no compatible local listener is already + running. - For `skills/list` app-server preflight output, enabled skills plus scan diagnostics are local evidence, not a lane blocker. Missing cwd coverage or zero enabled skills are blockers; inspect `first_error_path` and `first_error` before changing plugin or