Name the EchoLedger in 02 — settle write provenance for attribution and announcements

06 and 10 consumed a per-file app-vs-foreign classifier that 02's component
inventory never named and whose layering prose seemed to deny. Settled: a
new EchoLedger component records each BoardWriter operation's expected
outcome (content hash / absence / move pair); final-content matching
classifies each observed file, deciding both races (byte-identical foreign
write → app-mediated, accepted; same-window foreign overwrite → foreign,
last writer wins). Ledger is in-memory and per-store; launch catch-up is
all-foreign; bracketed operations bypass it. "Trusts its own writes no more
than anyone else's" is scoped to rendering; BoardWriter's no-hidden-state
bullet stands.

Claude-Session: https://claude.ai/code/session_01HJ7PhFNmQ19bvy9RMD6GSb
This commit is contained in:
2026-07-26 20:05:54 -04:00
parent abca29054c
commit 515708a11b
3 changed files with 5 additions and 4 deletions
+1 -1
View File
@@ -51,7 +51,7 @@ Switching (or creating-and-switching) a branch from the board popover:
Agent and hand edits arrive through the watcher like any change and get auto-committed on the same debounce — so agent work is undoable, attributed, and *described* in the same trail: foreign changes compose through the same message engine as app-mediated ones (Commit messages above — the pathfinder's generic "External edit: 2 cards changed" fallback is gone), with origin carried by the author field. One attribution exception: the app's own agent-guide writes (08-agent-integration.md) are the app's own Writer operations — app-mediated by the echo machinery, carrying the guide's version-marker first line — and committed as "Update agent guide (vN)", not "External edit". Known quirk, not a bug: undoing an "Update agent guide (vN)" commit restores an older guide that the app immediately re-upgrades — a one-bounce no-op undo (restore commit + fresh upgrade commit). Harmless; the guide is app-owned and self-healing by design.
**Commit attribution is structural, not just a message convention.** The Writer/echo machinery lets the auto-committer classify every observed change as **app-mediated** (the user acting through the app) or **foreign** (anything else). User-driven commits carry the user's git identity; foreign changes are committed under the pinned synthetic author **`Lanework External <[email protected]>`** — so any git client can filter, log, and blame by origin. The strings are API (users script against them; the `.invalid` TLD honestly marks a non-routable synthetic identity) — they change with the deliberateness of a schema change.
**Commit attribution is structural, not just a message convention.** The Writer/echo machinery (the **EchoLedger** — 02-architecture.md ▸ Components, where its matching rule and race cases are settled) lets the auto-committer classify every observed change, per file, as **app-mediated** (the user acting through the app) or **foreign** (anything else). User-driven commits carry the user's git identity; foreign changes are committed under the pinned synthetic author **`Lanework External <[email protected]>`** — so any git client can filter, log, and blame by origin. The strings are API (users script against them; the `.invalid` TLD honestly marks a non-routable synthetic identity) — they change with the deliberateness of a schema change.
**Where the user's git identity comes from** (no git install is assumed, and the sandbox doesn't read `~/.gitconfig` — honest limits, not bugs): **repo-local `.git/config` wins when present** — standard git semantics, readable in-sandbox because it lives under the board root, and the natural state of adopted/cloned boards. The board popover's git section exposes name/email fields that **write that repo-local config** — the setting *is* the file, portable to any git client, per-board by nature (work and personal boards can differ). Absent repo config, the **derived default** applies: the macOS account's full name plus `shortname@hostname` — git's own no-config fallback shape, zero ceremony. Commits pushed to a forge under the derived email won't link to a forge account; the popover fields are the fix when that matters. A debounce window containing both kinds is **split into two commits**, never mixed (flush-before-overwrite already orders them: foreign first, then the user's overwrite). Honest limit: the app distinguishes app-mediated from foreign, not human from agent — a hand edit in a text editor and an agent write look identical *unless the writer says otherwise via `modified-by` (below)*. Agents wanting precise attribution are encouraged (via the agent guide, 08-agent-integration.md) to commit their own changes; the app follows along.