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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Changelog

## 0.9.2 — Unreleased
## 0.9.3 — Unreleased

- Raise the bounded Advisor approval loop from five to eight reviews while
preserving immediate approval exit and fail-closed plan, ledger, and
round-limit handling before Executor work.

## 0.9.2 — 2026-07-25

- Correct explicit invocation examples to use the namespaced
`$codex-orchestration:codex-orchestration` skill label, while preserving
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ The model selected for the Codex task remains in charge. It passes work between
CODEX TESTS & DELIVERS
```

Planner and Advisor can work through several revisions. Codex stops as soon as the Advisor returns `PLAN_APPROVED`, with a safety limit of five reviews. If approval is not reached, execution stops and Codex shows you the latest plan and unresolved issues.
Planner and Advisor can work through several revisions. Codex stops as soon as the Advisor returns `PLAN_APPROVED`, with a safety limit of eight reviews. If approval is not reached, execution stops and Codex shows you the latest plan and unresolved issues.

## Why use it?

Expand Down
2 changes: 1 addition & 1 deletion RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

4. From a new Desktop task, verify one direct same-provider child route. Record `route accepted`; record `used and confirmed` only if the client exposes effective child model/provider/effort metadata.
5. If Claude Fable 5 is included in the release, verify both supported seat paths from a first-party Claude login: Fable Planner `create_plan`/`revise_plan` with a different Advisor, and Fable Advisor `review_plan` with root planning. Confirm the pinned primary model, exact allowlisted helper set reported by runtime metadata, effort, status, the bounded approval loop, and disable/restore. An unknown helper model is a release failure, not an implicit allowlist expansion.
6. If Claude Opus 5 is included in the release, separately qualify both live seat paths from a first-party Claude login. Run Opus Planner `create_plan` and force a reviewed `PLAN_REVISE` round through `revise_plan` with a distinct Advisor; then run Opus Advisor `review_plan` with root planning. For both paths confirm the pinned `claude-opus-5` primary, the primary-only allowed-runtime-model policy, the configured effort, no tools and no session persistence, `status --require-effective`, the five-review approval bound, and disable/restore. Any helper or other runtime model is a release failure. This is a manual release qualification; local preflight must never invoke Opus.
6. If Claude Opus 5 is included in the release, separately qualify both live seat paths from a first-party Claude login. Run Opus Planner `create_plan` and force a reviewed `PLAN_REVISE` round through `revise_plan` with a distinct Advisor; then run Opus Advisor `review_plan` with root planning. For both paths confirm the pinned `claude-opus-5` primary, the primary-only allowed-runtime-model policy, the configured effort, no tools and no session persistence, `status --require-effective`, the eight-review approval bound, and disable/restore. Any helper or other runtime model is a release failure. This is a manual release qualification; local preflight must never invoke Opus.
7. Merge only after every protected check passes.
8. Create a signed annotated tag named `v<manifest-version>` at the reviewed merge commit.
9. Re-run `python3 scripts/preflight.py full` on the tagged tree, then run `python3 scripts/release_check.py --require-tag` and publish a GitHub release from that tag using the matching changelog section.
Expand Down
2 changes: 1 addition & 1 deletion plugins/codex-orchestration/.codex-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "codex-orchestration",
"version": "0.9.2",
"version": "0.9.3",
"description": "Give Codex and audited external models safe, provider-pinned roles.",
"author": {
"name": "CJ Zafir",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -612,15 +612,15 @@ Advisor is optional. If none is configured, the root validates the Planner's dra
4. For `PLAN_REVISE`, assign stable IDs to material findings and send the canonical current version, latest critique, and compact cumulative findings ledger back to the same Planner route. If Planner is omitted, the root revises.
5. Require `PLAN_REVISION`, a complete `FINDINGS_LEDGER`, and the revised plan. Every latest finding must be `INCORPORATED` or `REJECTED` with a concrete reason. Reject stale source versions, missing or duplicated findings, and empty rationales.
6. Increment the version and send the new current plan plus compact ledger to a fresh Advisor call. Ask it to confirm or contest prior dispositions rather than repeat accepted findings.
7. Stop early on approval. Never exceed five total Advisor reviews.
7. Stop early on approval. Never exceed eight total Advisor reviews.

Carry only the original constraints, current plan, and compact ledger between fresh calls; do not duplicate complete transcripts. The root owns the canonical plan, versions, ledger, round count, semantic validation, and Executor release. Planner and Advisor never contact one another directly.

If review five still returns `PLAN_REVISE`, halt before Executor work. Give the user the latest plan and version, complete ledger, latest unresolved findings, and choices to override, re-scope, or change a route. Never label it approved.
If review eight still returns `PLAN_REVISE`, halt before Executor work. Give the user the latest plan and version, complete ledger, latest unresolved findings, and choices to override, re-scope, or change a route. Never label it approved.

A configured Planner or Advisor is required by default. Route failure, malformed output, missing context, stale version, or invalid ledger halts before Executor work. Only an explicit current-task best-effort instruction permits degradation:

- if the configured Planner fails, disclose it and let the root assume Planner duties for the remaining rounds without resetting the five-review budget;
- if the configured Planner fails, disclose it and let the root assume Planner duties for the remaining rounds without resetting the eight-review budget;
- if the Advisor fails, disclose it, end the loop, and label the latest validated plan `NOT_ADVISOR_APPROVED` before any allowed continuation.

Do not persist a best-effort flag. An explicit task override applies only to that task.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ The control surface and the route are separate:
- current task model is the one root orchestrator;
- Codex decides whether delegation is useful;
- optional Planner drafts and revises through the root; omitted Planner means the root plans;
- optional Advisor is directed through the root and reviews through a five-round bounded approval loop before Executor work;
- optional Advisor is directed through the root and reviews through an eight-round bounded approval loop before Executor work;
- executor packets are bounded and self-contained;
- children do not create descendants;
- user overrides and `no subagents` win;
Expand Down Expand Up @@ -293,7 +293,7 @@ The bundled Claude bridge is mechanically narrower than a child: its tools accep

Planner or Advisor failure is never approval. Configured seats are required for a non-trivial Executor plan unless the user explicitly marks one best-effort for the current task. Transport failure, malformed output, missing context, stale plan versions, or wrong routes stop Executor work by default.

Every Advisor call is fresh and stateless. The root carries the canonical current plan, numbered version, and compact cumulative findings ledger. `PLAN_REVISE` returns to the same Planner route; `PLAN_APPROVED` stops the loop. The root allows at most five Advisor reviews. Review five without approval halts with the current plan, ledger, and unresolved findings instead of silently executing.
Every Advisor call is fresh and stateless. The root carries the canonical current plan, numbered version, and compact cumulative findings ledger. `PLAN_REVISE` returns to the same Planner route; `PLAN_APPROVED` stops the loop. The root allows at most eight Advisor reviews. Review eight without approval halts with the current plan, ledger, and unresolved findings instead of silently executing.

## Goals and task lifetime

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@

POLICY_VERSION = 5
STATE_SCHEMA = 5
ADVISOR_REVIEW_LIMIT = 8
STATE_FILENAME = ".codex-orchestration-routing.json"
PROBE_VALUE = "CODEX_ORCHESTRATION_CAPABILITY_PROBE"
PLUGIN_ID = "codex-orchestration@codex-orchestration"
Expand Down Expand Up @@ -456,7 +457,7 @@ def __init__(
"clientInfo": {
"name": "codex_orchestration_installer",
"title": "Codex Orchestration Installer",
"version": "0.9.2",
"version": "0.9.3",
},
"capabilities": {"experimentalApi": True},
},
Expand Down Expand Up @@ -1125,6 +1126,17 @@ def build_policy(
advisor: dict[str, Any] | None,
designer: dict[str, Any] | None = None,
) -> tuple[str, str]:
advisor_review_limit = (
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
)[ADVISOR_REVIEW_LIMIT]
has_direct_route = executor["kind"] == "model" or (
planner is not None and planner["kind"] == "model"
) or (
Expand Down Expand Up @@ -1154,7 +1166,8 @@ def build_policy(
"early. PLAN_REVISE returns the canonical current plan and version, the "
"latest critique, and the cumulative findings ledger to the same configured "
"Planner route, or to the root when Planner is omitted, then reviews the "
"revised plan again. There may be at most five total Advisor reviews."
"revised plan again. There may be at most "
f"{advisor_review_limit} total Advisor reviews."
if advisor is not None
else (
"No Advisor is configured. Do not create a review loop; after a configured "
Expand Down Expand Up @@ -1189,9 +1202,9 @@ def build_policy(

{designer_mode}

The root owns the plan version, cumulative findings ledger, review count, validation, adjudication, and release to Executor. There is no Finalizer seat. For Advisor rounds two through five, send only the current plan and version plus a compact cumulative ledger, not prior transcripts. Ask the Advisor to confirm or contest dispositions without blindly repeating accepted findings. Reject a stale plan version or an invalid or incomplete ledger and halt before Executor.
The root owns the plan version, cumulative findings ledger, review count, validation, adjudication, and release to Executor. There is no Finalizer seat. For Advisor rounds two through {advisor_review_limit}, send only the current plan and version plus a compact cumulative ledger, not prior transcripts. Ask the Advisor to confirm or contest dispositions without blindly repeating accepted findings. Reject a stale plan version or an invalid or incomplete ledger and halt before Executor.

On PLAN_REVISE, record the latest finding IDs before revision. After the Planner returns, validate and merge each INCORPORATED or reasoned REJECTED disposition into the cumulative ledger before another Advisor call. A round-five PLAN_REVISE halts before Executor and produces a non-approval artifact containing the latest plan and version, full ledger, latest findings, and choices available to the user. It must not claim approval. Any required Planner or Advisor route failure also halts before Executor. Only an explicit current-task best-effort instruction changes failure handling: Planner failure permits the root to take over planning for the remaining rounds; Advisor failure may proceed only with the result labeled NOT_ADVISOR_APPROVED. No best-effort setting is persisted.
On PLAN_REVISE, record the latest finding IDs before revision. After the Planner returns, validate and merge each INCORPORATED or reasoned REJECTED disposition into the cumulative ledger before another Advisor call. A round-{advisor_review_limit} PLAN_REVISE halts before Executor and produces a non-approval artifact containing the latest plan and version, full ledger, latest findings, and choices available to the user. It must not claim approval. Any required Planner or Advisor route failure also halts before Executor. Only an explicit current-task best-effort instruction changes failure handling: Planner failure permits the root to take over planning for the remaining rounds; Advisor failure may proceed only with the result labeled NOT_ADVISOR_APPROVED. No best-effort setting is persisted.

When executor delegation materially improves speed, cost, quality, or context isolation, use only the configured executor route. Give each executor one bounded, self-contained packet with objective, relevant facts, constraints, owned files or read-only scope, dependencies, acceptance criteria, verification, and handoff format. Inspect every handoff, integrate it, and run final checks yourself.

Expand Down
2 changes: 1 addition & 1 deletion tests/plugin_lifecycle_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
MARKETPLACE_NAME = "codex-orchestration"
OLD_RELEASE = "a1d9c546665c3253cdcaa8fe5c0c060199a6126c"
OLD_VERSION = "0.5.0"
NEW_VERSION = "0.9.2"
NEW_VERSION = "0.9.3"
COMMAND_TIMEOUT_SECONDS = 60


Expand Down
38 changes: 36 additions & 2 deletions tests/test_native_routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,13 +452,24 @@ def test_policy_keeps_root_authority_and_pins_fork_none(self) -> None:
self.assertIn("never spawn descendants", mode)
self.assertIn("Explicit user instructions win", mode)
self.assertIn("Persistent and task-local Planner and Advisor routes", mode)
self.assertIn("at most five total Advisor reviews", mode)
self.assertEqual(NATIVE.ADVISOR_REVIEW_LIMIT, 8)
self.assertIn("at most eight total Advisor reviews", mode)
self.assertIn("PLAN_APPROVED ends review early", mode)
self.assertIn("round-five PLAN_REVISE halts before Executor", mode)
self.assertIn("rounds two through eight", mode)
self.assertIn(
"current plan and version plus a compact cumulative ledger, not prior transcripts",
mode,
)
self.assertIn("round-eight PLAN_REVISE halts before Executor", mode)
self.assertIn("non-approval artifact", mode)
self.assertIn("NOT_ADVISOR_APPROVED", mode)
self.assertIn("Planner failure permits the root to take over", mode)
self.assertIn("stale plan version", mode)
self.assertIn("invalid or incomplete ledger", mode)
stale_limit_word = "fi" + "ve"
self.assertNotIn(f"{stale_limit_word} total Advisor reviews", mode)
self.assertNotIn(f"round-{stale_limit_word} PLAN_REVISE", mode)
self.assertNotIn(f"rounds two through {stale_limit_word}", mode)
self.assertIn("There is no Finalizer seat", mode)
self.assertIn("configured Designer", mode)
self.assertIn("design artifacts", mode)
Expand Down Expand Up @@ -490,6 +501,29 @@ def test_policy_keeps_root_authority_and_pins_fork_none(self) -> None:
self.assertNotIn("tool_namespace", mode + usage)
self.assertNotIn("enabled = true", mode + usage)

def test_policy_renders_every_review_bound_from_the_authoritative_limit(
self,
) -> None:
executor = {"kind": "model", "model": "gpt-5.6-luna", "effort": "xhigh"}
planner = {"kind": "model", "model": "gpt-5.6-sol", "effort": "high"}
advisor = {"kind": "model", "model": "gpt-5.6-terra", "effort": "high"}

with mock.patch.object(NATIVE, "ADVISOR_REVIEW_LIMIT", 7):
mode, _ = NATIVE.build_policy(executor, planner, advisor)

for expected in (
"at most seven total Advisor reviews",
"rounds two through seven",
"round-seven PLAN_REVISE",
):
self.assertIn(expected, mode)
for hard_coded in (
"at most eight total Advisor reviews",
"rounds two through eight",
"round-eight PLAN_REVISE",
):
self.assertNotIn(hard_coded, mode)

def test_policy_root_fallback_planner_without_advisor_and_fable_hints(self) -> None:
executor = {"kind": "model", "model": "gpt-5.6-luna", "effort": "high"}
advisor = {"kind": "model", "model": "gpt-5.6-terra", "effort": "high"}
Expand Down
8 changes: 4 additions & 4 deletions tests/test_packaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ def test_plugin_marketplace_and_skill_names_are_aligned(self) -> None:

self.assertEqual(manifest["name"], "codex-orchestration")
self.assertEqual(manifest["skills"], "./skills/")
self.assertEqual(manifest["version"], "0.9.2")
self.assertEqual(manifest["version"], "0.9.3")
self.assertEqual(manifest["mcpServers"], "./.mcp.json")
self.assertRegex(
manifest["version"],
Expand All @@ -278,7 +278,7 @@ def test_native_and_custom_configurators_are_packaged(self) -> None:
self.assertFalse((SKILL_ROOT / "scripts" / "update_plugin.py").exists())
self.assertIn("config/batchWrite", native.read_text(encoding="utf-8"))
self.assertIn('"--repair"', native.read_text(encoding="utf-8"))
self.assertIn('"version": "0.9.2"', native.read_text(encoding="utf-8"))
self.assertIn('"version": "0.9.3"', native.read_text(encoding="utf-8"))
self.assertIn("validate_routing_state", routing_state.read_text(encoding="utf-8"))
self.assertIn("Standalone custom agent", custom.read_text(encoding="utf-8"))

Expand Down Expand Up @@ -453,7 +453,7 @@ def test_ci_runs_dual_version_plugin_lifecycle(self) -> None:
self.assertIn("@openai/codex@0.144.1", workflow)
smoke_text = smoke.read_text(encoding="utf-8")
self.assertIn('OLD_VERSION = "0.5.0"', smoke_text)
self.assertIn('NEW_VERSION = "0.9.2"', smoke_text)
self.assertIn('NEW_VERSION = "0.9.3"', smoke_text)
self.assertIn("old Advisor-only cache unexpectedly supports Planner", smoke_text)
self.assertIn("Upgraded installed skill is missing Planner contract", smoke_text)
self.assertIn("reused the Advisor-only 0.5.0 cache directory", smoke_text)
Expand Down Expand Up @@ -514,7 +514,7 @@ def test_advisor_protocol_is_bounded_and_root_only(self) -> None:
self.assertIn("PLAN_APPROVED", skill)
self.assertIn("PLAN_REVISE", skill)
self.assertIn("report only to the root", skill)
self.assertIn("Never exceed five total Advisor reviews", skill)
self.assertIn("Never exceed eight total Advisor reviews", skill)
self.assertIn("compact cumulative findings ledger", skill)
self.assertNotIn("at most one confirmation pass", skill)
self.assertIn("it never counts as approval", skill)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_release_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def commit(self, message: str) -> str:

class ReleaseCheckTests(unittest.TestCase):
def test_checkout_release_metadata_is_consistent(self) -> None:
self.assertEqual(RELEASE.run_check(REPO_ROOT, require_tag=False), "0.9.2")
self.assertEqual(RELEASE.run_check(REPO_ROOT, require_tag=False), "0.9.3")

def test_unreleased_checkout_is_not_tag_ready(self) -> None:
with self.assertRaisesRegex(RELEASE.ReleaseCheckError, "not tagged"):
Expand Down
Loading
Loading