tools/test_launcher_bridge.py tested crates/coa-core/src/launcher_bridge.py,
which the Rust launcher replaced; check.yml and release.yml still ran it, so
the release build failed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A console shutdown request is sometimes lost, and the world then keeps
running until the stop times out. While the world still runs and its
console still answers after 15 s, the launcher asks again (twice at most);
asking twice is harmless. Verified on a disposable repack with the first
World::ShutdownServ call suppressed in a debugger: the second request
stopped the world.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Repacks ship no .bat files, and the Manager starts servers with
coa-core::launcher, so the realm-profile patch it wrote into manage.py
served no one. It is no longer applied (realm creation, realm switching and
start).
Recognising the edits older Managers made (realm patch, bridge import fix)
moves into legacy_launcher, so updates and file checks still treat such a
file as unmodified. Nothing there writes a file.
manage.py itself stays complete in the repack: Managers up to 0.9.10 run it
through their bridge and patch its text, so it cannot become a thin wrapper
until those Managers are no longer supported.
Verified: coa-core unit tests; examples/manager_launcher_e2e on a disposable
repack (manage.py unchanged after a start with a realm profile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
launcher::relay does what BugReport/relay.py and manage.py relay-worker do,
with the same spool: the <key>.status files the world reads, the journal
relay.sqlite3 (deliveries, settings), relay.lock (byte 0) and the
relay-ready record. Service answers map to the same states and retry times;
a post interrupted mid-flight stays uncertain and is never resent.
When coa-launcher.exe supervises a world it runs the relay itself; the
Python supervisor still runs relay.py.
Verified:
- examples/relay_equivalence: the same reports and scripted service answers
through relay.py and the Rust relay give identical statuses, journal rows
(including retry times) and posts, and each continues the other's journal.
- examples/manager_launcher_e2e on a disposable repack: supervisor and relay
are coa-launcher.exe, start/stop interoperates with manage.py.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
send_without_answer closed the socket right after writing "server shutdown".
With the reply still unread, Windows resets the connection, and the world
can drop the command: a world supervised by coa-launcher.exe ignored the
shutdown and the stop timed out after 90 seconds. The reply is now read up
to the next prompt, the end of the connection or 10 seconds.
The E2E also checks which supervisor runs the world and that manage.py
stops a world the Manager started.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
driver::run now calls coa-core::launcher::ops::run in-process instead of
running Scripts/manage.py through launcher_bridge.py. Failures carry a typed
code (Error::Service) instead of being guessed from stderr text.
Moved into the launcher: realm profiles (realm id, name and databases from
Settings/realm-profile.json), extra worlds sharing the main database,
the shared world startup deadline and console readiness check, the owned-world
check on shutdown, force-stop-all, and an environment block without
AC_REALM_SERVER_PORT for update validation.
The world supervisor is Scripts/coa-launcher.exe when the repack ships it,
otherwise manage.py watch-world. Extra worlds get an unmodified manage.py.
The realm patch of manage.py stays for the repack's own .bat files.
Verified with examples/manager_launcher_e2e on a disposable repack.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
std::process::Command lets a Windows child inherit every inheritable handle of the parent: a
MySQL started by the launcher kept the caller's files and pipes open (found by the SQUID test,
whose log stayed locked). Services now start through CreateProcessW with
PROC_THREAD_ATTRIBUTE_HANDLE_LIST = [log, NUL], as Python's subprocess does. The MySQL client's
outputs are read while it runs (a full pipe stopped it) and its last error line is reported.
E2E on a disposable repack: launcher_e2e PASS again; squid_equivalence PASS: Rust and
squid_playerbots.py leave identical journals, acore_playerbots tables (row counts) and
playerbots.conf (4106 lines), and a second run of either changes nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- services: exclusive-bind port check, readiness by listener row or the server side of a
loopback connection, MySQL client, start MySQL/auth, stop-all (world through its console,
database last), status.
- world: start-world through a supervisor process (coa-launcher watch-world) that keeps the
bug-report relay (still BugReport/relay.py via the repack's Python) alive; detailed logging.
- squid: squid_playerbots.py in Rust with the same journals (coa_squid_migrations,
coa_squid_pending, coa_bots_installed).
- coa-launcher.exe: the manage.py commands and error texts (the Manager reads them).
- Services no longer inherit the launcher's stdin/stdout/stderr: a server holding the
output pipe kept whoever read the launcher's output waiting.
E2E (examples/launcher_e2e, disposable stable repack on free ports): coa-launcher start-all ->
manage.py sees and stops it; manage.py start-all -> coa-launcher sees and stops it; a second
Rust-only cycle leaves nothing running.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
First part of moving Scripts/manage.py into coa-core (plan §17, PY-1). Same formats on disk as
manage.py so both can run on one server: .state records, the byte-0 control lock msvcrt.locking
takes, configuration.json. prepare renders byte-for-byte what manage.py renders (checked against
the real script, COA_MANAGE_PY). State files are flushed to disk before they replace the old ones,
and a damaged record counts as no process instead of failing every later start.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tauri::generate_context! needs ../dist. The tests run before tauri-action builds the frontend, so a
run without a warm target cache cannot compile the app (seen on preview/modules-v2, run 42224568581).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>