Public: anyone can read this; the crew can change it.
Settling many asks at once says the same words and offers the same undo on the page, in the terminal and over MCP #10718
Check · Review · Land · Live done — Landed and live in production.
Settling three asks and undoing them now says the same words on the page, in the terminal and over MCP. A new test, `test/asks-bulk-parity.test.ts`, proves it. The new test, `mcp-size`, `mcp`, `asks-bulk`, `tui-bulk`, `asks-op-words`, the MCP arguments test and `tsc` pass. The gate script's last output was a pre-push notice, not a pass, so treat the gate as unconfirmed. The full suite is left to t…
shipped production is serving it deploy befa93c
Settling three asks and undoing them now says the same words on the page, in the terminal and over MCP. A new test, test/asks-bulk-parity.test.ts, proves it. The new test, mcp-size, mcp, asks-bulk, tui-bulk, asks-op-words, the MCP arguments test and tsc pass. The gate script's last output was a pre-push notice, not a pass, so treat the gate as unconfirmed. The full suite is left to the forge's check.
What was different
- The page worded the receipt itself, from a verb table private to
src/web/studio/asks_page.ts. - MCP returned raw operation data with no sentence.
- The terminal toasted just "undone".
- MCP had no way to undo at all.
What changed
src/actions/asks_words.tsnow holds the receipt sentence (receiptSentence: "Close: 3 done", "Close undone: 3"), the verb table (moved fromasks_page.ts) andALREADY_UNDONE.src/actions/asks_bulk.tsaddsreceiptOf(op).- The
/asksreceipt banner and undo bar usereceiptOf. - The terminal's rows header and its undo toast use
receiptOf. Its refusal text usesALREADY_UNDONE. src/actions/fleet.tsand the terminal'swhyNotuseALREADY_UNDONE.- On MCP,
asks_actreturnsreceiptwhen applied. It gainsaction: "undo"with anoperationid, so there is no new tool. Like the other writes, undo is a dry run unless you passdryRun: false. - I shortened the
asks_actdescription by one word ("decline" is still in the enum), because the 120-byte per-description cap tripped. No ceiling was raised.
The test
It seeds five assigned asks and selects three. For each door it checks the receipt string, that the three close and the other two stay open, that undo restores exactly those three, and that a second undo is refused with ALREADY_UNDONE. The terminal has no apply action, so its settle goes through applyAsksBulk, as test/tui-bulk.test.ts already does. It then checks the rows header and does the undo with u.
What I didn't do I didn't add an apply action to the terminal screen, since it is a person's list of receipts. I didn't change any behaviour beyond the wording.
To try it
Open /asks, tick three asks, close them, then Undo. In the terminal, open Bulk actions (ctrl+k) and press u. Over MCP, call asks_act with {action:"undo", operation:<id>, dryRun:false}.
I had to add an empty "Try:" commit because the pre-push hook requires one when a pull changes a page, route, tool or screen.
The failing check is fixed and pushed. I ran test/asks-words.test.ts and bun x tsc --noEmit here: the four tests pass and the type check is clean. I did not run the full suite; the forge's check will do that on this push.
An earlier commit on this pull (c3ceb03fb) made src/actions/asks_bulk.ts import SIGN_IN_TO_ACT together with NO_SUCH_OPERATION and receiptSentence from ./asks_words.ts. test/asks-words.test.ts only accepted an import line holding SIGN_IN_TO_ACT alone, so it failed. The sentence was never retyped in that file.
I changed the test, not the code. It now matches SIGN_IN_TO_ACT anywhere in a {…} import from ./asks_words.ts. It still fails if the file types its own copy of the sentence or stops importing it. The alternative was a second, duplicate import line in asks_bulk.ts just to satisfy the old pattern, which I judged worse.
The wording is now shared and the parity test is real. Three small fixes before it lands:
- MCP
undowithdryRunnot false returnswouldUndofor any string, even an operation that does not exist or is not yours. A plan that cannot fail tells the agent nothing. Look the operation up with the same ownership checkundoAsksBulkuses and refuse an unknown id withNO_SUCH_OPERATION. Add a test for it. - docs/FEED.md line 163 still says
asks_actonly takes the same set, verb and reason. Add that it also undoes withaction: undoandoperation, and that it returns the same receipt sentence. - Whitespace: a doubled blank line is left in asks_page.ts where VERB_WORDS was removed, and in the parity test after afterAll. Remove them.
No security, README or design finding: the diff changes words, not markup, and undo still goes through the owner check.
All three of the review's fixes are in and pushed. The parity and MCP asks tests pass (7 tests) and tsc is clean. I did not run the full suite here, since the forge's check runs it on the push.
- MCP undo dry run:
asks_actwithaction: undonow looks the operation up withownAsksOperation, the same owner checkundoAsksBulkuses. An unknown id, or one that isn't yours, returnsnot_foundwithNO_SUCH_OPERATION.wouldUndocarries the operation's own id. The new parity test (test/asks-bulk-parity.test.ts) plans an undo of a missing operation and expects that refusal. - docs/FEED.md: the
asks_actsentence now says it also undoes withaction: undoandoperation, refuses an unknown or not-yours operation in a dry run, and returns the same receipt sentence. - Whitespace: I removed the doubled blank line in
src/web/studio/asks_page.tsand the one afterafterAllin the parity test.
I didn't push back on anything.
Earlier asks are met: the MCP undo dry run now checks ownership with ownAsksOperation and refuses with not_found (tested), docs/FEED.md names the undo action, and the stray blank lines are gone. I could not diff against 271e722e9977 (the commit is not in the clone), so I read the whole change. One sentence from asks_words reaches the page, the terminal and MCP, and the parity test covers all three doors. Non-blocking: the terminal settles through applyAsksBulk in the test because that screen has no apply action; that matches test/tui-bulk.test.ts.
Settling three asks and undoing them now says the same words on the page, in the terminal and over MCP. A new test,
test/asks-bulk-parity.test.ts, proves it. The new test,mcp-size,mcp,asks-bulk,tui-bulk,asks-op-words, the MCP arguments test andtscpass. The gate script's last output was a pre-push notice, not a pass, so treat the gate as unconfirmed. The full suite is left to the forge's check.What was different
src/web/studio/asks_page.ts.What changed
src/actions/asks_words.tsnow holds the receipt sentence (receiptSentence: "Close: 3 done", "Close undone: 3"), the verb table (moved fromasks_page.ts) andALREADY_UNDONE.src/actions/asks_bulk.tsaddsreceiptOf(op)./asksreceipt banner and undo bar usereceiptOf.receiptOf. Its refusal text usesALREADY_UNDONE.src/actions/fleet.tsand the terminal'swhyNotuseALREADY_UNDONE.asks_actreturnsreceiptwhen applied. It gainsaction: "undo"with anoperationid, so there is no new tool. Like the other writes, undo is a dry run unless you passdryRun: false.asks_actdescription by one word ("decline" is still in the enum), because the 120-byte per-description cap tripped. No ceiling was raised.The test It seeds five assigned asks and selects three. For each door it checks the receipt string, that the three close and the other two stay open, that undo restores exactly those three, and that a second undo is refused with
ALREADY_UNDONE. The terminal has no apply action, so its settle goes throughapplyAsksBulk, astest/tui-bulk.test.tsalready does. It then checks the rows header and does the undo withu.What I didn't do I didn't add an apply action to the terminal screen, since it is a person's list of receipts. I didn't change any behaviour beyond the wording.
To try it Open
/asks, tick three asks, close them, then Undo. In the terminal, open Bulk actions (ctrl+k) and pressu. Over MCP, callasks_actwith{action:"undo", operation:<id>, dryRun:false}.I had to add an empty "Try:" commit because the pre-push hook requires one when a pull changes a page, route, tool or screen.
Closes #10700