Skip to content

P404: repair install lifetime and boundary ownership - #2556

Closed
trissim wants to merge 21 commits into
mainfrom
fix/p404-report-lifetime-recovery
Closed

trissim wants to merge 21 commits into
mainfrom
fix/p404-report-lifetime-recovery

Conversation

@trissim

@trissim trissim commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

P404: install reporting lifetime and runtime-safe outcomes

Draft at c6cd8d4f92bb10a7015ffdf8e49359f537627b5e, pushed and freshly remote-verified. Its hosted candidate passed all six executed rehearsal/publication/native tests. Not merged or installed; the separate watch-discovery gap below remains open.

Implementation and ownership

  • InstallReport.running() spans target-tree cleanup and posts one starting message and one terminal result. Missing completion becomes a failure. The original exception and cause propagate; Repo.module_file() maps actual checkout/wheel frames to source locations.
  • RootWriter owns schema permission independently of the terminal outcome. The outgoing Stack supplies the starting writer. CarriedPass.writer() is the sole installer CarriedWriter constructor; carried completion failures retain that proof. CreatingInstall shares creation while fresh and retiring siblings keep their different initial permissions.
  • UpgradeRefused(InstallFailure, CoordinationError) supplies its own failure lines. Deleted RefusedInstall, its forwarding method and the refusal-specific handler. CrashedInstall derives type/message from its original exception. FailedInstall is not another refusal adapter: it combines the failure with the observed live result, so failed exits retain bridge lines.
  • The current InstallOutcome owns progress. ObservedInstall shares the live observation between unfinished and failed states; finished state derives it from the completed Upgrade. There is no second InstallReport.live or bridge tuple. LiveRun.bridges and bridge_lines() are declared once on their parent. Install.install() resumes already-paused bridges in its resource lifetime even if a later pause/live operation fails.
  • BridgeResume members own their report lines: resumed, owner exited, replaced identity, or unobserved with its actual diagnostic and re-arm instruction. ProcessIdentity retains PID and start time. Deleted tuple sentinel/string outcomes, accumulator, callback failure and require_resumed paths.
  • BoundListener.of(site) observes real accepting sockets and retains the site. Direct construction is closed; only that factory produces the witness. The connector passes it immediately to SystemdUserService.ready, without sleeps or retries.
  • AbsolutePathText owns path normalization for install arguments, repository decoding and install scratch ingress. The OS-created scratch string and target-child payload use that codec, not another builder-side resolve(). Removed RepoChoice.resolved, caller resolution/replacement and import-time HOME. Staged builds derive home from the captured outgoing Stack.
  • The copied-session fixture extends SessionBridge and owns its binding, state path, process and log. Consumers no longer reconstruct state paths or unpack positional records. Short aliases are now confined to bridge socket lifetimes; CLI rehearsal storage keeps its physical trusted home. No private-registry or native qualification guard was weakened.

Pattern closure: IMPL-4/IMPL-9 move outcome decisions into nominal variants; IMPL-12 shares creation and live-result rendering on their parents. The existing MroDispatch gained class_handlers_for, which owns class-factory lookup for all three FieldCodec consumers. A new bridge outcome or reportable refusal extends its declaration, not a consumer roster.

Source evidence and deletion

Against fetched origin/main at 46391711d: 20 files, +1121/-362; production 12 files, +578/-187; tests 8 files, +543/-175. These are measurements, not a budget gate.

The ordered whole-source check reproduces 10 at a520d0d9d, 9 at the actual fresh base d55533885, and 0 now. Main had already deleted resources.callback(paused.resume) before this branch started; that deletion is not credited to this PR.

grep -rnE 'report\.failed\(error\)|callback\(paused\.resume\)|def announce\(self, report|def ready\(cls\) -> None|command\.(checkout|project)\.resolve\(\)|def resolved\(self\) -> Self' src --include='*.py' | wc -l
# 0
git grep -n -E 'RefusedInstall|SessionBridgeResumes|SessionBridgeResumeError|require_resumed|outcome\.writer|InstallFailure\.of' -- src tests
# no matches

Existing refactor-audit Package.load supplied the original-class/declaration/write/decision/import/caller census: before at f0d2d91d7, 443 production and 475 test modules, no parse omissions. After-census at c6cd8d4f9 again parsed 443 production and 475 test modules with zero omissions; overlays also used published source. These are syntactic evidence, not global semantic proof. NRA native family projection could not import because this permitted worker interpreter lacks tree_sitter; no packages were installed to work around that. Dynamic bindings and external dependency projection remain unproved. No executable proof script was added to the repository.

Checks and actual execution

  • Current head c6cd8d4f9: hosted run 39706061830, job 119586259416 executed 6 tests, all passed in 274.05s. Both CLI rehearsal variants now passed, all three real-publication variants passed, and the saved-owner DM/reply/native-input proof passed with one input proof, 2.050s startup and zero tree hashes. Native children were retired. This is the hosted synthetic merge b9aea276413a, not a machine installation. All five substantive hosted checks passed. The green review-verdict job contained only setup/cleanup, with no head-review step executed; it is not review acceptance. Full logs are retained as p404-c6-hosted.log and p404-c6-review-push.log under the owned scratch directory.
  • f5ba9f269: 83 passed, 3 skipped, 2 deselected in 84.21s across touched install announcements/paths, FieldCodec, bound-listener, real session bridge and bridge-owner tests. Five-file targeted mypy and touched Ruff checks passed. The skips are the built-runtime publication cases, not successes.
  • Sealed-listener follow-up: the real TCP/systemd-notify test passed in 0.22s, including rejection of direct construction. Targeted mypy and Ruff passed.
  • Current fixture-only home split: mypy/Ruff passed; the source guard still correctly skips all three built-runtime cases and excludes the two CLI rehearsals. It created no local runtime.
  • Hosted synthetic candidate for f5ba9f269, run 39704219516, job 119580321168: 4 passed, 2 failed in 241.17s. All three test_live_upgrade_family_after_real_publication variants actually passed: finished, RuntimeError and KeyboardInterrupt. They performed real build/carry/publication, retained data, selected the new runtime, resumed the same copied bridge process, asserted exactly two lifecycle posts and exactly one bridge line, and checked original exception identity and source file:line. The fourth pass was the actual saved-owner DM/reply/native-input proof, startup 1.680s with zero tree-hash calls and retired children.
  • That hosted run's two older CLI rehearsal variants failed on Private registry guard has an untrusted directory ancestor: my module fixture had applied the bridge socket alias to all HOME storage. This head limits that alias to the bridge fixture. The preceding f74d44526 candidate also passed all three publication variants, but its two CLI rehearsals failed earlier at native-package alias-versus-canonical relative_to; scratch ingress was subsequently corrected through the existing codec.

Family entrypoints: test_install_failure_family_through_upgrade, test_root_writer_family_through_upgrade, test_live_result_family_survives_codec_and_retained_history, test_live_upgrade_family_after_real_publication, test_copied_sessions_share_a_short_home_without_rebinding, and test_readiness_requires_the_started_sites_listening_socket.

Still open

  1. Head review and the merge queue remain open; corrected-head hosted qualification is complete. No local ratchets, full suite, fresh venv or native guard bypass were run.
  2. Source discovery: Serve.running scans only ~/.local/state/agent-comms/*-bridge-*, whereas claude.main defaults to ~/.local/state/claude-comms-inbox and accepts arbitrary --state. The retained watch claim promises state-path independence but its record only has bus/root/argv. unit-heartbeat retains this P404 finding; extending the shared WatchRecord/CLI ownership is outside the functions released in the order. A scope request timed out and is unconfirmed. Do not call this broad discovery complete or add a second argv parser/glob authority. Acceptance is one captured outcome for each default/custom-state live watch.
  3. Core progress write also timed out and is unconfirmed. Neither uncertain operation was replayed. Unit-goalblock subsequently reported live coordination database corruption; its retained read-only evidence says DatabaseError database disk image is malformed. That evidence was read, but this worker issued no database command. No further Core writes, live install or repair will be attempted against that store. No Core delivery claim was made.
  4. After merge, the merger still owes the real machine install's starting/terminal #comms posts and saved-session/live-agent reply journey. There is no P404 machine result post or merge commit to cite yet. Existing machine install 51 is not P404 acceptance.

Assumed: [] No skipped, unexecuted, older-head or private-hosted result is counted as current machine acceptance.

…ime-recovery

# Conflicts:
#	src/agent_comms/installer/upgrade.py
@trissim

trissim commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #2888, merged as fae2399. #2888's body maps every remaining piece of this draft to its current owner (file:line table); nothing functional is left to port.

@trissim trissim closed this Oct 23, 2026
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