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:
@@ -3,7 +3,7 @@
|
||||
|
||||
These are exactly the behaviors the shipped naive mock cannot reproduce (it appends
|
||||
everything at EOF), so without this the section-replace / section-append / missing-
|
||||
heading / frontmatter-replace paths are untested. Drives the real echo.py against
|
||||
heading / frontmatter-replace paths are untested. Drives the real chorus.py against
|
||||
mock_olrapi_hifi.py.
|
||||
|
||||
Run: python test_patch_semantics.py [--port 8820]
|
||||
@@ -20,7 +20,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"
|
||||
|
||||
failures = []
|
||||
@@ -58,9 +58,9 @@ def main():
|
||||
srv = subprocess.Popen([sys.executable, str(HERE / "mock_olrapi_hifi.py"), "--port", str(a.port)],
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
|
||||
|
||||
def echo(*args):
|
||||
env = dict(os.environ, ECHO_BASE=base, ECHO_KEY=KEY, ECHO_VERIFY="0")
|
||||
return subprocess.run([sys.executable, str(ECHO), *args], capture_output=True, text=True, env=env)
|
||||
def chorus(*args):
|
||||
env = dict(os.environ, CHORUS_BASE=base, CHORUS_KEY=KEY, CHORUS_VERIFY="0")
|
||||
return subprocess.run([sys.executable, str(CHORUS), *args], capture_output=True, text=True, env=env)
|
||||
|
||||
def ground(path):
|
||||
_, body = http("GET", f"{base}/__debug__?path={path}")
|
||||
@@ -80,30 +80,30 @@ def main():
|
||||
|
||||
# replace: Scope body becomes new; Other untouched.
|
||||
http("PUT", f"{base}/vault/{doc}", seed)
|
||||
echo("patch", doc, "replace", "heading", "Doc::Scope", tmp("new scope"))
|
||||
chorus("patch", doc, "replace", "heading", "Doc::Scope", tmp("new scope"))
|
||||
g = ground(doc) or ""
|
||||
check("heading replace swaps section body", "new scope" in g and "old scope" not in g, g)
|
||||
check("heading replace leaves sibling section intact", "## Other\nkeep me" in g, g)
|
||||
|
||||
# append: lands INSIDE the section (before '## Other'), not at EOF.
|
||||
echo("patch", doc, "append", "heading", "Doc::Scope", tmp("appended line"))
|
||||
chorus("patch", doc, "append", "heading", "Doc::Scope", tmp("appended line"))
|
||||
g = ground(doc) or ""
|
||||
check("heading append inserts within the section (not EOF)",
|
||||
"appended line" in g and g.index("appended line") < g.index("## Other"), g)
|
||||
|
||||
# prepend: lands at the TOP of the section body.
|
||||
echo("patch", doc, "prepend", "heading", "Doc::Scope", tmp("first line"))
|
||||
chorus("patch", doc, "prepend", "heading", "Doc::Scope", tmp("first line"))
|
||||
g = ground(doc) or ""
|
||||
check("heading prepend inserts at section top",
|
||||
"first line" in g and g.index("first line") < g.index("new scope"), g)
|
||||
|
||||
# missing heading -> 400 -> echo.py exits non-zero (the silent-loss guard).
|
||||
r = echo("patch", doc, "append", "heading", "Doc::Nope", tmp("lost?"))
|
||||
# missing heading -> 400 -> chorus.py exits non-zero (the silent-loss guard).
|
||||
r = chorus("patch", doc, "append", "heading", "Doc::Nope", tmp("lost?"))
|
||||
check("missing heading fails loud (non-zero exit)", r.returncode != 0, r.stderr)
|
||||
check("missing heading does not write", "lost?" not in (ground(doc) or ""))
|
||||
|
||||
# frontmatter replace on an existing field updates it.
|
||||
echo("fm", doc, "updated", "2026-06-22")
|
||||
chorus("fm", doc, "updated", "2026-06-22")
|
||||
g = ground(doc) or ""
|
||||
check("frontmatter replace updates existing field", "updated: 2026-06-22" in g, g)
|
||||
|
||||
@@ -112,7 +112,7 @@ def main():
|
||||
# every other line is preserved. (The old contract failed loud on exactly the
|
||||
# notes that needed repair.)
|
||||
before = ground(doc) or ""
|
||||
r = echo("fm", doc, "nonexistent_field", "x")
|
||||
r = chorus("fm", doc, "nonexistent_field", "x")
|
||||
check("fm creates a missing field (create-or-replace)", r.returncode == 0,
|
||||
r.stderr or r.stdout)
|
||||
g = ground(doc) or ""
|
||||
@@ -121,7 +121,7 @@ def main():
|
||||
all(line in g for line in before.splitlines() if line.strip()), g)
|
||||
|
||||
# raw `patch replace frontmatter` (no fallback) still fails loud on a missing key.
|
||||
r = echo("patch", doc, "replace", "frontmatter", "still_missing", tmp('"x"'))
|
||||
r = chorus("patch", doc, "replace", "frontmatter", "still_missing", tmp('"x"'))
|
||||
check("raw frontmatter PATCH of a missing field fails loud", r.returncode != 0, r.stderr)
|
||||
|
||||
print(f"\n{len(failures)} failure(s)" if failures else "\nall PATCH-semantics tests passed")
|
||||
|
||||
Reference in New Issue
Block a user