Skip to content

fix(kv-unified): suggest producing missing levels only where that clears the conflicts (#256) - #259

Merged
nissa-seru merged 1 commit into
anima-research:mainfrom
nissa-seru:fix/256-conflict-suggestion
Oct 19, 2026
Merged

nissa-seru merged 1 commit into
anima-research:mainfrom
nissa-seru:fix/256-conflict-suggestion

Conversation

@nissa-seru

Copy link
Copy Markdown
Contributor

Fixes #256.

kv-unified's infeasibility certificate now suggests producing the missing levels only where that would clear every conflict it lists. Otherwise its suggestion reads "Release or reconcile the listed constraints." The strategy puts the suggestion in the error a refused compile throws, so it's the line an operator reads.

Problem

For a constraint-conflict, CanonicalSummaryForest's certificate always suggested "Release or reconcile the listed constraints, or produce the missing levels." (src/adaptive/kv-unified.ts:1952 on main). I ran seven single-leaf conflicts through minimumTokens on main (cc599f19, Linux, Node 24.22.0). All seven got that suggestion. With L2 produced over the same chunks, two of them become feasible and the other five still conflict:

Conflict missingLevels Producing the level clears it
a lock at raw against a pin at L1, which exists [] no
a pin at level 1.5 [] no
a raw pin against a pin at L1 [] no
an exact constraint at L2, which is missing [2] yes
a floor at L2, above every level supplied [2] yes
a lock at raw against a pin at L2, which is missing [2] no: the lock still wants raw
a raw pin against an exact constraint at a missing L2 [2] no: the pin still wants raw

So choosing the wording from missingLevels.length, as #256 suggests, would fix the first three rows but not the last two.

Changes

  • src/adaptive/kv-unified.ts: the certificate suggests producing the missing levels only where every conflicting leaf, with the levels it requests added to its available levels, has a level all its constraints admit. Otherwise it suggests releasing or reconciling the listed constraints. constraintAdmits reads a constraint as buildLeaf does, so a constraint whose own level isn't a non-negative integer admits nothing. The over-budget suggestion is unchanged.
  • changelog.d/256-conflict-suggestion.fixed.md.

The check is one predicate over each conflict's constraints, which the certificate already lists. A count of missing levels can't tell the last two rows from the two above them, since all four have one. constraintAdmits repeats the rule buildLeaf applies to each constraint, so the two have to change together. Sharing one helper would mean rewriting buildLeaf's loop, which #213 also changes (see "Out of scope"). Each leaf counts only the levels it requests itself. Where leaves share a chain whose levels skip one, a level one leaf requests could also clear another leaf's conflict, and the suggestion would then leave out producing. By our reading, where a chain's levels run from 0 without a gap, as the test harness builds them, no level a leaf doesn't request can clear it.

Tests

  • New test, kv-unified suggests producing the missing levels only where that clears every conflict, in test/adaptive/kv-unified.test.ts. It runs the seven conflicts above and nine more, and pins each one's missingLevels and suggestion. Four of the nine pair two bounds on one leaf: a cap below an exact constraint at a missing level, a cap at it, a floor above a cap, and a floor below an exact constraint, with two levels missing. The other five are two-leaf certificates. In four, a missing level that would clear one leaf's conflict sits beside a conflict it wouldn't clear, with either leaf first. In the fifth, producing clears both.
  • It fails on main's certificate. It also fails with each of these changes to the check, each at the case that names it: the level validation removed, or only its negative-level part; every across the conflicts replaced by some, checking only the last conflict, or allowing the produce wording only for one conflict; a cap admitting every level, or only levels below it; a floor admitting every level, or only its own; a raw constraint admitting every level; and the wording chosen from missingLevels.length alone.
  • The over-budget test now also pins its suggestion, which this change moves to another branch of the same expression. It passes on main too.
  • npm test at c1cb6e16 (Linux, Node 24.22.0): 2,205 pass, 0 fail, against 2,204 in main's CI at cc599f19; the one more is the new test. Typecheck and build pass.
  • With fix(kv-unified): hold a level pin at the deepest level its chain supplies until the pinned level exists (#193) #213's head (26a97abe), kv-unified.ts merges cleanly and test/adaptive/kv-unified.test.ts conflicts only where both add tests after the same one. With both sets kept, typecheck and build pass, and the five test files the two PRs change all pass.

Not verified

  • The check's missingLevels.length > 0 guard matters only for a certificate with no conflicting leaf. minimumTokens raises a constraint-conflict with an empty list when a root's minimum cost is infinite (kv-unified.ts:1724), and I found no forest that reaches that path, so no test covers the guard. Without it, a certificate with no conflicts would suggest producing levels that aren't missing.
  • I ran Linux and Node 24.22.0 only. CI runs the other Node versions and macOS.

Out of scope

— Esther-2584 (Claude Opus 5.5). Effie-2289 (Claude Opus 5.5) read the change, before and after its review fixes. She checked constraintAdmits against buildLeaf, and proposed the cases for a cap at an exact constraint's missing level and a floor above a cap. An internal review found the gaps the other new cases close, and narrowed the text.

Checked and opened by Fiona-2757 (Claude Opus 5.5) through the shared nissa-seru account.

🤖 Written using Anthropic's Claude Opus 5.5 through the Rookery's tooling.

…ars the conflicts (anima-research#256)

The infeasibility certificate's suggestion for a `constraint-conflict` was
always "Release or reconcile the listed constraints, or produce the
missing levels." The strategy puts it in the error a refused compile
throws, so it's the line an operator reads. It told them to produce
levels where that would clear nothing. On main, a lock against a pin at a
level that exists, a pin at a level that is no integer, and a raw pin
against a pin each give `missingLevels: []` with the clause. Two conflicts
with a missing level get it too, though producing the level wouldn't
clear them: a lock against a pin at a missing level, and a raw pin
against an exact constraint at one.

The certificate now suggests producing the missing levels only where
every conflicting leaf, given the levels it requests, has a level all
its constraints admit, and otherwise says "Release or reconcile the
listed constraints." `constraintAdmits` reads a constraint as
`buildLeaf` does, so a constraint whose own level isn't a non-negative
integer admits nothing. Choosing the
wording from `missingLevels.length` alone, as anima-research#256 suggests, would leave
the lock and raw-pin cases above telling the operator to produce a level.

The new test takes sixteen conflicts, on one leaf or two, and pins each
one's missing levels and suggestion. It fails on main's certificate, and
with each of twelve changes to the check, such as a floor admitting only
its own level, a negative floor admitting every level, or only the last
conflict checked. The over-budget test now pins its suggestion too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Oct 19, 2026

Copy link
Copy Markdown

Required label not found on this PR.

@nissa-seru
nissa-seru merged commit e1d1a72 into anima-research:main Oct 19, 2026
7 checks passed
tel-0s added a commit that referenced this pull request Oct 19, 2026
…mand, the sibling-append fix and store diagnostics

What main brings since 021c0f3: #229, #230, #231 and #228 (latent joint
demand, age bands, subtree memo, merged-view memo), #260 (a sibling's
appends no longer cost a reader the whole slot), #259 and #253 fixes to
kv-unified, #263 (call spreads past the argument limit), #262 (derive
rebuilds a treeified forest in full), #117 (merge around a quarantined
group), #129 (the witnessed identity reminder), #261, #264 and the
namespace-scoped store diagnostics (#239).

Conflicts, all in src/message-store.ts, resolved by composition:

- The constructor keeps this branch's seeded construction (deriveOnto's
  seed: namespace, state id, the parent's blob manager and calibration, the
  parent's materialised array as the child's cache) and takes main's
  diagnostics `log`, named for the seed's namespace when there is one.
- The seven mutation sites take main's bookkeeping — `bumpWriteVersion`
  with the log, `bumpEditVersion` — while every freshness assignment keeps
  this branch's token, `slotRevision` (chronicle's `stateHeadOffset`, the
  log offset of the slot's newest chain record, which any handle's write
  moves; main's `currentWriteVersion` is an in-process counter that only
  writes through a MessageStore on the same JsStore object move).
- #260's appends-only extension (`appendedSince`) counts the appends since
  the cache was built as the counter's movement, and with `slotRevision` as
  the token that difference is a log distance, not a count — the seam
  nissa-seru's note on #238 described. The cache now carries the counter
  beside the token (`writeCount`): the extension runs only when counted
  writes moved since the cache was built and they explain the slot's growth;
  a revision that moved with no counted write is a write through another
  handle or process, which may be anything, and reloads. At a local append
  on a cache that wasn't current, main deferred the write-through to the
  next read; here the cache is extended at the append when the counted
  sibling writes plus this one explain the slot, and dropped otherwise —
  deferring let the next read extend past an uncounted edit (derive.test's
  surgeon through a branch view). The cheap path is kept for siblings on
  one handle, which is #251's case; across views it reloads, as it did.
  test/manager-diagnostics.test.ts accepts the two outcomes' lines beside
  main's "deferred".
- `bumpEditVersion` is kept where main calls it; its reader isn't, since
  this branch's getAll memoises the mapped view per internal record
  (storedViews), which an edit or removal misses by construction — main's
  edit version answers the same question for a cache this branch doesn't
  have. The comment at the helper says so.

Build and test on the composed rig: 2383 tests, 2383 pass, 0 fail.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
nissa-seru pushed a commit to nissa-seru/context-manager that referenced this pull request Oct 21, 2026
…h constraintAdmits

main's anima-research#259 (for anima-research#256) added constraintAdmits, the same rule as the hold's
admitsLevel: whether a leaf constraint admits a level, with a constraint
whose own level isn't a non-negative integer admitting nothing. levelPinHold
now calls constraintAdmits, and admitsLevel is gone. constraintAdmits' doc
adds that it reads the constraint alone, whatever the leaf's chain supplies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nissa-seru pushed a commit to nissa-seru/context-manager that referenced this pull request Oct 25, 2026
…h constraintAdmits

main's anima-research#259 (for anima-research#256) added constraintAdmits, the same rule as the hold's
admitsLevel: whether a leaf constraint admits a level, with a constraint
whose own level isn't a non-negative integer admitting nothing. levelPinHold
now calls constraintAdmits, and admitsLevel is gone. constraintAdmits' doc
adds that it reads the constraint alone, whatever the leaf's chain supplies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

kv-unified conflict suggestion says "produce the missing levels" when none are missing

1 participant