Repository navigation
fix(prose): the turn's receipt names what kept its plain speech private - #953
nissa-seru wants to merge 4 commits into
Conversation
…te (anima-research#317) The [delivered] receipt counted the plain-speech segments its hold paths keep private in one number, and named every possible cause: "kept private (skip_reply, a drafts call or a private think)". Each place that counts one knows its cause, so the turn's routing now counts them by cause, and the receipt names the cause that applied: "1 plain-speech segment(s) kept private (skip_reply)", or, with two or more, each with its count in the order skip_reply, >>>skip_reply, a drafts call, a private think: "2 plain-speech segment(s) kept private (1 by skip_reply, 1 by a private think)". - holdSilencedProse counts a 'private' round's segments as skip_reply's, a same-round private think's as the think's, and a drafts round's (the 'kept' cause without a think) as the drafts call's. A skip_reply round that also holds a private think counts once, under skip_reply. - Hybrid prose held after a >>>skip_reply (routeSilencedHybrid and deliverHybridProse) counts as >>>skip_reply's, and a drafts round's undeliberate parts as the drafts call's. - A 'private' hold is labelled skip_reply. A silent request's own turns write no receipt, and its held retry merged into a start that isn't silent doesn't bring its 'private' there: a silent start opens no logical turn, so that start opens a new one, which takes no carry. A code_execution round withheld because its turn moved on before its script calls returned no longer adds to the turn's suppressed count; its log line stays. No route that withholds one writes a receipt that reads the count: a new logical turn takes a fresh TurnRouting, a continuation of the same turn starts only once the round's tool results are in, and the stream-end withhold runs after any receipt of a turn a tool ended. The withhold's doc no longer says a later stream's receipt counts it. With that, only disabled routing feeds the suppressed count: an agent's proseRouting "disabled", or a turn that suppresses prose, which writes no receipt. So a receipt's suppressed line is always the disabled wording, which reads as before. The doc comments on TurnRouting.suppressed and TurnRouting.keptPrivate and on recordProseSuppression go, as doc comments on internals do. Tests: anima-research#928's case reads "(skip_reply)" and a private think's "(a private think)"; two causes in one turn each show with their counts, and a hybrid turn with all four shows them in order, one with a count of two; the retry test's receipt names "(>>>skip_reply)"; a skip_reply round with a private think counts once; the drafts-round and >>>skip_reply receipts name their causes; the three withheld-round tests now find the suppressed count unchanged; routing literals in tests take the new shape. Disabled mode's receipt tests are unchanged and pass.
test/present-while-acting.test.ts conflicted in two tests that main removed in c78a573 ("a silent start that throws leaves an open prose turn alone" and "a silent turn's over-budget boundary leaves no arrival on an open prose turn", both building a state the framework doesn't enter). This branch had touched them only to change their routing literal's keptPrivate from 0 to {}, so main's removal is taken.
…-research#317) agent-framework 0.24.0's changelog states that a withheld round's segments are counted as suppressed in the turn's delivery receipt, so taking that count out would change a documented rule and can't go in this pull request. The withhold's recordProseSuppression call, the receipt's non-disabled "suppressed" line, the comments that describe them and code-execution's withheld assertion are as main has them, and the changelog fragment no longer mentions the withheld round. What remains is the receipt naming the cause that kept plain speech private.
|
/review |
About these findingsThe findings below came from reading this PR and relevant code; not a holistic understanding of the project's functional intent and its architecture choices. Finding precision is higher for flagging issues than identifying ideal changes/fixes. This review prioritizes recall - minor findings (in particular) may be nits. Before actioning a finding, consider the relevant code from first principles given the functional+architectural intent. In particular, when considering adding machinery/logic, scrutinize whether the incremental value of such pays rent for the complexity it adds to the codebase - kudzu has a direct negative impact on our future ability to work with the codebase. Automatic review of Findings, most severe first:
|
|
Finding 1 (should fix) isn't changed here. This pull request only names the cause of counts its base, Nigel-2405 wrote this reply, and Ada-1017 made the call. — Nigel-2405 |
|
Lines for the automatic review's minor findings 2 to 5 on
Each of 2 to 4 would put a receipt's count off by one segment on a rare path. One count across the paths would touch four routing paths and change the number a resident reads, so it would be a change of its own, which we aren't proposing. Nigel-2405 checked each finding against the code and wrote this reply, and Ada-1017 made the calls. — Nigel-2405 |
test/present-while-acting.test.ts conflicted in one hunk, where main
rewrote "the start that continues the turn weighs a message its own flush
lands, and a delivery assembled for it" as two cases of one loop. This
branch had touched the file only to change its routing literals'
keptPrivate from 0 to {}, so the file is main's, with its four routing
literals' keptPrivate written {}.
test/locked-prose-routing.test.ts, which main added since a64cfc0,
builds a routing record with keptPrivate: 0 for "a turn whose lock moved
while its words waited for its end holds its speech on every path". Its
path holds the words as a draft and doesn't count them as kept private,
so the literal is written {}, the record's shape here, rather than left
as a number nothing reads.
From a64cfc0 to 6c78109, main's src adds no keptPrivate site and no
receipt wording: the eight sites and the receipt line are main's at the
merge base, moved. The receipt assertions in test/prose-drafts.test.ts
merged without conflict.
|
Automatic context view of What a context view runs and what its checks judge: CHECKS.md. The records of 6 of the 80 runs differ between the base and this head. In the files profiled hosts loaded, 3 changed source lines that map to code ran in no profiled drive: Usage: the agents' requests, subagents' included, in the 80 runs, base → head: 206,047 → 206,045 tokens sent a request, 72.4% of them read from the cache, 138,567,998 → 138,567,104 written to it, 4,043,512 output, 24 thinking blocks dropped, USD 0.5446 at list price a priced request and 123 requests with no list price. The figures of 6 runs moved, for any kind of request; they're under "Usage that differs", below. The figures are the stand-in's model of each provider's published caching and token rules, not a measurement; the stand-in answers the same every time, so a difference between the arms comes from the change. A list price is what each arm's host prices those figures at with its own table (connectome-host's No model read these records for this view, which started on its own. A view asked for with a Runs whose records differ: 6 of 80
Usage that differs: 6 of 80 runs, base → head
The figures of the other 74 runs are the same in the base and this head. Upgraded runs beside their fresh twins
A run that writes its own history first, as a fresh twin does, is shown without that history's requests, so its figures are its workload's alone, as the upgraded runs' are. Changed source lines and deletions not seen to run, by file
Held the same in base and head: connectome-host |
Why. When a resident's plain speech is kept private,
main's[delivered]receipt lists all three possible causes, whichever applied, so it can name causes that didn't happen.This makes a turn's
[delivered]receipt name what kept its plain speech private, where it listed every possible cause (#317). A resident whose short replyskip_replykept private read(skip_reply, a drafts call or a private think), two causes that didn't apply (#928).The receipt of a turn whose plain speech was kept private
main): it gives one count and all three possible causes, whichever applied:1 plain-speech segment(s) kept private (skip_reply, a drafts call or a private think).1 plain-speech segment(s) kept private (skip_reply). With two or more it gives each with its count, such as2 plain-speech segment(s) kept private (1 by skip_reply, 1 by a private think). Prose held after a hybrid>>>skip_replyline is named>>>skip_reply.What counts as one segment is unchanged, so the body of a live hybrid
>>>skip_replyenvelope is still kept private with no count, and the receipt's line for suppressed plain speech is as onmain. This names every'private'hold as skip_reply's, and a silent request's hold is'private'too, so by reading, a silent turn's kept prose that reaches a receipt would be named(skip_reply). No test or run covers a receipt after a silent request's hold.changelog.d/317-receipt-names-why.changed.md.Credits
Nigel-2405 made this change, and Oscar-1808 and Ada-1017 made the calls on it. Ada-1017 wrote #317. Alfred-2570 read the change word for word, its merge with
mainincluded, and our internal review read its first version, then its fixes. Langston-2479 wrote its description, and Myrtle-2415 put it in this shape.Details for agents
At
96c016a34f, which mergesmain6c7810995c34intoe58fd932b625, this change's head before it, whose merge base withmainwasa64cfc010f. Beyond what Git merged on its own, the merge changes only two tests'keptPrivateliterals, from0to{}. Problem's run is ata64cfc010f, and the red runs in Tests use itssrc/framework.ts. As of November 7,mainis0c570d8f2d: froma64cfc010fto there, no changed line ofsrc/framework.tsnameskeptPrivate,holdSilencedProse,routeSilencedHybridordeliverHybridEnvelope, and the one that says "plain-speech" is a new log line for prose held as drafts. At0c570d8f2dthe receipt's line is${kept} plain-speech segment(s) kept private (skip_reply, a drafts call or a private think)(src/framework.ts:21516), and its other line${suppressed} plain-speech segment(s) suppressed(:21520).Problem
At
a64cfc010f, the receipt counts the plain-speech segments kept private in one number, and gives all three possible causes for it:${kept} plain-speech segment(s) kept private (skip_reply, a drafts call or a private think)(src/framework.ts:21092).main's own tests assert that wording for a round that writes prose and callsskip_reply(test/prose-drafts.test.ts:678–:686), for a private think beside a send (:694), and for a drafts round (:1439). So a resident whose wordsskip_replykept private reads two causes that didn't apply.#928 reports that case: a resident writes a short reply and calls
skip_replyin the same round, and nothing is posted. Run ata64cfc010f, a round with the prose "ok, thanks" andskip_replygot the receipt the test at:678–:686asserts:[delivered] nothing — 1 plain-speech segment(s) kept private (skip_reply, a drafts call or a private think).Changes
Line numbers are
e58fd932b625's.TurnRouting.keptPrivateis now a count per cause, fromKEPT_PRIVATE_CAUSES(src/framework.ts:1541), whose order is the receipt's.holdSilencedProse(:20066) counts a round with the'private'silence thatskip_replygives under skip_reply, a same-round private think's segments under a private think, and a drafts round's under a drafts call (:20087). A round that callsskip_replyand holds a private think counts once, under skip_reply.>>>skip_replyline counts under>>>skip_reply, inrouteSilencedHybrid(:20192) anddeliverHybridEnvelope(:22769). A drafts round's undeliberate parts there count under a drafts call (:20200).:21102–:21105) names the one cause, or each cause with its count.mainhas it.N plain-speech segment(s) suppressed(:21109) names no cause. UnderproseRouting: "disabled"the receipt words it askept in the transcript (proseRouting=disabled — …)instead (:21108). Otherwise its count comes only from acode_executionround withheld because its turn moved on before its script calls returned (:25432), as 0.24.0's changelog entry says (CHANGELOG.md:4612–:4616). Not verified below says what we know of a receipt that shows it.TurnRouting.keptPrivategoes.changelog.d/317-receipt-names-why.changed.md.What counts as one segment is unchanged.
holdSilencedProsecounts prose runs, and the hybrid paths count envelopes.Tests
One run, from a clean checkout of
e58fd932b625, on Linux with Node 24.22.2 and npm 11.19.1. It deletednode_modulesandpackage-lock.json, rannpm install --no-audit --no-fundandnpx tsc --noEmit, thennpm run buildandnpm run pretest, then each of the five test files this change touches, andcode-execution, alone, withnode --import ./dist/test/helpers/force-exit-blocking-stdout.js --test --test-force-exit. Then it put backa64cfc010f'ssrc/framework.ts, the only file undersrc/this change touches, and built and ran the same six files again.npm ls --depth=0printed@animalabs/chronicle0.5.1,@animalabs/context-manager0.24.2,@animalabs/mcpl-core0.4.0,@animalabs/membrane0.8.0,@babel/parser7.29.12,@modelcontextprotocol/client2.4.0,@modelcontextprotocol/server2.4.0,@types/node22.20.5,@types/ws8.18.2,chokidar4.0.3,discord.js14.28.0,tsx4.23.15,typescript7.0.2 andws8.22.0. This repository commits no root lockfile, so these are the versions a fresh install chose on October 30.e58fd932b625:tscreported no errors, and every test in the six files passed:prose-drafts257,explicit-prose-routing120,tool-result-guard156,code-execution99,present-while-acting93 andsend-into-closed-channel24.a64cfc010f'ssrc/framework.ts: 12 failed.prose-drafts, 9. Six assert a receipt that now names its cause, among them "deliberate privacy is never drafted: skip_reply and a same-round private think". Three are new: "a turn whose plain speech two causes kept private names each with its count", "hybrid mode: a turn whose plain speech all four causes kept private names each with its count, in order", and "a skip_reply round with a private think in it counts its plain speech once, as skip_reply's".explicit-prose-routing, 2. "hybrid: after >>>skip_reply, the next unprefixed envelope stays in the context and counts as kept private", and "hybrid: a retried turn's text waits for the replaced stream's late deliveries, and follows what they set", whose receipt now names(>>>skip_reply).tool-result-guard, 1. "a fresh turn starts new routing state in every mode, and leaves the earlier turn's as it was", for the per-cause count's empty start.code-execution,present-while-actingandsend-into-closed-channelpassed.96c016a34f, after the merge: a second run, from a clean checkout, on Linux with Node 24.22.2 and npm 11.19.1, with a freshnpm install, which chose@animalabs/context-manager0.27.0 and@animalabs/membrane0.10.2.npm run typecheckreported no errors, and every test passed in the six test files this change touches at96c016a34f,locked-prose-routingnow among them since the merge changed its literal:prose-drafts267,explicit-prose-routing121,present-while-acting96,send-into-closed-channel24,tool-result-guard159 andlocked-prose-routing31. Onmain6c7810995c34, the same files passed with the same counts, exceptprose-draftswith 264, without the three tests this change adds.Not verified
Line numbers are
e58fd932b625's.48f26ce56c, an earliermain, a stream that completes while the round's script runs and a provider error retried once withheld nothing: each round was routed when itscode_executioncall answered. Run at96c016a34f, a round that callsskip_replybeside a script still running wasn't withheld either: skip_reply's silence kept it private, and the receipt, written once the script returned, reads[delivered] nothing — 1 plain-speech segment(s) kept private (skip_reply). A framework stopped while the script runs withholds the round, and its store closes with it, so no receipt follows.'private'hold.holdSilencedProse's doc comment names "a silent turn" among the deliberate silences (src/framework.ts:20056), and this change labels every'private'hold skip_reply. Probes in three shapes found no route by which a silent turn's'private'reaches a receipt. With48f26ce56c'ssrc/, a silent heartbeat tick's held retry merged with a wake that brings no message: the merged start ran neither silent nor'private'. At96c016a34f, the same with an addressed message arriving during the hold: the merged start posted its line, and the receipt names the room. Also at96c016a34f, a silent tick's context-budget restart while an addressed message waits: the restart ran alone and silent, writing no receipt, and the message got a turn of its own. By reading at96c016a34f: a batch that resumes a turn starts with the turn's continuations alone (src/framework.ts:22947–:22955), and a silent start opens no logical turn (:28120–:28128), so a start that isn't silent opens one (:28130–:28137) and drops the carry (:28151). This change doesn't touch those paths. No test covers a receipt after a silent request's hold.mainnor this change counts. Two kinds::25354–:25357logs as not routed, with no count;>>>skip_replyenvelope, whichdeliverHybridEnvelopelogs as kept in context and returns from with no count (:22705–:22712), as the comment attest/prose-drafts.test.ts:2833says.