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:
@@ -209,6 +209,32 @@ def mock_http():
|
||||
httpx.AsyncClient = original
|
||||
|
||||
|
||||
def control_named(html: str, name: str) -> dict[str, str]:
|
||||
"""The attributes of the one element carrying `name="…"`.
|
||||
|
||||
Exists so a test can ask "does the control that carries the name also carry
|
||||
the verb?". Two selects in the composer once delegated their `hx-patch` to
|
||||
an empty sibling form through the `form=` attribute, which scopes values but
|
||||
routes no events -- htmx binds a trigger to the annotated element, and
|
||||
`change` reaches ancestors, never siblings. Both controls were decorative
|
||||
for a whole release, and the tests passed the entire time because they
|
||||
asserted the markup that was there rather than the property that mattered.
|
||||
"""
|
||||
from html.parser import HTMLParser
|
||||
|
||||
found: list[dict[str, str]] = []
|
||||
|
||||
class Finder(HTMLParser):
|
||||
def handle_starttag(self, tag: str, attrs: list[tuple[str, str | None]]) -> None:
|
||||
got = {key: (value or "") for key, value in attrs}
|
||||
if got.get("name") == name:
|
||||
found.append(got)
|
||||
|
||||
Finder().feed(html)
|
||||
assert len(found) == 1, f"expected one element named {name!r}, found {len(found)}"
|
||||
return found[0]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def user_id(db: Session, registered: dict[str, str]) -> str:
|
||||
"""The registered user's id.
|
||||
|
||||
Reference in New Issue
Block a user