This repository adds a dependency-free orchestration layer to Codex. Astra handles planning and integration by default; you can choose Sol instead. Luna handles bounded, lower-cost tasks. Task records survive interruptions, and writers work in isolated Git worktrees.
The controller checks the model, reasoning effort, service tier, and required validation before accepting a candidate: a saved, validated snapshot of the proposed changes. It can then commit or push that candidate according to the task's delivery policy.
The default architecture is Astra-Luna, with Sol available as an explicit configuration choice. The skill identifier sol-luna-orchestration, existing commands (including invoke-sol-ultra.mjs), managed markers, and state namespaces keep their historical names for compatibility. There is no automatic fallback between families.
The launchers talk to the experimental Codex App Server over local stdio JSON-RPC instead of using native subagent routing. App Server applies and reports each route explicitly, while native multi-agent execution stays disabled. There is no legacy fallback. An incompatible protocol fails closed with exit code 2.
| Role | Model | Effort | Tier | Sandbox | Workspace | Best use |
|---|---|---|---|---|---|---|
| Root | Advanced | high | Standard | Current session | Main checkout | Planning, delegation, integration, final validation |
explore |
Luna | max | Fast | Read-only | Committed revision worktree | Repository discovery, documentation, contract tracing |
implement-lite |
Luna | max | Fast | Workspace write | Isolated worktree | Small, low-risk, tightly bounded edits |
playwright |
Luna | max | Standard | Read-only repository | Shared checkout | Browser inspection and authorized test interaction |
implement |
Advanced | medium | Standard | Workspace write | Isolated worktree | Changes that need stronger engineering judgment |
review |
Advanced | high | Standard | Read-only | Exact candidate worktree when requested | Independent plan and code review |
| Ultra | Advanced | ultra | Standard | Explicit takeover sandbox | Main checkout | Exceptional architecture, security, or concurrency decisions |
Every role uses model_verbosity = "low". Fast profiles force features.fast_mode = true, while Standard roles force it to false. Reasoning effort and output verbosity are independent settings.
Advanced means the configured Astra or Sol family. The table shows default efforts; optional role-specific settings can change them, except Ultra, which remains ultra. Choosing Sol does not establish that it performs like Astra at the same effort. Root retains planning and acceptance authority. Ultra is available only when the installed runtime advertises that effort and the operator explicitly authorizes a takeover.
Edit $CODEX_HOME/sol-luna-orchestration/config.json (~/.codex/sol-luna-orchestration/config.json when CODEX_HOME is unset). Preserve your delivery setting:
{
"automatic_delivery": false,
"models": {
"advanced": "astra@latest",
"economy": "luna@latest",
"efforts": {
"root": "high",
"implement": "medium",
"review": "high"
}
}
}To use Sol for root, implementation, review, and takeover, change only "advanced": "sol@latest". Missing model settings use the defaults above. An exact stable id such as gpt-6-astra pins a release for reproducibility. The remaining effort keys are explore, implement-lite, playwright, and ultra.
@latest is a project alias, not a Codex model id. The launcher queries model/list, selects the newest visible stable numeric release within that family, then checks effort and Fast support. It does not scrape release pages or trust catalog order. If the selected model lacks a required capability, the run stops with code 2; it never silently downgrades to an older model, effort, or tier. Unknown release naming still needs a resolver update.
npm run models
npm run root -- --cwd <repository>
The first command prints the requested aliases and resolved routes without starting a model turn. The second opens an interactive Codex root with the selected model, effort, and Plan Mode effort. Direct interfaces, including from the global skill link:
node .agents/skills/sol-luna-orchestration/scripts/model-routing.mjs inspect
node .agents/skills/sol-luna-orchestration/scripts/model-routing.mjs root --cwd <repository>
Editing JSON does not switch an open session. Plain codex and the desktop app use their own selected settings or the concrete defaults saved by the last global installation. Run the configured root command for fresh resolution, or explicitly reinstall to refresh global defaults for future sessions. Repository-specific settings may override installed defaults; the root command supplies its resolved route explicitly.
Every new assignment saves its selected route before execution. Queued assignments and retries keep that model even if configuration or the catalog changes. Each attempt still needs fresh routing evidence. To upgrade existing work, explicitly create a new assignment; do not edit its state. Legacy assignments without a saved route remain inspectable but cannot resume silently.
Once turn/start succeeds, explore gets 120 seconds to report item/* progress for the active thread. Each matching event resets this idle timer, but does not extend the overall timeout. The overall timeout defaults to 900 seconds and can be changed with --timeout-seconds. Other profiles use only the overall timeout.
Routing requires two matching sources of evidence. thread/settings/updated confirms the effective model, effort, and protocol tier, while the rollout turn_context confirms the model and effort used for the turn. Protocol priority is reported publicly as fast; protocol default is reported as standard.
Rebases, merges, cherry-picks, reverts, and conflict resolution stay with the root. It checks the checkout and fetches remote refs before acting. Do not use explore to guess what might conflict; after Git reports a conflict, the root may ask it one focused question, once. The root makes the edit and does not repeat a stalled or failed request.
Stop and ask the operator if the checkout is dirty, another Git operation is in progress, or another worktree has the target branch checked out. Push and force-push permissions do not change. The full preflight and conflict procedure lives in the skill.
Codex CLI 0.147.0 is the minimum supported version. The launchers use the experimental App Server protocol and the thread/settings/update behavior validated against that release. Verify newer releases with the included schema and live checks before relying on them.
Have these tools available before you start:
- Codex CLI, Git, and Node.js on
PATH. CI uses Node.js 22. npmfor the convenience commands andnpxfor Playwright MCP. The first Playwright launch needs registry access if the pinned package is not cached.- A Codex login with access to the models listed above when running executors or live checks. Offline checks do not need a login.
New writer assignments use automatic delivery by default. After validation and integration, the controller commits the candidate and pushes it if the branch has a matching configured upstream. Use --delivery manual for a task that must not commit or push, or disable automatic_delivery in the delivery configuration.
From this repository's checkout, run:
npm run install:global
The installer checks for conflicts before changing files. Running it again with the same settings leaves the installation unchanged. It:
- links the canonical skill to
$HOME/.agents/skills/sol-luna-orchestration; - uses a junction on Windows and a directory symlink on macOS or Linux;
- resolves the configured advanced family and saves that concrete root model and effort globally, with Standard tier and low verbosity;
- preserves your existing MCP configuration; the Playwright executor supplies its own server at runtime;
- preserves unrelated Codex configuration and instructions;
- installs the Ultra SessionStart and PreToolUse hooks without changing an existing
hooks.json; - migrates validated Sol-Sol and Sol-Terra links and managed blocks;
- refuses unrelated destinations, malformed managed markers, and ambiguous TOML.
Repository-specific Codex instructions still take precedence over the global defaults.
Before updating an installation, pause other sessions using the global skill and finish existing assignments. Do not migrate with active executors, unresolved locks, or unfinished assignments. The global link reads this checkout directly, so edits become visible before the installer runs.
The annotated tag pre-astra-migration-2026-09-04 preserves the pre-migration code. Back up the installer-managed config.toml, active global instructions, and sol-luna-orchestration/config.json outside the repository under $CODEX_HOME/checkpoints/pre-astra-migration-2026-09-04, recording missing files and the skill link target. Do not include authentication files or credentials.
To roll back, first preserve any uncommitted migration diff and pause consuming sessions. With operator authorization, restore the checkpoint code and the backed-up global defaults and managed instruction block, preserving unrelated settings changed since the backup. If the checkout is already committed, revert the migration commit instead of rewriting published history. The code tag does not restore global configuration. Never restore, rewind, or delete durable state, history, locks, or generation counters; resolve pending work through the existing gate and control commands before changing versions.
| System | Global skill link | Offline CI | Live routing |
|---|---|---|---|
| Windows | Junction | Required on windows-latest |
Pending |
| Linux | Directory symlink | Required on ubuntu-latest |
Pending |
| macOS | Directory symlink | Required on macos-latest |
Pending |
The required GitHub Actions matrix runs on Node.js 22 with Codex CLI 0.147.0. A weekly and manually triggered advisory matrix probes @openai/codex@latest without blocking master, because App Server compatibility can change in newer releases.
The offline smoke test does not start model turns or require authentication:
npm run verify:platform
npm run verify:platform -- --expected-codex-version 0.147.0 --output <path-outside-the-repository>
It creates isolated temporary HOME and CODEX_HOME directories, checks strict configuration and generated App Server schemas, verifies portable process fingerprints, installs twice, verifies the native link type and target, and removes the temporary state. Installation uses an explicit offline model-catalog fixture; this does not certify account availability. Its JSON result is written to stdout; diagnostics use stderr.
Live status remains Pending until that operating system has a successful artifact. Run live checks only from master, through the manual live-cross-platform.yml workflow, on dedicated self-hosted runners labeled codex-live plus windows, linux, or macOS.
Before enabling the workflow, configure the GitHub environment named codex-live with required reviewers. Each runner needs an isolated OS account, an existing Codex login, and the stdio Playwright MCP. The workflow does not copy personal tokens or use API-key secrets.
Windows can still report codex-windows-sandbox-setup.exe: Access is denied on machines where the sandbox helper cannot initialize. That platform remains pending when this occurs; the verification never reduces permissions or enables a bypass.
Send the briefing through stdin. These read-only examples run from this repository's checkout; change --cwd . to inspect another repository. The direct Node command is the canonical interface.
PowerShell:
"Find where executor profiles are defined. Report paths and responsibilities without changing files." | node .agents/skills/sol-luna-orchestration/scripts/invoke-profile-executor.mjs --profile explore --cwd . --sandbox read-only --timeout-seconds 900Bash on Linux or macOS:
printf '%s\n' 'Find where executor profiles are defined. Report paths and responsibilities without changing files.' | node .agents/skills/sol-luna-orchestration/scripts/invoke-profile-executor.mjs --profile explore --cwd . --sandbox read-only --timeout-seconds 900Both implementation profiles require explicit workspace-write and at least one permitted path. The examples below use echo, which works for these simple briefings in PowerShell and Bash. Replace the briefing and paths with your task; src/feature is an example path, not a directory in this repository. These examples use manual delivery, so they do not commit or push:
echo "Update validation messages in src/feature to US English without changing behavior. Run the relevant tests." | node .agents/skills/sol-luna-orchestration/scripts/invoke-profile-executor.mjs --profile implement-lite --cwd . --sandbox workspace-write --write-root src/feature --delivery manual
echo "Fix null input handling in src and add regression tests. Do not edit src/generated." | node .agents/skills/sol-luna-orchestration/scripts/invoke-profile-executor.mjs --profile implement --cwd . --sandbox workspace-write --write-root src --forbid-root src/generated --delivery manual
Read-only profiles reject workspace-write. Write profiles do not enable it automatically. Every profile rejects danger-full-access, approval overrides, and bypass flags. Control plane v2 and result format v2 are the defaults; use --control-plane v1 only for the legacy read-only interface.
npm run executor -- --profile ... is available for convenience, but the direct command provides the clearest option forwarding and exit-code behavior.
The launcher prints a colored route banner to stderr when the terminal supports color:
◆ PLAYWRIGHT · LUNA@LATEST → GPT-6-LUNA · MAX · STANDARD · READ-ONLY
Machine-readable JSON remains the only stdout output. NO_COLOR, TERM=dumb, and FORCE_COLOR are honored.
An assignment is a saved task contract tied to a full Git base revision. It records the permitted paths, checks, artifacts, review and approval requirements, and delivery policy. It also records priority and any allowed symlink or submodule operations. The briefing stays separate from public status, and the records live outside the repository under Codex home.
The global installer creates $CODEX_HOME/sol-luna-orchestration/config.json with automatic delivery enabled:
{
"automatic_delivery": true,
"models": {
"advanced": "astra@latest",
"economy": "luna@latest"
}
}Automatic delivery is the default for new writer assignments, but it can be turned off. After root or Ultra validates and integrates a candidate, the controller commits only that candidate. If the checked-out branch has a configured upstream with the same name, the assignment records it and the controller performs a normal fast-forward push. Without a matching upstream, delivery stops after the local commit.
Set automatic_delivery to false to return to unstaged manual integration. An explicit --delivery manual|commit|push overrides the setting for one new assignment. Existing and resumed assignments keep their stored policy. Read-only and review profiles always remain manual.
Save an assignment without starting it, then inspect the remaining work. Replace the example briefing and paths as above. reconcile starts assignments whose scopes do not conflict with active work and resumes pending transitions; it follows each assignment's stored delivery policy:
echo "Fix null input handling in src/feature and add regression tests." | npm run executor -- --profile implement --cwd . --sandbox workspace-write --write-root src/feature --delivery manual --enqueue-only
npm run control -- status --cwd .
npm run control -- next --cwd .
npm run control -- reconcile --cwd .
Every state change must name the current revision. A request based on an older revision is rejected; this is revision fencing.
An assignment moves through queued, running, result_ready, claimed, optional review and approval, integration_pending, and then delivery. Manual delivery uses integrated. Automatic delivery adds commit_pending and committed, plus push_pending and published when a push is required, before reaching acknowledged.
A Git failure moves the assignment to delivery_blocked and waits for an explicit retry. It does not loop. Blocked, failed, and recovery-required attempts can be archived and retried separately. Every mutation includes a unique action id, the expected state revision, and its authority. Exact replays are idempotent; altered replays and stale revisions fail closed.
The residual planner starts disjoint write scopes in parallel and retains active leases across blocked or failed attempts. It never uses capacity as a fan-out target and never falls back to a shared writable checkout.
explore reads a detached worktree at the assignment's committed base_revision. Changes to the main checkout do not affect that snapshot. The result includes read_revision:<sha> in checks, so you can see exactly which revision it read. Ask the root to inspect uncommitted changes; they are not part of the snapshot. Durable assignments keep their snapshots until acknowledgment and cleanup. Legacy read-only calls remove their temporary snapshots when they finish.
Worktrees and sandboxing cover different risks, so both stay enabled. workspace-write limits the executor process. The detached worktree keeps Git changes away from the main checkout. Before publication, the controller verifies the actual changed paths, rejects executor commits or HEAD changes, checks symlink and submodule policy, runs declared commands without a shell, and copies only declared in-scope artifacts.
After validation, the controller creates an immutable hidden candidate ref through Git plumbing. The executor never stages or commits. The candidate id binds the base revision, tree diff, contract, checks, and artifact manifest. Reusing an attempt with different content is rejected. A new writer assignment resolves its delivery policy once from the global configuration unless the launcher receives an explicit override.
Before integration, the controller applies the candidate to the current committed target in a temporary worktree and reruns required_checks. Those results apply only to the candidate, target revision, check contract, and combined Git tree that were tested. If the target changes, the checks must run again. Commit and push also require evidence for the exact tree being delivered. Checks must leave the source unchanged; ignored build outputs are allowed. Unrelated staged and working changes are preserved and excluded from delivery.
Put the relevant integration tests in required_checks, along with any setup they need in a clean checkout. An empty list leaves behavioral compatibility untested. Routing verification confirms which model ran; it does not establish that the code works.
Add --depends-on <assignment-id> for each prerequisite, such as an API contract another task needs. Dependencies must already exist in the same repository. A resumed assignment keeps its original dependencies.
The planner waits until each dependency has an acknowledged, verified result. Before the dependent task starts, the controller checks that the dependency's candidate paths match committed HEAD, records the result identities, and uses that HEAD as the task's base. If you integrated a dependency manually, commit its accepted changes first. Missing content, failed work, or evidence that changed before startup blocks the task; prerequisites are never skipped.
Add --resource-json <JSON-object> for each resource the executor and its required checks need. Each argument takes one JSON object. The assignment contract stores them in runtime_resources:
{
"runtime_resources": [
{ "name": "api", "kind": "port", "mode": "exclusive", "key": "3081", "env": "APP_PORT" },
{ "name": "cache", "kind": "cache", "mode": "isolated", "env": "BUILD_CACHE" },
{ "name": "database", "kind": "database", "mode": "isolated", "env": "TEST_DATABASE" }
]
}Exclusive reservations prevent cooperating runs from claiming the same kind and key across repositories that share Codex home. For ports, use an explicit number from 1 to 65535. You can also reserve identifiers for databases, caches, directories, and services. Keys must not contain connection strings or credentials.
Isolated database resources get a unique name. Isolated cache and directory resources get private temporary directories. The declared environment variables are set only in child processes. Reservations do not create databases, start containers, bind OS ports, or isolate undeclared services. All existing sandbox restrictions still apply.
The executor and its post-turn checks share one reservation. Checks on the combined tree and explicit result recovery use fresh resources, so they cannot depend on a cache or database left by an earlier phase. A conflicting reservation fails immediately. On normal completion, cleanup removes the run's directories and reservations only after registered child processes have stopped or their PIDs have been reused. If a process identity cannot be confirmed, cleanup is blocked. This check does not cover unregistered descendant processes.
Gate status lists machine-wide runtime_reservations. After an interruption, inspect the reservation you want to recover, then use its exact id:
node .agents/skills/sol-luna-orchestration/scripts/orchestration-gate.mjs recover-runtime --cwd <repository> --reservation-id <id>
Recovery checks that the reservation belongs to the repository and that every registered process has stopped or its PID has been reused. It never kills processes, releases Ultra, reruns a model, or creates databases or containers. Reservations do not expire automatically. Worktrees keep files separate, but Git metadata and the rest of the operating system are still shared.
For commit delivery, the controller builds a temporary index from the current branch and applies only the validated candidate. It creates a candidate-bound commit, updates the checked-out branch with compare-and-swap semantics, and synchronizes only the candidate paths in the real index. Unrelated staged and unstaged work is preserved.
Automatic push discovery reads only the checked-out branch's configured upstream with the same name and stores it in the assignment. Explicit push delivery still requires a named configured remote and an existing branch. Before pushing, the controller checks that the delivery commit's parent exists remotely and verifies ancestry. It then publishes the exact commit without --force and verifies the remote result. Unrelated local parent commits are never published.
The deterministic plumbing commit does not execute repository commit hooks or create a signed commit. Put mandatory validation in required_checks; repositories that require interactive hooks or signed commits should keep delivery manual.
Root-driven review and integration use explicit state revisions. Replace <id>, <n>, and <candidate-id> with current values from status. These commands show individual transitions, not a script to run unchanged from top to bottom. Commit, push, and retry commands apply only when the assignment's policy and state require them:
npm run control -- claim --cwd . --assignment-id <id> --revision <n>
npm run control -- request-review --cwd . --assignment-id <id> --revision <n>
echo "Review this candidate for correctness and missing tests. Do not change files." | npm run executor -- --profile review --cwd . --sandbox read-only --candidate-id <candidate-id>
npm run control -- approve --cwd . --assignment-id <id> --revision <n> --kind root
npm run control -- integrate --cwd . --assignment-id <id> --revision <n>
npm run control -- commit-delivery --cwd . --assignment-id <id> --revision <n>
npm run control -- push-delivery --cwd . --assignment-id <id> --revision <n>
npm run control -- retry-delivery --cwd . --assignment-id <id> --revision <n>
npm run control -- ack --cwd . --assignment-id <id> --revision <n>
An independent verdict is bound to the exact candidate id and revision. Optional operator approval is a separate authority. Before integration, the controller rechecks candidate integrity and path drift and refuses conflicts with local work.
After integration, reconcile can perform the declared commit, push, acknowledgment, and cleanup. A blocked delivery records a sanitized reason and waits for retry-delivery. Retrying the assignment is a separate operation that first archives the previous worktree. Acknowledged and abandoned workspaces can then be cleaned safely.
npm run dashboard -- --cwd .
npm run simulate -- --iterations 1000 --seed 73
The dashboard binds only to 127.0.0.1, localhost, or ::1. It uses a single-use URL token, an HttpOnly SameSite cookie, Host and Origin validation, CSRF protection, and a restrictive content security policy. It exposes the redacted projection, not briefings, raw events, or artifact contents, and permits only operator answers, approvals, and explicit delivery retries.
The deterministic simulator is pure: it changes neither Git nor durable state. It exercises successful candidates, commit and push delivery, delivery blocking and retry, zero-change readers, independent review, operator approval, blocked execution retry, recovery, stale action replay, stale candidate use, premature integration, and unauthorized actions.
{
"schema_version": 2,
"assignment_id": "uuid",
"attempt": 1,
"status": "completed | blocked | failed",
"profile": "explore | implement-lite | playwright | implement | review | null",
"thread_id": "string | null",
"model": "string | null",
"reasoning_effort": "string | null",
"service_tier": "fast | standard | null",
"routing_verified": false,
"sandbox_mode": "read-only | workspace-write",
"base_revision": "full Git object id",
"candidate": "object | null",
"summary": "string",
"changed_files": [],
"artifacts": [],
"operator_requests": [],
"checks": [],
"blockers": [],
"warnings": []
}The executor still supplies only task fields. The launcher adds observed routing metadata and the durable controller adds assignment, attempt, base revision, candidate, artifact, operator-request, and required-check evidence. routing_verified becomes true only when App Server settings confirm model, effort, and tier and rollout turn_context independently confirms model and effort.
The legacy v1 task payload remains available to compatible read-only callers. Ultra uses the shared routing fields and adds mode: "ultra", lock_id, integer generation, and executors. The generation is allocated once per Ultra epoch and is included even in terminal failures after acquisition.
Exit codes are stable:
0: completed with verified routing;1: blocked, failed, or Codex returned a task-level error;2: invalid invocation, capacity conflict, timeout, configuration, contract, MCP, or routing verification failure.
A lease reserves an executor slot for a registered process. The launchers acquire leases atomically at both repository and machine scope, enforcing these limits:
- Luna: 10 active executors per repository and 10 across the PC;
- Advanced (Astra and Sol combined): 4 active executors per repository and 4 across the PC;
- total machine capacity: 14 executors;
- Playwright: 2 across the PC, counted inside the Luna pool.
The serialized advanced pool is still named sol in status and durable records. All Astra and Sol releases share its four slots, including historical reservations. Selecting another family does not reset capacity, generations, or history.
The root and Ultra process do not consume executor slots. Executors started by Ultra do. Reaching an executor pool limit fails immediately. Durable assignments may remain explicitly queued until reconcile can start them; the launcher does not create a second hidden queue.
These numbers are upper bounds, not a target. Parallel work should have independent evidence or ownership. Overlapping writes must remain sequential, and creating redundant executors usually costs more integration time than it saves.
Inspect active leases and capacity with:
node .agents/skills/sol-luna-orchestration/scripts/orchestration-gate.mjs status --cwd .
status is an observational snapshot. It reports snapshot_at, persisted capacity, orphaned_run_ids, and pending_finalizations. It does not create a namespace, remove leases, update metadata, or write history. Process checks run outside the coordination mutexes: Windows uses one batched query, while Linux and macOS allow up to four concurrent inspections. A missing history entry produces an incomplete-snapshot warning, not a cleanup action.
Mutations revalidate captured records before changing state. The mutex wait remains five seconds; changed records allow at most three preflight attempts. Dead reservations may be removed only when every registered identity is confirmed stopped or reused. Unknown identities and corrupt state fail closed. Pending results keep their capacity reserved.
The launcher saves an immutable, hashed receipt before finalizing an executor. If coordination fails, it returns code 2 and status=failed, keeps the verified routing metadata, and prints the run id and recovery command. This is not a reason to rerun the model or discard the writer's worktree.
node .agents/skills/sol-luna-orchestration/scripts/orchestration-gate.mjs finalize --cwd . --run-id <id>
node .agents/skills/sol-luna-orchestration/scripts/orchestration-gate.mjs finalize --cwd . --run-id <id> --expected-revision <n>
Assignments require the second form, using the revision saved in the receipt and shown by status. Recovery verifies the receipt, stopped or reused process identities, current ownership, and the assignment attempt, revision, worktree content, and artifacts. Repeating the exact recovery does not republish the result. Recovery stops at result publication: it does not approve, integrate, commit, push, or invoke Codex again.
An Ultra result can be finalized only by its still-authorized owner in the same active epoch. A recovery-required lock or newer generation blocks it; finalize never recovers or releases Ultra. Receipts and separate completion confirmations live under the repository's existing state namespace in finalizations/<run_id>/ and are not automatically deleted. No finalization retry runs in the background.
The playwright profile starts a private stdio server named sol_luna_playwright, pinned to @playwright/mcp@0.0.80. You do not need a global Playwright MCP entry. The installer leaves existing MCP entries alone, including entries created by earlier releases. The internal name is reserved: if your configuration already uses it, the executor stops and asks you to rename that entry.
Only this executor's App Server process receives the private configuration. Its default_tools_approval_mode remains "approve" for browser actions authorized by the briefing, and browser_run_code_unsafe is disabled. The user's server named playwright is disabled in that process only; unrelated MCP entries are not changed.
Each run has a unique temporary directory with separate browser-temp and artifact subdirectories. The launcher checks that both are writable, removes inherited PLAYWRIGHT_MCP_* variables from the child environment, and passes --isolated and --output-dir. Cleanup runs after App Server stops, including on failure; cleanup errors are reported. Browser isolation does not replace sandbox or authorization rules.
Before starting the turn, the launcher verifies the exact package pin in effective configuration and waits up to 30 seconds, within the overall timeout, for the thread's MCP inventory. Package 0.0.80 bundles Playwright 1.63.0-alpha-2026-08-31; the server advertises that runtime version, not the package version. Both are checked separately. The inventory must also expose navigation, snapshot, click, screenshot, and close tools. Successful use requires a completed browser call from that server, thread, and turn without a tool error. Text mentioning Playwright does not count. The launcher adds playwright_mcp:verified, playwright_mcp_version:0.0.80, and playwright_runtime_version:1.63.0-alpha-2026-08-31 to checks.
Package upgrades are explicit: update the central package version and expected runtime version together with their regression expectations, then pass offline checks and the localhost browser probe before adopting the release. Runtime resolution never uses @latest. A browser or Windows sandbox failure leaves live verification pending; it does not trigger a version fallback or permission bypass.
Executor turns are non-interactive and start with App Server approval policy never. Command and file approvals are declined, permission grants return an empty grant, and MCP elicitations are declined using the response shape required by the installed App Server.
A non-blocking user-input request receives an empty answer and execution continues. A blocking, non-sensitive question is saved as an operator request. Once the answer is acknowledged and the assignment is retried, that answer becomes part of the next briefing. Sensitive input is never saved to durable state; the request fails closed.
Full interaction is allowed on localhost and explicitly named development or test environments. External sites are observation-only unless the briefing explicitly authorizes a named state-changing action and destination. Purchases, deletion, publishing, messaging, account or security changes, production mutation, and browser_run_code_unsafe are always prohibited.
Ultra temporarily replaces the root for one repository; it is not an executor profile. Run it only after a human has authorized the takeover and its reason. This read-only example sends a concrete briefing; replace it with the authorized task:
echo "Review the repository concurrency design. Report risks and unresolved decisions without changing files." | node .agents/skills/sol-luna-orchestration/scripts/invoke-sol-ultra.mjs --cwd . --reason "Concurrency review needs an exclusive takeover" --confirm-exclusive-takeover --sandbox read-only
The lock blocks normal sessions and has no automatic expiry. Every Ultra acquisition receives a repository-persistent, monotonically increasing generation shared by that epoch. Ultra may delegate only through the verified profiles, which inherit both CODEX_ORCHESTRATION_LOCK_ID and CODEX_ORCHESTRATION_GENERATION and consume the normal capacity pools. State and result transitions revalidate both values, so a stale epoch cannot finish, abandon, release, or publish results into a newer epoch.
State v2 registers the Ultra launcher and App Server plus each executor launcher and App Server. Each registration uses a PID, process-start fingerprint, instance id, host, platform, and architecture. Recovery is fail-closed: it succeeds only when every registered identity is dead or reused; same and unknown identities reject recovery. Recovery never terminates processes.
A verified terminal result releases the lock. Timeout, interruption, process, contract, or routing failure leaves recovery-required. Check and recover it only through the gate:
node .agents/skills/sol-luna-orchestration/scripts/orchestration-gate.mjs status --cwd .
node .agents/skills/sol-luna-orchestration/scripts/orchestration-gate.mjs history --cwd . --limit 50
node .agents/skills/sol-luna-orchestration/scripts/orchestration-gate.mjs recover --cwd . --lock-id <exact-lock-id>
Version 1 state is never silently upgraded while active. The gate reports it as legacy-unfenced; after all legacy owners have stopped, recovery additionally requires --confirm-legacy-recovery. In a v1-only repository, the next Ultra acquisition starts generation 1.
History is append-only and stores sanitized coordination metadata rather than briefings, model responses, or raw errors. Retention targets 1,000 events and prunes only terminal generations; the active generation is protected and can temporarily keep the count above that target. History corruption is reported as a warning and is never used as lock authority.
There is no TTL, heartbeat, automatic recovery, shared-checkout write fallback, dependency fallback, or shell: true execution. Ultra cannot release while one of its durable assignments remains unfinished. Descendants not launched through the registered launcher/App Server paths cannot be identified portably. Fencing prevents stale orchestration transitions and result publication, but it cannot atomically cancel an arbitrary unregistered descendant that is already mutating a worktree.
npm test
npm run verify:platform
npm run verify:live -- --schema-only
npm run verify:live -- --playwright-only
npm run verify:live
The unit suite covers the control plane from routing through delivery. It tests JSON-RPC ordering and failures, profile routing, durable state transitions, action replay, resource leases, worktree isolation, immutable candidates, automatic-delivery precedence, manual integration, candidate-only commits, push delivery, delivery retry, required checks and artifacts, independent review, and operator gates. It also exercises dashboard security, deterministic fault simulation, tiers and terminal colors, capacity races, Ultra fencing, process identity, fail-closed recovery, history retention, version 1 migration, Playwright evidence, installer rollback, and platform-specific paths.
Choose the check that matches what you need to verify:
verify:platformneeds no login and starts no model turns. It checks compatibility, the current process fingerprint, and the App Server response contracts for approvals, permission grants, user input, and MCP elicitation.verify:live -- --schema-onlyruns one root turn against the production output schema. Despite its name, this option uses a model.verify:live -- --playwright-onlychecks a localhost interaction and a real PNG, then verifies two overlapping isolated Playwright runs and rejection of a third lease. It does not run the other profiles.- The full
verify:liverun checks the root and every profile against protocol and rollout evidence. Write tests use temporary repositories, and read-only checks must leave this repository unchanged.
Add --output <path-outside-the-repository> to save verification evidence.
The root probe first reads effective global settings through config/read in a temporary repository with no local configuration and no model or effort overrides. Only after those defaults match does it negotiate a separate root turn. Writer probes explicitly use manual delivery, including Ultra's delegated writer; publication tests use only temporary local remotes. Evidence from an earlier Sol run does not certify Astra. Platforms not rerun remain pending, and successful acceptance proves compatibility, not a particular cost saving.