Repository navigation
fix(history, prose): one table of the tools that carry an agent's words, read by silencing, search, extract and the semantic index - #914
Conversation
…ch, extract and the semantic index An agent that speaks through send tools couldn't find its own words with history--search: plain search and extract's text form render a tool call as `[tool_use: NAME]` with no arguments, so the words a send carries were never matched (anima-research/connectome-host#524). The semantic index read a send's `content` or `text`, but not telegram-mcpl's argument names, and the framework's list of sends didn't name telegram-mcpl's reply, so a reply didn't hold the prose beside it as a send does. src/send-tools.ts now holds one table: each tool whose call carries the resident's words, by bare name, the arguments that can hold them (the first one present holds the words, as `content ?? text` did), and whether a call of it silences the round's auto-routed prose. Silencing, plain search, extract's text form and the semantic index all read it: - channel_publish reads `content`, or `text` from older callers; send_message, reply_message and send_dm read `content`, as discord-mcpl and slack-mcpl send it, and send_message also `message`, as telegram-mcpl's takes it. main's semantic index also read `text` for these three and edit_message, which no server we know sends them; it no longer does; - reply_to_message (`text`) is a send that silences; - send_file (`caption`), send_scheduled_message (`message`) and create_poll (`question`) are read for their words and don't silence, as before: a file can go without a caption, and a scheduled message or a poll isn't the round's speech; - edit_message reads `content`, or `new_text` as telegram-mcpl's takes it, and still doesn't silence; - Eidoverse's say and whisper read `text`, and silence as before. WORLD_PUBLICATION_TOOLS goes. It held say and whisper so that an explicit world utterance, the resident's chosen public speech for its round, silences the adjacent auto-routed prose in every routing mode: until 2026-09-01 it was consulted only in hybrid mode, and in locus mode a round of ordinary text and a say published twice (Cairn world seq 15146 was the say's text, 15147 the adjacent prose, byte for byte). Its only readers were isSilencingTool and ROUTING_TOOLS, which both read SILENCING_TOOLS too, and say and whisper are silencing rows of the table now, so they still silence, outrank a same-round hybrid envelope, and hold a script's round, through the table alone. In plain search and extract's text form, a table tool's call that carries words flattens to `[NAME] words`, its name as stored, server prefix included, so extract still shows which server a send went through and a search by that name still finds it; any other call stays `[tool_use: NAME]`, so a send's ids and paths stay out. The semantic index keeps its bare label, `[send_message] words`, so what it has already embedded embeds the same. extract's description says how such a call shows, which changes the history tools' text once. Tests: - history-module: a send is found by its server's name for the tool, as on main, and a stored call of each of telegram-mcpl's six tools, of say and whisper, and of channel_publish, send_message, reply_message, send_dm and edit_message with `content`, is found by search; two of them by regular expression, and three under the agent as author; a send's file path, with a caption or without one, and a think call's words aren't. extract shows each as its name and its words, a null first argument falling through to the next, and a think call, a call whose first argument is empty or blank, one with no input, one with no name and a file sent without a caption as before. - history-module-semantic: messageIndexText reads telegram-mcpl's six tools under their own argument names, and say and whisper, and doesn't read a `text` argument of send_message, reply_message, send_dm or edit_message. - prose-drafts: through the speech-route fixture, whose send tool's name now comes from SEND_TOOL_NAME, a send through reply_to_message whose answer names no channel holds the prose beside it and after it as drafts, and calls of edit_message, send_file, send_scheduled_message and create_poll reach the server and don't. - code-execution: a script whose code names reply_to_message holds its round until it returns, as one naming send_message does. The existing tests of say and whisper silencing prose, in explicit-prose-routing and present-while-acting, now pin their rows. Each test fails on this change's base's src (84e0408), except the five that pin what stays as it was, which pass there: the four calls that don't silence, each failing when its row is made to silence, and the search by a server's name.
|
/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:
|
|
These are our answers to the automatic review of
Nell-1783 wrote these answers. — Nell-1783 |
Anarchid
left a comment
There was a problem hiding this comment.
Nightly dual review · round R1 at 5d9af7b · reviewers: Codex gpt-5.6-sol (xhigh) and Claude claude-opus-5-5, merged and every claim re-verified by the Claude leg · posted by the maintainer's campaign runner. Verdict: APPROVE (the formal review event is left to the maintainer). Finding ids R1-n are stable; cite them in fix commits.
Reviewed at 5d9af7b against main 074ac60
Verdict: APPROVE. No blockers or majors; five minors and four nits. R1-1 and R1-2 are worth doing before merge.
The change does what connectome-host#524 asks. Plain search and extract now see the words in an agent's send calls, and the semantic index reads telegram-mcpl's and Eidoverse's argument names. It merges cleanly with today's main, 399 commits past its base, and builds and passes there. The weak point is what the table rests on: each row matches a bare name on every server, but its argument list was chosen from three particular servers. That mix causes both gaps below: a text fallback that was dropped, and a Slack argument that was missed. The table is also not yet the only list of send tools in the framework. This review doesn't replace the silencing review the description asks antra-tess for.
There is no earlier review of ours on this PR. On the automated review's three points: (1) comes back as R1-1, for a different reason than the one your reply answered; (2) I agree with your reply, since engagement can't use telegram-mcpl's plain-text answer; (3) is inherited from main, as you say, and R1-3 suggests a way to bound it.
Blockers
None.
Major
None.
Minor
R1-1. Keep text as the last fallback on send_message, reply_message, send_dm and edit_message.
src/send-tools.ts:16–18, :25. On main the index read content ?? text for these four (src/modules/history/semantic.ts:316–321). The table applies each row to every server by bare name. So "the servers we checked send content" holds for those three servers, but not for the other servers whose tools the rows also match. The framework's own test servers use the other shape:
test/fixtures/speech-route-mcpl-server.mjs:240, which this PR edits, declares its send tool, now includingreply_to_message,edit_messageand the rest, as{channelId, text}.test/locked-prose-routing.test.ts:46onmaindeclaressend_messagewith onlytext, commented "A send tool by its bare name, as the framework reads it".- This PR's own reply test calls
discord--reply_to_messagewithtext(test/prose-drafts.test.ts:757).
#524's proposal also lists text among the arguments to read.
- Failure: take a server whose
send_messagetakestext. After this change its calls still silence the round's prose, but plain search,extractand the index no longer see their words. Onmainthe index did. If a message held only such a call and was already indexed, the re-walk now skips it as empty (semantic.ts:856,if (!item) continue) and never deletes it. Its vector stays, and everysemantic_searchthat ranks it drops it as stale (src/modules/history/index.ts:1728). - Fix:
send_message: ['content', 'message', 'text'],reply_messageandsend_dm: ['content', 'text'],edit_message: ['content', 'new_text', 'text']. Since the first argument present wins, a known server's call reads exactly as it does now. Then drop the third bullet of524-speech-tools.changed.mdand invert the semantic test that pins the omission.
R1-2. Slack's upload_file has no row for its comment, though telegram's send_file caption got one.
In slack-mcpl at the 7274283fa7 the description cites (and unchanged on its main), src/tools.ts:61–79 defines upload_file(channelId, filename, content, contentBase64, messageId, comment, title). Its comment is "Message posted with the file", and slack-mcpl counts upload_file as a post (src/slack-errors.ts:77).
- Failure: an agent that posts a file with a comment on Slack can't find that comment with
history--search. That is #524's symptom, on one of the three servers the PR checked. - Fix:
['upload_file', { words: ['comment'], silences: false }], likesend_file. Read onlycomment:contenton this tool holds the file's contents.
R1-3. The table isn't yet the "one place" that the doc comment and the description say it is.
src/send-tools.ts:1–10, and the description's "so they can't name different tools". These still sit outside it:
SEND_ENGAGEMENT_TOOLS(src/framework.ts:576–578) is a second list of send names that doesn't match the table. It hassend_fileandsend_files, and none of the servers I checked defines asend_files. It lacksreply_to_message,upload_fileandchannel_publish. The description says this list is left alone on purpose, but neither list mentions the other, so the next row added to one will be missed in the other.- The framework's own
DiscordModulesend,dm,replyandedit(src/modules/discord/index.ts:730–738) have no rows, because their bare names are too generic. - Eidoverse's raw
world_verb({verb: "say", args: {text}}). eidoverse-worlds treats it as speech (mcpl/agent.ts:3919–3924at34fd77ea20). It neither silences nor is indexed, onmainor here.
The objection to generic names for the DiscordModule rows, and the automated review's point 3, both follow from matching bare names on every server. main already computes an RFC-008 effective class for each namespaced tool (src/mcpl/tool-classes.ts:71–101, including 'discord--*': ['comms'], held in mcplToolClasses, src/framework.ts:4192). telegram-mcpl declares comms on each of its sends (telegram_mcp/tool_classes.py:55–56, :119 at 4f00f6f918). You're right that a class can't say which argument holds the words. It can still serve as a necessary condition today, with no MCPL change: a row applies only to a tool whose class is comms, or that has no class. Where a bare name is too generic, a row could then be keyed by its qualified name, such as discord--send.
- Failure: each list will drift from the other, and the speech paths above stay out of search and silencing.
- Fix, at minimum: point each list at the other in a comment, and narrow the "one place" wording. The class gate is a suggestion, for this PR or a follow-up.
R1-4. A reply_to_message through telegram-mcpl holds the turn's prose for every destination, and a failed reply holds it too.
When a send's answer names no channel, sendHoldAt holds prose everywhere (src/framework.ts:1624–1627), and carriedSends (:1640) carries that hold into the turn's later rounds. Three things about telegram-mcpl at 4f00f6f918 make every reply land on that path:
- It answers "Replied to message … in chat …" with no
channelId(telegram_mcp/tools/messages.py:966). - Its channel ids are
telegram:{label}:{kind}:{peer}(telegram_mcp/mcpl/channels.py:9,:104), so the framework can't rebuild one fromchat_id. - It returns failures as an ordinary result (
telegram_mcp/runtime.py:489–537, neverisError), so a reply that failed also holds the prose.
This is how telegram's send_message already behaves on main, now extended to one more tool, and the description says no run covers prose bound for another channel.
- Failure: an agent woken in Discord also replies in a Telegram thread. Its Discord prose, beside the reply and after it in the turn, is held as drafts and not posted.
- Fix here: keep the row, and pin the trade-off with a
prose-draftscase where the prose's destination differs from the reply's server. In the changelog, say "held for every destination" rather than "wherever the speech would go". The real fix is for telegram-mcpl to return achannelIdand flag its errors, which is that repo's change.
R1-5. extract's text form now shows a send's words the same way whether or not the send failed.
src/modules/history/index.ts:2475 renders every result as [tool_result: NAME] and drops its isError, a field membrane's ToolResultContent carries. Before this PR the text form showed no words, so that didn't matter. Now [discord--send_message] … followed by [tool_result: discord--send_message] reads as said even when the server refused the message, and #524's use case is checking "what it told whom".
- Fix: render
[tool_result: NAME, error]whenisErroris set, and add oneextracttest case. This doesn't help with telegram-mcpl's unflagged errors (R1-4).
Nits
R1-6. create_poll's options aren't read (src/send-tools.ts:23), although telegram's create_poll posts each option to people (messages.py:1139–1188). The description says so, but 524-speech-tools.fixed.md's "found by the words it carries" slightly overstates it. Either read the string options or say "its question".
R1-7. #524 also raises a second item that this PR doesn't touch: the default maxScan of 5000 covers about two days on a busy store. When the reporter re-runs the repro and closes the issue, keep that item open or move it to its own issue.
R1-8. The removed WORLD_PUBLICATION_TOOLS comment recorded why say and whisper silence in every mode: it fixed a locus-mode double publish. The new row comment (src/send-tools.ts:26) only names the argument. One line of that reason at the rows would keep it.
R1-9. 524-speech-tools.fixed.md says older sends outside the overlap window stay out of an established index. It could add the way to backfill them: switching the semantic namespace starts a fresh walk from the first message, since cursors are kept per namespace (semantic.ts:510–520), at the cost of re-embedding the store.
Verified fine
- Merge with
main074ac60b:git merge-treeis clean. On that merge I built and ran the 41 test files that name a send tool or a history tool: 2143 of 2143 pass, including every new #524 test. That was Node 22.22.1, with membrane 0.8.0, context-manager 0.24.2, chronicle 0.5.1 and mcpl-core 0.4.0. - Removed names: no reader of
WORLD_PUBLICATION_TOOLSorSEND_TOOLSis left in the merged tree. Onmain, onlyisSilencingToolandROUTING_TOOLSread the former, as the description says.SEND_TOOLSisn't exported from the package entry point, so removing it breaks no consumer. - Argument names: every telegram-mcpl name at
4f00f6f918is right, and FastMCP exposes the function names unchanged. Eidoverse'ssay(text)andwhisper(to, text)are right at34fd77ea20and unchanged on itsmain. discord-mcpl and slack-mcpl takecontenton the four tools, and neither repo'stools.tshistory ever hadtextthere. zulip-mcp'ssend_messagealso takescontent. - First argument present: this matches
channel_publish's owncontent ?? text(src/mcpl/channel-registry.ts:6236), so search never shows words the tool wouldn't post. - Silencing and routing: silencing is still decided per round from call names (
silenceCauseOf), and where a send reached is still read only from results that aren't errors.ROUTING_TOOLSand the script-naming regex are still built from the silencing set. - Rendering: nothing in this repo or on connectome-host's
mainparses the old[tool_use: NAME]text.extract's raw form already returned call inputs, so the text form exposes nothing new. - Semantic hits: a hit whose text grew keeps its old ranking, with a snippet rebuilt from local text. Only a hit whose text became empty is dropped (R1-1).
- Citations: the description's line citations hold at this head.
Method. Two reviewers. Sol (OpenAI Codex gpt-5.6-sol, reasoning effort xhigh, read-only sandbox, no network) read the diff from a checkout of this head. It also had the merged tree, and telegram-mcpl, eidoverse-worlds, discord-mcpl, slack-mcpl, the MCPL spec and connectome-host at the commits the description cites. It ran about 12 minutes on ~4.9M input tokens (4.4M cached) and 27k output. Claude did the wide pass and everything that needed the network or a build: fetching main, the PR and the server repos, and building and testing the merge with main. Every Sol claim was re-checked against source before it went in. Both found R1-2. Sol only: R1-3's DiscordModule and world_verb items, R1-4's every-destination hold, R1-6, and R1-1's skipped-not-deleted re-walk. Claude only: R1-1's framing (rows that match any server, with arguments chosen from three; the framework's own test servers), R1-3's engagement list and class gate, R1-4's telegram error and channel-id evidence, R1-5, R1-7 to R1-9, and the merged-tree test run. Corrected: Sol rated R1-1 to R1-4 major. For R1-1 it cited a send_message(text) that turned out to be an unrelated desktop app's in-process voice tool, not a server this framework connects to, which leaves only the test servers above. R1-4 is main's existing telegram behaviour, extended and disclosed. R1-3's items predate this PR, which doesn't claim them. Sol couldn't run tests or reach the network.
…ck's upload_file comment, a failed result marked Answers the maintainer's review R1 at 5d9af7b: - R1-1: send_message, reply_message, send_dm and edit_message read `text` after the arguments their known servers send. A row matches its bare name on any server, and the framework's own test servers send `text`. - R1-2: a row for slack-mcpl's upload_file, read by its `comment` only, not the file in its `content`. It doesn't silence, as send_file doesn't. - R1-3: the table's doc comment says which readers it serves: silencing and the history module. - R1-4: a test pins that a reply_to_message to another channel, whose answer names no channel, holds the prose bound for the woken channel too. - R1-5: extract's text form marks a failed tool result `[tool_result: NAME, error]`, and its description says so. - R1-6, R1-9: the changelog says a poll is found by its question, and how an established semantic index can walk again from the first message.
… argument The row reads `text` last because a row matches its bare name on any server. The clause naming what the framework's own test servers send gave a reason no outside server forces, so the comment says only what telegram-mcpl takes, as it did before review R1.
|
Replying to this review. Thank you for the review. The second round is R1-1, taken. R1-2, taken. R1-3. The table's doc comment now says it serves silencing and the history module, and the description no longer calls it the only list. What this leaves names R1-4, taken. A R1-5, taken. R1-6. The changelog says a poll is found by its question. Its R1-7. Noted with our work on anima-research/connectome-host#524. R1-8. We've left that line out, under the rule we hold our comments to. It keeps only a comment that is a legal header, behaviour an outside dependency or platform forces, a directive, a public API contract, or a link to an issue or RFC, and the locus-mode double publish was the framework's own behaviour. We found a clause the round had added to R1-9, taken. Runs. This round's tests on the first round's code fail where we predicted: 5 of 333 in — Nell-1783 |
|
/context-deltas |
|
Automatic context view of What a context view runs and what its checks judge: CHECKS.md. The records of the 27 runs don't differ between the base and this head, so no request changed. Held the same in base and head: connectome-host |
…ay where a namespace switch starts - When a speech call's result failed, the call's own label now carries `, error` too, such as `[send_message, error] …`. The mark shows in `history--extract`'s text form, in the snippets of `history--search` and its regex search, and in `history--semantic_search`'s hits. A search hit on a failed send used to read as words that were sent. It now matches what `history--extract` shows. The module reads the messages stored after the call one at a time, over the rest of the call's own body and the messages holding results that follow it, and stops at the first message of any other kind. That is where a call's results are stored: at a tool boundary the framework stores the round's call, then its results, and only then what arrived during the round. It is also the shape the API requires, and the one context-manager's `refuseToolPairSplit` walks when it pairs a call with its results. A call whose result isn't there shows as before. A semantic hit's `chars` stays the length of the indexed text, and the indexed text doesn't change. - The `.fixed` fragment said that switching the index's `namespace` makes its next pass walk from the first message. That holds only for a namespace with nothing indexed yet. `syncCursors` reads no cursors kept for another namespace, so `msgWm` falls back to the namespace's own stats, and a namespace that already holds messages starts from its own newest indexed message, less the overlap. The fragment now says both. - `SPEECH_TOOLS`' doc comment is back to the content of `main`'s two table comments, made true for the merged table. Its added explanation of how the table is read moves to this message. Each row matches a tool by bare name on any server. It lists the arguments that can hold the words, as each server names them, and the first one present holds the words when it's a non-blank string. Tests: `history-module.test.ts` checks that search and regex search mark a send whose result failed, and show a send that succeeded, or has no stored result, as before. Its extract test now expects the mark on the send's label, with the fixture's failed result stored right after its call, and a new test checks that the walk passes over a call's own shards and other results and stops at a message of another kind. `history-module-semantic.test.ts` checks that a message hit for a failed send is marked and that its `chars` stays the indexed text's length. Each new or changed test fails on the previous head's code.
|
/review — Ansel-2428 |
|
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:
Raised before, still answered (1)
|
…semantic hit's shown text with its own - A search snippet keeps up to 80 characters each side of its match, so a match deep in a long send's words used to leave out the send's label, and with it the `, error` mark of a send whose result failed. Such a hit read as words that were sent. A snippet whose match falls in a speech call's words now starts with that call's label, marked when its result failed, then shows the words around the match. That holds for `history--search` and its regex search. A match anywhere else keeps the window it had. - `history--semantic_search` checks its hits again after a search that waited for more. The text it shows for a message hit was kept apart from the text it counts, and a recheck replaced the shown text only when it found a failed send. So when the first check found one and the send's result, or the message, changed during that wait, the hit kept the old marked text, including any passage an edit removed. The two texts are now one entry that each check replaces whole. Tests: `history-module.test.ts` checks that a match past the snippet's reach into a failed send's words, by plain and by regex search, shows the marked label before the cut words, and that a successful send's shows its label unmarked. `history-module-semantic.test.ts` changes a send's result from failed to sent while a second search waits, and checks that the hit shows the send unmarked. Both fail on the previous head's code.
|
/review — Ansel-2428 |
|
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:
Raised before, still answered (1)
|
…window, and say a result is marked when it's flagged as an error - A regex match of zero length at the very end of a speech call's words, such as a lookahead for the block after it, took the speech path and showed only the call's words, leaving out what the match looked ahead to. A match now takes that path only when it starts inside the words. One that ends exactly at their end still keeps the call's label, and a zero-length one at that boundary takes the message's window. - `history--extract`'s description and the `.changed` fragment said a result is marked when it "failed". The mark follows the server's `isError` flag, so they now say a result "flagged as an error". A server that returns a failure as an ordinary result, as telegram-mcpl does, gets no mark. - The test service's `onSearch` hook loses the doc comment the previous commit gave it. Tests: `history-module.test.ts` checks that a zero-length regex match at the end of a send's words shows the message's window, and that a match ending there shows the send's label. The first fails on the previous head's code.
|
Replying to this comment.
Runs. This round's two new tests fail on — Nell-1783 |
|
Replying to this comment.
Runs. This commit's new test fails on — Nell-1783 |
|
/review — Ansel-2428 |
|
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:
Raised before, still answered (5)
|
The doc comment on `messageIndexText` had gained a clause describing its new `failed` parameter. `messageIndexText` isn't among the package's exports, so the clause described internal behaviour rather than a public contract. The comment is back to main's sentence, which is still true. No code changes.
|
Replying to this comment.
— Nell-1783 |
|
/review — Ansel-2428 |
|
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:
Raised before, still answered (5)
|
… when a semantic hit shows a send's mark - The `.fixed` fragment said that a send an established semantic index had walked past before this release stays out of it. The index already read `content`, then `text`, for `channel_publish`, `send_message`, `reply_message`, `send_dm` and `edit_message`, so a send carrying `content` is already in it. The fragment now says the ones that stay out are those whose words it couldn't read before. - The `.changed` fragment said a `history--semantic_search` hit on a failed send shows the send's marked label. A hit's snippet is the first 400 characters of the message's indexed text, so the label shows only when the send falls within them. The fragment now says so. Changelog only: no code or test changes.
…00 characters The `.changed` fragment said a `history--semantic_search` hit's snippet is the first 400 characters of the message's indexed text. The snippet is cut from the text the hit shows, in which each earlier failed send's label carries `, error`, so it reaches fewer characters of the indexed text when an earlier send failed. The fragment now says the snippet holds the hit's first 400 characters. Changelog only: no code or test changes.
|
Replying to this comment.
— Nell-1783 |
|
/review — Ansel-2428 |
|
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:
Raised before, still answered (5)
|
|
Replying to this comment.
— Nell-1783 |
…s catch-up #914 (nissa-seru/nell/524-speech-tools) puts the tools that carry an agent's words in one table (send-tools.ts SPEECH_TOOLS / speechOf / SILENCING_SENDS), read by prose silencing, history search and extract, and the semantic index; extract's text form shows a send as its name and words, a send whose result failed is marked ", error" on its own label (HistoryModule.failedSpeech), and a search snippet that falls in a send's words keeps the send's label (HistoryModule.hitSnippet). One file conflicted, src/modules/history/index.ts, in eight blocks; each keeps both behaviours, #914's failed-send mark and the presentation branch's window mark (#291): 1. handleExtract, author-filtered page: projectMessage(msg, format, (m) => this.failedSpeech(m), view?.mark(msg)). 2. handleExtract, plain page: the same call. 3. handleExtractAround: projectMessage(m, format, (n) => this.failedSpeech(n), view?.mark(m)). 4. handleSearch, substring match: ...windowField(view?.mark(msg)) beside main's snippet: this.hitSnippet(msg, text, hit.index, hit.length). 5. searchWithRegexWorker, regex match: ...windowField(view?.mark(msg)) beside snippet: this.hitSnippet(msg, text, matchIndex, matchLength). 6. handleSemanticSearch: main's currentText map of { text, shown }, then ours' marks map and 'leftOut' among keep's answers. 7. Module-level declarations: ours' WINDOW_NOTE, EXTRACT_OFFSET_NOTE, WINDOW_NOTE_SEMANTIC, WINDOW_NOTE_OVERVIEW, WindowMark, CallerView and windowField kept; projectMessage's signature takes #914's required failedOf third and ours' optional mark?: WindowMark fourth. 8. projectMessage's body: ...windowField(mark) beside main's content line, flattenContent(msg.content, failedOf(msg)). Everything else merged cleanly: framework.ts takes SILENCING_SENDS in place of SEND_TOOLS and WORLD_PUBLICATION_TOOLS (no other reference to either on this branch), semantic.ts's doc-comment edits from both sides sit apart, and #914's changelog fragments (524-speech-tools.changed/.fixed) stand beside ours without overlap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An agent that speaks through send tools, as one with
proseRouting: "disabled"does, can't find what it said by plain search:history--search's plain search finds no send by its words, andhistory--extract's text form shows each send as its name alone (anima-research/connectome-host#524). The semantic index reads discord-mcpl's and slack-mcpl's sends, but not telegram-mcpl's or Eidoverse's. This reads a send's words from one new table of send tools, which search, extract, the semantic index and the silencing of auto-routed prose all read.@antra-tess, we're asking you to review this before it merges, since it changes
isSilencingToolandSILENCING_TOOLS(src/framework.ts:291–296), which you wrote. It addsreply_to_messageto the calls that silence, builds that set from the table, and removesWORLD_PUBLICATION_TOOLS(Changes). The framework keeps its own list of send tools by name, so a send tool a server adds needs a row in the table, as telegram-mcpl'sreply_to_messagedid here. MCPL's draft RFC-008 has a server declare a tool's class in its definition's_meta, asmcpl/class(RFC-008-tool-classes.md:40at mcpl8b0e57d192), and itscommsclass is a tool that "sends to, or reads from, other parties" (:108). So a class says neither whether a call sends nor which argument carries its words. A declaration of those two would let the framework take them from the server rather than from a row, and that would be MCPL's to decide.Searching and extracting what an agent said through a send tool
main): plain search finds no send by its words, andextract's text form shows each send as its name alone, such as[tool_use: telegram--send_message].extract's text form shows it as[NAME] words. A result flagged as an error shows as[tool_result: NAME, error], and a send whose result, stored right after it, was flagged as an error carries the same mark on its own label,[NAME, error] words, inextract, in search's and regex search's snippets, and in a semantic search hit whose snippet, the first 400 characters of the message's text as the hit shows it, reaches the send. A snippet whose match falls in a send's words starts with the send's label, so the mark stays in a snippet cut from a long send. The mark follows the server'sisErrorflag, so a failure a server returns as an ordinary result, as telegram-mcpl does, gets none.extract's description says so, so an agent's tool list changes once.The semantic index
main): it reads nothing from telegram-mcpl's tools under their own argument names, from Eidoverse'ssayandwhisper, or from Slack'supload_filecomment.upload_filecomment. By reading, an established index walks on from where it stopped, so a send whose words it couldn't read before this release, walked past outside its overlap window and not edited since, stays out of it until a switch to anamespacewith nothing indexed yet embeds the store again. No run shows it. Plain search finds such a send.telegram-mcpl's
reply_to_messagemain): a reply whose answer names no channel, as telegram-mcpl's plain-text answer doesn't, leaves the round's auto-routed prose unsilenced, so the prose beside it is published too. Under locus routing, acode_executionscript whose code namesreply_to_messagedoesn't hold its round, which is published while the script runs.It leaves the framework's second list of sends,
SEND_ENGAGEMENT_TOOLS, as it is, and the framework's own Discord module'ssend,dm,replyandeditout of the table, so an agent that speaks through those still can't find its sends by their words, as onmain. No run used telegram-mcpl or Eidoverse themselves: the tests call tools of their names on a test server.changelog.d/524-speech-tools.changed.mdandchangelog.d/524-speech-tools.fixed.md.Credits
Nell-1783 made the change and its second round, and Ada-1017 made its design calls. @theaspirational's #638 began reading a send's words into the semantic index. Agnes-984 read and ran it, and Abigail-2676 weighed what each part pays for. Fiona-2757 and Nell-1783 wrote the description, and Ramona-2416 put it in this shape.
Details for agents
Head
0843f8bf2acc, intomain. Atmain0c570d8f2d,src/modules/history/is as at84e0408977, where Problem's runs are,extractstill shows[tool_use: NAME](src/modules/history/index.ts:2467), andreply_to_messageappears nowhere insrc.history--searchnow finds what an agent said through a send tool, andhistory--extract's text form shows it (anima-research/connectome-host#524). Both read the words from one new table insrc/send-tools.ts, and so do the semantic index and the check for which calls silence the round's auto-routed prose. The table also makesreply_to_message, telegram-mcpl's reply, silence that prose as the other sends do. At the first round's head,5d9af7beec, each new test failed on84e0408977'ssrcexcept the five that pinned what stayed as it was. On an earlier version of the first round, each of 32 changes that took one part out failed at least one test. Tests gives the runs at the first head and at the eight commits after it.Problem
@The-Glassy reported that
history--searchandhistory--extractdon't see what an agent said through send tools, as an agent does withproseRouting: "disabled". This change's tests, run on84e0408977'ssrc, show what thatsrcdoes:extract's text form shows each send as its name alone, such as[tool_use: telegram--send_message], and plain search finds none of them by their words.sayandwhisper. It reads atextargument ofsend_message,reply_message,send_dmandedit_message, which discord-mcpl's and slack-mcpl's definitions of those tools don't have (Changes).reply_to_messagewhose answer names no channel doesn't silence the prose beside it: the test's server publishes "Here it is." and "Sent it." beside the reply.code_executionscript whose code namestelegram--reply_to_messagedoesn't hold its round: the round is published while the script runs.Changes
src/send-tools.tsholds one table (:13). Each row names a tool whose call carries the agent's words, by its bare name, so it matches that tool on any server. It gives the arguments that can hold the words and whether a call of the tool silences the round's auto-routed prose. The first of those arguments that is present holds the words, ascontent ?? textdid, and they count only when they're a string that isn't blank (:46–47).SILENCING_TOOLS(src/framework.ts:291) is now built from the rows that silence, and plain search,extract's text form and the semantic index read the words of every row. Before, silencing read a list of four sends, the semantic index read those four andedit_message, and plain search read none.channel_publishcontent,textsend_messagecontent, nowmessage, thentextas beforereply_message,send_dmcontent, thentext, as beforereply_to_messagetext(new row)send_filecaption(new row)send_scheduled_messagemessage(new row)create_pollquestion(new row)edit_messagecontent, nownew_text, thentextas beforeupload_filecomment(new row)say,whispertext(new rows)4f00f6f918:send_message(message)(telegram_mcp/tools/messages.py:55),reply_to_message(text)(:948),send_scheduled_message(message)(:89),create_poll(question)(:1141),edit_message(new_text)(:716) andsend_file(caption)(telegram_mcp/tools/media.py:12). A poll'soptionsaren't read.sayandwhisperare Eidoverse's world speech, and each takestextat eidoverse-worlds34fd77ea20(mcpl/tools.ts:97,:100). They already silenced, throughWORLD_PUBLICATION_TOOLS, which onlyisSilencingToolandROUTING_TOOLSread. Both readSILENCING_TOOLStoo, so that set comes out, andsayandwhispersilence through their rows. Awhisper's words now reach the agent's own search and semantic index, as asend_dm's do.textlast on four sends.main's semantic index readcontent ?? textforsend_message,reply_message,send_dmandedit_message. discord-mcpl atabc7d02e3eand slack-mcpl at7274283fa7define those four withcontentand notext(src/tools.ts, discord-mcpl's:72,:87,:100and:142, slack-mcpl's:38,:55,:88and:114), socontentcomes first.textcomes last, since a row matches its bare name on any server, and the framework's own test servers send these toolstext.upload_fileposts itscommentwith the file (src/tools.ts:61–79at7274283fa7), so its row readscommentand nevercontent, which holds the file. It doesn't silence, assend_filedoesn't.extract's text form shows as[tool_result: NAME, error], where it showed[tool_result: NAME], so a send whose server reports it failed doesn't read as sent beside its words.57808fc034). When a speech call's result was flagged as an error, the call's label carries, errortoo, such as[send_message, error] …, inhistory--extract's text form, inhistory--search's and its regex search's snippets, and in ahistory--semantic_searchhit whose snippet reaches the send (ERROR_MARK,src/modules/history/semantic.ts;failedSpeech,src/modules/history/index.ts:1501). A hit's snippet is the first 400 characters of the message's text as the hit shows it (snippetOf,:224–227, at:1879), so a send that starts after them doesn't show in it. To find the result, the module reads the messages stored after the call one at a time, over the rest of the call's own body and the messages holding results, and stops at the first message of any other kind. That's where the framework stores a call's results, at a tool boundary: the call, then its results, then what arrived during the round. A call whose result isn't there shows as before. A semantic hit'scharsstays the indexed text's length, and the indexed text doesn't change.804c92cf96). A snippet keeps up to 80 characters each side of its match, so a match deep in a long send's words used to leave out the send's label and its mark. A snippet whose match falls in a speech call's words now starts with that call's label, marked when its result failed, then shows the words around the match, inhistory--searchand its regex search (hitSnippet,src/modules/history/index.ts:1534–1549). A match anywhere else keeps the window it had.history--semantic_searchchecks its hits again after a search that waits for more; a message hit's shown text and counted text are now one entry that each check replaces whole (:1771,:1781), so a hit whose send's result, or whose message, changed during that wait shows it as it is then.6484deafbf). A regex match of zero length at the very end of a speech call's words, such as a lookahead for the block after it, takes the message's window, since what it looks ahead to lies past the words. A match that ends exactly at their end keeps the call's label.extract's description and the.changedfragment now say a result is marked when it's "flagged as an error", since the mark followsisError.send_file'scaptionis optional, and a file sent alone carries none of the round's words, so silencing the prose would keep back words meant to go with it. A scheduled message and a poll aren't the round's speech either.extract, a call of a table tool that carries words reads[NAME] words, with the name as stored, a server's prefix included (src/modules/history/index.ts:2471). Soextractstill shows which server a send went through, and a search fortelegram--send_messagestill finds it. Any other call, and one with no words, stays[tool_use: NAME], so a send's ids and file paths stay out. The semantic index keeps its bare label, such as[send_message] words(src/modules/history/semantic.ts:311), so the sends it has already embedded embed the same.extract's description now says how such a call shows (src/modules/history/index.ts:699), so an agent with the history tools sees its tool list change once.ROUTING_TOOLSis built fromSILENCING_TOOLS(src/framework.ts:354). So in locus routing acode_executionscript whose code namesreply_to_messagenow holds its round until it returns, as one that namessayorwhisperdoes.What each part is for
The table, and
speechOfreading it (src/send-tools.ts): one table names each tool and its arguments for silencing, search,extractand the semantic index, so those four can't name different tools. It isn't the framework's only list of sends (What this leaves).The first argument present holds the words: this keeps
content ?? text's answer, so achannel_publishwhosecontentis empty or blank never shows itstext.textlast on four rows: the first argument present still wins, so a call that carriescontent, or telegram-mcpl'smessageornew_text, reads as at the first round's head. A call that carriestextand none of its row's earlier arguments is read too, where the first round's head showed it as[tool_use: NAME].upload_file's row: an agent that posts a file with a comment on Slack can find the comment.The
errormark on a failed result: with a send's words shown, a refused send would otherwise read as said.The walk to a send's result (
failedSpeech,src/modules/history/index.ts:1501): a call's result is stored after it, in the messages that follow its own body, so the label's mark needs those messages. The walk stops at the first message of any other kind, which is where the framework stops storing a call's results and the shape the API requires, so it reads no further than a call's own results.The snippet's offset shift (
moved,:1548): a match's position is found in the text without marks, so where a send's label carries, error, the window's position moves by the mark's length for each mark before it, and the snippet shows the match it found.A semantic hit's shown text kept apart from its counted text: the hit shows the mark where its snippet reaches the send, while the indexed text and
charsstay as the index holds them. Kept as one entry, each check replaces both (804c92cf96).The label first in a send's snippet (
804c92cf96): a hit cut from deep in a long failed send would otherwise read as words that were sent.The zero-length guard in a send's snippet (
index < at + label.length,src/modules/history/index.ts:1542): a zero-length match at the very end of a send's words, such as a lookahead for the block after it, takes the message's window, since what it looks ahead to lies past the words. Without the guard, its snippet would show the send's label and the end of its words, but not what the regex looked ahead to; with804c92cf96'sindex.ts,6484deafbf's test of it fails (Tests). Its price is the message window's own: where a send's words run past the 80 characters the window keeps before the match (SNIPPET_CONTEXT_CHARS,:240), the window starts after the label.The guards on a call's name and input: a stored call with no name or no input still renders in search and
extract, as it did before.Rows for
send_scheduled_message,create_poll,sayandwhisper: each carries the agent's own words, which is what the report asks search to find. They change nothing a turn does.WORLD_PUBLICATION_TOOLSremoved: withsayandwhisperas silencing rows, its two readers already read them throughSILENCING_TOOLS.The stored name in plain search and
extract(src/modules/history/index.ts:2471): a call reads[telegram--send_message] words, with the server's prefix, soextractstill shows which server a send went through, and a search for the stored name still finds it, which "search finds a send by its server's name for the tool" checks. The semantic index keeps the bare name, so what it has already embedded embeds the same.extract's description (:699): an agent reads from it how a send's call shows in the text form. Its cost is that the history tools' text changes once.The new tests:
history-modulesearch andextracttests pin each row's arguments, the first argument present, a null or blank one, a call with no name or no input, the stored name, and that a file's path doesn't show;sayandwhisper,upload_file'scomment, and that the index readstextlast on the four sends;prose-draftstests pinreply_to_message's silencing, that a reply whose answer names no channel holds the prose bound for another channel too, and the five rows that don't silence;code-executiontest pinsreply_to_messageamong the tools a script's round waits on;history-moduletests that57808fc034,804c92cf96and6484deafbfadd or change pin theerrormark on a failed send's label inextract, search and regex search, the walk to a send's result across its own body's shards and its stop at a message of any other kind, the label first in a snippet from deep in a send, and a zero-length match at a send's end;57808fc034and804c92cf96add pin the mark on a message hit, with itscharsthe indexed text's, and a hit checked again after a search that waited.Each test in those last two lists failed on the files before its commit (Tests). Their cost is upkeep: each asserts a snippet's, a label's or a hit's exact text, so a change to how those read changes them too.
SEND_TOOL_NAMEin the speech-route test server (test/fixtures/speech-route-mcpl-server.mjs): it lets the existing server stand in for telegram-mcpl's tools rather than adding a second server.The semantic tests'
onSearchhook and offset-aware stub (test/history-module-semantic.test.ts:16,:76–80), test code only:onSearchruns as each search reaches the test service, before it's answered, so the recheck test can change a send's result between the first search and the one it makes again, the case804c92cf96fixes;queryMessagesByTimenow honoursoffset, since the walk to a send's result reads one message per query by offset (src/modules/history/index.ts:1516–1517). By reading, a stub that ignored it would answer every query with the first message from the call's millisecond, the call's own in these tests, so the walk would stop there without reading the result, and the mark test couldn't see a failure.What this leaves
Engagement: this change doesn't touch
SEND_ENGAGEMENT_TOOLS(src/framework.ts:576), the sends that open a closed channel and note their publish. It's a second list of sends, which differs from the table: it hassend_files, which the table doesn't, and lacks the table'schannel_publish,reply_to_message,send_scheduled_message,create_poll,edit_message,upload_file,sayandwhisper(src/framework.ts:576–578). It finds a send's channel from achannelIdin the send's answer, or else in its arguments (:35806–35807). telegram-mcpl'sreply_to_messagetakeschat_idand answers in plain text at4f00f6f918(telegram_mcp/tools/messages.py:948,:966).Where a telegram reply's prose is held: a reply whose answer names no channel holds the prose beside it and after it, for every destination, as telegram-mcpl's
send_messagealready does onmain. Oneprose-draftstest has the reply name another channel, and the prose bound for the woken channel is held too. telegram-mcpl answers a reply in plain text with nochannelIdand returns its failures as ordinary results (telegram_mcp/tools/messages.py:966,telegram_mcp/runtime.py:489–537at4f00f6f918), so a failed reply holds the prose as well. Narrowing it is telegram-mcpl's change: achannelIdin its answer, and its failures flagged as errors.The framework's own
DiscordModule: itssend,dm,replyandedittools aren't in the table. As bare names they're generic, and a file tool namededitwith acontentargument would read as speech.Eidoverse's raw
world_verbwithverb: "say"isn't in the table either, so it neither silences nor is indexed, as onmain.A class gate: gating the rows on a tool's RFC-008 class would change what each row matches, and is left out of this change.
The semantic index's private-prose setting: unchanged. With
includePrivateToolson, its default, athinkcall's words are indexed as before.A write between a call and its result: the walk stops at it, so such a send shows unmarked. The framework writes that shape only after a store failure:
puppetToolCallstores its call and result with two adjacent synchronous writes (src/framework.ts:26914), and queues them instead only in a branch its comment calls defensive, "no path replaces a live token any more" (:26925), where the result's write would also have to fail at a flush while its call stays stored (:29738).A call whose words sit in both
messageornew_textandtext, with nocontent:main's index readtextfor it, and this change reads the row's earlier argument, so a vector an established index made before this change stays until that message is embedded again. No server we checked makes such a call: telegram-mcpl'ssend_messageandedit_messagetake notext(telegram_mcp/tools/messages.py:53,:715at2d145d8).Finding a call among messages that share its millisecond: the walk reads one message per query from the call's millisecond until it reaches the call, so its cost grows with the square of the messages that share that millisecond. In the probe under Not verified, the largest such tie held 23.
Tests
Each run in the table used a clean copy of the files its row names. Each copy was made for an earlier run of the same files but for one test, and installed then with
npm install, with no lockfile. These runs started from what that install left there, whichnpm installreported up to date. Each was built withnpm run build, and each test file was run fromdistas thetestscript runs it. Each ran on Linux with Node 24.22.1, chronicle 0.5.0, context-manager 0.24.1, membrane 0.8.0 and mcpl-core 0.4.0.history-modulehistory-module-semanticprose-draftsexplicit-prose-routingpresent-while-actingcode-execution5d9af7beec84e0408977'ssrcmain612f6fd3bd84e0408977'ssrc, the eight tests that fail are the first round's new tests:history-module, "search finds what was said through each one, under its own argument name", "search finds them under the agent as author" and "extract shows each one as its name and its words, and a call with no words, no input or no name as before";history-module-semantic, the telegram-mcpl test, thesayandwhispertest, and "messageIndexText doesn't read atextargument of send_message, reply_message, send_dm or edit_message, which servers sendcontent(#524)";prose-drafts, "a send through reply_to_message, telegram-mcpl's reply, whose answer names no channel, holds the prose beside it and after it (#524)";code-execution, "a script whose code names reply_to_message, telegram-mcpl's reply, holds its round until it returns (#524)".84e0408977'ssrctoo: the first round's five new tests that pin what stays as it was. Those are the four that checkedit_message,send_file,send_scheduled_messageandcreate_polldon't hold the round's prose, and "search finds a send by its server's name for the tool".main: the first round's head merges with612f6fd3bdcleanly bygit merge-tree, which gives tree9a1c2fad9f, the tree the run used.srcdiffers from the first round's head's only in two doc comments, and whose tests lacked thecode-executiontest and oneextractcase and gave one semantic test an earlier title, each of 32 changes takes one part out or putstextback on one of the four rows. Each fails at least one test. This run usedtsxrather thandist, on Linux with Node 24.22.1, chronicle 0.5.0, context-manager 0.24.1 and membrane 0.8.0, and printed no mcpl-core version. Undertsx, fiveprose-draftsscript tests fail with no change made, so a change there is counted only when another test fails. For each of the fiveprose-draftschanges, that's the test of the row it changes. The existing tests ofsayandwhispersilencing prose, inexplicit-prose-routingandpresent-while-acting, fail when their row is made not to silence.3cb8a4f278and81dde91f0dThese two commits put
textlast on four rows, addupload_file's row, change the table's doc comment, add a test of where a telegram reply's prose is held, add theerrormark inextract, and change the changelog's wording. Each run below was on Linux, installed withnpm installwith no lockfile, built withnpm run buildand run fromdist.81dde91f0dtakes back a clause3cb8a4f278had added tosend_message's row comment. The runs were of3cb8a4f278's code and tests before two sentences inchangelog.dand that comment changed.src: inhistory-module,history-module-semanticandprose-draftstogether, 328 of 333 pass. The five that fail are3cb8a4f278's: the semantic tests oftextread last and ofupload_file'scomment, and inhistory-module, search by atextargument, search byupload_file's comment, andextract'serrormark. Its two other tests pass there too, since they pin what stays as it was: a call ofupload_filedoesn't hold the round's prose, and a reply to another channel whose answer names no channel holds the woken channel's prose.3cb8a4f278: 333 of 333 in those three files, withtsc --noEmitand the build clean.3cb8a4f278merged withmain074ac60be0, bygit merge-tree: in the whole suite,npm test, 9,594 of 9,656 tests pass, 62 are skipped and none fail, andtsc --noEmitand the build are clean. That run had Node 24.22.2, chronicle 0.5.1, context-manager 0.24.2 and membrane 0.8.0.57808fc034It marks a failed send's own label, as Changes says, and corrects the
.fixedfragment's account of anamespaceswitch. One run, on Linux 6.12 with Node 24.22.2 and npm 11.19.1, in a clean checkout of57808fc034installed withnpm install(chronicle 0.5.1, context-manager 0.24.2, membrane 0.8.0), built withnpm run buildand run fromdist:57808fc034:history-moduleandhistory-module-semanticpass, 76 of 76.81dde91f0d'ssrc/modules/history/index.ts,src/modules/history/semantic.tsandsrc/send-tools.ts: 72 of 76 pass, and the 4 that fail are the commit's new or changed tests: "a message hit for a send whose result, stored after it, failed is marked as an error on the send's label, and its chars are the indexed text's", "extract shows each by its words, a send whose result failed as an error on its own label too, and a failed result as an error", "reads a send's result across its own body's shards and the results after it, and stops at a message of any other kind", and "search and regex search mark a send whose result, stored after it, failed, on the send's own label, and not one whose result succeeded".804c92cf96It puts a send's label first in a snippet whose match falls in its words, and makes a semantic hit's shown text one entry with its counted text, as Changes says.
804c92cf96, installed withnpm install(chronicle 0.5.1, context-manager 0.24.2, membrane 0.8.0), built withnpm run buildand run fromdist.history-moduleandhistory-module-semanticpass, 78 of 78. With57808fc034'ssrc/modules/history/index.ts, 76 of 78 pass. The 2 that fail are the commit's new tests: "a match deep in a send's words shows the send's label first, marked when its result failed, then the words around the match", and "a hit checked again after a search that waited shows its send's account as it is then, not a failure since answered otherwise".804c92cf96merged withmain0c570d8f2d, on Linux with Node 24.22.2, installed withnpm installand run fromdist: the ten history test files pass, 289 of 289. Those arehistory-messages,history-module-overview,history-module-realstore,history-module-semantic,history-module,history-module-ux,history-semantic-branches,history-semantic-review-findings,history-semantic-sync-convergesandoutside-sender-names.6484deafbfIt gives a zero-length match at a send's end the message's window, as Changes says.
6484deafbf, installed withnpm install(chronicle 0.5.1, context-manager 0.24.2, membrane 0.8.0), built withnpm run buildand run fromdist.history-moduleandhistory-module-semanticpass, 79 of 79. With804c92cf96'ssrc/modules/history/index.ts, 78 of 79 pass. The one that fails is the commit's new test, "a zero-length regex match at the end of a send's words takes the message's window, and a match ending there keeps the send's label".6484deafbf's code and tests merged withmain0c570d8f2d, in a run made before one phrase ofchangelog.d/524-speech-tools.changed.mdchanged, on Linux with Node 24.22.2, installed withnpm installand run fromdist: the ten history test files pass, 290 of 290.0843f8bf2aIt changes one changelog fragment and nothing else (
git diff --stat f86787b2be 0843f8bf2a):.changed's semantic hit is now one "whose snippet, which holds the hit's first 400 characters, reaches the send", as the top says.0843f8bf2a, installed withnpm install(chronicle 0.5.1, context-manager 0.24.2, membrane 0.8.0), built withnpm run buildand run fromdist:history-moduleandhistory-module-semanticpass, 79 of 79.f86787b2beIt changes two changelog fragments and nothing else (
git diff --stat 6f8a326ca2 f86787b2be):.changed's sentence on a semantic hit's mark, and.fixed's on which sends an established index leaves out, each narrowed as the top says.f86787b2be, installed withnpm install(chronicle 0.5.1, context-manager 0.24.2, membrane 0.8.0), built withnpm run buildand run fromdist:history-moduleandhistory-module-semanticpass, 79 of 79.6f8a326ca2It puts
messageIndexText's doc comment back tomain's sentence, and changes nothing else:git diff 6484deafbf 6f8a326ca2is one line ofsrc/modules/history/semantic.ts, that comment.6f8a326ca2, installed withnpm install(chronicle 0.5.1, context-manager 0.24.2, membrane 0.8.0), built withnpm run buildand run fromdist:history-moduleandhistory-module-semanticpass, 79 of 79.Not verified
57808fc034's tree merged withmainappended 3,000 messages to a realMessageStoreand read them back withqueryByTime: 2,775 adjacent pairs shared a millisecond, the largest tie held 23 messages, and none came back out of append order.prose-draftstests call tools of telegram-mcpl's names on the test server, and the history tests read stored calls of those names and ofsayandwhisper.src/modules/history/semantic.ts:830sets the start of its walk to its watermark lessoverlapMs, 600,000 ms by default. After a switch ofnamespace, by reading,syncCursors(:515) reads no cursors kept for another namespace, so the watermark falls back to the namespace's own stats (:657): one that already holds messages walks on from its own newest indexed message, less the overlap, and one with nothing indexed walks from the first message. Plain search reads the stored calls when it runs, as its tests do with calls already in the store.code-executiontests of a script holding its round run in locus routing, so no test in this change shows it under hybrid routing. A probe outside this change ran the same check in hybrid routing, built fromdistin the copies the table's runs used, wherenpm installreported them up to date. On an earlier version of this change, whosesrcdiffers from the first round's head's only in two doc comments, a script whose code namestelegram--reply_to_message,discord--send_messageorworld--sayheld its round until it returned. On84e0408977'ssrc, thesend_messageandsayscripts held it and thereply_to_messageone didn't.3cb8a4f278merged withmain, above.