# EasySpecs CLI — commands, flags, and configuration

**Error codes:** OS exit codes, structured `**failureExitId`** values, and JSON field semantics are catalogued in `**[error-code.md](./error-code.md)**` (maintained with CLI changes).

Published package: `**@gluecharm-lab/easyspecs-cli**` (`easyspecs-cli`). Source of truth for routing and option registration: `[src/cli/cliProgram.ts](../../src/cli/cliProgram.ts)`, command dispatch and business flow wiring: `[src/cli/main.ts](../../src/cli/main.ts)`, merged settings: `[mergeEasyspecsCliSettings](../../src/cli/cliSettings.ts)`, per-repo config: `[src/config/easyspecsConfigFile.ts](../../src/config/easyspecsConfigFile.ts)`, CLI API URL resolution: `[src/easyspecsApiBaseUrlCli.ts](../../src/easyspecsApiBaseUrlCli.ts)`. The VS Code extension resolves API URLs via `[src/apiBaseUrlResolve.ts](../../src/apiBaseUrlResolve.ts)`; `**easyspecs-cli` does not use that chain** for System Manager origin (**SRS-43 R30 / R34**).

### Vocabulary (SRS-53)

End-to-end behaviour and file layout: see the repo root companion `**run-analysis-flow.md`** (no URL; same repository).


| Term            | One-line meaning                                                                                                     |
| --------------- | -------------------------------------------------------------------------------------------------------------------- |
| **Factory**     | The ordered **Generate Context** run (CLI: `**analysis`**; VS Code: Diagnosis **Run factory** / **Stop factory**).   |
| **Pipeline**    | A named stage the Factory composes: Synthesis, Coverage, Remediation, Link Mapping, Upload, or Download.             |
| **Workstation** | One atomic unit inside a Pipeline (OpenCode agent + deterministic validators / repair loop, or a programmatic step). |


**Stderr tags (human TTY mode):** phase changes may insert a blank line when the family changes. Tags include `**[factory]`**, `**[pipeline:synthesis]**` (and `coverage`, `remediation`, `link-mapping`, `upload`, `download`), `**[workstation:<id>]**` for OpenCode subprocess lines, `**[pool]**` / `**[queue]**` for work queues, `**[deprecated-setting]**` when a legacy `**config.json**` key was consumed (canonical keys: `**easyspecs.factory.***`, `**easyspecs.workstations.***`, `**easyspecs.pipelines.upload.***` — see SRS-53 alias table). `**[ace]**` and `**[open questions]**` unchanged. Pool **start** lines use `**[pool] start <id> (active N/M waiting W)**` — `**N**` in-flight workstations, `**M**` current cap, `**W**` ready FIFO queue depth (excludes pending items blocked on parents). Zero-reference remediation uses the same suffix on `**[pipeline:remediation] start <repo-relative-path> (active N/M waiting W)**`. ACE auto-learn start lines use the same `**(active N/M waiting W)**` suffix. See **[SRS-120](./.easyspecs/docs/srs/srs-120.md)** and **[SRS-123](./.easyspecs/docs/srs/srs-123.md)**.

Quick usage:

```bash
easyspecs-cli help
```

### Factory failure reporting (**SRS-57** / **SRS-70**)

When `**easyspecs-cli analysis`** exits **non-zero** with factory failures, human stderr prints a short banner and **one bullet per failed factory phase** (readable label, `**failureExitId**`, normative `**title**`, optional `**detail**`). **OS exit `5` is never used** — each phase maps to a dedicated code (**50–58** for post-readiness pipeline rows, **84–87** for **`diagnose_readiness`** readiness gates per **SRS-72**), **59** when multiple distinct phases fail in one run, or **49** when unattributed. `**--json**` adds:


| JSON key               | Meaning                                                                                                                                                                                                                                                                                  |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `**factoryFailures**`  | Array of `**{ factory, phase, exitCode, failureExitId, title, validationExitId?, detail?, validationSubcode? }**` — `**failureExitId**` is the canonical `**major.minor**` id (**SRS-58**); `**major**` matches **`process.exit`** (**SRS-70**). See `[error-code.md](./error-code.md)`. |
| `**failurePhase**`     | Last failed phase in macro order (alias for scripts).                                                                                                                                                                                                                                    |
| `**validationExitId**` | Legacy alias: same string as `**failureExitId**` when present.                                                                                                                                                                                                                           |
| `**exitMeaning**`      | First failure `**title**`, or that title plus “and *N* other pipeline phases failed”.                                                                                                                                                                                                    |
| `**error**`            | Orchestrator message; if several phases failed, a short appendix lists extra `**failureExitId`**s (see `[factoryValidationFailures.ts](../../src/factory/factoryValidationFailures.ts)`).                                                                                                |


**Phase → OS exit (generate_context):** **84–85** **`diagnose_readiness`** when a **readiness gate (a)** or **(b)** fails (**SRS-72** — OpenCode not installed, or credentials not ready); **86–87** are **reserved** (readiness no longer exits for a missing default model when **(a)** and **(b)** pass); **50** `create_analysis_worktree`, **51** `materialize_opencode_agents`, **52** `synthesis_convergence`, **53** `reference_coverage`, **54** `zero_reference_remediation_convergence`, **55** `reference_coverage_execution_report`, **56** `link_mapping_pipeline`, **57** `assemble_application_context_index`, **58** `backend_context_sync`, **49** unknown / unattributed. Full table: `[error-code.md](./error-code.md)`.

**Optional `validationSubcode` (v1):** `**R5_MACRO_PING_PONG`** when synthesis `**detail**` notes **R5** unstable convergence; `**COVERAGE_PERCENT_THRESHOLD`** when reference-coverage `**detail**` matches the strict percent gate pattern.

---

## Global options

### Analysis checkout (SRS-95)

| Flag                | Meaning                                                                                                                      |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `--in-place`        | Use the repository root (`--cwd`) as the analysis checkout (no detached worktree). Aliases: `--no-worktree`, `--noworktree`. |
| `--worktree <path>` | Use an explicit git checkout (workspace root or detached analysis worktree).                                                 |

Panel-spawned runs pass `--in-place` when **Run in → Workspace**, or `--worktree <abs>` for the selected checkout. `resolveAdHocCheckoutRoot` falls back to `--in-place` when no `--worktree` and no stored snapshot.

These flags may appear **before** the subcommand. Parsing and command registration are implemented with Commander in `[src/cli/cliProgram.ts](../../src/cli/cliProgram.ts)`, with command execution in `[src/cli/main.ts](../../src/cli/main.ts)`.


| Flag                       | Effect                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--cwd <dir>`              | Repository root for git resolution and file paths (default: current working directory).                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `--ci`                     | Non-interactive mode; feeds into merged settings (e.g. Factory outer-iteration default when unlimited). `**EASYSPECS_CI` is not read** — use this flag in CI.                                                                                                                                                                                                                                                                                                                                                                |
| `--json`                   | On supported exits, prints one JSON summary line on stdout; suppresses some human-oriented stderr unless `--verbose`. On `**analysis**` factory failure, the JSON line includes `**factoryFailures`** (**SRS-57** — one object per `**failed`** factory phase) plus `**failurePhase**`, `**validationExitId**`, composed `**error**`, and `**exitMeaning**` tailored to the first failure. **Exit codes:** **[SRS-70](../../.easyspecs/docs/srs/srs-70.md)** — OS **5** is not used; see `[error-code.md](./error-code.md)`. |
| `--verbose`                | Extra stderr logging where implemented.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `--api-base-url <url>`     | System Manager API origin for this process (overrides `easyspecs.apiBaseUrl` in config).                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `--environment production` | `--environment staging`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | Alias: `**--env**`. Overrides `easyspecs.deploymentEnvironment` for built-in URL selection when no explicit URL is set. |
| `--promote`                | After **`run synthesis`**: force promotion into the repo when **`easyspecs.analysis.promoteContextToWorkspace`** would turn it off. **`analysis`**: not required for promotion (promotion runs by default unless **`--no-promote`**).                                                                                                                                                                                                                                                                                        |
| `--no-promote`             | After **`run synthesis`** or **`analysis`**: skip copying **`.easyspecs/context`** into the repo for this run.                                                                                                                                                                                                                                                                                                                                                                                                               |
| `--help`, `-h`             | Show built-in help (also `help` as first positional). Does **not** create `.easyspecs/config.json`.                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `--version`, `-V`          | Print CLI package version (also `version` as first positional). Does **not** create config.                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `--config <path>`          | Parsed but **unused** by `main.ts` today; configuration is read from `<repoRoot>/.easyspecs/config.json`.                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `--remote-harness`         | **Force remote agent execution** for this CLI process: agent runs use **run-background-agent** + completion ledger polling (POST **`harness`** from **`agentHarness.kind`**), even when `easyspecs.remoteHarness.useRemoteOpenCodeExecution` is `false` in `config.json` (**SRS-1** **R1.4**, **AC11**). Subject to harness validation at command entry (**R1.7**).                                                                                                                                                          |
| `--cloud-harness`          | **Deprecated alias** for `--remote-harness` (one stderr warning per process). Prefer `--remote-harness`. (**SRS-1** **R1.4a**)                                                                                                                                                                                                                                                                                                                                                                                               |
| `--no-remote-harness`      | **Force local harness spawn** on the CLI host (e.g. `claude` or `opencode` per **`agentHarness.kind`**) even when `useRemoteOpenCodeExecution` is `true` in `config.json`. Alias: `--local-opencode-only` (**SRS-1** **R1.5**, **AC12**).                                                                                                                                                                                                                                                                                    |


Environment (stderr styling): `**NO_COLOR`** (any non-empty value) disables ANSI colors. `**FORCE_COLOR**` (any non-empty value) allows colors even when stderr is not a TTY. With `**--json**`, human stderr is normally suppressed; `**--json --verbose**` prints plain stderr **without** ANSI. In human mode, a **blank line** may be printed when the diagnostic **phase** changes (e.g. `**[factory]`** → `**[pool]**` / `**[workstation:listFeatures]**`). See **README** (Terminal diagnostics).

### Reports under `.easyspecs/logs/`

| Report | Filename | Format | Update model |
| ------ | -------- | ------ | -------------- |
| **Run log** (**SRS-76**) | `run-log-<UTC-start>.txt` | Plain text (stderr mirror) | **Append-only** |
| **Perf report** (**SRS-118**) | `perf-report-<UTC-start>.md` | Markdown table | **Full rewrite** — one row per pool work item, updated in place |
| **Pool pressure report** (**SRS-119**) | `pool-pressure-<UTC-start>.md` | Markdown table | **Full rewrite** — one row per step-down / step-up event |

#### Run logs (**SRS-76**)

Each CLI process (except `**help**` / `**version**`) mirrors human stderr to `**<repoRoot>/.easyspecs/logs/run-log-<UTC-start>.txt**` with `**fsync**` after every line. Files are plain text (no ANSI). First mirrored diagnostic after the header: `**[log] run log: <abs path>**`. Stdout and suppressed `**--json**` stderr are not mirrored. Argv passwords are redacted in the file header. See **[SRS-76](../../.easyspecs/docs/srs/srs-76.md)** / **README** (Run logs).

#### Performance reports (**SRS-118**)

Each CLI process that opens a run log also writes `**<repoRoot>/.easyspecs/logs/perf-report-<UTC-start>.md**`. The file is a **GitHub-flavoured Markdown** table: **one row per workstation-pool work item** (`workItemId`), rewritten on disk when counters, outcome, or elapsed time change. First diagnostic: `**[log] perf report: <abs path>**`. While rows are **running**, elapsed time refreshes at most every **2 s** (immediate flush on retry/spawn/outcome changes). v1 scope: **`drainWorkstationPool`** only (synthesis / diagnosis parallel remediation). Example: [`.easyspecs/docs/examples/perf-report-example.md`](./.easyspecs/docs/examples/perf-report-example.md). See **[SRS-118](./.easyspecs/docs/srs/srs-118.md)** / **[USER-MANUAL-SRS-118](./.easyspecs/docs/srs/USER-MANUAL-SRS-118.md)**.

#### Pool pressure reports (**SRS-119**)

Each CLI process that opens a run log also writes `**<repoRoot>/.easyspecs/logs/pool-pressure-<UTC-start>.md**`. The file is a **Markdown event table**: **one row per adaptive pool step-down or step-up**, with **Document**, **Workstation**, and **Step** columns (same semantics as the perf report). First diagnostic: `**[log] pool pressure report: <abs path>**`. Stderr **`[pool] duration pressure …`** lines are unchanged. Scope: all **`createAdaptivePoolRuntime`** / **`runAdaptiveConcurrentPool`** paths (synthesis pool, zero-reference remediation, ACE auto-learn). Example: [`.easyspecs/docs/examples/pool-pressure-example.md`](./.easyspecs/docs/examples/pool-pressure-example.md). See **[SRS-119](./.easyspecs/docs/srs/srs-119.md)** / **[USER-MANUAL-SRS-119](./.easyspecs/docs/srs/USER-MANUAL-SRS-119.md)**.

---

## Workstation host inventory (**SRS-75** / **SRS-62**)

`**easyspecs-cli workstation info**` prints the CPU count used for the **SRS-75 §4** heuristic (restored **SRS-64 §4.1**), total installed RAM, **`N_sys`** (JSON key **`maxSystemConcurrentAiWorkstations`**), and **`suggestedMaxConcurrentAi`** (= **`N_sys`**, advisory only — **SRS-111**). **macOS** (`darwin`) and **Linux** only; on other platforms the CLI exits **non-zero**, writes a short message to stderr, and does **not** print JSON on stdout (even if **`--json`** was passed).

**Formula (SRS-75 §4 / SRS-64 §4.1):** `N_cpu_float(P) = 15 + 8.5×(P−1)` for **`P ≤ 11`**, else **`100 + 8.5×(P−11)`**; `N_float = N_cpu_float(P) × min(1, R_GiB / (4×P))`; **`N_sys = max(1, round(N_float))`**. **Worked examples:** **`P=4, R=16 GiB → N_sys = 41`**; **`P=11, R=44 GiB → N_sys = 100`**. The **SRS-69** compact form `min(P, R/8)` is **withdrawn**. Only **`easyspecs.workstations.maxConcurrentAi`** caps concurrent AI workstations (**SRS-111**); use **`N_sys`** as a capacity hint, not a binding limit. Runtime **SRS-94** duration pressure may lower **`adaptiveMaxConcurrentAi`** below the operator static cap. See live signals via **`[host-pool]`** / **`[host-pool-rec]`** (**SRS-68**).

Use the **global** **`--json`** flag (before the subcommand) for one parseable summary line. JSON includes **`totalMemBytes`**, **`platform`**, **`cpusForCap`**, **`cpusForCapSource`**, **`physicalCpus`** (integer when physical cores are known, else **`null`**), **`maxSystemConcurrentAiWorkstations`**, **`suggestedMaxConcurrentAi`**, and deprecated **`hostHardMaxConcurrentAi`** (same value as **`suggestedMaxConcurrentAi`**).

This command does **not** read or create **`.easyspecs/config.json`**.

### OpenCode **`run`** diagnostics (**SRS-65**)

When Analysis (or other callers) passes a **`diagnosticLog`** sink into the extension host’s **`runOpenCodeAgent`** helper, each successful **`opencode run`** **spawn** with a real child **PID** also emits:

- **`scope=host`** — machine-wide **RAM** (**total** / **free** / **used** bytes) plus **1-minute load average**, logical **CPU** count, and **load / CPUs** (always when **`diagnosticLog`** is set — **[SRS-65](../../.easyspecs/docs/srs/srs-65.md)**).
- **`scope=child`** — once per run: **child CPU time** (milliseconds) and **RSS** bytes for that **PID** — **opt-in** via global **`--host-metrics`** (**[SRS-117](../../.easyspecs/docs/srs/srs-117.md)**; default **off**).

**Global flag:** **`easyspecs-cli --host-metrics analysis`** (or any subcommand) enables **`scope=child`** lines only; **`scope=host`**, **`[host-pool]`**, and **`[pool] duration pressure …`** are unchanged.

Tune concurrent agents with **`easyspecs.workstations.maxConcurrentAi`** and **`easyspecs-cli workstation info`** (**SRS-111** / **SRS-75**); **`[host-pool]`** lines help interpret **live** pressure alongside that configuration.

### Runtime adaptive pool pressure opt-out (**SRS-80**)

| Control                                                                                                                                                 | What it changes                                                                                                                                      |
| ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`easyspecs.workstations.maxConcurrentAi`**                                                                                                            | Sole operator ceiling for concurrent AI workstations (floor **1**, no product max — **SRS-111**).                                                    |
| **`easyspecs.workstations.enableAdaptivePoolPressure: false`** (legacy **`enableCpuAdaptivePoolPressure`**) or global **`--no-adaptive-pool-pressure`** | Disables runtime lowering of **`adaptiveMaxConcurrentAi`** after slow workstation completions (**SRS-94**). **`[host-pool]`** diagnostics still run. |
| Global **`--host-metrics`** (**SRS-117**) | Enables per-child **`scope=child`** RSS/CPU lines at agent exit; default **off**. Does **not** affect **`scope=host`** or **`[host-pool]`**. |
| **`easyspecs.workstations.slowWorkstationThresholdMs`**                                                                                                 | Wall-clock duration (default **420000** ms / 7 min) after which a completed pool item triggers step-down.                                            |
| **`easyspecs.workstations.recoveryQuietAfterStepDownMs`**                                                                                               | Quiet period (default **900000** ms / 15 min) before step-up by 1.                                                                                   |
| **`easyspecs.workstations.adaptivePoolPressureRecoveryTickMs`**                                                                                         | Recovery evaluation interval (default **30000** ms).                                                                                                 |

**CLI:** pass **`--no-adaptive-pool-pressure`** before the subcommand (e.g. `easyspecs-cli --no-adaptive-pool-pressure analysis`). Emits one stderr line for that process. **Precedence:** CLI flag **>** `config.json` / VS Code setting **>** default (**on**).

### Preferred agent harness (**SRS-91**)

Set which vendor CLI runs factory/synthesis agent steps via **`easyspecs.agentHarness`** in **`.easyspecs/config.json`**. **`config init`** seeds **`agentHarness`** with **`kind: opencode`**, **`skipCredentialsCheck: true`**, and **`useGraphify: true`** (**SRS-130** / **SRS-131**). When **`agentHarness.kind`** is **`claude-code`** and **`useGraphify`** is **`true`** (default), the CLI ensures Graphify artefacts on **`<analysis-checkout>`**: **`.claude/settings.json`** (create-only, never overwrites), **`CLAUDE.md`**, and **`AGENTS.md`** (create or append a **`## graphify`** section when missing; never edits an existing section). Set **`useGraphify: false`** to disable auto-provision. When **`agentHarness`** is **absent** in older files, **`kind`** still defaults to **`opencode`**; legacy **`openCode`** / **`openCodeRuntime`** remain supported for executable and pool settings.

```json
{
  "easyspecs": {
    "agentHarness": {
      "kind": "claude-code",
      "executable": "claude",
      "skipCredentialsCheck": true
    }
  }
}
```

| `kind`                                          | Executable default | Notes                                                                                                                                                                                                                                                          |
| ----------------------------------------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`opencode`**                                  | `opencode`         | Default; same as pre-SRS-91 behaviour.                                                                                                                                                                                                                         |
| **`claude-code`**                               | `claude`           | **Local & remote (**SRS-106**, **SRS-108**, **SRS-135**):** **`claude -p`** carries an explicit **task prompt file path** (read/follow); full §3.5 body via **`--append-system-prompt-file`** (local and remote). OpenCode-shaped orchestration still uses **`run --agent … {taskUserInstruction} -f`**. The short **`{taskUserInstruction}`** forbids misleading “attached/uploaded prompt” **static prose** only — **artefact ids in prompt paths** (e.g. `s3-bucket-policy-attachment`) are allowed. Materialize → **`.claude/agents/`**. Requires **`ANTHROPIC_API_KEY`** unless **`skipCredentialsCheck: true`**. |
| **`gemini-cli`**, **`cursor-cli`**, **`codex`** | (planned)          | Not implemented in v1 Step 1.                                                                                                                                                                                                                                  |

**Remote transport (**SRS-1**, **SRS-101**):** use **`easyspecs.remoteHarness`** for execution mode and worker HTTP settings only. POST body **`harness`** is **always** derived from **`agentHarness.kind`** (e.g. **`claude-code`** → **`claude`** on the worker). There is **no** separate **`remoteHarness.harness`** config key. **`doctor --readiness --json`** reports **`agentHarnessKind`**, **`postHarness`**, and **`harnessSource`** (always **`agentKind`**).

**Persist harness from the CLI (**SRS-99**):** **`easyspecs-cli config set-harness <kind>`** — see **[Config commands (SRS-99)](#config-commands-srs-99)**.

### Remote agent harness (**SRS-1**)

Delegate agent runs to a remote worker via **`POST …/run-background-agent`**, then wait for a completion file **`{completionLedgerDir}/{runId}.json`** on **shared disk** (same repo tree as the CLI worktree — not HTTP long-poll). POST body **`harness`** follows **`agentHarness.kind`** (e.g. **`claude-code`** → **`claude`**, **`opencode`** → **`opencode`**). Config flag **`useRemoteOpenCodeExecution`** is legacy naming for “remote vs local execution”; it applies to **all** harness kinds. Normative spec: compound-knowledge **`srs-1-remote-harness.md`**; implementation plan: **[PLAN-SRS-1](../../.easyspecs/docs/srs/PLAN-SRS-1.md)**.

| Control                                                        | Effect                                                                                                                                                |
| -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| **`easyspecs.remoteHarness.useRemoteOpenCodeExecution: true`** | Remote harness for all agent runs in this process (default **`false`**). Uses the active **`agentHarness.kind`** for POST **`harness`** (**SRS-91**). |
| **`--remote-harness`**                                         | **Force remote on** for this process (overrides config **`false`**).                                                                                  |
| **`--cloud-harness`**                                          | Deprecated alias for **`--remote-harness`**.                                                                                                          |
| **`--no-remote-harness`**                                      | **Force local** harness spawn on the CLI host (overrides config **`true`**).                                                                          |

**Precedence:** **`--no-remote-harness`** **>** **`--remote-harness`** / **`--cloud-harness`** **>** **`useRemoteOpenCodeExecution`** (config) **>** default **`false`**.

**Local harness install (**SRS-105**):** When **`effectiveUseRemote`** is **true** (config **`useRemoteOpenCodeExecution`**, global **`--remote-harness`**, or **`analysis`/`config init --remote`** with no **`--no-remote-harness`** override), factory readiness **does not** require OpenCode or Claude Code on the **CLI host** PATH — the harness runs on the **worker**. The CLI still validates remote harness config (**`baseUrl`**, **`requesterId`**, ledger dir, **`SERVICE_API_KEY`**, **`AI_MODEL_PRESET_ID`**) and worker support for POST **`harness`**. **`doctor --readiness`** may still report **`installed=false`** locally; that is informational.

**Examples:**

```bash
# One-off remote run without editing config.json
easyspecs-cli --remote-harness analysis

# Force local for one run while config has remote enabled
easyspecs-cli --no-remote-harness analysis
```

**Affects (when effective):** all commands that invoke **`runOpenCodeAgent`** — including **`analysis`**, **`run synthesis`**, **`update context`**, **`context drift`**, ACE offline learn, and remediation chains — with **no** per-command opt-in in v1 (**R9.2**).

**Config block** `easyspecs.remoteHarness` (see table below): `useRemoteOpenCodeExecution`, `baseUrl`, `requesterId`, `remoteOpenCodeWorkDir`, optional `workDirByLocalRoot`, `completionLedgerDir` (default `.easyspecs/harness-remote-run`), `submitTimeoutMs`, `completionPollIntervalMs`, `completionLedgerLogIntervalMs`. Legacy config block **`easyspecs.cloudHarness`** is merged as an alias when **`remoteHarness`** is absent (**R1.3**).

**Persist remote harness fields from the CLI (**SRS-99**):** **`config set-execution`**, **`config set-remote-*`**, and optional flags on **`config init`** — see **[Config commands (SRS-99)](#config-commands-srs-99)**. Do not confuse **`config init --remote`** (writes **`useRemoteOpenCodeExecution: true`** to **`config.json`**) with the **global** **`--remote-harness`** flag (this process only).

**Harness credentials (not in `config.json`):**

| Variable                 | Role                                                                                                   |
| ------------------------ | ------------------------------------------------------------------------------------------------------ |
| **`SERVICE_API_KEY`**    | HTTP header **`X-API-Key`** (content-generation M2M). Required when remote harness is effective.       |
| **`AI_MODEL_PRESET_ID`** | POST body **`ai_model_preset_id`** (worker DB preset UUID). Required when remote harness is effective. |

**Resolution order** (CLI workspace = `--cwd` / repo root): **`process.env`** → **`<workspace>/.env`**. If both are missing, remote factory commands fail fast (exit **3**). Shell exports win over `.env` when both are set.

**One-shot env for this CLI process only (**SRS-102**):** Repeatable tail flag **`--set <key> <value>`** on **`analysis`** and **`config init`** (may be interleaved with other tail flags). Allowed keys: **`SERVICE_API_KEY`**, **`AI_MODEL_PRESET_ID`**. Values are applied to **`process.env`** before remote harness validation; they are **not** written to **`config.json`**. **`--json`** reports **`envSet: { keys: [...], applied: true }`** without secret values.

```bash
easyspecs-cli analysis set-harness claude-code --remote --noworktree \
  --remote-base-url https://api.staging.easyspecs.ai \
  --remote-requester-id <content-application-uuid> \
  --set SERVICE_API_KEY "$SERVICE_API_KEY" \
  --set AI_MODEL_PRESET_ID "$AI_MODEL_PRESET_ID"
```

**Diagnostics:** remote runs log with prefix **`[remote-harness]`**; secrets are not printed (**AC14**). On enable, stderr shows **`remote agent execution enabled`** with **`agentHarness.kind`** and POST **`harness`**. For **`claude-code`** remote runs, when a prompt file is present the CLI logs **`[remote-harness] prompt_file=--append-system-prompt-file <relative-path>`** (**SRS-106**). **SRS-65** host/child spawn diagnostics apply only to **local** harness spawns.

**Submit failures (**SRS-92**, **SRS-98**):** When **`POST …/run-background-agent`** fails (any non-2xx, network error, or invalid 2xx), the CLI emits an indented **`remote-harness:`** tree to **stderr** (when not **`--json`**, or with **`--verbose`**) and to the active **text run log** (**.easyspecs/logs/run-log-*.txt`**). Sections: **`meta`** (UTC + timezone, consecutive failure count), **`readiness`** (embedded **`doctor --readiness`** lines + **`remoteHarness`** JSON), **`request`** (headers, pretty-printed body, **`curlCommand`** with **masked** **`X-API-Key`** only), **`response`** (status, headers, body). **`easyspecs.remoteHarness.maxConsecutiveSubmitFailures`** (default **`3`**) trips a **circuit breaker**: after that many **consecutive** submit failures the factory/synthesis pool **stops** (no further **`fetch`**), **`AbortController`** aborts in-flight work, and analysis exits with **exit 3**. A successful submit resets the counter.

**Completion ledger polling (**SRS-93**, **SRS-103**, **SRS-104**):** While waiting for **`{completionLedgerDir}/{runId}.json`**, the CLI logs an indented **`remote-harness-ledger:`** tree whenever it **reads** the file (via **`diagnosticLog`** → stderr + text run log). Each block includes **`outcome`** (`non_terminal`, `terminal_accepted`, `schema_rejected`, `run_id_mismatch`, `parse_error`, `wait_timeout`, …), **`ledger`** paths, **`elapsedMs`**, and a **`record`** summary (`status`, `sessionTitle`, `errorsCount` — not full error dumps except one truncated line on terminal success with errors). Repeated **`running`** snapshots are throttled to once per **`completionLedgerLogIntervalMs`** (default **30000**; **0** disables re-logs). With **`easyspecs.factory.debug: true`**, optional **`file_missing`** heartbeats use the same interval when no ledger file exists yet, and an **`extras:`** subsection appears for non-normative top-level keys (JSON-truncated values; excludes normative record fields). Optional **`tokenUsage`** on terminal records may include **`size`** (image tier label) and **`is_trial`** (trial/quota flag; omitted means false) per **SRS-1 R13.15** — validation only; pass/fail is unchanged.

**Registration:** global flags are parsed in `[cliProgram.ts](../../src/cli/cliProgram.ts)` and merged in `[cliSettings.ts](../../src/cli/cliSettings.ts)` (**PLAN-SRS-1** **M2**).

### Workstation pool host pressure (**SRS-68**)

While the synthesis / remediation **workstation pool** has **≥ 1** in-flight OpenCode run, **`easyspecs-cli`** (human **`diagnosticLog`** / stderr path) emits a **pair** of lines every **45 seconds** (not configurable in v1 — **no** env/flag to disable):

1. **`[host-pool]`** — **`activeWorkstations`**, **`staticMaxConcurrentAi`**, **`adaptiveMaxConcurrentAi`**, host RAM bytes, **`load1`**, **`load1PerCpu`**, optional Linux **`swapTotalB` / `swapFreeB`**, and four hints: **`hintScheduler`**, **`hintMemory`**, **`hintLimits`**, **`hintIo`** each **`OK`**, **`KO`**, or **`UNK`**. Hints are **heuristic** (not proof of root cause).
2. **`[host-pool-rec]`** — **`kind=none`** when no hint is **`KO`**; otherwise **`kind=advice text=…`** with short guidance tuned for **dedicated workers** (e.g. **Celery** running only this task): lower **`easyspecs.workstations.maxConcurrentAi`**, worker memory/CPU limits, **`ulimit`**, disk checks — **not** “close other desktop apps”. **`text=`** is capped in length and may truncate with **` …`**.

See **[SRS-68](../../.easyspecs/docs/srs/srs-68.md)** and **`USER-MANUAL-SRS-68.md`**.

---

## Shared configuration (SRS-43)

### File: `<repoRoot>/.easyspecs/config.json`

Canonical JSON created on first need (except `**help`**, `**version**`, `**workstation info**`, and `**doctor**` — no auto-create). Invalid JSON fails with a clear path (**R6**). Merged documents are also validated against **SRS-46** JSON Schema (`[srs-46-config.schema.json](../../.easyspecs/docs/srs/srs-46-config.schema.json)`) — see `[USER-MANUAL-SRS-46.md](../../.easyspecs/docs/srs/USER-MANUAL-SRS-46.md)`. If `**config.json`** is missing and `**.easyspecs/cli.json**` exists, settings are imported once (**§6 M2**) into the new file.

Merge uses **this file + global CLI flags** for most settings — no general `EASYSPECS_*` / `VITE_*` product overrides on **`easyspecs-cli`** (**SRS-43 R34**), except **`EASYSPECS_ENVIRONMENT`** for API origin resolution (**SRS-99** — see below).

**Workspace layout (SRS-48):** For `**analysis`**, `**update context**`, `**diagnose**`, `**upload**`, `**download context**`, and `**context link-graph**`, the CLI creates the minimal `**.easyspecs/**` directories (`**docs/srs**`, `**logs**`, `**context**`) under the relevant filesystem root when they are missing. You do not need to copy a EasySpecs workspace tree before first use.


| Path                                                       | Purpose                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| ---------------------------------------------------------- |   |
| `schemaVersion`                                            | Integer (e.g. `2`) for future migrations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `easyspecs.deploymentEnvironment`                          | `production` or `staging` (default `production`). Used with built-in System Manager URLs when `apiBaseUrl` is empty.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `easyspecs.apiBaseUrl`                                     | Explicit System Manager origin; wins after `--api-base-url`. Generated defaults use `**https://api.easyspecs.ai**` (production). Use `**""**` only if you want the built-in URL from `**deploymentEnvironment**` / `**--environment**` instead.                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `easyspecs.easyspecsProjectId`                             | Optional EasySpecs **project** id (Content application UUID). **Only** store for uploads / Sync — set via `**Send context`**, `**Changes → Sync**`, `**config set-project-id**`, or `**easyspecs-cli**`. **`upload context`** / **`upload republish`** read **only** this field (merged `**easyspecs.applicationId`** legacy key maps here).                                                                                                                                                                                                                                                                                                                                                    |
| `easyspecs.defaultGitRemoteUrl`                            | Optional primary `**git remote**` URL (HTTPS or SSH) for documentation / tooling; analysis still uses the live repo. Related: `**index-application-context.json**` `repository`, `**.easyspecs/analysis-worktree.json**` `repositoryRoot`.                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `easyspecs.cliSessionPath`                                 | Repo-relative or absolute path to `**cli-session.json**` written by `**auth login**`. Empty `**""**` → `**~/.easyspecs/cli-session.json**`. Effective path: global `**--session-path**` (if passed) → `**cliSessionPath**` → default home path (`[cliSession.ts](../../src/cli/cliSession.ts)`).                                                                                                                                                                                                                                                                                                                                                                                                |
| `easyspecs.openCode`                                       | `executable`, `skipCredentialsCheck` (legacy shortcut; see also `openCodeRuntime`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `easyspecs.analysis.*`                                     | Remaining analysis flags (e.g. `**promoteContextToWorkspace**`, ACE toggles). **SRS-46 cache** now lives under `**easyspecs.factory.cloudContextAnalyzed`** / `**…At**` (canonical); legacy `**easyspecs.analysis.cloudContext***` still read for one cycle.                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `easyspecs.factory.*`                                      | Factory: `**debug**` (stderr phase transitions; legacy `**easyspecs.macro.debug**`), backoff (`initialDelayMs`, `multiplier`, `maxDelayMs`), `maxOuterIterationsPerPipeline`, `maxPingPongCycles`, `synthesisRemediationShareBackoff`, `**skipRemediationPipeline**` (default **`true`** from **`config init`** — skip zero-reference remediation on **`analysis`** unless **`--enable-remediation-pipeline`** or set **`false`**), cloud-context-analyzed cache. Legacy `**easyspecs.orchestration.***` aliases per SRS-53.                                                                                                                                                                                                                                 |
| `easyspecs.jsonProgrammaticRepair`                         | **SRS-138:** when **`true`** (default), schema-gated JSON validators attempt **`jsonrepair`** self-heal before agent repair; writes validation reports under **`.easyspecs/logs/{stem}-validation/`**. Set **`false`** for pre-SRS-138 behaviour. |
| `easyspecs.workstations.*`                                 | Harness argv template, **`harnessTimeout`** (per-run timeout ms for local OpenCode, Claude Code, and remote ledger wait — default **900000** / 15 min from **`config init`**), **`enableAdaptivePoolPressure`** (default **false** from **`config init`**), repair attempts, pool width, coordination lock timeout. Legacy **`openCodeTimeoutMs`**, **`openCodeRuntime.run.timeoutMs`**, **`easyspecs.analysis.openCodeTest*`** / **`listJsonSchemaRepairAttempts`** / **`markdown*`** / **`maxConcurrentOpenCodeAgents`** / **`coordinationListLockTimeoutMs`** aliases. |
| `easyspecs.remoteHarness.*`                                | **SRS-1:** `useRemoteOpenCodeExecution` (default `false`), `baseUrl`, `requesterId`, `remoteOpenCodeWorkDir`, `workDirByLocalRoot`, `completionLedgerDir`, `submitTimeoutMs`, `completionPollIntervalMs`, `completionLedgerLogIntervalMs` (**SRS-93**, default `30000`, `0` = no non-terminal re-log). **SRS-98:** `maxConsecutiveSubmitFailures` (default `3`, min `1`). **SRS-101:** POST **`harness`** comes from **`agentHarness.kind`** only — not from `remoteHarness`. Deprecated keys: `enabled` (alias for `useRemoteOpenCodeExecution`), block `easyspecs.cloudHarness` (alias for `remoteHarness`). Legacy `remoteHarness.harness` / `harnessOverride` are stripped on config write. |
| `easyspecs.pipelines.upload.useBatch`                      | Upload batching preference. Legacy `**easyspecs.analysis.uploadUseBatch**`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `easyspecs.orchestration.*`                                | **Deprecated** — use `**easyspecs.factory.*`** (SRS-53); still merged when present.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `easyspecs.openCodeRuntime`                                | Annex-aligned block: `providers`, `run`, `coordinationRepairs`, `pool`, `projectConfigOverlay`. **SRS-139:** `projectLocalData` (default **`true`** on **`config init`**), `projectLocalDataDir` (default **`.easyspecs`**), `artefactFileLocalData` (default **`true`**) — one **`opencode.db` per output artefact file under **`.easyspecs/opencode/artefacts/<runId>/<basename>/`**; global auth via **`OPENCODE_AUTH_PATH`** when repo **`.opencode/auth.json`** absent. **`doctor --json`** reports **`openCodeDataMode`**, **`openCodeDataPath`**. Reset: delete **`.easyspecs/opencode/`**. See `[srs-43-opencode.schema.json](../../.easyspecs/docs/srs/srs-43-opencode.schema.json)`. |
| `easyspecs.diagnose.zeroReference.maxPercentNonReferenced` | `number`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | `**null**`. When `**null**`, `diagnose reference-coverage` does not fail on percent alone (**SRS-44**). |
| `easyspecs.diagnose.coordinationDuplicates.strict`         | Boolean (default `**true`**). When `**true**`, duplicate/orphan rows fail `diagnose coordination-duplicates`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `easyspecs.upload.contextDirectory`                        | Repo-relative or absolute override for `**upload republish**` context dir; empty `**""**` → automatic (analysis snapshot).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `easyspecs.upload.fetchContextAnalyzedInCloud`             | Boolean, default `**true**`. When `**true**`, after a fully successful `**upload context**` / `**upload republish**`, the CLI performs `**GET /api/content/application/:id**` and emits `**contextAnalyzedInCloud**` / `**contextAnalyzedInCloudAt**` on `**--json**`; updates `**easyspecs.factory.cloudContext***` cache (legacy `**easyspecs.analysis.cloudContext***`). When `**false**`, skips that GET and omits those JSON keys.                                                                                                                                                                                                                                                         |
| `easyspecs.upload.contextAnalyzedStatusTimeoutMs`          | Optional positive integer (ms). GET timeout; default **15000** if omitted.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `easyspecs.cli.bundledResourcesRoot`                       | Optional path to bundled `**resources/`** (folder containing `**harness-agents/**`). Empty → infer next to the CLI package.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `easyspecs.auth.ciLogin.email` / `password`                | Used only with `**--ci**` when `**--email` / `--password**` are omitted (**SRS-44**). Prefer CI-generated `**config.json`**; do not commit secrets.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |


### Resolved System Manager URL (`easyspecs-cli`)

**Order (**SRS-43**, **SRS-99**):**

1. Global **`--api-base-url`**
2. Non-empty **`easyspecs.apiBaseUrl`** in **`config.json`**
3. Valid **`process.env.EASYSPECS_ENVIRONMENT`** → **`staging`** → `https://api.staging.easyspecs.ai`, **`production`** → `https://api.easyspecs.ai` (trimmed, case-insensitive)
4. Global **`--environment`** / **`--env`**
5. **`easyspecs.deploymentEnvironment`** in **`config.json`**
6. Default **`production`** → `https://api.easyspecs.ai`

When **`easyspecs.remoteHarness.baseUrl`** is empty in config, remote harness HTTP uses the **same** resolved origin as above (**SRS-99**).

Built-in hosts ship in [`src/shared/api/easyspecsBuiltInApiUrls.ts`](src/shared/api/easyspecsBuiltInApiUrls.ts) (staging and production use the **`api.*.easyspecs.ai`** hosts).

**Examples:**

```bash
export EASYSPECS_ENVIRONMENT=staging
easyspecs-cli doctor --readiness   # resolves apiBaseUrl to staging when config URLs are empty

easyspecs-cli config set-environment staging   # persists deploymentEnvironment + apiBaseUrl + remoteHarness.baseUrl
```

### `EASYSPECS_ENVIRONMENT` (**SRS-99**)

| Value            | Effect at runtime (steps 3–6)      | Persisted by **`config set-environment`** / **`config init`** (when env is set at init)              |
| ---------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------- |
| **`staging`**    | `https://api.staging.easyspecs.ai` | Sets **`deploymentEnvironment`**, **`apiBaseUrl`**, and **`remoteHarness.baseUrl`** to the same host |
| **`production`** | `https://api.easyspecs.ai`         | Same for production                                                                                  |

Invalid non-empty values: one stderr warning per process; env step is skipped. Explicit non-empty **`apiBaseUrl`** / **`remoteHarness.baseUrl`** in **`config.json`** still win over **`EASYSPECS_ENVIRONMENT`** at runtime.

### No other EasySpecs `EASYSPECS_*` configuration env (**SRS-44** / **SRS-99**)

**`easyspecs-cli`** does **not** read **`EASYSPECS_*`** (except **`EASYSPECS_ENVIRONMENT`** above), **`VITE_*`**, or **`.env`** for product settings — use **`.easyspecs/config.json`** (+ global CLI flags and the config subcommands below). OS-level variables such as **`PATH`** still apply for finding **`node`**, **`opencode`**, **`git`**.

Remote harness credentials remain env-only: **`SERVICE_API_KEY`**, **`AI_MODEL_PRESET_ID`** (workspace **`.env`** or shell).

Provider API keys for OpenCode runs should live under `**easyspecs.openCodeRuntime.providers**` in `**config.json**`; they are injected into the **child** OpenCode process (**R13**) and redacted in `**doctor --inspect-config`** (**R17**).

---

## Diagnose / context flags

Used after `diagnose <subcommand>` or `**context link-graph`**:


| Flag         | Values                    | Meaning                                                                                                                                             |
| ------------ | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--root`     | `workspace` or `worktree` | Whether paths resolve against the workspace repo root or an analysis checkout (`[parseTailFlags](../../src/cli/parseTailFlags.ts)`).                |
| `--worktree` | path                      | Analysis git checkout (required for `--root worktree` when no path embedded in `--root` flow; combined with explicit `--worktree` — see `main.ts`). |


---

## Reference coverage resolver (SRS-128)

Normative spec: **[srs-128.md](.easyspecs/docs/srs/srs-128.md)**. Plan: **[PLAN-SRS-128.md](.easyspecs/docs/srs/PLAN-SRS-128.md)**.

A **100%-programmatic** stage that runs around each **`review*List`** synthesis step. It merges two deterministic generators — **(B)** per-row precision candidates and **(C)** a global file→row assignment — then:

- **auto-attaches** high-confidence `sourceReferences` to rows (no AI), SRS-114-validated and recorded in a provenance ledger;
- writes a **Tier-B `.candidates.txt` sidecar** consumed by the existing review agent;
- defers the rest to **SRS-30**.

Auto-attach is post-review (the AI cannot drop deterministic cites); the sidecar is written pre-review. All artefacts live under `<contextDir>/reference-coverage-resolver/`.

### Configuration (`easyspecs.synthesis.referenceCoverageResolver`)

| Key | Type | Default | Meaning |
| --- | ---- | ------- | ------- |
| `enabled` | `boolean` | `true` | Master switch. `false` → pre-SRS-128 behaviour (no index, sidecar, prompt block, auto-attach, or ledger). |
| `mode` | `"conservative"\|"balanced"\|"aggressive"` | `balanced` | Signal subset, per-row caps, and tier thresholds. |
| `autoWrite` | `"off"\|"config_gated"\|"on"` | `config_gated` | `off` = propose-only (sidecar only); `config_gated` = auto-write only when the precision audit passes `minAutoWritePrecision`; `on` = auto-write whenever Tier-A holds (still SRS-114-gated). |
| `maxHintsPerRow` | `number` | mode default | Per-row candidate cap override. |
| `minAutoWritePrecision` | `number` | `0.9` | Auto-write disabled below this measured precision. |
| `auditHoldoutFraction` | `number` | `0.15` | Hold-out fraction for the precision self-audit. |
| `writeDebugArtefact` | `boolean` | `false` | Write full resolution JSON to `.easyspecs/logs/`. |
| `maxImportScanBytesPerFile` | `number` | `65536` | Per-file read bound for the import-edge scan. |

### Configuration (`easyspecs.synthesis.nodeGeneration`) — SRS-141

Selective node-depth generation. Each key is a knowledge-node **index `type`** with one boolean `autoGenerate`.

| Aspect | Rule |
| ------ | ---- |
| **Scope-root types** | `Feature`, `View`, `Service`, `Entity`, `Tool`, `Infrastructure`, `QA`. |
| **Child types** | `UseCase`, `Scenario`, `Interaction`, `Method`, `Field`, `Relationship`, `Component`, `TestCase`. |
| **`config init` default** | Shallow-first — every scope root `autoGenerate: true`, every child `autoGenerate: false`. |
| **Block wholly absent** | Legacy / full depth — every type behaves as `autoGenerate: true`. |
| **Type key missing** | Defaults to `autoGenerate: true`. |
| **`autoGenerate: false`** | Synthesis does not enqueue that type’s coordination/markdown work, and `expandAfterSuccess` does not fan out into it. Root scope lists kept as cross-scope dependency anchors when needed (e.g. `listFeatures` for `listQa`). |
| **Always generated** | `project.md`, `architecture.md`, `repo-surface-scan.json` — never gated by this policy (SRS-141 §5.2). |
| **Remediation** | Zero-reference remediation never resurrects a disabled type: a file routed to a disabled scope is ignored and counted as resolved (SRS-141 §5.6). |

```jsonc
{
  "easyspecs": {
    "synthesis": {
      "nodeGeneration": {
        "Feature":  { "autoGenerate": true },   // FE-*
        "UseCase":  { "autoGenerate": false },  //   FE-*_UC-*
        "Scenario": { "autoGenerate": false }   //     FE-*_UC-*_SC-*
        // … remaining scope roots true, remaining children false
      }
    }
  }
}
```

Deepen later without editing config: `context expand <code>` / `context expand-type <type>` (above).

### `reference-coverage strip`

Removes **exactly** the references recorded in resolver ledgers (reverse of auto-attach), matched by `rowCode` + `path` + range, then deletes the consumed ledger so a re-run is a no-op. Manual and AI-added references are never touched.

```bash
easyspecs-cli reference-coverage strip                 # all resolver ledgers under the worktree
easyspecs-cli reference-coverage strip --list features-list   # single list stem
easyspecs-cli --json reference-coverage strip          # machine-readable summary
```

Accepts the same `--root` / `--worktree` analysis-checkout flags as `diagnose`.

---

## Config commands (SRS-99)

Normative spec: **[srs-99.md](.easyspecs/docs/srs/srs-99.md)**. Plan: **[PLAN-SRS-99.md](.easyspecs/docs/srs/PLAN-SRS-99.md)**.

Targeted writers patch **`.easyspecs/config.json`** via **`updateEasyspecsConfig`** (SRS-46 validated, atomic write). Missing **`config.json`** is bootstrapped like **`config set-project-id`**. Use **`config apply --file`** for full-document replacement.

### One-shot onboarding (`config init`)

Bootstrap **`config.json`** and set harness, local/remote execution, and/or **`remoteHarness`** fields in **one** command (single write). This is **not** three chained subcommands.

**Keyword form:**

```bash
easyspecs-cli config init set-harness claude-code --remote \
  --remote-base-url https://api.staging.easyspecs.ai \
  --remote-requester-id <content-application-uuid> \
  --remote-ledger-dir .easyspecs/harness-remote-run \
  --remote-submit-timeout 30000 \
  --remote-poll-interval 2000 \
  --remote-ledger-log-interval 30000
```

**Equivalent flag form** (no `set-harness` keyword):

```bash
easyspecs-cli config init --harness claude-code --remote \
  --remote-requester-id <content-application-uuid>
```

| `config init` flag                      | Writes to `config.json`                                                       |
| --------------------------------------- | ----------------------------------------------------------------------------- |
| **`--overwrite`**                       | Replace existing file with bootstrap defaults, then apply other options       |
| **`--harness <kind>`**                  | **`easyspecs.agentHarness.kind`** (same as **`set-harness <kind>`**)          |
| **`set-harness <kind>`**                | Literal keyword after **`init`**; same as **`--harness`** (do not use both)   |
| **`--remote`**                          | **`easyspecs.remoteHarness.useRemoteOpenCodeExecution: true`**                |
| **`--local`**                           | **`useRemoteOpenCodeExecution: false`**                                       |
| **`--remote-base-url <url>`**           | **`remoteHarness.baseUrl`** only (does not change **`apiBaseUrl`**)           |
| **`--remote-requester-id <uuid>`**      | **`remoteHarness.requesterId`**                                               |
| **`--remote-ledger-dir <path>`**        | **`remoteHarness.completionLedgerDir`**                                       |
| **`--remote-submit-timeout <ms>`**      | **`remoteHarness.submitTimeoutMs`** (integer ≥ 1000)                          |
| **`--remote-poll-interval <ms>`**       | **`remoteHarness.completionPollIntervalMs`** (clamped **500–30000** on write) |
| **`--remote-ledger-log-interval <ms>`** | **`remoteHarness.completionLedgerLogIntervalMs`** (integer ≥ 0)               |

**Behaviour notes:**

- If **`config.json`** already exists and **`--overwrite`** is omitted, bootstrap may be **`unchanged`**, but any harness / **`--remote`** / **`--local`** / **`--remote-*`** options still update the file (**`outcome: updated`** in **`--json`**).
- When **`EASYSPECS_ENVIRONMENT`** is set in the shell at init time, **`deploymentEnvironment`**, **`apiBaseUrl`**, and **`remoteHarness.baseUrl`** are seeded from the canonical table (unless **`--remote-base-url`** overrides the remote URL).
- **`--remote`** on **`init`** is **not** global **`--remote-harness`** (per-run only).
- Valid but unimplemented harness kinds (**`gemini-cli`**, **`cursor-cli`**, **`codex`**) still save; stderr prints **`Not supported harness: …`** once (**exit 0**).

**`--json`** success may include **`outcome`**, **`agentHarnessKind`**, **`useRemoteOpenCodeExecution`**, **`execution`** (`remote` | `local`), and a **`remoteHarness`** object with only keys set in this invocation.

### Harness and deployment

| Command                                            | Arguments                                                                          | Config keys written                                                                                                             |
| -------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| **`config set-harness <kind>`**                    | **`opencode`**, **`claude-code`**, **`gemini-cli`**, **`cursor-cli`**, **`codex`** | **`easyspecs.agentHarness.kind`** and **`executable`** (default for kind: `opencode` → `opencode`, `claude-code` → `claude`, …) |
| **`config set-execution`**                         | Exactly one of **`--remote`** or **`--local`**                                     | **`easyspecs.remoteHarness.useRemoteOpenCodeExecution`**                                                                        |
| **`config set-environment <staging\|production>`** | Case-insensitive                                                                   | **`deploymentEnvironment`**, **`apiBaseUrl`**, **`remoteHarness.baseUrl`** (same URL for all three)                             |

**Supported harnesses today:** **`opencode`**, **`claude-code`**. Other valid kinds write config but emit the not-supported warning.

### Per-field `remoteHarness` writers

Use these when you only need to change one key (preserves all other **`remoteHarness`** fields).

| Command                                          | Argument                                          | Config key                                        |
| ------------------------------------------------ | ------------------------------------------------- | ------------------------------------------------- |
| **`config set-remote-base-url <url>`**           | `http://` or `https://` origin, no trailing slash | **`remoteHarness.baseUrl`**                       |
| **`config set-remote-requester-id <uuid>`**      | Content application / requester UUID              | **`remoteHarness.requesterId`**                   |
| **`config set-remote-ledger-dir <path>`**        | Repo-relative or absolute                         | **`remoteHarness.completionLedgerDir`**           |
| **`config set-remote-submit-timeout <ms>`**      | Integer ≥ 1000                                    | **`remoteHarness.submitTimeoutMs`**               |
| **`config set-remote-poll-interval <ms>`**       | Clamped to **500–30000** on write                 | **`remoteHarness.completionPollIntervalMs`**      |
| **`config set-remote-ledger-log-interval <ms>`** | Integer ≥ 0                                       | **`remoteHarness.completionLedgerLogIntervalMs`** |

**`config set-execution --remote`** is the setter for **`useRemoteOpenCodeExecution`**; there is no separate **`set-remote-execution`** subcommand required in v1 (alias may be added later).

### Quick reference vs global flags

| Goal                                                    | Use                                                                                                       |
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| Persist remote execution for all future runs            | **`config set-execution --remote`** or **`config init … --remote`**                                       |
| One factory run on remote worker without editing config | Global **`--remote-harness`** before **`analysis`**                                                       |
| Point API + remote POST at staging                      | **`config set-environment staging`** or **`export EASYSPECS_ENVIRONMENT=staging`** + empty URLs in config |
| Set only remote worker URL                              | **`config set-remote-base-url https://api.staging.easyspecs.ai`**                                         |

---

## Command reference

Each row lists **command-specific CLI tokens**, then **what configuration applies**.


| Command                                                          | Command-specific flags / tokens                                                                                                                                                                                                                                                                                                        | Configuration & notes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |   |
| `help`, `--help`                                                 | —                                                                                                                                                                                                                                                                                                                                      | Global options only; prints usage.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `version`                                                        | —                                                                                                                                                                                                                                                                                                                                      | Prints `[PKG_VERSION](../../src/cli/main.ts)` from the bundle.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `doctor`                                                         | Optional `**--readiness`**, `**--inspect-config**`                                                                                                                                                                                                                                                                                     | **Does not** create `config.json`. Default (no flags) = `**--readiness`**: first line `**cliVersion=**` (same as **`version`** / **`--version`**), then repo root, resolved API URL, OpenCode installed/credentials, agents dir. With **`--json`**, readiness adds top-level **`cliVersion`** (string) plus **`lines`**. When remote harness is configured or forced, **`--readiness --json`** also reports **`remoteHarness.useRemoteOpenCodeExecution`**, **`remoteHarness.effectiveUseRemote`**, and optional reachability fields (**SRS-1** **AC16**). `**--inspect-config**` alone: redacted `**merged**` + `**easyspecsConfig**` (no **`cliVersion`** line). `**--readiness --inspect-config**`: both. Global flags apply (`**--cwd**`, `**--ci**`, `**--api-base-url**`, `**--session-path**`, `**--environment**`, `**--promote**` / `**--no-promote**`, `**--remote-harness**` / `**--no-remote-harness**`). |
| `config init`                                                    | **`--overwrite`**; **`--harness <kind>`** or tail **`set-harness <kind>`**; **`--remote`** \| **`--local`**; **`--remote-base-url`**, **`--remote-requester-id`**, **`--remote-ledger-dir`**, **`--remote-submit-timeout`**, **`--remote-poll-interval`**, **`--remote-ledger-log-interval`**; **`--set <key> <value>`** (**SRS-102**) | **SRS-99:** Bootstrap **`config.json`** if missing (or **`--overwrite`**). Optional harness + execution + **`remoteHarness`** patches in **one** atomic write. See **[Config commands (SRS-99)](#config-commands-srs-99)** for one-shot examples. **`--json`**: `{ ok, outcome, path, agentHarnessKind?, execution?, remoteHarness?, envSet? }`. Init **`--remote`** ≠ global **`--remote-harness`**. **`--set`** applies to **`process.env`** only (not **`config.json`**).                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `config show`                                                    | Optional **`--redact`**                                                                                                                                                                                                                                                                                                                | Reads **`config.json`** (SRS-46). **`--json`**: `{ ok, path, config }`. Exit **misconfiguration** if missing.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `config apply`                                                   | Required **`--file <path.json>`**                                                                                                                                                                                                                                                                                                      | Full document replace; SRS-46 validate. **`--json`**: `{ ok, path, config }`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `config set-harness <kind>`                                      | Positional **`kind`**                                                                                                                                                                                                                                                                                                                  | **SRS-99:** **`easyspecs.agentHarness.kind`** and matching **`executable`**. Warns on stderr for not-yet-implemented kinds; exit **0**. Bootstrap if missing. **`--json`**: `agentHarnessExecutable`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `config set-execution`                                           | **`--remote`** \| **`--local`** (exactly one)                                                                                                                                                                                                                                                                                          | **SRS-99:** **`easyspecs.remoteHarness.useRemoteOpenCodeExecution`**. Usage exit if neither or both.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `config set-environment <staging\|production>`                   | Positional environment                                                                                                                                                                                                                                                                                                                 | **SRS-99:** **`deploymentEnvironment`**, **`apiBaseUrl`**, **`remoteHarness.baseUrl`** (canonical **`api.*.easyspecs.ai`** hosts).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `config set-remote-base-url <url>`                               | Origin URL                                                                                                                                                                                                                                                                                                                             | **SRS-99:** **`remoteHarness.baseUrl`** only.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `config set-remote-requester-id <uuid>`                          | UUID                                                                                                                                                                                                                                                                                                                                   | **SRS-99:** **`remoteHarness.requesterId`**.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `config set-remote-ledger-dir <path>`                            | Path                                                                                                                                                                                                                                                                                                                                   | **SRS-99:** **`remoteHarness.completionLedgerDir`**.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `config set-remote-submit-timeout <ms>`                          | Milliseconds                                                                                                                                                                                                                                                                                                                           | **SRS-99:** **`remoteHarness.submitTimeoutMs`**.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `config set-remote-poll-interval <ms>`                           | Milliseconds (clamped 500–30000)                                                                                                                                                                                                                                                                                                       | **SRS-99:** **`remoteHarness.completionPollIntervalMs`**.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `config set-remote-ledger-log-interval <ms>`                     | Milliseconds (≥ 0)                                                                                                                                                                                                                                                                                                                     | **SRS-99:** **`remoteHarness.completionLedgerLogIntervalMs`**.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `config set-project-id <id>`                                     | —                                                                                                                                                                                                                                                                                                                                      | **`easyspecs.easyspecsProjectId`**. Bootstrap if missing.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `config set-git-remote <url>`                                    | —                                                                                                                                                                                                                                                                                                                                      | **`easyspecs.defaultGitRemoteUrl`**. Bootstrap if missing.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `auth login`                                                     | `**--email <email> --password <password>**`, optional `**--session-path <path>**` (tail; updates `**config.json**`)                                                                                                                                                                                                                    | Calls `**POST /api/authentication/login**`. On success prints `**OK**` (stdout), exit **0**, and writes tokens + `**apiBaseUrl`** to the session file. Tail `**--session-path**` updates `**easyspecs.cliSessionPath**` in `**config.json**` (normalized repo-relative when under the repo) and writes the session there for this run; omit it to use the existing `**easyspecs.cliSessionPath**` / default `**~/.easyspecs/cli-session.json**`. Global `**--session-path**` (before the command) overrides the effective session file for any command without changing `**config.json**`. On failure prints `**KO: …**` (stderr), exit `**auth**`. `**easyspecs-cli --ci auth login**` without argv credentials uses `**easyspecs.auth.ciLogin**` in `**config.json**`. `**--password**` on argv is visible to `**ps**`.                                                                                             |
| `auth logout`                                                    | —                                                                                                                                                                                                                                                                                                                                      | Deletes the effective session file (same path rules as `**auth login**`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `auth status`                                                    | —                                                                                                                                                                                                                                                                                                                                      | Reads session file.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `run synthesis`                                                  | —                                                                                                                                                                                                                                                                                                                                      | Single SRS-9 context pass (same as extension **Run Analysis**). `**requireOpenCode`** → executable + credentials unless skip flag. Uses `**merged.pipelineOpenCode**` from `**config.json**`. `**--promote` / `--no-promote**` controls promotion after success.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `run synthesis resume-missing`, `run synthesis resume-synthesis` | Optional `**--worktree <path>**`; `**resume-missing**` also accepts repeatable `**--keys <missingKey>**` (filters missing-artefact rows)                                                                                                                                                                                               | Same pool implementation: resolves checkout via `--worktree` or stored snapshot (`[resolveAdHocCheckoutRoot](../../src/cli/main.ts)`). OpenCode + `**merged.pipelineOpenCode**`. Extension Diagnosis panel delegates parallel missing remediation to `**resume-synthesis**`, sequential/all to `**resume-missing**`, single-row **Regenerate** to `**resume-missing --keys**`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `worktree create`                                                | —                                                                                                                                                                                                                                                                                                                                      | Creates detached analysis checkout at HEAD; writes file-backed workspace snapshot under `**<repoRoot>/.easyspecs/**`. `**--json**`: `**worktreePath**`, `**fullPath**`, `**repoRoot**`, `**folderName**`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `worktree remove`                                                | —                                                                                                                                                                                                                                                                                                                                      | Removes saved analysis worktree from snapshot + disk (`[extensionPanelCliCommands.ts](../../src/cli/extensionPanelCliCommands.ts)`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `agents materialize`                                             | Optional `**--worktree <path>**`                                                                                                                                                                                                                                                                                                       | Copies bundled harness agents into checkout `**.opencode/agents**` (OpenCode) or the active harness layout; notes materialization in workspace snapshot. **After upgrading the CLI**, re-run materialize so agent specs pick up bundled path changes (e.g. **`.easyspecs/context/`** coordination root — SRS-133).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `context promote`                                                | Optional `**--worktree <path>**`                                                                                                                                                                                                                                                                                                       | Copies `**.easyspecs/context**` from analysis checkout into `**--cwd**` repo root.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `context assemble-index`                                         | Optional `**--worktree <path>**`                                                                                                                                                                                                                                                                                                       | Runs link-mapping + writes `**index-application-context.json**` under checkout context.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `workstation run`                                                | Required `**--worktree <path>**`, `**--step <ContextSynthesisTestStep>**` or markdown `**--detail-agent**` + `**--output-basename**`; optional scope `**--feature**`, `**--use-case**`, `**--entity**`, `**--infrastructure**`, `**--qa**`                                                                                             | Single OpenCode workstation (synthesis tree **Run / Re-run**, repo-surface preflight). `**requireOpenCode**`; uses `**merged.pipelineOpenCode**`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `analysis`                                                       | Optional tail: `**--force-new-context-analysis**`, `**--no-worktree**` / `**--noworktree**` / `**--in-place**` (**SRS-71**). **SRS-126:** zero-reference remediation **off by default** (`**easyspecs.factory.skipRemediationPipeline: true`**). **`--enable-remediation-pipeline`** — opt in for this run. **`--skip-remediation-pipeline`** (**SRS-78**) — explicit skip (redundant when default applies). **`--zero-reference-convergence=strict                                                                                                                    | best_effort`** (**SRS-73**; ignored when remediation skipped). **`--upload`** → usage. **`--synthesis-only`**: hidden, deprecated (**SRS-60**). **SRS-100 (persist before factory):** same harness / execution / `**remoteHarness**` flags as **`config init`** — keyword `**set-harness <kind>**` or `**--harness <kind>**`, `**--remote**`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | `**--local**`, `**--remote-base-url**`, `**--remote-requester-id**`, `**--remote-ledger-dir**`, `**--remote-submit-timeout**`, `**--remote-poll-interval**`, `**--remote-ledger-log-interval**`, `**--set <key> <value>**` (**SRS-102**, repeatable; `SERVICE_API_KEY` \| `AI_MODEL_PRESET_ID` only; `process.env` for this run). May be interleaved with analysis flags. Writes **`.easyspecs/config.json`** once, then runs the Factory. **`--json`:** `**configPersist**` when persist flags present; `**envSet**` when `**--set**` used (keys only); `**remediationPipelineSkipped: true**` when remediation was skipped. Distinct from global **`--remote-harness`** (per-process only). Example: `**easyspecs-cli analysis set-harness claude-code --remote --remote-base-url https://api.staging.easyspecs.ai --remote-requester-id <uuid> …**` | **SRS-32** full **Factory** loop: synthesis convergence (until no missing artefacts), coverage, zero-ref (unless skipped — default skip since **SRS-126**), report, **link mapping**, index assembly; **Phase 7** backend sync is skipped in CLI (**`skipBackendSync`**). **On success:** always promotes **`.easyspecs/context`** from the analysis worktree into **`<repoRoot>`** unless **`--no-promote`** or the checkout is already the repo root (stderr **`[pipeline:analysis]`**); **`easyspecs.analysis.promoteContextToWorkspace`** does **not** apply to this step. Push context with **`upload context`** / **`upload republish`** after analysis if you need cloud sync. Factory timing from `**merged.factory`** + `**--ci**` defaults. Debug: `**easyspecs.factory.debug**`. OpenCode options from `**merged.pipelineOpenCode**`. **SRS-46:** if `**easyspecs.factory.cloudContextAnalyzed`** is `**true**` and `**--force-new-context-analysis**` is absent, exits **0** without running the Factory (`**analysisSkipped**`, …). **SRS-59:** stale worktree registration recovery — see **§ Analysis git checkout** below. |
| `update context`                                                 | Optional `**--no-worktree**` / `**--noworktree**` (**SRS-71**). **SRS-60** removed `**--upload**`.                                                                                                                                                                                                                                     | **SRS-55:** incremental context refresh — baseline → git delta → analysis checkout → `**changes-since-date.md`** → optional remediation → **`--promote` / `--no-promote**` (global) → persist `**lastRunAt**`. With **`--no-worktree`**, the checkout is **`<repoRoot>`** (no temp worktree; seed/promote steps that would copy repo → same repo are skipped). Run **`upload context`** separately if you need cloud sync.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `diagnose reference-coverage`                                    | `**--root workspace**`                                                                                                                                                                                                                                                                                                                 | `**--root worktree**`, optional `**--worktree <path>**`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Gate when `**easyspecs.diagnose.zeroReference.maxPercentNonReferenced**` is a finite number (not `**null**`): failure when metric exceeds it.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `diagnose coordination-duplicates`                               | Same `**--root**` / `**--worktree**`                                                                                                                                                                                                                                                                                                   | `**easyspecs.diagnose.coordinationDuplicates.strict**`: when `**true**` (default), duplicate/orphan issues fail the exit code.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `diagnose coverage-report`                                       | Same `**--root**` / `**--worktree**`                                                                                                                                                                                                                                                                                                   | Uses repo/worktree paths only; no extra env in `main.ts`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `diagnose missing-artefacts`                                     | Same `**--root**` / `**--worktree**`                                                                                                                                                                                                                                                                                                   | Reads artefact snapshot from workspace state; no OpenCode.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `diagnose artefact <artefact>`                                   | `**--perf-report <path>**`, `**--run-log <path>**`, `**--out-dir <path>**`; batch: `**--from-perf-report**`, `**--outcome failed\|success\|all**`, `**--limit N**`; same `**--root**` / `**--worktree**`                                                                                                                            | **SRS-122** + **SRS-134:** programmatic timeline markdown at `**.easyspecs/artefact-diagnostic/<stem>.diagnostic.md**` (`**diagnosticVersion: 2**`). Includes **`## Harness commands (sessions)`** near the top with copy-paste **`opencode -s <sessionId>`** / **`claude -r <uuid>`** per spawn (**SRS-136:** resolves primary spawns with `stdoutBytes=0` via `primary complete` log lines and `sessionId captured:`; **SRS-137:** log-grounded **`phaseLabel`** per spawn — primary, inner repair, pool-retry primary, open-questions). No OpenCode to generate the file. Exit **89** when inputs missing or batch finds zero rows. `**--json**`: `**diagnosticPath**`, `**failureKind**`, `**contributingFactors**`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `diagnose zero-reference`                                        | Optional `**--worktree <path>**`, `**--in-place**` / `**--no-worktree**` / `**--noworktree**` (**SRS-71**, **SRS-124**); repeatable `**--paths <repo-relative>**` (subset remediation; no `--root` branch)                                                                                                                                 | `**requireOpenCode**`; `**merged.pipelineOpenCode**` for pool concurrency/argv/timeout. Checkout from `**--worktree**`, `**--noworktree**` (repository root), or snapshot (`[resolveAdHocCheckoutRoot](../../src/cli/main.ts)`). On full success, runs **SRS-51** markdown link graph on `**<checkout>/.easyspecs/context`** before exit (**non-zero** if link validation fails).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `context link-graph`                                             | `**--root workspace`**                                                                                                                                                                                                                                                                                                                 | `**--root worktree**`, optional `**--worktree <path>**`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | **SRS-51:** deterministic navigation sections in context markdown under `**<root>/.easyspecs/context`**. No OpenCode. `**--json**`: `**ok**`, `**contextDir**`, `**error**`, `**brokenLinks**`. Exit **78** on broken relative links inside EasySpecs nav regions (**SRS-70**).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `context drift <referencePath>`                                  | `**--label <slug>**`, `**--index <path>**`, `**--dry-run**`, `**--no-worktree**` / `**--noworktree**` (**SRS-71**; mutually exclusive with **`--root worktree`** / **`--worktree <path>`**). Optional `**--root**` / `**--worktree**` tail tokens are accepted for other routing.                                                      | **SRS-56:** analysis checkout → OpenCode drift agent → `**.easyspecs/context/drift/drift-<slug>-<date>.md`** + reference index patch → global `**--promote` / `--no-promote**`. Success stdout: drift report path. With **`--no-worktree`**, the checkout is **`<repoRoot>`** (no temp worktree). **`--json`** may include **`analysisInPlace: true`** when that mode ran.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `context expand <nodeCode>`                                      | `**--types <csv>**` (descendant index types, e.g. `UseCase,Scenario`), `**--full-depth**`, `**--dry-run**`                                                                                                                                                                                                                              | **SRS-141:** deepen **one** existing parent node (`**FE-07**`, `**FE-07_UC-02**`, `**SV-03**`, `**DM-01**`, `**IN-02**`, `**QA-01**`, `**XP-04**`) on demand. Runs **in-place** on `**<repoRoot>/.easyspecs/context**` (existing artefacts required). Target types: `**--types**` > `**--full-depth**` (ignores config) > config `**autoGenerate:true**` descendants (may no-op). Missing intermediate types auto-included. `**requireOpenCode**`; reuses synthesis pool + index assembly. `**--json**` success reports `**parentsDiscovered**`, `**workItemsRun**`, `**artefactsCreated**` (plus `**codes**`, `**skippedWorkItemCount**`, `**indexAssemblyOk**`, `**plans**`). `**--dry-run**` prints `**parentsDiscovered**` / `**plannedWorkItems**` / `**plans**` without spawning. Exit **2** usage/parent-not-found, **80** run failure. |
| `context expand-type <typeOrPrefix>`                             | `**--types <csv>**`, `**--full-depth**`, `**--dry-run**`                                                                                                                                                                                                                                                                                | **SRS-141:** deepen **every** existing root row of a scope. `**<typeOrPrefix>**` = scope-root index type (`**Feature**`) or code prefix alias (`**FE**`, `**SV**`, `**IN**`, `**QA**`, `**XP**`, `**DM**`, `**TS**`). Discovers root codes from the scope list (`**features-list.json**`, …), then runs the same expand for each. Same options/flags/exit codes as `**context expand**`. |
| `download context`                                               | Optional `**--force**`, `**--replace-from-cloud**`                                                                                                                                                                                                                                                                                     | **SRS-49:** requires `**auth`** session. `**GET /api/content/application/:id**` → `**srs_discovery**` ids → `**POST /api/batch/content/srs_discovery/get**` → writes files under `**<repoRoot>/.easyspecs/context**`. `**--force**` overwrites existing paths; `**--replace-from-cloud**` deletes local context files first (preserves root `**easyspecs-upload-target.json**`). `**--json**` stdout: `**downloaded**`, `**skipped**`, `**failed**`, `**localRemoved**`. Exit `**upload**` when any row fails.                                                                                                                                                                                                                                                                                                                                                                                                        |
| `upload context`                                                 | —                                                                                                                                                                                                                                                                                                                                      | Requires `**auth**` session. Context dir: `**<repoRoot>/.easyspecs/context**`. Project id: `**easyspecs.easyspecsProjectId**` in `**config.json**` only. **SRS-46:** on full success, optional cloud status GET (see `**fetchContextAnalyzedInCloud`**); `**--json**` may add `**contextAnalyzedInCloud**` / `**contextAnalyzedInCloudAt**`; updates `**easyspecs.factory.cloudContext***` when fetch is enabled (legacy `**easyspecs.analysis.cloudContext***`).                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `upload republish`                                               | —                                                                                                                                                                                                                                                                                                                                      | Same auth + upload pipeline; context dir from `**easyspecs.upload.contextDirectory**` if non-empty and valid, else analysis worktree `**…/.easyspecs/context**` from snapshot, else error. Same project id rule as `**upload context**`. Same **SRS-46** JSON + cache behaviour as `**upload context`**.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `ace clear`                                                      | —                                                                                                                                                                                                                                                                                                                                      | Deletes `**<repoRoot>/.easyspecs/context/learnings**`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `ace learn`                                                      | Optional `**--worktree <path>**`                                                                                                                                                                                                                                                                                                       | `**requireOpenCode**`; worktree chosen if `--worktree` points at checkout with `**.opencode/schemas/ace**`, else `**repoRoot**` (`[main.ts](../../src/cli/main.ts)`). `**merged.pipelineOpenCode**` for spawn argv/timeout.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `ace auto-learn`                                                 | Optional `**--worktree <path>**`                                                                                                                                                                                                                                                                                                       | `**requireOpenCode**`; checkout = `--worktree` if valid git dir, else `**repoRoot**`. `**merged.pipelineOpenCode.maxConcurrentOpenCodeAgents**` caps parallelism.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |


### Analysis git checkout (**SRS-59**)

The Factory normally creates a **detached** analysis checkout under `**$TMPDIR/easyspecs-analysis/easyspecs-NNN`** (`[worktreeManager.ts](../../src/analysis/worktree/worktreeManager.ts)`). **`--no-worktree`** / **`--noworktree`** on **`analysis`**, **`update context`**, or **`context drift`** skips that step and uses **`<repoRoot>`** (resolved **`--cwd`**) as the checkout instead (**SRS-71**). If the OS removes that folder (or it is deleted) **without** `**git worktree remove`**, Git may still list the path and `**git worktree add**` fails with *missing but already registered worktree*. **SRS-59:** `**easyspecs-cli*`* detects that failure class, runs `**git worktree prune**` with `**cwd**` = the repository root (from `**--cwd**` / current directory), then retries `**git worktree add**` **once** at the same allocated path (`[gitWorktreeStaleRecovery.ts](../../src/analysis/worktree/gitWorktreeStaleRecovery.ts)`). Stderr may include `**[worktree] git worktree prune — cleared stale registration for <folder> (<absolute path>)`** before the retry. Manual troubleshooting: `[.easyspecs/docs/srs/USER-MANUAL-SRS-59.md](../../.easyspecs/docs/srs/USER-MANUAL-SRS-59.md)`.

---

## Notes

- Commands that **spawn OpenCode** call `**requireOpenCode`**: binary must respond (`easyspecs.openCode.executable` / `opencode` on `**PATH**`), and credentials must look ready unless `**easyspecs.openCode.skipCredentialsCheck**`, or provider keys exist in `**config.json**`, or the parent shell already has provider env (probe behaviour in `[harness/adapters/opencode/readiness](../../src/harness/adapters/opencode/readiness.ts)`).
- `**download context**` / `**upload context**` / `**upload republish**` require a prior `**auth login**` session (`**--email**` / `**--password**`, or `**--ci**` with `**easyspecs.auth.ciLogin**` in `**config.json**`).
- Unknown commands print help and exit with a usage error.
- Git subprocesses may set `**GIT_TERMINAL_PROMPT=0**` on the child environment (`[runCoveragePipeline](../../src/pipelines/coverage/coveragePipeline.ts)`).
- Any command path that **creates** the analysis git worktree (`**analysis`**, **`run synthesis**`, **`update context**`, **`context drift**` without `**--dry-run**` or **`--no-worktree**`, etc.) benefits from **SRS-59** stale-registration recovery in `**createAnalysisWorktree`**, not only `**analysis**`.

