Files
echo/docs/history/echo-improvements-prompt.md
Jason Stedwell 46e4b18a00 Move field report to docs/history; refresh README repository layout
- docs/history/echo-improvements-prompt.md (ROADMAP-2.0 §5 box ticked)
- README repository layout brought to 1.5.1 reality: root docs
  (CHANGELOG/TODO-1.6/ROADMAP-2.0/docs/history), build.py + dist/
  (gitignored, secret-bearing), current eval harness contents, hooks/,
  and the full scripts/ roster (recall/reflect/triage/queue/
  concurrency/quality/output/doctor/hook modules) with the legacy
  commands/ dir annotated as the TODO-1.6 migration target. Also
  restores the eval-descriptor fix lost in the prior commit (edit was
  applied in memory but never written back).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 13:32:07 -05:00

4.3 KiB
Raw Permalink Blame History

ECHO plugin — improvements & fixes (frontmatter completeness)

Context

While doing routine vault edits I found that partially-populated entity notes accumulate silently. An audit of resources/people|companies|references found 18 of 71 notes missing status and/or with empty tags (tags: [] or no key). Neither echo doctor nor vault_lint.py (/echo-health) catches this. I hand-fixed the vault, but the tooling should prevent the drift and make it easy to remediate. Three changes, root-cause first.

Root cause — capture writes incomplete canonical frontmatter

New entity notes created via capture land with tags: [] and no status key (verified on freshly-captured reference and company notes). Every such note is then "incomplete" by convention (complete siblings like resources/references/obsidian-local-rest-api.md carry status: active + real tags). So the drift is created at write time.

Fix (in capture / _build_note, echo_ops.py):

  • Stamp a kind-appropriate default status (e.g. active for person/company/reference/project) instead of omitting it.
  • Replace the empty tags: [] scaffold with either (a) an auto-seeded baseline tag (the note's kind, e.g. - company) so the key is never empty, or (b) a required --tags argument for entity kinds, or (c) a needs-tags marker tag that the completeness check (below) flags. Pick one; my preference is (a)+(b): seed - <kind> and accept --tags a,b,c to enrich at capture time.
  • Acceptance: a note created by capture "X" --kind company passes the completeness check below with zero follow-up edits.

Fix 2 — fm cannot create a missing key (only replace)

echo.py: cmd_fm() is cmd_patch(path, "replace", "frontmatter", field, ...). If the field is absent, the Obsidian REST API returns 400 invalid-target ("The patch you provided could not be applied"). This means echo fm note.md status active fails on exactly the notes that need it (status missing), forcing agents into GET-modify-PUT, which is error-prone (I clobbered tags on 2 notes with a naive rewrite before catching it).

Fix: make fm create-or-replace. On invalid-target, fall back to inserting the key into the frontmatter block (after type:, preserving all other lines) and re-PUT — or add an explicit fm --create / set op. Must be surgical: never rewrite unrelated frontmatter or body.

  • Acceptance: echo fm note.md status active succeeds whether or not status already exists, changing nothing else in the file.

Fix 3 — vault_lint.py: add an incomplete-frontmatter check

Today REQUIRED_FM = ("type", "created") only, and status is validated solely for projects (folder↔status match). Nothing requires status/tags on entity notes.

Fix: add a check that, for entity kinds (person, company, reference, and any others that should be classified — area, skill), flags:

  • missing or empty status
  • missing tags key, or tags: [], or a tags: key with no list items

Emit as a new check id incomplete-frontmatter with a message like "{path}: missing status" / "{path}: empty tags". Keep it kind-scoped so episodic/journal/session notes (which legitimately omit these) are not flagged. Consider a per-kind required-field map rather than the single global REQUIRED_FM tuple, so the rule is data-driven.

  • Acceptance: running vault_lint.py on a vault with a tags: [] company note reports one incomplete-frontmatter violation; a fully-populated note reports none.

Nice-to-have

  • A --fix / remediation mode (or a sweep sub-pass) that backfills status: <default> and a - <kind> baseline tag on flagged notes, so cleanup is one command instead of a hand-rolled script.
  • Note the zsh gotcha for any shipped helper loops: unquoted scalar for x in $var does NOT word-split in zsh — use arrays or while read.

Evidence / where I looked

  • echo.py cmd_fm (~L405), arg parser (~L673), cmd_patch.
  • vault_lint.py REQUIRED_FM (L42), per-note loop (~L161173), check-label registry (~L301+), walk()/list_dir() enumeration.
  • echo_ops.py _build_note (~L103) — has a status_v param already, so capture is positioned to stamp status.
  • Entity index _agent/index/entities.json stores path/kind/title/aliases/last_seen — not tags/status (so a completeness check must read notes, as vault_lint already does).