Two selects that never wrote anything, and a queue
The approval card in Auto mode and the missing /effort were one bug. Both
selects hung their hx-patch on an empty sibling form reached by form="…",
and htmx binds a trigger to the annotated element: change fires on the
select and bubbles to its ancestors, which a sibling is not. The live rows
read agent_mode=manual and params_json={} while the browser showed Auto and
Effort: high. policy.py was never involved.
The verb moves onto the control; the empty form stays as value scoping,
which is the half of the CLAUDE.md note that was right. conftest gains
control_named so a test asserts the element carrying the name carries the
verb, rather than asserting the markup that was there throughout.
The composer's highlight was a third instance of the same carelessness in
CSS: .tok-mention is written for the transcript and scoped to nothing, so
the mirror painted its token in accent-coloured monospace over the
textarea's own text. Scoped under .msg; the mirror restates transparency
and font rather than inheriting them, and bleeds by box-shadow.
/effort is now offered before the first prompt and _new_chat reads it.
/index re-walks the project directory on demand, file_write drops the
listing it just invalidated, and the index ladder falls through to SFTP on
a host that refuses exec instead of returning nothing.
A second message during a reply is queued rather than starting a second
concurrent generation: a real Message row with queued set, so it survives a
restart and can be withdrawn. _drain hands one on at the end of a reply,
_inject takes one in at a tool-round boundary so an agent can be steered
mid-task. Stop leaves the queue undelivered. The terminal's Auto toggle
becomes off/copy/send, and send posts straight to the chat without touching
the composer.
@ now offers notes, skills, this chat's attachments and a URL to fetch; a
knowledge base attaches as a reference rather than a copy. copy_document
carries provenance, which was the one attach path that dropped it.
Also fixes an unrelated live bug: the round loop compared against the
global MAX_ROUNDS of 3 while sizing itself from the agent budget of 40, so
agent replies stopped after three rounds and reported forty.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -180,6 +180,43 @@ async def test_forgetting_a_connection_drops_its_listings():
|
||||
assert index_service.cached("profile-2", "/work") is not None
|
||||
|
||||
|
||||
async def test_a_host_that_refuses_to_run_commands_still_gets_a_listing():
|
||||
"""SFTP is the rung for exactly this, and the ladder used to skip it.
|
||||
|
||||
An `ExecError` from git or find -- an SFTP-only account, a forced command,
|
||||
a shell that is `/bin/false` -- escaped the loop and was caught outside it,
|
||||
which returned an empty index without ever trying the one method that would
|
||||
have worked.
|
||||
"""
|
||||
|
||||
class _NoExec(_Fake):
|
||||
async def run(self, request):
|
||||
raise ExecError("This account may not run commands.")
|
||||
|
||||
executor = _NoExec(tree={"/work": [RemoteEntry("README.md", False, 5)]})
|
||||
|
||||
found = await index_service.build(executor, "/work")
|
||||
|
||||
assert found.source == "sftp"
|
||||
assert "README.md" in found.paths
|
||||
|
||||
|
||||
async def test_forgetting_one_tree_leaves_the_others():
|
||||
"""What a write invalidates is the directory it wrote into, not the machine.
|
||||
|
||||
Two chats on one box in different trees share nothing but the connection,
|
||||
and dropping both would make every write cost somebody else a walk.
|
||||
"""
|
||||
executor = _Fake(answers={"git ls-files": _ok("a.py\n")})
|
||||
await index_service.ensure(executor, "profile-1", "/work")
|
||||
await index_service.ensure(executor, "profile-1", "/other")
|
||||
|
||||
index_service.forget_dir("profile-1", "/work")
|
||||
|
||||
assert index_service.cached("profile-1", "/work") is None
|
||||
assert index_service.cached("profile-1", "/other") is not None
|
||||
|
||||
|
||||
def test_reading_the_cache_never_does_work():
|
||||
"""`harness` calls this synchronously while assembling the system message,
|
||||
so it must never be the thing that opens a connection."""
|
||||
|
||||
Reference in New Issue
Block a user