feat(knowledge): settle §6.2 — supersession computed, because newer-wins is false on the real library - #90
Open
guyettinger wants to merge 6 commits into
Open
feat(knowledge): settle §6.2 — supersession computed, because newer-wins is false on the real library#90guyettinger wants to merge 6 commits into
guyettinger wants to merge 6 commits into
Conversation
`docs/research/persistence-layers.md` §6.2 recorded one question as deliberately open: whether a Knowledge layer is persisted append-only -- the first state in this store that is neither rebuildable nor authored -- or stays derived. The doc required that it be decided deliberately rather than as a side effect of the first table. This is the decision, and no table. THE SOURCE IS RE-VERIFIED FIRST, because code had shipped on it. The preprint was cited here from v1; arXiv now serves a v2 revised 2026-06-12, and `06c5a57` shipped on v1's numbers in between. Every figure this repo built on is unchanged in v2 -- the pilot table, the CI and McNemar p, and the two that are load-bearing: the keyword-router reversal (delta -0.125), which is the whole argument for deriving a tier rather than storing one, and the three-session *core* threshold that is `CORE_SESSIONS`. A preprint can be revised out from under a citation; this one was not. MEASURED ON THE REAL LIBRARY, AND IT DECIDED BOTH HALVES. 12 recordings, SQLite read-only: `keymap_change` is 12 occurrences and ONE distinct payload, so nothing supersedes anything and the chain a naive design would build is length 1. `display_change` is 12 occurrences and EIGHT distinct payloads -- but only TWO configurations. Seven of the eight are the same 1920x1080@2 primary with a DIFFERENT `id` every session (180, 185, 206, 219, 247, 296, 297); the eighth is a real two-display setup. macOS re-mints the identifier per session, so `id` is a decoy and geometry discriminates. Three things follow. Keying supersession on the observed identifier would have minted eight facts where there are two -- the first table wrong on the first data it saw. The two real configurations do NOT supersede one another; a laptop is docked some days and not others, so "newer wins" would delete a configuration that is still true. And deciding whether display 180 and 185 are one display IS entity identity, which is therefore a PREREQUISITE of every candidate rather than the second one: "is this the same display", "is this the same document", "is this the same app version" are one question. DECISION ONE -- `DERIVED_LIBRARY_TABLES`, not a sixth bucket. The paper is append-only because its system does not retain the evidence; a conversation is gone. `CAPTURED_TABLES` is exactly that retained evidence, and a re-index must never touch it -- so re-derivability is free here and the paper's reason does not transfer. A fact outliving the deletion of its evidence is already precedented and needs no new rule: `removeSession` does not rebuild the graph, `observations` survives while `sources` thins, and `stabilityOf` already separates `undefined` from `[]`. DECISION TWO -- exclusivity is per fact TYPE, and supersession is COMPUTED. The paper's update mechanism and this repo's §7 decline of contradiction resolution collide on the display data, and the paper's rule loses. Every distinct value is kept with its sources; a type declares whether its values can coexist; "current" is answered per query and stored nowhere. That is the storage/query litmus applied to itself -- a stored `superseded_by` edge is a storage-level commitment to a ranking. The spec ships the contract only: `src/knowledge/` as a leaf, ordering INJECTED because `tMono` restarts per session and `started_at` is joined at query time, and a resolver that refuses three ways -- `coexisting` has no current value, a tie declines on `bindHabit`'s precedent, and undatable sources are disclosed. Counts only; a percentage would be `FrameResult.score` renamed. WHAT IT DELIBERATELY DOES NOT DO is decide whether two values are the same value. Fed the real display data it reports eight, not two. That is the seam: it makes the display case legible AS an identity problem instead of silently mis-keying it, which is what a stored edge would have done while looking correct. Cycle 0 of four. No table, no bucket edit, no stage, no UI, no MCP tool. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KNMAPpjspVHJU1kiuRpcba
Four tasks, each with its own test cycle and commit: the contract and the exclusive ranking, the three refusals, the barrel export, and the docs that settle §6.2 where a future session will actually find them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KNMAPpjspVHJU1kiuRpcba
The Knowledge layer as typed semantics: a fact is a kind and its distinct observed values, each carrying the recordings that saw it. Ordering across recordings is INJECTED, because t_mono restarts every session and session.started_at is joined at query time -- the same shape EdgeRecency takes, and for the same reason. Nothing here reads a clock. A value ranks by its LATEST source, not its first, and the composite is started_at + t_mono so two observations inside one recording still order. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KNMAPpjspVHJU1kiuRpcba
… real data A coexisting fact has NO current value and says so. Measured on the real library: display_change is 12 occurrences and 8 distinct payloads but only TWO configurations, and the two do not supersede one another -- a laptop is docked some days and not others, so "newer wins" would delete a configuration that is still true. The answer is the set. A tie DECLINES, on bindHabit's precedent. A fact no recording can date says that rather than guessing, and undatable sources are counted beside a ranking that still stands -- stabilityOf's undefined-vs-[] rule. Every path states a reason and returns counts. No fraction anywhere. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KNMAPpjspVHJU1kiuRpcba
Placed beside stabilityOf, which it is the companion of: both are the paper's semantics as a pure function over evidence the store already holds, and neither stores anything. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KNMAPpjspVHJU1kiuRpcba
persistence.md's "decision left open, on purpose" becomes the decision: DERIVED_LIBRARY_TABLES when Knowledge has a table, supersession computed rather than stored, exclusivity declared per fact type. The reasoning that matters is the measurement, so it travels with the rule -- 8 display payloads for 2 real configurations, because macOS re-mints the display id every session. That measurement also reordered the remaining cycles: entity identity is a PREREQUISITE of every Knowledge candidate, not the second of three. Also corrects two claims the settlement made stale: CLAUDE.md's stability bullet still said the call was left open, and persistence.md's section intro named stability.ts as the only thing that exists instead of a store. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KNMAPpjspVHJU1kiuRpcba
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Settles
docs/research/persistence-layers.md§6.2, the one question PR #89 deliberately left open: whether DeskRAG gets a persisted Knowledge layer with supersession chains that survive a re-index, or stays derived.It stays derived, and the reason is a measurement rather than a preference.
The paper's rule is false on this library
Roynard's Knowledge is append-only and updated by supersession — newer claim wins, older marked superseded. Read off the real store (SQLite read-only, 12 recordings, 2026-08-17 → 2026-08-29):
keymap_changedisplay_changeSeven of the eight display payloads are the same 1920×1080@2 primary carrying a different
idevery session (180, 185, 206, 219, 247, 296, 297) — macOS re-mints the identifier, soidis a decoy and geometry is what discriminates. The eighth is a genuine two-display setup.Three consequences, and each one killed a design:
superseded_bykeyed on the observed identifier would have minted 8 facts where there are 2 — the first table wrong on the first data it ever saw.keymap_changemakes the opposite point just as sharply: one distinct payload across twelve recordings, so there is nothing to supersede at all.The decision
Knowledge is
DERIVED_LIBRARY_TABLESwhen it eventually has a table. The paper is append-only only because its system does not retain the evidence — an agent learns a fact from a conversation and the conversation is gone.CAPTURED_TABLESis exactly that retained evidence, and its defining property is that a re-index must never touch it. Re-derivability is free here, so the expensive side would have bought a property this store does not need at the price of the re-index invariant.Supersession is computed; exclusivity is declared per fact TYPE. Every distinct value is kept with its sources, the type says whether its values can coexist, and current is answered per query and stored nowhere — the storage/query litmus from PR #89 applied to itself, since a stored edge is a storage-level commitment to a ranking.
What ships
src/knowledge/facts.ts— one leaf module, zero imports, no clock read. Ordering across recordings arrives injected as(sessionId) => number | undefined, on theEdgeRecency/RecencyOptionsprecedent, becauset_monorestarts every session andsession.started_atis joined at query time.currentValue()ranks a value by its latest source, not its first, on the compositestarted_at + t_monoso two observations inside one recording still order. It refuses four ways, each an answer rather than an error:coexistingfact has no current value — the answer is the set;bindHabit's precedent;stabilityOf'sundefined-vs-[]rule;Counts only, never a ratio — asserted in a test, not merely stated. A percentage here would be
FrameResult.scoreunder a new name.It deliberately does not decide whether two values are the same value. Fed those eight display payloads it reports eight. That is the seam, not a defect: it makes the case legible as an identity problem instead of silently mis-keying it, which is precisely what a stored edge would have done while looking correct.
No table
No
schema.tsedit, no sixth bucket, no stage, no UI, no MCP tool. This is cycle 0 — the invariant — following thesrc/trace/stability.tspattern exactly, where the omission of storage is the design.Testing
13 tests in
test/knowledge.current.test.ts: the exclusive path (5) and every refusal (7), plus a barrel check.npm run typecheck,npm test(2379 passed / 16 skipped) andnpm run buildall pass — the build matters specifically here, since the app importsdist/and a barrel export that does not survive it stays invisible until a later cycle needs it.Docs
persistence.md's "The decision left open, on purpose" becomes "The decision, made 2026-09-04", carrying the measurement rather than the conclusion alone. §6.2 is marked settled, andCLAUDE.mdgains the rule.Two stale claims the settlement created were also corrected:
CLAUDE.md's stability bullet still ended "that call is left open deliberately," andpersistence.md's section intro namedstability.tsas the only thing existing instead of a store. Both are paraphrases, so the planned grep would not have caught either.Also re-verifies the preprint the whole line of work rests on: it was cited here from v1, arXiv now serves a v2 revised 2026-06-12, and every load-bearing figure is unchanged — including the keyword-router reversal (Δ = −0.125), which is the argument for deriving a tier rather than storing one, and the core threshold of three independent sessions, which is
CORE_SESSIONS.What would reopen this
A fact type whose evidence is genuinely not retained; a rebuild that cannot reproduce a fact in session order; or a measured need for a stored edge.
🤖 Generated with Claude Code
https://claude.ai/code/session_01KNMAPpjspVHJU1kiuRpcba