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

39 lines
4.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).