This commit is contained in:
2026-06-11 10:57:01 -05:00
parent cfddfd23af
commit 06a90e2099
13 changed files with 395 additions and 38 deletions
@@ -27,6 +27,7 @@ The endpoint has a **valid TLS certificate**, so `-k` is not needed (add it only
- Durable principles, memory model, and safety rules: `references/operating-contract.md`
- Bootstrapping an empty vault, repair, and schema migrations: `references/bootstrap.md`
- Vault layout and frontmatter conventions: `references/vault-layout.md`
- Complete endpoint→logic routing map (every write destination, its trigger, and why it's distinct): `references/routing-map.md`
- Full API reference with every endpoint pattern and the memory routing map: `references/api-reference.md`
## Operating Contract & Safety
@@ -46,18 +47,26 @@ Load at the start of any substantive conversation — anything beyond a single q
### Loading procedure
The cold-start reads are independent — **issue them in parallel** (one batch of 45 GETs), not sequentially. Parallel loading is ~3× faster wall-clock for the same call count.
The cold-start reads are independent — **issue them in parallel** (one batch of 56 GETs), not sequentially. Parallel loading is ~3× faster wall-clock for the same call count.
| # | GET | Notes |
|---|-----|-------|
| 1 | `/vault/_agent/echo-vault.md` | The bootstrap marker. 404 → vault not set up; follow `references/bootstrap.md`. 200 → check its `schema_version` and migrate if older. |
| 2 | `/vault/_agent/memory/semantic/operator-preferences.md` | Jason's profile |
| 3 | `/vault/_agent/context/current-context.md` | Active scope + Scope History |
| 4 | `/vault/_agent/sessions/` (listing) | Pick the ~5 most recent by reverse lex sort (filenames `YYYY-MM-DD-HHMM-<slug>.md`, so lex == chrono); only read the ones whose slugs look relevant |
| 4 | `/vault/_agent/heartbeat/last-session.md` → then `/vault/_agent/sessions/` | **Read the heartbeat first** — a one-line pointer (`<session-log-path> @ <ISO-timestamp>`) written at the end of the previous session. It's an O(1) jump to the latest log, so you can skip or shortcut the full listing. Fall back to the `sessions/` listing only if the pointer is missing or looks stale; then pick the ~5 most recent by reverse lex sort (filenames `YYYY-MM-DD-HHMM-<slug>.md`, so lex == chrono) and read only the relevant ones. |
| 5 | `/vault/journal/daily/YYYY-MM-DD.md` | Today's note; 404 is fine — it's created on first agent activity |
| 6 | `/vault/inbox/captures/inbox.md` | Inbox depth probe — feeds the load-time reconcile below. 404 is fine (empty inbox). |
Do not read every session log — older sessions are reachable via `POST /search/simple/?query=...` when needed.
**Reconcile at load (do this every cold start, after the batch returns).** The batch already fetched everything needed for a cheap self-check — run it before diving into the work so memory maintains itself instead of drifting:
1. **Inbox depth (Inbox Triage).** If `inbox/captures/inbox.md` (GET #6) holds dated capture lines older than ~7 days that were never routed, surface the count once and offer to triage — see **Inbox Triage** below. This is the load-time trigger that makes triage self-firing rather than something you only run when asked.
2. **Scope drift.** Compare `## Scope` in `current-context.md` (GET #3) against what Jason just asked for. If they diverge, follow **Scope Switching** to record the prior scope and set the new one.
Keep the reconcile to a single short line to Jason (e.g. "3 inbox captures from last week are still un-routed — triage now?"); don't let it crowd out the actual request.
**If a specific project is in play**, follow up with a **search across all lifecycle subfolders** (`active/`, `incubating/`, `on-hold/`, `archived/`) — searching one folder at a time misses notes filed elsewhere. Search by **both the slug AND any human title** Jason used in this conversation:
```bash
@@ -265,24 +274,42 @@ When scope changes:
This keeps a rolling trail of recent scopes in one file instead of spawning separate stash notes. Trim Scope History to the last ~10 entries when it grows past that.
## Weekly Review (opt-in)
## Journal Rollups (the journal is one continuum)
On the **first substantive session of a new ISO week**, offer a short weekly rollup; write it only if Jason accepts. Path: `reviews/weekly/YYYY-Www-review.md` (ISO week, e.g. `2026-W24-review.md` — lex-sorts chronologically), `type: review`. Scope: open threads across `projects/active/`, inbox items aging past ~7 days, and the week's `## Scope History` changes from `current-context.md`. Keep it a light digest, not a vault-health audit. Detect the trigger by computing the current ISO week (`date +%G-W%V`) and checking whether that week's review note already exists.
The journal is a single append-only chronological stream. Rollups are just coarser-grained journal entries over the same timeline, so they **all live under `journal/`** — there is no separate `reviews/` tree. One place to read the whole time-series story, daily through annual.
The other cadences: **monthly** is automatic (the Vault Health pass below); **quarterly** and **annual** are **manual / on request only**.
| Cadence | Path | Trigger | Type |
|---------|------|---------|------|
| Daily | `journal/daily/YYYY-MM-DD.md` | first agent activity that day | `daily-note` |
| Weekly | `journal/weekly/YYYY-Www.md` (e.g. `2026-W24.md`) | first substantive session of a new ISO week — **opt-in**, offer first | `weekly-note` |
| Monthly | `journal/monthly/YYYY-MM.md` | first substantive session of a new calendar month — offer alongside the Vault Health pass | `monthly-note` |
| Quarterly | `journal/quarterly/YYYY-Qn.md` | **manual / on request only** | `review` |
| Annual | `journal/annual/YYYY.md` | **manual / on request only** | `review` |
All filenames lex-sort chronologically. Detect the weekly trigger with `date +%G-W%V` and check whether that week's note already exists; monthly with `date +%Y-%m`.
A weekly/monthly rollup is a **light digest** — open threads across `projects/active/`, inbox items aging past ~7 days, and the period's `## Scope History` changes from `current-context.md`. It is *not* a vault-health audit (that's an agent-maintenance artifact — see below).
## Vault Health (monthly)
On the first substantive session of a calendar month, run a quick health pass and write findings to `reviews/monthly/YYYY-MM-vault-health.md`. Don't auto-fix without asking.
On the first substantive session of a calendar month, run a quick health pass and write findings to `_agent/health/YYYY-MM-vault-health.md`. This is **agent self-maintenance, not a journal entry** — it lives under `_agent/` because it's about the vault's mechanical integrity, not Jason's work narrative. Don't auto-fix without asking.
Checks:
Run the bundled linter first — it mechanically checks the invariants below so you don't eyeball them:
```bash
bash "${CLAUDE_PLUGIN_ROOT}/skills/echo-memory/scripts/vault-lint.sh"
```
Checks (the linter asserts each and prints violations):
1. **Stale active projects** — for each note in `projects/active/`, check `updated:` >30 days. Likely belongs in `on-hold/`.
2. **Unprocessed inbox** — GET `inbox/captures/inbox.md`. List items older than 14 days that never moved through the triage protocol.
3. **Duplicate slugs across lifecycle folders** — any slug appearing in more than one of `active/`, `incubating/`, `on-hold/`, `archived/` is broken state.
4. **Broken-heading risk** — sample 23 frequently-PATCHed files; confirm `## Agent Log`, `## Scope`, `## Fact / Pattern`, `## Observations` headings still exist.
4. **Folder ↔ `status:` mismatch** — any `projects/<lifecycle>/` note whose `status:` frontmatter disagrees with its folder.
5. **Wikilinks in frontmatter** — any `[[...]]` inside a YAML frontmatter block (breaks Obsidian reading view).
6. **Duplicate `## Agent Log` headings** — any daily note with more than one.
The pass is cheap (a few searches + a directory listing) and pays for itself by catching drift before it requires a reorg.
The pass is cheap and pays for itself by catching drift before it requires a reorg. Write the findings as a digest; act on them only with Jason's go-ahead.
## Daily Note — Agent Log
@@ -349,6 +376,12 @@ curl -s -X PATCH -H "$AUTH" \
| Meeting notes / call recap | `resources/meetings/YYYY-MM-DD-<slug>.md` | PUT |
| Skill / plugin capability entry (catalog, not the build work) | `_agent/skills/active/<slug>.md` (→ `_agent/skills/archived/` when retired) | PUT |
| Daily activity / Agent Log | `journal/daily/YYYY-MM-DD.md` — see **Daily Note — Agent Log** above | PATCH (with auto-create) |
| Weekly / monthly rollup | `journal/weekly/YYYY-Www.md` · `journal/monthly/YYYY-MM.md` — see **Journal Rollups** | PUT |
| Quarterly / annual review | `journal/quarterly/YYYY-Qn.md` · `journal/annual/YYYY.md` (manual / on request) | PUT |
| Vault-health audit (agent self-maintenance) | `_agent/health/YYYY-MM-vault-health.md` — see **Vault Health** | PUT |
| Session-end orientation pointer | `_agent/heartbeat/last-session.md` (one line: `<session-log-path> @ <ISO-timestamp>`) | PUT |
> **The complete, audited routing map lives in `references/routing-map.md`** — every write destination with its trigger, what lands there, and why it's distinct from its neighbors. This table is the quick-reference; the map is the authority. If a path isn't in the map, it shouldn't be written to.
**Decision mirrors:** if the decision belongs to an existing note in `projects/active/`, add a `[[wikilink]]` to the ADR under that project's `## Key Decisions` heading (PATCH). If no matching project note exists, skip the mirror — the by-date ADR is sufficient; do not invent topical mirror files in `decisions/by-project/`.
@@ -382,6 +415,18 @@ curl -s -X PUT \
Then add a one-line entry to today's daily note via the **Daily Note — Agent Log** procedure above.
Finally, update the heartbeat pointer so the next session can orient in one GET (this is what closes the loop with loading-procedure Step 4 — a pointer nobody writes is a pointer nobody can read):
```bash
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
printf '%s @ %s\n' "_agent/sessions/${SESSION_FILENAME}" "$NOW" \
| curl -s -X PUT -H "Authorization: Bearer 241265fbe6830934a9a4ad3e69335f64a42153b663aa5b0017cb1ea1217b2bab" \
-H "Content-Type: text/markdown" --data-binary @- \
"https://echoapi.alwisp.com/vault/_agent/heartbeat/last-session.md"
```
`last-session.md` is a single-line pointer overwritten (PUT) each session end — never appended, so it can't grow or duplicate.
## Vault Unreachable
If the API returns a connection error, timeout, or `502`, tell Jason once that the memory vault is unreachable (a `502` usually means Obsidian/the REST plugin is not running on the backend), then proceed without memory. Do not retry repeatedly.