Skip to content

feat(preview): previewContext can leave given pins out of its select - #356

Merged
nissa-seru merged 1 commit into
anima-research:mainfrom
nissa-seru:paloma/preview-exclude-pins
Oct 28, 2026
Merged

nissa-seru merged 1 commit into
anima-research:mainfrom
nissa-seru:paloma/preview-exclude-pins

Conversation

@nissa-seru

Copy link
Copy Markdown
Contributor

previewContext now takes opts.excludePins, a list of pin and document-mark ids, and its dry select reads those pins as absent. A caller can ask whether a compile would fit were a pin gone, without unpinning it. Nothing is unpinned or written, and no compile takes the option. This change also has a dry compile put back the autobiographical strategy's owned-message memo, and adds a test that three previews and then a compile say the kv-unified label-ceiling warning once, as #271 describes the warning.

Problem

To learn whether a compile would fit without a pin, a caller had to unpin it and pin it again. In the autobiographical strategy at f966779f, unpin and pinRange each call persistPins (src/strategies/autobiographical.ts:3652, :3596), and pinRange gives the pin a new id from pinIdCounter (:3586). The caller this is for is agent-framework's restart preview (anima-research/agent-framework#842), once agent-framework holds a turn's opening rows with a raw pin (anima-research/agent-framework#867): its preview should decline a restart only if the restart wouldn't fit even without that pin.

With f966779f's src/context-manager.ts and src/strategies/autobiographical.ts under this change's tests, the preview that leaves the pin and the mark out still doesn't fit: the option is ignored there. The dry compile there also leaves the owned-message memo as its dry select set it.

Changes

Line numbers from here on are at 01c5a4042f.

  • src/context-manager.ts: previewContext(budget, overrides, opts) takes opts.excludePins, ids as pinRange and markDocument return them, and its doc comment says what the option does (:1960 to :1964, :1969). The strategy's own previewContext takes the same field (src/strategies/autobiographical.ts:2641).
  • src/strategies/autobiographical.ts:
    • When it's given ids, previewContext sets previewPins to a copy of the strategy's pin list without them before its dry select (:2675 to :2677), and clears it in its finally (:2736). The strategy's own list isn't touched.
    • pinnedPositions, pinLevelBounds and rawPinsDiagnostics read selectPins, which is that copy while a preview has one and the strategy's list otherwise (:2402, :3882 to :3885, :3954 to :3957, :13092 to :13108).
    • The change adds no log call.
    • A dry compile now saves and puts back _ownedIdsCache, the memo of the messages the strategy's chunks and summaries own (:20207, :20237). The memo's key is built from counts alone (:19093), and ownedMessageIds returns the memo whenever the key matches (:19094).
  • changelog.d/preview-exclude-pins.added.md and changelog.d/dry-select-owned-ids.fixed.md.

Tests

In test/dry-run-preview.test.ts, three new tests of the pins previewContext is told to leave out, on a store of eight inputs with a document mark on the first, a pin over the rest, a summary of all eight and ten newer rows:

  • With the inputs held raw, a preview at the test's budget doesn't fit, and with the pin and the mark left out, it fits. Leaving out the pin alone gives fewer tokens than holding it, and leaving out the mark too gives fewer again. An id no pin has, alone or beside the pin's, and the pin's name passed as an id change nothing. Afterwards the pin list, the messages and the count of pin writes are as they were, and the live compile still refuses as over budget. Once both are really unpinned, a plain preview gives the same tokens as the preview that left them out.
  • After a preview that left the pin out throws, a plain preview gives what it gave before.
  • A pin written while a preview runs is in the strategy's list afterwards.

Two tests under "a plain dry compile commits nothing":

  • "puts back whatever a dry select wrote, on a throw too" now sets and checks _ownedIdsCache.
  • A new test, of behaviour the code already has: with a stand-in kv-unified solve that reports the label ceiling passed, three previews and then the live compile log the warning once. fix(kv-unified): a label-ceiling hit no longer fails the compile (#252) #271's description says "The warning is said once per strategy".

On Linux with Node 24.22.1, membrane 0.8.0 and chronicle 0.4.0, in a clean checkout of 01c5a4042f, installed with npm ci:

  • Typecheck: tsc --noEmit exits 0.
  • The whole suite: npm test runs 3,420 tests, and all pass. At f966779f, run the same way, it runs 3,416, and all pass.
  • test/dry-run-preview.test.ts: 26 of 26 pass.
  • Red-first: with f966779f's two source files in place, tsc reports two errors, both at the test file's excludePins options, and still writes the build. 2 of the file's 26 tests fail: the first test above, at "with the pin and the mark left out the inputs read as their summary, and it fits", and "puts back whatever a dry select wrote", at _ownedIdsCache.

Not verified

  • The hierarchical path. With adaptiveResolution off, ContextManager.previewContext gives null or an over-budget report (test/dry-run-preview.test.ts:681, :704). None of this change's tests leaves a pin out there, so none shows that answer change with a pin left out.
  • A left-out pin with a level bound. None of this change's tests leaves out a pin with level or maxLevel. In resolvedPinBounds, :3907 reads this.readsPinBounds() ? this.pinLevelBounds(messages) : undefined, and readsPinBounds returns true only with adaptiveResolution set and a foldingStrategy of kv-stable or kv-unified (:3923 to :3925).
  • A real store. No run compiled against a store outside the tests. The new tests' store holds eight inputs and ten newer rows.
  • agent-framework. No run called the option from agent-framework.
  • Platforms. Every run was on Linux with Node 24.22.1.

  • Changelog fragments added: changelog.d/preview-exclude-pins.added.md and changelog.d/dry-select-owned-ids.fixed.md.

Paloma-2644 wrote the change, and Oscar-1005 set the option's shape. Esther-2584 read and ran four versions of it. The last of them differs from this commit only in a clause about what a preview logs, in src/context-manager.ts's doc comment and in changelog.d/preview-exclude-pins.added.md, and in its commit message. Two internal reviews read earlier versions. Daniel-2612 wrote this description.

A caller that keeps a range raw with a pin can't ask whether a compile
would fit were the pin gone without unpinning it, which writes the pin
list twice and changes what every other reader sees meanwhile.
previewContext's opts.excludePins takes pin and document-mark ids, as
pinRange and markDocument return them, and the dry select reads those
pins as absent: while it runs, the select's pin readers (the chunker's
compressible set, the selectors' raw and level bounds, the over-budget
diagnostics) read a copy of the strategy's list, taken as the preview
starts, without them. The list itself is untouched, so a pin written
meanwhile is kept, though that select doesn't read it. Nothing is
unpinned or written, and the change adds no log call; an id no pin has
is ignored, and the option is on previewContext alone, so no compile
can be told to ignore a pin.

A dry select now also puts back the strategy's owned-message memo, whose
key, counts alone, can match chunks the dry rebuild cut differently;
the test of what a dry select puts back checks it. A test also pins
that the kv-unified label-ceiling warning is said once in a strategy's
life, with previews' solves among those that spend it, as anima-research#271 decided:
three previews past the ceiling and the live compile after them say it
once.

The tests hold eight summarised inputs, a document mark on the first
and a pin over the rest, and newer rows. Leaving out the pin folds what
it held while the mark keeps its input, and leaving out both folds more;
an unknown id, beside a held one or alone, and a pin's name change
nothing; the excluded preview equals the preview after really
unpinning; the pin list, the messages and the pin writes are unchanged
after; and the live compile still refuses. A preview that throws leaves
every pin read again, and a pin written while a preview runs is kept.
@nissa-seru
nissa-seru merged commit 44da17a into anima-research:main Oct 28, 2026
9 checks passed
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