Skip to content

QUALITY-928: Orchestration unified stack — observer-parent restore (M3) - #14608

Draft
cephalonaut wants to merge 44 commits into
matthew/orch-unified-m2from
matthew/orch-unified-m3
Draft

QUALITY-928: Orchestration unified stack — observer-parent restore (M3)#14608
cephalonaut wants to merge 44 commits into
matthew/orch-unified-m2from
matthew/orch-unified-m3

Conversation

@cephalonaut

Copy link
Copy Markdown
Contributor

Description

Adds durable restore for owned cloud runs that are observed via the shared-session viewer path (QUALITY-928). Stacks on M2.

What

  • AgentConversationData.is_durable_observer_parent: bool — persisted marker that survives restart.
  • mark_conversation_as_durable_observer_parent on BlocklistAIHistoryModel — stamps the marker and task ID on the parent placeholder when TaskOwnership::Owned is confirmed.
  • restore_durable_observer_parent_for_task — re-attaches the eagerly-hydrated conversation to the restored ambient pane before shared-session replay.
  • Eager hydration in conversation_loader.rs — durable observer parents load at startup with their event cursor, excluded from navigation history.
  • Durable-parent restore path in ambient_pane_restoration.rs — when RestoreOrNavigateToConversation fires for a terminal owned run, installs the persisted conversation in cloud-mode presentation instead of opening a fresh compose pane.

Why

Before this, owned cloud runs observed via a shared link always restored as a fresh compose pane on restart — the observer cursor and child hierarchy were not preserved. This PR makes that restore durable.

Alternative: making is_viewing_shared_session persistent for all viewer conversations (not just owned ones) would achieve the same result more simply and extend the behavior to collaborators too. This PR keeps the feature isolated so that decision can be made independently.

Linked Issue

QUALITY-928

Testing

  • test_durable_observer_parent_marker_is_written_before_shutdown — marker is persisted and task_id is stamped.

  • historical_durable_observer_parent_restores_cursor_and_child_hierarchy — cursor and child hierarchy survive a history-model cold-start.

  • running_durable_observer_snapshot_selects_shared_session_reattach — running observer pane restores into a live session attach.

  • terminal_durable_observer_snapshot_restores_existing_parent_and_children — terminal observer pane restores into cloud-mode with existing conversation and child hierarchy.

  • I have manually tested my changes locally with ./script/run

Agent Mode

  • Warp Agent Mode - This PR was created via Warp's AI Agent Mode

cephalonaut and others added 10 commits July 29, 2026 10:48
Spec covers the unified two-PR stack approach (M1: core tracker + family
stream; M2: pane path + transcript). Supersedes the Phase 0 interim
approach. Key decisions documented: OrchestrationUnifiedStack single flag,
viewer transcript authorization resolved (§9.1), parent session ACL
propagation via warp-server PR #12252.

Co-Authored-By: Oz <oz-agent@warp.dev>
- Retitle: now describes M1+M2 two-PR stack from master
- §1: rewrite scope to M1/M2; note Phases 1-3 incremental roadmap superseded
- §3: retitle to 'M1 — Core Tracker + Unified Stream'; replace Phase 0
  client implementation descriptions with M1 implementation (tracker,
  family drain, classify_family_event, SessionLinked signal)
- §4: replace 'two stacks' with north-star architecture diagram
- §5/§6: update from 'blocks naive reuse' to 'drove the unified design'
- §8: already updated (migration plan)
- §10: rewrite deletion scorecard with M1/M2 framing
- §11: update risks and validation to remove Phase 0/1/3 references;
  resolve include_self topology question

Co-Authored-By: Oz <oz-agent@warp.dev>
…ck flag

Introduce the unified child-tracking state machine that will become the
sole entry point for orchestration child state on both owner and viewer
sides (TECH QUALITY-928 §7.2), plus the dogfood feature flag that gates
the M1 stack.

`OrchestrationChildTracker::observe_child` classifies every discovery
signal (Started, SessionLinked, Lifecycle, Seeded, Registered) into the
four-step logic: tombstone drop, create-or-update placeholder, status
write-through, claim-time metadata refetch, and pane materialization.
The side effects that need broader streamer plumbing (placeholder
creation, metadata fetch via AgentConversationsModel, pane path) are
stubbed here and wired up by T2 and M2; the tracker's own state machine
is complete and unit-tested.

Co-Authored-By: Oz <oz-agent@warp.dev>
Add the unified family-stream fan-out behind FeatureFlag::OrchestrationUnifiedStack:

- classify_family_event + FamilyEvent: classify one include_self ancestor SSE
  event into ParentSelf / ChildStarted / ChildSessionLinked / ChildLifecycle /
  Opaque relative to the parent's own self_run_id.
- drain_family_events: routes discovery/session-link/lifecycle signals to
  OrchestrationChildTracker::observe_child, delivers ParentSelf inbox via the
  existing handle_event_batch (owner only), and dispatches cursor authority by
  mode (owner: local + server; viewer: local only).
- Host an OrchestrationChildTracker in ConversationStreamState (owner) and
  OrchestratorStreamState (viewer), created lazily by the flag-on drain.
- Gate the seven drain_sse_events / drain_ancestor_events call sites through
  drain_owner_events / drain_viewer_events dispatchers. Flag-off preserves the
  legacy per-conversation and ancestor drains unchanged.
- Derive PartialEq/Eq on AgentRunEvent for classification tests; add cfg(test)
  accessors on the tracker for sibling-module drain tests.

Tests: classify_family_event (all five variants + edge cases) and
drain_family_events owner/viewer routing, inbox delivery, and viewer
no-server-push cursor authority.

Co-Authored-By: Oz <oz-agent@warp.dev>
…cleanup

Sub-task A: the OrchestrationChildTracker now records the unified
is_remote_child placeholder flavor (TECH QUALITY-928 §7.4) for every child
it materializes in both owner and viewer mode. TrackedChild gains an
is_remote_child marker (true for tracker-materialized placeholders, false
for in-band children that own a real local conversation). Claim-time
metadata fetches route through AgentConversationsModel::get_or_async_fetch_task_data
(the shared fetch authority, §7.6 item 1); a synchronous cache hit resolves
the placeholder inline, a miss resolves on a later re-drive. The real
dispatch is compiled out of unit-test builds, which keep the dispatch-counter
stand-in. Adds a viewer-mode unit test asserting the placeholder is
is_remote_child and never is_viewing_shared_session.

Sub-task B: no code changes required — FeatureFlag::OrchestrationViewerStreamer
and FeatureFlag::OwnerOrchestrationAncestorStreamer never existed in the
implementation (the shipped stack uses OrchestrationUnifiedStack +
WaitForEventsParentRegistration), and the legacy viewer REST polling path
(fetch_children / schedule_next_poll / maybe_kick_polling / apply_children_fetch)
is already absent from OrchestrationViewerModel. Fixed a stale doc comment in
terminal_pane.rs that referenced the removed apply_children_fetch.

Co-Authored-By: Oz <oz-agent@warp.dev>
…_family_events

When a child_agent_started (ChildStarted) or lifecycle event arrives in the
flag-ON family drain (drain_family_events), ensure a local is_remote_child
AIConversation placeholder exists for the child before routing the signal
through the tracker. This makes the orchestration pill bar populate
immediately for out-of-band (cloud) children, fixing QUALITY-928 M1.

Previously observe_child delegated placeholder creation to an async
AgentConversationsModel fetch that did not call back into the tracker on
a cache miss, so ChildSpawned was never emitted and the pill bar never
rendered the child.

The fix adds two methods to OrchestrationEventStreamer:
- ensure_remote_child_placeholder: idempotency-guarded async metadata fetch
- finish_remote_child_placeholder: creates the AIConversation from fetched
  metadata, marks it is_remote_child, assigns the run_id, and persists

In drain_family_events, ChildStarted and ChildLifecycle (backstop) both
call ensure_remote_child_placeholder when mode == Owner before the
existing tracker.observe_child call. Debug logging added for all child
event variants.

Co-Authored-By: Oz <oz-agent@warp.dev>
Bug 1: Status badges not updating
- In OrchestrationChildTracker::apply_lifecycle, add history model
  write-through when a lifecycle event arrives for a tracked child.
  Looks up the child conversation by run_id via
  conversation_id_for_agent_id, then calls update_conversation_status
  so the pill bar badge reflects the lifecycle transition immediately.
- Previously the comment said 'T2: also write status through
  BlocklistAIHistoryModel' but the implementation was a stub.

Bug 2: Clicking child pill shows zero state
- In PaneGroup::attempt_remote_child_hydration, for the LiveAttach
  case, also call attach_execution_session_to_ambient_pane after
  apply_existing_ambient_task_to_pane.
- Previously enter_viewing_existing_session set AgentRunning status
  but never emitted ExecutionSessionReady, leaving the pane
  disconnected from the live session and showing blank content.

Co-Authored-By: Oz <oz-agent@warp.dev>
Mechanical M1 terminology rename:
- ChildTrackingMode -> OrchestrationEventConsumer
- Owner -> Primary
- Viewer -> Observer

This type describes family-event consumption and cursor responsibility only
(Primary delivers parent-self + server cursor; Observer drops parent-self and
persists local cursor only). No behavior change.

Co-Authored-By: Oz <oz-agent@warp.dev>
Remove references to TECH QUALITY-928 section numbers, M1/M2/T1/T2
milestone labels, legacy type names, and speculative future-work prose
from doc comments in orchestration_child_tracker.rs,
orchestration_event_streamer.rs, and mod.rs.

No logic changes.

Co-Authored-By: Oz <oz-agent@warp.dev>
@cla-bot cla-bot Bot added the cla-signed label Jul 31, 2026
When spawn_metadata_fetch gets a cache miss, the guard stays set and
later re-drives from Lifecycle/Started signals return early at the
guard check -- even after the async fetch completes and the cache is
warm. The task never gets picked up by the tracker.

Fix: when the guard is already set, poll AgentConversationsModel once
to check if the fetch completed. If the cache is warm, clear the guard
and call apply_seeded. If still in flight, AgentConversationsModel's
own in-flight dedup suppresses a redundant network request.

Co-Authored-By: Oz <oz-agent@warp.dev>
@cephalonaut
cephalonaut force-pushed the matthew/orch-unified-m2 branch from 62ec14f to 1030741 Compare July 31, 2026 22:11
@cephalonaut
cephalonaut force-pushed the matthew/orch-unified-m3 branch 4 times, most recently from c07c191 to 87d6305 Compare August 1, 2026 02:18
…drain

In drain_family_events, only parent_self_events were passed to
handle_event_batch. convert_lifecycle_events (inside handle_event_batch)
filters by run_id != self_run_id, so it needs child events to inject
lifecycle notifications into OrchestrationEventService for the parent
conversation. Without this, the parent BlocklistAIController never
receives child lifecycle events, causing a regression for local parents
with local or remote children.

Fix: collect child lifecycle events during the drain loop and extend
parent_self_events before the handle_event_batch call in Primary mode.

Co-Authored-By: Oz <oz-agent@warp.dev>
@cephalonaut
cephalonaut force-pushed the matthew/orch-unified-m2 branch from 74d38ec to c902025 Compare August 1, 2026 03:06
@cephalonaut
cephalonaut force-pushed the matthew/orch-unified-m3 branch from 87d6305 to 0646657 Compare August 1, 2026 03:07
…older unification

- Rewrite orchestration_child_tracker.rs module doc in plain language:
  guides children from discovery to pane materialization
- Add TODO documenting intent to unify is_remote_child and
  is_viewing_shared_session into a single is_remote_placeholder flag

Co-Authored-By: Oz <oz-agent@warp.dev>
@cephalonaut
cephalonaut force-pushed the matthew/orch-unified-m2 branch from c902025 to 9c46577 Compare August 1, 2026 05:12
@cephalonaut
cephalonaut force-pushed the matthew/orch-unified-m3 branch 2 times, most recently from 33eae80 to 1bd4310 Compare August 1, 2026 05:22
…ntConsumer

The conversation_id field in TrackedChild was never read — all actual
conversation lookups use history.conversation_id_for_agent_id(run_id).
OrchestrationEventConsumer existed solely to ferry conversation IDs into
TrackedChild. Both are now removed.

- Remove OrchestrationEventConsumer enum (Primary/Observer distinction
  is already captured by FamilyDrainMode in the streamer)
- Remove conversation_id from TrackedChild
- Simplify OrchestrationChildTracker::new() to take only parent_task_id
- Remove placeholder_conversation_id() method
- Simplify ChildSignal::Registered to a unit variant

Co-Authored-By: Oz <oz-agent@warp.dev>
@cephalonaut
cephalonaut force-pushed the matthew/orch-unified-m2 branch from 5ffcb5e to 96119ad Compare August 1, 2026 15:31
@cephalonaut
cephalonaut force-pushed the matthew/orch-unified-m3 branch 2 times, most recently from 53051b7 to 71bfd7c Compare August 1, 2026 15:42
cephalonaut and others added 2 commits August 1, 2026 13:25
Consolidate the TECH spec so M1's document stands alone and reflects
the final simplified architecture:

- Rename owner/viewer to Primary/Observer throughout
- Add ChildPaneOrigin, TaskOwnership, ConversationAccess definitions
- Update placeholder flavor descriptions with current field names
- Update tracker step descriptions to match simplified implementation
  (no conversation_id in TrackedChild, no OrchestrationEventConsumer)
- Update pane path §4.3 with capability-separation clarification
- Update cursor and delivery path descriptions to use Primary/Observer
- Update north-star diagram labels

Co-Authored-By: Oz <oz-agent@warp.dev>
Introduce a mode-agnostic child pane dispatch decision that returns the
same result for owner and viewer given identical task state. This is the
unified replacement for decide_remote_child_hydration_action; callers are
wired up in a follow-up task.

Co-Authored-By: Oz <oz-agent@warp.dev>
cephalonaut and others added 26 commits August 1, 2026 13:25
…iedStack

Restore flag-OFF paths so M2's unified child pane dispatch sits safely
behind FeatureFlag::OrchestrationUnifiedStack (dogfood-only).

- Restore RemoteChildHydrationAction enum, decide_remote_child_hydration_action,
  hydrate_task_backed_hidden_child_pane, attempt_remote_child_hydration,
  hydrate_remote_child_transcript_in_place, attach_ambient_session_and_maybe_tombstone,
  and process_pending_remote_child_hydrations as flag-OFF paths in hydration.rs.
- Restore ensure_shared_session_viewer_child_pane as flag-OFF path in restoration.rs.
- Restore pending_remote_child_hydrations field on PaneGroup and its drain call
  in handle_pending_ambient_restoration_event.
- Flag-dispatch in create_hidden_child_agent_pane: flag-ON routes through
  materialize_child_placeholder_pane (M2 unified path); flag-OFF routes
  through the original hydrate_task_backed_hidden_child_pane / viewer
  loading placeholder.
- Flag-dispatch in EnsureSharedSessionViewerChildPane handler: flag-ON calls
  attach_child_session(Viewer); flag-OFF calls ensure_shared_session_viewer_child_pane.
- Add OrchestrationUnifiedStack override to tests that exercise flag-ON behavior.

Co-Authored-By: Oz <oz-agent@warp.dev>
Carry the M1 zero-state pane fix into M2's unified owner materialization path by attaching the known shared session after binding the ambient task.

Co-Authored-By: Oz <oz-agent@warp.dev>
…estigation

Instruments the full status-badge and pane-click failure paths to
trace both bugs without changing behavior:

Status path (Bug 1):
- drain_family_events: ChildStarted / ChildLifecycle / ChildSessionLinked events (INFO)
- ensure_remote_child_placeholder: entry / skip-existing / fetch-start (INFO)
- finish_remote_child_placeholder: success / failure / skip / created (INFO)
- apply_lifecycle: entry tracker_known/fetch_in_flight, history-lookup result,
  update_conversation_status before/after, emit ChildStatusChanged (INFO)

Pane path (Bug 2):
- create_hidden_child_agent_pane: entry flags + feature flag state (INFO)
- materialize_child_placeholder_pane: idempotent-skip / entry / task-detail (INFO)
- materialize_owner_child_pane: match-arm taken / Pending (INFO)
- attach_child_session: entry / owner pane-not-found / task_id / result (INFO)
- attach_execution_session_to_ambient_pane: entry has_ambient_model /
  via-ambient-model-ok / no-terminal-manager / non-viewer-manager /
  via-viewer-ok (INFO)

No behavioral changes. All log lines share the
[orchestration-unified-debug] prefix.

Co-Authored-By: Oz <oz-agent@warp.dev>
Root causes (confirmed by [orchestration-unified-debug] log):

Bug 1 (status badges):
apply_started never inserted a TrackedChild into children[], so every
subsequent Lifecycle call saw tracker_known=false and exited immediately.

Fix: insert a TrackedChild eagerly in apply_started (before the async
metadata fetch) so Lifecycle and SessionLinked signals see the child
and call update_conversation_status. stamp_conversation_id_for_run then
patches the stand-in conversation_id once finish_remote_child_placeholder
creates the real AIConversation.

Bug 2 (zero-state pane) - two sub-causes:

2a (running child): AgentConversationsModel had stale Queued/Inactive
task data from the initial discovery fetch; the linked session_id was
never injected, so decide_child_pane_materialization returned Pending.
Fix: on ChildSessionLinked in drain, call
update_task_as_running_with_session to mark the cached task InProgress +
is_sandbox_running=true + session_id so the next pill click returns
AttachLive.

2b (completed child): task data still showed Queued after Succeeded;
no conversation token was present so LoadTranscript was never returned.
Fix: on terminal ChildLifecycle, call evict_and_refetch_task to drop
the stale cache entry and start a fresh fetch. The Pending arm of
materialize_owner_child_pane now registers in pending_remote_child_hydrations
so process_pending_remote_child_hydrations re-drives with the fresh data
(AttachLive or LoadTranscript) when TasksUpdated fires.

Co-Authored-By: Oz <oz-agent@warp.dev>
Owner-side Pending children now use the existing child loading presentation instead of the generic cloud-agent composing zero state.

When a session becomes attachable, discard the loading pane and construct a fully initialized ambient shared-session viewer before swapping it into the prior visible anchor. This mirrors normal ambient restoration and avoids rendering agent input while a deferred pane has transient unbounded constraints.

Completed children keep the loading pane until the transcript fetch and placeholder merge succeed, then switch the terminal model to read-only ambient transcript state before restoring content. This makes Pending-to-LoadTranscript safe without exposing the composing input.

Flag-off behavior remains unchanged. No flex assertions or terminal input layout code are modified.

Co-Authored-By: Oz <oz-agent@warp.dev>
AgentViewController previously derived EnteredAgentView.is_new solely from exchange_count == 0. Existing remote-child placeholders are often empty while restoring or before their first streamed exchange, so they were misclassified as brand-new cloud conversations and received the generic New Oz cloud agent conversation zero-state header.

Under OrchestrationUnifiedStack, preserve the existing remote-child identity and emit is_new=false. New cloud conversations and flag-off behavior are unchanged.

Co-Authored-By: Oz <oz-agent@warp.dev>
Completed owner-side remote children were forced into ViewingAmbientConversation transcript state, which makes TerminalModel permanently read-only and suppresses the footer even when ownership resolves to an editable Oz follow-up.

Route owner completed hydration through the established restored ambient cloud-mode pane path after merging the canonical child placeholder. Preserve owner mode by explicitly clearing passive shared-session state; ownership then chooses NotShared plus the normal continuation input, while passive viewers retain the read-only transcript path.

Running child restore remains on the active shared-session viewer path. The existing remote-child is_new=false fix remains unchanged.

Co-Authored-By: Oz <oz-agent@warp.dev>
Completed child restoration used generic replace_pane, which requires the loading pane to be present in the layout tree. Persisted child panes are restored off-tree, so the fully restored replacement was immediately cleaned up after tree replacement failed and the original loading pane remained forever.

Use the child-specific lifecycle instead: capture any visible swap anchor, discard the loading child, construct and restore the owner ambient continuation pane, attach it off-tree, update child_agent_panes, and re-swap only when needed. Owner continuation, passive viewer read-only behavior, pending safety, and existing-child header semantics remain intact.

Co-Authored-By: Oz <oz-agent@warp.dev>
Trace canonical conversation/root identity, follow-up ambient task/session state, shared-session load mode and replay suppression, shared Init conversation resolution, and incoming CreateTask vs current root identity.

No behavior changes and no prompt/message/token content logging.

Co-Authored-By: Oz <oz-agent@warp.dev>
AppendFollowupScrollback previously relied solely on AgentConversationReplayStarted to initialize terminal and controller replay gates. Some follow-up sessions deliver existing transcript Init/CreateTask events before that marker, allowing a server-backed root to be treated as an optimistic task and corrupting the restored conversation.

Initialize both replay-gate halves synchronously when the append-mode EventLoop is created. The existing request-aware check still permits unseen live request IDs, and AgentConversationReplayEnded clears the gate normally.

Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
Co-Authored-By: Oz <oz-agent@warp.dev>
Remove stale tracker dead-code annotation, avoid treating Unspecified lifecycle events as terminal for task refetch, and document follow-up cleanup for duplicate metadata fetches and child registries.\n\nCo-Authored-By: Oz <oz-agent@warp.dev>
Remove is_durable_observer_parent from M2 so the observer-parent restore
feature can land as a separate PR. Under flag-ON, a terminal owned cloud
run viewed as an observer now falls through to a fresh compose pane on
restart instead of the durable restore path — identical to the flag-OFF
and pre-M2 baseline behavior, so this is a deferral not a regression.

Removes:
- AgentConversationData.is_durable_observer_parent field (persistence)
- AIConversation.is_durable_observer_parent field + getter/setter
- BlocklistAIHistoryModel::{mark_conversation_as_durable_observer_parent,
  restore_durable_observer_parent_for_task}
- Eager-hydration branch in conversation_loader.rs
- Durable-parent gate in ambient_pane_restoration.rs
- TaskOwnership::Owned ownership check in OrchestrationViewerModel
- restore_durable_observer_parent_for_task call in terminal_manager.rs
- Corresponding tests (moved to M3 branch)

Co-Authored-By: Oz <oz-agent@warp.dev>
current_root_id and current_root_has_server_data were declared in the
CreateTask action handler alongside a log::info! that referenced them.
The log statement was removed by the debug cleanup pass but the variable
declarations were left behind.

Co-Authored-By: Oz <oz-agent@warp.dev>
Restore the original M1 comment; the rewrite was a side-effect of the
is_durable_observer_parent extraction.

Co-Authored-By: Oz <oz-agent@warp.dev>
#2: Remove empty if-block for is_existing_child_placeholder in controller.rs
#3: Remove dead selected_conversation_id/root_identity bindings in shared_session.rs
#4: Simplify ensure_remote_child_conversation comment to focus on idempotence
#10/#11: Update tracker field comments to use Primary/Observer terminology
    and explain when None

Co-Authored-By: Oz <oz-agent@warp.dev>
- Remove fetch_in_flight in apply_lifecycle (code now calls apply_started directly)
- Simplify Pending arm pane creation to is_none() check (pane_id was unused)
- Remove root_task binding in hydrate_owner_child_transcript (never referenced)
- Remove has_ambient_model binding in attach_execution_session_to_ambient_pane

Co-Authored-By: Oz <oz-agent@warp.dev>
#8 (M2-specific): Remove stamp_conversation_id_for_run calls from
finish_remote_child_placeholder. The function is no longer on the
tracker since conversation_id was removed from TrackedChild.

#9: OVM's flag-ON path now uses AgentConversationsModel::get_or_async_fetch_task_data
instead of a direct ai_client fetch + cache_task_data. A cache hit
calls register_child immediately; a miss adds to pending_task_ids_for_discovery
and resolves on the next TasksUpdated event via drain_pending_task_discoveries.

Also removes cache_task_data from ACM (no longer called anywhere) and
removes the now-redundant cache_task_data call from
materialize_viewer_child_pane_from_task (task data is already in ACM).

Co-Authored-By: Oz <oz-agent@warp.dev>
- Remove conversation_id and placeholder_conversation_id() from
  apply_started's eager TrackedChild insertion (field was removed in #8)
- Prefix mode with _ in finish_remote_child_placeholder (unused since
  stamp_conversation_id_for_run calls were removed in #8)

Co-Authored-By: Oz <oz-agent@warp.dev>
@cephalonaut
cephalonaut force-pushed the matthew/orch-unified-m2 branch from 8dd85d8 to 6c23d40 Compare August 1, 2026 17:26
…ud runs

When a user owns a /cloud-agent run but opens it via the shared-session
viewer path (e.g. through a shared link), this PR makes that pane restore
durably across restarts instead of opening a fresh compose pane.

What:
- AgentConversationData.is_durable_observer_parent: bool (persisted marker)
- mark_conversation_as_durable_observer_parent: stamps marker + task_id on
  the parent placeholder when TaskOwnership::Owned is confirmed by OVM
- restore_durable_observer_parent_for_task: re-attaches the eagerly-hydrated
  conversation to the restored ambient pane before shared-session replay
- Eager hydration in conversation_loader.rs: durable observer parents load
  at startup with their event cursor, excluded from navigation history
- Durable-parent restore in ambient_pane_restoration.rs: RestoreOrNavigate
  installs the persisted conversation in cloud-mode instead of fresh compose

Note: this could alternatively be replaced by making is_viewing_shared_session
persistent for all viewer conversations (not just owned ones), which would be
simpler. This PR keeps the feature isolated for independent evaluation.

Co-Authored-By: Oz <oz-agent@warp.dev>
@cephalonaut
cephalonaut force-pushed the matthew/orch-unified-m3 branch from 71bfd7c to 4f2a186 Compare August 1, 2026 17:26
@cephalonaut
cephalonaut force-pushed the matthew/orch-unified-m2 branch from 081761c to 53b8105 Compare August 2, 2026 20:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants