Skip to content

Refuse connector sends before maintenance-held locks - #1999

Merged
trissim merged 5 commits into
mainfrom
fix/connector-maintenance-refusal
Oct 19, 2026
Merged

trissim merged 5 commits into
mainfrom
fix/connector-maintenance-refusal

Conversation

@trissim

@trissim trissim commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Connector maintenance refusal

Owner: pi-install-gate. Direct maintenance order 2932, resumed after the main install. P255's recorded-check interpreter correction remains separate with claude-code.

Head: 354b3aeec7e3375d27325a23de10b053aa19e80a, including main b793c7325c99c3a0f721df0ac85978daa3eb2763.

Where the request waited

Connector.opened_wire entered client_wire, which constructed Comms without asking the maintenance barrier. Registration.__init__ at src/agent_comms/registration.py:72 immediately called self.store.read(), so an install holding the registry could park the request there. A request reaching Messaging.send also waited on the wire lock at src/agent_comms/messaging.py:57, before its existing maintenance check.

The HTTP boundary correctly catches ToolRefusedError; MaintenanceHeldError failed to declare that it belongs to this family. The initial attempted fix enumerated both errors in the connector, which was wrong (MEMB-2). That tuple, union annotation and connector import are deleted. MaintenanceHeldError(RelationViolationError, ToolRefusedError) now declares the fact once.

Owner and consumers

  • client_wire now calls the existing barrier's assert_open_unlocked before borrowing the default route or constructing Comms. Thus connector, CLI and other one-shot consumers share the early check rather than repairing just one send handler.
  • This is a negative check only. An unlocked read never permits a write; determining checks remain under the original operation locks. An incomplete phase change still refuses the read.
  • The connector now has no production diff against main. Its existing single ToolRefusedError catch and parameter annotation handle the subclass automatically. Runtime C3 is MaintenanceHeldError -> RelationViolationError -> ToolRefusedError -> ValueError -> Exception -> BaseException -> object; existing relation-error consumers remain valid. CoreError still produces the exact held response. No new status mapping, maintenance state, timeout, retry, worker or queue is introduced.
  • Exact search rg -n 'Maintenance holds ingress' src/agent_comms finds the refusal text only at maintenance_barrier.py:51; the phase family still owns it.

Before/after source inventories used the existing refactor-audit Package parser: all 381 Core modules parsed, zero omissions. Original declarations, imports, inheritance, writes, checks and executable consumers were read across connector, composition root, maintenance barrier, registry/store locking, messaging and protocol owners. Existing callbacks and dynamic MCP dispatch are not claimed statically resolved. Private before/after site receipts remain local. No new class or alternate decoder is added.

Actual connector proof on the pushed head

tests/verify_connector_maintenance.py starts the installed connector entrypoint and interpreter with this checkout's Core source selected for an isolated loopback server. This is a candidate-source proof, not an update of the installed live connector.

The script seeds a real private protocol bus, copies it, starts the real authenticated server, advances the copy to Installing, then holds its wire and registry locks exclusively throughout one curl comms_send:

  • HTTP 200, tool isError=true.
  • Core error type held: Maintenance holds ingress: the bus is under maintenance.
  • curl: 0.065158 seconds; complete subprocess wall time: 0.074923 seconds. Both are checked against the requested 0.1-second limit.
  • Original and copied bus bytes unchanged; original remains outside maintenance.
  • Private auth, copied bus and server are retired after the proof. No provider/agent is launched and no user session or live bus is changed.

Three focused HTTP cases also pass on the final head: Draining, Paused and Installing, additionally holding the default-route directory exclusively. These cover both an explicit independent root and a selected default root.

Checks

  • src ratchet 0, tools ratchet 0, no key where head > base, on the exact head above against main b793c73.
  • Existing Src-Budget: 8 mechanism covers the six net added production lines. No checker/baseline changes, suppressions, casts or Any annotations.
  • Current connector/barrier batch: 49 passed, 1 deselected, including all three maintenance phase cases.
  • Earlier broader connector/barrier/watch batch: 53 passed, 2 failed, 1 deselected. Both failures were separately reproduced with unchanged main source: the route-writer watch test times out waiting for its obsolete acquisition hook; the carry-watch fixture cannot import cutover.backup in its child. The deselected older proof writes another owner's scratch directory; the new real HTTP proof above ran instead.
  • Checks use the authorized shared Core Python and the existing pinned audit scripts read-only, without cloning, syncing or creating an environment. Final checks are serial and do not enable the repository's global coverage/parallel defaults.

Storage and remaining delivery

The attempted nested dependency worktree violated the path rule and was refused. The dependency checkout is now in the permitted P2 path, pinned to the same existing main submodule object. The two owned checkouts plus sole scratch directory total about 100 MiB, well below the 3 GB cap. P2 remains needed for these checks; pi-cn-phase3 reported that its read-only borrowing has ended. Retirement must preserve actual borrowers and the required checkout creation order.

Not merged or installed by this work. This remains draft, with install-rehearsal failed: full two-machine practice-install acceptance is still incomplete, and the 3 GB cap forbids the approximately 26 GiB desktop copy. Only commit outcomes are published; private copies and logs remain local.

Assumed: no installation, restart, environment modification or large rehearsal is authorized. P255's correction is not a dependency of this connector repair.

@trissim trissim changed the title Refuse connector calls immediately while maintenance holds the bus Refuse connector sends before maintenance-held locks Oct 19, 2026
MaintenanceHeldError composes relation and tool refusal behavior; connector consumers catch the one owning family. Remove the explicit exception tuple and union. Tighten real copied-bus proof to 0.1 seconds.
@trissim
trissim merged commit 5b1debe into main Oct 19, 2026
1 of 4 checks passed
trissim added a commit that referenced this pull request Oct 19, 2026
Integrate #2010 #1999 #2011: refused sends answer at once; rotate stops crashing
@trissim
trissim deleted the fix/connector-maintenance-refusal branch October 20, 2026 01:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant