mirror of
https://github.com/israerusan/unwritten.git
synced 2026-07-22 08:30:00 +00:00
src/core/gaps.mjs held a RAW NUL byte: pairKey wrote the pair separator as a literal 0x00
instead of an escape. Git classified the file as binary, so `git show --numstat` printed
"- -" and every change to it rendered as "Binary files differ" — the file holding all three
heuristics could never be reviewed in a diff again, and ripgrep/git grep skipped it. The
escape produces a byte-identical runtime string. test/source-hygiene.test.mjs asserts no
source file under src/, scripts/ or test/ contains a NUL, and pins the separator as a byte
no path can contain: with a printable one, ("A","B|C") and ("A|B","C") key the same and the
two pairs merge.
bridgeMaxLinks and maxOutlinksPerNote are independent sliders, so bridge >= cap was
reachable — at the cap slider's OWN MINIMUM, with the bridge slider at its default (6 vs 5).
Every note that survived the cap was then a bridge by definition, cooccur was never written
for any vault, and "Unexplained pairs" read "None found." forever with pairsConsidered: 0 and
no warning. The headline heuristic was silently off. resolveGapOptions now clamps the cap
above the bridge size; it only ever raises the cap, so a user's bridges stay bridges.
An explicit null string in data.json — a sync merge, a truncated write, a hand edit —
survived Object.assign (which only fills `undefined`), and refreshLicense's licenseKey.trim()
then threw inside onload(). The plugin did not degrade, it FAILED TO LOAD: no settings tab,
no commands, no way back without hand-editing JSON. Every string field is re-typed at load,
behind a `satisfies StringKeys<UnwrittenSettings>` list so a new string setting that is not
coerced is a compile error.
buildSnapshot retained every note's body for the whole run — ~160 MB of live JS strings on a
20k-note vault, on a plugin that ships isDesktopOnly:false and therefore runs on phones —
when only decision-tagged notes are ever read. Bodies are now retained only for decision
notes; word counts, which every note needs for stub hubs, are still computed for all.
snapshot.ts and gaps.mjs share one exported decisionSourceOf() so they cannot drift and
strand a decision note with no body (which would report it as unjustified — the worst
possible false positive).
community-plugins.json credited "saiken" while the manifest credits "Israel Avila". The
contract test pinned every other field, so the drift alarm had a hole in exactly the one that
disagreed; it now asserts author, and that the listed repo lives under the manifest's
authorUrl account.
Also re-synced src/shared from obsidian-plugin-core and committed src/shared/MANIFEST.sha256,
so `sync-shared --check` gates the vendored tree on a fresh clone.
Deliberately untouched: the Pro card (a later phase builds the features it advertises).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R3f6GZ2A8FbZerhJEpJQBE
|
||
|---|---|---|
| .. | ||
| install-vault.mjs | ||
| sync-shared.mjs | ||