- DispatchSection: add cloud-hypervisor option (labelled experimental,
default off); default stays docker; hint states a KVM-host CHV worker
must claim the task (otherwise it stays queued; attachments are
rejected by the backend at creation) and that the frontend only passes
the executor value through, never bypassing the backend allowlist.
- EnvSection: same option, because the backend env-create allowlist
(local/docker/cloud-hypervisor) does allow it; note states env
allowlist excludes cloud.
- TaskListSection: add cloud-hypervisor to the executor filter so CHV
tasks are findable; list rows already showed executor.
- TaskDetailPanel: pure display note for cloud-hypervisor tasks
(experimental, default off, no VM controls); detail already showed
executor from the task API.
- README: document the CHV console entry, experimental marking, and
verbatim backend-error display.
- Rebuilt cloud/console.html from src (tsc + vite single-file build);
no backend change: executor is already returned by _task_to_dict.
Stack: #216 -> #221 -> this. Worker breakpoint branch now calls any
executor providing goal_resume_breakpoint_task (Local keeps its
original path, gateway tasks still loud-fail); WarmPool
reconcile_orphans is called on CHV worker startup and close with
audit-on-failure; per-VM SSH host keys are provisioned before VMM
start via cloud-init NoCloud seed (keyscan kept only as weak
fallback, CHV_HOST_KEY_MODE=provisioned|keyscan); CHV_NET_JSON is
validated in preflight and explicit direct egress is audited;
chv-guest/build.sh is now real steps with --dry-run and fails fast
without /dev/kvm. Tests: new test_chv_gaps.py (18), cloud full
1514 OK (skipped=4). No real VM verification: no KVM host.
Startup race: concurrent init_db (cloud-server + worker) ran
CREATE TABLE / CREATE INDEX IF NOT EXISTS against a DB where the
object did not exist yet; the check-then-create is not atomic, so
both sessions created it and one died with UniqueViolation on
pg_type_typname_nsp_index (tables) or pg_class_relname_nsp_index
(indexes). Hit on the 37ba687 and 7983567 deploys (7983567 added
idx_tasks_parent); systemd restart healed it every time.
Fix (advisory lock, not catch-duplicate_object): init_postgres_schema
takes pg_advisory_xact_lock (key 20261108, released at its commit),
and server.init_db takes the session-level lock for the whole PG
branch so the _ensure_* objects outside POSTGRES_SCHEMA
(task_continue_messages, user_gateway_keys, task_attempts,
task_audit_loop_state) are covered too. Same idiom as the existing
RATE_LIMIT_SWEEP_ADVISORY_KEY sweep lock; key is distinct.
Tests: two new real-PG regression tests (skipped without
CLOUD_TEST_PG_DSN), 4 processes racing on an empty schema, for
init_postgres_schema and for full init_db; both fail on the
unfixed code and pass with the fix.
Tasks gain kind='main'|'side' (NULL=main). A side thread hangs under a
parent task, defaults to the local executor, never builds a worktree,
branch or container unless explicitly asked, and writes its conclusion
back to the parent via task_followups on terminal transition.
- server: kind validation + side-thread gates (parent required, no
coordinator_parts/upload_id/environment_id, parent must not be a
coordinator), local executor default, GET /tasks kind filter,
/worktree 404 and /fork /merge /pr 409 for side threads.
- worker: side-default shunt in _run_attempt (no-worktree scratch cwd,
in-memory plan=1 when a repo is present, seed/checkpoint skipped),
_writeback_side_thread, marker task-create kind field + inheritance,
side threads excluded from marker spawn counts.
- host/sandbox: --no-worktree plumbing for dispatch/goal/popen
(Local + Docker), scratch cwd work/side-threads/<task-id>.
- console (classic only): kind filter + [旁聊] rows + '开旁聊' entry,
side-thread dispatch mode, side-thread list in detail, merge/PR/fork
hidden for side threads; build exit 0, console.html sha df39db5a.
- tests: new test_sidethread.py (29 tests) + kind backfill tests in
test_p2_migrations.py; full cloud suite 3900 tests OK (skipped=13).
Design: verification/codex-muse-role-alignment-design-20261108.md
Summary: verification/codex-muse-role-align-p1p2-20261108.md
Local only: not pushed, not merged, not deployed.
Platform coordinator.py is the single source of truth; the skill
coordinator template (templates/roles/00-coordinator.md + SKILL.md
appendix 00 + body) now derives from it instead of describing a
second, model-played coordinator (U1-U13).
- server: _create_task accepts parent_task_id (404/400 checks,
coordinator-under-coordinator rejected); follow-ups to a running
coordinator return 409 at entry (_create_followup /
_deliver_to_thread) with documented alternatives (U14/U15).
- tests: 8 new coordinator tests (depth limit, lineage, replay,
follow-up 409); appendix sync/provenance/transplant suites green.
- check_appendix.py: 12 chapters match templates/roles/.
Tenant networks were created bare (docker network create <name>),
letting Docker's default pool hand out subnets outside the iptables
8889 allowlist (172.16.0.0/12). The smoke tenant landed on
192.168.32.0/20, so containers never reached the egress proxy and
every model turn timed out at 600s twice.
Fix (B): derive a deterministic /24 inside 172.16.0.0/16 from
sha256(network name) with linear probing on IPAM overlap, max 32
probes. 172.16.0.0/16 is the one /16 in the allowlist Docker never
auto-assigns (default pool starts at 172.17.0.0/16). No persistent
state; capacity 256 tenant networks per host, explicit failure when
exhausted. Pre-existing out-of-range networks are reused as-is and
need manual removal (see README).
Tests: 7 new (candidate properties incl. wrap-around, pinned argv,
overlap probe, exhaustion, empty name).
- Vendor codex-ui-stack host/ as cloud/codex-ui-host/ (original/ not vendored, 650MB)
- Bridge emulates 7 methods missing in codex 0.164.0-alpha.9, exposes 29
experimental-protocol methods (150 -> 179), Linux binary candidates,
read-only POST /__codex_git
- GET /console 302s to the UI host (:8095) when reachable, else falls back
to the classic React console; GET /console-classic always serves it
- Alignment tables: docs/codex-native-ui-alignment.md
- Tests: test_console 7, new test_codex_ui_host 4, handler method pin 247 -> 249
DispatchSection keeps a single 'goal 模式' checkbox (the former
invincible option): checking it sends goal_mode=1 plus
no_time_limit=1. TaskDetailPanel drops the '无敌版' label.
Ported verbatim from feat-console-goal-unify commit 395dffb
(base 622c51e); console.html rebuilt from cloud/console-react
with npm run build:console (tsc+vite pass) and synced verbatim
from dist/index.html (sha256 daba1941...).
- test_column_notnull_probe_sees_gap3_outcome now branches on the
post-ensure probe: when outcome is relaxed (PG; SQLite 3.53+)
the resume write must land and read back NULL; on older SQLite
the ALTER is swallowed and INSERT OR IGNORE drops the row
(version-dependent boundary, not a migration failure).
- attempt_ledger.py comments updated: ALTER COLUMN DROP NOT NULL
works on Postgres and SQLite 3.53.0+, swallowed before that.
- PG proof summary section 3.9/8 corrected to the same boundary.
- attempt_ledger.ensure: relax gap-3 outcome NOT NULL on Postgres
(ALTER COLUMN outcome DROP NOT NULL, probe-gated); without it
every resume write on a gap-3-migrated PG table fails with
NotNullViolation. SQLite keeps NOT NULL (no such ALTER).
- db.PgCursor: PRAGMA table_info fake rows had the notnull flag
inverted (NO -> 0); now matches sqlite semantics (1 = NOT NULL).
No caller read row[3] before, so this was latent.
- ledger_onboarding: stale 'PG path unverified' notes updated to
point at the real-PG proof.
- tests: pragma_info notnull pin (fake driver) + _column_notnull
probe pins (fresh + gap-3 SQLite).
Proof: 28/28 checks on real Postgres 16.15 (see verification/
codex-task-attempts-pg-proof-20261106.md).
User decision 2026-11-05: unified schema, rejecting the rehearsal
runtime column-union workaround and a ledger rename. One definition
(cloud.attempt_ledger.TASK_ATTEMPTS_DDL, 12 columns) shared by
cloud.attempts and cloud.db.POSTGRES_SCHEMA; ensure builds the
unified table fresh and migrates old resume, gap-3 ledger, and
rehearsal shapes in place with rows intact. aggregate counts
ledger rows only (outcome NULL = resume row, never counted).
SQLite verified by 8 new tests; Postgres real-DB upgrade path
explicitly NOT verified (see batch summary). Local commit only:
no merge, no push, no deploy.