- ci_event_wait: OPERATION_SPECS (op-specific completion authority: branch_ci/pull_request_checks -> own runs; master_ci/deploy/release -> deploy qualification); SINGLE LOGICAL AWAIT (all nonterminal conditions re-arm internally; model sees only terminal verdicts or real deadline) - await-operation tool: op_type parameter - runtime wait guard + cwd guard RETIRED (canonical hook = documented no-op; prevention lives in tests, not interception) - wr_root_resolver.py: ONE wait/resume root authority; split-brain fails loudly; start_wr_daemon.py resolves root once; governed reconciliation - construction_activation._auto_structural_awareness: PROJECT_BUILD bootstrap auto-registers Registry + spine manages edge (no manual bookkeeping); fixed latent dispatch_from_decision mode-object bug - research_memory_port.py: future-facing Memory port inside existing INWARD (provider contract, zero-result fast path, teachable-moment candidate seam; APPLICABILITY governs transfer) - v1_parity_manifest.py: 56 rows, 0 blocking mandatory-V1 items - G1-G12 gate evidence in store/scaffold/t21.5_gates.json
65 lines
2.1 KiB
Python
65 lines
2.1 KiB
Python
#!/usr/bin/env python3
|
|
"""start_wr_daemon.py — T21.5 §7: THE canonical daemon restart launcher.
|
|
|
|
T21.4 live finding: `python3 daemon.py` defaults WR_ROOT to the daemon's
|
|
install dir, so a restart silently opened a second event store. This
|
|
launcher resolves the root ONCE through scripts/wr_root_resolver.py (the
|
|
single authority) and passes it explicitly — nobody has to remember to
|
|
export WR_ROOT.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
import urllib.request
|
|
from pathlib import Path
|
|
|
|
_SCRIPT_DIR = Path(__file__).resolve().parent
|
|
sys.path.insert(0, str(_SCRIPT_DIR))
|
|
import wr_root_resolver as resolver # noqa: E402
|
|
|
|
DAEMON_CODE_CANDIDATES = (
|
|
"/home/nick/.local/share/mimo-wait-resume/daemon.py",
|
|
)
|
|
|
|
|
|
def find_daemon_code() -> Path:
|
|
for cand in DAEMON_CODE_CANDIDATES:
|
|
p = Path(cand)
|
|
if p.is_file():
|
|
return p
|
|
raise RuntimeError(f"daemon.py not found in {DAEMON_CODE_CANDIDATES}")
|
|
|
|
|
|
def main() -> int:
|
|
root = resolver.resolve_root()
|
|
daemon = find_daemon_code()
|
|
env = dict(os.environ,
|
|
WR_ROOT=str(root), # daemon data root
|
|
RUNE_WR_ROOT=str(root), # adapter-side authority
|
|
RUNE_WR_CODE=str(daemon.parent))
|
|
port = sys.argv[1] if len(sys.argv) > 1 else "8791"
|
|
proc = subprocess.Popen(
|
|
[sys.executable, str(daemon), port],
|
|
env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
|
start_new_session=True)
|
|
# bounded startup probe (process-local, not an external wait)
|
|
for _ in range(20):
|
|
try:
|
|
with urllib.request.urlopen(
|
|
f"http://127.0.0.1:{port}/health", timeout=1) as r:
|
|
if r.status == 200:
|
|
print(f"wr daemon up: pid={proc.pid} root={root} "
|
|
f"port={port}")
|
|
return 0
|
|
except OSError:
|
|
time.sleep(0.25)
|
|
print(f"wr daemon did not become healthy (pid={proc.pid})", file=sys.stderr)
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|