ver 1.5.0 — six-improvement pass: retention, routing, self-firing memory

1. capture-update keeps the whole body (was truncating to first line)
2. frontmatter completeness: kind-default status + kind-seeded tags at
   capture, fm create-or-replace, incomplete-frontmatter lint, sweep
   backfill (one data source: KIND_STATUS/KIND_REQUIRED_FM)
3. pre-write duplicate gate (exit 76, --merge-into/--force)
4. recall corpus + ranking: sessions/journal indexed (down-weighted),
   BM25 x freshness x status fusion, recall --json, index schema 2
5. session hooks: SessionStart auto-load, Stop reflection nudge
6. one-tap triage verb with processing-log audit; --json on read verbs

+22 mock end-to-end tests; all suites green. Docs current (README,
CHANGELOG, SKILL.md, commands, references, MAINTENANCE, eval README).
Drops tracked .DS_Store (gitignored); retires README-gretchen.md
(moved to gitignored dist/); adds the field report that drove item 2.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jason Stedwell
2026-07-03 12:57:52 -05:00
parent e76add6192
commit be3fd36ebf
31 changed files with 1209 additions and 174 deletions
+66
View File
@@ -1,5 +1,71 @@
# Changelog
## 1.5.0
Six improvements from the July 2026 review + the frontmatter-drift field report.
### Fixed — capture-on-update kept only the first body line (silent data loss)
`capture` on an existing entity appended `- <date>: <first line>` and **discarded the
rest of the body**. It now appends the whole body as a dated block — first line on the
bullet, continuation lines indented under it. Idempotency (bullet-per-day) unchanged.
### Added — frontmatter completeness (prevent → tool → detect → remediate)
A field audit found 18/71 entity notes missing `status`/`tags`; the drift was created
at write time and invisible to the tooling. Four coordinated changes, driven by one
data source (`KIND_STATUS` / `KIND_REQUIRED_FM` in `echo_index.py`):
- **`capture`** stamps a kind-appropriate default `status` (all kinds, not just
projects) and seeds `tags` with the note's kind; new `--tags a,b` enriches it.
Reflect/triage proposals accept a `"tags"` field.
- **`echo.py fm`** is now **create-or-replace**: on `400 invalid-target` (missing key)
it surgically inserts the one line into the frontmatter block and re-PUTs, verified —
it previously failed on exactly the notes that needed repair.
- **`vault_lint.py`** gains a kind-scoped `incomplete-frontmatter` check
(missing/empty `status`/`tags` on entity notes; block-style lists handled).
- **`sweep.py --apply`** backfills flagged notes (kind-default status + baseline tag).
### Added — pre-write duplicate gate in capture
A strong fuzzy candidate (score ≥ 0.6, env `ECHO_DUP_GATE`) now **stops** `capture`
before it creates a near-duplicate (exit `76` + candidate list) instead of warning
after the file exists. `--merge-into <slug>` routes the capture as an update to the
existing entity (mention learned as an alias); `--force` creates anyway. Weak
resemblances still create + warn as before.
### Changed — recall corpus + ranking (recall-index schema 2)
- The BM25 corpus now includes **session logs and journal notes** (down-weighted 0.5 /
0.4 so entity notes win ties) — past discussions are findable without having been
promoted into an entity note. `echo.py put` keeps the index current for corpus paths.
- Ranking fuses BM25 with **freshness** (half-life decay on `updated:`, floored at 0.6,
env `ECHO_FRESH_HALF_LIFE`) and **status** (`active` ×1.1, `on-hold` ×0.9,
`archived` ×0.75) — a stale archived note no longer outranks the live one.
- Hits print `updated:`/`status:`; `recall --json` emits structured results.
- The recall index carries per-doc meta (schema 2); an old index is discarded and
rebuilt automatically on the next recall/sweep.
### Added — session hooks (self-firing load & reflect)
New `hooks/hooks.json` + two hook scripts. **SessionStart** runs `echo.py load` and
injects the output as context (skips resume/compact; NOT-CONFIGURED degrades to an
instruction, never an error). **Stop** nudges once per substantive session (≥5 user
turns, env `ECHO_REFLECT_MIN_TURNS`) when nothing was reflected or session-logged —
reflection itself still previews and asks before writing. Both use the 1.4.2 CoWork
path fallback and always exit 0.
### Added — one-tap inbox triage + `--json` on read verbs
New `echo.py triage`: `--list --json` parses the inbox into `{line, date, text,
age_days}`; a proposals file (reflect schema + `"line"`) is classified → previewed →
applied via `capture`, with the `inbox/processing-log/` audit line written per move
(originals never deleted). `/echo-triage` rewritten around it. Also: `--json` for
`recall`, `link`, and `scope show` (`resolve`/`capture` already had it).
Tests: +22 feature cases (mock end-to-end incl. hooks), patch-semantics updated for
the new `fm` contract; all suites green. Docs: SKILL.md, README, command docs updated.
## 1.4.2
### Fixed — CoWork sandbox plugin-path resolution