1
0
forked from jason/echo

Phase 3: per-member namespacing — schema 5 (the core group conversion)

One shared vault, per-member namespaces for all churn-prone state:
- _agent/members/<member>/{preferences,current-context,heartbeat,inbox}.md
  + _agent/sessions/<member>/ session logs; shared group-profile.md
  (with auto-rostered ## Members) and inbox/captures/group.md.
- Self-onboarding: load detects a bootstrapped vault lacking THIS
  member's namespace and seeds it (bootstrap.member_bootstrap()).
- load = 8 reads (marker, group profile, my prefs/context/heartbeat,
  shared daily, my inbox, group inbox); heartbeat fallback lists my
  sessions dir; scope show/set is per member.
- Member-tagged shared writes: daily Agent Log lines and entity-update
  dated blocks are '- YYYY-MM-DD [member]: …'; capture --inbox targets
  my inbox; triage lists both inboxes and writes attributed audit lines.
- routing.json: member-anchors / inbox-group / member-segmented session
  routes; single-user anchor paths retired (schema 5). vault_lint runs
  aging-inbox + scope-drift per member. migrate.py 4->5 for alpha vaults.
- Scaffold: member-* seeds ({{MEMBER}}-stamped), group-profile/group-inbox
  seeds, marker schema 5. Docs (SKILL, vault-layout, routing-map,
  bootstrap) synced; manifest -> 2.0.0-alpha.3; rebuilt plugin (81 files).

Verified: all suites green (25/25 unit, scaffold, routing-sync 36 routes,
4 mock e2e, run_eval unchanged) + 19-check two-member smoke: bootstrap ->
self-onboard -> interleaved tagged daily log -> cross-member update
attribution -> independent scopes -> mine/group triage -> lint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 15:16:13 -05:00
parent b3bc5272d5
commit 33a2d73a22
31 changed files with 449 additions and 184 deletions
+5 -5
View File
@@ -287,9 +287,9 @@ def eval_write_safety(h):
# replayed append -> whole-line idempotency, no duplicate
ops += 1
h.run(CHORUS, "append", "inbox/captures/inbox.md", f"- {TODAY}: replay me")
h.run(CHORUS, "append", "inbox/captures/inbox.md", f"- {TODAY}: replay me")
n = (h.ground("inbox/captures/inbox.md") or "").count("replay me")
h.run(CHORUS, "append", "_agent/members/eval-member/inbox.md", f"- {TODAY}: replay me")
h.run(CHORUS, "append", "_agent/members/eval-member/inbox.md", f"- {TODAY}: replay me")
n = (h.ground("_agent/members/eval-member/inbox.md") or "").count("replay me")
if n != 1:
silent += 1
@@ -303,13 +303,13 @@ def eval_durability(h, dead_base):
h.seed("_agent/chorus-vault.md", "---\nschema_version: 4\n---\n# marker\n")
queued_ok = 0
for i in range(3):
r = h.run(CHORUS, "append", "inbox/captures/inbox.md",
r = h.run(CHORUS, "append", "_agent/members/eval-member/inbox.md",
f"- {TODAY}: offline capture {i}", base=dead_base)
queued_ok += int(r.returncode == 0 and "queued" in r.stdout)
# vault returns: flush replays; a second flush must not duplicate
h.run(CHORUS, "flush")
h.run(CHORUS, "flush")
inbox = h.ground("inbox/captures/inbox.md") or ""
inbox = h.ground("_agent/members/eval-member/inbox.md") or ""
landed = sum(1 for i in range(3) if f"offline capture {i}" in inbox)
dupes = sum(inbox.count(f"offline capture {i}") - 1
for i in range(3) if f"offline capture {i}" in inbox)