Repository navigation
Conversation
trissim
marked this pull request as ready for review
October 19, 2026 18:05
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
Contributor
Author
|
P233’s latest instruction now removes this status/hook feature from #1950 rather than shipping it twice. #1921 was never merged; its source remains preserved on fix/bridge-compacting. #1950 head 92fa315 is lifetime-only and stays Connected during compaction. Its PR body records this changed scope explicitly. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Claude bridge compaction
Follow-up: Monitor expiry confirmed on the live bus (EDT)
The bridge lifetime, not compaction or its 15-second probe, causes the gap.
Read-only one-second observations of the live registry and watch lock captured:
The registry release-to-registration gap is 13.656 seconds. The Monitor
expired 1800.185 seconds after its original start at 13:26:57.368. Its expiry
notice is in the original Claude transcript at line 65373; the replacement
Monitor start is line 65388 and Watching event is line 65394:
~/.claude/projects/-home-ts-code-projects-openhcs/696126c1-8112-4b77-8879-c1f14ea961ce.jsonl.The raw live samples are retained at
/home/ts/.cache/agent-scratch/pi-bridge-compact/live-expiry-20261019.jsonl;live-expiry-transition.jsonrecords the before/after cuts.For the earlier window, the same original transcript records Monitor expiry at
13:25:52.522 (line 64683), an automatic compaction boundary at
13:26:35.528 with duration 96.545 s (line 64689), replacement Monitor start
at 13:26:57.368 (line 64748), probe output at 13:26:59.844 (line 64746),
the 15-second probe notice at 13:27:14.854 (line 64751), and Watching at
13:27:17.309 (line 64758). Thus the expiry occurred during the compaction
(its inferred start is 13:24:58.983).
There is no persisted registry-status history for that earlier interval:
RegistryStore replaces the current JSON (
registry_store.py:316), rather thanjournaling status transitions. I cannot honestly give an exact 13:25 release
commit from retained registry history. The earlier gap is supported by Claude's
original events and the owning cleanup path; the later identical expiry was
observed directly in the live registry, process presence and watch lock.
The live bus was never mutated, and its session/Monitor were never stopped by
this investigation.
Proposed lifetime repair — design only, not implemented
session. SessionStart hooks idempotently start its bridge outside the hook/tool
process group; unconfigured sessions are not silently assigned a bus name.
Resume/compact reuse the same endpoint rather than restarting it.
Registration.external_process lease, transcript subscription, DeliveryRow and
ObservedPart journal. The daemon, not a Monitor tool call, owns these lifetimes.
never a newer resumed incarnation with the same session ID. The daemon also
watches that identity through the existing process lifetime facilities, so a
crash cannot leave it behind. A real 2.1.296 SessionStart(source=resume) hook,
launched with inherited CLAUDE_* cleared, supplied CLAUDE_PID=1154649 matching
its actual Claude ancestor and CLAUDE_CODE_SESSION_ID matching the hook input.
Capture and validate that process through ProcessIdentity (PID plus start
time), then use the existing launch/pidfd facilities. No bare PID, guessed
process name or SessionEnd-only cleanup. The real hook witness is retained at
hook-process-witness/witness.jsonunder this task's scratch root.event stream, not the process running the bridge. A Unix-domain connection is
the attachment lifetime. No new durable event spool or second delivery store.
and transcript observation. Pause inbox admission before accept/attempt
persistence while no reader exists; a fresh attachment must prove its own
MonitorProbe in the real transcript.
Existing emitted/UNKNOWN attempts and part observations are retained and
never automatically replayed. Reader disconnection after emission preserves
uncertainty, not a fabricated successful delivery.
the bridge's actual attachment state. Do not start another watcher, restart
on a timer, lengthen Monitor timeouts, or copy attachment state into Toad.
actual bridge/session exit releases presence to no_bridge.
Acceptance after approval: shorten a scratch Monitor's lifetime (not production
policy), let it expire during an actual compaction, and demonstrate the same
bridge PID/lease throughout; no delivery while unattached; fresh probe before
new input; exact observation/ack without replay; and cleanup on SessionEnd and
Claude process death. Also exercise resume and compact hooks for idempotence.
Real Toad check completed
Opened the real installed Toad application using candidate Core source, the
real ACP process and a private bus, in plain st on isolated Xvfb
:1947.No fake registry or manually set compaction state. A real Claude 2.1.296 manual
/compactemitted PreCompact at 13:59:36.841, the existing open sidebar rowpainted Compacting context, and SessionStart(source=compact) arrived at
14:00:00.181. The row then returned to ordinary Working activity with
backend status Connected and the same bridge PID 1130413. No no_bridge between.
The stale ordinary activity is not claimed to be a Ready indication.
Personally inspected these actual pixel captures:
/home/ts/.cache/agent-scratch/pi-bridge-compact/toad-view/compacting-row.png(13:59:47.285) and
after-compaction.png(14:00:05.975).The saved message area shows actual Context compacted notices. Private run:
branch-toad, sessionef1e646f-dcda-4020-9fc8-c203a45a14e2.A first capture missed the initial compaction; a second fresh command ran before
its standalone test bridge finished attaching, so it did not publish Compacting.
Both completed; neither input was retried. The third, separate fresh compaction
started only after verifying the bridge was attached and is the physical UI
acceptance above. This is verification of the existing status change, not an
implementation of the proposed daemon lifetime.
Assumed / still to establish before implementation
name or adopt an unrelated CLI session.
hook sources/versions must satisfy the same validated process/session
boundary; missing or unrelated identity is refused, not guessed. SessionEnd
alone is not crash cleanup.
13:56–13:57 registry transition was directly observed.
The report has been moved here from
docs/bridge-compaction.md, which is removedfrom the PR. No lifetime implementation, merge, installation or restart-queue
write has been made.
Initial compaction-status implementation and source evidence
PR: #1921 (do not merge).
Owner: pi-bridge-compact. Base:
2be439d1e.Result and remaining gap
Real Claude Code
/compact, with its own Monitor watching a private Core bus,now reports
connected -> compacting -> connected. The same bridge PID andregistration survive.
CompactingThreadStatusowns the working presentation⌛ Compacting context; Toad has no new code or summary-string checks.Direct compaction alone did not drop main's bridge: main stayed connected
through real compactions with both Claude 2.1.303 and pinned 2.1.296. The later
live observation above identifies the missing ingredient: the Monitor's actual
30-minute expiry. The proposed daemon-lifetime repair has not yet been built.
Cause traced in source
cli_bridges/shared.py:289-298: the watch registers its endpoint for its tasklifetime.
registration.py:86-105clears that exact endpoint toNoProcessand
NoBridgeThreadStatuswhen the watch context exits.cli_bridges/shared.py:520-534: an unobserved probe prints its 15-secondnotice and continues waiting. It neither drops registration nor exits.
A real standalone-watch run reproduced that notice while every CLI sample
remained connected. Changing that timeout would not fix the reported state.
thread_execution.py:165-169: registration previously replaced every activeexternal status with plain Connected, so adding a compaction subclass alone
would lose it on re-registration. It now takes
requested.with_bridge();connected states preserve themselves, other active statuses become Connected.
manual_compaction_bridge.py:71-138drives native Pi/ACP compaction. It doesnot observe Claude's external session and must not own this lifecycle.
Ownership change
agent_comms_hooks/compaction.py:10-50declares PreCompact andSessionStart(matcher=compact) hooks through the existing installer discovery.
The first enters Compacting; the latter is Claude's first explicit
post-compaction observation and restores Connected, including a manual command
with no subsequent model response. Neither unclaimed nor lapsed watches change
presence. No timeout, thread-name special case, second status store, or UI guess.
WatchClaimremains the session-to-live-watch binding. Its claim now livesinside the registered endpoint lifetime (
cli_bridges/shared.py:289-298), soit cannot represent a process still waiting to register. A compaction observation
captures that endpoint, rechecks the live watch, and calls Registration's
transaction fenced by incarnation, owner generation and exact process identity
(
registration.py:107-117). A delayed observation cannot revive a stopped orreplaced bridge. The hook path for ordinary prompts/tools remains stdlib-only.
Before editing, NRA's
--codemod-source-indexindexed 21 bridge/status/hookmodules, 362 classes and their methods. The expanded after index covers 27
modules/415 classes. A follow-through using NRA's existing
ModuleSyntaxIndexprojects classes, imports, assignments, decisions, comparisons and calls:
4,817 baseline sites and 4,883 changed sites, plus 3,253 sites in seven codec,
process, CLI and Toad consumer modules. Zero parse errors; the new compaction
module is naturally absent from the baseline. This is a bounded source census,
not a whole-repository audit or proof of dynamic plugin binding.
The complete source evidence remains under the scratch owner below as
source-before.json,source-after.json,syntax-before.json,syntax-after.json, andsyntax-consumers.json. Dynamic metaclass discoveryuses the existing DeclaredFamily/FieldCodec mechanism; supporting family
round-trip tests cover the new member. Toad consumers continue taking the
original ThreadPresentation/status objects (BOUND-8); no second interpretation.
Real proof
Command (main and branch were separate fresh sessions, never retries):
All CLAUDE_, CLAUDECODE, PI_ and inherited AGENT_COMMS_* variables are cleared;
only the private Core root/source binding is supplied. Existing configured
Claude authentication/provider/model is used, not a replacement provider.
MCP is disabled for the scratch session. Claude itself starts Monitor with
timeout_ms=1800000, runs a real/compact, emits its actual hooks, saves areal compact boundary and reports
compact_result=success. A logging shimpasses those actual hook inputs to the branch's hook functions. No fabricated
hook inputs, fake registry or synthetic compaction response.
The installed
agent-commsCLI's actualmain(['--root', ROOT, 'threads'])entrypoint is called once per second in a warmed interpreter: cold process
startup on this machine takes several seconds and would falsify that sampling
interval. Each full returned JSON row and timestamp is retained in
main-296/statuses.jsonlandbranch-296/statuses.jsonl. Both exit 0.Bridge-owned events, from the real saved Claude transcript, not inferred status:
Main transcript:
~/.claude/projects/-home-ts--cache-agent-scratch-pi-bridge-compact-main-296/dd9feb64-e430-4ef6-aa69-8070c86674e7.jsonl.Branch transcript:
~/.claude/projects/-home-ts--cache-agent-scratch-pi-bridge-compact-branch-296/a3fbae1a-2334-490b-bfaa-97a6c1dbd0fc.jsonl.The main transcript's actual probe/watching events are at lines 22 and 28;
the branch transcript's are at lines 35 and 52. The compact boundaries are
retained there; actual hook records are in each run's
hooks.jsonl.Actual hook times (UTC Unix seconds):
CLI samples, one row per second after initial registration (the initial
no_bridgesamples are before the watch attaches, not a compaction gap):The first branch compacting sample is 1792432141.407; its first connected
sample afterward is 1792432154.488. Hook subprocess imports/publication add
roughly two seconds on this machine. No claim of instantaneous hook processing.
No dropped/changed bridge PID during either compaction; no actual Toad UI run
or global installation is claimed.
Checks
All commands use
timeout 300 ... python -X faulthandler.pytest -n0 --no-cov tests/test_thread_status.py tests/test_watch_guard.py tests/test_registration_process_identity.py.now-required transcript
position, and one staged upgrade fixture cannotimport
cutover.backup. Rechecked those two complete files against unchangedmain: the identical 5 failed, 15 passed. Reported fixture repairs to their
existing owners pi-cn-p3 and ci-1370; no weakening/excluding assertions.
pi-cn-p3 subsequently reported the five direct Monitor inputs repaired in
PR Select Claude subagents and retain complete imported context #1917, with 45 reader/Monitor checks passing. The required position
predates that reader PR. Those fixture changes are not on this branch.
and also failed its whole-project 85% coverage threshold on this focused set.
Subsequent checks explicitly use
-n0 --no-covand persistent scratch basetemp.git diff --checkclean. No merge, runtime publication or restart-queue write.Assumed / unresolved
registry release/re-registration were subsequently observed. Direct compact
alone does not reproduce the cause.
pinned-version runs demonstrate. An interrupted/failed compaction without
that hook is not covered by this success-path acceptance.
the existing live Claude settings/launchers were deliberately not changed.
personally inspected real Toad pixels on the affected open-row path.
Scratch and process custody
Owner/purpose: pi-bridge-compact,
/home/ts/.cache/agent-scratch/pi-bridge-compact, private buses, concise real-runlogs, source census and bounded pytest output. Original Claude transcripts,
auth, live bus and uncertain attempts are untouched. All owned proof-session
and Monitor processes are stopped after their original successful compactions.
No test input was retried. Keep the concise real proof until PR review; older
exploratory generated output can be retired after checking borrowers.
The provided checkout already existed outside
/home/ts/wt; no additionalworktree was created. Resource check used the repository's
tools/agent_resource_check.pybecause/home/ts/bin/agent-resource-checkisabsent. Headroom was warning (3–4.6 GiB available RAM, /tmp 3.9 GiB free), so
sessions ran sequentially and reused installed dependencies.