Skip to content

Sync 89 upstream commits and retry dropped WebSocket turns - #34

Merged
olety merged 90 commits into
rust-oneironfrom
oneiron/upstream-sync-20261006
Oct 23, 2026
Merged

olety merged 90 commits into
rust-oneironfrom
oneiron/upstream-sync-20261006

Conversation

@olety

@olety olety commented Oct 7, 2026

Copy link
Copy Markdown
Member

Summary

Merge all 89 upstream commits from 672085e883c25a8a5f10c77ad8e5d3fb38dd6584 into the Oneiron Rust fork, preserving the 212 fork-only commits and local open PrimeIntellect-ai#3201 compatibility patch. Ordered merge parents: 925ee93ab0fa50eb47067285aade7fcc3722ed59, 672085e883c25a8a5f10c77ad8e5d3fb38dd6584.

Includes PrimeIntellect-ai#3197 stream-drop classification/guard, PrimeIntellect-ai#3361 [DONE] termination, PrimeIntellect-ai#3362 configured max tokens, and upstream lifecycle/kernel/factory changes. All 55 conflicts have recorded resolutions (ledger below); upstream structure is retained while fork capabilities stay available.

WebSocket retry and continuation

  • Keep WebSockets. Mid-stream closes, including abnormal close/restart, become structured transport failures. One session-owned retry episode permits at most three retries at 2s / 4s / 8s, and composes with retry silently dropped provider streams PrimeIntellect-ai/prime-agent#3197 rather than adding an independent replay loop.
  • Replay from committed session state. A committed tool executes once. Failed partial assistant text is superseded rather than doubled. Cancellation stops replay.
  • Exact websocket_connection_limit_reached: one bounded immediate same-provider refresh outside the ordinary retry budget; repeated caps terminate. Cached sockets rotate at acquisition after 55 minutes, never mid-flight.
  • Preserve cached delta continuation across upstream per-call X-ACP-Model-Request-ID / Idempotency-Key churn. Only these two correlation keys are excluded from connection identity; URL/auth/routing remain identity-sensitive. They still travel on fresh handshakes. Reused WebSocket handshakes cannot refresh per-call headers; no per-message metadata or SSE fallback was invented.

Ownership and public API

Provider transport/classification/cache stay in pa-ai; retry policy/committed-state rollback stay in pa-core; hosted execution/ACP/admission stay in pa-daemon; wire vocabulary stays in pa-types; rendering stays in pa-tui; delivery/aggregate events stay in pa-telemetry. Crate README scopes/non-goals/public APIs and cycle-free dependency direction are retained. The CLI remains the composition root.

Classification: internal retry/cache changes plus documented structured public failure/retry variants and provider SDK-name/borrowed error-name API. No new fork-specific external dependency or cross-crate re-export; upstream dependency changes are accompanied by Cargo.lock. Strict workspace Clippy and deny pass. Retry adoption/error telemetry uses the existing fire-and-forget seam, including transport aggregate network_error, without prompt/session/file contents or render-path network waits.

Compatibility and preserved wire names

Protocol 7 / combined schema 32 (protocol-7-schema-32-20261006c0a1), capability-gated optional metadata, tool selection, models.json fallback, compaction, affinity and memory behavior are preserved. Wire identifiers such as PI_PACKAGE_DIR, ai.primeintellect.prime-agent, provider IDs, settings keys, _meta, compatibility keys and lockfile names remain byte-compatible. These preserved identifiers are not user-visible branding changes.

Verification

  • Exact final source manifest: 17c18cc092c2ee7452e86ad036ef9b3f204cdf9a951880bc8ef40f8cd1226928; 1,672 files; tree d84164932bf8e2af1d7f2ab0d1e9e9a7d437651a.
  • Native Linux (mel): 5,388 passed / 0 failed / 19 ignored. Full workspace, strict all-target Clippy, fmt, debug/bootstrap, release workspace, deny, policy and packaging steps exit 0 on this successor source. Prior-source green and prior outer booking pipe failure remain separate historical evidence.
  • Native Mac: 4,277 passed / 0 failed / 14 ignored (five touched libraries); final fmt/strict workspace all-target Clippy/debug/deny pass. No full Mac workspace-test pass is claimed..
  • Runtime: 712 passed; factory: 268 passed / 3 skipped; packaging: 170 passed / 3 skipped, separately counted and not added to Rust totals.
  • Behavioral cached-continuation RED 0/1, then fresh GREEN 46/0. Native focused cap checks 18. Final real cached-cap witness: 3 targets / 37 checks passed on immutable candidate SHA 64182502d94a6a74515ea5d9c6358b5a24dbf8797182744e852e420420ea8683. Original real-cap failures remain preserved and excluded..

Parity-diff evidence

All seven final same-binary steps passed: genericWS8, Codex7, peer-close2, nativehello, fullTTY2 and six retained frame diffs. Candidate SHA 64182502d94a6a74515ea5d9c6358b5a24dbf8797182744e852e420420ea8683; source-bound result /Users/olety/Desktop/temp/prime-agent-sync-20261006/resume14-parity/runs/20261007T055704365773Z-pid33030/result.json. Behavioral proof with intentional differences, not pixel-identical output.. The verifier keeps all original assertions: generic WS 8 cases, Codex 7 cases, TS/Rust peer-close 2 targets, real native daemon hello, full interactive tmux TTY on both binaries, and six retained frame diffs. It binds one immutable final candidate and every source hash. Wire stdout is byte-compared and input/timing behavior is exercised. Intentional fork differences are reported, not claimed equal: Rust replaces failed partial text while canonical TS retains it; existing layout/dock/mode differences and combined schema32 vs TS schema30 remain explicit.

Release safety

Release 0.9.8-oneiron.20261006.1: signed commit f64ee8f74e8cc15ca7dd7a84126065599011e644, tree d84164932bf8e2af1d7f2ab0d1e9e9a7d437651a; native Mac+mel builds and no-activate installs verified. Mac /Users/olety/.local/share/prime-agent-oneiron-rs/0.9.8-oneiron.20261006.1; Arch /home/lexi/.local/share/prime-agent-oneiron-rs/0.9.8-oneiron.20261006.1. Manifest SHA 6f12d52f9a63bb202e5b696028d0381f5f105abb987625020a124c31fcb684d5; SHA256SUMS SHA a69a96ad84ed68f970b8e8f131be5720061a223198a5c1a3709207951b8e868a; controller SHA 079ff4f137ab90cb1e3e214df5ce109655d12cb72b302a93dc3e2b23215759f2. Pinned real catalogs:1199 models/42 transport tuples/68 services.. Mac build/stage is native and sequential; Linux build goes through fleet on mel; selyse/Arch only verifies and installs. Both installers use --no-activate. Current .20261003.2, launchers, live feed pointers and daemons remain untouched. Activation and rollback are deferred to an owner-approved quiet window and recorded in the report.

Conflict ledger

The table below records the initial merge resolutions. Subsequent retry/integration fixes are described above; an initial resolution is not a claim that its file received no later changes.

Conflict resolution ledger — 55 resolved paths
Path Resolution
crates/pa-ai/src/providers/anthropic/mod.rs Kept upstream OAuth client-identity tests and retained fork response-model and affinity behavior.
crates/pa-ai/src/providers/anthropic/stream_tests.rs Retained both fork response-model regressions and upstream HTTP content-type regressions.
crates/pa-ai/src/providers/openai_completions/stream.rs Applied upstream [DONE] termination and decoder guard while preserving fork response-model attribution.
crates/pa-ai/src/providers/openai_completions/stream_drop.rs Retained fork stream-drop tests and added the upstream held-open [DONE] regression.
crates/pa-ai/src/utils_inner/stream_failure.rs Retained typed WebSocket transport failure, safe messages, and fork fields with unknown defaults.
crates/pa-ai/src/utils_inner/stream_failure/tests.rs Retained WebSocket transport and provider-error-code regressions.
crates/pa-cli/src/command_registry.rs Kept fork tool-selection help rows removed upstream.
crates/pa-cli/src/global_flags.rs Kept fork tool, hosted-daemon, and no-extensions flags; restored the tool and goal-budget global scanning contract.
crates/pa-cli/src/mode.rs Kept fork tool fields and argv handling with upstream telemetry opt-out and serialized environment tests.
crates/pa-cli/src/print_runtime.rs Adopted upstream daemon-only ACP selectors and semantic identity; retained fork hosted-print, JSON, transport, prewarm, resume, and tool-selection behavior.
crates/pa-core/README.md Documented upstream semantic-edge and kernel APIs alongside fork memory, retry, transport, resource, and headless behavior.
crates/pa-core/src/kernel/bootstrap/mod.rs Required upstream rename readiness and the fork file-handle snapshot guard.
crates/pa-core/src/kernel/bootstrap/venv/probe.rs Combined upstream rlm.rename readiness with fork installed-runtime snapshot safety checks.
crates/pa-core/src/kernel/manager/mod.rs Kept fork internal-cell notices and upstream per-execution timeout propagation.
crates/pa-core/src/kernel/manager/requests.rs Used the upstream shared abort-grace deadline while retaining the fork bounded interrupt for blocked writes.
crates/pa-core/src/kernel/provisioner.rs Kept fork start receipts and abandoned-bootstrap telemetry suppression; added upstream pending-stop and doomed-startup teardown handling.
crates/pa-core/src/kernel/shared.rs Retained both fork KernelCellLine and upstream KernelBashCommands.
crates/pa-core/src/models/custom.rs Preserved fork custom-model compatibility and Anthropic affinity tests, plus upstream explicit-max-token regressions.
crates/pa-core/src/session_engine/auto_retry/tests.rs Retained fork response-model, retry-episode, jitter-bound, and WebSocket regressions without changing retry semantics.
crates/pa-core/src/session_engine/compact_session.rs Kept fork affinity and retry options inside the upstream semantic summary-slice flow; streaming and split-turn behavior remains intact.
crates/pa-core/src/session_engine/compaction_arms.rs Carried fork summary session ID and retry policy with upstream semantic-edge recording.
crates/pa-core/src/session_engine/engine.rs Preserved fork tool, memory, retry, and WebSocket behavior alongside upstream timing, semantic-edge, side-question, and background-MCP handling.
crates/pa-core/src/session_engine/error_classify.rs Retained typed WebSocket transport classification and safe error-code regressions.
crates/pa-core/src/session_engine/mod.rs Kept fork affinity documentation and summary retry state; added upstream semantic edges, side-question stream, and factory agent-directory state.
crates/pa-core/src/session_engine/provider_retry.rs Kept the existing one-shot WebSocket retry regression; no retry implementation change.
crates/pa-core/src/session_engine/runtime_wiring.rs Combined fork memory-limit options with the upstream late agent-message callback.
crates/pa-core/src/session_engine/wiring.rs Retained fork summary-retry and affinity wiring; added upstream semantic-edge wiring.
crates/pa-core/src/tools/golden_replay.rs Kept fork memory verifiers and initialized the upstream late-message callback.
crates/pa-core/src/tools/ipython.rs Retained fork memory notices and description limits; added upstream bash summaries and late-message callback.
crates/pa-core/src/update/installer.rs Kept self-update refusal first and retained upstream installed-channel/platform selection and tests.
crates/pa-daemon/README.md Kept fork hosted-session APIs and documented upstream daemon-only ACP and MCP credential behavior.
crates/pa-daemon/src/acp/autorefine.rs Removed the obsolete in-process ACP implementation; worker and shared session-engine paths own the behavior.
crates/pa-daemon/src/acp/compaction_arms/tests.rs Removed the obsolete in-process ACP test harness; worker tests own the behavior, with constructor and cache-threshold fixture updates.
crates/pa-daemon/src/acp/daemon.rs Adopted the upstream daemon-only ACP state machine and shared the fork DaemonLink; retained tool-selection refusal and explicit hosted-link waits.
crates/pa-daemon/src/acp/goal_continuation.rs Removed the obsolete in-process continuation path; daemon worker tests own the behavior.
crates/pa-daemon/src/acp/in_process_config.rs Removed the obsolete in-process model switch; ACP uses worker SetModel, which retains the session transport.
crates/pa-daemon/src/acp/wire_config.rs Kept upstream picker and close checks on the shared DaemonLink, with explicit waits for the new calls.
crates/pa-core/src/session_engine/telemetry.rs Adopted upstream low-frequency run/session counters rather than restoring removed per-occurrence events. Retained fork WebSocket adoption, typed network aggregate classification, and affinity ownership.
crates/pa-daemon/src/agent_engine/config.rs Kept fork offline, no-skills, tools, and durable-replay configuration; added upstream execution mode.
crates/pa-daemon/src/lib.rs Retained the fork hosted-headless module and added upstream Herdr pane identity.
crates/pa-daemon/src/prompt_admission.rs Preserved fork route-timeout recovery and adopted upstream atomic enqueue, cancelled-ID refusal, and cancelOwned behavior.
crates/pa-daemon/src/supervisor.rs Adopted the upstream client-specific turn budget and retained the fork typed timeout token.
crates/pa-daemon/src/supervisor/clients.rs Combined fork cancellation-safe line reads with upstream event ordering and retained both regression sets.
crates/pa-daemon/src/supervisor/supervision.rs Kept fork child-exit watches and cleanup; added upstream synced relaunch records, deadlines, replay budget, and stderr attribution.
crates/pa-daemon/src/supervisor/worker_lifecycle.rs Kept fork session policy, tool selection, and exit watches alongside upstream pane environment and route budget.
crates/pa-daemon/src/types.rs Summary wire type carries fork tool selection and the upstream Anthropic warning marker.
crates/pa-daemon/src/worker/summary.rs Reports both fork tool selection and the persisted upstream warning marker.
crates/pa-daemon/src/worker/turn.rs Adopted upstream running-admission tracking and settle cleanup; cancellation ownership remains enforced at enqueue without a duplicate pickup guard.
crates/pa-telemetry/src/catalog.rs Kept upstream low-frequency aggregate rules and added reviewed fork event categories, guard events, and safe error-code vocabulary.
crates/pa-telemetry/src/events.rs Kept upstream lifecycle builders and removed folded per-occurrence events; retained the fork headless builder and flush-based test helper.
crates/pa-telemetry/src/lib.rs Adopted upstream counters and lifecycle exports while retaining the fork headless surface and classifier error vocabulary.
crates/pa-telemetry/src/platform.rs Kept fork schema revision 4 for headless tool selection over upstream revision 3.
crates/pa-tui/src/daemon_client.rs Re-exported both fork startup-failure and upstream kernel-not-running classifiers.
crates/pa-tui/src/interactive.rs Kept fork tool-selection configuration and added upstream new-child parent/depth identity.
prime-agent-runtime/src/rlm/repl.py Retained fork memory and snapshot-safety APIs while adding upstream factory activities and the MCP status lane.

Supplemental integration resolution

  • crates/pa-daemon/src/daemon_link.rs: Shared transport supports upstream request_ok while retaining optional pending slots, close-fast behavior, and fork hosted-headless budgets.

kevinjosethomas and others added 30 commits September 30, 2026 20:09
The x86_64-apple-darwin legs in continuous.yml and release.yml move from
the macos-15-intel pool to the macos-15 Apple-silicon pool, cross-compiling
for x86_64 (the target stays x86_64-apple-darwin). The M1 links this binary
~2.2x faster than the intel pool with far tighter variance (484 s vs 1046 s
same-commit link tail), killing the 29-minute outliers; the livecheck runs
the x64 artifact under Rosetta 2, the runtime install-rust.sh gives an
x86_64 payload on an Apple-Silicon Mac.
…ct-ai#3223)

The hillclimb era's measurement cluster dies whole: the TS-side
faux-provider driver (scripts/ts_faux_extension.js) and the perf battery it
drove - scripts/battery/ in its entirety (stream_throughput.py,
streaming_render.py-era survivors, sustained_cpu.py, perf_wave.py,
perf_gate.py, ci_perf_wave.sh, run_perf_wave.sh, the recorded
perf-baseline.json, and the support libs that served nothing else:
batterylib.py, mock_provider.py, perf.py, scale_corpus.py,
ts_identity.py) - plus the Makefile perf-wave target and its .PHONY entry.
Every member had zero live references: no workflow invocation (benchmark.yml
is long gone, only the dead Makefile mirror remained), no test shells out to
them, the crontab and the fleet tooling never touch them. The benchmark
campaign harness on the bench node is the modern performance tooling.

Folded onto main after the scripts purge (PrimeIntellect-ai#3178) merged: that purge already
removed the parity harnesses this lane's map had found importing the faux
(acp_compaction_parity.py, compact_parity.py, mcp_hint_parity.py,
paste_stream_parity.py, spinner_cadence.py, streaming_render.py and the
visual-parity cluster), so those deletions fold to no-ops; the purge's
merged form kept the wave serving only itself - the faux header named
exactly the two wave dimensions as its consumers - so the whole battery
directory goes here instead.

The golden regeneration harness (crates/pa-core/tests/golden/harness.mjs)
is not needed post-port either: the replay tests assert against the frozen
corpus at runtime, and the TS checkout remains the reference if a golden
ever needs re-derivation. The corpus stays, frozen provenance noted in the
replay module doc.

Doc/comment repairs where kept files named a removed script: the Makefile
drops the perf-wave mirror, golden_replay.rs's harness.mjs cites become the
corpus's provenance notes, and differential_cli.rs's TMPDIR comment no
longer cites batterylib's scrubbed_env.
* add the serper key flow to mcp

The /mcp view gains the api-key credential class: the daemon's
get_mcp_connections serves a credentials section (the api-key
catalog), the view renders 'Serper (web search)' alongside the MCP
connections, and Enter runs the paste-the-key prompt that stores
AuthCredential::ApiKey at the 'serper' slot - the exact contract the
websearch runtime reads. The /login-is-providers-only removal left no
home for the key; /mcp is it.

The SKILL.md (repo + installed) and the runtime's missing-key message
now point at the new flow; the existing auth.json reading path is
unchanged (a stored key keeps working, and the /logout row pairs with
the new entry).

File-size compliance: the view's row model moved to
mcp_view/rows.rs and provider_login.rs's tests moved beside it
(the additions pushed both files over the 1,000-line cap).

* reload the auth store at the mcp view open

The daemon's get_mcp_connections serves the api-key credential rows
(and the roster) from the manager's cached AuthStorage copy; the
interactive client's /mcp key flow stores through its own storage
instance in its own process, so the row kept reading 'Not configured'
until a daemon restart. The open now reloads the shared store first
(TS buildServiceCatalogViews re-reads the store on every open) - the
Macroscope finding on the first push.

* retrigger ci: the synchronize push fired no ci run

---------

Co-authored-by: kt <kt@users.noreply.github.com>
…llect-ai#3228)

* pa-tui: the Anthropic subscription warning fires once per session lifecycle - not on every open

The warning's shown-flag lived on the TUI instance (session_ui.rs's
anthropic_subscription_warning_shown, reset at every SessionUi
construction), so every session open re-drew the ban-risk warning.
Operator directive 2026-09-29: the shown-state persists in the session's
own durable store - once per session LIFECYCLE.

The persisted state (the thread_goal_state pattern):
- A custom row, customType "anthropic_subscription_warning_shown", data
  {"shown": true}, rides the session JSONL (the one durable store):
  branch-faithful, survives daemon restarts and worker replacements.
- pa-core's windowed walk parses the marker on the active chain (the same
  sparse-parse condition as the goal row) and serves
  WindowedSessionStore::anthropic_warning_shown(); the snapshot carries it
  with a serde-default false (v7 sidecars stay servable; the default is
  the fail-open direction) and live appends fold it.
- The daemon store hydrates on BOTH opens (full scan + the windowed
  snapshot, so the marker hydrates from the discarded prefix) and both
  fork arms re-hydrate from their own adopted rows (the fork's live flag
  agrees with its own file; a fork cut above the marker is a genuinely
  new session and answers closed).

The wire:
- New Rust-native client command mark_anthropic_warning_shown (the TS
  gate is per interactive-mode instance; the TS protocol has no
  counterpart): the worker persists the marker row idempotently and
  get_state serves SessionSummary::anthropic_warning_shown
  (skip-when-absent, so older workers never emit it).
- pa-tui: the credential-detection arm (startup / model-select /
  api-key-save) gains the lifecycle gate as the LAST check before
  warning - the settings toggle (warnings.anthropicExtraUsage) stays
  first, the per-instance flag stays as the view's dedup, and the extra
  get_state round trip only happens when a warning is actually pending.
  The read fails OPEN (an absent field from an older daemon, or an
  unreadable state, draws the warning rather than suppressing it). The
  mark is fire-and-forget bounded 10s (a failed mark costs one repeated
  warning on the next open, never a suppressed one).
- The login-completed arm (a fresh auth landing) RE-WARNS - each
  completed subscription login is its own event - and marks too.

Tests (the five directive behaviors):
- pa-tui/tests/anthropic_warning_lifecycle_headless.rs (the scripted
  socket harness): new-session warns once + marks exactly once;
  marked-session reattach never re-warns; the toggle off never warns;
  a fresh login re-warns on a marked session; the absent-field degrade
  fails open.
- pa-cli e2e over the REAL supervisor: run one warns once (the fake
  subscription credential + the scripted anthropic model - the harness
  engine gains the opt-in "model" script knob for it), the marker row is
  durable in the session file, get_state serves the gate over the wire;
  a FRESH process attaching renders no warning. The supervisor routes
  the new frame through the generic active-session selector.
- pa-daemon: the store mark/dual-reopen hydration, the worker
  dispatch/get_state/idempotence, the fork arms' own-rows hydration, the
  window-scan hydration (the marker beyond the retained boundary, cold +
  sidecar; a data.shown=false row never hydrates); command_breadth
  extends the Rust-native accept list + fixture.

* pa-tui: the Anthropic warning's bots round - the gate rides the active branch and the harness settle waits the mark

* pa-cli: the warning e2e's missing-file panic names the file, not the session id (CodeQL)

* pa-cli: the missing-file panic's format argument count (the CI round's compile fix)
…only, and one shard dep cache (PrimeIntellect-ai#3233)

- continuous.yml + release.yml: swap dtolnay/rust-toolchain to the action pin that honors the toolchain input; the old pin has no toolchain input, so every release binary built with rustc 1.95.0 while ci.yml tests on 1.98.1.
- continuous.yml + release.yml: build -p pa-cli --bin prime-agent — the jobs package only prime-agent, and pa-daemon's [[bin]] was a second thin-LTO link with no consumer (~26% of the build step, measured).
- ci.yml: one shared rust-cache key for the 8 test shards — per-shard keys held 8x ~500 MB per ref and evicted the release-build caches (the repo sits at the 10 GB quota; the shards were 83% of it).

Co-authored-by: Sebastian <sebastian@primeintellect.ai>
…rimeIntellect-ai#3229)

The owner-watchdog daemon thread wakes once a second for the whole
life of every kernel just to re-probe the owner. On macOS/BSD register
the owner's exit (kqueue NOTE_EXIT) and on Linux a pidfd, then block on
the single exit event: zero wakes until the owner dies. A 30 s poll
remains only as the fallback when exit notification cannot be
registered (old kernels, seccomp, CPython without os.pidfd_open).
Owner resolution, reparent semantics, the exit path, and Windows are
unchanged.
…rkers (PrimeIntellect-ai#3242)

The adopted-worker monitor polled liveness at 1 Hz forever (a `ps`
subprocess per tick on macOS). It now parks on the kernel's exit
notification (Linux pidfd, macOS kqueue EVFILT_PROC/NOTE_EXIT, both
awaited through tokio's AsyncFd), with a clearly marked 30 s liveness
poll only where the watch cannot register. pa-core's open_pidfd now
returns io::Result so the wait can tell ESRCH (already exited) from an
unwatchable pid. The adopted arm matches the Child arm's shape: it
waits for the exit, then honors the stop flags.
…ai#3047)

* feat(pa-tui): the scoped agents view's ctrl+n starts the session under the scope root

Operator-directed divergence (snimu, 2026-09-28): the unscoped view's
ctrl+n keeps starting a depth-0 root session (TS `createNewSession`), but
a scoped view's ctrl+n starts the new session UNDER the scope root - one
level below it and in its directory - so it lists in that same scoped
view and the agents-back return lands on it, selected.

The create stays the plain daemon `create` carrying two config keys the
daemon already accepts for RLM children (`parentSessionPath`,
`rlmDepth`): no new wire op, no runtime metadata, no ledger edge - the
new session is a user-owned top-level runtime whose file lands in the
scanned sessions dir.

- `scope_depth` becomes `pub(crate) scope_root`/`ScopeRoot`: the depth
  label, the parent file (via the forest's own `parent_record_file` so
  the create binds exactly what the roster links), and the root's cwd,
  resolved once.
- The new action rides `OpenedRow` (TS `finish({ type: "open" })`);
  `new_session: bool` is deleted instead of widened (#10/#6).
- `SessionSelection::NewChild { parent_session_file, rlm_depth }` carries
  the binding through `create_config`; the pre-attach startup paint and
  the fork match arms stay explicit; `/new` keeps its root semantics.
- Adoption telemetry: `tui agents new scoped` { depth } (schema v1,
  primitives only) via the new `InteractionTelemetry::scoped_agent_created`
  seam, emitted in `SessionUi::open` once the daemon accepted the create;
  the unscoped path emits nothing.

Regression tests: the scoped ctrl+n binds under the scope root
(whole-object `OpenedRow`), the unscoped override pins plain `New`, and
`create_config` carries the parent binding (whole-object).

* feat(pa-tui): the dock counts a user-created child session as a child

Operator ruling (snimu, 2026-09-28): "agents and users should be seen as
equal collaborators working on the same project" - the parent chat's dock
counters include sessions a user created under the parent (the scoped
agents view's ctrl+n), not only spawned subagent runtimes.

The one TUI parentage rule moves to `subagents.rs`, already the shared
subagent-family data layer: `is_subagent_summary` relocates unchanged
and `depth_consistent_binding` extracts the forest's depth-consistent
parent test. The dock's walk links a live top-level row whose binding
names the linking owner's file one level up, so a user child counts
direct (or nested under a subagent) exactly like a spawned one; a
same-depth fork stays a sibling and a message-less draft stays out of
the walk, exactly as the agents view hides it. The forest keeps its
record-shaped wrapper; forest behavior is unchanged.

Side effect: a resumed subagent file reopened as a top-level runtime now
counts in the dock too, ending the dock/agents-view mismatch.

Regression: a_bound_session_counts_as_a_child_and_a_fork_or_draft_does_not
(whole-object SubagentCounts); the redundant non_subagent_rows_never_link
folds its fork case into it.
* perf(startup): the probe-grid fast-poll cut - both connect-probe grids tighten 5ms -> 1ms (timing-only)

* docs: the probe-interval comments state current behavior, not the poll-interval history (bugbot df2563c5 fix)

* docs: drop the predecessor-record clause from the daemon probe comment (bugbot df2563c5 follow-up)

---------

Co-authored-by: hillclimb-lane <lane@hillclimb.local>
…eIntellect-ai#3286)

Two unit tests instantiated the real panel login UIs and invoked
on_auth, whose production implementation calls
pa_core::platform::browser::open_in_browser - so every local
cargo test run opened real OAuth pages in the developer browser:
github.com/login/device (GitHub Copilot device flow),
claude.ai/oauth/authorize (Anthropic), and auth.example/authorize
(Codex). Delete the browser-opening tests and the now-unused
Duration import; production code is untouched (the opener behavior
is correct at runtime).
…meIntellect-ai#3275)

Co-authored-by: Seth Karten <32787133+sethkarten@users.noreply.github.com>
…ai#3253)

* show installer progress and add a --force daemon stop

* fix the printed rollback after a typescript takeover

* only signal a pid that is a prime agent daemon under --force

* report each --force outcome with its own reason

* give the installer the splash banner and a minimal step list

* keep the download path out of the renderer's shell command

* retrigger ci

* retrigger ci

---------

Co-authored-by: Seth Karten <32787133+sethkarten@users.noreply.github.com>
…t-ai#3276)

Co-authored-by: Seth Karten <32787133+sethkarten@users.noreply.github.com>
* never set git identity - the ambient auth owns the attribution

* extend the identity rule: no co-authored-by trailers ever

* scope the identity rule to commits that land in this repo

* AGENTS.md: the identity comes from the ambient git config, not auth

---------

Co-authored-by: Sebastian <sebastian@primeintellect.ai>
Co-authored-by: Seth Karten <32787133+sethkarten@users.noreply.github.com>
…-ai#3156)

* pa-daemon: supervisor_roster.rs split - under 1,000 lines

The 1,799-row roster-serving module becomes its 30-row facade
plus two test-family children at the same tree position
(supervisor_roster::{passivation,delta_push}):

- The facade keeps the product whole (the zero-move cut): the
  `WorkerRosterDelta` record, the entire `impl Supervisor` (the
  subscribe handling, the delta merge, the stop-path passivation,
  the roster pushes, the `write_roster_summary` arms), and the
  free fns (`passivated_summary`, `normalize_model_to_durable_pair`).
- passivation.rs (650): the passivation + snapshot family - the
  stop-path passivation (the anchored-child preservation, the
  live-field stripping, the family pruning), the rewrite/diff-baseline
  snapshots, the registration-mark + re-registration rules, with the
  seed-tests fixtures the family drives; the shared drain fixture
  resolves through the facade.
- delta_push.rs (514): the roster-delta push family - the supervisor
  fixture, the sequence-drop, the flip/push batching, and the push
  benchmark.
- The shared `drain_roster_pushes` fixture (both families drive it,
  23 passivation call sites) rides in the facade under `#[cfg(test)]`
  per the agents_view shared-fixture precedent; the children reach it
  through `use super::*`.

ZERO product moves: the facade's crate::supervisor_roster::X paths
(WorkerRosterDelta and every impl member) resolve unchanged; the test
families reach the product through `use super::*` with the
supervisor_roster_seed::tests fixture block riding in the
passivation child (its sole consumer).

Byte-proof: each family child is byte-identical to the tip block
modulo the enumerated headers, the uniform dedent, one
fmt-canonical signature wrap, and the seed use block
(verify_supervisor_roster.py); the facade is the tip minus the test
mass plus the two cfg(test) decls; the tip blob reassembles
ZERO-DELTA. External-path audit: the only external reference
(supervisor/clients.rs's crate::supervisor_roster::WorkerRosterDelta)
resolves unchanged.

* ci: the empty-commit retrigger (the shard 4/8 tui_dispatches_slash_commands_menu_and_suggestions red is the adjudicated view-render/data-arrival ordering flake, red-slashmenu-tui-dispatch-parallel-flake-20260930-1 - solo x3 GREEN at the exact head cf8f86f plus the pristine-tip control x2 GREEN in the lane sandbox; this diff is the pa-daemon supervisor_roster pure-structure split, zero pa-cli/tui surface; the rerun-failed route is banned by the fleet retrigger policy)

* pa-daemon: drop the split-narration doc leftovers

---------

Co-authored-by: Sebastian <sebastian@primeintellect.ai>
Co-authored-by: Seth Karten <32787133+sethkarten@users.noreply.github.com>
…ync (write_file_atomic gains the per-call-site sync option) (PrimeIntellect-ai#3283)

* perf(pa-daemon): the supervisor-config persist drops the TS-absent fsync (L1 prebuild)

write_file_atomic made TS writeFileAtomicSync's per-call-site OPT-IN
fsync unconditional at every call site. The supervisor-config persist is
the only pre-bind durable write on the daemon boot path (Supervisor::new,
before bind_transport) and its TS counterpart (persistSupervisorConfig,
daemon-supervisor.ts) passes no fsync option: the unsynced rename is the
TS daemon's own posture for this write. One trapped fresh+sync_all+rename
fsync is the ~39-40ms class the daemon-init bind-span decompose measures
on the warm-create path.

Scope: TempSync enum (the per-site posture, the worker journal's
Finalize-enum shape) with the synced default preserved for every other
caller (worker descriptors, identity-pending, update_prepare); only
persist_supervisor_config moves to write_file_atomic_unsynced. The boot
error contract is unchanged (a failed write still fails the boot; only
the sync is dropped). The persisted config has no in-tree readers
(load_supervisor_config has zero callers) - a write-only parity mirror.

Tests: the unsynced arm replaces an existing destination with temp
cleanup, and persist_supervisor_config round-trips through
load_supervisor_config (parseability through the unsynced write).

* test(pa-daemon): the config round-trip compares fields, not the non-PartialEq struct

The prebuild test used assert_eq! on PersistedSupervisorConfig, which
derives no PartialEq - a test-compile E0369 under clippy --all-targets.
Compare the three fields instead (same oracle: the unsynced persist stays
parseable and the loaded record belongs to its socket).

* style(pa-daemon): rustfmt shape for the round-trip test's field tuple

cargo fmt --all --check wants the 3-tuple broken across lines (it
exceeds the width on one line).
* make release-prepare use the pinned toolchain and drop the ignored toolchain inputs

- release-prepare.yml: swap dtolnay/rust-toolchain to the 1.98.1 action pin (same fix as PrimeIntellect-ai#3233); the old pin ignored the toolchain input and installed 1.95.0.
- ci.yml: delete the ignored toolchain inputs (the action SHA selects 1.98.1).
- ci.yml: fix the shared cache comment; the shards' dev-dependencies differ, so the first leg to save defines the entry and other legs get a partial hit.

* drop the ignored toolchain input in the windows triage workflow

* retrigger ci

* retrigger ci
…llect-ai#3293)

* let the down arrow always reach the menus under the prompt

* keep the escape test off the tray and wait on what the tray tests check
…ai#3289)

* select the top search hit when the agents view query changes

Typing a query kept the previous row selected by identity, or clamped its index onto the last hit when that row was filtered out, so the best match sat above the cursor. Every query edit now selects the first row (the top-ranked hit); clearing the query returns to the session selected before the search, or the top if it is gone. A move while searching keeps the user's pick through the clear.

* select the topmost row on every agents view query change

Drop the pre-search restore: the clear is a query change like any other and selects the top row too.

* Revert "select the topmost row on every agents view query change"

This reverts commit b530f41.
…ect-ai#3281)

The e2e socket helpers poll the daemon socket with a 100 ms read timeout
in a loop, clearing the line buffer at the top of every iteration.
BufRead::read_line keeps the bytes that arrived before the timeout fires
and returns Err, so the next iteration discarded the first half of the
line and parsed only the tail:

    parse response line: Error("expected value", line: 1, column: 1)

A long line straddling the timeout, like a chunked attach snapshot, then
failed the suite: attach::chunked_snapshot_attach_streams_begin_chunk_end
flaked three times in CI (PR PrimeIntellect-ai#3258 run 36817673817, PR PrimeIntellect-ai#3249 run
36898991027, and once more on PrimeIntellect-ai#3258).

Clear the buffer only after a complete line was consumed, so partial
bytes survive the timeout; the deadline is unchanged. The pa-cli e2e
helpers use the same loop and get the same fix: continue_guard_e2e.rs
re-created the buffer inside the loop, so the buffer moves out of the
loop instead.
…lect-ai#3245)

* ci(test): run the idle passivation e2e and the release-dir kernel suites

The merged bootstrap step readies the product's kernel venv, but the
idle passivation e2e still self-skipped: its private PA_TEST_KERNEL_PYTHON
lookup had no HOME fallback and CI set nothing. It now uses the crate's
standard PA_E2E_KERNEL_PYTHON-else-default-path lookup, so the venv the
bootstrap builds reaches it (PA_TEST_KERNEL_PYTHON goes away).

The kernel suites that read an installed release dir run too: pa-core's
abort_kernel_cell and turn_boundary_kernel already honored PI_PACKAGE_DIR,
and the pa-daemon abort_kernel/steer_boundary e2es and the agent-engine
abort test now take the same branch. The shard step exports PI_PACKAGE_DIR
at the checkout (the runtime dir the bootstrap itself resolves), and the
Install uv step names its real consumer.

* fix two racy e2e tests and drop the release-dir gates

Both tests failed on this branch and on other branches that never set
PI_PACKAGE_DIR; the races are in the tests:

- read_goldens threw away events that arrive before the prompt reply.
  The turn can finish first, so its turn_end was lost and the test
  waited out its 15s deadline. It now keeps those events, like
  lifecycle.rs does.
- worker_fd_count_stable_across_prompts counted the worker's fds at the
  moment the prompt returned, while the turn's short-lived roster-push
  sockets were still open. Each sample now waits for the count to drop
  back to the baseline plus slack; a leaked fd never closes, so a leak
  still fails. wait_until's panic now prints the deadline and points at
  the caller.

The five live-kernel suites (pa-daemon abort_kernel_e2e,
steer_boundary_batch_e2e and the lib abort test; pa-core
abort_kernel_cell and turn_boundary_kernel) do not need a release dir:
a debug build finds the checkout's runtime and skills on its own. They
now run whenever the kernel venv exists.

* ci: stop setting PI_PACKAGE_DIR for the test step

The live-kernel suites no longer read a release dir, so the job-wide
variable has no reader that needs it.
…meIntellect-ai#3282)

The pa-daemon e2e helpers poll the daemon socket with a short read
timeout in a retry loop and either clear the line buffer at the top of
every iteration or re-create it inside the loop. BufRead::read_line
keeps the bytes that arrived before the timeout fires and returns Err,
so the next iteration discarded the first half of the line and parsed
only the tail.

Same fix as PrimeIntellect-ai#3281, swept over the remaining helpers: clear the buffer
only after a complete line was consumed, so the fresh-per-iteration
buffer in the drain loops moves out of the loop instead. Deadlines and
messages are unchanged.
snimu and others added 21 commits October 3, 2026 14:42
…s transport deletion, event parity (PrimeIntellect-ai#3264)

* fix(acp): the daemon-attached ACP session follows the CLI session flags and lifecycle

The daemon-attached ACP mode created a throwaway client-owned worker on
every session/new, ignored --resume/--continue/--fork/--session-dir and
the telemetry posture, and killed the worker on session/close and at EOF.

Startup now creates and attaches the daemon session the flags select
(TS main.ts): pa-cli resolves the session with the same selector the
print and rpc modes use (lifted out of build_session_manager_with_lease,
with the stored-cwd rule shared through stored_session_cwd), sends the
resolved sessionPath, cwd and sessionDir, sets telemetryDisabled, and
marks only --no-session sessions client_owned. A failure after the
daemon is up (bad selector, busy session, create or attach refusal)
prints Error: and exits 1 before any ACP frame instead of falling back
to in-process mode.

session/new binds that session instead of creating one. session/close
runs the shared stop sequence, releases MCP and fences the producer,
but keeps the worker, so a later session/new binds it again. A
session/close with the wrong id no longer drops the live session. EOF
releases MCP and ends only a client-owned session
(complete_owned_session); a resident session survives and is detached
when the link drops; its running prompt is cancelled, as TS's EOF abort
does (cancel_prompt_admission with cancelOwned).

Worker::new built the prompt-admission registry twice: the turn runner
committed admissions in one copy while cancel_prompt_admission read the
other, so a cancelOwned cancel of a running prompt answered cancelled
and never aborted the turn. The runner now shares the worker's
registry.

* fix(acp): await session/new before the next frame and resolve relative resume paths

A session/new that was still running when stdin closed installed its
session after teardown had already run, so nothing ever released it:
the producer's sink kept the writer open and the process never exited,
and the MCP servers it had admitted stayed registered. The reader loop
now awaits the session/new handler before it reads the next frame (the
same rule the in-process ACP loop applies to every handler), so EOF is
only seen after the session is installed and the existing teardown
releases it. Every other handler still runs concurrently: session/close
and set_config_option can wait behind session/cancel.

A path-like --resume selector went to the daemon unchanged and the
worker resolved it against the stored session cwd, so a relative path
from the CLI's working directory opened the wrong file or created a new
one and started a second worker. TS resolves the file before sending
it (SessionManager.setSessionFile), so select_headless_session now
makes the opened resume path absolute where the session is selected.

* fix(acp): --continue sends an absolute session path like --resume

A relative --session-dir makes find_most_recent_session_for_cwd return
a path joined under it, so --continue sent the daemon a relative session
path. The supervisor resolves create paths against its own cwd, so the
reuse lookup missed the live resident and launched a second worker the
lease refused. The continue arm now absolutizes the selected file
exactly like the resume arm; sessionDir itself stays as given, matching
TS runtimeConfigFromArgs.

* test(acp): run the ACP e2e suites on the daemon-attached path

Both ACP e2e suites rode the in-process fallback, but the product's ACP
mode prefers the daemon-attached transport, so the coverage now spawns
the sandboxed supervisor and drives the same wire the installed binary
speaks.

- acp_mode_e2e: one daemon-attached constructor (the in-process spawn
  and the dead PRIME_AGENT_AGENT_DIR env are gone); the supervisor
  socket lives on the harness struct. The 20 migrated tests keep their
  assertions; the 8 turn-running scripts gain engine: "faux" (the
  worker runs the real agent engine only with the key), and the
  threshold script pins maxTokens: 4096 -- the worker seam defaults to
  16384, which turned the test's reserve-shaped threshold degenerate
  (a non-positive threshold disables the trigger, so the arm never
  fired). The redundant mcp-capabilities twin and the three daemon
  subsets are deleted (the goal test absorbs the tokenBudget check),
  and the overflow re-probe loop is gone: the daemon releases the
  prompt slot before it answers, so the probe cannot hit the
  running-turn guard.
- acp_config_option_e2e: the in-process harness and both its tests are
  gone. The locked-provider refusal is not TS daemon behavior (the SDK
  counts authHeader as configured auth), and the faux worker does not
  follow set_model yet, so the switch flow stays unit-covered (PR-8b
  owns that seam). The transport-neutral assertions -- fixture
  discovery, the effort level list, the unknown-session error --
  moved into the daemon picker test.
- acp/daemon.rs: initialize now runs the same protocolVersion schema
  check the in-process arm runs (the TS SDK zod-validates initialize on
  every connection); the string-protocol-version test is red without
  it.
- README: the offline e2e sentence names the daemon-attached transport.

* changelog: acp daemon initialize validation

* test(acp): the faux worker follows set_model

The faux branch of AgentSessionEngine::resolve_model returned the script
model unconditionally, so a set_model switch on the daemon-path e2e
harness succeeded without switching: the pickers kept reporting the
script model. Return the script model while the selection is unset or
names it; any other selection resolves through the registry (the faux
provider is registered under the `faux` api, so a models.json faux-api
model streams through the same script). Port the deleted in-process
suite's model-switch assertions into the daemon picker test: the plain
model drops the effort picker and refuses `high`, the turn after a
switch settles, switching back restores `high`, and the map model
accepts `xhigh`.

* fix(daemon): derive the engine model selection from the create config only

AgentSessionEngine::new fell back to PRIME_AGENT_MODEL_PROVIDER /
PRIME_AGENT_MODEL when the create config carried no provider or model.
The faux worker branch always passes none, so with the env pair
exported a never-switched scripted session started with an
env-derived selection, which resolve_model treats like an explicit
set_model switch - the session left the script model and resolved
through the registry. The non-faux worker branch already reads that
env pair and passes it in the create config, so the fallback only
duplicated the read. Delete it: the worker is now the single owner
of the env read, and only an explicit switch moves a scripted
session off the script model.

* refactor(acp): the composition root serves ACP only over the daemon

`acp_mode_main` now starts like every other daemon-attached mode: it
ensures a supervisor is listening, creates the daemon session the CLI
session flags select, and serves the ACP surface over it. When the
daemon cannot start it fails with `Error:` and exit 1, matching the
TypeScript CLI, which has no in-process ACP path on this branch. The
`try_daemon_attached_acp` fallback (and its "using in-process mode"
stderr line) go away with the short-circuit.

The scripted-session seam renames `PRIME_AGENT_ACP_DAEMON_SCRIPT` to
`PRIME_AGENT_FAUX_SCRIPT`, the interactive mode's env: the value is a
script file path forwarded through the create config, so the ACP and
interactive modes now share one contract instead of two env names for
the same mechanism. The two e2e helpers follow the rename.

* refactor(acp): delete the in-process ACP transport

The daemon-attached transport is now the only ACP transport, so the
in-process engine and its session machinery go: prompt.rs, session.rs,
in_process_config.rs, compaction_arms.rs (and its tests), autorefine.rs,
goal_continuation.rs, and the events.rs loop-event projection
(wire_events.rs already maps the daemon worker's session events).
mod.rs shrinks to the shared helpers and the module tree;
handle_initialize/validate_initialize and the prompt-block error move
into daemon.rs next to their only callers, and AdmittedPrompt moves
into types.rs next to parse_prompt_blocks.

pa-agent's clear_all_queues goes with them (the deleted handlers were
its only callers). The pa-daemon scope README and the ACP capture notes
drop their in-process clauses.

* fix(acp): a session/cancel request answers method not found like TS

The TS SDK registers session/cancel only as a notification; a request
framing falls through to the generic method-not-found answer. The Rust
daemon-attached transport had grown a special case that answered `{}`
instead, so a client could not tell an unhandled method from a handled
one. The reader loop now only arms the stop for the notification form.

* fix(acp): the settle waits for RLM quiescence, the stop cancels the children

An ACP prompt answered end_turn while the subagents it started were still
running: both quiescence frames reported a hard-coded outstandingSubagents
of 0, the terminal frame published right after the completion, and
cancel/close stopped the parent's turn only, leaving the RLM children
running. EOF, conversely, waited behind an in-flight settle.

- The settle now reads the live roster (get_rlm_children) for the
  completion envelope, then loops on the headless-completion barrier with
  waitForRlmQuiescence (TS PrimeIntellect-ai#1612 finalizePendingTerminal): the barrier is
  the event with no timer, it answers once every tracked child run
  settles, and the loop re-reads the roster before publishing the terminal
  frame with a zero count. A failed barrier or read fails the prompt with
  "ACP lifecycle reconciliation failed: ..." instead of degrading, since a
  degraded status would spin the loop.
- The stop sequence behind session/cancel and session/close cancels every
  roster row after WaitForIdle (TS cancelOutstandingRlmChildren in
  stopSessionWork); the cancels run one after another and their failures
  are ignored like every other stop step (declared divergence from
  Promise.allSettled and from the stopFailure latch).
- Only the prompt handlers are owned by the JoinSet EOF shuts down before
  the teardown (TS aborts the controller and exits), so the process never
  waits on a prompt still settling; the aborted prompt handler leaves its
  turn slot set, so the teardown still sends the owned-admission cancel.
  The other request handlers stay detached spawns, so a session/close in
  flight at EOF runs to completion — its tx clone holds writer.await — and
  its session still stops and releases its servers. session/new stays
  awaited inline.
- The terminal frame reports the settlement's own status observation, and
  the stop reason reads the abort flag after the settlement (TS
  terminalStatus / terminalSettlementCancelled).

The CLI's ACP create also forwards PRIME_AGENT_FAUX_CHILD_SCRIPT (a script
file) as the create-config childScript, extending the PRIME_AGENT_FAUX_SCRIPT
verification seam (the worker has consumed the key since PrimeIntellect-ai#3171), so e2e
lanes can script the spawned children.

The new kernel-gated e2e lanes prove: the completion reports the live
child and the terminal reports zero after the settled child's notice turn
drained inside the barrier; a cancel during the settle cancels the held
child and answers cancelled; EOF during the settle exits while the
resident session's child keeps running. The lanes run resident sessions
because the RLM spawn ledger needs the parent's session file: a
--no-session parent cannot spawn children at all.

* test(acp): spawn lanes stop writing receipts into a dropped temp dir

The receipts TempDir was dropped when spawn_lane returned, before the
cell ran on the prompt: open(spawn.json) raised FileNotFoundError after
the successful spawn, the except arm raised again, and every lane's
tool call ended as an error no test read. The cell now only spawns and
prints the child id; a failed spawn already surfaces through the tool
error.

* fix(acp): a failed MCP admission clears the applied server list

The replace wire command can fail after the worker already applied the
list: the 30 s request timeout drops a lost acknowledgement. The worker
rolls back only when it answers with a failure, so the daemon path left
the servers owned by the abandoned session owner. Clear them
best-effort, an owner-scoped empty replace like close and teardown,
mirroring TS `clearAcpMcpServers(serverNames).catch()` in the
`replaceAcpMcpServers` catch.

* fix(acp): report heartbeat changes and user bash runs like TS

The daemon-attached link dropped the supervisor's daemon-global
heartbeats_changed broadcast and mapped the worker's user-level bash
events to nothing, so a client never learned that the session's
heartbeat schedule changed or saw a shell command another client ran.
An empty tool result also published an empty content block. All three
diverged from the TypeScript adapter (08ff1b2):

- the reader classifies heartbeats_changed as its own frame kind, and
  the consumer publishes session_info_update with heartbeatsChanged at
  origin turn 0, connection-scoped like TS rather than an event of the
  active prompt turn;
- wire_events maps bash_start/bash_output/bash_end to a synthetic tool
  call keyed by run id (prime-agent-bash-<runId>), so the started run,
  its streamed chunks and the settled status are addressable;
- tool_result_text drops empty text blocks before the join and returns
  None for an empty result, so no update carries empty content.

* fix(supervisor): keep the worker's event-before-response order on the client hop

The worker guarantees that every session event a turn emitted reaches the
socket before the command response that ends the turn (the fan-out flush
barrier in write_response_frame). The supervisor's per-client writer broke
that guarantee: its select polled the response and event arms at random, so
a response could be written ahead of events that were published - and
already queued - before it. ACP compensated for the reorder with a 30 s
wait for a post-turn marker.

The writer now polls biased, with the session-event arm ahead of the
response arm, so a queued event is always written before a queued reply -
the order the worker itself sends, restored at the hop. The ACP marker
machinery (the turn_emitted oneshot, the agent run counters, the 30 s
timeout and its failed-before-run special case) is deleted; the settle
reads the stream directly.

A slow-reader e2e drives a large streamed turn through a client that reads
nothing until a second connection's wait_for_idle proves the turn settled
(the probe attaches first and waits for the turn's agent_start, so its
dispatch cannot beat the prompt), then asserts the reply arrived behind
the queued events - counting only events whose wire offset starts past
any kernel socket buffer, >= 8 of them, the test's own precondition -
and after the turn's turn_end and agent_end.

* fix(daemon): long turn routes get the TS 24 h worker budget instead of a 10-minute cap

* fix(acp): the daemon session create sends only an env telemetry opt-out, like the interactive client

* fix(acp): each bound session starts its own event mapping state, like TS
…Intellect-ai#3290)

* factory: port the state-machine harness entry kind, validator, dag compiler, and executor (PrimeIntellect-ai#2397 + PrimeIntellect-ai#2401)

* factory: address review findings (join-pause skip, exact guard equality, cancellation claim, harness spec isolation, refinement update path, digest resume hint)

* factory: second review round (unhashable join entries, validation never raises, JSON-null machine form is absent)

* factory: a terminal entry no longer queues an un-admittable retry

A foreach sibling failing with retries left AFTER its entry already went
terminal (a sibling failed it permanently first) was reset to pending --
_next_pending_instance serves running entries only, while _run_complete
and the stall detector both counted the pending instance as in-flight, so
the control loop never finished the run. The retry is now queued only
when the entry can re-admit it (entry.status == running); a terminal
entry's failed sibling settles as an error instance, and the policy call
is a no-op on a terminal entry (Cursor Bugbot finding on PrimeIntellect-ai#3199).

* factory: a resident-saturated cap fails the run instead of wedging

With every max_parallel slot held by resident instances, queued work can
never be admitted -- a resident never settles, so no slot ever frees,
_run_complete stays false (a pending instance blocks completion), and the
residents keep children in flight, so the no-in-flight stall detector
never fires: the control loop polled forever (two resident entry states
with max_parallel 1, or eight residents plus a task at the default cap).
The loop now detects the un-admittable dead end (_resident_cap_starved:
cap saturated and every running instance a resident) and fails the run
with a deterministic executor error; stop() still tears the resident
children down (Cursor Bugbot finding on PrimeIntellect-ai#3199).

* factory: a resident in flight cannot mask the pending-entry stall

Bugbot review finding on the resident stall series: the dead-end stall
check ran only with nothing in flight, but an admitted resident never
settles, so a pending entry waiting on an input source that never
settled kept the control loop polling collect forever -- in_flight
true, _run_complete false (the pending entry blocks quiescence),
_resident_cap_starved false (it sees queued instances, not unprepared
entries). The run wedged in "running"; only stop() ended it.

The stall detection now also fires when the only in-flight work is
resident instances: residents never settle and can never unblock a
pending entry (they declare no outputs and no outgoing transitions),
so a pending entry with no instances behind them is the same dead end.
stop() still tears the resident children down.

Regression: test_resident_in_flight_stall_fails_the_run_instead_of_wedging
fails without the resident_only_in_flight gate ("never left the running
state") and passes with it; the resident completion tests are unchanged.

* factory: an unsettled optional foreach.over expands to zero items instead of failing

* factory: machine validation rejects a required self-input that can never bind

A required self-input needs the state's own prior settle on its first
entry, which cannot exist, so the entry stays pending until the stall
detector fails the run. Optional self-inputs are the designed loop form
and stay valid; this mirrors the dag compiler's cannot-depend-on-itself
rule (cursor review finding on PrimeIntellect-ai#3199).

* feat(tui): /factory live view with machine diagram highlighting

The /factory live view over the kernel factory executor, for both users
and agents:

- kernel (rlm/factory.py): factory.graph fuses the machine structure
  (states, transitions, guards, entry states, declared order) with the
  live runtime state (status() node data, active nodes, last-fired
  edges, events tail, usage, budget consumed); a stored spec id answers
  the static structure, no ref answers every live run. A bounded
  factory.watch blocks until the run state/instance shape changes or
  the timeout elapses (change detection over a per-run revision the
  ledger-touching seam bumps), then returns the same snapshot plus
  changed.

- host bridge: the out-of-band factory_activity kernel frame (the
  bash_activity precedent: a running cell never delays the live view)
  carries graph/status/watch/run/stop/resume. The session_engine
  factory_host follows the system_router_host pattern from PrimeIntellect-ai#3184: a
  config of session facts captured in create_session, and a run
  preflight that resolves every declared model selector through the
  credential-backed catalog (exact form, TS short form, the
  stale-provider session-model gate), enforces the daemon allowedModels
  pin, and requires request auth before any child spawns.

- daemon: the factory_activity command (session-addressed, advertised
  by the factory_activity capability) reaches the session kernel
  through the bridge; unknown actions and malformed targets fail
  loudly on both sides of the wire.

- TUI: the /factory view - one panel per live run: the machine diagram
  (status-glyphed state rows in declared order, connector rows with
  guard labels, joins once, back edges marked, active nodes bright,
  pending dim, last-fired edges marked), the run state, instances
  running and queued, budget consumed, and the milestone tail.
  Orchestration keys stop/resume the selected run; m copies the same
  graph model as genuine Mermaid source (flowchart TD with classDef
  active styling and linkStyle marks on the fired edges). The refresh
  rides the run collect cycle (a bounded watch on the selected run,
  then the graph list) with signature hysteresis: only a notice-worthy
  run-shape change lights the changed marker, never a per-transition
  repaint.

* fix(tui): the factory review round — wire parity, page entry, hysteresis, and the preflight seam

The kernel<->TUI field-name seam (live probe: the TUI read camelCase
`runId`/`specId` while the kernel graph emitted snake_case, so every run
row dropped and the view was always empty) is fixed on BOTH sides: the
activity lane now emits the protocol's camelCase wire keys end to end
(`_wire_payload` re-keys the reply's rows; the in-kernel conversation
API stays snake_case), and the TUI parser tolerates both spellings —
each side pinned independently (a Python battery test asserts the
reply's wire keys; the TUI fixture is the real reply shape captured
from a live activity probe, with a mechanical snake_case mirror
asserting both spellings parse to the identical struct).

The entry surface reworks to the page family: the activity dock's new
factory group (`⚙ N factory`, arrow-traversable like subagents/
heartbeats/shells) opens the factory page from the dock's Enter and
the click; the `/factory` builtin is dropped (the shells/subagents
pages carry no command either); the always-on 2s poll (the bash poll's
cadence and capability gate) keeps the dock's live-run count current
while the page is closed, and a reply without the runs list reports on
the page's error line instead of painting a fake empty state.

Review-thread fixes: the hysteresis signature drops `elapsed_ms` (the
clock never trips the changed marker); the render budget window keeps
the trailing chrome and the selected run's panel visible (never the
leading-rows truncation); the Mermaid emitter paints pending nodes
with a dim `pending` class (never bright active); the preflight's
unresolvable subagent reference skips only its own state (the other
states' declared models still preflight); the refresh cycle
serializes like the heartbeat refresh (one in flight with a queued
tick — the 2s tick never mints an epoch the in-flight reply can't
match) and the whole cycle sits inside the request bound; a
same-session rebind keeps the page mounted; `graph()` deep-copies each
transition's `from` list (the snapshot owns its rows — a consumer
mutating a join can't corrupt the active run).

Also two platform-hygiene fixes for pre-existing macOS-local merge-gate
blockers (byte-identical failures on the pristine branch head): the
pidfd stubs' `#[must_use]` parity with the linux implementation, and
the boot_reap unix helpers' dead-code marks for their linux-only
callers.

* fix(tui): the second factory review round — scoped preflight, guard identity, and the page seams

Nine review threads on the reworked head, all valid, each with a
mutation-checked pin:

- Scoped harness ids (`local:`/`global:`) now preflight their own store
  (`split_harness_scope` mirrors the kernel harness's
  `_strip_scope_prefix`): a scoped run resolves the exact factory entry
  and subagent references it will execute, where raw map lookups missed
  the prefixed ids and skipped the whole model preflight.
- The fired-edge identity includes the guard: the kernel's fired events
  and last_fired edges carry `when`, and the TUI edge match requires it —
  two guarded transitions sharing one from+to pair never cross-mark.
- The Escape key closes the page (`key_event_to_id` reports "escape";
  the match accepted only "esc").
- The render honors degenerate viewports: the `.max(6)` floor is gone,
  a 1-row viewport renders exactly one row, and the tail-clip keeps the
  hint.
- The Mermaid source neutralizes label-breaking text (the `|` delimiter,
  the node-label quote, newlines) in guards, state ids, and run names.
- watch() rejects NaN timeouts by identity (NaN passes every comparison;
  the deadline would reach asyncio.sleep and raise).
- The activity error reply rides the same wire cap as the success
  reply: a huge validation reason fails loudly under the cap instead of
  exceeding the transport bound.
- The factory cache dies with its session: the attach fold and the
  different-session rebind reset `factory_graph` (the bash registry's
  pattern), and the fold asks for the first frame's refresh — the dock
  count and a reopened page never show the previous session's runs.

* fix(tui): the third factory review round — bounded graph history, live-activity painting, and guard fidelity

Six review threads on the round-2 head, all valid, each with a
mutation-checked pin:

- The unscoped graph reply bounds its terminal history: every live run
  reports, the newest GRAPH_RUNS_WINDOW terminal runs follow, and the
  registry's unbounded completed-run history no longer feeds one
  polling reply's snapshot construction (by-ref snapshots stay
  available for all of them).
- The wire conversion treats guard dicts as verbatim: a guard's
  comparison value mirrors the executor's declared condition, so its
  nested keys never re-key (the structure keys are single words
  already).
- The diagram paints by the node's live activity, not the aggregate
  status: a multi-entry state whose latest entry settled while an
  earlier one still runs stays bright in both the terminal diagram and
  the Mermaid active class.
- The valueless guard label renders `output.path op` (verdict exists,
  not verdictexists) — the path and the separator join the valued
  form's shape.
- The malformed-reply guard requires the runs value to be an array:
  {"runs": {}} is a malformed lane, not zero runs.
- The rebind fold keeps the factory cache when the page was never
  mounted under another session: a same-session rebind no longer wipes
  the dock's live-run count (every rebind path attaches first, so the
  attach fold owns the cache's cross-session reset).

* fix(kernel): the fourth factory review round — non-finite guard values never ride the wire

A non-finite float in a guard comparison value (when.value = NaN or
Infinity, directly or nested in a contains needle list) passed every
validation (a NaN float is a scalar) and rode the reply frames as the
non-JSON NaN/Infinity tokens — every strict consumer of the activity
lane (the host bridge's serde_json parser included) would fail to parse
the reply.

Fixed at the source and belted at the wire:

- _validate_guard rejects non-finite guard values deeply
  (_value_is_finite walks lists and objects): a machine declaring one
  is invalid at create/validate time, so no run or spec graph can ever
  emit one (pinned in test_guard_values_must_be_finite — NaN, +inf,
  -inf, and a nested dict needle).
- _cap_factory_frame fails loudly on a non-finite frame instead of
  emitting the token (json.dumps(..., allow_nan=False) belt; the loud
  error names the non-finite values) — pinned in
  test_a_non_finite_frame_fails_loudly.

Both pins mutation-verified: removing the validation check fails the
source pin, removing the belt fails the wire pin.

* fix(tui): the factory view reads newest-first, the newest run selected on open

* fix(tui): factory strings scrub terminal control bytes at the parse seam

* fix(tui): the RLM child status notices render in the subagent-message class

The operator's 2026-10-01 directive on the factory UX: "the factory
child status notices should be like subagent messages not user
messages". Factory children are kernel children (the executor admits
them with rlm.spawn), and a child that settles without an explicit
reply lands the daemon's terminal notice row in the parent transcript
- the classic subagent-completion injection. The TUI dispatch classed
those rows (rlm_child_terminal_notice / rlm_child_failure) as
injected-prompt rows: the class of turn prompts (heartbeat fires, goal
contexts), so every factory child's exit read as another user-style
turn prompt between the user's own turns.

The seam is the render dispatch (crates/pa-tui/custom_message/mod.rs),
not the kernel: the factory executor emits no transcript rows of its
own (factory.progress stays the deliberately-unported ledger-only
milestone lane), so the Python runtime is untouched. The two notice
types now decode to the agent_message component's class - the same
quiet, counterpart-attributed row the children's own agent_message
mail renders - with the child's session name as the counterpart and
the full notice text as the expandable body, so nothing is lost. The
notices still drive their followUp wake turn (the daemon's notice
action is unchanged); only the transcript row's class moves. The
2026-09-23 diamond-row divergence is superseded and its render code
retired.

Tests: rlm_child_notice_rows_decode_to_the_agent_message_class pins
the class for all three outcome rows (the failure, the no-reply exit,
the cancelled exit) plus the pre-details content-token name fallback -
mutation-checked by reverting the dispatch to the injected-prompt arm
(the pin fails with "must decode to the agent-message class"). The old
diamond-row decode/render pins are retired with the code.

* fix(tui): the factory page mount holds the malformed-reply contract

* docs(skills): agent-facing factory SKILL.md — authoring, running, and watching machine workflows

The factory surface shipped with only the module docstring, a one-line
harness digest hint, and the refinement planner bullet; an agent working
in the RLM had nothing callable to learn the machine form from. This adds
skills/factory/SKILL.md, the missing agent-facing reference:

- what the factory is and the harness entry kind (create_factory validates
  either form at write time; an invalid spec is never stored);
- a complete worked machine spec derived from the seed pr-manager shape
  (entry state, guarded branching, optional-input loop binding, bounded
  max_entries, resident lifecycle) and a dag-form example derived from
  review-sweep (one-join compilation, foreach expansion) — both
  validated against the branch's own validator;
- the authoring reference: states/ports/transitions/guards, the required
  self-input rejection, joins, legal cycles, foreach, residents, the run
  block (max_parallel as a global budget, max_transitions pause/resume),
  failure policies, budgets, and the stall detectors;
- the rlm.factory run/status/stop/resume/graph/watch API with a
  run-and-monitor snippet, the camelCase wire note, and the
  kernel-restart and preview-cap operating limits;
- discovery pointers: the machine library (arriving on the stacked PR) and
  the TUI factory page (dock group, keys, Mermaid export).

* docs(skills): trim the factory SKILL.md to a discovery stub pointing at rlm.factory.help()

The user ruled that agents read the factory docs via rlm.factory.help():
the full authoring reference now lives in the kernel (embedded from this
skill into prime-agent-runtime/src/rlm/factory.py on the factory-core
branch), so the SKILL.md keeps a thin discovery stub to prevent two
drifting copies: what the factory is and its harness entry kind, the
machine-library and TUI-page pointers, and the prominent instruction to
call rlm.factory.help() for the full authoring reference and API guide.

* factory: rlm.factory.help() returns the embedded authoring reference

The user design call for the factory surface: agents must get the
factory documentation from rlm.factory.help() - no file-path reading.
The fact-checked 282-line agent-facing reference (authoring rules,
guards/joins/cycles, foreach, budgets and policies, stall detectors,
and the API with worked examples) is embedded as the module-level
constant FACTORY_HELP in rlm/factory.py, and the _RLMFactoryNamespace
gains a sync help() -> str returning it - no filesystem resolution, so
packaged kernels (where the repo layout is not adjacent) see the same
guide. The factory module docstring and the namespace docstring both
point at help() (help(rlm.factory) shows the API plus the pointer), and
factory.py's __all__ exports the constant. The reference content is the
skills/factory SKILL.md guide (frontmatter stripped) - the discovery
stub on the stacked view branch points agents at this call.

The worked examples bound their emitted payloads (a Macroscope review
finding on the embedded guide): the pr-manager verdict caps its
findings at three one-line findings and the review-sweep file list
caps at the eight most relevant paths, because captured answers are
capped previews (~160-200 chars) - an unbounded payload truncates at
the cap and fails to bind. The example commentary states the rule; the
Safety section keeps the compact-outputs guidance.

Tests: test_factory_help_returns_the_full_reference pins the
call-level surface (rlm.factory.help()): the section structure, the
guard op set, foreach expansion, max_parallel as the run's global
budget, the stall detectors, the stop/resume contract, the
run/status/stop/resume/graph/watch snippet, and the bounded example
payloads - mutation-checked by emptying the constant (the pin fails
on '# Factory').

* feat(tui): per-stage agent occupancy on the factory diagrams

The factory page reads as a page of machine diagrams with per-stage
agent counts. The kernel's graph/status node rows carry `running`/
`queued` (admitted children in flight / prepared instances waiting
for a parallel slot) in `_state_report` - the exact `status()` node
shape, single-word keys riding the wire's camelCase conversion
unchanged. The ASCII diagram's node labels render the counts
(`reviewing (3 run · 2 queued)`, a stage at rest keeps its plain
label, row colors unchanged), and the Mermaid export's node labels
carry the same fragment so a pasted diagram shows stage occupancy;
the TUI reads the kernel's counts with the instance rows as the
older-kernel fallback.

* fix(kernel): a done run with children in flight stays live in the unscoped graph

Regression (Macroscope): the unscoped graph classified liveness by run
state alone, so a `done` run whose resident child is still in flight
(admitted residents never block completion - the finished milestone
tells the operator to `rlm.factory.stop()` them) dropped out of the
reply once it left the GRAPH_RUNS_WINDOW terminal history: the page
lost the run and its stop control while the child kept running.
Liveness is children-shaped: a run with any instance still in flight
reports regardless of the window, and the window holds only runs with
no child in flight. Pinned in
test_a_done_run_with_children_in_flight_reports_live (reverting the
children clause drops the run from the reply; a stopped run outside
the window still leaves it).

* fix(tui): the factory page rides the picker keyset, and the scale battery

The copy-mermaid action is gone (the m key, the CopyMermaid action,
the clipboard arm, the mermaid emitter): the kernel graph data is
unchanged, so agents may still build a Mermaid diagram from
rlm.factory.graph() - only the TUI button dies.

The page rides the activity pages' picker keyset (arrows + Enter + Esc,
the heartbeats/bash pages' family): the arrows move the run selection,
Enter opens the selected run's in-page action rows (the offered set
reads the run's live state, tracked by action identity so a fold clamps
it - never a stale target), Enter runs the tracked action, Esc backs
out or closes. The letters are not page keys.

One defect found by the scale battery and fixed: the dock's count
filtered on state alone, dropping a done run whose resident children
still run - live by the kernel's own unscoped-list rule, still carrying
its stop control. The count now reads the same one liveness rule
(FactoryRunSnapshot::is_live).

The scale battery: the seven-state pr-manager-shaped machine (the
review/fix loop on guarded branches, the fan-in join, the resident
watchdog) renders its whole shape; the fifteen-run world (live,
stopping, paused, done-with-residents, failed, stopped; many instances
at a stage) pins the liveness count, the selection stability across
folds and trims, the render window slide at fifteen panels, the
refresh tick's notice-worthy markers at scale (the occupancy drain, the
fired-marker move, the active-node transition), the focused-run
orchestration, and the rapid-churn coherence (interleaved keys, no
dropped keypress, no stale-index action).

* factory: an optional input binds the sentinel over every no-value source

Review finding (PR PrimeIntellect-ai#3199, cursor Medium): _prepare_entry bound the
optional null sentinel only while the source had NO settle. Once the
source settled with an error, a missing port, or a failed JSON capture,
the optional input still failed the dependent entry -- one failed fixer
iteration or unparseable fix report killed the review/fix loop that
optional inputs exist to enable.

Binding now classifies the source first: a settled source either offers
a captured value or a no-value failure; the optional form binds the null
sentinel (and an optional foreach.over expands to zero items) in every
no-value case, and only the required form fails over a settled-but-
valueless source -- the required form still just waits while the source
never settled. The authoring reference documents the decided errored-
source semantics.

Regression coverage: optional input over an errored source re-enters with
the sentinel; an optional foreach.over over an errored source expands
empty; an optional input over a settled source with no captured value
proceeds while the required form fails ('has no captured output'); an
optional input over a failed JSON capture proceeds while the required
form fails ('no JSON object containing output'); a required input over
an errored source still fails the dependent entry.

* factory: run.max_children bounds total admissions; whitespace-only inline subagent fields are write-time invalid

* factory: an opt-in gate -- `factory.enabled` (default off) refuses the namespace

The factory ships disabled: the user turns it on with `/factory on` (the
persisted `factory.enabled` setting in the agent dir's settings.json, the
same nested-camelCase document the daemon and TUI settings surface write,
shaped like compaction/agentTraces). While it is off, every `rlm.factory`
call except `help()` -- run, status, stop, resume (and the later
graph/watch) -- and every factory harness write (`create_factory`,
`update_factory`, and the generic create/update/upsert paths, so a
refinement edit cannot author a factory either) refuses with ONE exact
clean message: "the factory is disabled; run /factory on to enable it".
`help()` stays available so the embedded authoring reference is readable
before opting in.

The settings seam (`factory_enabled()` in factory.py) resolves the agent
dir the way the rest of the runtime does and reads the file leniently
like the Rust loader: a missing file or key, a wrong-typed value, or a
corrupt document all read as the disabled default -- fail-closed, a clean
refusal, never a crash. The refusal precedes spec resolution (run) and
spec validation (creates/updates), so the disabled message is
unconditional while off. Deleting factory entries stays available:
cleanup is not authoring or execution.

FACTORY_HELP's opening documents the enabled/disabled contract with the
/factory on|off|status pointer and the exact refusal. Pins
(mutation-checked) in test_factory.py: FactorySettingReadTest (the real
settings-document round-trip; missing/wrong-typed/corrupt reads stay
disabled; removing the fail-closed default fails it),
FactoryOptInGateTest (empty settings refuse run with the exact message
and start nothing; status/stop/resume refuse; create_factory refuses
before spec validation and persists nothing; generic updates refuse;
delete stays available; help() answers while disabled; enabled settings
-> run proceeds through the scripted kernel; removing the run gate or the
harness gates fails the refusals), and FactoryHelpTest's opt-in
assertions (emptying the guide's contract fails them). The shared
executor setUp isolates the agent dir and writes the enabled setting in
the real shape, so every executor test runs through the live gate;
test_harness's module setup installs the same enabled setting for its
factory CRUD tests.

* factory: configured subagent names label children; the guide matches the namespace

Two review threads on the port, both real:

- The inline subagent `name` was accepted at write time and then dropped:
  _resolve_subagents discarded it and _admit always built the generated
  _child_name, so a state configured with {"prompt": ..., "name":
  "reviewer"} spawned sw-reviewing-<run>-i0 instead of reviewer - the label
  agents message the child by. The name now rides through run creation to
  the spawn call: the state's first instance is named exactly the
  configured name, and later instances (max_entries re-entry, foreach
  fan-out) and retries keep the generated label's -i<n>/-a<n> suffixes,
  because one state's settled children stay registered for the run's life
  and the supervisor rejects duplicate sibling names. Write-time checks
  keep a persistable factory spawnable: the name is capped at the host's
  64-character subagent-name limit (SUBAGENT_NAME_MAX_LENGTH), and names
  duplicated across states - which would collide at spawn time - are
  rejected like duplicate state ids.

- FACTORY_HELP advertised `rlm.factory.watch(...)` and `rlm.factory.graph()`
  examples, but the namespace ships run/status/stop/resume/help only, so an
  agent following the guide hit AttributeError. Those views are the stacked
  live-view PR's kernel surface (a 675-line implementation), so the smallest
  correct fix here is the exact invariant: the guide now documents only what
  the namespace has (the call examples are dropped, the gate sentence and
  the safety bullet match, and the views are named as roadmap prose), pinned
  by a test that extracts every dotted rlm.factory.<call>(...) the guide
  teaches and asserts it exists on the namespace.

Mutation-checked: reverting the spawn-side or resolve-side name use fails
the executor pins; removing each write-time check fails its validation
pin; re-adding a watch() example fails the guide/namespace pin.

Tests: cd prime-agent-runtime && uv run python -m unittest test.test_factory
(174 green); the full unittest discover run differs from HEAD only in the
pre-existing test_bash journal and agent-message failures (verified at
8151b72).

* factory: the guide's activity-lane bullet names the stacked view PR's wire

The bullet documented the camelCase factory_activity wire (runId, specId,
timeoutMs) in the present tense, but that lane is the stacked live-view
PR's surface - this branch's milestone notices ride factory.progress with
snake_case keys. Same accuracy class as the watch/graph trim in the
parent commit: the guide describes what this branch ships, and names the
stacked surface as roadmap. Tests stay green (174).

* factory: suffixed spawn labels fit the host cap; shadowed names rejected at write time

Second review round on the configured-subagent-name fix, two findings
(both real):

- A configured name at the 64-character cap fits its first instance, but
  the -i<n>/-a<n> suffixes for re-entry, foreach fan-out, and retries can
  push the label past the host's spawn-name cap, so every later admission
  of an otherwise valid state failed. _spawn_label now builds the suffix
  first and shrinks an overflowing base with a digest of the full name -
  exactly the generated label's state-id token trick - because a bare
  truncation could collide for two long names sharing the truncated
  prefix; distinct names stay distinct and every admission fits.
- A state configured as foo-i1 passes validation but collides at spawn
  time with foo's later instances. The write-time duplicate check now
  rejects a name another configured name can suffix onto, in either
  order (the never-generated -i0/-a1 do not shadow and stay valid).

Pins (mutation-checked, each fails when its check is reverted):
SpawnLabelTest.test_suffixed_labels_stay_within_the_host_cap (cap fit,
digest distinctness, retry and combined suffixes) and the shadowing cases
of test_inline_subagent_name_length_and_uniqueness. The guide's name
contract states both rules.

Tests: cd prime-agent-runtime && uv run python -m unittest test.test_factory
(175 green).

* factory: the shadowing error names the state that generates the labels

Cursor review finding (Low, real): the write-time shadowing check matched
both collision directions but always attributed the -i<n>/-a<n> labels to
the earlier state's name. When the shorter name is declared second, the
current state's name generates the suffixed labels and the earlier state's
name is one of them - the message claimed the reverse. The check now
resolves the direction first: the earlier-name-base case keeps its
message, and the current-name-base case says this state's name suffixed by
re-entry, foreach, and retries collides with the earlier state.

Pin (mutation-checked: swapping the messages fails it): the reversed
declaration-order case of
test_inline_subagent_name_length_and_uniqueness asserts the exact
attribution, and the forward case pins its own.

* factory: the opt-in gate's client surface — /factory on|off|status, the shared settings key, the lane advertisement gate

The factory's client surface for its opt-in gate: the /factory
[on|off|status] client command (the /nightly family) persists
factory.enabled through the pa-core settings manager — FactorySettings
shaped like agentTraces in the agent dir's settings.json, the same
nested-camelCase key the kernel's factory gate reads — plus the
ClientSettings trait pair and the CLI setting macro. The daemon's
factory_activity lane advertisement reads the same setting fresh per
connection (advertised_server_capabilities filters the lane while the
factory stays disabled, on both hello surfaces — the supervisor's and
the worker's), and the dock's factory group mounts exactly while the
daemon advertises the lane (ActivityDock::factory_group, driven by the
hello's serverCapabilities).

* factory: complete the opt-in gate — the merged-tree reconciliation, the pins, and the docs

The merged tree's gate completion: graph_factory and watch_factory join
the namespace's require_factory_enabled() gates and the out-of-band
activity seam gates at its top (the lane's run action would otherwise
bypass the namespace's gate), so every gated surface refuses with the
one message while factory.enabled is off. FACTORY_HELP teaches the
graph()/watch() call examples this branch's namespace ships (the
guide-matches-namespace invariant stays: every dotted call the guide
teaches exists on the namespace), and the guide's TUI bullet matches
the shipped keyset (arrows + Enter + Esc; the old j/k/s/r/m letters
never were page keys). The kernel usage block unifies behind
_usage_report (the merged status snapshot carried the core's
max_children field while the graph's shared report lacked it — the
graph-equals-status fusion pin caught the drift).

Pins: the daemon lane advertisement (no settings -> no lane; enabled ->
advertised; disabled again -> gone), the dock's unadvertised-lane
contract (no row segment, no traversal, no click region), the headless
/factory e2e (the status arm's disabled default, the on/off round trip
through the settings seam, the usage line, and no factory group while
the lane is unadvertised), the pa-cli seam round-trip's settings-file
content assertions (factory.enabled true/false beside the other keys),
and the kernel gate's graph/watch/activity refusals. Mutation-checked:
removing the advertisement filter, the dock group condition, the panels
wiring, the settings write, or each kernel gate fails its named pin.
The skills/factory SKILL.md stub documents the opt-in contract (default
off, /factory on to enable, help() answers while disabled).

* factory: the review round — the gate's global-scope read, the graph's run block, and the view's fold/caret/dead-flag fixes

Macroscope and Cursor findings on the opt-in head, all real:

- get_factory_enabled reads the GLOBAL scope only (Macroscope): the
  merged global-and-project read could diverge from the kernel's
  agent-dir-file gate — a project .prime/agent/settings.json with
  factory.enabled could advertise the lane or flip /factory status
  while the kernel refused (or the reverse). The getter now reads the
  exact document set_factory_enabled writes and the kernel gate reads;
  pinned by factory_enabled_reads_the_global_scope_only (a project
  override never enables; the write lands in the global file).
- Both hello surfaces read the gate behind spawn_blocking (Macroscope):
  the settings file's metadata and locked read never blocks an
  executor thread on the handshake path (the daemon's settings-read
  posture); the read stays fresh per connection.
- The graph's machine block carries run.max_children (Macroscope):
  _machine_structure omitted the total-admission limit, so a consumer
  could not reconstruct the validated configuration; pinned by the
  fuses test's default and the declared-value graph test.
- The malformed fold keeps the dock's cached reply (Cursor): a reply
  without the runs list used to clobber factory_graph anyway, zeroing
  the dock's live-run count and a remount's cache; the fold now takes
  only well-formed batches (fold_factory_reply), pinned by the fold
  test (mutation-checked).
- The factory page suppresses the stale editor caret (Macroscope):
  frame_cursor now treats the open factory view as an input-less
  overlay (the settings menu's guard family), pinned by
  the_factory_page_suppresses_the_stale_editor_cursor.
- The dead factory_view_open_flag is gone (Cursor): written at four
  sites, read nowhere — the close arm and the rebind fold key off
  factory_view_session and view.factory_view, and the key dispatch
  reads view.factory_view.

* factory: /factory off refuses while runs are live — the lifecycle guard

kevinjosethomas's finding on the retargeted head: /factory off while a
run is active writes factory.enabled=false and strands the run — the
kernel control loop is not gated by the setting (it keeps admitting and
collecting children), but every factory surface (rlm.factory.stop/
status/graph, the activity lane, and the page after the next client
start) refuses, so a running factory loses its stop and visibility path
until the factory is enabled again.

The guard: /factory off refuses while the session's kernel reports live
runs, naming the count and the stop path (the page's stop action or
rlm.factory.stop); the runs stop first, then the off write lands. The
count rides live_factory_runs (session_ui/factory.rs): one bounded
factory_activity graph request through the lane — the same lane and
the same is_live liveness rule the dock's count reads (a live state, or
children still in flight, so a done run with resident children counts
exactly like its panel and its stop control). The unadvertised-lane and
query-failure classes (an older daemon, no kernel booted) cannot carry
live runs, so those proceed unchanged.

Pins (factory_command_headless.rs, the mock daemon advertising the lane
and answering the graph request a real kernel would): the refusal names
the live count (a running run and a done-with-residents run both count;
a fully terminal run never blocks), the gate stays enabled after the
refused off, and the off proceeds once no run is live —
mutation-checked: removing the guard fails the refusal pin.

Riding along: the base-flip merge's composition repairs (the factory
arm's match closers, the shared-import wrap) and the /telemetry
command's ClientSettings stub seam (telemetry_command_headless.rs).

* factory: the macroscope round — the guard-value key rule and the preflight's null machine

Two review findings, fixed at the branch's own seams:

- _value_is_finite (rlm/factory.py) checked a guard comparison value's
  floats recursively but never its object KEYS: a dict with a non-finite
  float key or a non-string key passed validation, then broke the reply
  frames at the wire — json.dumps(..., allow_nan=False) raises on the NaN
  key (the lane answers the wire-cap error instead of the graph), and a
  non-string key is either coerced (the wire object no longer matches the
  declared machine) or rejected outright. The dict branch now requires
  string keys, the same source-invalid rule as the non-finite floats.

- FactoryHost::spec_model_selectors read machine.or(dag) without the
  null-is-absent filter every settled seam makes: a stored
  {"dag": ..., "machine": null} (a dag-form edit the planner accepts and
  only the /refine writer can store) read as Some(Value::Null), the
  fall-through to the dag never ran, and the preflight answered no
  declared models — exempting the dag's inline model selections from the
  catalog, allowlist, and auth checks before any child spawned. Both
  reads now filter null exactly like refinement::planner.

Pins: test_guard_value_object_keys_must_be_strings (a NaN float key, an
int key, and a tuple key reject; string keys stay valid) and
a_null_machine_falls_through_to_the_dag_spec (the dag form's declared
models preflight; the null-dag mirror; the allowlist gate refuses the run
end to end) — both mutation-checked: reverting either fix fails its pin.

* factory: the second review round — the off guard fails closed on an unreadable count, the preflight leaves the executor

- /factory off's guard now fails closed when the lane-advertised client
  cannot read the live-run count: a timed-out or malformed graph reply
  cannot prove zero live runs, and an unknown liveness must not open the
  gate — the refusal names the unreadable count and the retry, and the
  gate stays enabled. A client whose hello never advertised the lane
  keeps the fail-open read (an older daemon has no executor to protect;
  a client started before /factory on already sees the frozen hello the
  settled surfaces rule describes), and the count query itself still
  rides regardless of the advertisement — the hello-predates-the-gate
  client counts live runs exactly like the advertised one.

- The factory run preflight (engine.rs) reads the harness states, the
  model catalog, and the auth caches from disk — that blocking work now
  rides tokio::task::spawn_blocking instead of the async lane, the
  daemon's established settings-read posture (the advertisement gate's
  hello reads), so a stalled filesystem can never stall the worker's
  other activity. FactoryHost is Clone for the move; the semantics are
  unchanged (an executor-topology fix, so no behavioral pin — the
  preflight batteries still pin the resolution, allowlist, and auth
  outcomes).

Pins: factory_off_refuses_when_the_advertised_lane_cannot_count (the
malformed-lane refusal, the gate stays enabled; mutation-checked:
removing the fail-closed arm fails the pin), the two None-path pins
split by advertisement (the unadvertised client proceeds on an
unreadable count; the hello-predates client still counts), and the
existing live-count pins unchanged.

* factory: the guard-value traversal stops at a cycle

A self-referential list or dict passed as a `contains` guard value made
`_value_is_finite` chase the cycle to a RecursionError — the write path
crashed instead of answering the validation error. The traversal now
threads the per-branch ancestry and rejects a container it is already
inside: the encoder refuses circular references outright, so a cycle can
never be a valid comparison value. A shared-but-acyclic reference (the
same object appearing twice, a diamond) stays valid — each branch checks
it independently, so only a true ancestor repeat reads as a cycle.

Pinned in test_guard_value_cycles_reject_without_exhausting_the_stack:
a self-referential list and a self-referential dict each answer the
finite validation error, and a diamond stays valid. Mutation-checked:
disabling the ancestry check fails the pin with the RecursionError.

* factory: the preflight resolves a subagent reference the kernel's way — tier by tier, id then title

The preflight's reference lookup swept the id across both harness stores
before any title scan, so a global ID answered before a local TITLE of
the same name — while the kernel's own resolution (verified against the
kernel: its harness get answers the id before the title scan, within
the tier it reads) spawns the local titled subagent. The preflight then
validated, allowlisted, and authenticated the GLOBAL id's model while
the run spawned the local titled subagent's model — the allowlist pin
judged the wrong model, and a blocked model could spawn.

The lookup now walks the kernel's order: for each tier (local, then
global, filtered by the reference's scope prefix) the id, then the
title, before the next tier — so a local title wins over a global id of
the same name, a local id still shadows a global id, and a scoped
reference still resolves its own store exactly as before.

Pinned in a_local_title_wins_over_a_global_id_of_the_same_name
(factory_host/tests.rs): a local TITLE "researcher" against a global ID
"researcher" reads the local titled subagent's model, a gate allowing
only the global id's model refuses the run, and a gate allowing the
actual model passes it. Mutation-checked: restoring the id-first sweep
fails the pin (the selectors read the global id's model).

* factory: the preflight's title match takes the kernel's list order, and guard-value leaves stay JSON

Two more review findings at the branch's own seams:

- Among duplicate titles within a tier, the preflight's title scan now
  picks the (path, id)-least entry — the kernel's own title scan reads
  `harness.list`, sorted by (kind, path, title, id), so among same-title
  entries it picks the (path, id)-least; the preflight's old
  map-iteration find offered whichever entry the store's iteration
  happened to surface first, validating (and allowlisting) one
  metadata.model while the kernel executed another.

- `_value_is_finite` now rejects leaves outside JSON's scalar set: a
  tuple (or a set, bytes, any container the JSON grammar has no spelling
  for) used to pass validation and serialize as something other than
  the declared shape — an array — carrying any non-finite float inside
  it past the finiteness traversal to the saved spec and the wire
  frames, where the strict consumers reject the whole frame. The
  validation message now names the JSON-shape rule (the finiteness
  wording covered only the float class).

Pins: a_duplicate_title_resolves_the_kernel_list_order (same-title
locals resolve the (path, id)-least entry's model; mutation-checked:
max_by instead of min_by fails the pin), and the object-keys pin extends
to the non-JSON leaf shapes — (float('nan'),), a plain tuple, a set,
and bytes each reject (mutation-checked: restoring the True fall-through
fails the pin).

* factory: the guard-value depth bound and the local-title test's local spec

Two macroscope threads on the retargeted head (both follow-ons to the
just-closed guard-value/preflight rounds):

- _value_is_finite descended one recursion per nesting level with no
  bound: a deep-but-ACYCLIC container (the cycle rule's other half)
  chased the nesting to the interpreter's stack -- RecursionError on
  the write path instead of the validation answer (measured crash
  floor: ~499 levels at the default recursion limit, ~2 frames per
  level). The traversal now bounds nesting at MAX_GUARD_VALUE_DEPTH
  (256, half the crash floor, generous past any real guard value):
  every downstream seam recurses per level the same way (the
  snapshot's deepcopy, the wire conversion, the reply frames'
  encoder), so a value past the bound cannot ride any of them. The
  rejection joins the one finite-JSON-data message (which now
  enumerates the depth rule). Pinned:
  test_guard_value_depth_rejects_without_exhausting_the_stack (a
  306-deep value answers the validation error, a shallow nested value
  stays valid; mutation-checked: without the bound the deep value
  passes validation and the pin fails).

- a_local_title_wins_over_a_global_id_of_the_same_name stored its
  spec only in the GLOBAL harness while the titled subagent lives
  locally: the kernel's unprefixed spec lookup reads one store (its
  harness get has no local-then-global fall-through -- verified
  against harness.py's get: _global_target(global_=False) is None, so
  the unprefixed lookup answers the local entries alone), so the run
  the end-to-end allowlist pins judge would reject as an unknown spec
  before any spawn. The spec entry now also lives in the local harness
  the test configures -- the bypass scenario is a run the kernel
  actually accepts. The host's spec_model_selectors doc line claimed
  the harness's unprefixed get shares its local-then-global tier
  order; corrected (the host is deliberately broader; the kernel's
  run() stays the authority).

* factory: the no-kernel off read is a definitive zero, and the wire cap never drops a live run

* factory: the page's picker keys paint on the key — the input loop's render contract, pinned end to end

* factory: the overlay's activity counts the instance layer - a failed foreach entry's running siblings keep the stage active, pinned at both seams

---------

Co-authored-by: Sebastian <sebastian@primeintellect.ai>
* fix: detach daemon and update restart from client terminal session

* test: pin the launcher client env and bound the detached-daemon cleanup

the launcher e2e now runs ensure_daemon_running_with in a re-exec'd
client process with PRIME_AGENT_CODING_AGENT_DIR, TMPDIR, and the worker
scrub list pinned on its spawn, so the launched supervisor writes into
the fixture instead of the ambient agent dir (verified: the old shape
leaked auth.json, logs, and daemon-workers into a poison dir); the
detached-daemon guard now waits bounded for the socket to stop accepting
after the shutdown command and SIGKILLs the supervisor's process group
when it never exits.

* retrigger ci after a registered shard-4 flake
* input goes live at first paint — the session open runs beside the run loop

The operator's input-latency report (2026-10-03): the first frame lands
fast, but time-to-type waits on the whole open pipeline — warm it
measured ~174ms past first paint (isolated probe), and on cold launches
the create/attach/dock folds stretched that to 0.6-1.6s. Root cause:
the port gated the ENTIRE run loop on the session open; TS `init()` runs
`ui.start()` (the input surface is live) and awaits
`rebindCurrentSession()` on an already-serving event loop.

- pa-tui: `run_interactive_surface` starts at PAINT (a NEW chat's
  chrome mounts before the open, as today); `SessionUi::open` runs as an
  in-flight background task; an OPENING loop serves editor-level input
  (editor.handle_input, paste, resize) at the run loop's own
  MIN_RENDER_INTERVAL cadence and queues everything the session owes
  (submits, commands, overlay keys, mouse) in the existing pending
  VecDeque, in order — the first queue parks the echo path so the editor
  always holds a prefix of the input stream. On open completion ONE fold
  runs today's post-open block unchanged (stats/rebuild/notices/dock
  folds — the zero-shift dock ruling kept) and the run falls into
  today's main loop, draining the queue first: a typed-ahead submit is
  held and delivered at the fold, never dropped. app.exit (ctrl+d) on
  the empty editor still exits immediately. One carve-out: a first-run
  onboarding launch keeps the base input ordering — the login/trace
  panes own the input channel after the open (the phase needs the
  session, so it cannot run during the opening phase), and the opening
  loop leaves the channel to them instead of serving the editor.
- pa-core (operator ruling): after the eager kernel build completes,
  the prewarm kicks the kernel's generic-MCP open in the background (the
  bounded mcp_tool_listing the connections view uses). The join point
  stays at first use: an early tool call joins the in-flight open and
  succeeds, bounded, never an error.
- pa-daemon: the passive scheduled-jobs snapshot warms at daemon boot
  (the scan spawns beside the other boot passes, before the accept loop
  serves), so the first client heartbeats_list serves the warm snapshot
  instead of scanning the session-artifacts tree inline inside the
  client's open. Invalidation semantics unchanged; a client that
  connects before the scan lands joins it exactly as today.

Red-first verifiers: a real-pty e2e (crates/pa-cli/tests/
input_live_open_pty_e2e.rs) against a mock supervisor whose attach
stalls 2.5s — keystrokes must echo while the open is stalled (an
ordering oracle: the echo frame precedes the attach's content frame in
the byte stream) and the typed-ahead text + queued Enter must submit
once the session lands; kernel_mcp_settle.rs pins the kernel-boot join
(a mid-boot tool call waits the in-flight build and succeeds) and the
MCP settle (the configured server spawns with no user use anywhere;
the first call, issued mid-handshake, waits and succeeds); the
scheduling_catalog unit proves the boot warmup serves the first read
without an inline scan.

Measurement (the conductor's input-live probe, renderer-grid echo,
matched pair, send_at=0): before (base 2b962da) 411ms first client on
a fresh daemon / 226-227ms warm-scanned; after 61-64ms on every trial
(the daemon warmup covers the first client; echo-after-frame 193ms ->
28ms).

* the opening phase honors the keymap and the stash; the eager MCP settle leaves the cell queue

Pre-bar review wave (three findings on the pushed head):

- pa-tui (keymap-blind opening dispatch): the opening loop's editor echo
  now consults the effective keybindings — a key the post-open dispatch
  would claim for an app/session action queues behind the open and
  dispatches through the full keymap-aware ladder at the fold, instead
  of echoing into the editor as a stray motion (default `left` on the
  empty editor is `app.agents.back`, not a cursor move; any user-bound
  single char or space runs its action). The claim set is the
  fresh-state projection of SessionUi::handle_key's ladder
  (`opening_echo_key_claimed`), pinned by unit tests for the default and
  the user-bound shapes.
- pa-tui (stash/early-input ordering): a session-reopen launch never
  accepts typed-ahead into the editor during the open. The fold's
  `restore_prompt_stash_on_open` gates on the editor being empty, so
  pre-restore input would silently consume a stashed draft's restore —
  and the opening exit arm would exit the session on Ctrl+D against the
  pre-restore empty editor. Reopen typed-ahead queues and dispatches
  after the fold's restore (base ordering exactly); new-session launches
  (no stash can exist) keep the echo path. Pinned by a two-run headless
  e2e: draft -> agents-view auto-stash -> stalled reopen with Ctrl+D
  during the open -> the draft restores before the queued exit key
  evaluates and the run stays open.
- pa-core + the kernel runtime (eager MCP status on the cell queue): the
  eager settle's mcp_status request rode the manager's single execution
  queue, so a slow or unreachable server parked the user's first python
  cell behind its handshake for up to the per-server timeout. Both
  halves now have a dedicated lane: the kernel runtime's serve loop
  dispatches mcp_status to a concurrent task (the registry's per-server
  locks still join an early user call onto the same in-flight open), and
  the host's mcp_tool_listing resolves its done event by request id
  without taking the execution slot (the factory-activity lane's shape),
  bounded by the per-server timeout plus a settle margin. Pinned
  red-first: with the settle's listing provably in flight on a hanging
  server, the first python cell completes without waiting (the queued
  shape waited 8.5s out the handshake).

* the boot warmup waits out the adopt pass; the kernel tests explain their lock allow

Two bot-thread clears from the review of bd21410:

- pa-daemon: the passive-catalog warmup now waits for the boot's adopt
  pass (a watch signal; fail-open when the sender is gone) before
  scanning. The scan's live-worker filter consults the registry, so a
  scan racing adoption would cache the just-adopted worker's artifacts
  as a passive row and serve the stale row for the snapshot's refresh
  window — adoption never invalidates the catalog. A plain startup's
  adopt pass is ms-scale, so the snapshot still lands well before the
  first client read. Pinned: with the adopt signal unfired the snapshot
  never lands; once fired, it does.
- pa-core: the kernel tests' `clippy::await_holding_lock` allows now
  carry the reason next to the attribute (LIVE_KERNEL_LOCK is
  process-global env serialization — the lock's own doc), matching the
  crate's explain-exceptions-next-to-the-allow rule.

* the adopt-order pin asserts the waiter, not the scan's absence

The cursor bot's methodology finding on the new ordering test: a fixed
150ms sleep followed by an empty-snapshot assertion was the pass
condition, not a failure bound — a buggy helper that returned early
could still pass on a slow runner while the empty-tree scan had not
stored its snapshot yet. The negative pin now sits on the waiter
itself: with the adopt signal unfired the helper must stay PENDING
through the whole window (an early return finishes in microseconds and
fails the assert deterministically; a correct helper can only return on
the signal or the sender's death, neither of which happens). The
positive pin keeps the poll-with-deadline scan assertion and adds a
bounded waiter completion.

---------

Co-authored-by: Seth Karten <32787133+sethkarten@users.noreply.github.com>
* send the json content type on provider requests

* keep the content-type comments to current behavior
…i#3347)

* pa-daemon: free the ACP prompt slot before the response leaves

The ACP turn settle sends its response through the async FrameSink and
only then clears the connection's prompt slot: the writer task can put
the response on the wire while the turn task still waits on the
connection mutex, so a client that prompts again the instant it reads
the response races the slot clear and gets the still-set slot's 'A
prompt turn is already running' refusal — the CI-wave flake shape
behind acp_threshold_auto_compaction_publishes_the_compaction_meta's
second prompt (registered red-acp-auto-compaction-20261002-1, stopReason
null at acp_mode_e2e.rs:1729). Clear the slot before every settle-path
send, matching the daemon-attached path's release_turn_slot ordering.

The turn-failure text now also echoes to stderr (a stop-reason assert
loses the internal_error message), the failing test prints the whole
response envelope on mismatch, and a new test opens the admission
window on twenty sequential prompts.

* state the admission contract in the settled-prompt test's doc comment
* fix misleading clipboard success when tmux blocks osc52

* fix clipboard copy routing through local tools and tmux

* fix unconfirmed clipboard reporting and e2e expectations

* skip remote clipboard helpers before terminal forwarding

* retain oversized remote clipboard helper fallback

* report the oversized remote clipboard fallback honestly

* pin the remote helper outcome with a regression test and trim the duplicated comment

* drop the history-narrating comment from the remote helper regression test

* drain tmux probe pipes, forward to the pane's client, and stop blaming tmux for oversized copies

* pin the probe drain with a pipe-filling regression test

* trim the probe drain doc to its current contract

* gate the clipboard cap by length and encode only for the osc52 write

* satisfy the workspace clippy gate: backtick pipe_to, div_ceil, use before statements

* read set-clipboard from its server scope so forwarding survives legacy tmux

* retrigger ci after a shard-4 timing flake

* retrigger ci after a registered shard-4 flake

* pa-cli: settle the slash-menu dispatch race on the catalog refresh landing (flake fix)

* state the catalog barrier's two cases

* retrigger ci after a registered wave-b10 flake
… gaps (PrimeIntellect-ai#3303)

* fix(kernel): share in-flight startup across first touch and teardown

* fix(kernel): close the panic wedge and the stop-gate gap in the shared startup

Review follow-ups on PR PrimeIntellect-ai#3257 (both blockers independently verified by
both adversarial reviewers at 21da7aa):

- F1 panic wedge: a panic in run_startup or a progress listener skipped
  the memo clear and the done send, which closed the watch and wedged
  every later ensure() on the dead memo. The boot now runs on its own
  JoinHandle, so the publisher always clears the memo and sends the
  result, converts a panicked boot into a "kernel startup task failed"
  failure, and clears the listener state so the next ensure() boots
  fresh (self-heal; the memo re-arms on close). The watch now carries
  the startup result itself: joined callers keep the manager even when
  a concurrent stop takes the provisioner's live owner (TS
  managerPromise), and a dispose-raced boot rejects with the honest
  disposed-startup cause instead of handing callers a dead manager.

- F2 stop-gate gap: stop_kernel took the manager and installed the
  pending_stop gate in two separate lock passes; a revival ensure in the
  gap captured the previous gate and could restore against a snapshot
  still being flushed. The gate is now installed in the same lock block
  that takes the manager.

- F3 dispose fixture vacuity: the dispose leg pins the absolute
  interpreter count at dispose-return (exactly one spawn; the aborted
  retry never ran) in addition to the stasis assert.

- Fixtures: a panicking progress handler must not poison the next boot
  (the re-arm regression leg), and a revival boot must wait for the
  in-flight stop's held host request before its first progress stage
  (exact ordering barrier, not flush timing).

* fix(kernel): close the provisioner's four residual mid-boot lifecycle gaps

Staged hardening set for the PrimeIntellect-ai#3257 shared-startup head (the design
consult's unresolved-risk list + the Macroscope 07:10/07:15 R3
residuals, both fresh reviewers dispositioned pre-existing and
recommended this follow-up):

- Atomic settle/publish: the publisher's disposed check, memo-generation
  check, listener teardown, and manager park now share ONE lock scope
  (TS ensure()'s `managerPromise === startup` guard). A stale publisher
  can no longer clear a NEWER memo armed underneath it - a defunct clear
  inside the park->publish->clear window used to wipe the newer memo and
  arm a transient duplicate kernel - and a boot can no longer park a
  live kernel into a provisioner that reported itself torn down between
  the old separate check and publish scopes (the dispose TOCTOU).
- kill() clears the startup memo (TS kill() clears `managerPromise`):
  a boot doomed mid-flight is killed by its own settle instead of
  parking a resident kernel nobody owns; the next ensure() boots fresh.
- Concurrent stops of one in-flight boot share the first stop's gate
  (`pending_stop_for_startup`): the take-losing stop task used to open a
  fresh gate before the winner's final snapshot flush, un-gating a
  revival to race that flush over the same on-disk file. The take-loser
  that still exists (against a direct-arm stop) now waits the currently
  armed gate before opening its own.

Fixtures: kernel_startup_memo.rs (stale-publisher generation survival;
kill-during-boot; dispose-during-boot end-state pin; kill vs PrimeIntellect-ai#3257's
panic-cleanup re-arm) + the concurrent-stops fixture in
kernel_stop_revive.rs. All drain-boundary observables on the
single-threaded test runtime (the settle chain runs in one scheduler
drain; no sleeps, no interposition into wake chains). Failing-first
verified on the pre-fix head: the kill/stale fixtures delivered a LIVE
resident kernel; the concurrent-stops fixture caught the loser's
early gate open in the settle drain.

* test(kernel): the two bot-finding fixtures (fail-first shapes)

The Macroscope/Bugbot findings on the stopgate-hardening head, as
drain-boundary fixtures BEFORE the fix (RED by construction; the
fail-first receipts are the ffix-branch gates cited in the fix commit):

- superseding_stop_after_kill_cannot_deadlock_the_revival_gate: a stop
  armed for one boot, kill(), a revival gated on that stop's gate, and a
  second stop of the revived boot that supersedes it; the take-losing
  first stop waiting the currently-installed gate forms the wait cycle
  (first stop -> second stop's gate -> revival boot -> first stop's
  gate) - the oracle is the bounded settle, so the cycle shape wedges
  against the 30s timeout. Absolute spawn count pinned.
- doomed_settle_keeps_the_newer_boot_listener_state: a doomed boot
  settling against a newer memo generation must not wipe the newer
  boot's progress listeners/replay stage - a late joiner of the newer
  boot replays that boot's current stage to a fresh handler; the gated
  counting wrapper holds each boot's handshake at a file barrier (the
  wrapper reads its ordinal from the count file, so the count file is
  pre-created; a Drop guard opens every gate on any exit so a wrapper
  never outlives the test as an orphan), making the settle ordering a
  test-controlled barrier, never a timing race.

* fix(kernel): chain superseding stop gates and scope the settle's listener teardown

The five Macroscope/Bugbot findings on the stopgate-hardening head reduce
to two root causes; both fixed, with the drain-boundary fixtures that
failed first on the unfixed tree (RED receipt: gate_1790910402 on the
fixtures-only tree e3b92c4 - fmt rc0, clippy rc0, test rc101 with
EXACTLY the two new fixtures failing at their designed oracles:
"a late joiner must replay the active boot's stage" and "the first stop
settled (no wait cycle)", both bounded-settle timeouts; everything else
green; the compile-caught authoring attempts gate_1790908504/
gate_1790908744/gate_1790909006/gate_1790909694 are the fixture
authoring cycle, incl. the counting-wrapper ordinal-read-before-create
bug that made one early RED vacuous):

- The stop-gate CHAINING (main's PrimeIntellect-ai#3257 shape) dropped by the JOIN:
  arm_stop now captures the gate the arm REPLACED and the stop task
  waits THAT before opening its own - never the currently-installed
  gate. The installed-gate design missed the supersede window (the
  take-losing second stop's loser saw its OWN gate installed and opened
  before the first direct stop's final snapshot flush settled,
  un-gating a revival to race that flush) and admitted a three-task
  wait cycle (a stop waiting a superseding gate that waits a revival
  that waits the first stop's gate). The chain is strictly-older-only,
  so acyclic by construction; the same-boot JOIN is unchanged (the
  second stop of the same boot still never arms).

- The settle's listener teardown is generation-scoped: the doomed boot
  clearing startup_listeners/last_startup_message unconditionally wiped
  a NEWER memo generation's progress listeners and replayed stage. The
  clear now runs only when the settling memo is the active generation
  (or no memo is armed at all - the stale-entries case).

* fix(kernel): drop the redundant gate clone; relax the fixture's replay assert

- arm_stop installs the arm's own receiver directly (clippy redundant_clone:
  the clone was the value's last use after the Armed struct stopped
  carrying stop_rx).
- the listener fixture's replay oracle asserts the replay FIRES with the
  active boot's current stage; pinning the exact stage string made the
  oracle flaky against legitimate later stages the boot emits around its
  held handshake ("Preparing Python runtime...").

* fix(kernel): generation-scope the startup progress emit

The 4880ab3 settle fix scoped the LISTENER TEARDOWN to the active memo
generation but left the EMIT unscoped, and its own fixture proved it:
doomed_settle_keeps_the_newer_boot_listener_state failed 3/3 on the
fixed tree (replayed "Preparing Python runtime..." instead of "Starting
Python kernel...") - a boot kill() invalidates still emitted its later
stages, overwriting the newer generation's last_startup_message and
firing the newer boot's listeners with the wrong boot's stage.

The emit now threads the boot's memo generation through
run_startup/start_kernel/start_kernel_impl and writes the shared
progress state (last_startup_message + the listener fan-out) only when
the emitting memo is still the ACTIVE generation; the boot's own
on_progress handler keeps firing unconditionally (it is its caller's,
not shared state). Also drops the redundant stop_rx.clone() that
red-inked CI's clippy job on the swarm push (redundant_clone at the
arm_stop install).

Gates (keeper worktree, CARGO_TARGET_DIR=.cargo-target-keeper-3303):
fmt rc0; clippy --workspace --all-targets -D warnings rc0; kernel
fixtures kernel_startup_memo 5/5 + kernel_stop_revive 5/5 (the
previously-red doomed_settle fixture green); pa-core lib 1043 passed, 1
failed - workspace_snapshot::head_tree_baseline_excludes_only_absent
_skip_worktree_paths, a git-2.34.1 sparse-checkout artifact reproduced
outside the lane (git 2.34.1 marks root files S after `sparse-checkout
set --cone`; CI's newer git passes it; the PR does not touch
workspace_snapshot).

* fix(kernel): clear the killed generation's shared progress state at kill()

FINDINGS-1 from the pre-push review (gpt-6-sol cross-model reviewer,
report /home/ubuntu/handoffs/REVIEW-3303.md): kill() invalidates the
startup memo but retained the killed boot's last_startup_message and
startup_listeners, so the next ensure() replayed the killed boot's stale
stage to a fresh handler and the newer boot's emits fanned out to the
killed generation's listeners - the 5e3037a emit gate stops the doomed
boot's own emissions but not the B-to-A delivery or the stale replay.

kill() now clears both shared fields in the same lock scope that
invalidates the memo (the same stale-entries reasoning as the settle's
generation-scoped teardown), and the listener fixture grows the
transition oracles the reviewer asked for: A's joiner's collector must
show nothing after the kill, and the newer boot's handler must show
exactly its own stage twice (fan-out + boot-local, the pre-existing
main shape) with no stale replay first.

RED receipt: with only the kill-clear reverted, the fixture fails at
"the killed generation's listener must not hear the newer boot"
(kernel_startup_memo.rs:531); restored, all 10 kernel fixtures pass.
Gates: fmt rc0; clippy --workspace --all-targets -D warnings rc0;
pa-core lib 1043 passed, 1 git-2.34.1 sparse-checkout artifact
(workspace_snapshot, untouched by this PR, green on CI's newer git).

* fix(kernel): dead generations do not retry and do not surface restores

Macroscope's follow-up finding on 5901a45 (thread r4162712975): a
run_startup task whose memo kill() invalidated still consumed its retry
- each attempt spawns an interpreter and runs restore/bootstrap, and the
stale attempt's settle only killed its kernel, leaving duplicate kernel
work and stale restore notifications/state against the fresher
generation.

- The retry loop now checks boot_generation_is_live (memo still the
  armed startup generation AND provisioner not disposed) before each
  retry AND again after the backoff, so a kill() during the backoff
  window cannot resurrect a dead generation's boot. The disposed arm of
  the old guard folds into the same check (dispose() does not clear the
  memo, so both conditions are needed).
- The restore surface (on_restore + last_restore) is now
  generation-gated with the same predicate: a boot kill() invalidated
  restores its snapshot only for the settle to kill its kernel, so its
  restore must not fire the notification nor pollute last_restore.

Fixture: kill_during_the_retry_backoff_pins_the_spawn_count - a
fail-first flaky wrapper (first invocation exits 1 before the
handshake; later invocations exec the real kernel python), kill()
during the backoff window, and the absolute spawn count pinned at 2.
RED receipt: with the memo check removed the fixture fails 3 vs 2 (the
doomed boot consumed its retry); restored, all 11 kernel fixtures pass.

Gates: fmt rc0; clippy --workspace --all-targets -D warnings rc0;
pa-core lib 1043 passed / 1 git-2.34.1 sparse-checkout artifact
(workspace_snapshot, untouched by this PR, green on CI's newer git).

* fix(kernel): a dead generation does not report unavailable skills

Cursor Bugbot's finding on 155a876 (thread r4162867816): a boot that
kill() invalidated still fired on_unavailable_skills after its
bootstrap - the callback shares the restore notice's mailbox, so a
discarded kernel could append stale skills-unavailable rows the next
turn showed the model, even though on_restore/last_restore are
generation-gated in the same function.

The skills report now uses the restore gate's exact contract: the
generation-liveness check and the callback share ONE lock scope (the
callbacks must not re-enter the provisioner, same as the
startup-progress listeners), so a kill() can no longer slip between the
check and the report.

Fixture: doomed_boot_does_not_report_unavailable_skills - the gated
counting wrapper holds the doomed boot's interpreter, kill() invalidates
its generation, the newer boot arms with the SAME broken skill, and the
mailbox must carry exactly the live boot's report. RED receipt: with the
gate reverted the fixture fails 2 vs 1 (the dead generation's report
lands first); restored, all 12 kernel fixtures pass.

Gates: fmt rc0; clippy --workspace --all-targets -D warnings rc0;
pa-core lib 1043 passed / 1 git-2.34.1 sparse-checkout artifact
(workspace_snapshot, untouched by this PR, green on CI's newer git).

* chore: empty retrigger of CI (test shard 3's acp_threshold_auto_compaction_publishes_the_compaction_meta is a known flake: same test green at 155a876 and flaky on main's own cf285dc runs)

* fix(daemon): gate the interleave race harness to unix

Main's PrimeIntellect-ai#3004 promoted the windows cross-check (clippy for
x86_64-pc-windows-gnu), and the new job is red on main's own tip:
PrimeIntellect-ai#3195's interleave harness binds a unix-socket probe, but tokio gates
UnixListener behind all(unix) - the module fails to cross-compile for
windows (E0433, the single red in pa-daemon's lib-test target).

The module declaration now carries #[cfg(unix)] (the same contract as
feed's #[cfg(unix)] tests - every race test in the module rides the
unix-socket probe, so the whole module is unix-only). This is an
upstream break the lane folds and fixes so its head can be green; main
carries the same fix independently.

Gates: cargo check -p pa-daemon --all-targets --target
x86_64-pc-windows-gnu rc 0; fmt rc 0; clippy --workspace --all-targets
-D warnings rc 0; pa-daemon turn_stream_tests 42 passed on unix.

* fix(kernel): spent stop arms release and doomed teardowns never flush

The two Cursor Bugbot findings on 4c74a09, plus the cross-model
reviewer's TOCTOU finding on the first fix shape:

1. "Stop-arm memo never released" (Medium): pending_stop_for_startup
   is written when a stop arms against an in-flight boot and never
   cleared when that boot settles or kill() invalidates it. The arm's
   memo receiver is a strong handle to the settled StartupResult, so a
   spent arm keeps a parked-or-failed manager alive past stop_kernel -
   a later failed shutdown's process would leak with the stale
   receiver as its sole owner. Both spenders now release the arm (the
   boot's settle and the kill() that invalidates it), through one
   shared release_spent_stop_arm.

2. "Doomed boot can clobber snapshots" (Medium): a boot kill()
   invalidated still runs start_kernel_impl to completion, and its
   failure teardowns shut down with the dispose snapshot policy
   (default true) against the same snapshot_dir the replacement boot
   restores from. The four failure teardown sites now route through
   hold_snapshot_flush_gate: the policy (dispose policy while the
   boot's memo is still armed, never a flush for a kill()-invalidated
   boot; dispose() does not clear the memo so a disposed boot keeps
   the dispose's own policy) AND, when the policy still flushes, a
   pending-stop revival gate for the teardown's own flush - the
   decision and the gate install share ONE lock scope, so a kill()
   landing between the decision and the flush still leaves the
   replacement boot gated on this teardown (the same revival gate
   stop_kernel arms against its own flush; a stop ACTIVELY armed for
   this boot is the one exemption - its memo wait settles only after
   this teardown, so its own gate already covers the flush - while a
   merely installed older gate, including one long settled and left in
   place, covers nothing and is replaced by the teardown gate).

Also gates the two unix-only kernel test targets
(kernel_startup_memo.rs, kernel_stop_revive.rs) with #![cfg(unix)] -
main's PrimeIntellect-ai#3004 promoted the windows cross-check and the whole-target
/bin/sh wrapper fixtures never cross-compiled (the sibling kernel
targets and lock_compat already carry the gate).

RED receipts (provisioner unit tests): release no-op -> both arm
tests fail; flush gate install removed -> the gate assert fails;
restored -> 12/12.

Gates: fmt rc0; clippy --workspace --all-targets -D warnings rc0; the
12 kernel fixtures pass; pa-core lib 1046 passed / 1 git-2.34.1
sparse-checkout artifact (workspace_snapshot, untouched by this PR,
green on CI's newer git); clippy --workspace --all-targets --target
x86_64-pc-windows-gnu -D warnings rc0.

* fix(kernel): dispose waits kill-doomed boots and stops chain superseded gates

snimu's five remaining review items (2026-10-02T20:36:31Z comment):

1. StopArm::Joined deleted. The join made a second stop of one
   in-flight boot wait the first stop's gate after arm_stop had
   already written the second caller's dispose_snapshot, splitting the
   two snapshot policies. Concurrent stops of one boot now supersede:
   each stop arms its own gate and its task waits the strictly older
   gate it superseded before opening its own, so a revival cannot
   cross before the final flush settles; pending_stop_for_startup
   stays (the flush-gate exemption is load-bearing), now consulted by
   the failed-boot teardown instead of the join.

2. kill() parks the startup memo it invalidates (doomed_startups)
   instead of clearing it. The doomed boot's kernel exists until its
   settle, and a later dispose() must wait that settle the same way it
   waits an armed memo - without the park, a kill followed by a
   dispose skipped the doomed boot entirely and could orphan its
   kernel at worker exit. Each parked memo is spent by its own boot's
   settle.

3. The settle path checks the generation before the disposed flag
   (settle_decision): a killed+disposed boot reaching Ok tears down
   with kill() semantics and never flushes a snapshot, and
   kill->ensure->dispose cannot overlap two flushes on one
   snapshot_dir. The order is pinned by a unit test.

4. The four memo fixtures hold their boots at file gates instead of
   spinning on yield_now; the flaky fixture holds before its failure
   on the same schedule, so a kill lands mid-boot by construction.
   Both gated wrappers self-release when their fixture directory
   vanishes (the counting loop falls through to the real interpreter,
   whose stdin pipe is gone; the flaky loop exits 1), so a cancelled
   test cannot leave a polling shell behind.

5. The main merge folds PrimeIntellect-ai#3306's #![cfg(unix)] inside interleave.rs,
   dropping c9de47a's duplicate mod gate from the resolution (the
   duplicated_attributes clippy risk).

Red-first regressions, both demonstrated failing on the pre-change
behavior: dispose_after_a_kill_waits_the_doomed_boot and
a_killed_then_disposed_boot_never_flushes_the_snapshot; the
settle-order unit pin fails with the old disposed-first order. The
joined-gate drain test is deleted with its arm.

Cross-family review (bugbot, gpt-6-sol): READY-CLEAN, no findings.

* fix(kernel): a kill while the boot waits gates it before the spawn

cursor[bot] finding on 8d29f37 ("Killed boot still spawns
interpreter", Medium): the boot permit gate checked only the dispose
signal, so a kill() landing while a boot was still waiting - queued
behind an in-flight stop's gate or the boot permit itself - let the
doomed generation spawn an interpreter its own settle then had to kill
(no flush, no park, but a real boot racing the replacement generation
on the same snapshot_dir and kernel-stderr.log).

The permit-gate closure now rechecks boot_generation_is_live as the
last point before the interpreter spawn and fails fast with "Kernel
provisioner killed before start" - non-retryable (FATAL_MARKERS
"provisioner killed", unit-pinned). The settle stays the correctness
backstop for the post-check race.

Red-first regression
a_kill_while_the_boot_waits_the_stop_gate_spawns_no_interpreter: boot
A holds mid-handshake at a file gate; a stop arms against it; kill #1
invalidates A; a fresh ensure arms memo B (polled once on the test
thread so the arm is guaranteed, not yield-assumed); kill #2 invalidates
B while it still waits; releasing A settles the chain and B fails the
liveness check before the spawn. Red on the unfixed tree (B spawned a
183ms handshake and settled "killed during startup"); green after (the
spawn count stays at one).

Also folds pi/main c24ac22 (factory/machine lib, acp single daemon
path, semantic-edge ledger, telemetry first-frame fix, factory e2e,
subagent relisting, factory state-machine port, vouches).

Cross-family review (bugbot, gpt-6-sol): READY-CLEAN on the fix and on
the test-hardening re-review.
…t-ai#3338)

* test(tui): pin accepted-then-killed session error render

* test(tui): wait for worker-admitted user row before kill

* test(tui): kill after worker reports streaming admission

* test(tui): trace killed-close delivery across client pump

* test(tui): trace close apply-to-frame visibility

* test(tui): attribute killed-turn probe output to its own case

* fix(tui): surface killed session as persistent TS error row

* test(tui): assert close error survives cancelled-turn status
…-ai#3278)

* make update --rollback and --archive work on installer installs

* say the current install was kept when the installer fails

* keep the bundled installer and the prefix check private

* recheck the rollback source under the lock and test archive and rollback end to end

* retrigger ci

* release the install lock when the rollback source vanished

* recheck the rollback source before the sweep

* clean the stage on a bad archive and refuse a channel flag on a local update

* run the local installer modes handed off on windows so the payload can be replaced

* sweep the extraction stage on the refused and failed installs

* split the trusted shell resolution from the installer child command

* refuse a windows handoff up front when nothing is kept or the archive cannot install

* retrigger ci

* answer the windows rollback pre-flight from the installer's own scan

* retrigger ci

* retrigger ci

* remove a stray clippy output file committed by mistake

* refuse an archive whose payload disagrees with its name

* gate the unix-socket interleave harness off the windows cross-check

* answer the local modes' preflight from the installer's own archive check

* own the check knobs, bound the version probe, and sweep the check stage

* retrigger ci

* initialize the check trap's stage so an inherited variable cannot become its target

* bound the version probe with a reaping runner and refuse an unresponsive payload

* retrigger ci

* retrigger ci

* move the probe files out of the payload stage and take the continuous version stamp

* scope the version probe to the archive mode

* require a working version probe and sweep the interrupted install's staging

* bound the version probe's output writes

* keep the version probe on the staged tree's own package manifest

* retrigger ci

* guard the version probe's output limit against platform failure

* drop the interleave gate now that the harness gates itself

* spawn the windows handoff behind the detached process wall

* read the unmarked ps1 rollback slot when the generations record is silent

* normalize the ps1-written marker on the rollback read and refuse a prefix the record cannot hold

* carry the resolved prefix past the substitution trim and read every recorded marker through the normalizer

* retrigger ci

* read the ps1-encoded markers through the ownership gate and strip the probe report's carriage return

* accept the v-prefixed archive name and wait out the handoff parent before the payload touch

* run the installer from its held handle and ask liveness of a trusted tasklist

* restore the parked tree through the publish window and let the windows local modes run without python
…ort of TS PrimeIntellect-ai#2529) (PrimeIntellect-ai#3344)

* port TS PrimeIntellect-ai#2529: rlm.rename across runtime, daemon, supervisor, and agents view

- Python runtime: rlm.rename(new_name, session_id=...) + rlm.rlm.rename + __all__; _collect_target_selector gains the what label
- pa-core: rlm.rename host handler (spawn-name rules), RlmSubagentHost::rename, NoRlmChildren local self-rename; RUNTIME_READY_CHECK requires rlm.rename
- pa-types: DaemonCommand::Rename gains renamedBy (parent marker) + RENAMED_BY_PARENT
- pa-daemon: SupervisorChildSessions rename routing (self by supervisor, children by id only), supervisor name-reservation ladder shared by the saved-session and live rename routes, ledger rename for Rename + SetSessionName, worker session_renamed transcript notice
- pa-tui agents view: ctrl+r / /name renames subagent rows too
- prompts/core.md documents rlm.rename; .changes fragment

* fmt: apply rustfmt to the rlm.rename port

* fix clippy: hoist the session Arc out of the boxed rename future; rename the test reply binding

* fix clippy/rustc: ladder match arm types, or-pattern parens, str compare, guard drop, test Arc double-wrap

* fmt: or-pattern continuation indent

* fix routing: the ladder passes the inner route result through (drop the double wrap)

* fix e2e: RlmSpawnRequest initializer gains spawned_by_request_id

* review 1: set_session_name takes the typed path (ledger lands), rename wakes passivated targets, one summary->scope projection, one id predicate, one renamed-by spelling, drop dead roundtrip test, trim duplicated test cases

* fmt: review-1 worker notice compare shape

* test fix: the notice test creates its worker without a name (the named fixture made the first rename a real rename)

* golden: regenerate the system-prompt snapshot for the core.md rlm.rename line

* fix(rlm): the parent rename wakes a twice-passivated child through the durable selector

The host rename addressed the child by the registry record's spawn-time
active id with no retry, while prompt_child already retried a miss by the
child's durable selector: after a passivation, a revival re-keys the
supervisor roster's child row to the fresh worker's id (the registration's
roster write drops the old id's index), and once that worker passivates
again the spawn-time id resolves nowhere - no resident, no roster row, no
ledger edge - so the parent rename failed with `Unknown active session`
while a prompt to the same child survived.

One shared durable-wake retry now backs both the prompt and the rename
(extracted from prompt_child, not copied): same `Unknown active session:`
predicate, same durable_child_selector resolution, same failure contexts.
The red test drives the whole chain: passivate, revive by the durable id,
passivate again, then the parent rename - which failed on the previous
head with `rename session "<spawn-time id>": Unknown active session`.

* fix(worker): the session_renamed notice persists under the rename's own core lock

handle_rename persisted the session_info name write under the core lock,
dropped it, and emit_custom_row re-locked core to persist the
session_renamed notice - two lock scopes while worker commands run
concurrently, so a racing command could interleave its writes between a
rename's name and its notice and order the notice against a contradicted
name history (TS serializes both in one synchronous step).

emit_custom_row now splits into a persist half (persist_custom_row,
called with the caller's guard) and a broadcast half
(broadcast_custom_row): the rename persists the notice inside the same
lock scope as the name write and broadcasts after the lock drops, and the
notice's JSON shape stays defined once. The persist-error policy is
unchanged (the display row stays advisory, never failing the rename).

* test: keep the child session id out of the rename-wake assert message

* test(supervisor): property-test the live rename ladder's family-name reservation

* fix(rlm): serialize parent rename and delete of a child

---------

Co-authored-by: Kevin Thomas <kevin.jt2007@gmail.com>
…rimeIntellect-ai#3258)

* fix(daemon): stop client-owned workers 30s after their owner's last connection closes

A worker created as client-owned (ACP sessions, TUI --no-session) kept
running when its owner died without a clean close - an editor SIGTERM, a
crash, an EOF during session/new - because nothing in the supervisor
tracked whether the owning client was still connected. Owned workers are
hidden from other clients, so the session stayed stuck running until the
daemon stopped; adopted owned workers survived daemon restarts the same
way.

Port the TS supervisor's scheduleOwnedWorkerCleanup:

- Track live connections (connection id -> the shared effective-client-id
  cell dispatch already writes), so "owner connected" scans the live set
  like TS's this.clients: one process can hold several connections with
  the same id.
- Split handle_client into a registration wrapper plus the read loop, so
  every exit path (EOF, failed writes, even the hello write) deregisters
  and considers the owner-disconnect cleanup; the old write-failure
  returns skipped the disconnect tail entirely.
- Arm a 30s grace timer per owned resident (ResidentWorker.owner_cleanup)
  when its owner's last connection closes, and after the boot adoption
  pass. The expiry re-checks the owner id, the live connections, and that
  the same resident is still registered before stopping the worker
  through the graceful per-session stop. A later arm replaces a pending
  timer, so the stop is measured from the owner's last disconnect, and a
  reconnect inside the grace (or any live connection of the owner at
  expiry) makes the expiry a no-op.

* fix(daemon): let each owner-disconnect timer take only its own handle

When the owner-disconnect timer woke, it took whatever handle sat in the
resident's `owner_cleanup` slot. If the owner reconnected and disconnected
again at exactly the grace boundary, the re-arm could store a newer
timer's handle there while the old timer was still mid-poll: the old
timer then took the new timer's handle, and since the re-arm had aborted
it, it was cancelled at its first await inside `stop_worker`, halfway
through the stop. The expiry now clears the slot only when it holds its
own handle (AbortHandle id == task id, under the slot mutex), so a
replaced timer returns without stopping the worker and a timer that
wins the race is never aborted mid-stop.

The phase-4 leg of the test now also asserts the expiry cleared the
slot, instead of trusting the yields as proof that the expiry ran.

* fix(daemon): measure the owned-worker grace from the timer's arm

The spawned cleanup task computed its sleep deadline at its first poll,
not when schedule_owned_worker_cleanup armed it, so the grace effectively
started whenever the task was first polled. Compute the deadline at the
arm and sleep_until it, so the grace always runs from the owner's
disconnect.

* fix(daemon): arm the owner cleanup when a client-owned worker is registered

A connection that closed while its create was still in flight scanned the
registry before the new worker was inserted, so that worker was never
armed and ran on until the next daemon start. launch_worker now arms the
cleanup right after the insert; the arm skips owners that are still
connected, so a normal create is unaffected.

* fix(daemon): veto expired cleanup after owner reconnects

---------

Co-authored-by: Kevin Thomas <kevin.jt2007@gmail.com>
The transcript click target only accepted tool cards, bash cards, agent messages and shell completions. Compaction summaries, refinement notices, skill invocations and injected prompts are clickable in v0.9.8 and their renderers already follow the per-row expansion, so add them to the click target.
…ort (PrimeIntellect-ai#3323)

The flow tests keep failing when a runner holds port 53692: the probe-and-skip
guards pass the probe, then the flow's own bind loses the race a few
milliseconds later (the CI failure in run 37061119942). The flow tests never
needed the registered port, so they now run on a free loopback port through a
login_with_server seam; the pa-cli test exercises the store step directly
through store_anthropic_login. Production still binds the registered port.
The probe, the skip guards, and CALLBACK_PORT_LOCK are deleted.
…t-ai#3361)

* pa-ai: end the openai-completions stream at the [DONE] marker

The SSE read loop terminated only at body EOF; a server that keeps the
response open past `data: [DONE]` (a persistent SSE channel with no
content length, no chunked terminator, no half-close) left the stream
reading forever — the turn stalled mid-stream with the tool call parsed
but never executed, no kernel spawned, and the ACP prompt waiting on an
uncapped request. The TS SDK stops at the marker; the port now breaks the
read loop there and skips the post-marker decoder flush.

* trim the done-marker comments to the non-obvious fact

---------

Co-authored-by: Seth Karten <32787133+sethkarten@users.noreply.github.com>
…refine/agent-message updates, stop failures, MCP owner, input pause) (PrimeIntellect-ai#3348)

* acp e2e: exercise the daemon-attached MCP clear path and the unreachable-daemon exit

The worker release is observed through the real replace_acp_mcp_servers
command: a foreign-owner probe fails the ownership fence while the
session is open and succeeds after session/close and after the EOF
teardown, proving both clear sites ran on the daemon session. With the
socket path blocked by a non-socket file the supervisor never comes up,
and --mode acp must print one Error line, exit 1, and write nothing to
stdout: no in-process fallback serves ACP frames.

* acp: stop-failure fence, input-pause lease, per-connection MCP owner (TS parity)

* pa-daemon: emit subagent, refine and agent-message session events and map them to ACP _meta (TS parity)

* fix(acp): retain mcp owner names until clear acknowledged

* fix: emit and fence ACP refinement lifecycle events

* test: run refinement event fence under a Tokio runtime

* fix: fence ACP close and wake paused idle waiters

* style: remove merge marker padding in child records

* test: exclude buffered wakes in ACP race regressions

---------

Co-authored-by: Kevin Thomas <kevin.jt2007@gmail.com>
Co-authored-by: Seth Karten <32787133+sethkarten@users.noreply.github.com>
* red: the 755 output-budget wire pins

* honor an explicitly configured max tokens

* cargo fmt

* silence the pass-by-ref lint on the serde skip predicates

* saturate the thinking fold and tighten the max tokens comments

* drop the deleted-mechanism clause from the overflow test comment
… at the copy that serves (PrimeIntellect-ai#3364)

* publish install.ps1 on every beta cut and point the windows one-liner at the copy that serves

* review: qualify the no-github claims for the windows entry route and trim the publish narration

* retrigger ci: the test-summary job died in the runner 15m starvation window (cancelled, no log; all 8 shards green)

* retrigger ci again: the wave died in the same org-wide runner 15m starvation window (cancelled/skipped legs, zero red units; every failing leg passed on the prior head)

* retrigger ci after the runner-pool recovery (the canary wave completed green; the prior waves died in the famine)

---------

Co-authored-by: Seth Karten <32787133+sethkarten@users.noreply.github.com>
…tstrap (PrimeIntellect-ai#3346)

* fix(kernel): refuse to snapshot live bash handles and log dropped protocol frames

* fix(kernel): refuse to reopen pickled raw fd numbers during restore

* fix(kernel): bound the provisioner's runtime bootstrap execute

* fix(kernel): prove the wedge tests red on behavior (off-cell handle, bare wakeup await)

* fix(kernel): pass the bootstrap bound through the shared execute path

* fix(kernel): bound blocked protocol writes during abort and shutdown

* fix(kernel): retire stdin when an aborted request write tears its frame

A timed-out oversized request force-aborts while its JSON-lines frame is
still mid-pipe. Cancelling the writer leaves the request unsent or torn:
the runtime never sees it, its execution never settles, and the kernel
stays busy forever — every later request fails as busy after the reuse
window, and any frame written next glues onto the prefix and drops as
invalid JSON. Retire the kernel stdin when the early-settle cancel
actually joined a cancelled writer: later writes fail fast, the runtime
reads EOF and shuts down, and the provisioner replaces the defunct
kernel.

The abort watcher's interrupt needs no such handling: its ~60-byte frame
is a single write on a non-blocking pipe, all-or-nothing below
PIPE_BUF, so the grace deadline can drop it without tearing.

---------

Co-authored-by: Kevin Thomas <kevin.jt2007@gmail.com>
Integrate the 89 upstream commits through 672085e while preserving the
Oneiron WebSocket transport, delta continuation, compaction, model catalog
fallback and local model compatibility patch.

Retry dropped provider turns from committed state with the shared bounded
2s/4s/8s transport ladder. Preserve committed tool results and supersede
failed partial output. Retain WebSockets and recover the connection cap.

Keep the combined daemon protocol at 7 with schema 32. Update native test
fixtures and side-by-side packaging for the merged upstream APIs.
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your workspace is out of credits. Ask your workspace admin to add credits to resume reviews. Manage billing

@coderabbitai

coderabbitai Bot commented Oct 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Essentials
  • Run ID: 7f24c502-4fc4-49df-b489-f4d3a676860e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@cursor

cursor Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

PR Summary

High Risk
Touches core agent transport retry, daemon/ACP session lifecycle, kernel restore, and release channel publishing—areas where regressions affect live sessions, updates, and installs.

Overview
Merges 89 upstream commits onto the Rust fork while keeping fork-only behavior (tool selection, hosted daemon, memory/retry wiring, schema 32). The headline runtime change is mid-stream WebSocket recovery: dropped sockets become typed transport failures; the session engine retries with 2s / 4s / 8s (three attempts), replays from committed state, and handles connection-limit caps and cached-socket rotation without doubling tools or partial text.

ACP, daemon, and RLM gain broad TypeScript parity: daemon-only ACP (no in-process fallback), session flags, single-turn ownership, 24h long routes, client-owned --no-session cleanup, subagent quiescence/rename/survive-restart, kernel snapshot safety around bash fds, and semantic-edge ledgers plus per-request correlation headers on model calls. Product surface additions include prime-agent factory list/import/export and rlm.rename.

Release and CI shift the train to main: contributor vouch/trust gates, in-workflow windows-cross and cargo-deny, Windows MSVC release artifacts and R2-published install.ps1, beta auto-publish via release-beta, and guards so TS updaters cannot install Rust tarballs. Docs add CONTRIBUTING (Discussions-first), AGENTS/BUGBOT rules (no co-authored commits, paint must not await telemetry), and .cargo/config.toml sets DO_NOT_TRACK=1 for tests.

Smaller fixes span TUI (code wrap, expandable rows, CSI panic), maxTokens from models.json, SSE [DONE] termination, installer/update behavior, and many .changes/ notes.

Reviewed by Cursor Bugbot for commit f64ee8f. Bugbot is set up for automated code reviews on this repo. Configure here.

@cursor

cursor Bot commented Oct 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: df8195b7-63d2-440e-af45-1b02b78b2bb0)

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Prime Agent performance — failed

PR f64ee8f7 compared with main a1cb1fc6.

Benchmark execution did not complete successfully. Missing measurements are not performance wins.

Failure diagnostics:

  • Repository secret PRIME_SANDBOX_API_KEY is not configured

See the saved per-trial logs and terminal transcripts for details.

Overall: 0 regressed · 0 improved · 0 no clear change · 42 unavailable.

Metric Main This PR Change
Cold startup — — —
Warm startup — — —
Installation — — —
Compressed release artifacts — — —
Installed footprint — — —
Idle memory, summed RSS — — —

Python runtime

Metric Main This PR Change
Python kernel startup — — —
Python cell round trip — — —
Empty bash command — — —
Bash git status — — —
Bash 32 KiB output — — —
35 cells / 9 shell calls — — —
Python interrupt to done — — —
Python state snapshot — — —
Python state restore — — —
Python idle RSS — — —
Python RSS after pandas workload — — —

Session transport

Metric Main This PR Change
Full-history transfers per warm session switch — — —
Private frame decode, 32 MiB in 8 KiB chunks — — —

UI interactions

Metric Main This PR Change
Resume large session (cold) — — —
CPU, resume large session — — —
Switch into large session — — —
CPU, switch into large session — — —
Open agents view from a session — — —
CPU, open agents view — — —
Full agents roster, many sessions — — —
CPU, full agents roster — — —
Open another session from agents view — — —
CPU, open from agents view — — —
Reopen resident large session — — —
CPU, reopen resident session — — —
Open subagent session at depth 6 — — —
CPU, open subagent at depth 6 — — —
Open chain parent from agents view — — —
CPU, open chain parent — — —
Scheduled catalog, first request — — —
CPU, scheduled catalog — — —
Scheduled catalog, repeated request — — —
CPU, repeated catalog — — —
Cold worker with three catalog scans — — —
CPU, cold worker and scans — — —
UI memory after interactions — — —

Cost pending or unavailable; sandbox usage has not been collected.
Run, logs, and downloadable raw results

Methodology and samples

Main resolved at 2026-10-07T10:29:51.158671+00:00. Harness a1cb1fc6.
Linux x64, 4 vCPU, 8 GB RAM, 20 GB disk; region us.
Image: node:24-bookworm@sha256:be23f54a88d34e8824c741b19b91064094f92c1c97b194144bfc8b50d67258e2.
Stock tools, skills, daemon, and Python bootstrap enabled; fresh homes and a fixed Git fixture.
Onboarding is dismissed; the editor starts without a selected model or submitted prompt.
Medians shown. Arrows require a 20% timing/memory change plus absolute floors and IQR.
These practical noise floors are not a statistical significance test.
Cold means stopped Prime processes; OS filesystem caches are not flushed.
No model requests or credentials. Installation excludes build/setup time.
Installer tarballs use loopback; npm/Python downloads use the network with fresh caches.
Artifact size counts release tarballs; footprint after first use includes registry packages.
MB is decimal. Summed RSS can double-count shared pages; PSS is recorded when available.
Provisioning, setup, and build durations are recorded separately in the raw results.
Kernel probes use the installed JSONL runtime, outside the TUI/TypeScript host.
Per trial: 50 Python cells, 5 calls per shell case, and one 35-cell mix (9 git status calls).
Cell/shell values are batch means; other runtime timings are single operations.
State fixture: a 10,000-row × 8-column integer DataFrame and a 10,000-integer list.
Restore runs in a fresh kernel, including pandas imports; kernel startup is excluded.
Kernel RSS covers the isolated Python process; loaded RSS follows the pandas workload.
Transport benches run node against the prepared source build, outside the installed home.
The switch benchmark drives one warm switch into a 48k-entry session through a real
daemon and counts full-history crossings: streamed replacement snapshots, inline
replacements, and full-history refetch responses.
Frame decode times one 32 MiB private frame, snapshot-chunk header, pushed in
8 KiB chunks; the wire shape of multi-MB frames on the daemon-worker channels.
UI trials use a fresh fixture set: 194 top-level sessions including one ~40 MB transcript,
40 ledger fan-out children, and a 6-deep subagent chain (~46 spawn edges).
Large fixtures hold 1,999 complete triples (~5 MB JSONL); medium 119; subagents 399 each.
Interactions: cold --resume of a large session, warm /resume switch, left-arrow to agents view,
roster settle with many saved sessions, search-and-open of another large session,
reattaching to that resident session, opening the chain parent, and drilling to depth 6.
Readiness is the rendered transcript tail plus a confirmed editor echo.
CPU metrics sum utime+stime across the whole benchmark-user process tree per interaction.
UI memory sums RSS after the interactions; PTY byte counts are in the raw results.
A separate catalog fixture has 2,300 sessions, 2,298 edges, and 13 paused scheduled-job owners.
Catalog timings cover first/repeated reads and cold worker creation under three pending scans.
All expected jobs and owner metadata are checked; worker readiness excludes TUI rendering.
Costs estimate full sandbox lifetimes at configured rates, including setup and build.
Budget target: $1; not a billing cap. Performance changes are informational.
Failed or incomplete execution fails the workflow; saved artifacts remain available.
Each side stops a phase after 2 identical consecutive failures.
Skipped trials are not attempted samples. Warm startup requires a successful cold launch.

Metric Main successful/attempted PR successful/attempted Main spread PR spread
Cold startup 0/0 0/0 — —
Warm startup 0/0 0/0 — —
Installation 0/0 0/0 — —
Compressed release artifacts 0/0 0/0 — —
Installed footprint 0/0 0/0 — —
Idle memory, summed RSS 0/0 0/0 — —
Python kernel startup 0/0 0/0 — —
Python cell round trip 0/0 0/0 — —
Empty bash command 0/0 0/0 — —
Bash git status 0/0 0/0 — —
Bash 32 KiB output 0/0 0/0 — —
35 cells / 9 shell calls 0/0 0/0 — —
Python interrupt to done 0/0 0/0 — —
Python state snapshot 0/0 0/0 — —
Python state restore 0/0 0/0 — —
Python idle RSS 0/0 0/0 — —
Python RSS after pandas workload 0/0 0/0 — —
Full-history transfers per warm session switch 0/0 0/0 — —
Private frame decode, 32 MiB in 8 KiB chunks 0/0 0/0 — —
Resume large session (cold) 0/0 0/0 — —
CPU, resume large session 0/0 0/0 — —
Switch into large session 0/0 0/0 — —
CPU, switch into large session 0/0 0/0 — —
Open agents view from a session 0/0 0/0 — —
CPU, open agents view 0/0 0/0 — —
Full agents roster, many sessions 0/0 0/0 — —
CPU, full agents roster 0/0 0/0 — —
Open another session from agents view 0/0 0/0 — —
CPU, open from agents view 0/0 0/0 — —
Reopen resident large session 0/0 0/0 — —
CPU, reopen resident session 0/0 0/0 — —
Open subagent session at depth 6 0/0 0/0 — —
CPU, open subagent at depth 6 0/0 0/0 — —
Open chain parent from agents view 0/0 0/0 — —
CPU, open chain parent 0/0 0/0 — —
Scheduled catalog, first request 0/0 0/0 — —
CPU, scheduled catalog 0/0 0/0 — —
Scheduled catalog, repeated request 0/0 0/0 — —
CPU, repeated catalog 0/0 0/0 — —
Cold worker with three catalog scans 0/0 0/0 — —
CPU, cold worker and scans 0/0 0/0 — —
UI memory after interactions 0/0 0/0 — —

Failures:

  • Repository secret PRIME_SANDBOX_API_KEY is not configured

@olety

olety commented Oct 23, 2026

Copy link
Copy Markdown
Member Author

Superseded by #35: #35. It carries all of #34 plus 170 newer upstream commits. Local Linux workspace and Mac checks passed; the side-by-side release passed isolated smokes and remains unactivated. Please review/merge #35 with a merge commit; do not merge #34 separately.

@olety
olety merged commit a3f30d8 into rust-oneiron Oct 23, 2026
8 of 9 checks passed
olety added a commit that referenced this pull request 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.

4 participants