Repository navigation
drafts: name each held draft by its length wherever a resident is told of it - #993
nissa-seru wants to merge 4 commits into
Conversation
… is told of it
A resident told "a plain-speech segment held as draft d-x7..." couldn't tell
which of its messages that was without calling drafts to list them: the
held-draft notice, the turn-end receipt and the next turn's catch-up name
drafts by id alone, and quote none of their words, since an agent observing
the writer's slot is shown them too. Each now gives each draft's length
beside its id, counted and worded as drafts' read and listing give it
("N chars"), so the two can be matched by eye, a character outside the
Basic Multilingual Plane included.
- The held-draft notice and the turn-end receipt name a turn's drafts in
the order the resident wrote them, the order the turn holds them in:
"held as drafts d-a (11 chars) and d-b (28 chars), in the order you wrote
them".
- The catch-up of open drafts gives each one's length before its state,
"d-a (14 chars; held (not sent); ...)", with no order, since its
drafts can come from earlier turns.
The tests that pinned the old texts word for word now pin the lengths, and
a new one pins the notice's count against drafts read's for a draft with
an emoji in it.
|
Automatic review of 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. Consider explicitly mentioning which findings you accept/decline (by code-formatted numerical id) and why; this enables future rounds of automated review to take your notes into account. Findings, most severe first:
|
…their order
The receipt's list is the turn's drafts as routing collected them, and a
hybrid round adds an earlier turn's bounced draft that a bare {{unsent}}
names after the drafts the round held, so "in the order you wrote them"
could put a draft written this turn before one written turns earlier. The
receipt now names each draft by its length alone. The held-draft notice
keeps the clause for two or more: it names one hold's drafts, which are its
segments in the order given, so the order holds there by construction.
|
This answers the automatic review of
CreditsHamish-2699 made the change and wrote this reply. Ada-1017 decided the finding, and Paloma-2644 read the change. Details for agentsHead
Runs: Linux, Node 24.22.2, npm 11.19.1,
|
|
/review |
|
Automatic review of 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. Consider explicitly mentioning which findings you accept/decline (by code-formatted numerical id) and why; this enables future rounds of automated review to take your notes into account. Findings, most severe first:
|
…re held as a draft
The held-draft notice, the receipt's held line and the catch-up named each
draft by its length, but three other places that tell a resident about a
draft it can still resend named it by id alone: the bounce notice, whose
"Your text (N chars)" counts the words as written and so differs from the
draft once {{unsent}} is written out; the notice for a message held after a
refused {{unsent}}; and the receipt's line for a draft that resends only with
confirmDuplicate. Each now gives the draft's length as drafts read counts it.
Lines about drafts the resident can no longer resend (let go, dismissed,
delivered), and replies about a draft it named itself, stay as they were.
The no-words test held words the notice's own text partly shares, so a notice
that leaked one of them could pass; it now holds words no notice uses and
asserts none appears. The hybrid receipt test ruled out one order phrase; it
now pins the listed drafts whole, so any clause after the list fails. The
changelog fragment states the rule and names each place, and no longer says a
length tells two drafts apart.
|
This answers the automatic review of
CreditsHamish-2699 made the changes and wrote this reply. Ada-1017 decided each finding, and Paloma-2644 read the changes. Details for agentsHead
Runs: Linux, Node 24.22.2, npm 11.19.1,
|
|
/review |
|
Automatic review of 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. Consider explicitly mentioning which findings you accept/decline (by code-formatted numerical id) and why; this enables future rounds of automated review to take your notes into account. Findings, most severe first:
|
…gelog names the catch-up's cap
A bare {{unsent}} whose send fails re-bounces the draft it sent, and the
notice's words as written are the token itself, so a notice that counted
them would say 10 where the draft holds 14. The re-bounce test now asserts
the notice names the draft with its own length. The changelog fragment says
the catch-up gives the length of the first eight drafts it names, and names
any more by id.
|
This answers the automatic review of
CreditsHamish-2699 made the changes and wrote this reply. Ada-1017 decided each finding, and Paloma-2644 read the changes. Details for agentsHead
Runs: Linux, Node 24.22.2, npm 11.19.1,
|
|
Closing this without merging. It named each held draft by its length, and by its order where that was true, in the notices, receipts and catch-up that name a resident's held drafts. To act on held drafts, a resident needs to know which draft holds which words. A length doesn't tell it that. It's a proxy, and it invites a choice by size. To resend all of them or dismiss all of them, the held-draft notice already gives both calls with their ids, and it lists one hold's ids in the order they were written. To act on one draft and not another, one So held drafts stay named by id, as on Hamish-2699 built this change and wrote this comment, and nissa decided to close it. — Hamish-2699 |
Why. A resident told that its plain speech was "held as draft d-x7…" can't tell which of its messages that is without listing its drafts, because the notice names drafts by id alone and quotes none of their words.
This gives each draft its length, beside its id, wherever a resident is told its words are held as a draft or is given a summary of drafts it can still resend (the catch-up's first eight), counted and worded as
draftsreads and lists it.The held-draft notice
main): "… not sent — 2 plain-speech segments held as drafts d-a, d-b — because …"The turn-end receipt
main): "2 plain-speech segment(s) held as drafts d-a, d-b (not sent — drafts can resend them unchanged, or dismiss them)"The next turn's catch-up of open drafts
main): "… that no recent notice in your context names: d-a (held (not sent); explicit send in its turn)"The notice for words that couldn't be delivered
main): "… It is held as draft d-x; nothing is lost. …"The notice for a message held after a refused
{{unsent}}main): "… Your message is held as draft d-y, with {{unsent}} written out; …"The receipt's line for a draft that resends only with a confirmation
main): "draft d-x is unconfirmed: … — sending it again could post it twice (resend needs confirmDuplicate: true)"Only the notice gives an order: it names one hold's drafts, always in the order they were written, while the receipt and the catch-up can name drafts from earlier turns. The length is the one
drafts(action: "read")and the listing show for the same draft, so the two match, and it's the draft's own text, the words a resend would send, so it leaves out a routing prefix the resident wrote before them, and after a{{unsent}}is written out it counts the words written out. Lines about drafts the resident can no longer resend (let go, dismissed, delivered), and replies about a draft it named itself, name it by id as before. Naming drafts by length comes from #668, which this replaces;mainalready carries its other parts.changelog.d/held-draft-lengths.changed.md.Credits
Hamish-2699 built it and wrote this description, from Nigel-2405's naming in #668. Paloma-2644 did the rent pass, Ada-1017 made the design calls, and nissa approved it.
Details for agents
Head
130e590f0a34, four commits onmainaa134e75fc19:c4d57db8b2be, the change;293b03d16b84, which answers the automatic review ofc4d57db8b2be;2676cb15398a, which answers the review of293b03d16b84; and130e590f0a34, which answers the review of2676cb15398a.Problem
draftsHeldNotice(src/framework.ts:22890–22926ataa134e75fc19), the turn-end receipt's held line (:23670–23676) andnoticeUnnoticedDrafts' catch-up (:23028–23046) name each held draft by id alone. The notice quotes no words, since an agent that observes the writer's slot is shown it too (:22917–22918), anddraftPreviewserves the drafts tool's listing only (:22933). So a resident with several held segments has to calldrafts(action: "list")to learn which is which. Three other places that tell a resident about a draft it can still resend name it by id alone too: the bounce notice's "It is held as draft d-x" (:26510,:26514), whose "Your text (N chars)" counts the words as written, the refused-{{unsent}}notice's held copy (:25999), and the receipt's unconfirmed line (:23685).Changes
draftLength(d)(src/framework.ts:22927–22929at the head):d.text.lengthas "N chars", the count and word ofdrafts read's "Text (N chars, exactly as a resend publishes it)" (:23315ataa134e75fc19) and the listing's "N chars" (:22951).draftsNamed(ds)(:22931–22935): each draft as "id (N chars)", two or more joined with "and".:22922) names its drafts withdraftsNamed, and for two or more adds ", in the order you wrote them": it names oneholdProseDraftscall's drafts, whichProseDrafts.holdreturns in segment order (src/prose-drafts.ts:363–396).:23682) names its drafts withdraftsNamedalone. Its list isrouting.drafts, to whichrouteSilencedHybridappends an earlier turn's bounced draft that a bare{{unsent}}names after the round's own holds (:22768–22769), so it makes no order claim.:23049) putsdraftLengthfirst in each draft's parenthesis, before its state and reason. The drafts beyond its eighth are still named by id alone.:23694), the bounce notice (:26519,:26523) and the refused-{{unsent}}notice's held copy (:26008) givedraftLengthafter the id.src/that shows a resident a draft's id, read in place, either answers a draft the resident named itself (thedraftstool's results, and the notices about the draft{{unsent}}named) or is about a draft it can no longer resend (the receipt's let-go, dismissed and delivered lines), and stays as it was.test/prose-drafts.test.tsgains "the held-draft notice names a draft by its length, and quotes none of its words", and "the held-draft notice counts a draft as drafts read does, a character outside the BMP included", which holds a draft with an emoji and compares the notice's number withdrafts read's, and "hybrid: a turn-end receipt naming an earlier turn's draft beside the turn's own gives each its length and claims no order". "hybrid: a bounce whose {{unsent}} expands names the held draft by its own length, not the words as written" bounceslost, then{{unsent}} and more, and asserts the notice names the held draft at 13 chars. The no-words test holdsquokka zebraand asserts neither word appears, the hybrid receipt test pins the listed drafts whole, and the bare-{{unsent}}re-bounce test intest/explicit-prose-routing.test.tsasserts the notice gives the re-bounced draft's own length. The tests that pinned the changed texts word for word now pin the lengths, intest/prose-drafts.test.ts,test/present-while-acting.test.tsandtest/explicit-prose-routing.test.ts.test/supplied-text-in-context.test.ts's stub drafts gain atext.CHANGELOG.md's entry for an earlier release, which quotes the receipt ("held as drafts d-… (not sent …)") and stays as the record of that release.Tests
Linux, Node 24.22.2, npm 11.19.1,
npm install(the root package has no lockfile),npm run build, thennode --testover the built files.Red at
aa134e75fc19with the head's four test files: 513 tests, 15 fail: every test that pins one of the three texts, and the new two.Green at a clean checkout of
c4d57db8b2be:test/prose-drafts.test.ts,test/present-while-acting.test.ts,test/supplied-text-in-context.test.ts,test/explicit-prose-routing.test.ts,test/locked-prose-routing.test.ts,test/outgoing-stream.test.tsandtest/release-changelog.test.tspass 624/624.The count's test against a code-point count: with
draftLengthcounting[...d.text].lengthunder the same word, the test with a character outside the BMP fails on the number,'4' !== '5', and the other 265 tests intest/prose-drafts.test.tspass.293b03d16b84, answering the automatic review:c4d57db8b2bewith the new receipt test added: 267 tests intest/prose-drafts.test.ts, 1 fails, the new test. Its receipt read "held as drafts d-6wzqcts (8 chars) and d-nwfeuh5 (10 chars), in the order you wrote them", the draft written this turn before the one bounced a turn earlier.293b03d16b84: the seven filestest/prose-drafts.test.ts,test/present-while-acting.test.ts,test/supplied-text-in-context.test.ts,test/explicit-prose-routing.test.ts,test/locked-prose-routing.test.ts,test/outgoing-stream.test.tsandtest/release-changelog.test.tspass 625/625.test/prose-drafts.test.tspass.2676cb15398a, answering the review of293b03d16b84:293b03d16b84with the newtest/prose-drafts.test.ts: 268 tests, 8 fail: the new bounce test, and the tests whose pinned bounce, copy and unconfirmed texts gained a length, one of which runs once in explicit mode and once in hybrid. I had counted 7 before the run, missing that second mode.2676cb15398a: the same seven files pass 626/626.test/prose-drafts.test.tsat293b03d16b84, and fails the new no-words test alone at2676cb15398a's tree.130e590f0a34, answering the review of2676cb15398a:2676cb15398awith the new re-bounce assertion:test/explicit-prose-routing.test.tspasses 120/120, since the code already gives the draft's own length.2676cb15398a, the re-bounce test passes and only the expanded-bounce test fails; with the new assertion, the re-bounce test fails too.130e590f0a34: the same seven files pass 626/626.Not verified
drafts readand the listing count, which can differ from what a model reads as characters for emoji and combining marks; it matches what the resident can read back.