From b007d375c1af71ae2c48bb76ee279b7828ba2007 Mon Sep 17 00:00:00 2001 From: Hally Maschine Date: Tue, 1 Sep 2026 11:59:21 -0700 Subject: [PATCH] feat: accept model effort level You can now tell CE how hard the model should think when it writes code, when it writes a plan or brainstorm, and when Codex reviews. If you don't set it, nothing changes (still high). If you pick a value a tool can't use, we skip it. We never quietly pick a different one. You can also say this in an LFG run, including OpenCode. If you don't say how hard, we use your checkout config, or high. OpenCode gets this as --variant. Codex review now also accepts max and none. Fixes EveryInc/compound-engineering-plugin#1569 Fixes EveryInc/compound-engineering-plugin#1415 Fixes EveryInc/compound-engineering-plugin#1565 --- .compound-engineering/config.example.yaml | 7 +- docs/guides/ce-brainstorm.md | 2 +- docs/guides/ce-plan.md | 2 +- docs/guides/ce-work.md | 7 +- docs/guides/configuration.md | 9 +- docs/guides/lfg.md | 2 +- ...1-1152-feat-work-engine-effort-pin-plan.md | 300 ++++++++++++++++++ .../references/reasoning-elevation.md | 2 +- .../scripts/elevation-dispatch.sh | 8 +- .../scripts/cross-model-adversarial-review.sh | 4 +- .../scripts/cross-model-doc-review.sh | 4 +- .../ce-plan/references/reasoning-elevation.md | 2 +- skills/ce-plan/scripts/elevation-dispatch.sh | 8 +- .../ce-setup/references/config-template.yaml | 7 +- skills/ce-setup/scripts/check-health | 9 + .../references/cross-model-execution.md | 4 +- .../references/cross-model-work-eval.md | 2 +- .../ce-work/references/execution-engines.md | 3 +- skills/ce-work/references/input-triage.md | 2 +- skills/ce-work/references/return-to-caller.md | 2 +- skills/ce-work/scripts/cross-model-work.sh | 92 +++++- skills/ce-work/scripts/unit_workspace_jobs.py | 18 ++ .../ce-work/scripts/unit_workspace_state.py | 44 ++- skills/lfg/references/stage-routing.md | 5 +- .../ce-code-review-cross-model-routes.test.ts | 3 +- tests/skills/ce-setup-check-health.test.ts | 22 ++ .../ce-work-cross-model-integration.test.ts | 22 +- .../skills/ce-work-cross-model-routes.test.ts | 36 ++- tests/skills/ce-work-outcome-spine.test.ts | 11 +- .../ce-work-unit-workspace-init.test.ts | 53 +++- tests/skills/elevation-dispatch.test.ts | 23 ++ .../helpers/ce-work-workspace-harness.ts | 4 +- .../unified-plan-artifact-contract.test.ts | 6 +- 33 files changed, 649 insertions(+), 76 deletions(-) create mode 100644 docs/plans/2026-09-01-1152-feat-work-engine-effort-pin-plan.md diff --git a/.compound-engineering/config.example.yaml b/.compound-engineering/config.example.yaml index 49cd37d86..63bb7f123 100644 --- a/.compound-engineering/config.example.yaml +++ b/.compound-engineering/config.example.yaml @@ -72,7 +72,9 @@ # elevation. # plan_model: fable # model alias for the plan-authoring step (no default) +# plan_effort: high # optional Claude CLI effort; default: high # brainstorm_model: fable # model alias for approach generation (no default) +# brainstorm_effort: high # optional Claude CLI effort; default: high # --- PR concept teaching (ce-commit-push-pr) --- # When a PR introduces a concept new to the codebase, the generated PR @@ -123,8 +125,8 @@ # `fable` or a full id such as `claude-opus-5` for claude; `gpt-*` for codex, # optionally namespace-qualified such as `openai.gpt-5.6-sol` when the codex CLI # routes through a non-default `model_provider`). -# Effort levels: claude low|medium|high|xhigh|max, codex minimal|low|medium| -# high|xhigh, grok low|medium|high; cursor/composer routes take none. A value +# Effort levels: claude low|medium|high|xhigh|max, codex none|minimal|low|medium| +# high|xhigh|max, grok low|medium|high; cursor/composer routes take none. A value # the peer cannot honor skips the pass with a stated reason -- never substituted. # cross_model_review_mode: off # auto | off (default: auto) @@ -151,6 +153,7 @@ # model: composer # - harness: codex # model: "gpt-5.6" +# effort: max # optional; default: high # - harness: claude # configured default model # --- ce-promote --- diff --git a/docs/guides/ce-brainstorm.md b/docs/guides/ce-brainstorm.md index 7116c128e..cd0a917b1 100644 --- a/docs/guides/ce-brainstorm.md +++ b/docs/guides/ce-brainstorm.md @@ -276,7 +276,7 @@ Not from the Phase 4 menu. Software next steps are `ce-plan` or `lfg` (which pla ## Model elevation -When you want a specific model for the heavy reasoning step, `ce-brainstorm` can generate approaches on that model instead of your session model. Only approach generation is dispatched, with read access so it can verify its brief. The rest of the skill stays on your session model. Name a model in the prompt (`use fable`, `have opus generate these`), or set `brainstorm_model: ` in CE config (`config.local.yaml` then `config.yaml`). A prompt request overrides the config key. +When you want a specific model for the heavy reasoning step, `ce-brainstorm` can generate approaches on that model instead of your session model. Only approach generation is dispatched, with read access so it can verify its brief. The rest of the skill stays on your session model. Name a model in the prompt (`use fable`, `have opus generate these`), or set `brainstorm_model: ` in CE config (`config.local.yaml` then `config.yaml`). A prompt request overrides the config key. Optional `brainstorm_effort` pins Claude CLI effort for that elevated step; default: high. This works on any harness. The host serves the chosen model natively where it can, otherwise it invokes the Claude CLI (which must be installed and authenticated), otherwise it runs the step on your session model and says which precondition was unmet. Setting `brainstorm_model` therefore takes effect in every harness you run `ce-brainstorm` in, not just Claude Code. diff --git a/docs/guides/ce-plan.md b/docs/guides/ce-plan.md index 8c6183968..4fa4741ee 100644 --- a/docs/guides/ce-plan.md +++ b/docs/guides/ce-plan.md @@ -297,7 +297,7 @@ No. Proof is on the non-software wrap-up menu (save, publish, or both). Software ## Model elevation -When you want a specific model for the heavy reasoning step, `ce-plan` can author the plan on that model instead of your session model. Only the interpret-findings-then-author step is dispatched, with read access so it can verify its brief. Dialogue and research stay on your session model. Name a model in the prompt (`use fable`, `have opus plan this`), or set `plan_model: ` in CE config (`config.local.yaml` then `config.yaml`). A prompt request overrides the config key. +When you want a specific model for the heavy reasoning step, `ce-plan` can author the plan on that model instead of your session model. Only the interpret-findings-then-author step is dispatched, with read access so it can verify its brief. Dialogue and research stay on your session model. Name a model in the prompt (`use fable`, `have opus plan this`), or set `plan_model: ` in CE config (`config.local.yaml` then `config.yaml`). A prompt request overrides the config key. Optional `plan_effort` pins Claude CLI effort for that elevated step; default: high. This works on any harness. The host serves the chosen model natively where it can, otherwise it invokes the Claude CLI (which must be installed and authenticated), otherwise it runs the step on your session model and says which precondition was unmet. Setting `plan_model` therefore takes effect in every harness you run `ce-plan` in, not just Claude Code. diff --git a/docs/guides/ce-work.md b/docs/guides/ce-work.md index c97f0c341..4a42e711a 100644 --- a/docs/guides/ce-work.md +++ b/docs/guides/ce-work.md @@ -221,7 +221,7 @@ When another workflow owns the post-implementation shipping gates (final simplif This mode keeps `ce-work` on implementation and local verification. Mid-implementation "Simplify as You Go" still runs during Phase 2. After that, `ce-work` returns a structured envelope with changed files, completed units, verification evidence, and blockers, sets `standalone_shipping_skipped: true`, and does not run the standalone shipping tail. The caller remains responsible for every post-implementation gate. -Automatic callers can also pass `implementation_engine:` (one `mode`, `target`, `model`, and `source` binding) and `implementation_run:` (resume that existing run) before the plan path. +Automatic callers can also pass `implementation_engine:` (one `mode`, `target`, `model`, `effort`, and `source` binding) and `implementation_run:` (resume that existing run) before the plan path. ## Choose the Implementation Author @@ -250,12 +250,13 @@ work_engine_preferences: model: composer - harness: codex model: "gpt-5.6" + effort: max - harness: claude ``` The [central configuration reference](./configuration.md#implementation-routing) explains how this checkout-local default interacts with current-task, session, and project instructions. -Each candidate has a `harness` (`codex`, `claude`, `grok`, `cursor`, or `opencode`) and an optional `model`. Omitting `model` means that harness's configured default. Composer is a model family reached through Cursor, so it is written as `harness: cursor` plus `model: composer`. Keep CLI flags and commands out of config. +Each candidate has a `harness` (`codex`, `claude`, `grok`, `cursor`, or `opencode`), an optional `model`, and an optional `effort` (default: high). Omitting `model` means that harness's configured default. Composer is a model family reached through Cursor, so it is written as `harness: cursor` plus `model: composer`. Keep CLI flags and commands out of config. `off`, a commented or missing mode, and an invalid mode preserve the native default. `off` affects only standing config; it does not cancel applicable live intent or a caller binding. Both `prefer` and `require` try ordered candidates, then fall back natively on the current harness and session model with one disclosure. `require` keeps the requested external identity fixed while viable and never substitutes an unrequested external recipient. @@ -280,7 +281,7 @@ Every CE Work runner start pins a two-hour hard cap independently of the shared | `` | Triage by complexity (Trivial / Small-Medium / Large) | | `use Codex` / `with Cursor` / `only use Composer` | Request or require an external implementation author. The host still verifies, commits, and ships. | | `mode:return-to-caller ` | Outer-orchestrator use: implement and locally verify, then return structured evidence without the standalone shipping tail | -| `mode:return-to-caller implementation_engine: ` | Automatic-caller form carrying one implementation-only `mode`, `target`, `model`, and `source` binding | +| `mode:return-to-caller implementation_engine: ` | Automatic-caller form carrying one implementation-only `mode`, `target`, `model`, `effort`, and `source` binding | | `implementation_run:` or `resume run ` | Resume, inspect, or clean up that existing external run. Does not start new work. | | Knowledge-work plan (`execution: knowledge-work`) | Produce the planned deliverable; skip branch, test, review, and PR machinery | diff --git a/docs/guides/configuration.md b/docs/guides/configuration.md index a480ca7c3..499399a4c 100644 --- a/docs/guides/configuration.md +++ b/docs/guides/configuration.md @@ -44,11 +44,11 @@ All settings are optional. Commented examples are documentation, not active valu | all artifact-writing skills | `docs_root` | Repo-relative folder every CE artifact subdirectory lives under. Set only in `config.yaml`. Unset -> `docs`. See [Artifact root](#artifact-root). | | [`ce-ideate`](./ce-ideate.md), [`ce-brainstorm`](./ce-brainstorm.md), [`ce-plan`](./ce-plan.md) | `ideate_output`, `brainstorm_output`, `plan_output` | Artifact format: `md` or `html`. Defaults are HTML for ideation and markdown for brainstorms/plans. Pipeline contexts force markdown. | | [`ce-plan`](./ce-plan.md) | `plan_skip_scoping_confirm` | `true` skips the normal pre-plan scope confirmation; default `false`. It does not suppress genuine blockers or the post-plan menu. | -| [`ce-plan`](./ce-plan.md), [`ce-brainstorm`](./ce-brainstorm.md) | `plan_model`, `brainstorm_model` | Model elevation: send the reasoning-heavy step to a named model (e.g. `fable`, `opus`) instead of the session model. Value is a model alias; a prompt request or an orchestrator's `plan_model:` carrier (e.g. from `lfg`, honored even in pipeline mode) overrides it. Takes effect on every harness — natively where the host serves the model, else via the Claude CLI, else inline. No default (elevation off). | -| [`ce-work`](./ce-work.md), [`lfg`](./lfg.md) | `work_engine_mode`, `work_engine_preferences` | Ordered implementation-author preferences. Mode is `off`, `prefer`, or `require`; each entry has a `harness` and optional `model`. See [Implementation routing](#implementation-routing). | +| [`ce-plan`](./ce-plan.md), [`ce-brainstorm`](./ce-brainstorm.md) | `plan_model`, `brainstorm_model`, `plan_effort`, `brainstorm_effort` | Model elevation: send the reasoning-heavy step to a named model (e.g. `fable`, `opus`) instead of the session model. Value is a model alias; a prompt request or an orchestrator's `plan_model:` carrier (e.g. from `lfg`, honored even in pipeline mode) overrides it. Takes effect on every harness — natively where the host serves the model, else via the Claude CLI, else inline. No default (elevation off). `plan_effort` / `brainstorm_effort` pin Claude CLI effort; default: high. | +| [`ce-work`](./ce-work.md), [`lfg`](./lfg.md) | `work_engine_mode`, `work_engine_preferences` | Ordered implementation-author preferences. Mode is `off`, `prefer`, or `require`; each entry has a `harness`, optional `model`, and optional `effort`. See [Implementation routing](#implementation-routing). | | [`ce-code-review`](./ce-code-review.md), [`ce-doc-review`](./ce-doc-review.md) | `cross_model_review_mode` | Whether the automatic cross-model pass may send review content to a second provider: `auto` (default, current behavior) or `off`. `off` is evaluated before any peer or route is resolved, keeps every local reviewer and the local adversarial fallback, and is reported as "disabled by checkout config" rather than as an unavailable route. A direct conversation request for a peer overrides `off` for that run; a conversation prohibition overrides `auto`. | | [`ce-code-review`](./ce-code-review.md), [`ce-doc-review`](./ce-doc-review.md) | `cross_model_peer` | Preferred cross-model review target: `codex`, `claude`, `grok`, `cursor`, `composer`, or `opencode`. `grok` binds the native grok CLI when it is installed, and falls back to Grok through Cursor only when that CLI is absent and Cursor is a sanctioned recipient. The review skills still apply host-independence and route-availability gates. | -| [`ce-code-review`](./ce-code-review.md), [`ce-doc-review`](./ce-doc-review.md) | `cross_model_model`, `cross_model_effort` | Pin the resolved peer target's model (an alias such as `fable` or a full id such as `claude-opus-5`, same family as the target; a codex id may carry its serving provider's namespace, such as `openai.gpt-5.6-sol`, when the CLI routes through a non-default `model_provider`) and reasoning effort (claude `low`..`max`, codex `minimal`..`xhigh`, grok `low`..`high`; cursor-agent routes accept none). Unset keeps the skills' editorial mapping. A value the peer cannot honor skips the pass with a stated reason rather than substituting; a conversation request overrides both. | +| [`ce-code-review`](./ce-code-review.md), [`ce-doc-review`](./ce-doc-review.md) | `cross_model_model`, `cross_model_effort` | Pin the resolved peer target's model (an alias such as `fable` or a full id such as `claude-opus-5`, same family as the target; a codex id may carry its serving provider's namespace, such as `openai.gpt-5.6-sol`, when the CLI routes through a non-default `model_provider`) and reasoning effort (claude `low`..`max`, codex `none`..`max`, grok `low`..`high`; cursor-agent routes accept none). Unset keeps the skills' editorial mapping. A value the peer cannot honor skips the pass with a stated reason rather than substituting; a conversation request overrides both. | | [`ce-commit-push-pr`](./ce-commit-push-pr.md) | `pr_teaching_section`, `pr_teaching_archive`, `auto_babysit` | Toggle PR concept teaching, opt into explainer archival, or opt out of the default babysit handoff. Defaults: `true`, `false`, and `true`. `auto_babysit` governs the standing watch handed off after a PR is opened or pushed to -- the open-ended one that spends tokens until you merge. It does not govern [`lfg`](./lfg.md)'s in-pipeline babysit, which is bounded (3 fix rounds, ~30-45 min), ends on its own, and is how that pipeline reaches its "CI decided" completion. | | [`ce-product-pulse`](./ce-product-pulse.md) | `pulse_product_name`, `pulse_lookback_default`, `pulse_primary_event`, `pulse_value_event`, `pulse_completion_events` | Product identity, reporting window, and the events that represent engagement, value, and completion. The setup interview writes these values. | | [`ce-product-pulse`](./ce-product-pulse.md) | `pulse_quality_scoring`, `pulse_quality_dimension`, `pulse_analytics_source`, `pulse_tracing_source`, `pulse_payments_source`, `pulse_db_enabled` | Optional quality scoring and read-only data-source routing. | @@ -67,10 +67,11 @@ work_engine_preferences: model: composer - harness: codex model: "gpt-5.6" + effort: max - harness: claude ``` -Supported harnesses are `codex`, `claude`, `grok`, `cursor`, and `opencode`. Omitting `model` uses that harness's configured default. Composer is a model family reached through Cursor, so request it with `harness: cursor` and `model: composer`. +Supported harnesses are `codex`, `claude`, `grok`, `cursor`, and `opencode`. Omitting `model` uses that harness's configured default. Composer is a model family reached through Cursor, so request it with `harness: cursor` and `model: composer`. Optional `effort` default: high. `ce-work` walks the list in order and skips an entry equivalent to the current host/default model. A different explicit model in the same harness remains eligible. With either `prefer` or `require`, an unavailable list falls back to native implementation on the current harness and session model with one disclosure. `require` keeps the requested external identity fixed while viable; it never authorizes an unrequested external recipient or turns route unavailability into a blocker. diff --git a/docs/guides/lfg.md b/docs/guides/lfg.md index 30d5ab44f..4bfaab53f 100644 --- a/docs/guides/lfg.md +++ b/docs/guides/lfg.md @@ -95,7 +95,7 @@ Planning has to land an implementation-ready code plan. Implementation has to re ### You can route two stages, not the whole run -Planning can be authored on a named model (`plan with fable`) via `ce-plan`'s model elevation. Implementation can be sent to a harness (`use Codex for implementation`, `only use Composer for implementation`). Unscoped assignments bind to implementation only. Standing defaults live in CE config (`plan_model`, `work_engine_mode`, `work_engine_preferences`). See [Implementation routing](./configuration.md#implementation-routing). +Planning can be authored on a named model (`plan with fable`) via `ce-plan`'s model elevation. Implementation can be sent to a harness (`use Codex for implementation`, `only use Composer for implementation`); an explicit effort token rides that same carrier. Unscoped assignments bind to implementation only. Standing defaults live in CE config (`plan_model`, `work_engine_mode`, `work_engine_preferences`). See [Implementation routing](./configuration.md#implementation-routing). Both a preference and a requirement fall back to the current harness/session model with one disclosure when the external route cannot run. A requirement keeps the requested external identity fixed while viable; it never authorizes another external recipient. `lfg` does not ask whether to weaken the route. diff --git a/docs/plans/2026-09-01-1152-feat-work-engine-effort-pin-plan.md b/docs/plans/2026-09-01-1152-feat-work-engine-effort-pin-plan.md new file mode 100644 index 000000000..983c3cbcd --- /dev/null +++ b/docs/plans/2026-09-01-1152-feat-work-engine-effort-pin-plan.md @@ -0,0 +1,300 @@ +--- +title: "Work-engine effort pin - Plan" +type: feat +date: 2026-09-01 +origin: "https://github.com/EveryInc/compound-engineering-plugin/issues/1569" +artifact_contract: ce-unified-plan/v1 +artifact_readiness: implementation-ready +product_contract_source: ce-plan-bootstrap +execution: code +--- + +# Work-engine effort pin - Plan + +## Goal Capsule + +- **Objective:** A checkout can pin reasoning effort for implementation, planning/brainstorm elevation, and review on one vocabulary that includes Codex `max` and `none`, and an explicit pin the route cannot honor is skipped rather than swapped for another tier. +- **Means:** Smallest patches to existing adapters, validators, config keys, and docs (KTD1, KTD2, KTD3). +- **Authority:** Issues #1569, #1415, #1565 and session-settled Key Decisions outrank inferred polish. `AGENTS.md` config-maintenance and skill-isolation rules outrank a shared-file extraction. +- **Execution profile:** Three units, one PR. Mechanical tests on existing `--emit-adapter` and health suites. Invoke `ce-skill-work` before editing anything under `skills/**`. +- **Stop conditions:** Stop if honoring a work pin requires expanding the authorization key-set or sharing `CROSS_MODEL_EFFORT_OVERRIDE` with work. Do not add OpenCode `model#variant` config syntax. +- **Tail ownership:** The invoking pipeline (`lfg`) owns simplify, review, commit, PR, and CI. + +--- + +## Product Contract + +### Summary + +Add an optional `effort` field on `work_engine_preferences`, sibling `plan_effort` / `brainstorm_effort` scalars, and one shared effort vocabulary reused by review, work, and elevation. Codex review (and any other Codex consumer of that vocabulary) accepts `max` and `none`. Omission keeps today's editorial `high` on work and elevation. Unhonored tokens skip; they are never substituted. + +Product Contract preservation: N/A (bootstrap). + +### Problem Frame + +Work adapters hard-code `high` (#1569). Elevation's Claude CLI worker hard-codes `EFFORT="high"` (#1415). Review's Codex whitelist still rejects `max` and `none` even though the Codex CLI accepts them (#1565). A checkout can pin review effort today and cannot make the matching choice for implementation or elevated planning. + +### Key Decisions + +- **Smallest possible pins.** (session-settled: user-directed — chosen over a broad architecture: a thrown-away prior implementation was too large.) Governs R1, R8, R9. +- **Separate work and review knobs.** (session-settled: user-directed — chosen over sharing `CROSS_MODEL_EFFORT_OVERRIDE` with work.) Governs R2, R7. +- **One shared vocabulary including Codex `max` and `none`.** (session-settled: user-directed — chosen over reusing today's review Codex whitelist as-is.) Governs R4, R5. +- **Omission keeps editorial `high`.** (session-settled: user-directed — chosen over changing the standing default.) Governs R3. +- **OpenCode 2 `model#variant` is out of scope.** (session-settled: user-directed — chosen over encoding effort in the OpenCode model string.) Governs R9. +- **No new subsystems.** (session-settled: user-directed — chosen over carrier grammar, authorization key-set, receipts, attestation, and `CONCEPTS.md` entries.) Governs R8, R10. + +### Requirements + +**Pins** + +- R1. `work_engine_preferences` entries may include optional `effort`. When a pin is set and the selected route honors it, `ce-work` adapters use that token instead of hard-coded `high`. +- R2. `plan_effort` and `brainstorm_effort` are ordinary sibling scalars next to `plan_model` / `brainstorm_model`. They use the same local-then-tracked cascade. +- R3. Omitting a work or elevation effort pin leaves the editorial tier `high` unchanged. + +**Vocabulary and fail-closed** + +- R4. The shared effort vocabulary is `none|minimal|low|medium|high|xhigh|max|default`. Review, work, and elevation reuse it. Codex accepts `max` and `none`. +- R5. A token the resolved route cannot honor is not applied and is never replaced with another legal token. The adapter fail-closes if that token is still passed in. The orchestrator omits the override instead, with a named reason, and keeps the candidate. +- R6. Cursor work routes take no effort override. A YAML `effort` on those routes is omitted per R5; the candidate still runs. + +**Non-expansion** + +- R7. Work does not read `CROSS_MODEL_EFFORT_OVERRIDE`. Review keeps that env var. +- R8. Health accepts the new keys so they are not reported as malformed. It does not grow new effort-value checks or report fields. +- R9. This work does not add OpenCode 2 `model#variant` config, authorization fields, attestation, or new receipt keys. The LFG `implementation_engine` carrier includes `effort` (`null` when unset); that value is not copied into the authorization schema. +- R10. Config template, byte-identical example, `docs/guides/configuration.md`, and consumer skill docs name the new keys in the same change. + +### Success Criteria + +- SC1. `--emit-adapter` for work Codex/Claude/Grok shows the pinned token when set and `high` when unset. +- SC2. Review Codex `--emit-adapter` accepts `max` and `none`; the old fail-closed `["codex", "max"]` row is gone. +- SC3. Elevation `--emit-adapter` shows `--effort` from `plan_effort` / `brainstorm_effort` when set, else `high`. +- SC4. A `work_engine_preferences` entry with `effort:` does not make `check-health` report an unsupported entry. + +### Acceptance Examples + +- AE1. Work pin honored + - **Covers:** R1, R3, R4 + - **Given:** a Codex preference with `effort: max` + - **When:** the work adapter emits argv + - **Then:** Codex carries `model_reasoning_effort=max`, not `high` +- AE2. Elevation omission + - **Covers:** R2, R3 + - **Given:** `plan_model` set and `plan_effort` unset + - **When:** the elevation worker emits argv + - **Then:** `--effort high` remains +- AE3. Unhonored token + - **Covers:** R5, R6 + - **Given:** `effort: xhigh` on a Cursor work preference + - **When:** that candidate is selected + - **Then:** `CE_WORK_EFFORT_OVERRIDE` is not exported; the Cursor adapter runs with no effort flag +- AE5. Adapter fail-closed + - **Covers:** R5 + - **Given:** `CE_WORK_EFFORT_OVERRIDE=banana` on Codex, or `=high` on Cursor + - **When:** `--emit-adapter` runs + - **Then:** exit 2 with a named reason; argv does not contain a substituted legal token +- AE4. Codex review `max` + - **Covers:** R4, R5 + - **Given:** `CROSS_MODEL_EFFORT_OVERRIDE=max` on the Codex review route + - **When:** `--emit-adapter codex` + - **Then:** the command includes Codex `max` and does not fail closed as "not a Codex level" + +### Scope Boundaries + +- In: optional work `effort`; `plan_effort` / `brainstorm_effort`; LFG `implementation_engine.effort`; shared vocabulary; Codex `max`/`none`; docs/template/health acceptance. +- Out: OpenCode 2 `model#variant`; sharing `CROSS_MODEL_EFFORT_OVERRIDE` with work; health value validation; new receipt keys; attestation; authorization key-set; `ce-pov` hard-coded `high`. +- **Deferred to Follow-Up Work:** live-prompt effort wording for elevation beyond config scalars; OpenCode work `--effort` (that adapter has no effort flag today). + +--- + +## Planning Contract + +### Assumptions + +- A1. Work effort reaches `cross-model-work.sh` as `CE_WORK_EFFORT_OVERRIDE` on the runner `start` environment (same inherit path as `CE_PEER_HARD_SECS`). It is not an `env` argv prefix and not an authorization field. +- A2. Elevation effort applies to the Claude CLI worker only. Native in-harness dispatch has no per-call effort flag; session effort stays as the host provides it. +- A3. The shared enum is a duplicated token list in existing validators, not a new shared file (skills cannot import across directories). +- A4. OpenCode and Cursor work routes take no effort override this change. +- A5. Elevation effort is config-only this change; no new `plan_effort:` carrier. + +### Key Technical Decisions + +- KTD1. **Patch existing functions, do not add subsystems.** (session-settled: user-directed — chosen over a broad architecture: prior implementation was too large.) Add `route_effort` / `validate_effort_override` next to work's existing `route_model`; replace `EFFORT="high"` in `elevation-dispatch.sh`; extend review's existing Codex case. Cite R1, R2, R4, R9. +- KTD2. **Work uses `CE_WORK_EFFORT_OVERRIDE`, not the review env var and not authorization.** (session-settled: user-directed — chosen over sharing `CROSS_MODEL_EFFORT_OVERRIDE`. Binding may carry `effort`; authorization does not.) Runner `worker_env` already forwards `os.environ`. Cite R7, R8, R9. +- KTD3. **Per-route honor set stays fail-closed; only Codex's set grows.** Claude stays `low|medium|high|xhigh|max`. Grok stays `low|medium|high`. Codex becomes `none|minimal|low|medium|high|xhigh|max`. Cursor-family and OpenCode work reject any override. Editorial defaults stay: work/elevation `high`, review Codex `xhigh`. Cite R3, R4, R5, R6. +- KTD4. **Health awk learns `effort` as a known optional field and ignores the value.** Same dash-first and indented shapes as `model`. `ITEM` output stays harness + model. Cite R8. + +### High-Level Technical Design + +Pin resolution is one gate on three consumers: + +```mermaid +flowchart TB + pin[Config pin or unset] + unset{Pin set?} + vocab{In shared vocabulary?} + route{This route honors it?} + editorial[Editorial default] + skip[Skip pin - named reason] + honor[Pass token to existing flag] + pin --> unset + unset -->|no| editorial + unset -->|yes| vocab + vocab -->|no| skip + vocab -->|yes| route + route -->|no| skip + route -->|yes| honor +``` + +Work Codex/Claude/Grok already have an effort flag; replace the literal `high`. Review Codex already has a whitelist; add `none` and `max`. Elevation CLI already passes `--effort "$EFFORT"`; stop hard-coding the value. + +### Implementation Constraints + +- Invoke `ce-skill-work` before editing `skills/**`. +- Keep byte-identical pairs: review workers' `validate_effort_override`, both `elevation-dispatch.sh` copies, both `reasoning-elevation.md` copies, config template and `config.example.yaml`. +- Do not put CLI flags in config YAML. `effort` is intent; adapters choose the flag (`--effort` vs `model_reasoning_effort=`). + +### Sequencing + +U1 (shared enum + review Codex) first so work and elevation validators copy a current list. U2 (work) and U3 (elevation + docs) follow. One PR. + +--- + +## Implementation Units + +### U1. Shared vocabulary and Codex review `max`/`none` + +- **Goal:** Review accepts Codex `none` and `max`. The shared vocabulary is the one work and elevation will copy. +- **Requirements:** R4, R5, R10 +- **Dependencies:** none +- **Files:** + - `skills/ce-code-review/scripts/cross-model-adversarial-review.sh` + - `skills/ce-doc-review/scripts/cross-model-doc-review.sh` + - `skills/ce-code-review/references/cross-model-review.md` (comment/docs if they restate the Codex list) + - `tests/skills/ce-code-review-cross-model-routes.test.ts` + - `tests/skills/ce-doc-review-cross-model-routes.test.ts` (only if it duplicates the fail-closed table) +- **Approach:** + 1. Extend `validate_effort_override` Codex arm with `codex:none` and `codex:max`. Update the comment that restates the list. + 2. Keep the function byte-identical across the two review workers. + 3. Remove `["codex", "max"]` from the fail-closed table. Add happy-path `--emit-adapter` coverage for Codex `max` and `none`. + 4. Do not change editorial `route_effort` defaults. +- **Patterns to follow:** Existing `validate_effort_override` / `route_effort` split; fail-closed tests around `tests/skills/ce-code-review-cross-model-routes.test.ts`. +- **Test scenarios:** + - Codex override `max` emits `model_reasoning_effort` `max` and exit 0. + - Codex override `none` emits `none` and exit 0. + - Claude override `minimal` still fails closed. + - Cursor override `high` still fails closed. + - Unset still emits Codex editorial `xhigh`. + - `validate_effort_override` bodies stay byte-identical across the two scripts. +- **Verification:** `bun test tests/skills/ce-code-review-cross-model-routes.test.ts` (and the doc-review counterpart if touched). + +### U2. Work-engine optional `effort` + +- **Goal:** A preference `effort` pin replaces hard-coded `high` on Codex/Claude/Grok work adapters. +- **Requirements:** R1, R3, R4, R5, R6, R7, R8 +- **Dependencies:** U1 +- **Files:** + - `skills/ce-work/scripts/cross-model-work.sh` + - `skills/ce-work/references/execution-engines.md` + - `skills/ce-work/references/cross-model-execution.md` + - `skills/ce-setup/scripts/check-health` + - `tests/skills/ce-work-cross-model-routes.test.ts` + - `tests/skills/ce-setup-check-health.test.ts` +- **Approach:** + 1. Add `route_effort` / `validate_effort_override` that read `CE_WORK_EFFORT_OVERRIDE`, copy U1's per-route honor set, and keep editorial `high` when unset. + 2. In `adapter_argv`, replace literal `high` on Codex, Claude, and Grok with `route_effort`. Do not add an effort flag to Cursor, Composer, Grok-Cursor, or OpenCode. + 3. If `CE_WORK_EFFORT_OVERRIDE` is set and the route cannot honor it, fail closed the same way model override already does. + 4. Document: when the selected preference has `effort` and the route honors it, export `CE_WORK_EFFORT_OVERRIDE` on the runner `start` call. On Cursor, Composer, Grok-Cursor, and OpenCode, omit the env even if YAML has `effort`, and name that skip. Do not put it on worker argv. Do not read `CROSS_MODEL_EFFORT_OVERRIDE`. + 5. In `read_work_engine_preferences`, accept `effort` in the same dash-first and indented shapes as `model`. Do not emit it. Do not validate the token. +- **Execution note:** Invoke `ce-skill-work` before editing ce-work or ce-setup skill files. +- **Patterns to follow:** `CE_WORK_MODEL_OVERRIDE` + `validate_model_override`; health parser's existing `model` arms; `--emit-adapter` tests in `tests/skills/ce-work-cross-model-routes.test.ts`. +- **Test scenarios:** + - Unset: Codex still emits `model_reasoning_effort=high`; Claude/Grok still `--effort high`. + - `CE_WORK_EFFORT_OVERRIDE=max` on Codex replaces `high` with `max`. + - `CE_WORK_EFFORT_OVERRIDE=xhigh` on Claude replaces `high`. + - `CE_WORK_EFFORT_OVERRIDE=high` on Cursor exits 2 with a named incompatibility. + - `CE_WORK_EFFORT_OVERRIDE=banana` on Codex exits 2; argv does not contain a substituted legal token. + - Health: a valid preference with `effort: max` does not print `unsupported work_engine_preferences entry`. + - Health: `effort` does not appear as a new availability/detail field. +- **Verification:** `bun test tests/skills/ce-work-cross-model-routes.test.ts tests/skills/ce-setup-check-health.test.ts`. + +### U3. Elevation scalars and config docs + +- **Goal:** `plan_effort` / `brainstorm_effort` drive the Claude CLI elevation worker. Template, example, and guides name every new key. +- **Requirements:** R2, R3, R4, R5, R9, R10 +- **Dependencies:** U1 +- **Files:** + - `skills/ce-plan/scripts/elevation-dispatch.sh` + - `skills/ce-brainstorm/scripts/elevation-dispatch.sh` + - `skills/ce-plan/references/reasoning-elevation.md` + - `skills/ce-brainstorm/references/reasoning-elevation.md` + - `skills/ce-setup/references/config-template.yaml` + - `.compound-engineering/config.example.yaml` + - `docs/guides/configuration.md` + - `docs/guides/ce-plan.md` + - `docs/guides/ce-brainstorm.md` + - `docs/guides/ce-work.md` + - `tests/skills/elevation-dispatch.test.ts` + - `tests/skills/ce-setup-check-health.test.ts` (template advertises the new keys) +- **Approach:** + 1. Replace `EFFORT="high"` with editorial `high` unless `CE_ELEVATION_EFFORT_OVERRIDE` is set. Validate against the Claude honor set from U1. Unhonored: skip (named reason), never substitute. + 2. In `reasoning-elevation.md`, resolve `plan_effort` / `brainstorm_effort` with the ordinary-key rule next to the model key. When set, export `CE_ELEVATION_EFFORT_OVERRIDE` on the existing `start` invocation. Keep both copies byte-identical. + 3. Comment the new keys in the template beside `plan_model` / `brainstorm_model` and as optional `effort` on the work-engine example. Copy byte-identically to `config.example.yaml`. + 4. Update `docs/guides/configuration.md` (options table + implementation routing) and the three consumer guides. Codex review docs change `minimal..xhigh` to include `none` and `max`. +- **Execution note:** Invoke `ce-skill-work` before editing ce-plan / ce-brainstorm skill files. +- **Patterns to follow:** Model-elevation config comments; `tests/skills/elevation-dispatch.test.ts` argv assertions; `keeps the committed example identical to the bundled template`. +- **Test scenarios:** + - Unset: `--emit-adapter` still contains `--effort` `high`. + - `CE_ELEVATION_EFFORT_OVERRIDE=xhigh` emits `--effort` `xhigh` and not `high`. + - `CE_ELEVATION_EFFORT_OVERRIDE=minimal` fails closed (not a Claude level). + - Both elevation workers stay byte-identical; both `reasoning-elevation.md` copies stay byte-identical. + - Template contains `plan_effort`, `brainstorm_effort`, and work `effort`; example equals template; `configuration.md` names each key. +- **Verification:** `bun test tests/skills/elevation-dispatch.test.ts tests/reasoning-elevation-parity.test.ts tests/skills/ce-setup-check-health.test.ts`. + +--- + +## Verification Contract + +| Gate | When | Command / signal | +|---|---|---| +| Unit tests | After U1–U3 | `bun test tests/skills/ce-code-review-cross-model-routes.test.ts tests/skills/ce-work-cross-model-routes.test.ts tests/skills/elevation-dispatch.test.ts tests/skills/ce-setup-check-health.test.ts tests/reasoning-elevation-parity.test.ts` | +| Full suite | Before PR | `bun run test` (same suite CI runs; includes `--parallel`) | +| Release metadata | If skill/docs counts or config surfaces change | `bun run release:validate` | +| Skill prose | Before editing `skills/**` | Invoke `ce-skill-work` | + +--- + +## Definition of Done + +- Global: R1–R10 hold on the diff. No authorization, LFG engine, OpenCode `model#variant`, shared review env var, receipts, or `CONCEPTS.md` changes. Abandoned spikes are absent. +- U1: Codex `max`/`none` accepted; fail-closed table updated; review workers identical. +- U2: Work `--emit-adapter` honors a pin and keeps `high` on omission; Cursor pin fails closed; health accepts `effort`. +- U3: Elevation omission stays `high`; template/example/docs name the keys. +- PR: draft; body includes `Fixes #1569 #1415 #1565` plus Security Disclosure and Agent Disclosure. + +--- + +## System-Wide Impact + +Config schema is a public checkout contract. Review, work, and elevation all consume the same vocabulary but keep separate override env vars. Health remains a parser, not an effort policy engine. + +## Risks & Dependencies + +- **Health awk shapes.** Unusual YAML (flow maps, multiline) already fails today. Do not turn this into a general YAML parser. +- **Byte-identical drift.** Editing one review worker, one elevation script, or only the template (not the example) fails CI. +- **Idle budget at `max`.** A work pin of `max` can lengthen silent stretches; `CE_PEER_IDLE_SECS` stays as-is this change (#1565 caveat). + +## Documentation / Operational Notes + +- Central reference: `docs/guides/configuration.md`. +- Consumer pages: `docs/guides/ce-work.md`, `ce-plan.md`, `ce-brainstorm.md`. +- Template comments are the in-checkout documentation surface `/ce-setup` copies. + +## Sources / Research + +- #1569 work hard-coded `high`; #1415 elevation `EFFORT="high"`; #1565 Codex CLI enum includes `none` and `max`. +- Work adapter: `skills/ce-work/scripts/cross-model-work.sh` `adapter_argv`. +- Review validator: `validate_effort_override` in both cross-model review scripts; fail-closed row `["codex", "max"]` in `tests/skills/ce-code-review-cross-model-routes.test.ts`. +- Elevation worker: `skills/ce-plan/scripts/elevation-dispatch.sh` (byte-identical brainstorm copy). +- Runner forwards `os.environ` into `worker_env` (`skills/ce-work/scripts/peer-job-runner.py`). +- Health parser: `read_work_engine_preferences` in `skills/ce-setup/scripts/check-health`. diff --git a/skills/ce-brainstorm/references/reasoning-elevation.md b/skills/ce-brainstorm/references/reasoning-elevation.md index a3fe2b0c6..dccba5e8c 100644 --- a/skills/ce-brainstorm/references/reasoning-elevation.md +++ b/skills/ce-brainstorm/references/reasoning-elevation.md @@ -23,7 +23,7 @@ If the session model already **is** the resolved model, elevation is moot: skip When elevation is active, resolve an adapter in this fixed order and use the first that serves the requested model: 1. **Native in-harness dispatch.** Attempt the platform subagent primitive with a per-agent model override (e.g. `model: "fable"` on the Claude Code `Agent`/`Task` tool). Capability is proven by attempt, not self-assessment — a harness that can serve the model natively does; one that cannot fails the attempt and falls through. **Receipt rule (R6):** a native run whose serving-side receipt names a *different* model family than requested falls through to the next adapter; a run with *no* receipt proceeds and is recorded as unverified (it does NOT fall through). -2. **Claude CLI.** Run the bundled `scripts/elevation-dispatch.sh` worker as a detached job (see Off-host dispatch). Available when `claude` is on PATH. Do not preflight authentication in the host command context: the detached worker's provider-capable call is authoritative, and an authentication failure there follows Recovery. +2. **Claude CLI.** Run the bundled `scripts/elevation-dispatch.sh` worker as a detached job (see Off-host dispatch). Available when `claude` is on PATH. Read `plan_effort` / `brainstorm_effort` with the same ordinary-key rule as the model key and export `CE_ELEVATION_EFFORT_OVERRIDE` on `start` when the token is honored. Do not preflight authentication in the host command context: the detached worker's provider-capable call is authoritative, and an authentication failure there follows Recovery. 3. **Inline on the session model.** The always-available fallback. Elevation is never a correctness dependency: every adapter failure degrades to the next, and inline always completes the run. diff --git a/skills/ce-brainstorm/scripts/elevation-dispatch.sh b/skills/ce-brainstorm/scripts/elevation-dispatch.sh index e28e1dc63..dca78d5eb 100755 --- a/skills/ce-brainstorm/scripts/elevation-dispatch.sh +++ b/skills/ce-brainstorm/scripts/elevation-dispatch.sh @@ -31,7 +31,13 @@ RUN_SUCCEEDED=false log() { printf '[elevation] %s\n' "$*" >&2; } -EFFORT="high" # settled: elevation runs at high effort +EFFORT="high" +if [ -n "${CE_ELEVATION_EFFORT_OVERRIDE:-}" ]; then + case "$CE_ELEVATION_EFFORT_OVERRIDE" in + low|medium|high|xhigh|max) EFFORT="$CE_ELEVATION_EFFORT_OVERRIDE" ;; + *) log "effort override '${CE_ELEVATION_EFFORT_OVERRIDE}' not compatible with claude elevation"; exit 2 ;; + esac +fi # Read-only tool posture (R7): the available built-in set, not a denylist. The # elevated step reads the repo (Read/Glob/Grep) and may check current facts on diff --git a/skills/ce-code-review/scripts/cross-model-adversarial-review.sh b/skills/ce-code-review/scripts/cross-model-adversarial-review.sh index 04f8ae119..7699b5f9d 100755 --- a/skills/ce-code-review/scripts/cross-model-adversarial-review.sh +++ b/skills/ce-code-review/scripts/cross-model-adversarial-review.sh @@ -330,7 +330,7 @@ validate_model_override() { # Accept an effort override only where the route exposes an effort flag and the # value is one that CLI documents (claude: low|medium|high|xhigh|max; codex -# model_reasoning_effort: minimal|low|medium|high|xhigh; grok: low|medium|high). +# model_reasoning_effort: none|minimal|low|medium|high|xhigh|max; grok: low|medium|high). # cursor-agent routes imply effort in the model id, so any override there is # invalid for the route rather than silently dropped. Empty means "no override". validate_effort_override() { @@ -338,7 +338,7 @@ validate_effort_override() { [ -n "$effort" ] || return 0 case "$route:$effort" in claude:low|claude:medium|claude:high|claude:xhigh|claude:max) ;; - codex:minimal|codex:low|codex:medium|codex:high|codex:xhigh) ;; + codex:none|codex:minimal|codex:low|codex:medium|codex:high|codex:xhigh|codex:max) ;; grok-cli:low|grok-cli:medium|grok-cli:high) ;; opencode:none|opencode:minimal|opencode:low|opencode:medium|opencode:high|opencode:xhigh|opencode:max|opencode:default) ;; *) return 1 ;; diff --git a/skills/ce-doc-review/scripts/cross-model-doc-review.sh b/skills/ce-doc-review/scripts/cross-model-doc-review.sh index a3b897d30..757e695ee 100755 --- a/skills/ce-doc-review/scripts/cross-model-doc-review.sh +++ b/skills/ce-doc-review/scripts/cross-model-doc-review.sh @@ -320,7 +320,7 @@ validate_model_override() { # Accept an effort override only where the route exposes an effort flag and the # value is one that CLI documents (claude: low|medium|high|xhigh|max; codex -# model_reasoning_effort: minimal|low|medium|high|xhigh; grok: low|medium|high). +# model_reasoning_effort: none|minimal|low|medium|high|xhigh|max; grok: low|medium|high). # cursor-agent routes imply effort in the model id, so any override there is # invalid for the route rather than silently dropped. Empty means "no override". validate_effort_override() { @@ -328,7 +328,7 @@ validate_effort_override() { [ -n "$effort" ] || return 0 case "$route:$effort" in claude:low|claude:medium|claude:high|claude:xhigh|claude:max) ;; - codex:minimal|codex:low|codex:medium|codex:high|codex:xhigh) ;; + codex:none|codex:minimal|codex:low|codex:medium|codex:high|codex:xhigh|codex:max) ;; grok-cli:low|grok-cli:medium|grok-cli:high) ;; opencode:none|opencode:minimal|opencode:low|opencode:medium|opencode:high|opencode:xhigh|opencode:max|opencode:default) ;; *) return 1 ;; diff --git a/skills/ce-plan/references/reasoning-elevation.md b/skills/ce-plan/references/reasoning-elevation.md index a3fe2b0c6..dccba5e8c 100644 --- a/skills/ce-plan/references/reasoning-elevation.md +++ b/skills/ce-plan/references/reasoning-elevation.md @@ -23,7 +23,7 @@ If the session model already **is** the resolved model, elevation is moot: skip When elevation is active, resolve an adapter in this fixed order and use the first that serves the requested model: 1. **Native in-harness dispatch.** Attempt the platform subagent primitive with a per-agent model override (e.g. `model: "fable"` on the Claude Code `Agent`/`Task` tool). Capability is proven by attempt, not self-assessment — a harness that can serve the model natively does; one that cannot fails the attempt and falls through. **Receipt rule (R6):** a native run whose serving-side receipt names a *different* model family than requested falls through to the next adapter; a run with *no* receipt proceeds and is recorded as unverified (it does NOT fall through). -2. **Claude CLI.** Run the bundled `scripts/elevation-dispatch.sh` worker as a detached job (see Off-host dispatch). Available when `claude` is on PATH. Do not preflight authentication in the host command context: the detached worker's provider-capable call is authoritative, and an authentication failure there follows Recovery. +2. **Claude CLI.** Run the bundled `scripts/elevation-dispatch.sh` worker as a detached job (see Off-host dispatch). Available when `claude` is on PATH. Read `plan_effort` / `brainstorm_effort` with the same ordinary-key rule as the model key and export `CE_ELEVATION_EFFORT_OVERRIDE` on `start` when the token is honored. Do not preflight authentication in the host command context: the detached worker's provider-capable call is authoritative, and an authentication failure there follows Recovery. 3. **Inline on the session model.** The always-available fallback. Elevation is never a correctness dependency: every adapter failure degrades to the next, and inline always completes the run. diff --git a/skills/ce-plan/scripts/elevation-dispatch.sh b/skills/ce-plan/scripts/elevation-dispatch.sh index e28e1dc63..dca78d5eb 100755 --- a/skills/ce-plan/scripts/elevation-dispatch.sh +++ b/skills/ce-plan/scripts/elevation-dispatch.sh @@ -31,7 +31,13 @@ RUN_SUCCEEDED=false log() { printf '[elevation] %s\n' "$*" >&2; } -EFFORT="high" # settled: elevation runs at high effort +EFFORT="high" +if [ -n "${CE_ELEVATION_EFFORT_OVERRIDE:-}" ]; then + case "$CE_ELEVATION_EFFORT_OVERRIDE" in + low|medium|high|xhigh|max) EFFORT="$CE_ELEVATION_EFFORT_OVERRIDE" ;; + *) log "effort override '${CE_ELEVATION_EFFORT_OVERRIDE}' not compatible with claude elevation"; exit 2 ;; + esac +fi # Read-only tool posture (R7): the available built-in set, not a denylist. The # elevated step reads the repo (Read/Glob/Grep) and may check current facts on diff --git a/skills/ce-setup/references/config-template.yaml b/skills/ce-setup/references/config-template.yaml index 49cd37d86..63bb7f123 100644 --- a/skills/ce-setup/references/config-template.yaml +++ b/skills/ce-setup/references/config-template.yaml @@ -72,7 +72,9 @@ # elevation. # plan_model: fable # model alias for the plan-authoring step (no default) +# plan_effort: high # optional Claude CLI effort; default: high # brainstorm_model: fable # model alias for approach generation (no default) +# brainstorm_effort: high # optional Claude CLI effort; default: high # --- PR concept teaching (ce-commit-push-pr) --- # When a PR introduces a concept new to the codebase, the generated PR @@ -123,8 +125,8 @@ # `fable` or a full id such as `claude-opus-5` for claude; `gpt-*` for codex, # optionally namespace-qualified such as `openai.gpt-5.6-sol` when the codex CLI # routes through a non-default `model_provider`). -# Effort levels: claude low|medium|high|xhigh|max, codex minimal|low|medium| -# high|xhigh, grok low|medium|high; cursor/composer routes take none. A value +# Effort levels: claude low|medium|high|xhigh|max, codex none|minimal|low|medium| +# high|xhigh|max, grok low|medium|high; cursor/composer routes take none. A value # the peer cannot honor skips the pass with a stated reason -- never substituted. # cross_model_review_mode: off # auto | off (default: auto) @@ -151,6 +153,7 @@ # model: composer # - harness: codex # model: "gpt-5.6" +# effort: max # optional; default: high # - harness: claude # configured default model # --- ce-promote --- diff --git a/skills/ce-setup/scripts/check-health b/skills/ce-setup/scripts/check-health index 973cd2038..6b85a2664 100755 --- a/skills/ce-setup/scripts/check-health +++ b/skills/ce-setup/scripts/check-health @@ -278,6 +278,15 @@ read_work_engine_preferences() { if (!have_item) have_item = 1 next } + if (line ~ /^[[:space:]]*-[[:space:]]*effort[[:space:]]*:/) { + emit_item() + have_item = 1 + next + } + if (line ~ /^[[:space:]]+effort[[:space:]]*:/) { + if (!have_item) have_item = 1 + next + } print "ERROR\tunsupported work_engine_preferences entry: " trim(line) } END { diff --git a/skills/ce-work/references/cross-model-execution.md b/skills/ce-work/references/cross-model-execution.md index b3cbd2c82..34c54d0b5 100644 --- a/skills/ce-work/references/cross-model-execution.md +++ b/skills/ce-work/references/cross-model-execution.md @@ -52,7 +52,7 @@ Ordinary synchronous native units stay in the active checkout. Ordinary native s Direct and return-to-caller runs expose the same receipt facts even though their prompting and shipping tails differ: -- `implementation_engine_binding`: resolved `mode`, `target`, `model`, and `source`; +- `implementation_engine_binding`: resolved `mode`, `target`, `model`, `effort`, and `source`; - `requested_route` and `actual_route`, including every intermediary; - `requested_model`, `actual_model`, and served-model receipt status; - `fallback_reason` or `null`; @@ -92,7 +92,7 @@ When set, `CE_WORK_RUNS_ROOT` is the parent CE Work directory containing all `` with skill `ce-work`; the runner label must equal the unit id exactly, without the attempt id or another suffix, and `--result-path` must be `/implementation-result.json`. Both `--input-digest` and the adapter's expected-packet argument must use the exact `packet_digest` returned by `prepare`; omitting the runner flag or recomputing/substituting either value makes the job ineligible for `record-job`. The runner and controller share the same root automatically: `CE_WORK_RUNS_ROOT` wins when set, otherwise both derive CE Work state from `CE_PEER_JOBS_ROOT`. Set `CE_PEER_HARD_SECS=7200` on every production CE Work runner start rather than relying on the shared runner default. Set `CE_PEER_IDLE_SECS=600` for route-qualified `incremental` activity and `CE_PEER_IDLE_SECS=0` for `hard-only` or otherwise untrustworthy activity. The 600-second window resets on progress and detects a stall; it is not a wall-clock maximum. Its production worker command is `cross-model-work.sh `; invoke the returned adapter path directly as the first worker argv, without a `bash`, `sh`, or `env` prefix, and pass only the controller-returned `authorization_path`, never a caller-supplied route string or ambient model override. The runner exports its controller-visible job id to the worker. Before prompt construction or external CLI start, the adapter must pass that runner-exported job id and obtain controller `authorize-dispatch` success by calling `unit-workspace.py` `authorize-dispatch`. That success reads the actual runner metadata and exact worker argv, rejecting a shell prefix or substituted adapter before egress, then atomically binds that job id to the exact attempt before egress along with the authorization digest, workspace, packet path and digest, and result directory while revalidating the controller-owned exact route, model, and intermediary contract. A second job for the attempt is refused. A missing or refused handshake, including hand-authored or cross-attempt authorization, refuses egress. Model pins come only from the authorized projection. The adapter's `--emit-adapter` mode remains introspection only and never authorizes production dispatch. Capture the returned job id immediately with `unit-workspace.py` `record-job`, using the controller-returned `attempt_id` verbatim; this idempotently confirms the same validated binding. Never hold one host tool call open for the external runtime and never let the adapter select another recipient. +4. **Start one fixed author.** The controller-issued authorization schema binds `run_id`, `unit_id`, and `attempt_id` to the fixed route/model/intermediary and packet contract. Call `peer-job-runner.py` `start --no-sweep --input-digest ` with skill `ce-work`; the runner label must equal the unit id exactly, without the attempt id or another suffix, and `--result-path` must be `/implementation-result.json`. Both `--input-digest` and the adapter's expected-packet argument must use the exact `packet_digest` returned by `prepare`; omitting the runner flag or recomputing/substituting either value makes the job ineligible for `record-job`. The runner and controller share the same root automatically: `CE_WORK_RUNS_ROOT` wins when set, otherwise both derive CE Work state from `CE_PEER_JOBS_ROOT`. Set `CE_PEER_HARD_SECS=7200` on every production CE Work runner start rather than relying on the shared runner default. Set `CE_PEER_IDLE_SECS=600` for route-qualified `incremental` activity and `CE_PEER_IDLE_SECS=0` for `hard-only` or otherwise untrustworthy activity. The 600-second window resets on progress and detects a stall; it is not a wall-clock maximum. Its production worker command is `cross-model-work.sh `; invoke the returned adapter path directly as the first worker argv, without a `bash`, `sh`, or `env` prefix, and pass only the controller-returned `authorization_path`, never a caller-supplied route string or ambient model override. The runner exports its controller-visible job id to the worker. Before prompt construction or external CLI start, the adapter must pass that runner-exported job id and obtain controller `authorize-dispatch` success by calling `unit-workspace.py` `authorize-dispatch`. That success reads the actual runner metadata and exact worker argv, rejecting a shell prefix or substituted adapter before egress, then atomically binds that job id to the exact attempt before egress along with the authorization digest, workspace, packet path and digest, and result directory while revalidating the controller-owned exact route, model, and intermediary contract. A second job for the attempt is refused. A missing or refused handshake, including hand-authored or cross-attempt authorization, refuses egress. Model and effort pins come only from the authorized projection. The adapter's `--emit-adapter` mode remains introspection only and never authorizes production dispatch. Capture the returned job id immediately with `unit-workspace.py` `record-job`, using the controller-returned `attempt_id` verbatim; this idempotently confirms the same validated binding. Never hold one host tool call open for the external runtime and never let the adapter select another recipient. 5. **Observe without steering.** Interleave runner `status --skill ce-work` or `wait --skill ce-work --max-secs 60` calls with separate `unit-workspace.py` `sync-job` calls; every bare-job-id runner `status`, `wait`, `result`, or `reap` call must carry `--skill ce-work` under the same `CE_WORK_RUNS_ROOT` / `CE_PEER_JOBS_ROOT` selection used at start. Report unit, route, elapsed time, latest meaningful activity, activity posture, and terminal state after each cycle. For a qualified silent terminal-only route, `hard-only` is the normal posture: disable idle timeout, retain the universal hard cap, and never infer failure or fallback merely from absent incremental activity. A live or temporarily unreachable attempt is still authoritative: do not start fallback or duplicate work. Reap only through the explicit controller/runner path and retain its workspace. 6. **Terminalize complete Git output.** On authoritative `done`, call `unit-workspace.py` `terminalize`. Require its pinned synthetic transport commit to have the recorded base as sole parent and the complete final workspace tree. In a later host call, inspect the actual transport diff, changed paths, binary/mode/rename/delete evidence, adapter result, packet expected scope, and any scope-expansion request. A generated byproduct or any unexplained difference between actual transport paths, expected scope, and the worker's evidence is unexpected scope: preserve it for host resolution and do not acquire integration. 7. **Integrate through the fail-stop controller transaction.** After the separate scope-inspection call accepts the complete transport, invoke `unit-workspace.py integrate --run-id --unit-id --commit-message --verification-summary [--allowed-head ] -- `. Pass a simple Verification Contract command as direct argv. If it contains shell syntax such as `$(...)`, a pipe, `&&`, a redirect, or a glob, invoke an explicit shell with pipe-failure handling on the first attempt, for example `-- bash -o pipefail -c 'test "$(cat delegated.txt)" = "expected"'`; quoting `$(...)` as a direct argument does not expand it. Do not manually chain or conditionally reproduce this transaction. diff --git a/skills/ce-work/references/cross-model-work-eval.md b/skills/ce-work/references/cross-model-work-eval.md index fe2e8bc2a..78b95f307 100644 --- a/skills/ce-work/references/cross-model-work-eval.md +++ b/skills/ce-work/references/cross-model-work-eval.md @@ -55,7 +55,7 @@ without a receipt. | E3 direct require | `ce-work only use Composer for /plans/feature.md`; Composer route is unavailable inside the current host boundary; caller is interactive | Current-turn `require`; disclose the unavailable route once and continue on the current harness/session model without prompting, elevating, or substituting another external recipient. | | E4 Cursor identity | `ce-work use Cursor for /plans/feature.md` on a Cursor host with no distinct model request | `cursor` means Cursor's default route and collapses to native same-host execution; it is not rewritten to Composer. | | E5 no false model receipt | A successful external route has no trustworthy served-model receipt | Requested model/route remain distinct from actual; actual model is `unverified`, never guessed from the requested label. | -| E6 LFG carrier | LFG input says `use Codex for implementation`; earlier planning/review stages are about to run | Strip the routing directive from product input; retain exactly the four-field implementation carrier; pass it only in the portable CE Work return-to-caller envelope; LFG owns the shipping tail. | +| E6 LFG carrier | LFG input says `use Codex for implementation`; earlier planning/review stages are about to run | Strip the routing directive from product input; retain the implementation carrier; pass it only in the portable CE Work return-to-caller envelope; LFG owns the shipping tail. | | E7 config prefer | Headless LFG on Codex has no live or caller binding; config is `prefer` with ordered `codex@default`, `claude@default`; Claude is unavailable | Skip the equivalent Codex default, preflight Claude, then fall back once to native with both candidate outcomes disclosed; LFG continues its one shipping tail. | | E8 config require | Headless LFG has config `require` with ordered `cursor@composer`, `codex@default`; both are unavailable or equivalent to the host | Record both candidate outcomes, disclose the native fallback once, and continue on the current harness/session model without prompting, elevating, or substituting another external recipient; LFG retains tail ownership. | | E9 selected-plan dirt | The selected plan is the only dirty path before external dispatch | Disclose and create a plan-only checkpoint, record it in the run/envelope, then use its SHA as the clean unit base. | diff --git a/skills/ce-work/references/execution-engines.md b/skills/ce-work/references/execution-engines.md index ad75f5f95..e077675c9 100644 --- a/skills/ce-work/references/execution-engines.md +++ b/skills/ce-work/references/execution-engines.md @@ -21,7 +21,7 @@ Lower sources may fill an unspecified detail but cannot contradict or broaden a A live request such as "use Codex" is preference-strength by default. Interpret unambiguous strict intent such as "must use Codex" or "only use Codex" as requirement-strength; intent is the contract, not any single keyword. The resolved mode is `prefer` or `require`. Requirement strength fixes the requested external identity while that route is viable; it never authorizes another external recipient and does not turn route unavailability into a blocker. -Live or contextual intent may name one route or an ordered fallback list (for example, "prefer Cursor with Grok, then Codex"). Preserve that order and normalize each harness/model candidate with the same rules as standing configuration. A typed caller binding remains a single already-selected candidate; do not widen its exact four-field grammar into a list. +Live or contextual intent may name one route or an ordered fallback list (for example, "prefer Cursor with Grok, then Codex"). Preserve that order and normalize each harness/model candidate with the same rules as standing configuration. A typed caller binding remains a single already-selected candidate; do not widen its grammar into a list. For example, current-task strict Composer resolves to Composer with `require` even when a caller Codex binding and config Cursor preference are both present. Without that task instruction, a caller Codex binding sourced from the current LFG task keeps that provenance. Without applicable live or caller intent, the ordered config candidates apply only when standing mode is enabled. @@ -63,6 +63,7 @@ work_engine_preferences: - `work_engine_preferences`: one or more ordered candidate objects - `harness`: `codex | claude | grok | cursor | opencode` - optional `model`: a model id or family understood by that harness; omission means its configured default +- optional `effort`: default high; a token the route cannot honor is omitted, never substituted Do not put CLI commands or flags in configuration. The list expresses implementation intent; the skill's adapter recipes and local inspection determine how to invoke it. Composer is therefore `{ harness: cursor, model: composer }`, while `{ harness: cursor }` means Cursor's configured default. diff --git a/skills/ce-work/references/input-triage.md b/skills/ce-work/references/input-triage.md index 172c0a312..01e3485bc 100644 --- a/skills/ce-work/references/input-triage.md +++ b/skills/ce-work/references/input-triage.md @@ -24,7 +24,7 @@ This skill discovers plans under `/plans/`. Resolve `` when you firs **Recovery activation comes first.** Before normal plan, path, blank-input, or bare-prompt classification, interpret whether the user is semantically asking to resume, inspect status, reap, or clean up an existing external implementation run and has supplied its run id. This is intent recognition, not verb-only matching. Validate the id with the controller's safe-id contract: `^[A-Za-z0-9._-]{1,128}$` and at least one non-period character. When this direct recovery intent is present, read `references/cross-model-execution.md`, use that run id as authoritative for the requested controller operation, and return the observed state or blocker. Recovery must not dispatch a new worker, select a new route, fall through to latest-plan discovery, or run either shipping tail. When every unit is already cleaned, **completed recovery is read-only reconciliation**: Do not rerun test, build, format, install, generation, or `verify-run`; report the stored unit and plan-wide verification receipts. If recovery intent is clear but the run id is missing, request the id instead of guessing or classifying the text as new work. -**Otherwise, parse a leading mode token.** If `` begins with `mode:return-to-caller` (or the legacy aliases `mode:caller-owned-tail` / `caller:lfg`), strip that token before anything else and enter **Return-to-Caller Mode** — implement and locally verify only, then return the structured envelope instead of running the standalone shipping tail. Before the plan path, accept up to two optional carriers in this fixed order: first one compact JSON object prefixed exactly `implementation_engine:`, then one run id prefixed exactly `implementation_run:`. Fully validate and normalize both before any workspace action. The engine object must contain exactly four fields: `mode` is `prefer` or `require`; `target` is `codex`, `claude`, `grok`, `cursor`, `composer`, or `opencode`; `model` is a string pin or `null`; and `source` is a non-empty caller-visible provenance string. The run carrier is accepted only for return-to-caller recovery and must satisfy the safe-id contract above. Reject malformed JSON, missing/extra fields, invalid field types or values, an unsafe run id, an out-of-order carrier, or a duplicate carrier. The entire remaining string is the plan path. A mode token or carrier with no following path is an error; report it instead of treating control data as a bare prompt. Without either optional carrier, the original `mode:return-to-caller ` form is unchanged and standing configuration remains eligible. +**Otherwise, parse a leading mode token.** If `` begins with `mode:return-to-caller` (or the legacy aliases `mode:caller-owned-tail` / `caller:lfg`), strip that token before anything else and enter **Return-to-Caller Mode** — implement and locally verify only, then return the structured envelope instead of running the standalone shipping tail. Before the plan path, accept up to two optional carriers in this fixed order: first one compact JSON object prefixed exactly `implementation_engine:`, then one run id prefixed exactly `implementation_run:`. Fully validate and normalize both before any workspace action. The engine object must contain `mode`, `target`, `model`, and `source`: `mode` is `prefer` or `require`; `target` is `codex`, `claude`, `grok`, `cursor`, `composer`, or `opencode`; `model` is a string pin or `null`; `effort` is a string pin or `null` (omit for null); and `source` is a non-empty caller-visible provenance string. Reject malformed JSON, missing/extra fields, invalid field types or values, an unsafe run id, an out-of-order carrier, or a duplicate carrier. Omitted `effort` is not a missing field. The run carrier is accepted only for return-to-caller recovery and must satisfy the safe-id contract above. The entire remaining string is the plan path. A mode token or carrier with no following path is an error; report it instead of treating control data as a bare prompt. Without either optional carrier, the original `mode:return-to-caller ` form is unchanged and standing configuration remains eligible. When `implementation_run:` is present, recovery wins over ordinary input classification: read `references/cross-model-execution.md`, use `resume --run-id ` as the authoritative entrypoint, and return the normal Return-to-Caller envelope after reconciliation. Preserve the supplied `implementation_engine` binding when present. Do not resolve a different route, redispatch, reimplement, rerun completed verification, or start another caller tail. diff --git a/skills/ce-work/references/return-to-caller.md b/skills/ce-work/references/return-to-caller.md index 3fb3270df..5c34b4c5c 100644 --- a/skills/ce-work/references/return-to-caller.md +++ b/skills/ce-work/references/return-to-caller.md @@ -13,7 +13,7 @@ Return: - `u_ids_completed` - `verification_results` - `verification_evidence`: one entry per attempted behavior-bearing unit, plus any non-behavioral unit where tests were intentionally skipped. Each entry states the unit/task, `behavior_changed`, `existing_tests_inspected`, `tests_added_or_changed`, tests used unchanged, red failure or characterization observed when applicable, verification commands/results, and any exception reason. For units executed by subagents, this entry is assembled from each worker's returned evidence, not reconstructed from the diff — the red-before-implementation observation exists only in the worker's report. -- `implementation_engine_binding`: the resolved one-run `mode`, `target`, `model`, and `source`, or `null` when native execution was selected without a binding +- `implementation_engine_binding`: the resolved one-run `mode`, `target`, `model`, `effort`, and `source`, or `null` when native execution was selected without a binding - `requested_route` and `actual_route`: target plus harness/intermediary identity, kept separate when fallback or same-family substitution occurred - `requested_model` and `actual_model`: the request and receipt-attributed served identity (`unverified` when the route supplies no trustworthy receipt) - `fallback_reason`: `null` when none, otherwise the observed route-unavailable or substitution reason diff --git a/skills/ce-work/scripts/cross-model-work.sh b/skills/ce-work/scripts/cross-model-work.sh index df8abf884..b8a2dedb0 100755 --- a/skills/ce-work/scripts/cross-model-work.sh +++ b/skills/ce-work/scripts/cross-model-work.sh @@ -95,6 +95,40 @@ validate_model_override() { esac } +route_effort() { # -> authorized effort, else CE_WORK_EFFORT_OVERRIDE on emit-adapter, else high + if [ "${AUTH_BOUND:-}" = 1 ]; then + if [ -n "${EFFORT_REQUESTED:-}" ]; then + printf '%s' "$EFFORT_REQUESTED" + return + fi + printf 'high' + return + fi + if [ -n "${CE_WORK_EFFORT_OVERRIDE:-}" ]; then + case "$1" in + codex|claude|grok-cli|opencode) printf '%s' "$CE_WORK_EFFORT_OVERRIDE"; return 0 ;; + esac + fi + printf 'high' +} + +# Accept CE_WORK_EFFORT_OVERRIDE only where the route exposes an effort flag +# and the value is in that route's honor set (claude: low|medium|high|xhigh|max; +# codex: none|minimal|low|medium|high|xhigh|max; grok: low|medium|high; +# opencode --variant: none|minimal|low|medium|high|xhigh|max|default). +# Cursor-family work routes honor none. Empty means "no override". +validate_effort_override() { + local route="$1" effort="${CE_WORK_EFFORT_OVERRIDE:-}" + [ -n "$effort" ] || return 0 + case "$route:$effort" in + claude:low|claude:medium|claude:high|claude:xhigh|claude:max) ;; + codex:none|codex:minimal|codex:low|codex:medium|codex:high|codex:xhigh|codex:max) ;; + grok-cli:low|grok-cli:medium|grok-cli:high) ;; + opencode:none|opencode:minimal|opencode:low|opencode:medium|opencode:high|opencode:xhigh|opencode:max|opencode:default) ;; + *) return 1 ;; + esac +} + adapter_argv() { case "$1" in codex) @@ -102,7 +136,7 @@ adapter_argv() { # editorial tier explicitly, matching the claude/grok routes' --effort high. printf '%s\0' codex exec --ignore-user-config --ignore-rules --ephemeral \ -s workspace-write -C "$WORKSPACE" --json -o "$RAW_RESULT" \ - -c model_reasoning_effort=high + -c model_reasoning_effort="$(route_effort codex)" [ "$(route_model codex)" = auto ] || printf '%s\0' -m "$(route_model codex)" printf '%s\0' - ;; @@ -112,14 +146,14 @@ adapter_argv() { printf '%s\0' claude -p --safe-mode --no-session-persistence \ --permission-mode bypassPermissions --tools Read,Write,Edit,Bash \ --allowed-tools 'Bash(*)' \ - --effort high --output-format stream-json --verbose + --effort "$(route_effort claude)" --output-format stream-json --verbose [ "$claude_model" = auto ] || printf '%s\0' --model "$claude_model" ;; grok-cli) local grok_model grok_model="$(route_model grok-cli)" printf '%s\0' grok --prompt-file "$PROMPT_FILE" --cwd "$WORKSPACE" \ - --effort high --permission-mode acceptEdits \ + --effort "$(route_effort grok-cli)" --permission-mode acceptEdits \ --tools Read,Write,Edit --disable-web-search --no-memory --no-subagents \ --no-plan --max-turns 50 --output-format streaming-json --verbatim [ "$grok_model" = auto ] || printf '%s\0' --model "$grok_model" @@ -143,6 +177,11 @@ adapter_argv() { printf '%s\0' opencode run --dir "$WORKSPACE" --format json --auto --file "$PROMPT_FILE" printf '%s\0' "Follow the attached unit packet. Return only the implementation result JSON." [ "$(route_model opencode)" = auto ] || printf '%s\0' --model "$(route_model opencode)" + if [ "${AUTH_BOUND:-}" = 1 ]; then + [ -n "${EFFORT_REQUESTED:-}" ] && printf '%s\0' --variant "$(route_effort opencode)" + elif [ -n "${CE_WORK_EFFORT_OVERRIDE:-}" ]; then + printf '%s\0' --variant "$(route_effort opencode)" + fi ;; *) return 1 ;; esac @@ -157,6 +196,10 @@ if [ "${1:-}" = "--emit-adapter" ]; then printf "model override '%s' not compatible with route '%s'\n" "${CE_WORK_MODEL_OVERRIDE:-}" "$ROUTE" >&2 exit 2 } + validate_effort_override "$ROUTE" || { + printf "effort override '%s' not compatible with route '%s'\n" "${CE_WORK_EFFORT_OVERRIDE:-}" "$ROUTE" >&2 + exit 2 + } adapter_argv "$ROUTE" >/dev/null 2>&1 || { printf "unknown route '%s'\n" "$ROUTE" >&2; exit 2; } adapter_argv "$ROUTE" | tr '\0' ' ' printf '\n' @@ -211,7 +254,7 @@ import json, os, re, stat, sys source, expected_packet_digest, output = sys.argv[1:] required = { "schema_version", "run_id", "unit_id", "attempt_id", "route", "target", "harness", - "intermediaries", "model_requested", "restriction_posture", + "intermediaries", "model_requested", "effort_requested", "restriction_posture", "restrictions", "activity_posture", "packet_digest", } contracts = { @@ -283,7 +326,16 @@ try: value = json.loads(b"".join(chunks)) except (ValueError, UnicodeDecodeError) as exc: fail(f"authorization is malformed JSON: {exc}") - if not isinstance(value, dict) or set(value) != required: + if not isinstance(value, dict): + fail("authorization keys do not match the exact controller schema") + keys = set(value) + required_without_effort = required - {"effort_requested"} + if keys == required: + pass + elif keys == required_without_effort: + value = dict(value) + value["effort_requested"] = None + else: fail("authorization keys do not match the exact controller schema") if type(value["schema_version"]) is not int or value["schema_version"] != 1: fail("authorization schema_version must be 1") @@ -303,6 +355,19 @@ try: fail("authorization restrictions must be a string list") if not model_allowed(route, value["model_requested"]): fail("authorization model is incompatible with the fixed route") + effort = value["effort_requested"] + effort_tokens = {"none", "minimal", "low", "medium", "high", "xhigh", "max", "default"} + if effort is not None and (not isinstance(effort, str) or effort not in effort_tokens): + fail("authorization effort is incompatible with the fixed route") + honor = { + "claude": {"low", "medium", "high", "xhigh", "max"}, + "codex": {"none", "minimal", "low", "medium", "high", "xhigh", "max"}, + "grok-cli": {"low", "medium", "high"}, + "opencode": {"none", "minimal", "low", "medium", "high", "xhigh", "max", "default"}, + } + if effort is not None and route in honor and effort not in honor[route]: + fail("authorization effort is incompatible with the fixed route") + effort_field = "" if effort is None else effort packet_digest = value["packet_digest"] if not isinstance(packet_digest, str) or not re.fullmatch(r"[0-9a-f]{64}", packet_digest): fail("authorization packet_digest is not lowercase SHA-256") @@ -311,7 +376,7 @@ try: authorization_digest = __import__("hashlib").sha256(b"".join(chunks)).hexdigest() fields = ( authorization_digest, value["run_id"], value["unit_id"], value["attempt_id"], - route, target, harness, value["model_requested"], value["activity_posture"], posture, + route, target, harness, value["model_requested"], effort_field, value["activity_posture"], posture, ) out = os.open(output, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600) try: @@ -327,7 +392,7 @@ AUTH_EXIT=$? AUTH_FIELDS=() while IFS= read -r -d '' field; do AUTH_FIELDS+=("$field"); done < "$AUTH_VALUES" -[ "${#AUTH_FIELDS[@]}" -eq 10 ] || { log "controller authorization projection is incomplete"; exit 2; } +[ "${#AUTH_FIELDS[@]}" -eq 11 ] || { log "controller authorization projection is incomplete"; exit 2; } OBSERVED_AUTH_DIGEST="${AUTH_FIELDS[0]}" RUN_ID="${AUTH_FIELDS[1]}" UNIT_ID="${AUTH_FIELDS[2]}" @@ -336,8 +401,10 @@ ROUTE="${AUTH_FIELDS[4]}" AUTH_TARGET="${AUTH_FIELDS[5]}" AUTH_HARNESS="${AUTH_FIELDS[6]}" MODEL_REQUESTED="${AUTH_FIELDS[7]}" -ACTIVITY_POSTURE="${AUTH_FIELDS[8]}" -RESTRICTION_POSTURE="${AUTH_FIELDS[9]}" +EFFORT_REQUESTED="${AUTH_FIELDS[8]}" +ACTIVITY_POSTURE="${AUTH_FIELDS[9]}" +RESTRICTION_POSTURE="${AUTH_FIELDS[10]}" +AUTH_BOUND=1 RUNNER_JOB_ID="${CE_PEER_JOB_ID:-}" [[ "$RUNNER_JOB_ID" =~ ^[A-Za-z0-9._-]{1,128}$ && "$RUNNER_JOB_ID" =~ [A-Za-z0-9_-] ]] || { log "runner job identity is missing or unsafe" @@ -692,6 +759,13 @@ if ! command -v "$BINARY" >/dev/null 2>&1; then exit 2 fi +if [ "${AUTH_BOUND:-}" != 1 ]; then + validate_effort_override "$ROUTE" || { + printf "effort override '%s' not compatible with route '%s'\n" "${CE_WORK_EFFORT_OVERRIDE:-}" "$ROUTE" >&2 + exit 2 + } +fi + ARGS=() while IFS= read -r -d '' token; do ARGS+=("$token"); done < <(adapter_argv "$ROUTE") diff --git a/skills/ce-work/scripts/unit_workspace_jobs.py b/skills/ce-work/scripts/unit_workspace_jobs.py index 7a2fa7695..296e07e0b 100644 --- a/skills/ce-work/scripts/unit_workspace_jobs.py +++ b/skills/ce-work/scripts/unit_workspace_jobs.py @@ -102,6 +102,24 @@ def cmd_prepare(args) -> tuple[str, dict]: authorization = attempt_authorization(doc, args.activity_posture, uid, attempt_id, packet_digest) authorization_bytes = (json.dumps(authorization, sort_keys=True, separators=(",", ":")) + "\n").encode() authorization_digest = digest_bytes(authorization_bytes) + if existing: + matching_recorded = [ + attempt for attempt in existing.get("attempts", []) if attempt.get("attempt_id") == attempt_id + ] + if matching_recorded: + recorded_auth = matching_recorded[0].get("authorization") + recorded_digest = matching_recorded[0].get("authorization_digest") + if ( + isinstance(recorded_auth, dict) + and isinstance(recorded_digest, str) + and normalize_authorization(recorded_auth) == normalize_authorization(authorization) + and os.path.lexists(authorization_path) + ): + recorded_bytes = read_private(authorization_path, MAX_JSON_BYTES) + if digest_bytes(recorded_bytes) == recorded_digest: + authorization = recorded_auth + authorization_bytes = recorded_bytes + authorization_digest = recorded_digest contract_wave_base = existing.get("wave", {}).get("base") if existing else base expected_contract = { "dependencies": list(args.dependency), diff --git a/skills/ce-work/scripts/unit_workspace_state.py b/skills/ce-work/scripts/unit_workspace_state.py index a0a28dbd5..d82df331e 100644 --- a/skills/ce-work/scripts/unit_workspace_state.py +++ b/skills/ce-work/scripts/unit_workspace_state.py @@ -632,6 +632,28 @@ def parse_json_arg(raw: str, label: str) -> dict: "opencode": {"target": "opencode", "harness": "opencode", "intermediaries": [], "default_model": "auto", "restriction_posture": "cooperative"}, } +BINDING_EFFORT_TOKENS = frozenset({ + "none", "minimal", "low", "medium", "high", "xhigh", "max", "default", +}) +ROUTE_EFFORT_HONOR = { + "claude": frozenset({"low", "medium", "high", "xhigh", "max"}), + "codex": frozenset({"none", "minimal", "low", "medium", "high", "xhigh", "max"}), + "grok-cli": frozenset({"low", "medium", "high"}), + "opencode": frozenset({"none", "minimal", "low", "medium", "high", "xhigh", "max", "default"}), +} + + +def normalize_binding(binding: dict) -> dict: + out = dict(binding) + out.setdefault("effort", None) + return out + + +def normalize_authorization(auth: dict) -> dict: + out = dict(auth) + out.setdefault("effort_requested", None) + return out + def route_model_allowed(route: str, model: str) -> bool: if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._:/-]*", model): @@ -658,14 +680,22 @@ def route_model_allowed(route: str, model: str) -> bool: def fixed_route_contract(binding: dict, egress: dict, word: str = "BLOCKED") -> dict: if not isinstance(binding, dict) or not isinstance(egress, dict): raise Operational(word, "run binding or egress sanction is malformed") - expected_binding_fields = {"mode", "target", "model", "source"} - if set(binding) != expected_binding_fields: - raise Operational(word, "binding must contain exactly mode, target, model, and source") + required_binding_fields = {"mode", "target", "model", "source"} + allowed_binding_fields = required_binding_fields | {"effort"} + fields = set(binding) + if not required_binding_fields <= fields or not fields <= allowed_binding_fields: + raise Operational(word, "binding must contain mode, target, model, and source, and may include effort") if binding.get("mode") not in {"prefer", "require"}: raise Operational(word, "binding mode must be 'prefer' or 'require'") source = binding.get("source") if not isinstance(source, str) or not source or "\0" in source or len(source.encode()) > 256: raise Operational(word, "binding source must be a non-empty string of at most 256 bytes") + effort = binding.get("effort") + if effort is not None and (not isinstance(effort, str) or effort not in BINDING_EFFORT_TOKENS): + raise Operational( + word, + "binding effort must be null or one of: none, minimal, low, medium, high, xhigh, max, default", + ) route = egress.get("route") contract = ROUTE_CONTRACTS.get(route) if not contract: @@ -712,6 +742,11 @@ def attempt_authorization( "harness": contract["harness"], "intermediaries": list(contract["intermediaries"]), "model_requested": model or contract["default_model"], + "effort_requested": ( + binding.get("effort") + if binding.get("effort") in ROUTE_EFFORT_HONOR.get(route, frozenset()) + else None + ), "restriction_posture": contract["restriction_posture"], "restrictions": list(restrictions), "activity_posture": activity_posture, @@ -791,6 +826,7 @@ def cmd_init(args) -> tuple[str, dict]: binding = parse_json_arg(args.binding_json, "binding") egress = parse_json_arg(args.egress_json, "egress") fixed_route_contract(binding, egress, "REFUSED") + binding = normalize_binding(binding) rd = run_dir(rid) try: os.mkdir(rd, 0o700) @@ -822,7 +858,7 @@ def cmd_init(args) -> tuple[str, dict]: or existing_source.get("digest") != actual_digest ): raise Operational("BLOCKED", "run id already belongs to another repository or source") - if existing.get("binding") != binding or existing.get("egress") != egress: + if normalize_binding(existing.get("binding") or {}) != binding or existing.get("egress") != egress: raise Operational( "BLOCKED", "run id binding or egress sanction differs from the recorded fixed contract; resume with the recorded contract or choose a new run id", diff --git a/skills/lfg/references/stage-routing.md b/skills/lfg/references/stage-routing.md index bcdd6bfad..8a613a2e5 100644 --- a/skills/lfg/references/stage-routing.md +++ b/skills/lfg/references/stage-routing.md @@ -21,11 +21,12 @@ Requirement strength is inferred from the whole instruction, not one word: "use ## Implementation carrier grammar -When implementation resolves to one candidate, retain one transient `implementation_engine` object with exactly these four fields: +When implementation resolves to one candidate, retain one transient `implementation_engine` object with `mode`, `target`, `model`, `effort`, and `source`: - `mode`: `prefer` or `require` - `target`: exactly one of `codex`, `claude`, `grok`, `cursor`, `composer`, or `opencode` — a **harness** name, never a model name - `model`: the explicit model pin, otherwise `null` +- `effort`: an explicit effort token, otherwise `null` - `source`: caller-visible provenance identifying the current LFG instruction A directive that names a bare **model** with no harness (e.g. "use fable", "with opus") is a model *pin*, not a target: encode it as the harness that serves that model family with the alias in `model` — a Claude-family model (`fable`, `opus`, `sonnet`, `haiku`) is `{"target":"claude","model":""}`. Never put a model name in `target`; if you cannot map the named model to one of the five harnesses, that is a routing-carrier blocker, not a `null` binding that silently drops the user's instruction. @@ -44,6 +45,6 @@ When a planning-stage directive resolved, prefix the `ce-plan` invocation with i Use `mode:return-to-caller ` when no scalar transient carrier exists, including when a retained ordered current-task assignment is still active in context. When the scalar carrier exists, use the exact string-host form `mode:return-to-caller implementation_engine: `. -Serialize its exact `implementation_engine.{mode,target,model,source}` data as compact JSON immediately after the `implementation_engine:` prefix (for example `implementation_engine:{"mode":"prefer","target":"codex","model":null,"source":"lfg-current-turn"}`). This is structured caller data in a portable string envelope, not part of the plan path or implementation prompt. Pass no empty carrier when it does not exist. `ce-work` then resolves a retained ordered current-task assignment when present, otherwise applicable session/project intent and standing per-checkout configuration. LFG is an automatic, headless caller: it never prompts to weaken a requirement-strength route. +Serialize its exact `implementation_engine.{mode,target,model,effort,source}` data as compact JSON immediately after the `implementation_engine:` prefix (for example `implementation_engine:{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"lfg-current-turn"}`). This is structured caller data in a portable string envelope, not part of the plan path or implementation prompt. Pass no empty carrier when it does not exist. `ce-work` then resolves a retained ordered current-task assignment when present, otherwise applicable session/project intent and standing per-checkout configuration. LFG is an automatic, headless caller: it never prompts to weaken a requirement-strength route. The optional `implementation_run:` carrier is recovery-only. Never include it on the initial step-2 call. On the one evidence-reconciliation recovery, place it after the same engine carrier when one existed and before the unchanged plan path: `mode:return-to-caller implementation_run: ` or `mode:return-to-caller implementation_engine: implementation_run: `. A safe id matches `^[A-Za-z0-9._-]{1,128}$` and contains at least one non-period character. Reject a malformed or duplicate run/engine carrier instead of launching work. diff --git a/tests/skills/ce-code-review-cross-model-routes.test.ts b/tests/skills/ce-code-review-cross-model-routes.test.ts index acdb5fa42..83e5b281d 100644 --- a/tests/skills/ce-code-review-cross-model-routes.test.ts +++ b/tests/skills/ce-code-review-cross-model-routes.test.ts @@ -1930,6 +1930,8 @@ describe("cross-model provider kernel parity (code-review vs doc-review)", () => expect(emitAdapter("claude", script, { CROSS_MODEL_EFFORT_OVERRIDE: "xhigh" })).toContain("--effort xhigh") expect(emitAdapter("claude", script, { CROSS_MODEL_EFFORT_OVERRIDE: "xhigh" })).not.toContain("--effort high") expect(emitAdapter("codex", script, { CROSS_MODEL_EFFORT_OVERRIDE: "medium" })).toContain('model_reasoning_effort="medium"') + expect(emitAdapter("codex", script, { CROSS_MODEL_EFFORT_OVERRIDE: "max" })).toContain('model_reasoning_effort="max"') + expect(emitAdapter("codex", script, { CROSS_MODEL_EFFORT_OVERRIDE: "none" })).toContain('model_reasoning_effort="none"') expect(emitAdapter("grok-cli", script, { CROSS_MODEL_EFFORT_OVERRIDE: "medium" })).toContain("--effort medium") // unset -> editorial defaults unchanged expect(emitAdapter("claude", script)).toContain("--effort high") @@ -1940,7 +1942,6 @@ describe("cross-model provider kernel parity (code-review vs doc-review)", () => test("an effort override the route cannot honor fails closed in both skills", () => { const cases: Array<[string, string]> = [ ["claude", "minimal"], // not a claude CLI level - ["codex", "max"], // not a codex reasoning level ["grok-cli", "xhigh"], // not a grok level ["grok-cursor", "high"], // cursor-agent routes imply effort in the model id ["composer", "high"], diff --git a/tests/skills/ce-setup-check-health.test.ts b/tests/skills/ce-setup-check-health.test.ts index 3bdd0d15c..807350330 100644 --- a/tests/skills/ce-setup-check-health.test.ts +++ b/tests/skills/ce-setup-check-health.test.ts @@ -188,6 +188,9 @@ describe("ce-setup check-health", () => { expect(template).toContain("plan_model") expect(template).toContain("brainstorm_model") + expect(template).toContain("plan_effort") + expect(template).toContain("brainstorm_effort") + expect(template).toMatch(/^#\s+effort:/m) expect(template).not.toContain("plan_use_fable") expect(template).not.toContain("brainstorm_use_fable") expect(template).not.toContain("fable_nudge") @@ -588,6 +591,25 @@ describe("ce-setup check-health", () => { } }) + test("accepts optional effort on a work_engine_preferences entry without emitting it", async () => { + const root = await mkdtemp(path.join(os.tmpdir(), "ce-setup-health-")) + + try { + await initConfiguredRepo( + root, + "work_engine_mode: prefer\nwork_engine_preferences:\n - harness: codex\n effort: max\n", + ) + const result = await runCheckHealth(root, "/usr/bin:/bin") + + expect(result.exitCode).toBe(0) + expect(result.stdout).not.toContain("unsupported work_engine_preferences entry") + expect(result.stdout).toContain("CE Work implementation engine: prefer -> codex@default") + expect(result.stdout).not.toMatch(/effort:/) + } finally { + await rm(root, { recursive: true, force: true }) + } + }) + test("rejects a model entry that is not attached to a harness", async () => { const root = await mkdtemp(path.join(os.tmpdir(), "ce-setup-health-")) diff --git a/tests/skills/ce-work-cross-model-integration.test.ts b/tests/skills/ce-work-cross-model-integration.test.ts index aa82fde27..f865e621b 100644 --- a/tests/skills/ce-work-cross-model-integration.test.ts +++ b/tests/skills/ce-work-cross-model-integration.test.ts @@ -144,7 +144,7 @@ describe("ce-work serial cross-model transaction", () => { "--repo", repo, "--plan", plan, "--plan-digest", planDigest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U-scope"],"restrictions":[]}', ).word).toBe("READY") @@ -245,7 +245,7 @@ printf '%s\n' '{"terminal_status":"scope_expansion","summary":"shared contract n "--repo", repo, "--plan", plan, "--plan-digest", planDigest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U-redact"],"restrictions":[]}', ).word).toBe("READY") @@ -366,7 +366,7 @@ printf '%s\n' '${JSON.stringify({ "--repo", repo, "--plan", plan, "--plan-digest", planDigest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U"],"restrictions":[]}', ).word).toBe("READY") const prepared = control( @@ -499,7 +499,7 @@ class FeatureTest(unittest.TestCase): expect(control( runs, "init", "--run-id", "transaction-run", "--repo", repo, "--plan", plan, "--plan-digest", planDigest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U1"],"restrictions":[]}', ).word).toBe("READY") const packet = "transaction packet" @@ -618,7 +618,7 @@ class FeatureTest(unittest.TestCase): control( runs, "init", "--run-id", "run-verify-lock", "--repo", repo, "--plan", plan, "--plan-digest", planDigest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U1"],"restrictions":[]}', ) const packet = "plan-wide verification lock packet" @@ -686,7 +686,7 @@ class FeatureTest(unittest.TestCase): control( runs, "init", "--run-id", "run-verify-abandoned", "--repo", repo, "--plan", plan, "--plan-digest", planDigest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U1"],"restrictions":[]}', ) const packet = "abandoned plan-wide verification packet" @@ -746,7 +746,7 @@ class FeatureTest(unittest.TestCase): control( runs, "init", "--run-id", "transaction-fail", "--repo", repo, "--plan", plan, "--plan-digest", planDigest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U1"],"restrictions":[]}', ) const packet = "failing transaction packet" @@ -812,7 +812,7 @@ class FeatureTest(unittest.TestCase): control( runs, "init", "--run-id", "transaction-restore-fail", "--repo", repo, "--plan", plan, "--plan-digest", planDigest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U1"],"restrictions":[]}', ) const packet = "restore failure packet" @@ -879,7 +879,7 @@ class FeatureTest(unittest.TestCase): control( runs, "init", "--run-id", runId, "--repo", repo, "--plan", plan, "--plan-digest", planDigest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U1"],"restrictions":[]}', ) const prepared = control( @@ -948,7 +948,7 @@ class FeatureTest(unittest.TestCase): "--repo", repo, "--plan", plan, "--plan-digest", planDigest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U4a"],"restrictions":[]}', ).word).toBe("READY") @@ -1100,7 +1100,7 @@ printf '%s\n' '{"terminal_status":"completed","summary":"done","changed_files":[ expect(control( runs, "init", "--run-id", "wave-run", "--repo", repo, "--plan", plan, "--plan-digest", planDigest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["wave"],"restrictions":[]}', ).word).toBe("READY") diff --git a/tests/skills/ce-work-cross-model-routes.test.ts b/tests/skills/ce-work-cross-model-routes.test.ts index 50acd24aa..dc13fbca9 100644 --- a/tests/skills/ce-work-cross-model-routes.test.ts +++ b/tests/skills/ce-work-cross-model-routes.test.ts @@ -172,7 +172,7 @@ function run( } invoke( "init", "--run-id", runId, "--repo", f.canonical, "--plan", plan, "--plan-digest", planDigest, - "--binding-json", JSON.stringify({ mode: "prefer", target: contract.target, model: forgedAuthorization ? null : authorizationOverrides.model_requested ?? null, source: "test" }), + "--binding-json", JSON.stringify({ mode: "prefer", target: contract.target, model: forgedAuthorization ? null : authorizationOverrides.model_requested ?? null, effort: null, source: "test" }), "--egress-json", JSON.stringify({ sanction_source: "test", route, intermediaries: [...contract.intermediaries], exposed_material: [unitId], restrictions: [] }), ) const base = spawnSync("git", ["-C", f.canonical, "rev-parse", "HEAD"], { encoding: "utf8" }).stdout.trim() @@ -265,6 +265,40 @@ describe("ce-work fixed write routes", () => { expect(opencode).not.toContain("--model") }) + test("CE_WORK_EFFORT_OVERRIDE replaces editorial high on effort-bearing routes", () => { + expect(emit("codex").stdout).toContain("model_reasoning_effort=high") + expect(emit("claude").stdout).toContain("--effort high") + expect(emit("grok-cli").stdout).toContain("--effort high") + + const codexMax = emit("codex", { ...process.env, CE_WORK_EFFORT_OVERRIDE: "max" }) + expect(codexMax.status).toBe(0) + expect(codexMax.stdout).toContain("model_reasoning_effort=max") + expect(codexMax.stdout).not.toContain("model_reasoning_effort=high") + + const claudeXhigh = emit("claude", { ...process.env, CE_WORK_EFFORT_OVERRIDE: "xhigh" }) + expect(claudeXhigh.status).toBe(0) + expect(claudeXhigh.stdout).toContain("--effort xhigh") + expect(claudeXhigh.stdout).not.toContain("--effort high") + + expect(emit("opencode").stdout).not.toContain("--variant") + const opencodeHigh = emit("opencode", { ...process.env, CE_WORK_EFFORT_OVERRIDE: "high" }) + expect(opencodeHigh.status).toBe(0) + expect(opencodeHigh.stdout).toContain("--variant high") + }) + + test("an effort override the route cannot honor fails closed", () => { + const cursor = emit("cursor", { ...process.env, CE_WORK_EFFORT_OVERRIDE: "high" }) + expect(cursor.status).toBe(2) + expect(cursor.stderr).toContain("effort override 'high' not compatible with route 'cursor'") + + const banana = emit("codex", { ...process.env, CE_WORK_EFFORT_OVERRIDE: "banana" }) + expect(banana.status).toBe(2) + expect(banana.stderr).toContain("effort override 'banana' not compatible with route 'codex'") + expect(banana.stdout).not.toContain("model_reasoning_effort=high") + expect(banana.stdout).not.toContain("model_reasoning_effort=medium") + expect(banana.stdout).not.toContain("model_reasoning_effort=max") + }) + test.each(ROUTES)("%s receives one workspace and bounded packet", (route) => { const f = fixture() const bin = fakeBin(route, f.capture) diff --git a/tests/skills/ce-work-outcome-spine.test.ts b/tests/skills/ce-work-outcome-spine.test.ts index 54bb3e041..ff230f7f5 100644 --- a/tests/skills/ce-work-outcome-spine.test.ts +++ b/tests/skills/ce-work-outcome-spine.test.ts @@ -282,17 +282,19 @@ describe("ce-work cross-model engine contract", () => { expect(triage).toContain("prove the canonical Git snapshot is byte-for-byte unchanged") }) - test("keeps the caller carrier implementation-only and exactly four fields", async () => { + test("keeps the caller carrier implementation-only with required mode, target, model, and source", async () => { const triage = await readTriage() const engines = await readRepoFile("skills/ce-work/references/execution-engines.md") const carrier = sliceSection(engines, "### Typed caller binding", "### Target and identity vocabulary") - expect(triage).toContain("exactly four fields") - for (const field of ["mode", "target", "model", "source"]) { + expect(triage).toContain("must contain `mode`, `target`, `model`, and `source`") + for (const field of ["mode", "target", "model", "effort", "source"]) { expect(triage).toContain(`\`${field}\``) } expect(triage).toContain("`mode` is `prefer` or `require`") expect(triage).toContain("`model` is a string pin or `null`") + expect(triage).toContain("`effort` is a string pin or `null`") + expect(triage).toContain("Omitted `effort` is not a missing field") expect(triage).toContain("`source` is a non-empty caller-visible provenance string") expect(triage).toContain("Fully validate and normalize both before any workspace action") expect(carrier).toContain("fully validated and normalized typed caller binding") @@ -319,7 +321,8 @@ describe("ce-work cross-model engine contract", () => { expect(phase0).toContain("implementation_engine:") expect(phase0).toContain("one compact JSON object") - expect(phase0).toContain("exactly four fields") + expect(phase0).toContain("must contain `mode`, `target`, `model`, and `source`") + expect(phase0).toContain("Omitted `effort` is not a missing field") expect(phase0).toContain("implementation_run:") expect(phase0).toContain("`^[A-Za-z0-9._-]{1,128}$`") expect(phase0).toContain("Reject malformed JSON, missing/extra fields, invalid field types or values") diff --git a/tests/skills/ce-work-unit-workspace-init.test.ts b/tests/skills/ce-work-unit-workspace-init.test.ts index 64b0f750a..5e921e693 100644 --- a/tests/skills/ce-work-unit-workspace-init.test.ts +++ b/tests/skills/ce-work-unit-workspace-init.test.ts @@ -54,7 +54,7 @@ describe("ce-work unit workspace controller: init, identity, and dispatch author { GIT_DIR: path.join(decoy.repo, ".git"), GIT_WORK_TREE: decoy.repo }, "init", "--run-id", "run-sanitized-git-env", "--repo", f.repo, "--plan", f.plan, "--plan-digest", f.digest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U"],"restrictions":[]}', ) expect(initialized.word).toBe("READY") @@ -133,7 +133,7 @@ describe("ce-work unit workspace controller: init, identity, and dispatch author { CE_WORK_RUNS_ROOT: "", CE_PEER_JOBS_ROOT: peerRoot }, "init", "--run-id", "run-peer-root-only", "--repo", f.repo, "--plan", f.plan, "--plan-digest", f.digest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U2"],"restrictions":[]}', ) expect(result.word).toBe("READY") @@ -267,7 +267,7 @@ describe("ce-work unit workspace controller: init, identity, and dispatch author const resumed = ctl( runs, "init", "--run-id", "run-prompt", "--repo", f.repo, "--prompt-brief", first.brief, "--prompt-digest", first.digest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["P1"],"restrictions":[]}', ) expect(resumed).toMatchObject({ @@ -289,7 +289,7 @@ describe("ce-work unit workspace controller: init, identity, and dispatch author const f = makeRepo() const runs = path.join(tmp("ce-work-runs-"), "ce-work") const brief = packetFile("bounded prompt\n") - const binding = '{"mode":"prefer","target":"codex","model":null,"source":"test"}' + const binding = '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}' const egress = '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["P1"],"restrictions":[]}' expect(ctl( @@ -348,7 +348,7 @@ describe("ce-work unit workspace controller: init, identity, and dispatch author test("validates the fixed route at init and refuses conflicting resume sanctions", () => { const f = makeRepo() const runs = path.join(tmp("ce-work-runs-"), "ce-work") - const binding = JSON.stringify({ mode: "require", target: "codex", model: null, source: "test" }) + const binding = JSON.stringify({ mode: "require", target: "codex", model: null, effort: null, source: "test" }) const invalid = ctl( runs, "init", "--run-id", "invalid-route", "--repo", f.repo, "--plan", f.plan, "--plan-digest", f.digest, "--binding-json", binding, @@ -360,10 +360,11 @@ describe("ce-work unit workspace controller: init, identity, and dispatch author expect(existsSync(path.join(runs, "invalid-route"))).toBe(false) for (const [runId, malformed, message] of [ - ["missing-binding-mode", { target: "codex", model: null, source: "test" }, "exactly mode, target, model, and source"], - ["invalid-binding-mode", { mode: "preferred", target: "codex", model: null, source: "test" }, "mode must be 'prefer' or 'require'"], - ["extra-binding-field", { mode: "prefer", target: "codex", model: null, source: "test", extra: true }, "exactly mode, target, model, and source"], - ["empty-binding-source", { mode: "prefer", target: "codex", model: null, source: "" }, "source must be a non-empty string"], + ["missing-binding-mode", { target: "codex", model: null, effort: null, source: "test" }, "must contain mode, target, model, and source"], + ["invalid-binding-mode", { mode: "preferred", target: "codex", model: null, effort: null, source: "test" }, "mode must be 'prefer' or 'require'"], + ["extra-binding-field", { mode: "prefer", target: "codex", model: null, effort: null, source: "test", extra: true }, "must contain mode, target, model, and source"], + ["empty-binding-source", { mode: "prefer", target: "codex", model: null, effort: null, source: "" }, "source must be a non-empty string"], + ["invalid-binding-effort", { mode: "prefer", target: "codex", model: null, effort: "banana", source: "test" }, "effort must be null or one of"], ] as const) { const malformedResult = ctl( runs, "init", "--run-id", runId, "--repo", f.repo, "--plan", f.plan, @@ -375,12 +376,41 @@ describe("ce-work unit workspace controller: init, identity, and dispatch author expect(existsSync(path.join(runs, runId))).toBe(false) } + const pinned = ctl( + runs, "init", "--run-id", "effort-pin", "--repo", f.repo, "--plan", f.plan, + "--plan-digest", f.digest, + "--binding-json", JSON.stringify({ mode: "prefer", target: "codex", model: null, effort: "max", source: "test" }), + "--egress-json", JSON.stringify({ sanction_source: "test", route: "codex", intermediaries: [], exposed_material: ["U"], restrictions: [] }), + ) + expect(pinned.word).toBe("READY") + expect(JSON.parse(readFileSync(path.join(runs, "effort-pin", "manifest.json"), "utf8")).binding.effort).toBe("max") + + const legacy = ctl( + runs, "init", "--run-id", "legacy-four-field", "--repo", f.repo, "--plan", f.plan, + "--plan-digest", f.digest, + "--binding-json", JSON.stringify({ mode: "prefer", target: "codex", model: null, source: "test" }), + "--egress-json", JSON.stringify({ sanction_source: "test", route: "codex", intermediaries: [], exposed_material: ["U"], restrictions: [] }), + ) + expect(legacy.word).toBe("READY") + expect(JSON.parse(readFileSync(path.join(runs, "legacy-four-field", "manifest.json"), "utf8")).binding.effort).toBeNull() + const legacyManifestPath = path.join(runs, "legacy-four-field", "manifest.json") + const legacyDoc = JSON.parse(readFileSync(legacyManifestPath, "utf8")) + delete legacyDoc.binding.effort + writeFileSync(legacyManifestPath, `${JSON.stringify(legacyDoc)}\n`) + const legacyResume = ctl( + runs, "init", "--run-id", "legacy-four-field", "--repo", f.repo, "--plan", f.plan, + "--plan-digest", f.digest, + "--binding-json", JSON.stringify({ mode: "prefer", target: "codex", model: null, effort: null, source: "test" }), + "--egress-json", JSON.stringify({ sanction_source: "test", route: "codex", intermediaries: [], exposed_material: ["U"], restrictions: [] }), + ) + expect(legacyResume).toMatchObject({ word: "READY", body: { resumed: true } }) + for (const [index, model] of ["composer-2.5-fast", "grok-4.6", "cursor-grok-4.6-high", "model@beta"].entries()) { const runId = `invalid-cursor-model-${index}` const invalidModel = ctl( runs, "init", "--run-id", runId, "--repo", f.repo, "--plan", f.plan, "--plan-digest", f.digest, - "--binding-json", JSON.stringify({ mode: "require", target: "cursor", model, source: "test" }), + "--binding-json", JSON.stringify({ mode: "require", target: "cursor", model, effort: null, source: "test" }), "--egress-json", JSON.stringify({ route: "cursor", intermediaries: [], restrictions: [] }), ) expect(invalidModel.word).toBe("REFUSED") @@ -425,6 +455,7 @@ describe("ce-work unit workspace controller: init, identity, and dispatch author harness: "codex", intermediaries: [], model_requested: "auto", + effort_requested: null, restriction_posture: "adapter-enforced", restrictions: [], activity_posture: "hard-only", @@ -757,7 +788,7 @@ describe("ce-work unit workspace controller: init, identity, and dispatch author linkedController, runs, "init", "--run-id", runId, "--repo", f.repo, "--plan", f.plan, "--plan-digest", f.digest, - "--binding-json", '{"mode":"prefer","target":"codex","model":null,"source":"test"}', + "--binding-json", '{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"test"}', "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U"],"restrictions":[]}', ).word).toBe("READY") const packet = packetFile("linked adapter packet") diff --git a/tests/skills/elevation-dispatch.test.ts b/tests/skills/elevation-dispatch.test.ts index 3f6bc6499..c438e69b5 100644 --- a/tests/skills/elevation-dispatch.test.ts +++ b/tests/skills/elevation-dispatch.test.ts @@ -162,6 +162,29 @@ describe("elevation-dispatch worker", () => { for (const flag of NEVER_FLAGS) expect(argv).not.toContain(flag) }) + test("CE_ELEVATION_EFFORT_OVERRIDE replaces editorial high when the Claude route honors it", () => { + const r = spawnSync("bash", [WORKER, "--emit-adapter", "fable", "/fake/handoff/xyz"], { + encoding: "utf8", + env: { ...process.env, CE_ELEVATION_EFFORT_OVERRIDE: "xhigh" }, + }) + expect(r.status).toBe(0) + const argv = (r.stdout ?? "").split("\0").filter(Boolean) + expect(argv).toContain("--effort") + expect(argv).toContain("xhigh") + expect(argv).not.toContain("high") + }) + + test("an unhonored CE_ELEVATION_EFFORT_OVERRIDE fails closed", () => { + const r = spawnSync("bash", [WORKER, "--emit-adapter", "fable", "/fake/handoff/xyz"], { + encoding: "utf8", + env: { ...process.env, CE_ELEVATION_EFFORT_OVERRIDE: "minimal" }, + }) + expect(r.status).toBe(2) + expect(r.stderr).toContain("effort override 'minimal' not compatible") + expect(r.stdout ?? "").not.toContain("high") + expect(r.stdout ?? "").not.toContain("xhigh") + }) + test("a matching receipt yields a matched envelope with the output", () => { const stub = "#!/bin/sh\n" + diff --git a/tests/skills/helpers/ce-work-workspace-harness.ts b/tests/skills/helpers/ce-work-workspace-harness.ts index 6f683e757..97f52ee44 100644 --- a/tests/skills/helpers/ce-work-workspace-harness.ts +++ b/tests/skills/helpers/ce-work-workspace-harness.ts @@ -163,7 +163,7 @@ export function initWithBinding( "--repo", fixture.repo, "--plan", fixture.plan, "--plan-digest", fixture.digest, - "--binding-json", JSON.stringify({ mode, target: "codex", model: null, source: "test" }), + "--binding-json", JSON.stringify({ mode, target: "codex", model: null, effort: null, source: "test" }), "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["U2"],"restrictions":[]}', ) } @@ -185,7 +185,7 @@ export function initWithPrompt( "--repo", fixture.repo, "--prompt-brief", brief, "--prompt-digest", packetDigest(prompt), - "--binding-json", JSON.stringify({ mode: "prefer", target: "codex", model: null, source: "test" }), + "--binding-json", JSON.stringify({ mode: "prefer", target: "codex", model: null, effort: null, source: "test" }), "--egress-json", '{"sanction_source":"test","route":"codex","intermediaries":[],"exposed_material":["P1"],"restrictions":[]}', ), } diff --git a/tests/skills/unified-plan-artifact-contract.test.ts b/tests/skills/unified-plan-artifact-contract.test.ts index 3a24d25cd..81a559929 100644 --- a/tests/skills/unified-plan-artifact-contract.test.ts +++ b/tests/skills/unified-plan-artifact-contract.test.ts @@ -342,10 +342,10 @@ describe("unified plan artifact contract", () => { expect(carrier).toContain('"use Codex for implementation"') expect(carrier).toContain('"only use Composer for implementation"') expect(carrier).toContain("implementation_engine") - for (const field of ["mode", "target", "model", "source"]) { + for (const field of ["mode", "target", "model", "effort", "source"]) { expect(carrier).toContain(`\`${field}\``) } - expect(carrier).toContain("exactly these four fields") + expect(carrier).toContain("`effort`") expect(carrier).toContain("Never pass") expect(carrier).toContain("`ce-plan`") expect(carrier).toContain("planning or review") @@ -375,7 +375,7 @@ describe("unified plan artifact contract", () => { const step2 = carrier expect(step2).toContain("mode:return-to-caller implementation_engine: ") expect(step2).toContain("mode:return-to-caller implementation_engine: implementation_run: ") - expect(step2).toContain('implementation_engine:{"mode":"prefer","target":"codex","model":null,"source":"lfg-current-turn"}') + expect(step2).toContain('implementation_engine:{"mode":"prefer","target":"codex","model":null,"effort":null,"source":"lfg-current-turn"}') expect(step2).toContain("portable string envelope") expect(step2).toContain("standing per-checkout configuration") expect(carrier).toContain("Do not construct a carrier from standing configuration")