Skip to content

feat(observe): read how often a fact was restated across superseding keys - #165

Merged
0thernet merged 2 commits into
mainfrom
feat/supersession-depth
Sep 22, 2026
Merged

0thernet merged 2 commits into
mainfrom
feat/supersession-depth

Conversation

@0thernet

@0thernet 0thernet commented Sep 22, 2026 •

Copy link
Copy Markdown
Member

Completes the pair. #164 shipped recordRevisions, which reports how often one record key was rewritten, and documented a limitation it could not fix: this profile does not correct a fact by rewriting its key. applySupersessionPolicyV1 writes a new observation whose supersedes names the older one.

The consequence is that the signal shipped in #164 is blind to exactly the churn this profile produces. Every key in a correction chain is typically written once, so each reports revisions: 0, while the fact behind them was restated repeatedly. The two numbers measure different things and neither subsumes the other.

What this adds

ohObservationSupersessionV1(store, key) follows supersedes toward the oldest record and reports depth, origin, resolved, loop, missing, truncated.

It asks a store for get and nothing else, through a narrow OhObservationReadStoreV1 port, so a snapshot or change-feed reader answers it without local SQL and the read cannot commit.

The honesty condition, again

resolved is true only when the walk ends at a record superseding nothing, and only then are depth and origin exact. A cycle, the 8192-link bound, or an absent record reports resolved: false, names the missing key, and leaves depth a floor with origin as merely the oldest readable key.

This is deliberate and it is the lesson from #164's review: the defect found there was a reader whose partial answer was shaped identically to a complete one, so no consumer could discount it. A supersession walk has three ways to stop early, so the same trap was available here and is closed by construction rather than by documentation.

Loop detection, the chain bound, and treating a damaged chain as unusable rather than repairable follow reachesExcluded, which already walks these links for the supersession policy. No second convention.

No meaning attached

A fact restated many times may be contested, progressively refined, or simply discussed often, and this does not distinguish them. Whether a depth warrants review stays the application's decision, matching the split #164 established and AGENTS.md requires.

Worth stating because the temptation is real: a naive reading would rank high-depth facts as low-confidence. The measurements behind this work argue against exactly that move — a model's coarse self-reported confidence did not predict its own errors (1.21x and 1.03x lift, intervals spanning 1.0), and a third party's published high label failed to predict even its own revision (8.8% vs 9.7%, n=14,560). Churn is worth reporting because it needs no external answer key; turning it into a trust score is the step that has repeatedly failed to measure.

Verification

Tested against a chain built by the production ingestion path, not by hand: the existing supersession test's three linked observations read depth: 2 with the oldest key as origin, and each key's own revision count is zero — which is the whole point. Damaged chains are covered by a narrow fake store exercising an absent link, an absent start key, a cycle, the chain bound, and three invalid keys.

bun run typecheck clean; bun test ./src/observe.test.ts ./src/store.test.ts ./src/sqlite/store.test.ts 72 pass, 0 fail; check:cost-surfaces 21 surfaces; check:effect passes; site/tests/source.test.ts 15 pass; bun run build byte-reproducible across two runs with no untracked files; spec/v1/observation.md and its site/public mirror confirmed identical by diff.

Additive and read-only. No new record kind, identifier grammar, migration, cost surface, or change to the observation value or the OhStoreV1 port.

🤖 Generated with Claude Code


Second pass: independent review

One real defect and four wrong claims, fixed in the pushed head.

The walk gated only on the observation value, never on record.kind. A record stored at an edition:obs- key under another kind was counted as a chain link and the result stamped resolved: true — the one field documented as exact. Reproduced against a real store: a kind: "entity" record mid-chain read depth: 2, resolved: true while every other reader in the file rejected it. It now goes through parseOhObservationRecordV1, as applySupersessionPolicyV1 already does, and reports the record as damage.

That fix immediately rejected two of my own test fakes, which carried empty dependencies and were not well-formed observation records. They now list the receipt, the cited source and the superseded key, sorted.

Four claims were wrong and are corrected, not softened:

claim correction
"a snapshot or libSQL reader satisfies it" OhStoreV1 is async with no get; a snapshot is a record array. Only OhSqliteStore and the SDK store qualify, so the port widens nothing yet.
"each key in a chain reads revisions: 0" True only on the observeOhV1({ supersession: true }) path. applySupersessionPolicyV1 re-puts a record when its link changes, so keys linked that way read 1. The signals overlap.
resolved: true implies a complete chain It does not. A link comes from a facet-and-speaker match over a bounded lookup, so a fact whose facet drifted starts a fresh chain and resolves at a lower depth.
"the 8192-link chain bound" 8192 records are read, so the longest resolvable chain carries 8191 links.

Because of the third correction, this no longer claims to count restatements. depth describes the link graph the store holds, and the doc and spec now say that directly. Also documented: depth counts one link past the last readable record when unresolved, missing === key is the case where nothing was read, and a cycle closing beyond the bound reports truncated rather than loop.

The reviewer confirmed by dumping every table before and after eight calls including throwing ones that the read writes nothing, found no bypass of key validation across 48 generated keys plus 13 hostile ones, and confirmed the spec mirror byte-identical.

Rebased onto current main; dist/ rebuilt after the rebase. typecheck clean, observe/store/sqlite-store 73 pass, check:cost-surfaces 21 surfaces, check:effect passes, site/tests/source.test.ts 15 pass.

@vercel

vercel Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
oh-beam-reader-runtime Ready Ready Preview Sep 22, 2026 4:54am UTC
oh-computer Ready Ready Preview Sep 22, 2026 4:54am UTC
oh-vercel-wasm Ready Ready Preview Sep 22, 2026 4:54am UTC

Request Review

0thernet and others added 2 commits September 22, 2026 00:52
…keys

`recordRevisions` reports how often one record key was rewritten. This profile
does not correct a fact by rewriting its key: it writes a new observation whose
`supersedes` names the older one. So every key in a correction chain is typically
written once and reports a revision count of zero, while the fact behind them was
restated repeatedly. The revision count was exact and cheap and simply could not
see this, which the store specification already said and this closes.

`ohObservationSupersessionV1` follows `supersedes` from one observation toward the
oldest record. It asks a store only for `get`, so a snapshot or change-feed reader
answers it without local SQL, and it never writes.

The honesty condition is the same one the revision read needed. `resolved` is true
only when the walk ends at a record superseding nothing, and only then are `depth`
and `origin` exact. A cycle, the 8192-link bound, or an absent record reports
`resolved: false`, names the missing key, and leaves `depth` a floor and `origin`
the oldest key that could be read. A partial answer must not be shaped like a
complete one; that was the defect independent review found in the feed reader, and
it is the same trap here.

Loop detection, the chain bound, and the treatment of a damaged chain as
unusable rather than repairable follow `reachesExcluded`, which already walks
these links for the supersession policy.

No meaning is attached. A fact restated many times may be contested,
progressively refined, or simply discussed often, and the profile does not
distinguish those; whether a depth warrants review stays the application's
decision. Additive and read-only: no new record kind, identifier grammar,
migration, cost surface, or change to the observation value.

Verified against a chain built by the production ingestion path rather than by
hand — the existing supersession test's three linked observations read depth 2
with the oldest key as origin, while each key's own revision count is zero.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ur claims

Independent review found the walk gating only on the observation *value*, so a
record stored at an `edition:obs-` key under another kind was counted as a link
and the result was stamped `resolved: true` — the one value documented as exact.
Confirmed against a real store: a `kind: "entity"` record in the middle of a
three-link chain read `depth: 2, resolved: true` while every other reader in the
file rejected it. The walk now goes through `parseOhObservationRecordV1`, as
`applySupersessionPolicyV1` already does, and reports the record as damage.

Fixing that exposed two of my own test fakes as ill-formed: they carried empty
dependencies, which is not a well-formed observation record. They now list the
receipt, the cited source and the superseded key, sorted, so the damaged-chain
cases exercise real records instead of ones the stricter gate would reject
anyway. A case for the foreign-kind record is added.

Four claims were wrong and are corrected rather than softened:

- The read port does not admit "a snapshot or libSQL reader". `OhStoreV1` is
  async and has no `get`; a snapshot is a record array. The type states the
  requirement, and today only `OhSqliteStore` and the SDK store satisfy it.
- The two signals are not independent. `applySupersessionPolicyV1` re-puts a
  record when its link changes, so a key linked by that path reads one revision.
  Only the `observeOhV1({ supersession: true })` path leaves each key at zero.
- `resolved: true` does not mean the chain is complete. A link comes from a
  facet-and-speaker match over a bounded lookup, so a fact whose facet changed
  between sessions starts a fresh chain and resolves at a lower depth. `depth`
  describes the link graph, not the number of restatements, and the naming now
  says so.
- The bound is 8192 records read, so the longest resolvable chain is 8191 links.

Also documented: `depth` counts one link past the last readable record when
unresolved, `missing === key` is the case where nothing was read, and a cycle
closing beyond the bound reports `truncated` rather than `loop`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@0thernet

Copy link
Copy Markdown
Member Author

Independent review found one real defect and four wrong claims of mine. All fixed in 29bf338; CI green on both operating systems.

The defect is worth naming precisely. The walk gated on the observation value and never on record.kind, so a record stored at an edition:obs- key under another kind was followed as a chain link and the result stamped resolved: true — the single field documented as exact. Reproduced against a real store: a kind: "entity" record mid-chain read depth: 2, resolved: true while every other reader in the file rejected it. It now goes through parseOhObservationRecordV1, as applySupersessionPolicyV1 already does.

That fix then rejected two of my own test fakes, which carried empty dependencies and were not well-formed observation records. The stricter gate catching the tests that were supposed to check it is the gate working.

Four claims corrected rather than softened. The read port does not admit a snapshot or libSQL reader — OhStoreV1 is async with no get. The two signals are not independent: applySupersessionPolicyV1 re-puts on relink, so those keys read one revision. resolved: true does not mean the chain is complete, because a link comes from a facet-and-speaker match over a bounded lookup and a drifted facet starts a fresh chain. And the bound is 8192 records read, so 8191 links.

The third correction changed what this feature claims. It no longer reports how often a fact was restated; it reports how far the recorded link chain reaches. Those differ whenever the linker's heuristic missed, and the earlier framing would have had a consumer read a heuristic's output as a count of events.

That is the same failure this whole line of work keeps finding, now three times over: a number that is wrong in a way nothing downstream can detect. The partial-feed read in #164, the unjudged lexical proxy scored against a release threshold, and this. Each was individually plausible and each produced an answer shaped exactly like a correct one.

Merging with the limitation on the record. resolved: true reports that the walk reached a record superseding nothing, not that the store's link graph is complete — a chain degraded by candidatesTruncated still resolves. Surfacing that degradation in the result is separate work and is not pretended to be done here.

@0thernet
0thernet merged commit af2402f into main Sep 22, 2026
17 checks passed
@0thernet
0thernet deleted the feat/supersession-depth branch September 22, 2026 05:00
0thernet added a commit that referenced this pull request Sep 22, 2026
…urated

#165 shipped with a gap stated in its own merge note: `resolved: true` reports
that the walk reached a record superseding nothing, not that the store's link
graph is complete. A link is chosen from a keyword lookup bounded at 100
candidates, and when that lookup saturates the true predecessor may never have
been examined, so the new record starts a fresh chain. The read then returned
`resolved: true` with a short depth and no signal at all.

The store already records this. Every observation's receipt lists the session's
observations whose candidate lookup hit the bound, so the read now checks the
receipt of each record it walks and reports `candidatesTruncated`. Receipts are
memoised, so a chain drawn from one session reads its receipt once.

What the flag does and does not promise is stated in both the doc comment and the
specification. True means a record on the walk had its lookup saturate, so the
chain may be short by an unknown amount and `depth` is not a floor on the links
that exist. False means no record on the walk was affected. It is not a promise
that the link graph is right: a facet that changed between sessions starts a
fresh chain and leaves no trace for any reader to find, which is a limit of the
linker rather than something this read can recover.

Tested with a receipt that names one of two linked observations, so the flag is
true reading from the named record and false reading from the other. Adding the
field without a case that drives it true would repeat the defect review found in
hraness/sponge#303, where new behaviour passed every gate while nothing pinned it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@0thernet 0thernet mentioned this pull request Sep 22, 2026

This branch was successfully deployed

3 active deployments
Preview – oh-vercel-wasm — 29bf3385 Deployed Sep 22, 2026 by vercel[bot]
Preview – oh-beam-reader-runtime — 29bf3385 Deployed Sep 22, 2026 by vercel[bot]
Preview – oh-computer — 29bf3385 Deployed Sep 22, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant