forked from jason/echo
Phase 1: mechanical echo→chorus rename with back-compat shims
Identity rename, no behavior change (CHORUS-PLAN.md Phase 1): - Plugin echo-memory → chorus-memory: manifest (v2.0.0-alpha.1), skill dir, 16 scripts (chorus.py, chorus_config.py, …), EchoError → ChorusError, /chorus-* commands, hook paths, docs, scaffold seeds, eval harness, build.py. Docs endpoint → chorusapi.mpm.to. - Env ECHO_* → CHORUS_*; config → ~/.claude/chorus-memory/config.json; state dir → ~/.chorus-memory/; marker → _agent/chorus-vault.md. Back-compat shims (one major version): - chorus_config aliases legacy ECHO_* env at import; reads a legacy echo-memory config.json when no CHORUS config exists (writes never land there); doctor/config report the legacy source. - State dir honors an existing ~/.echo-memory when the new dir is absent (offline queue not stranded). - Marker dual-probe in load/doctor/bootstrap/lint/sweep/migrate: a pre-fork _agent/echo-vault.md counts as bootstrapped; bootstrap repair won't write a second marker; routing gains agent-marker-legacy (GET). Verified: 25/25 unit tests, scaffold + routing-sync checks, 4 mock e2e suites, run_eval metrics unchanged, +6 shim smoke tests green. Rebuilt chorus-memory.plugin (79 entries). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+14
-14
@@ -6,7 +6,7 @@ Phase 2 — vault UP: `flush` replays them idempotently; the writes land.
|
||||
Phase 3 — vault UP: `load` caches the orientation reads.
|
||||
Phase 4 — vault DOWN: `load` serves the last-known-good cache and says OFFLINE.
|
||||
|
||||
Drives the real echo.py against eval/mock_olrapi.py. No creds, no live vault.
|
||||
Drives the real chorus.py against eval/mock_olrapi.py. No creds, no live vault.
|
||||
Run: python test_offline_queue.py [--port 8840]
|
||||
"""
|
||||
from __future__ import annotations
|
||||
@@ -21,7 +21,7 @@ import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
ECHO = HERE.parent / "echo-memory.plugin.src" / "skills" / "echo-memory" / "scripts" / "echo.py"
|
||||
CHORUS = HERE.parent / "chorus-memory.plugin.src" / "skills" / "chorus-memory" / "scripts" / "chorus.py"
|
||||
KEY = "test-key-not-a-real-secret"
|
||||
DEAD = "http://127.0.0.1:59998" # nothing listens here -> connection refused
|
||||
|
||||
@@ -58,10 +58,10 @@ def main():
|
||||
mock_base = f"http://127.0.0.1:{a.port}"
|
||||
state = tempfile.mkdtemp()
|
||||
|
||||
def echo(base, *args):
|
||||
env = dict(os.environ, ECHO_BASE=base, ECHO_KEY=KEY, ECHO_STATE_DIR=state,
|
||||
ECHO_VERIFY="0", ECHO_TODAY="2026-06-22")
|
||||
return subprocess.run([sys.executable, str(ECHO), *args], capture_output=True, text=True, env=env)
|
||||
def chorus(base, *args):
|
||||
env = dict(os.environ, CHORUS_BASE=base, CHORUS_KEY=KEY, CHORUS_STATE_DIR=state,
|
||||
CHORUS_VERIFY="0", CHORUS_TODAY="2026-06-22")
|
||||
return subprocess.run([sys.executable, str(CHORUS), *args], capture_output=True, text=True, env=env)
|
||||
|
||||
def start_mock():
|
||||
p = subprocess.Popen([sys.executable, str(HERE / "mock_olrapi.py"), "--port", str(a.port)],
|
||||
@@ -80,10 +80,10 @@ def main():
|
||||
srv = None
|
||||
try:
|
||||
# --- Phase 1: vault DOWN -> writes are queued, not lost ------------------
|
||||
r = echo(DEAD, "put", "_agent/sessions/2026-06-22-1200-x.md", tmp("EVALMARK-session\n"))
|
||||
r = chorus(DEAD, "put", "_agent/sessions/2026-06-22-1200-x.md", tmp("EVALMARK-session\n"))
|
||||
check("offline put exits 0 (queued)", r.returncode == 0, r.stderr)
|
||||
check("offline put reports queued", "queued (offline)" in r.stdout, r.stdout)
|
||||
r = echo(DEAD, "append", "inbox/captures/inbox.md", "- 2026-06-22: EVALMARK-inbox")
|
||||
r = chorus(DEAD, "append", "inbox/captures/inbox.md", "- 2026-06-22: EVALMARK-inbox")
|
||||
check("offline append reports queued", "queued (offline)" in r.stdout, r.stdout)
|
||||
outbox = Path(state) / "outbox.ndjson"
|
||||
check("two writes are persisted to the outbox",
|
||||
@@ -91,27 +91,27 @@ def main():
|
||||
|
||||
# --- Phase 2: vault UP -> flush replays them -----------------------------
|
||||
srv = start_mock()
|
||||
r = echo(mock_base, "flush")
|
||||
r = chorus(mock_base, "flush")
|
||||
check("flush replays the queue", "flushed 2" in r.stdout, r.stdout + r.stderr)
|
||||
check("queued PUT landed in the vault", (ground("_agent/sessions/2026-06-22-1200-x.md") or "").find("EVALMARK-session") >= 0)
|
||||
check("queued APPEND landed in the vault", "EVALMARK-inbox" in (ground("inbox/captures/inbox.md") or ""))
|
||||
check("outbox is empty after a clean flush", not outbox.exists() or not outbox.read_text(encoding="utf-8").strip())
|
||||
|
||||
# idempotent replay: a second flush of the same content must not duplicate.
|
||||
echo(DEAD, "append", "inbox/captures/inbox.md", "- 2026-06-22: EVALMARK-inbox") # re-queue same line
|
||||
echo(mock_base, "flush")
|
||||
chorus(DEAD, "append", "inbox/captures/inbox.md", "- 2026-06-22: EVALMARK-inbox") # re-queue same line
|
||||
chorus(mock_base, "flush")
|
||||
inbox = ground("inbox/captures/inbox.md") or ""
|
||||
check("replay is idempotent (no duplicate line)", inbox.count("EVALMARK-inbox") == 1, inbox)
|
||||
|
||||
# --- Phase 3: vault UP -> load caches the orientation reads ---------------
|
||||
http("PUT", f"{mock_base}/vault/_agent/echo-vault.md", "schema_version: 4\nEVALMARK-marker\n")
|
||||
r = echo(mock_base, "load")
|
||||
http("PUT", f"{mock_base}/vault/_agent/chorus-vault.md", "schema_version: 4\nEVALMARK-marker\n")
|
||||
r = chorus(mock_base, "load")
|
||||
check("online load shows marker", "EVALMARK-marker" in r.stdout, r.stdout)
|
||||
check("load wrote a cache dir", (Path(state) / "cache").exists())
|
||||
|
||||
# --- Phase 4: vault DOWN -> load serves the cache ------------------------
|
||||
srv.terminate(); srv.wait(timeout=5); srv = None
|
||||
r = echo(DEAD, "load")
|
||||
r = chorus(DEAD, "load")
|
||||
check("offline load flags OFFLINE", "OFFLINE" in r.stdout, r.stdout)
|
||||
check("offline load serves cached marker", "EVALMARK-marker" in r.stdout, r.stdout)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user