Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
3a2b4c3
Limit native watcher failure delivery to its read and decode contract
trissim Oct 23, 2026
6ec31d9
Record backend capability ownership and complete source boundary
trissim Oct 23, 2026
29d106e
Exercise native descriptor failure delivery through the real watcher
trissim Oct 23, 2026
d8559df
Inherit thread backend contracts and derive default selection
trissim Oct 23, 2026
55edba7
Share typed one-field encoding with JSON shape families
trissim Oct 23, 2026
9215d55
Remove remaining physical bus locks through existing state owners
trissim Oct 23, 2026
85643b9
Read executable physical bus references in deletion phase evidence
trissim Oct 23, 2026
0d76980
Exercise backend declaration lifecycle without mutating sealed state
trissim Oct 23, 2026
194957e
Separate historical cursor contention incident from current owner path
trissim Oct 23, 2026
e92b742
Keep independent Pi event ABI coverage in roster plan
trissim Oct 23, 2026
d3d5769
Judge roster phase by internal declarations and retain producer ABI
trissim Oct 23, 2026
98a2305
Keep committed-tree grep within its repository owner
trissim Oct 23, 2026
b29df73
Register CLI argument shapes through existing wire metaclass
trissim Oct 23, 2026
c921aba
Use existing registry lifecycle for birthless identities
trissim Oct 23, 2026
067532a
Exercise birth marker decoding and declaration lifecycle
trissim Oct 23, 2026
8ce13fc
Merge commit '0d76980578eaacc3d93f16e0197edab2f8c1ffeb' into integrat…
trissim Oct 23, 2026
4f19c69
Merge accepted CLI declarations and reconcile roster plan receipts
trissim Oct 23, 2026
fe0507e
Merge commit '98a2305f119de92ba64a3f3220031fc33bf17c70' into integrat…
trissim Oct 23, 2026
534cb07
Merge accepted bus deletion with current row ownership and phase proofs
trissim Oct 23, 2026
8dd402e
Merge commit '534cb07ccb86af072f660f01290f90ed59c645ee' into next/bir…
trissim Oct 23, 2026
a785a21
Derive path evidence and consume the sibling registry typing fix
trissim Oct 23, 2026
eca97aa
Bind phase evidence to declarations and separate the import layers
trissim Oct 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/debt-ratchet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ jobs:
python-version-file: pyproject.toml

- name: Install agent-comms
run: python -m pip install -e '.[dev]'
run: |
python -m pip install uv==0.12.0
uv sync --locked --all-extras
echo "$GITHUB_WORKSPACE/.venv/bin" >> "$GITHUB_PATH"

- name: No new structural debt against the base
env:
Expand Down
82 changes: 78 additions & 4 deletions plans/REMAINING-SLOP-PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,87 @@ Three audits, one per catalog rung group, each finding read in the code and chec
- `RecoveryCondition` matching a lifecycle by `declared_name` string equality across two families (`recovery_states.py:21-26`): `issubclass`.

**6.4 Rosters and registries beside families (MEMB-1/2/4, about 120).**
- Five hand-rolled `__init_subclass__` registries: `Carry.declared` (`installer/carry.py:45-54`), `ThreadRename.kinds` (`thread_management.py:557-567`, gone with 4.1), `BirthlessIncarnation.members` (`thread_identity.py:185-192`), `CliToolArguments.types` (`tool_calls.py:89-95`), `_start_phases`/`_end_events` (`pi_events.py:59-93`): `DeclaredFamily` or attributes through the MRO.
- `PI_0_85_1_EVENT_KINDS`, 36 names re-typed beside `NativePiEvent.known_kinds()` (`pi_native_backend.py:63-81`, check at `agent_backend.py:428`): derive the union; the equality check goes.
- `THREAD_CAPABILITIES` plus `serves_threads`/`serves_new_threads` flags plus `validate_backends` probing with `getattr` (`agent_backend.py:358-466`): `ThreadServingBackend(AgentBackend, Steers, Compacts)` and `NewThreadBackend`; ABC instantiation enforces the roles.

Backend capability slice (#2623, integration base `acb2ddd0e`, 2026-10-23):
deleted `THREAD_CAPABILITIES`, both serving flags, `validate_backends` and the
old default-selection method. `ThreadServingBackend` inherits `Steers` and
`Compacts`; both record default factories invoke `NewThreadBackend.selected`.
Capability bases precede `AgentBackend`, whose concrete refusal method would
otherwise hide the abstract turn obligation. Sealing attaches only concrete
locations after validating unique ownership. An incomplete declaration cannot
rebind a location. `NoSessionFile` constructs the shared `UnstartedRuntime`;
both copied backend `unstarted_type` declarations are deleted.
NRA census covers 1,528 Core/hooks/tools/tests and paired Toad modules:
6,973 original classes, 6,752 projected classes, zero parse omissions; the
221 unprojected originals remain visible. Source evidence is not a behavior
proof. Afterward: 1,529 modules, 6,974 original/6,753 projected classes, zero
parse omissions; Core remains 953 modules/5,341 original classes. The extra
dependency module/class came from the concurrently edited Toad source tree.
The before/after receipts remain in the owned backend-capability-rosters
scratch directory. Focused source checks pass 27 cases (25.69s), including
fresh CLI/UI imports, typed native/durable locations, ABC refusal without
location mutation, worker backend reuse and a real durable host reading 260
saved rows while its SQLite writer stays open. The initial 24-pass/3-failure
run exposed an untyped session-file fixture, a stale caller roster and missing
local Node dependencies; all three were corrected, without counting that run
as green. Installed copied-agent DM/reply/tab-return qualification belongs to
the later paired integration; this branch has not changed the live runtime.
Review corrected the declaration tests: they now re-execute the actual backend
declaration module through standard `runpy.run_path` in a fresh namespace,
declare real nominal subclasses and invoke the actual seal lifecycle. They do
not monkeypatch production sealing, registry contents or backend locations.
The abstract-class exclusion/no-rebind, competing-location and zero/two-default
cases pass (3 cases, 0.11s); fresh-entrypoint import checks also pass. Production
is unchanged from the 27-case qualification; the class census above describes
that implementation checkpoint before the test-fixture correction.
The independent Pi producer/converter event comparison is retained;
deriving expected producer events from the converters would erase its check.
This slice does not close `RostersPhase` or edit its evidence declaration.

- Four remaining hand-rolled `__init_subclass__` registration owners: `Carry.declared` (`installer/carry.py:42-58`), `ThreadRename.kinds` (`thread_management.py`; still present, not removed by 4.1 at this integration base), `BirthlessIncarnation.members` (`thread_identity.py:214-226`), and `_start_phases`/`_end_events` (`pi_events.py:59-93`). Move registration through the existing nominal mechanisms or derive behavior through the MRO; preserve each boundary's actual keys and consumers. `CliToolArguments.types` and its private hook are removed by the CLI argument slice below.
- Preserve the independent Pi producer ABI contract: `PI_0_85_1_EVENT_KINDS` in `pi_native_backend.py` declares the pinned runtime's emitted kinds, while `NativePiEvent.known_kinds()` declares converter coverage. `AgentBackend.require_event_family` compares these independent answers and reports missing or extra converters. Deriving the expected producer kinds from converters would erase this check. Refactor the internal `_start_phases`/`_end_events` registration lifecycle without weakening producer/converter coverage; removal of the external manifest is not a completion condition.
- Backend roles: removed by #2623 through `ThreadServingBackend(Steers, Compacts, AgentBackend)` and declaration-derived `NewThreadBackend.selected`; abstract methods enforce thread obligations. `PiDurableBackend` remains a non-thread backend.
- Display names re-typed from class names in about 40 places (`tools.py` labels, `notification_progress.py:45-157`, `todos.py:1502/2012/2088/2605/2675`, `display_order.py:76-136`, `relationships.py:382/397`, `turn_phase.py:169/218`, `config_options.py:192-194`, `input_attempt.py:400/440/447` `public_status`): derived from the declared name, literal only where the text differs.
- Sigils `#`/`@` spelled by hand at about 22 sites (`catalog_document.py:61-232`, `channels.py`, `channel_targets.py`, `goal_presentation.py:71-72`, `goal_actions.py:305/318`, …): owned by `Tag.channel` and `ThreadTarget.mention`.
- Store and file names restated outside their class: `root / "wire"` in about 20 places across 17 modules (the virtual bus plan's done-grep for `_wire_lock_path` misses every `_store_lock(root / "wire")`; its grep gains that pattern), `~/.local/state/agent-comms` six times, bridge inbox dirs, `"registry.json"` at `installer/upgrade.py:566`, `".registry-owner-guard"` at `goal_history.py:208`, `active-route.json` five times, Pi `auth.json` in four modules: `X.path_in(root)` and one state-home owner.

CLI argument slice (#2626, base `acb2ddd0e`, 2026-10-23): the existing
`RegisteredWireMeta` now owns argument-model registration after Pydantic/ABC
construction. Each concrete declaration supplies its genuine external
`cli_names` tuple as the registry key; decode derives matching shapes from
that registry without retaining another alias map. Both production consumers,
Claude `ToolUse.call` and OpenCode `ToolState.call`, keep the shared decode
entrypoint. The seven shapes retain all 13 case-sensitive external spellings,
extra input, canonical previews and edit patches. Unknown names and invalid
arguments remain the original call; validation refusal belongs to its runtime.
Abstract specializations cannot overwrite concrete aliases. The existing
registry rejects duplicate tuples at declaration; overlapping nonidentical
tuples raise an explicit ambiguity at decode, before argument validation can
turn the conflict into an ordinary refused call. Overlap is not checked at
declaration. A new shape still needs only its class and alias declaration;
the improvement is shared declaration lifecycle and collision behavior, not
a claimed reduction from multiple authored rosters.

NRA before/after source receipts cover Core/hooks/tests/tools, paired Toad and
the installed metaclass-registry/Pydantic source: 1,643/1,644 modules,
7,434/7,438 original classes, 7,144 projected classes in each, zero parse
omissions. The 290/294 unprojected originals remain explicit; the four added
classes are function-local declaration controls. Dynamic source projection
does not prove their runtime behavior. The compact index also leaves the
`AutoRegisterMeta` package re-export unresolved as a base of
`RegisteredWireMeta`; source inspection and a fresh-process runtime MRO read
confirm its composition with `ModelMetaclass`. Focused source checks pass 31 cases
(0.63s), exercising real Claude/OpenCode models, edit presentation and actual
fresh declaration lifecycles without patching registry state. The first run
passed 26 and failed two new assertions that incorrectly put call identity on
search preview records; identity is checked on its owning `ToolCall` and
emitted transcript instead. Receipts remain in the owned
`cli-tool-argument-family` scratch directory. Fresh CLI/UI-model import checks
also pass (two cases, 3.73s). This slice is a published draft,
not merged, installed or live-qualified. Shared phase proof belongs to its bus
owner; the other registries and the remaining name/sigil/path work keep 6.4
open.

### Wave 7: records where parameter lists and dicts stand in

- `ManagedLaunchRequest`: nine parameters declared in seven signatures and forwarded six times (`native_child.py:222/341`, `native_pi.py:491/536`, `native_session_prepare.py:67/88`, `backend.py:112`, `stream_agent_events` has 23), with one lock-launch-fence procedure written twice. About 60.
Expand Down Expand Up @@ -226,7 +300,7 @@ Waves 4 to 8 were written on 2026-10-21 and declared no phase in `plan_phases.py
| wire_renames | 4.6 | no `WireName("admission…` in src | one unit, Toad in lockstep |
| owned_dispatch | 5.4 | no `TurnInboxReceiver`, no hand `separators=(",", ":")` | one unit |
| kind_families | 6.3 | no `TagAction`, `ExecutionOrigin`, `WhenBusy`, `ControlClassification`, `MembershipChange` or `ViewKind` enum classes | by file |
| rosters | 6.4 | no `PI_0_85_1_EVENT_KINDS`, `THREAD_CAPABILITIES` or `serves_new_threads` | one unit |
| rosters | 6.4 | internal registries and capability/name/path rosters derive from their owners; retain independent Pi producer/converter coverage | by family; shared phase proof has one owner |
| records | 7 | no `ConnectivityFacet`/`observe_connectivity`; no `nonlocal` in the five document updaters | one unit |

9.4 is Tristan's ruleset change. 9.5, 9.6, 9.9, 9.10 and 9.11 are claude-ai's and are measured by their own numbers in this section: a touched-tests PR check, the full ratchet list in the job summary, commander calls under 3 s, one GitHub reader, and one stack lock. The order across plans stays Tristan's: the virtual bus batch first, then multiple inheritance, the commander-native plan, then this plan. Phases with no stored data run in parallel with all of them on disjoint files.
Expand Down
35 changes: 33 additions & 2 deletions plans/VIRTUAL-BUS-PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,41 @@ Everything lands as one batch through the merge queue, drop-on-fail. Then one in

Each phase, its predecessors and the evidence on `origin/main` that proves it done are declared in `src/agent_comms/plan_phases.py`; the commander's plan-idle rule and order refusals read that family, and accepting a task never marks a phase done.

- `git grep -n "offset\|max_bytes\|bus.jsonl\|bus_meta\|_wire_lock_path\|has_private_wire_fields\|/ \"wire\"" src` returns no bus hits (the last pattern catches the hand-spelled `root / "wire"` lock path at about 20 sites).
- `BusDeletionPhase.evidence` finds the five deleted modules absent and no known physical bus symbols, filename literals or lock-path expressions in Python source. Its AST check reads executable uses, including `/ "wire"`, `.with_name("wire")` and `.joinpath("wire")`, without matching prose or its own pattern declaration. This is a known-syntax screen; aliases and dynamically constructed paths still require source review. Remaining byte offsets and budgets are judged by their actual owner, since external native transcripts stay files.
- The deleted modules are gone.
- A process killed mid-publish leaves the whole message or none of it.
- A reader that meets a held bus lock waits for it and never ends a turn (step C). Today `NativeSourceCursor._advance_proven` (`native_source_cursor.py:83`) takes the boundary with `blocking=False`, and with no contention allowance when no input is committed (`:79-80`). So any concurrent writer ends the turn with `BlockingIOError`: three times on core-5d8da75aa between 12:20 and 12:39 on 2026-10-21, with no install running.
- Concurrent bus publication must not abort native cursor settlement. The current path is `CoordinatedTurn.cursor_status_for` → `CursorDelivery.advance` → `CursorOwner.advance_async` → `CursorOwner._advance_owned`. Bus reads use `Bus.observing` and SQLite snapshots; the cursor owner commits through the coordinator session's existing write transaction. `Coordination.run_async` runs this work through its worker with the default `StoreWait` contention policy. Qualify that path under competing writers within its bounded wait; no physical bus lock is reintroduced.
- Every history converts with every record round-tripping.
- On every machine: Toad pages history, delivery wakes units, claims hold, and a real message to a live agent is answered.
- Lines deleted are reported first; the census confirms.

Historical regression evidence: on core-5d8da75aa, the former
`NativeSourceCursor._advance_proven` in `native_source_cursor.py` took its
boundary with `blocking=False`, without contention allowance when no input
was committed. Concurrent writers ended turns with `BlockingIOError` three
times between 12:20 and 12:39 on 2026-10-21, with no install running. That
class and file have since been deleted; the incident motivates the current
contention obligation above, rather than describing a current source site.

## Deletion checkpoint from the current integration branch

At `acb2ddd0e`, all five old modules are already absent. Three physical bus locks
remain: `NativeReplyPreparation.finished`, `SendRefusal.record`, and
`CollaborationLedger.merge`. The last uses `.with_name("wire")`, which the old
grep misses. The old grep also matches its own `bus_meta` pattern declaration,
so its result does not establish whether physical bus uses remain.

The deletion branch removes those locks through the existing owners. Unread
publication keeps its local preparation lock, source revision check and SQLite
write transaction; a busy SQLite writer leaves completed readers pending.
Refusal recording keeps maintenance permission under the registry lock until
its SQL transaction finishes. Ledger merge keeps the registered-author snapshot
through the existing document update, in the declared registry-before-ledger
lock order. None creates a replacement bus lock.

The phase check reads committed source through `MainTree`. Its controls include
the actual remaining path spellings, physical filenames and removed symbols,
alongside comments and docstrings that describe deletion. This source checkpoint
does not mark the phase complete on main or qualify an installed runtime. The
merger still checks the affected copied native/Toad journey, including a fresh
DM, its actual reply and tab return.
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,6 @@ extend-immutable-calls = ["agent_comms.store_files.ExclusiveLock"]
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true

[tool.uv.sources]
metaclass-registry = { git = "https://github.com/OpenHCSDev/metaclass-registry", branch = "main" }
Loading
Loading