echo-memory — v0.7.1
Persistent memory for Claude / CoWork sessions via the ECHO Obsidian vault, driven over the Obsidian Local REST API. No MCP server — the skill makes direct REST calls, now through a bundled validated client (scripts/echo.sh).
Built for Jason Stedwell (Director of Technical Services / Systems Engineer, MPM / ALABAMA wISP), who is both the operator and the architect of this vault. This is a personal plugin, not for distribution.
This repository (jason/echo-v.05) holds the plugin source (tracked tree at echo-memory.plugin.src/), the built echo-memory.plugin package artifact (rebuilt on each version bump), and a credential-free A/B eval/ harness.
0.7 in one line: the prose-and-raw-curl skill grew an executable spine — a status-checking API client, a machine-readable routing manifest the linter enforces, deterministic bootstrap/migrate scripts, an advisory multi-writer lock, four slash commands, and an eval harness. See the 0.7.0 version-history entry for the full list.
Core design principle — the plugin is the single source of truth
Everything that defines how ECHO behaves — bootstrap/repair logic, the operating contract, the taxonomy, frontmatter conventions, the routing map, and the canonical note templates — ships inside the plugin under skills/echo-memory/.
The vault itself holds data only. There are no CLAUDE.md / BOOTSTRAP.md / STRUCTURE.md / index.md control docs in it. The single in-vault control artifact is a one-line marker, _agent/echo-vault.md, that records the schema version.
Three consequences follow:
- Self-bootstrapping — point the REST API at an empty Obsidian vault and the plugin stands up the full folder tree, templates, anchors, and marker from its bundled
scaffold/. - Easy to update — change behavior by updating the plugin, not by editing files scattered through the vault.
- Portable — the same plugin brings any empty vault online identically; nothing essential depends on files pre-existing in the vault.
Configuration
The plugin is hardcoded for a single personal endpoint:
- Server:
https://echoapi.alwisp.com(reverse proxy → backend Obsidian Local REST API). This is the only valid endpoint — never use LAN addresses (10.x,192.168.x,:27124); those belong to retired memory systems. Override per-run withECHO_BASE. - Auth: a bearer token stored in the skill /
referencesand injected byscripts/echo.sh. The key lives only in the plugin — never inside a vault note (per the vault's own safety rules). Override withECHO_KEY. - TLS: the endpoint presents a valid certificate, so
-kis not needed.
Vault paths are addressed at the root (e.g. GET /vault/_agent/context/current-context.md). Prefer scripts/echo.sh over raw curl: it injects auth, checks HTTP status (a failed write exits non-zero instead of looking like success), retries transient 5xx, verifies PUTs, and does idempotent appends.
Requirements
- Obsidian running on the backend with the Local REST API plugin enabled.
- HTTPS reachability to
https://echoapi.alwisp.comfrom the Claude / CoWork session environment.
Repository layout
echo-v.05/
├── echo-memory.plugin ← built, installable plugin (zip artifact, rebuilt on version bump)
├── echo-memory-<version>.plugin ← versioned build artifacts (history)
├── eval/ ← credential-free A/B eval harness (0.6 vs 0.7); not bundled
│ ├── mock_olrapi.py ← deterministic mock of the REST API + fault injection
│ ├── run_eval.py ← orchestrator (runs the real echo.sh vs modeled raw curl)
│ └── README.md
└── echo-memory.plugin.src/ ← tracked source tree (the plugin)
├── .claude-plugin/plugin.json ← manifest (name, version, description)
├── README.md ← plugin-level README
├── commands/ ← slash commands: echo-load, echo-save, echo-triage, echo-health
└── skills/echo-memory/
├── SKILL.md ← operating procedure (authoritative)
├── references/
│ ├── operating-contract.md ← durable principles + safety rules + concurrency
│ ├── bootstrap.md ← bootstrap / repair / migrate manifest
│ ├── vault-layout.md ← canonical layout + frontmatter conventions
│ ├── routing-map.md ← complete endpoint→logic routing map (human authority)
│ ├── api-reference.md ← REST endpoint patterns + routing map
│ └── session-log-template.md
├── scripts/ ← executable logic (NEW in 0.7)
│ ├── echo.sh ← validated API client (auth, status-check, retry, verify, lock)
│ ├── routing.json ← canonical machine-readable route manifest (linter enforces it)
│ ├── vault-lint.sh ← read-only invariant checker (Vault Health)
│ ├── bootstrap.sh ← deterministic, idempotent vault setup/repair
│ └── migrate.sh ← deterministic schema migration (dry-run by default)
└── scaffold/ ← verbatim files the bootstrap writes into the vault
├── echo-vault.md ← bootstrap marker
├── README.vault.md ← thin human signpost
├── anchors/ ← operator-preferences, current-context, inbox seeds
└── templates/ ← 8 canonical note templates
Division of responsibility: SKILL.md owns day-to-day procedure (loading order, search-first, triage, scope switching, PATCH rules) and points at the bundled tooling. references/operating-contract.md owns the durable, client-independent principles, safety rules, and concurrency model. scripts/routing.json is the machine-readable source of truth for routing; references/routing-map.md is its human-readable authority. The other references are the canonical layout, API, and bootstrap specs.
Bundled tooling (0.7)
Executable logic ships under skills/echo-memory/scripts/; the agent prefers it over hand-built curl.
| Tool | Purpose |
|---|---|
echo.sh |
The validated API client. get/ls/map/search/put/post/append/patch/fm/bump/delete/lock/unlock/scope. Injects auth, checks HTTP status (non-zero exit on ≥400 — a failed write can't masquerade as success), one bounded retry on transient 5xx/connection errors, read-back verify on PUT, idempotent append (read-before-POST), correct :: heading targets. scope show reports the active scope + its freshness; scope set "<text>" switches it atomically (archive prior → replace → stamp scope_updated). |
routing.json |
The canonical machine-readable route manifest — one regex pattern per valid destination plus retired paths. The single source of truth for "what may be written where"; vault-lint.sh enforces it. |
vault-lint.sh |
Read-only invariant checker (Vault Health). Real YAML parsing, clock injected via ECHO_TODAY, exits 3 if the vault isn't bootstrapped (instead of falsely reporting "clean"). Includes a scope-drift check (flags when ≥ SCOPE_STALE_SESSIONS session logs postdate scope_updated). |
bootstrap.sh |
Deterministic, idempotent, probe-before-write vault setup/repair (--dry-run to preview). Resolves the scaffold relative to itself, so it works from any CWD. |
migrate.sh |
Deterministic schema migration. Dry-run by default; destructive steps gated behind --apply and printed first. |
Slash commands
/echo-load (cold-start read), /echo-save <text> (route + persist), /echo-triage (drain the inbox), /echo-health (run the linter) — explicit, reproducible entry points to the procedures below.
Concurrency (shared vault)
ECHO is read/written by multiple clients (Claude Code and CoWork). Single-line files (heartbeat/last-session.md, current-context.md::Scope) and append targets (inbox.md, ## Agent Log) assume one writer at a time. Before an overlapping burst of writes, take the cooperative advisory lock (echo.sh lock <id> → _agent/locks/vault.lock, TTL-reclaimable) and release it at session end. Idempotent append and status-checked writes are the second line of defense.
Vault layout (data only, root-addressed)
/vault/
├── README.md ← thin human signpost (NOT read for routing)
├── inbox/
│ ├── captures/ ← quick captures (inbox.md), date-prefixed lines
│ ├── imports/ ← raw imported material
│ └── processing-log/
├── journal/ ← one append-only time-series stream; rollups are coarser journal entries (no separate reviews/ tree)
│ ├── daily/ ← YYYY-MM-DD.md (has an "## Agent Log" section)
│ ├── weekly/ ← YYYY-Www.md (ISO week, opt-in rollup)
│ ├── monthly/ ← YYYY-MM.md (monthly rollup)
│ ├── quarterly/ ← YYYY-Qn.md (manual / on request)
│ ├── annual/ ← YYYY.md (manual / on request)
│ └── templates/
├── projects/ ← lifecycle: incubating → active → on-hold / archived
│ ├── active/ incubating/ on-hold/ archived/
│ └── project-template.md
├── areas/ ← business / personal / learning / systems
├── resources/
│ ├── companies/ ← <slug>.md — organizations (clients, vendors, partners, employers)
│ ├── concepts/ references/ meetings/
│ └── people/ ← <name>.md
├── decisions/
│ ├── by-date/ ← YYYY-MM-DD-<slug>.md (ADR-style) — the canonical home
│ └── decision-template.md (by-project/ is retired, not created)
│ (reviews/ is retired — journal rollups live under journal/; vault-health under _agent/health/)
└── _agent/
├── echo-vault.md ← bootstrap marker: schema_version + date (plugin-owned probe)
├── context/ ← current-context.md and task bundles
├── memory/
│ ├── working/ ← transient, time-boxed
│ ├── episodic/ ← what happened, when
│ └── semantic/ ← durable facts / patterns (operator-preferences.md)
├── sessions/ ← YYYY-MM-DD-HHMM-<slug>.md
├── health/ ← YYYY-MM-vault-health.md (monthly self-maintenance audit)
├── templates/ ← canonical note templates (seeded from the plugin's scaffold/)
├── skills/ ← active / archived capability catalog
├── heartbeat/ ← last-session.md pointer (read at load, written at session end)
└── locks/ ← vault.lock — cooperative advisory multi-writer lock (echo.sh lock/unlock)
Memory model
| Layer | Path | What it holds |
|---|---|---|
| Working | _agent/memory/working/ |
Transient, time-boxed state |
| Episodic | _agent/memory/episodic/ |
What happened, when |
| Semantic | _agent/memory/semantic/ |
Durable facts, patterns, preferences (operator-preferences.md) |
| Context | _agent/context/ |
Task-focused reading lists + active scope |
Operating procedures (logic)
Cold-start loading
Load memory at the start of any substantive conversation (anything beyond a single quick factual question). The cold-start reads are issued in parallel (one batch of 5–6 GETs):
_agent/echo-vault.md— the bootstrap marker.404→ vault not set up (run bootstrap);200→ checkschema_versionand migrate if older than the plugin's._agent/memory/semantic/operator-preferences.md— Jason's profile._agent/context/current-context.md— active scope + Scope History._agent/heartbeat/last-session.mdthen_agent/sessions/— read the heartbeat pointer first (O(1) jump to the latest log); fall back to the listing's ~5 most recent by reverse lexical sort.journal/daily/YYYY-MM-DD.md— today's note (404is fine).inbox/captures/inbox.md— inbox-depth probe for the load-time reconcile.
After the batch, reconcile: if the inbox holds captures older than ~7 days, surface the count and offer to triage (so triage self-fires rather than waiting to be invoked); and if the active scope diverges from what Jason just asked for, run Scope Switching.
If a specific project is in play, follow up with a search/simple/ across all lifecycle subfolders, querying both the slug and any human title used in conversation, then read the match at projects/<lifecycle>/<slug>.md. Loading is not narrated to the operator.
Inbox triage
inbox/captures/inbox.md is the catch-all. At session start, GET it; if it holds lines older than ~7 days that were never routed, surface a count once and offer to triage. Accepted items route to their proper home (preference → operator-preferences; project idea → projects/incubating/; durable fact → semantic; person fact → resources/people/), with the move recorded one-line in inbox/processing-log/YYYY-MM-DD.md as an audit trail. Originals aren't deleted unless explicitly asked.
Project lifecycle
Projects move through four folders; the folder name and the status: frontmatter field MUST agree — they are two views of one state. A file in projects/active/ with status: on-hold is broken state.
| Folder | status: |
Meaning |
|---|---|---|
incubating/ |
incubating |
Idea captured; not actively worked |
active/ |
active |
Current work (default for anything in motion) |
on-hold/ |
on-hold |
Paused but still tracked; resumable |
archived/ |
archived |
Done, abandoned, or rolled up — not deleted |
Promotion = move the file and update status: in the same change.
Scope switching
_agent/context/current-context.md tracks one active scope. It is the most churn-prone state — several sessions a day across different topics — so without care a new session silently inherits a stale scope (the same failure class as inbox auto-fire). 0.7.1 hardens this three ways:
- Freshness signal — a
scope_updated:frontmatter timestamp records when scope last changed. - One-command switch —
echo.sh scope set "<new scope>"does it atomically: archive the prior scope to## Scope History(dated, truncated), replace## Scope, and stampscope_updated. (Manual fallback: prepend history → replace## Scope→ PATCHscope_updated; the field must already exist or PATCH returns400 invalid-target.) Scope History is trimmed to the last ~10 entries. - Drift detection — at load the agent runs
echo.sh scope show(prints the scope, itsscope_updated, and how many sessions have been logged since) and states + confirms scope before working, switching if it diverges. As a backstop,vault-lint.shflags when ≥SCOPE_STALE_SESSIONS(default 3) session logs postdatescope_updated— surfaced in/echo-health, so drift is mechanically evaluable rather than invisible.
Daily note — Agent Log
After substantive activity, append a one-line entry to today's daily note's ## Agent Log. The procedure is resilient: GET the note → if 404, PUT it from the template → if 200 but the heading is missing, POST the heading. Heading detection greps the raw markdown for an anchored ^## Agent Log — not the document-map JSON, whose headings are ::-delimited paths (a bug fixed in 0.4.1 that previously appended duplicate headings).
Session logging
At the end of substantive conversations (those producing decisions, artifacts, or commitments), write a session log to _agent/sessions/YYYY-MM-DD-HHMM-<slug>.md. The four-digit HHMM component is canonical, not optional — it makes filenames lex-sort in true chronological order, which cold-start loading depends on. New writes are validated against ^\d{4}-\d{2}-\d{2}-\d{4}-[a-z0-9-]+\.md$.
Journal rollups
The journal is one append-only time-series stream; rollups are coarser-grained entries on the same timeline, so they live under journal/ — there is no separate reviews/ tree.
- Weekly — opt-in; offered on the first substantive session of a new ISO week, written only if accepted, to
journal/weekly/YYYY-Www.md. - Monthly — offered alongside the Vault Health pass on the first session of a calendar month, to
journal/monthly/YYYY-MM.md. - Quarterly / annual — manual / on request only, to
journal/quarterly/YYYY-Qn.mdandjournal/annual/YYYY.md.
Vault Health (monthly)
Agent self-maintenance (not a journal entry), written to _agent/health/YYYY-MM-vault-health.md. Run scripts/vault-lint.sh (or /echo-health) with ECHO_TODAY = the conversation's date so stale/aging math uses one clock. It mechanically asserts: folder↔status mismatch, duplicate slugs across lifecycle folders, wikilinks in frontmatter (swept across all folders), duplicate ## Agent Log headings, stale active projects (updated: > 30 days), aging inbox items (> 14 days), paths matching no route in routing.json or sitting at a retired path, frontmatter integrity (missing required fields, updated < created, future dates, wikilinks leaking into source_notes), and scope drift (≥ SCOPE_STALE_SESSIONS session logs dated after scope_updated). Exit codes: 0 clean · 1 violations · 2 unreachable · 3 not bootstrapped. Findings are reported, not auto-fixed.
Write-safety rules
- Search first (mandatory for new notes). Before creating any slug-addressed note,
POST /search/simple/?query=<slug>across the whole vault (and search the human title too) — listing one folder misses duplicates elsewhere. On a match: promote/merge from a non-active subfolder, update-in-place in the same folder, or ask which is canonical if elsewhere. - Read before append (idempotency).
POSTis not idempotent — a retry duplicates lines. Before any POST that adds an entry (inbox, Agent Log, an Observations/Log heading), GET the target and skip if the exact line is already present. (Does not apply to PUT or PATCH-replace.) - Bump
updated:on substance. PATCH the frontmatterupdated:to today after a meaningful content change (status update, decision, scope switch). Skip it for routine log appends — bump on substance, not heartbeat. - Preserve
created:. It is the earliest known date the entity was tracked anywhere — never reset it to "today" when merging. - No
[[wikilinks]]in frontmatter — YAML parses them as nested lists and they break. Cross-references go in a## Relatedbody section.source_notesholds plain relative-path strings (backward links to inputs only). - Check the HTTP status (or use
echo.sh). APATCHto a missing heading returns400 invalid-target(40080) and the write is silently lost; a transient503or an accepted-but-unpersistedPUTcan do the same.echo.shenforces this (exit ≠ 0, retry, read-back verify); rawcurlmust branch on-w "%{http_code}". Treat ≥ 400 as a failed op — surface it, don't continue.
REST API operations
Server https://echoapi.alwisp.com, bearer auth, root-addressed paths.
| Operation | Method | Notes |
|---|---|---|
| Read file | GET /vault/<path> |
Raw markdown; 404 = absent |
| Read heading | GET /vault/<path>/heading/<H1>::<H2> |
::-delimited, URL-encode spaces as %20 |
| List dir | GET /vault/<dir>/ |
Trailing slash; returns {files, folders} |
| Document map | GET + Accept: application/vnd.olrapi.document-map+json |
Discover exact heading / block / frontmatter targets |
| Append | POST /vault/<path> |
Appends to end-of-file; creates if absent |
| Create / overwrite | PUT /vault/<path> |
Auto-creates intermediate directories |
| Patch section | PATCH /vault/<path> |
Operation: append|prepend|replace, Target-Type: heading|frontmatter|block |
| Search | POST /search/simple/?query=<terms> |
Returns [{filename, score, matches}] |
| Delete | DELETE /vault/<path> |
Only on explicit operator request |
PATCH heading targets must be the full ::-delimited path from the top-level heading (e.g. Operator Preferences::Fact / Pattern) — a bare subheading name returns 400 invalid-target (errorCode 40080). GET the document map first when unsure of the exact path, and copy the heading string verbatim.
Memory routing map
| Situation | Vault path | Method |
|---|---|---|
| Quick capture / unsorted | inbox/captures/inbox.md (date-prefixed line) |
POST |
| Raw imported material | inbox/imports/ |
PUT |
| Operator preference / durable fact | _agent/memory/semantic/operator-preferences.md |
PATCH |
| Other durable facts, patterns | _agent/memory/semantic/<slug>.md |
PUT |
| Event record (what happened) | _agent/memory/episodic/<slug>.md |
PUT |
| Short-lived, time-boxed state | _agent/memory/working/<slug>.md |
PUT |
| Task-scoped context / focus | _agent/context/current-context.md |
PATCH / PUT |
| Working-session log | _agent/sessions/YYYY-MM-DD-HHMM-<slug>.md |
PUT |
| Long-running project state | projects/<lifecycle>/<slug>.md (folder and status: MUST agree) |
PUT + PATCH |
| Standing area of responsibility (no end state) | areas/<domain>/<slug>.md (business/personal/learning/systems) |
PUT |
| Non-obvious decision (ADR) | decisions/by-date/YYYY-MM-DD-<slug>.md (mirror into a project's ## Key Decisions; else skip) |
PUT |
| Person context | resources/people/<name>.md |
PUT / PATCH |
| Concept / reference note | resources/concepts/ or resources/references/ |
PUT |
| Meeting notes / call recap | resources/meetings/YYYY-MM-DD-<slug>.md |
PUT |
| Skill / plugin capability entry | _agent/skills/active/<slug>.md (→ archived/ when retired) |
PUT |
| Daily activity / Agent Log | journal/daily/YYYY-MM-DD.md |
POST / PATCH |
| Journal rollup | journal/{weekly/YYYY-Www,monthly/YYYY-MM,quarterly/YYYY-Qn,annual/YYYY}.md |
PUT |
| Vault-health audit (agent self-maintenance) | _agent/health/YYYY-MM-vault-health.md |
PUT |
| Session-end orientation pointer | _agent/heartbeat/last-session.md |
PUT |
| Bootstrap marker (plugin-owned) | _agent/echo-vault.md (schema_version, date) |
GET / PUT |
The complete endpoint→logic map — every path with its trigger and why it's distinct — is skills/echo-memory/references/routing-map.md.
Slug rules: kebab-case, ASCII, ~40 chars max. Every file carries canonical YAML frontmatter.
Frontmatter conventions
Every note opens with this block (fill what applies; leave the rest empty rather than guessing):
---
type: # daily-note | project | area | concept | reference | person |
# meeting | decision | review | session-log | working-memory |
# episodic-memory | semantic-memory | context-bundle | skill | ...
status: # active | draft | done | archived | complete | incubating | on-hold
created: # YYYY-MM-DD — earliest known date the entity was tracked
updated: # YYYY-MM-DD — bumped on meaningful change
tags: []
agent_written: # true on agent-generated notes
source_notes: [] # plain relative paths (backward links) — NEVER [[wikilinks]]
---
agent_written: true + a populated source_notes is the key signal separating agent-managed content from human-authored content.
operator-preferences.md — Rules vs Observations
## Fact / Pattern— promoted, deduped, timeless rules (no date prefix).## Observations— timestamped raw observations (- 2026-06-06: ...); the default landing zone for new evidence.
Observations are promoted into Fact / Pattern (dropping the date) once a pattern stabilizes, and trimmed to the last ~30 entries.
Bootstrap, repair & migration
The plugin carries everything needed to stand up a vault in scaffold/; there is no dependency on any in-vault control doc. In 0.7 these are scripts (scripts/bootstrap.sh, scripts/migrate.sh), not hand-run curl loops — the prose in references/bootstrap.md documents what they do and serves as the fallback.
- Probe — GET
_agent/echo-vault.md.200→ bootstrapped (checkschema_version);404→ run a fresh bootstrap. - Fresh bootstrap (
bootstrap.sh; idempotent, additive — never overwrites): create the folder tree (a one-line README seeds each leaf since Obsidian/git ignore empty dirs) → PUT the 8 templates to their mirrored vault paths → PUT the 3 anchor seeds only if absent (the operator-preferences seed is deliberately empty — no fabricated facts) → PUT the thin vault README → PUT the marker last (so the vault is only flagged "set up" once everything is in place) → write a first-run trace (daily note + session log).--dry-runpreviews. - Repair —
bootstrap.shagain: GET-probes each path and creates only what's missing; malformed files are flagged, never replaced. - Migrations —
migrate.shreads the marker'sschema_version, applies each intervening migration (dry-run by default;--applyto perform moves/deletes), and stamps the marker.0 → 1removed the old in-vault control docs (CLAUDE.md/BOOTSTRAP.md/STRUCTURE.md/index.md);1 → 2foldedreviews/intojournal/+_agent/health/.
Skill triggers
| Skill | Triggers |
|---|---|
echo-memory |
"remember that", "save to memory", "what do you know about me", "load my profile", "check my notes", "log this decision", "add to my inbox" — and proactively at the start of substantive conversations |
Safety rules (operating contract)
- Do not fabricate facts, relationships, or prior decisions.
- Do not mass-restructure the vault unless explicitly asked.
- Do not delete notes unless deletion is explicitly requested and clearly safe.
- Never store secrets or API keys inside a vault note.
- Default to additive updates and explicit status changes over destructive edits.
- Write in third person about Jason ("Jason prefers X"). Do not cross-write with other vaults (Bryan's
goldbrainis separate).
If the API returns a connection error, timeout, or 502 (usually Obsidian / the REST plugin not running), tell Jason once that the vault is unreachable and proceed without memory — don't retry repeatedly.
Version history
| Version | Highlights |
|---|---|
| 0.3.0 | Source promoted from zip-only to a tracked tree (echo-memory.plugin.src/); .plugin becomes a build artifact. All 7 skill-improvement items applied: search-first before writes, resilient daily Agent Log, created: semantics, project lifecycle + folder↔status rule, canonical HHMM session filenames, read-most-recent-N sessions, source_notes defined as backward links. |
| 0.4.0 | Efficiency + robustness pass: parallel cold-start loading, idempotent POST (read-before-append), doc-map-before-first-PATCH, scoped updated: bump, Inbox Triage, Scope Switching, monthly Vault Health, Rules-vs-Observations split, formal deprecation of decisions/by-project/, heartbeat pointer. |
| 0.4.1 | Bugfix: daily-note Agent Log heading detection now greps raw markdown for ^## Agent Log instead of the ::-delimited doc-map JSON (which never matched and appended duplicate headings). Added Scope Switching cold-start test harness. |
| 0.5.0 | Self-bootstrap + control-logic-in-plugin. Plugin becomes the single source of truth: bundled scaffold/ (8 templates, 3 anchor seeds, thin vault README, marker) bootstraps an empty vault with no external/local-path dependency. New operating-contract.md (principles + safety from the old in-vault CLAUDE.md); bootstrap.md rewritten as a portable bootstrap/repair/migrate manifest. Cold-start probe moved from /vault/BOOTSTRAP.md to _agent/echo-vault.md (carries schema_version). Live vault migrated to data-only. |
| 0.5.1 | Routing-doc consistency pass: decision-mirror heading unified to ## Key Decisions; stale Current status PATCH examples corrected to Status; vault-layout inline project example refreshed to the real template. All 17 projects/active/ notes normalized losslessly to the canonical template heading set; android-mqtt-shell moved to incubating/ (was broken status: upcoming in active). Plugin repackaged (21 files). |
| 0.6.0 | Schema 2. #8 Inbox auto-fire: the Loading procedure adds an inbox-depth GET and a load-time Reconcile step (inbox triage + scope-drift), so triage self-fires. #10 Routing: reviews/ retired — weekly/monthly/quarterly/annual rollups fold into journal/{weekly,monthly,quarterly,annual}/, vault-health moves to _agent/health/; new references/routing-map.md is the complete audited endpoint→logic map. Recs: heartbeat pointer operationalized (read first at load, written at session end); new scripts/vault-lint.sh mechanically checks vault invariants. Dead refs pruned (archive/, _agent/outputs/, resources/source-material). Migration 1 → 2 in bootstrap.md. |
| 0.7.1 | Scope-drift fix. Scope is the most churn-prone state (several sessions/day) and had no freshness signal, so sessions silently ran under stale scope (same failure class as #8). Added a scope_updated: frontmatter timestamp (maintained automatically), an echo.sh scope show / scope set command (atomic switch: archive prior → replace → stamp), and a vault-lint.sh drift check (flags when ≥ SCOPE_STALE_SESSIONS, default 3, session logs are dated after scope_updated) — making drift mechanically evaluable via /echo-health. Tightened the SKILL load-reconcile to state and confirm scope every session and switch before working. (Also fixed a bash nested-quote parse bug found while building scope, where show could fall through into set.) |
| 0.7.0 | Schema 2 (unchanged layout). Hardening pass — gave the prose-and-curl skill an executable spine. S2 scripts/echo.sh: one validated client wrapping every verb with auth, HTTP-status checking (failed writes exit non-zero instead of looking like success), one bounded retry on 5xx, read-back-verified PUT, and idempotent append. S3 scripts/routing.json: canonical machine-readable route manifest; vault-lint.sh enforces it (flags unknown/retired paths). S4 deterministic scripts/bootstrap.sh + scripts/migrate.sh (idempotent, dry-run, probe-before-write; fixes the old CWD-relative @scaffold/... empty-body bug). S5 cooperative advisory lock (_agent/locks/vault.lock) + documented multi-writer model. M1/M2/M5 linter rewrite: real YAML parsing, injected clock (ECHO_TODAY), exits 3 (not "clean") on an un-bootstrapped vault, plus routing-membership + frontmatter-integrity checks. M3 status-check guidance throughout. M4 four slash commands (/echo-load, /echo-save, /echo-triage, /echo-health). Added a credential-free A/B eval/ harness (mock REST API + fault injection): isolates a −76% generated-token I/O layer and 4 → 0 silent write failures vs 0.6. |