israerusan_effort-index/scripts
Israel Avila 0375ada3e0 Fix the writer election, the sidebar-click data loss, and three review findings
The suite's single-writer election is now per EVENT KIND (shared core,
SIGNALS_API_VERSION 2), synced in here. It has to be: Note Decay emits
open/rename/delete and never edit/dwell, and this plugin is the only one that
emits edit/dwell. A per-plugin slot meant whoever recorded first owned the log
and every event the loser emitted was dropped forever — so with Note Decay
installed and winning the coin flip (listener-registration order, re-rolled on
every restart), editMs was ZERO for every note in the vault. "Most expensive
notes" was permanently empty, the CSV export said "No effort data to export
yet", and the product silently did nothing. test/writer-election.test.ts stands
a note-decay-shaped store up first, lets it take `open`, and asserts our edit
and dwell still land on disk and still come back out of the fold — while `open`
is still appended exactly once, by its owner.

active-leaf-change fires for every leaf, including this plugin's OWN sidebar
panel. The tracker asked getActiveViewOfType(MarkdownView), which returns null
for a sidebar leaf, and dispatched `close` — and closeBurst DISCARDS any burst
under minSessionMs. Type 4 s, click the Effort panel, type 4 s more, go idle:
8 seconds of real editing recorded as zero. A leaf change into something that
is not a note is now treated exactly like a window blur (dwell pauses, the burst
stays open, the dead-man tick still closes it on the idle rule); a leaf change
into a different note still ends the session, which is what the listener is for.

Also:
- CSV injection: a note path beginning = + - @ (or tab/CR) exported as a live
  formula into Excel/Sheets/Numbers. Prefixed with an apostrophe and quoted.
- A non-string licenseKey in a corrupt data.json crashed onload() outright
  (`licenseKey.trim is not a function`) — no view, no settings tab, no way for
  the user to fix the file. Every field is now coerced back to its declared type
  at the single boundary where data.json enters (settings.ts coerceSettings).
- community-plugins.json said "saiken", the manifest says "Israel Avila". The
  obsidian-releases entry is now asserted field-by-field against the manifest.

test/run.mjs now awaits each async .test.ts (exported as `done`) instead of
firing it and moving on: the writer registry is a process-wide global, so two
interleaved tests elect into each other's registry — and a test nobody awaited
could "pass" without running an assertion.

Every one of the five was proved failing against the pre-fix code first.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3f6GZ2A8FbZerhJEpJQBE
2026-07-14 12:36:39 -04:00
..
install-vault.mjs Add Effort Index — measured editing effort per note (free tier) 2026-07-14 07:46:29 -04:00
sync-shared.mjs Fix the writer election, the sidebar-click data loss, and three review findings 2026-07-14 12:36:39 -04:00