1
0
forked from jason/echo
Files
chorus/README.md
T
jason f2bfd70dd4 Phase 5: group-voice docs, onboarding runbook, CHORUS icon — v2.0.0-rc.1
The final phase of CHORUS-PLAN.md. Code- and doc-complete for group use;
rc pends live deployment at chorusapi.mpm.to.

- Repo README rewritten for CHORUS: group model (schema 5), concurrency
  posture, configuration, baked-build onboarding, layout, testing, 2.0
  version history (ECHO history referenced at the fork tag).
- Operator→member voice sweep across SKILL.md (incl. the trigger
  description, now group-aware), all references, 8 command docs, plugin
  README; member preferences / semantic layer / triage guidance describe
  the group model.
- docs/ONBOARDING.md: member runbook — vault stand-up, per-member config,
  bake, deliver, self-onboarding first load, norms, offboarding.
- docs/USAGE.md rewritten as the member usage guide.
- New icon: chorus-icon.svg + 1024/512/64 PNGs (three voices, one center).
- Removed ECHO-era assets (icons, spec PDF, perf briefs); ECHO planning
  docs retired to docs/history/. All preserved at echo-fork-point.
- eval/README documents test_multimember.py. Manifest -> 2.0.0-rc.1;
  rebuilt chorus-memory.plugin.

Verified: routing-sync, 25/25 unit, scaffold, 5 mock e2e suites,
run_eval metrics green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 15:41:58 -05:00

12 KiB

chorus-memory — v2.0.0-rc.1

CHORUS is shared group memory for Claude / CoWork sessions: one Obsidian vault serving a whole team, driven over the Obsidian Local REST API. Every member's sessions read the same knowledge graph and write to it with full attribution — where ECHO reflected one voice back, CHORUS is many voices in one performance.

Architected by Jason Stedwell. CHORUS is a group-based fork of ECHO (the single-operator original, preserved at the echo-fork-point tag) and a clean break from it: fresh vaults on fresh machines, no ECHO back-compat. The conversion plan and settled design decisions live in CHORUS-PLAN.md; deployment endpoint: https://chorusapi.mpm.to.

The skill makes direct REST calls through a bundled validated client (scripts/chorus.py). The whole toolchain is pure Python (stdlib only), so it runs identically on Windows, macOS, and Linux — no bash, no platform-specific date.

This repository (jason/chorus) holds the plugin source (tracked tree at chorus-memory.plugin.src/), the built chorus-memory.plugin artifact (rebuilt on each version bump; versioned builds ship as Gitea releases), and a credential-free eval/ test harness.


The group model (schema 5)

One vault, many members. Each machine is configured with the group and its own member id (a kebab-case slug like jason), and every agent write is stamped author: <member>.

  • Shared by the group: projects, areas, resources, decisions, the journal (one team timeline — every ## Agent Log line is member-tagged - YYYY-MM-DD [member]: …), semantic/episodic/working memory including group-profile.md (what the team is + a ## Members roster), a shared group inbox for unowned items, the entity/recall indexes, and the duplicate gate (two members can't create parallel notes for the same client — a blocked capture names whose entity it collided with).
  • Per member, no contention by construction: _agent/members/<member>/{preferences,current-context,heartbeat,inbox}.md and _agent/sessions/<member>/ — each member's profile, active scope, last-session pointer, personal captures, and session logs are theirs alone.
  • Self-onboarding: a new member installs their plugin and runs a session — load detects the missing namespace, seeds it, and adds them to the roster.
  • Group lenses: recall --author <member> filters the graph to one member's notes; load --all-members shows every member's scope and last session.
  • Visibility is by design: one vault, one key — everything in it (including member preferences) is readable by the whole group. Truly private material belongs in a personal vault, not CHORUS.

Concurrency posture

Namespacing removes most contention outright. What stays shared-write is built for interleaving: member-tagged whole-line-idempotent appends for logs/inboxes, and the machine indexes are guarded by an advisory lock with bounded retry/backoff (CHORUS_LOCK_RETRIES, default 4) plus a fresh re-read inside the critical section, so concurrent captures merge instead of clobber. Lock owner ids carry the member (<member>-<client>-<pid>), making contention attributable. Full model: skills/chorus-memory/references/operating-contract.md.


Configuration

The plugin ships no group, member, endpoint, or key — each machine supplies them via a machine-local JSON config (resolution in scripts/chorus_config.py):

  • File: ~/.claude/chorus-memory/config.json — honors $CLAUDE_CONFIG_DIR; path overridable with $CHORUS_CONFIG. Shape: { "group": …, "member": "<kebab-slug>", "endpoint": "https://…", "key": "…" }. member is required — it attributes every write.
  • Per-field env override: CHORUS_GROUP, CHORUS_MEMBER, CHORUS_BASE (endpoint), CHORUS_KEY.
  • Set up a machine: chorus.py config import <file> adopts a config you've been handed; chorus.py config set --group … --member … --endpoint … --key … writes one from values; config init scaffolds a template; chorus.py config prints the resolved config (key redacted) with per-field sources.
  • First run: an unconfigured machine reports NOT CONFIGURED (load exits 78; /chorus-doctor flags it red) and the skill asks the member for their config before doing memory work. The config is never committed and never written into a vault note.

Per-member baked builds — the onboarding mechanism

The CoWork sandbox doesn't bridge your real ~/.claude, but the plugin itself is mounted every session — so each member's credentials travel inside their artifact:

python build.py --bake-key --from alice-config.json --label alice
# -> dist/chorus-memory-2.0.0-rc.1-alice.plugin  (carries alice's member id + the vault key)

A complete baked set (endpoint + key + member) is authoritative — it can't be shadowed by stale env vars or leftover configs. The committed source ships zero credentials; baked artifacts land in dist/ (gitignored) and are delivered directly to their one member, never committed or published. The full member-onboarding runbook is docs/ONBOARDING.md.


Requirements

  • Python 3 in the session environment (stdlib only; python3, or python / py -3 on Windows).
  • Obsidian running on the backend with the Local REST API plugin enabled.
  • HTTPS reachability to https://chorusapi.mpm.to from the Claude / CoWork session environment.

Repository layout

chorus/
├── README.md · CHANGELOG.md · CHORUS-PLAN.md   ← this file · release log · conversion plan + decisions
├── docs/                            ← ONBOARDING.md (member runbook) · USAGE.md · history/
├── build.py                         ← deterministic .plugin builder (--bake-key/--strip-key/--label/--outdir)
├── chorus-memory.plugin             ← built, installable plugin (current pointer; versioned builds → Gitea releases)
├── chorus-memory.config.template.json ← blank config template (group/member/endpoint/key)
├── dist/                            ← per-member baked artifacts (secret-bearing) — GITIGNORED, never committed
├── eval/                            ← credential-free test harness; not bundled
│   ├── mock_olrapi.py · mock_olrapi_hifi.py   ← deterministic REST-API mocks (+ fault injection / real PATCH semantics)
│   ├── run_eval.py                  ← retrieval / dedup / write-safety / durability metrics
│   ├── test_features.py · test_reflect.py · test_offline_queue.py · test_patch_semantics.py
│   ├── test_multimember.py          ← 26-check two-member end-to-end suite (the group behavior)
│   └── results/latest.json
└── chorus-memory.plugin.src/        ← tracked source tree (the plugin — single canonical tree)
    ├── .claude-plugin/plugin.json   ← manifest (name, version, description)
    ├── README.md                    ← plugin-level README (configuration, tooling)
    ├── hooks/hooks.json             ← SessionStart auto-load · Stop reflection nudge
    ├── commands/                    ← /chorus-{load,save,recall,triage,health,sweep,reflect,doctor}
    └── skills/chorus-memory/
        ├── SKILL.md                 ← operating procedure (authoritative)
        ├── references/              ← operating-contract · bootstrap · vault-layout · routing-map · api-reference
        ├── scripts/                 ← pure-Python toolchain (chorus.py client, bootstrap/migrate/sweep,
        │                              vault_lint, routing.json manifest, capture/recall/triage/reflect ops)
        └── scaffold/                ← verbatim seeds bootstrap writes into the vault
                                       (group-profile, group-inbox, member-* anchors, templates, marker)

Division of responsibility: SKILL.md owns day-to-day procedure; references/operating-contract.md owns durable principles, safety rules, and the N-writer concurrency model; scripts/routing.json is the machine-readable source of truth for what may be written where (references/routing-map.md is its human authority; check_routing.py keeps them in sync mechanically).


Vault layout (data only, root-addressed)

/vault/
├── inbox/           ← captures/group.md (unowned, member-tagged) · imports/ · processing-log/
├── journal/         ← daily/ (shared team timeline, member-tagged Agent Log) · weekly/ monthly/ quarterly/ annual/
├── projects/        ← active/ incubating/ on-hold/ archived/   (folder == status:)
├── areas/           ← business/ personal/ learning/ systems/
├── resources/       ← people/ companies/ concepts/ references/ meetings/
├── decisions/       ← by-date/ (ADR-style)
└── _agent/
    ├── chorus-vault.md      ← bootstrap marker (schema_version: 5, plugin-owned)
    ├── members/<member>/    ← preferences.md · current-context.md · heartbeat.md · inbox.md   (per member)
    ├── sessions/<member>/   ← YYYY-MM-DD-HHMM-<slug>.md   (member-namespaced session logs)
    ├── memory/              ← working/ · episodic/ · semantic/ (incl. group-profile.md)
    ├── context/ health/ templates/ skills/ index/ locks/

The complete map — every write destination with its trigger and why it's distinct — is skills/chorus-memory/references/routing-map.md. Frontmatter conventions (incl. the required author: on agent-written notes) are in references/vault-layout.md.


Bundled tooling & commands

scripts/chorus.py is the validated client: keep-alive connection-pooled, HTTP-status-checked (failed writes exit non-zero), retrying, read-back-verified PUTs, whole-line idempotent appends. High-level ops collapse the write discipline into one call: capture (route + complete frontmatter incl. author: + index + auto-link + member-tagged log line, with a pre-write duplicate gate), resolve, recall [--author <member>] (hybrid BM25 + graph over the shared corpus), link, triage (both inboxes, attributed audit trail), load [--all-members] (8-read cold start + self-onboarding), plus scope, lock/unlock, config, doctor, and the vault maintenance scripts (bootstrap.py, migrate.py, sweep.py, vault_lint.py).

Slash commands: /chorus-load, /chorus-save, /chorus-recall, /chorus-triage, /chorus-health, /chorus-sweep, /chorus-reflect, /chorus-doctor. Session hooks auto-load memory at SessionStart and nudge reflection once per substantive session at Stop.


Testing

Everything runs offline against deterministic mocks — no credentials, no live vault:

python3 chorus-memory.plugin.src/skills/chorus-memory/scripts/test_chorus_client.py   # unit + routing-sync guard
python3 chorus-memory.plugin.src/skills/chorus-memory/scripts/test_v1_scaffold.py     # module interfaces + config
python3 eval/test_features.py          # capture/resolve/recall/link/sweep end-to-end
python3 eval/test_multimember.py       # 26-check two-member group lifecycle
python3 eval/test_offline_queue.py eval/test_patch_semantics.py eval/test_reflect.py
python3 eval/run_eval.py               # retrieval / dedup / write-safety / durability metrics

Version history

Version Highlights
2.0.0-rc.1 Phase 5: full group-voice docs, member onboarding runbook, CHORUS icon. Plan complete — pending live deployment at chorusapi.mpm.to.
2.0.0-alpha.4 Phase 4 hardening: bounded lock retry on index writes, attributed duplicate gate, recall --author, load --all-members, N-writer concurrency contract, committed two-member e2e suite.
2.0.0-alpha.3 Phase 3 (the core): schema 5 per-member namespacing, group profile + group inbox, self-onboarding load, member-tagged shared writes, per-member lint, migrate 4→5.
2.0.0-alpha.2 Phase 2: {group, member, endpoint, key} config (member required), author: stamped on every agent write, missing-author lint, member-attributed locks/queue, per-member baked builds.
2.0.0-alpha.1 Phase 1: mechanical echo→chorus rename; clean break from ECHO (no back-compat).

The pre-fork ECHO history (v0.3 → v1.5.1) lives in the jason/echo repo and at this repo's echo-fork-point tag.