Skip to content

fix(native-chat): release sessions after provider root exit - #20502

Merged
brennanb2025 merged 21 commits into
mainfrom
brennanb2025/native-chat-launch-visibility
Sep 15, 2026
Merged

brennanb2025 merged 21 commits into
mainfrom
brennanb2025/native-chat-launch-visibility

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 45 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​2200 $\color{#cf222e}{\Huge{\mathbf{−}}}$​2076 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​124
Prod 79 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​3268 $\color{#cf222e}{\Huge{\mathbf{−}}}$​1809 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1459

ELI5

Opening a structured agent chat now gives the user a usable chat view immediately. Provider acquisition continues behind that view, drafts and sends remain durable, and a slow or refused structured launch never silently turns into a legacy terminal.

What Changed

  • Fixed the root lifecycle bug where a provider root had exited but an unverified descendant snapshot made eviction fail forever.
  • Release only the lease owned by a provider root after first-hand root-exit or processless proof; live and genuinely unproven descendants still fail closed.
  • Create a provisional native-chat tab immediately, gate provider reads and mutations until publication, and keep the durable outbox available while acquisition is pending.
  • Persist launch identity across renderer reloads, normalize interrupted launches to manual Retry, and tombstone cancelled sessions so late publication cannot resurrect a closed tab.
  • Removed the renderer deadline and every structured-launch-to-terminal fallback, including definitive-refusal fallback. Unknown outcomes retain the existing visibilityUnknown/manual Retry path.
  • Keep option discovery and prompt-cancel capability negotiation fenced to the published provider session.
  • Retain one phase-timed, privacy-safe agentSession.create span with no branch, path, prompt, session content, or sensitive identifier attributes.

Why

#19154 records a Claude root that exited before producing output. Because the descendant snapshot was still unverifiable, eviction treated the dead lease owner like a live descendant and could not release the lease or host session.

The durable lease identifies the provider root PID and start time, so first-hand root death is enough to release that root-owned lease. This does not claim that unknown descendants exited; later cleanup still preserves that uncertainty.

The user-facing fix is architectural: the chat surface is available immediately and acquisition is background work. A timeout or refusal therefore cannot replace the structured route with a different product surface.

Linked Issue

Fixes #19154

Testing

  • Targeted launch/lifecycle command: 38 test files passed (38), 344 tests passed (344); every path was supplied as a separate pnpm test argument.
  • Post-refactor native-chat smoke: 4 test files passed (4), 26 tests passed (26).
  • New behavior ablations went red when their implementation behavior was removed and were restored from SHA-256-verified copies.
  • pnpm run check:code-quality:changed: passed with 0 findings across 103 changed files.
  • git diff --check: passed; lockfile restored and clean.
  • pnpm tc: reached TypeScript but failed on 73 pre-existing editor errors caused by mixed installed prosemirror-model 1.25.4/1.25.11 trees. No reported error references the native-chat changes.
  • No full test suite, install, stash, checkout, rebase, or visible-window Electron run was performed.

Review

The branch was merged with current origin/main and pushed as 5d8692db1c. The current head also structurally splits the upstream message-rail windowing coverage to satisfy the repository max-lines rule without a lint suppression and keeps transport gating render-pure. Earlier automated comments about a startup label and 30-second terminal fallback describe superseded behavior; the current design intentionally removes both. Please review the immediate provisional-tab lifecycle, root-owned lease release, and cancellation/publication fencing on this head.

Notes

  • fix(native-chat): wait for the runtime capability probe before resolving the creation launch route #19819 is merged and included; it fixes route capability timing, while this PR fixes the provider-root exit/eviction lifecycle and makes structured chat immediately usable during acquisition.
  • Folder workspaces and git worktrees use the same structured launch contract.
  • Linux, Windows, SSH, relay, paired web, and mobile manual runs were not performed locally; no platform-specific process call, wire field, or persisted schema was added.

@coderabbitai

coderabbitai Bot commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 9a2d59b8-f929-4f5a-acbf-668a84f8f0e6

📥 Commits

Reviewing files that changed from the base of the PR and between b4432ec and 361ee00.

📒 Files selected for processing (1)
  • src/renderer/src/i18n/locales/en.json
💤 Files with no reviewable changes (1)
  • src/renderer/src/i18n/locales/en.json

Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

The change adds agent-session creation tracing with phase timing and span attributes. Claude acquisition and cleanup now use centralized resolution and classified exit errors. Structured renderer launches no longer fall back to legacy terminal surfaces after refusal or deadline outcomes. Launch state moves to a registry, callers use structured settlement results, and pending creation and recovery messages identify the selected agent.

Priority: ➖ Normal

Merge Risk: 🟡 Moderate · up to 361ee

A stalled structured launch can remain pending without a usable recovery surface. Add a bounded settlement or equivalent recovery path before merging.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.03% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 59 functions across 47 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR satisfies the coding requirements in [#19154]. structured-agent-session-claude-root-exit.test.ts verifies that a Claude provider-root exit releases the captured lease, records `deathEvidence:…
Out of Scope Changes check ✅ Passed The changed Claude cleanup, eviction, acquisition, launch-progress, recovery-message, settlement, observability, and test files support the objectives in [#19154]. The fallback and deadline changes pr…
Title check ✅ Passed The title clearly identifies the primary fix: releasing native-chat sessions after the provider root exits.
Description check ✅ Passed The description is detailed, on-topic, and includes the ELI5, changes, rationale, linked issue, testing, review, and notes sections. It omits several template sections, including Visual Proof, AI Disc…
Full details: Docstring Coverage

Explanation

Docstring coverage is 22.03% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 59 functions across 47 files. (1 skipped: 1 unsupported.)


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: dfc60991-5464-4acb-b58c-36978faf7487

📥 Commits

Reviewing files that changed from the base of the PR and between c6548b9 and 643dcf9.

📒 Files selected for processing (22)
  • IMPLEMENTATION-NOTES.md
  • src/main/claude/claude-structured-acquisition-launch.ts
  • src/main/claude/claude-structured-session-acquisition.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-acquisition-options.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-acquisition.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-adapter.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-attach-flow.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-attach-orchestration.ts
  • src/main/observability/agent-session-instrumentation.ts
  • src/main/observability/instrumentation.test.ts
  • src/renderer/src/lib/launch-work-item-direct-agent-routing.test.ts
  • src/renderer/src/lib/launch-work-item-direct-agent-routing.ts
  • src/renderer/src/lib/pending-worktree-creation.test.ts
  • src/renderer/src/lib/pending-worktree-creation.ts
  • src/renderer/src/lib/structured-agent-launch-settlement.test.ts
  • src/renderer/src/lib/structured-agent-launch-settlement.ts
  • src/renderer/src/lib/structured-agent-session-launch-callers.ts
  • src/renderer/src/lib/structured-agent-session-launch-deadline-fallback.test.ts
  • src/renderer/src/lib/structured-agent-session-launch-registry.ts
  • src/renderer/src/lib/structured-agent-session-launch.ts
  • src/renderer/src/lib/worktree-creation-structured-session.test.ts
  • src/renderer/src/lib/worktree-creation-structured-session.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

The new deadline-then-legacy settlement is only understood by two of its consumers, so a slow launch now takes the wrong path in the full-create, folder-workspace, new-tab, and fork flows.

Reviewed changes

  • Phase-timed agentSession.create tracing — new agent-session-instrumentation.ts wraps create-intent attaches once and threads a phase recorder through orchestration → attach flow → acquisition → adapter → Claude acquisition; attributes carry only closed phase names and timings.
  • starting-chat pending phase — WorktreeCreationPhase gains starting-chat, worktree creation sets it immediately before launch, and getCreationProgressLabel names the pending agent.
  • 30s deadline fallback — settleStructuredAgentLaunch races the launch against an injectable deadline that claims the shared fallback as deadline-then-legacy, keeps cancellation ahead of it, and retires a late structured publication.
  • Generalized fallback claim — claimFallback replaces claimDefinitiveRefusalFallback (kept as a deprecated alias); the callers group only transitions from pending/unknown, and the worktree session retires late structured sessions.
  • Registry extraction / Claude refactor — launch registry and status hooks move to structured-agent-session-launch-registry.ts; Claude launch resolution is extracted to stay under the line cap.

ℹ️ Nitpicks

  • IMPLEMENTATION-NOTES.md is a 132-line agent work log added at the repo root. It reads like a scratch plan/ablation record rather than repo documentation; consider dropping it from the PR (or moving the durable parts into docs/reference/).

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

…hat-launch-visibility

# Conflicts:
#	src/main/native-chat/agent-session-wire/structured-agent-session-attach-flow.ts

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

The delta since the prior pullfrog review (643dcf9) is the merge commit 348149f and a test-hygiene follow-up b053abe. It resolves the prior [!IMPORTANT] finding that deadline-then-legacy was only understood by two consumers.

  • Centralized legacy-fallback interpretation — new structuredAgentLegacyFallbackFromSettlement normalizes refused-then-legacy and deadline-then-legacy into one result, and full-create, folder-workspace create, new-tab, fork, and direct-routing all consume it instead of branching on the refusal kind alone.
  • Universal late retirement — the per-flow onStructuredLate hook is gone; the shared settlement loop calls the new retireStructuredAgentSessionSurface whenever the fallback ran, and registers it before the post-fallback cancellation check, so a structured publication that lands after the fallback is retired in every flow.
  • Census ratchet — a new test asserts no production file outside the settlement module names the legacy kinds directly; confirmed by grep (only the settlement module and tests match).
  • Cleanup — IMPLEMENTATION-NOTES.md is deleted and post-merge test hygiene restored the hoisted mocks and formatting.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

Commit 04673918 ("Make structured fallback settlement exhaustive") landed after the previous review; it tightens the legacy-fallback API rather than changing behavior.

  • Type-narrowing guard — added isStructuredAgentLegacyFallbackSettlement, and overloaded structuredAgentLegacyFallbackFromSettlement so a settlement narrowed by the guard returns a non-null result.
  • Exhaustive consumers — launch-work-item-direct-agent-routing.ts and worktree-creation-structured-session.ts narrow first, then consume the fallback without optional chaining, and drop their default / merged failed branches. The remaining switches cover every non-legacy kind, so a future settlement kind becomes a compile error instead of a silent fallthrough.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@brennanb2025 brennanb2025 changed the title fix(native-chat): make slow structured launches visible and bounded fix(native-chat): release sessions after provider root exit Sep 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: dc85e667-0e6f-492d-a464-8a4feb2dc132

📥 Commits

Reviewing files that changed from the base of the PR and between 0467391 and b4432ec.

📒 Files selected for processing (35)
  • src/main/claude/claude-structured-session-close.test.ts
  • src/main/claude/claude-structured-session-close.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-claude-root-exit.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-eviction.test.ts
  • src/main/native-chat/agent-session-wire/structured-agent-session-eviction.ts
  • src/renderer/src/components/sidebar/folder-workspace-composer-submit.ts
  • src/renderer/src/components/terminal-pane/terminal-agent-session-fork.test.ts
  • src/renderer/src/components/terminal-pane/terminal-agent-session-fork.ts
  • src/renderer/src/hooks/composer-state/full-creation-execution.ts
  • src/renderer/src/hooks/composer-state/full-creation-structured-launch.test.ts
  • src/renderer/src/hooks/composer-state/full-creation-structured-launch.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/lib/launch-agent-in-new-tab-structured.test.ts
  • src/renderer/src/lib/launch-agent-in-new-tab-structured.ts
  • src/renderer/src/lib/launch-agent-in-new-tab.ts
  • src/renderer/src/lib/launch-agent-structured-chat-guard.test.ts
  • src/renderer/src/lib/launch-work-item-direct-agent-routing.test.ts
  • src/renderer/src/lib/launch-work-item-direct-agent-routing.ts
  • src/renderer/src/lib/launch-work-item-direct.ts
  • src/renderer/src/lib/onboarding-folder-agent-launch.ts
  • src/renderer/src/lib/structured-agent-launch-no-terminal-fallback.test.ts
  • src/renderer/src/lib/structured-agent-launch-settlement-caller-census.test.ts
  • src/renderer/src/lib/structured-agent-launch-settlement.test.ts
  • src/renderer/src/lib/structured-agent-launch-settlement.ts
  • src/renderer/src/lib/structured-agent-session-launch-callers.ts
  • src/renderer/src/lib/structured-agent-session-launch-failure-toast.ts
  • src/renderer/src/lib/structured-agent-session-launch-refusal-fallback.test.ts
  • src/renderer/src/lib/structured-agent-session-launch-registry.ts
  • src/renderer/src/lib/structured-agent-session-launch.test.ts
  • src/renderer/src/lib/structured-agent-session-launch.ts
  • src/renderer/src/lib/worktree-creation-flow-execute.ts
  • src/renderer/src/lib/worktree-creation-structured-recovery.ts
  • src/renderer/src/lib/worktree-creation-structured-session.test.ts
  • src/renderer/src/lib/worktree-creation-structured-session.ts
  • src/renderer/src/lib/worktree-creation-structured-unknown-outcome.test.ts
💤 Files with no reviewable changes (5)
  • src/renderer/src/components/terminal-pane/terminal-agent-session-fork.ts
  • src/renderer/src/hooks/composer-state/full-creation-structured-launch.ts
  • src/renderer/src/lib/structured-agent-launch-settlement-caller-census.test.ts
  • src/renderer/src/lib/structured-agent-session-launch-refusal-fallback.test.ts
  • src/renderer/src/components/terminal-pane/terminal-agent-session-fork.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/src/lib/structured-agent-session-launch-registry.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.

@brennanb2025

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 14, 2026 •

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — one documentation note inline.

Reviewed changes

Commit b4432ecee2 reworks the PR into a root-cause fix and removes the deadline/terminal-fallback design the earlier reviews covered. That commit is the delta since the prior pullfrog review at 04673918c3; the rest of the branch is unchanged.

  • Provider root-exit releases the lease — claude-structured-session-close.ts classifies an unproven published-session close and an unproven acquisition cancel through claudeAcquisitionCleanupError, throwing AgentSessionAcquisitionRootExitObservedError (root: exited, tree: unverifiable) or AgentSessionPreSpawnError (processless) instead of returning false. Only a genuinely unproven exit still returns false.
  • Eviction continues on actionable verdicts — structured-agent-session-eviction.ts swallows those two errors and runs the remaining steps (drain → sink teardown → release-lease → forget); every other throw, including …ExitUnprovenError, still aborts so the next close is a real retry.
  • Deadline and terminal fallback removed — structured-agent-launch-settlement.ts drops legacyFallback/claimFallback/STRUCTURED_AGENT_LAUNCH_DEADLINE_MS, the caller-group refusal settlement, the late-surface retirement module, and the terminal fallback in every renderer entrypoint; a definitive refusal now settles as failed.
  • Agent-named unknown recovery — markStructuredWorktreeLaunchUnconfirmed takes the narrowed provider and the unknown toast interpolates {{value0}}; en.json and its tests were updated.
  • Census ratchet — a new test asserts no production renderer file names the fallback kinds or the deadline constant.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Independent hidden-CDP QA — structured chat starting visibility

Validated on worktree /Users/brennanbenson/orca/workspaces/orca/native-chat-launch-visibility at 6dae2fb44f (brennanb2025/native-chat-launch-visibility). App identity: Orca: brennanb2025/native-chat-launch-visibility. Hidden launch with ORCA_BACKGROUND_LAUNCH=1 and CDP (no show / focus / OS automation).

The production pending row and creation panel name the selected agent and keep the elapsed counter visible:

Claude — pending row + panel, elapsed 57s

Starting Claude chat shown in the pending row and panel with 57s elapsed

Codex — pending row + panel, elapsed 11s

Starting Codex chat shown in the pending row and panel with 11s elapsed

Renderer console: 0 errors (unrelated Tooltip / React Grab warnings only). No production files changed.

Method note: the starting-chat phase was held through the real store APIs (beginPendingWorktreeCreation with phase: 'starting-chat') so the elapsed counter could be photographed. A live Claude provider spawn was not used, because a healthy launch can finish in ~1.5s.

Local evidence: /tmp/orca-pr20502-qa-evidence/

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status

PASS WITH RISK — no valuable code fixes required.

  • Same-model adversarial review completed; no proven regressions or meaningful in-scope fixes found.
  • Final readiness checklist completed with no P0/P1/P2 findings.
  • Validation passed: targeted changed-test suite (17 files, 151 tests), additional lifecycle/proof suites (47 + 52 + 66 tests), pnpm tc, pnpm tc:node, pnpm tc:web, changed-code quality (0 findings across 41 files), and git diff --check.
  • Hidden Electron/CDP proof on the exact PR worktree confirmed the rendered pending row and creation panel show agent-specific “Starting Claude chat…” / “Starting Codex chat…” text with elapsed time and 0 renderer errors. Screenshots are attached in the QA comment above.
  • No mobile-facing RPC/protocol/pairing surfaces were changed.

Residual risk: this session did not exercise a live provider-backed create, real SSH/relay/network-failure paths, Windows/WSL runtime, or the full local suite. GitHub’s static analysis check is red from three existing mobile test warnings unrelated to this diff; the changed-code quality gate is clean.

No commit or push was necessary.

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Merge conflicts resolved and pushed in commit 5c4d95146e (merged origin/main at 539d4d1f32).

Resolved conflicts in Claude structured acquisition and acquisition-options tests while preserving the PR's launch lifecycle/instrumentation changes and current-main fast-mode/session-option behavior. Focused tests (25) and pnpm tc pass; merge state is now MERGEABLE (UNSTABLE pending CI).

@greptile-apps

greptile-apps Bot commented Sep 15, 2026 •

Copy link
Copy Markdown

Too many files changed for review (124 files, 100 file limit).

Bypass the limit by tagging @greptile-apps to review.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

This delta rebases the structured-chat launch/close lifecycle onto main's surface-based launch API. Two behavioral gaps come with it: a closed published chat can be re-mirrored because only provisional closes get a cancellation tombstone, and a caller coalescing onto a retained failed launch has its prompt silently dropped.

Reviewed changes

Incremental delta 7e7125dcf9..a145a79dab — the dc9c77c8e8 "fix native chat launch visibility" reconciliation (87 files) plus the a145a79dab merge of origin/main (b61a2347b9).

  • Adopted main's { surface } launch result everywhere — new-tab, work-item direct, fork, quick launch, source-control recovery, onboarding, worktree creation, full-create, and folder-workspace entrypoints now consume LaunchAgentInNewTabResult.surface and thread beforeSurfaceOpen, so a workspace can reveal before its surface opens.
  • Extracted the launch state machine — structured-agent-session-launch-registry.ts plus new structured-agent-session-launch-persistence.ts (localStorage launch records and session-scoped cancellation tombstones) and structured-agent-session-launch-reload.ts.
  • Provisional launch — structured-agent-session-provisional-tab.ts and beginStructuredAgentSessionProvisionalLaunch open the chat tab and expose the durable sessionId before host acquisition settles; NativeChatLaunchRetry / NativeChatDeliveryRetry and useNativeChatProvisionalLaunch drive retry and transportEnabled.
  • Reworked structured-chat tab close — closeUnifiedTab removes the local row synchronously and delegates host retirement to retireStructuredAgentSessionTab, with tombstone suppression wired into the snapshot/mirror path and silent cancellation during bulk worktree purges.
  • Gated provider transport on launch lifecycle — new use-structured-agent-session-transport, …-transport-state, and …-options withhold provider hold/read/mutate and the option surface while a launch is pending, failed, or visibility-unknown.

ℹ️ Nitpicks

  • useNativeChatProvisionalLaunch guards retry on worktreeId sourced from useNativeChatFileLinkContext; when that context is null the NativeChatLaunchRetry affordance still renders but the click is a no-op (src/renderer/src/components/native-chat/use-native-chat-provisional-launch.ts:11-16). Passing the worktree the pane overlay already holds would remove the dead end.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

provisional: boolean
onError?: (error: unknown) => void
}): void {
if (args.provisional) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cancellation tombstone is written only for provisional closes, but this delta also moved local row removal ahead of host retirement (tabs-close-actions.ts removes synchronously; this function fires agentSession.close and session.tabs.close asynchronously). A published session has no tombstone, so a host frame that still contains it — an unrelated worktree update during the close round-trip, or any frame at all when the close RPC rejects — re-mirrors the row through buildMirroredAgentTabs. On main the close awaited the host calls and then removed the row, and a rejection surfaced a toast; now a failed retirement is swallowed as a console warning with no retry, so the resurrected row can persist.

Technical details
# Published structured tabs have no close tombstone

## Affected sites
- `src/renderer/src/runtime/structured-agent-session-tab-retirement.ts:66-70` — tombstone only when `args.provisional`; `retireStructuredAgentSessionTab` swallows rejections (no `onError` from `tabs-close-actions.ts`).
- `src/renderer/src/store/slices/tabs/tabs-close-actions.ts:51-71` — `provisional` is false for a published session (`shouldRetainStructuredAgentSessionLaunchTab` covers only pending/visibility-unknown/failed), and the row is removed later in the same synchronous `set`.
- `src/renderer/src/runtime/web-session-tabs-sync/terminal-surfaces.ts:64-69` and `apply-preparation-browser.ts:177-182` — the mirror/suppression path only consults the tombstone, never `withLocalSessionTabCloseOwner` (that owner is consumed by `session-tab-ipc-bridge.ts` only).
- `src/renderer/src/components/tab-group/workspace-tab-close-commands.ts` — the pre-`dc9c77c` branch awaited `closeStructuredAgentSession`/`session.tabs.close` before `closeUnifiedTab` and toasted via `reportStructuredSessionCloseError`; that ordering was removed.

## Required outcome
- Closing a published structured chat must not re-add its tab from a host frame that predates the host acknowledging the close, and a failed host retirement must be retried or reported rather than silently lost.

## Suggested approach (optional)
- Record the cancellation tombstone for every user-initiated structured tab close (not only provisional), or restore the local row only after the host close resolves; pass an `onError` that reports and/or retries the host close.

Comment on lines +181 to +190
// Why: failed launches keep their draft/outbox, so a retry must not stage the same prompt twice.
const text = retrying ? '' : outboxPromptText(joined)
const stagedPrompt = text
? enqueueStructuredAgentSessionLaunchPrompt(existing.intent.sessionId, text)
: null
if (!retrying) {
launchDraft.seedStructuredAgentLaunchDraft(existing.intent.sessionId, agent, joined)
}
const { prompt: _retryPrompt, ...joinedWithoutPrompt } = joined
const callerOptions = retrying ? joinedWithoutPrompt : joined

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When an existing state is failed or visibility-unknown, retrying strips prompt for every joining caller, not just a re-stage of the same prompt. A second launch for the same agent:worktreeId identity that carries a different prompt therefore has it neither staged in the outbox nor seeded into the composer, and settleStructuredAgentLaunchPrompt returns undefined — so no promptDeliveryResult is reported and the retained outbox entry delivers the earlier prompt in its place.

Technical details
# Retried launch drops a joining caller's prompt

## Affected sites
- `src/renderer/src/lib/structured-agent-session-launch.ts:181-190` — `text = retrying ? '' : outboxPromptText(joined)` plus `callerOptions = retrying ? joinedWithoutPrompt : joined` applies to any joiner, while the comment only justifies skipping a duplicate re-stage (`failed launches keep their draft/outbox`).
- `src/renderer/src/lib/structured-agent-session-launch-prompt.ts:97` — with no `prompt`, `settleStructuredAgentLaunchPrompt` returns `undefined`, so the caller's `promptDeliveryResult` is absent.
- Impact: `runSourceControlAgentActionStart` treats a missing `promptDeliveryResult` as success and fires `onLaunched`, so irreversible follow-up runs as if the prompt reached the provider.

## Required outcome
- A joiner with a prompt distinct from the retained entry must have it staged/delivered (and report a delivery result); only a re-staged duplicate may be skipped.

## Suggested approach (optional)
- Compare the joining prompt against the retained draft/outbox entry (or carry an explicit `reuseStagedPrompt` flag) and strip the prompt only on a match; stage it normally otherwise.

@brennanb2025

Copy link
Copy Markdown
Contributor Author

PR #20502 Electron validation — native structured-chat launch visibility

Scenario

Hidden Electron validation of brennanb2025/native-chat-launch-visibility (PR #20502) with ORCA_BACKGROUND_LAUNCH=1 and CDP on 127.0.0.1:9333 / renderer 127.0.0.1:5176. Isolated profile seeded with completed onboarding and structured-chat settings enabled. Temp git repo /tmp/orca-pr-20502-validation/repo added as the workspace.

Launched a structured Claude chat from the real New tab → Claude menu. Sent a composer message after the ordinary chat/composer appeared. Launched a structured Codex chat from launchAgentInNewTab and inspected the same-turn provisional tab.

No production code was edited. Launch-policy was rebuilt by the electron-vite dev runner (main+preload) before the app started.

Identity

window.api.app.getIdentity():

  • name: Orca: brennanb2025/native-chat-launch-visibility
  • isDev: true
  • devLabel / worktreeName: native-chat-launch-visibility
  • devBranch: brennanb2025/native-chat-launch-visibility
  • devRepoRoot: /Users/brennanbenson/orca/workspaces/orca/native-chat-launch-visibility

Visible evidence

Claude (New tab menu, immediately after click):

  • Tab label Claude Chat, content type agent-session, ptyId: null
  • Ordinary empty native chat: “Start a chat with Claude”
  • Composer placeholder “Send a message…”
  • No “Starting Claude chat…” interstitial
  • Resource Manager: 0 terminal sessions

Claude after send (same tab/session id):

  • User bubble: Reply with the single word pong and nothing else.
  • Assistant: pong (“Worked for 2s”)
  • Tab id stayed structured-agent-session-claude_e9305a11_db62_4912_8a22_3b05005c8fe4
  • Session id stayed claude_e9305a11_db62_4912_8a22_3b05005c8fe4
  • Still 0 terminal sessions

Codex (same-turn launch handle):

  • elapsedMs: 2
  • surface.kind: local-agent-session
  • lifecycle: pending, status: pending
  • Tab Codex Chat, ptyId: null, no starting-label
  • After paint: “Start a chat with Codex” + composer, still no terminal fallback

Backing store evidence

  • Settings: experimentalNativeChat, experimentalStructuredNativeChat, openAgentTabsInChatByDefault all true
  • Runtime capabilities include agent-session.structured.v1
  • Unified tabs: two agent-session rows (claude + codex), empty ptyIdsByTabId
  • Claude launch registry retired to idle after publication (expected)
  • Codex same-turn registry: pending before host publication
  • Renderer error collector: []
  • Playwright console: 0 errors, 3 warnings (React DevTools / Tooltip / React Grab) — none native-chat
  • Electron log: no Structured agent launch failed / Error: matches for this run; one pre-existing MaxListenersExceededWarning on BrowserWindow

Screenshots

Uncropped 1728×994 hidden-renderer CDP captures:

orca-pr-20502-before.png

orca-pr-20502-after.png

orca-pr-20502-codex.png

Local paths (not committed): /tmp/orca-pr-20502-before.png, /tmp/orca-pr-20502-after.png, /tmp/orca-pr-20502-codex.png

Cleanup

Playwright session pr20502 closed. Validation Electron process group for run-electron-vite-dev.mjs -c /tmp/orca-electron-vite-5176.mjs terminated after command-line check. Isolated profile/repo under /tmp/orca-pr-20502-validation removed. Screenshots were uploaded via gh image and were not committed.

Remaining gaps

  • window.api.runtime.call is contextBridge-frozen (writable:false, configurable:false); a renderer wrap could not deterministically delay or fail agentSession.create. Provider attach on this idle machine was fast (~2s Claude; Codex pending only in the same JS turn).
  • NativeChatLaunchRetry / visibility-unknown was not exercised; no injected unknown RPC outcome.
  • Close-before-attach and late-publication suppression were not exercised in this live run.
  • Linux/Windows/SSH/relay/paired-web/mobile were not run.
  • Dev chrome (React Grab toolbar) is visible in screenshots; it is not a product interstitial.

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review complete on 1224a61f49.

  • Same-model review/fix loop returned clean and pushed the meaningful fixes.
  • Fixed the duplicate initial structured-chat prompt race by coordinating launch settlement with the mounted outbox and rechecking the persisted head before dispatch.
  • Fixed the cancellation lifecycle race by retaining cancellation tombstones until any in-flight launch settles and a newer authoritative census can retire them.
  • Updated close-path tests and the launch retry localization catalog.
  • Validation passed: 102 focused tests in the reviewer loop (84 lifecycle-focused tests rerun locally), pnpm run check:code-quality:changed with 0 findings, pnpm tc:web, localization catalog/runtime/extraction/coverage checks, formatting, lint, and git diff --check.
  • Hidden Electron/CDP validation passed with screenshots attached in the preceding comment. Claude and Codex opened directly into native chat with no terminal fallback or startup interstitial.

The current PR matrix is green for typecheck, test shards, package (macOS and Windows), changed E2E build/specs, and wire compatibility. The remaining red static analysis/verify result is from src/shared/agent-status-legacy-adapter.ts:95, an unrelated file from the current base branch that violates the newly enabled anti-slop rule; no PR file is implicated.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No new issues found in this commit. The two findings from the previous review remain open and unchanged by this delta, so approval is withheld.

Reviewed changes

Reviewed the incremental delta 5d8692db1c..f9054bc564 — commit f9054bc564 ("coordinate launch prompt settlement", 13 files).

  • Shared launch-prompt dispatch — new shareStructuredAgentLaunchPromptDispatch / getStructuredAgentLaunchPromptDispatch keyed by sessionId:clientMessageId:fence, so the provisional-chat outbox hook and the launch settlement issue one agentSession.send for the same staged entry; launch entries gain source: 'launch' and a fence-agnostic prefix lookup lets a hook that mounts before the fence joins the in-flight admission.
  • Outbox dispatch extraction — the RPC/disposition path moved to structured-agent-session-outbox-dispatch.ts; readOutbox gained recoverDispatching, and the mounted reader demotes an interrupted dispatching entry to unconfirmed only when no launch dispatch is in flight, so mirror reads no longer mutate state.
  • Cancellation retirement fencing — new structured-agent-session-launch-cancellation.ts arms a tombstone's retirement at authoritativeInventorySequence + 1 when the cancelled launch settles; refreshLocalStructuredSessionTabs allocates the authoritative-inventory sequence before IPC and threads it through StructuredSessionSnapshotApplyOptions, so a reply that began before settlement can no longer retire the fence.
  • Status hook extraction — useStructuredAgentLaunchStatus moved to structured-agent-session-launch-status.ts (re-exported from structured-agent-session-launch.ts) to keep the registry under the max-lines cap.

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Closing readiness pass completed at 1224a61f49.

  • Verdict: pass with residual risk; no proven P0/P1/P2 defect in the PR diff.
  • All changed-test batches passed (395 tests), plus pnpm tc, changed-code quality (0 findings), localization checks, diff checks, and the focused lifecycle suites.
  • The review fixes remain scoped to native structured-chat launch visibility, outbox ownership, cancellation fencing, close-path expectations, and required retry copy.
  • A narrow renderer-reload scenario remains unproven: a persisted cancellation tombstone has no in-memory launch-settlement fence after reload. This is low-impact residual risk with no release block and no additional scope added.
  • The only red remote checks remain static analysis and aggregate verify because the synthetic merge includes the unrelated base-branch finding at src/shared/agent-status-legacy-adapter.ts:95 (Reflect.get).

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found in the two follow-up commits.

Reviewed changes

Reviewed the incremental delta f9054bc564..1224a61f49 — commits b25c9d3b3e ("align unified close ownership") and 1224a61f49 ("correct lifecycle imports and test typing"), 6 files.

  • Fixed a broken lifecycle import — snapshot-apply.ts now imports beginStructuredAgentSessionAuthoritativeInventory from structured-agent-session-launch-cancellation instead of re-importing it from the registry, which never re-exported it.
  • Aligned close tests with the unified owner — useTabGroupWorkspaceModel.focus.test.ts, terminal-workspace-keydown.test.ts, and useTabGroupTabCloseCommands.structured-session.test.ts now assert the close routes through closeUnifiedTab and that closeStructuredAgentSession / session.tabs.close / cancelStructuredAgentLaunch are not called directly.
  • Test typo fix — declared initialProps explicitly in use-structured-agent-session-outbox.test.tsx to give the fence prop a stable number | null type.
  • i18n copy — added the worktree.creation.flow.structured.launch.unknown string and the NativeChatLaunchRetry strings (failed / unknown / retry) to en-runtime-required.json and en.json; no duplicate keys.

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — one reachability note inline.

Reviewed changes

Incremental delta 1224a61f49..c516c10a10 — commit c516c10a10 ("fence restored launch cancellations", 4 files).

  • Restored cancellation fences — restoreCancellationRetirementFences seeds a restored / retireAfterInventory: null entry for every persisted tombstone the process has not already seen, so a tombstone loaded after a reload is no longer retired by the next authoritative census.
  • Host cleanup before retirement — claimStructuredAgentLaunchCancellationCleanups claims each restored tombstone once, refreshLocalStructuredSessionTabs issues a best-effort agentSession.close, and settleStructuredAgentLaunchCancellationCleanup arms the fence at authoritativeInventorySequence + 1 on success (or resets cleanupStarted for retry on failure).
  • Tombstone id export — readStructuredAgentLaunchCancellationTombstoneSessionIds exposes the persisted ids to the fence restore.
  • Test — a restored cancellation is drained across an authoritative then an ordinary inventory: the tombstone is retained until the close settles, exactly one agentSession.close is issued, and it retires on the following census.

The two inline findings from the prior review (structured-agent-session-launch.ts:181-190, structured-agent-session-tab-retirement.ts:66) are unchanged by this delta, so approval remains withheld.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

const result = response.result as { snapshots?: RuntimeMobileSessionTabsResult[] }
const result = isStructuredSessionInventoryResponse(response.result) ? response.result : {}
const snapshots = result.snapshots ?? []
if (options.authoritative === true || result.authoritative === true) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result.authoritative can only be true for a client that advertises SESSION_TABS_AUTHORITATIVE_INVENTORY_RUNTIME_CAPABILITY, and the desktop renderer's fixed capability list omits it (src/main/ipc/runtime.ts:86-93). On native this branch therefore never fires, so a restored tombstone's host cleanup runs only when launch recovery or the retired-epoch repair lane issues an explicit authoritative: true refresh — not on the non-authoritative startup restore. Fail-closed either way (the tombstone keeps suppressing), so this is a deferred host-child close rather than a correctness gap; flagging it in case the intent was to drain restored tombstones at startup.

Technical details
# Restored-cancellation cleanup is unreachable on the desktop startup path

## Affected sites
- `src/renderer/src/runtime/local-structured-session-tabs-sync/inventory-refresh.ts:82` — the `result.authoritative === true` cleanup gate.
- `src/main/ipc/runtime.ts:86-93` (and `:138-145`) — the desktop renderer's `clientCapabilities` list, which omits `SESSION_TABS_AUTHORITATIVE_INVENTORY_RUNTIME_CAPABILITY`.
- `src/main/runtime/rpc/methods/session-tabs-inventory.ts:20-26,58-61` — `projectInventory` strips `authoritative` unless the client advertised the capability.
- `src/renderer/src/runtime/local-structured-session-tabs-sync/inventory-refresh.ts:45-57` — `restoreLocalStructuredSessionTabsOnce` calls `refreshLocalStructuredSessionTabs(expectedGeneration)` with no `options`, so the pre-IPC cleanup also does not run at startup.

## Required outcome
- Confirm whether restored tombstones are intended to be drained on the native startup restore or only by the explicit-authoritative repair/recovery lanes. If the former, the startup restore needs to trigger the cleanup (e.g. pass `authoritative: true`, or invoke `startRestoredLaunchCancellationCleanup` for a labeled census in the `startLocalStructuredSessionTabsSync` path); if the latter, a brief comment noting the desktop label gap would prevent a future reader assuming startup drains them.

## Open questions for the human (optional)
- Is the deferred host-child close on native acceptable, given the tombstone suppresses the row until a later authoritative refresh runs?

@brennanb2025
brennanb2025 merged commit 1457d39 into main Sep 15, 2026
31 of 33 checks passed
ivanvykopal pushed a commit to ivanvykopal/orca that referenced this pull request Sep 20, 2026
…#20502)

* fix(native-chat): bound structured chat launch

* Fix post-merge test hygiene

* Make structured fallback settlement exhaustive

* fix(native-chat): release sessions after root exit

* chore(i18n): remove legacy fallback copy

* test(native-chat): remove terminal fallback census

* docs(native-chat): clarify root-exit lease proof

* chore(native-chat): drop unrelated formatting

* fix native chat launch visibility

* test(native-chat): split message rail windowing coverage

* fix(native-chat): keep transport gating render-pure

* fix(native-chat): coordinate launch prompt settlement

* test(native-chat): align unified close ownership

* fix(native-chat): correct lifecycle imports and test typing

* fix(native-chat): fence restored launch cancellations

* fix(native-chat): fence authoritative cancellation snapshots
salvadorgu7 added a commit to OrbittechIA/orca that referenced this pull request Sep 23, 2026
* feat(work-items): port structured Start to Orca 1.4.203

Work Item Start opens exactly one structured agent session for a linked work item,
delivers its prompt once, and refuses before creating anything when it cannot.

A squash-port of the proven 1.4.201 checkpoint (05ac9d8)
onto the official 1.4.203 base (tag v1.4.203, commit 776e424,
tree df262ab). 1.4.203 ships no Work Item Start surface, so
the whole contract still applies; the substrate it lands on changed in eight files and the
delta was re-decided there instead of replayed.

Contract (unchanged from the checkpoint)
- `agent-session.work-item-start.v1` is a negotiated runtime capability; an older host does
  not advertise it and the route refuses instead of half-opening a session.
- `launchOrigin: 'work-item-start'` admits one launch to the structured route and
  `launchAuthority` records who admitted it; both are host-derived, never wire-accepted.
- Strict (`submit-after-ready`) launches declare no `legacyFallback`: a definitive refusal
  settles as `failed` and no terminal writer opens behind it.
- The strict preflight runs BEFORE `git worktree add` and before `createFolderWorkspace`.
- A structured session is created on the runtime that owns its workspace; an ambiguous
  owner fails closed.
- `worktree ps` projects the structured `sessionId` and provider handle for Core adoption.
- Packaged builds embed version/commit/tree/buildId; `status.get` publishes it and
  `runtime.buildAttestation` hashes the running executable on demand.

Re-decided on 1.4.203
- Unknown send refusal: 1.4.203 (stablyai#20133) retains the operation id and replays the same
  envelope through the host's durable send plan, and names parking the entry as the
  head-of-queue wedge stablyai#19863 removed. The checkpoint's `unconfirmed`/-1 parking hunk in
  `requeueStructuredAgentSessionSendRefusal` is dropped; the official function is kept
  verbatim. The Start path still reports `deliveryUnknown` from the refusal code so no
  second writer is opened, and its test now asserts the official same-envelope replay
  (`retryUnknown` is compatibility-only on 1.4.203 and is no longer sent).
- RPC params type parity (new in 1.4.203) required `runtime.buildAttestation` in the
  generated catalog; regenerated with the official script.
- Mobile Tasks parity hashes recomputed over the merged 1.4.203 source.
- Localization catalog: the Start strings now have `en.json` entries, which the 1.4.201
  checkpoint lacked; `verify:localization-*` passes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Re-decided on 1.4.209 (port of this commit onto stablyai/orca v1.4.209)
- 1.4.209 (stablyai#20502) removed the renderer terminal fallback and made structured launches open
  a provisional chat tab that owns its own retry. Strict Start no longer needs to withhold a
  `legacyFallback`: none exists. Strict worktree/direct/folder Starts now await the launch
  settlement and prompt delivery on top of the provisional handle; non-strict launches keep
  the upstream fire-and-forget behaviour.
- The pending-creation recovery lane (`worktree-creation-structured-recovery.ts`) that
  1.4.209 deleted is kept for strict Start only; its retry re-enters the launch registry,
  which restarts an unknown launch under the same intent instead of minting another.
- The shared launch-prompt dispatch (stablyai#20502) returns a boolean; `deliveryUnknown` is carried
  beside it, and read back from the outbox entry when the mounted chat ran the shared send.
- stablyai#20944 stopped routing on terminal arguments (`requiresTuiLaunchCommand`); the strict
  preflight and planners follow it, so they predict the same route as the post-create plan.
- `launchOrigin` and the owning runtime target now survive the intent retry/restore split
  and the reload-persisted launch record (the create fingerprint covers `launchOrigin`).
- Mobile: the createSupport probe uses the typed RPC operation; the new-worktree route
  builder carries the combined setup + session warning.

* fix(work-items): scope every status event per subscriber; certify against 1.4.203

Two blockers the independent review found on 4e098c4, each with a regression that fails
on the previous code.

Status feed access scope (P1)
- `subscribe(includeSession)` filtered only the opening snapshot; `broadcast` looked for a
  top-level `sessionId`, which a `status` event never has (it names its session at
  `event.session.sessionId`). A paired runtime denied a Work Item Start session therefore
  received every later publish and ownership revoke for it.
- Every session-bearing event now passes the subscriber's filter (`scopeStatusEvent`):
  `status` is withheld, `snapshot` is filtered, `end` always arrives. The filter is dropped
  together with its subscriber on direct `unsubscribe`, on the disposer, and on emit failure;
  a re-subscribe under the same id never inherits an earlier filter.
- Regressions: the feed itself (denied subscriber gets an empty snapshot and zero status
  events across publish and revoke, the allowed session still flows, `end` exactly once,
  filter cleanup on both removal paths) and the RPC boundary through
  `agentSession.subscribeStatus` with `canAccessWorkItemStartStructuredSession`, for a
  paired runtime and a mobile client on their own connections while the owner's connection
  receives both events. The RPC fixture now shares one real feed per test over a journal the
  test can advance, and exposes the live reply list of a streaming call.

Certification baseline
- `WORK_ITEM_START_E2E_BASELINE_VERSION` was still `1.4.201`, so this candidate's own
  evidence validator would have rejected a correct 1.4.203 paired run. It is `1.4.203` now,
  fixed rather than derived from the manifest: the manifest says what was packaged, the
  constant says what the owner certifies, and a self-consistent 1.4.201 pair must still be
  refused. Fixtures bind to the official v1.4.203 base (776e424 / df262ab, buildId
  7bbdd813f784, as `build-provenance.mjs` derives it). Matched 1.4.199 and 1.4.201 pairs are
  rejected on both sides even when they match their own manifest; a 1.4.201 side behind a
  1.4.203 side is rejected by baseline, identity, and version mismatch. The Windows-client,
  distinct-artifact, commit/tree/buildId, capability, single-executor, one-prompt, and
  zero-terminal checks are unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(work-items): close the remaining 4e098c4 review findings (A–H)

Each finding has a regression that fails on 8b87c61 and passes here; each was proved by
reverting only that finding's implementation.

A  Strict quick-create keeps `launchOrigin: 'work-item-start'` and declares no
   `legacyFallback`; a definitive refusal settles as `structured-refused` (workspace kept,
   no writer, no terminal, Retry allowed).
B  Every launch exposes `recovery = { intent, clientMessageId }`. The pending creation
   persists it on an unknown launch/delivery and Retry re-enters with `recover`: the exact
   intent (same session id and create envelope, replayed through `reconcileUnknownLaunch`)
   and the staged prompt looked up by operation id. A lost staged entry is unknown, never
   success. Prompt staging moved to `structured-agent-session-launch-staging.ts`.
C  A definitively refused launch prompt is deleted from the outbox instead of requeued
   under a fresh id; only unknown / not-yet-admitted refusals retain and replay the same id.
D  Mobile admission is tri-state (`terminal` / `structured` / `refused` / `unknown`) with a
   10 s status timeout; both Start entry points stop BEFORE `worktree.create` on refused or
   unknown and never fall back to the terminal.
E  The mobile send envelope is persisted in the operation journal before dispatch;
   `agent_session_operation_unknown` on `agentSession.send` is unknown; unknown outcomes
   replay the same operation id a bounded number of times and stay persisted until settled.
F  `prepareStructuredAgentSessionCreateForWorktree` passes `expectedWorktreeTarget` to the
   resolver, where the full record comparison (path, instance, identity, host, creator) runs.
G  `worktree ps` merges the host's projection with the hook-store row that shares its pane
   key: one row per session, with `sessionId` and provider handle.
H  Build provenance is fail-closed: any working-tree change yields no identity, an
   `ORCA_BUILD_COMMIT/TREE` that contradicts (or cannot be confirmed by) the repository
   throws, the packaged `app.asar` bundle is verified in `afterPack`, the candidate manifest
   is written after every artifact, `build:*` run `verify:build-provenance`, and the paired
   spec requires `ORCA_CANDIDATE_MANIFEST`. `ORCA_BUILD_UNCERTIFIED=1` is the only escape and
   embeds nothing certifiable.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Re-decided on 1.4.209
- B lands on the 1.4.209 launch registry: a recovery re-enters the registered state when it
  still exists (a restart that finds, rather than skips, the staged operation) and otherwise
  rebuilds it from the persisted intent through `reconcileUnknownLaunch`. Publication is
  verified with the authoritative tab snapshot (stablyai#20502 removed the renderer-tab shortcut).
- A: 1.4.209 has no terminal fallback, so a refused strict create simply settles `failed`
  and maps to `structured-refused`.
- `recovery` is optional on hand-built settlements; the settle loop always sets it.
- D: the mobile admission read goes through a typed `status.get` RpcOperation, as the
  1.4.209 raw-request-port ratchet requires.

* fix(build): do not count electron-vite's transient config bundle as a dirty build input

The first fail-closed package refused itself: electron-vite bundles its config into
`electron.vite.config.<timestamp>.mjs` in the working directory while the config loads —
which is when `readBuildProvenanceLiteral` runs — and deletes it afterwards. It is derived
from the tracked config, not an input. Ignored in git and excluded by exact pattern in
`dirtyBuildInputs`; a modified config or any other stray file still yields no identity.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(work-items): strict quick-create never reads missing delivery evidence as success

Re-review counterexample on e7567c6: a structured settlement that resolves with a session
but carries no prompt-delivery state completed the strict creation, because
`!delivery || delivery.delivered` took the success branch. Missing evidence is not evidence
of delivery. In strict mode an absent delivery result now settles as `promptDeliveryUnknown`
with the launch's `recovery`, so Retry reconciles the same session and operation; nothing
completes, nothing mints a second session, and no terminal fallback exists on this path.

Regression: the counterexample itself, asserting durable unknown with the recovery intent,
one launch, and only the structured chat's own activation (never a startup terminal).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(build): bind provenance to the real embed site and keep the transient bundle out by timing

Two re-review holes in the provenance gate.

P1 — a name-based whitelist admitted ANY `electron.vite.config.<digits>.mjs` as clean, so an
unverifiable file could hide under a numeric transient name. No name is exempt now: the build
wrapper (`run-electron-vite-build.mjs`) reads the identity BEFORE electron-vite writes its
transient config bundle and hands it to the config through
`ORCA_ELECTRON_VITE_BUILD_PROVENANCE`; the config accepts it only if its commit and tree are
what git reports at load time (malformed, contradicted or unconfirmable hand-overs throw), and
falls back to the strict read without it. The gitignore entry is gone, so a lookalike is a
dirty input like any other and the identity is null. Regression: the exact counterexample
(`?? electron.vite.config.123456789.mjs` → dirty → null) beside the genuine path (hand-over
accepted while the transient is on disk).

P2 — the bundle verifier accepted any bundle in which the expected strings occurred somewhere,
so `value:null` plus a comment passed. The runtime read site now carries an exact sentinel
(`site:'orca:build-provenance:embed', value:<substituted>`, checked at runtime so no minifier
drops it) and the verifier parses the value right after that sentinel: exactly one site, a
well-formed identity, equal to the expected one; null, an unreplaced identifier, a malformed
object, two sites, or another build's identity all refuse, whatever else the bundle contains.
Regressions: null + expected strings in a comment; forged identity at the site + expected
strings elsewhere; duplicate sites; unreplaced identifier; sentinel byte-equal on both sides.

Packaging hooks: `artifactBuildCompleted` validates the AppImage synchronously again and
returns the manifest write; `ORCA_BUILD_UNCERTIFIED` is read per call. The electron-builder
hook tests broken since b52da80 are fixed (layout tests run as throwaway packages) and two
deterministic refusals added: a manifest under a contradicted override, and an afterPack
whose packaged bundle embeds null.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(work-items): settle mobile M1 only on ledger settlement, hold the workspace lifecycle through attach, full sha for mac local builds

Final source round of the independent review (R1–R3), each with a regression that fails on
the previous implementation.

R1 — mobile durable journal. `deliverWorkItemStartPrompt` cleared the persisted M1 for every
non-unknown mutation result, so a lost reply followed by an `agent_session_operation_capacity`
("try later") replay deleted M1 and reported undelivered while the host may hold it. The
canonical `agentSessionRefusalOperationState` now decides: `unknown` and `pending-admission`
(and any code the ledger does not classify) replay the SAME operation id a bounded number of
times and, still undecided, stay persisted and are reported `unconfirmed` with the exact
envelope; only `settled-rejected` (and a pre-handler failure that never reached the host)
clears. M2 is never minted.

R2 — worktree target TOCTOU. The full expected-target comparison ran in the resolver, then
launch preparation awaited, then `attach` ran with nothing keeping the record what it was.
The runtime now has a workspace lifecycle hold (a second instance of the existing
shared/exclusive `WorktreeTerminalMutationLock`, its own key space): a structured create
holds the shared side from authoritative resolution through attach — scoped creates before
resolving, generic ones once resolved — and `removeManagedWorktree` (every removal branch,
therefore every replacement) takes the exclusive side around the whole removal, refusing as
`worktree_lifecycle_busy` past a bounded wait rather than being granted later. Belt to that
brace: `commit` re-resolves the exact full target (path, instance, identity, host, creator)
under the hold immediately before attach and refuses on any difference. Deterministic
regressions with the real `OrcaRuntimeService`: A attaches once; A replaced by B during a
paused preparation → attach never called; a removal waits for the in-flight create; a create
arriving during a removal sees B and is refused; a removal past its deadline is busy. The
terminal-only lock is not used: removal never took it.

R3 — mac local build. `getLocalBuildIdentity` passed `git rev-parse --short=12` as
`ORCA_BUILD_COMMIT`, which the provenance gate compares byte-for-byte with `rev-parse HEAD`,
so the local mac build refused itself. It now hands over the full sha (and refuses anything
else); only `createLocalBuildVersion` abbreviates it for the human version suffix. The
verifier is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(work-items): strict quick-create fails closed on a thrown launch or a generic failed settlement

Last material blocker of the independent review on 0fd77da: with a strict (`submit-after-ready`)
non-empty prompt, `launchStructuredWorktreeSession` still answered `accepted: true` when the
launch threw during launch-intent creation (an ambiguous runtime owner) or when the settlement
was a generic `{ kind: 'failed' }`; the caller then completed and removed the pending creation
with no structured session and no delivery proof.

Both now settle as `failure: 'structured-launch'` in strict mode — the same discipline as a
host refusal: the workspace exists with no writer, no legacy terminal opens, the creation is
marked as a retryable error instead of completed, and nothing mints a second session. The
non-strict path is unchanged.

Regressions (the strict suite moved to `worktree-creation-structured-session-strict.test.ts`
to stay under max-lines): a thrown `worktree_runtime_owner_ambiguous` from the launch's sync
prologue, and a generic failed settlement, each asserting `accepted: false`,
`failure: 'structured-launch'`, no fallback claim, no startup terminal, no after-wake terminal,
no trust preflight; and, at the flow level, that such a result is kept on the retry surface
and never completes or removes the pending creation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Re-decided on 1.4.209: the strict lane also fails closed when the provisional chat surface
could not open (reveal failed) and the creation was not cancelled; the strict suite runs the
real plan, settle loop and provisional tab, and asserts the only activation is the chat's own.

* fix(work-items): strict quick-create fails closed when the launcher itself throws

The same fail-open one level above e1d9c5e's fix: the caller of
`launchStructuredWorktreeSession` in `worktree-creation-flow-execute.ts` caught any exception
that escaped the launcher, logged it, and fell through to `completeWorktreeCreation` with
`structuredLaunchAccepted` still true — no session, no delivery proof, pending creation removed.

For a strict (`submit-after-ready`) Work Item Start an escaped exception now marks the
existing workspace with the retryable `structured-launch` error and returns before completion:
no legacy terminal, no pending removal, no second session. The non-strict path keeps its
historical behaviour (treated as a failed-but-accepted launch).

Regression at the caller level: `launchStructuredWorktreeSession` itself is mocked to reject
(not the plan inside it); `completeWorktreeCreation` never runs, the pending entry is a
retryable `structured-launch` error bound to the worktree, and no startup / activation
terminal is seeded. Fails on e1d9c5e's file, passes here; the helper-level throw and generic
failed-settlement tests are kept.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* chore(work-items): clear the changed-code quality gate (83 type-assertion findings) without behaviour change

`check:code-quality:changed` against the official v1.4.203 base reported 83
`typescript/consistent-type-assertions` findings across the port (9 production sites, 66 in
tests). It now passes with 0 findings.

Production: checked narrowing instead of casts. A tiny shared guard (`isUnknownRecord`) narrows
untyped input in the build-provenance parser, the launch-authority parser and the mobile
admission reader; the attestation stream is typed as an `AsyncIterable<Buffer | string>`; the
pre-create preflight declares the detected-agents promise type the routing already requires;
the session-tab projection spreads the base projection over the change instead of asserting;
the IPC dispatch response needed no assertion at all. One production cast is unavoidable and
carries the repository's line-specific SAFETY rationale: the `globalThis.ORCA_BUILD_PROVENANCE`
read, which the packager's define substitutes textually and which the cli/mobile tsconfigs
(no `build-constants.d.ts`) also compile. The inherited web settings API `satisfies … as`
carries one too (the web preload implements a checked subset).

Tests: `vi.hoisted` state is declared with types instead of asserted; RPC/mock params are read
through `isUnknownRecord`; `JSON.parse` results are assigned to typed bindings; DOM elements are
narrowed with `instanceof`; worktree-target fixtures are typed with `Pick<Worktree, …>`.
Fixtures that deliberately pin a subset of a large interface (runtime stores, structured hosts,
protected resolvers, GlobalSettings) keep a single SAFETY-annotated cast each, usually behind a
small helper so one rationale covers the repeats. No rule is disabled and no allowlist grows.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* chore(work-items): recompute Mobile Tasks parity hashes over the merged source

The refactor-parity guard pins hashes of the Mobile Tasks source; the port
onto v1.4.205 merges the Work Item Start changes into upstream's RpcOperation
migration of the same files, so the screen-hook, statement, declaration and
semantics fingerprints (and the semantics line count, 3452 -> 3458) are
recomputed over the merged tree, exactly as the 1.4.203 port did. Render tree,
style and diff-hook fingerprints are unchanged. No behaviour change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Re-decided on 1.4.209: recomputed over the Work Item Start changes merged into the 1.4.209
Mobile Tasks source (semantics line count 3274 -> 3280); render tree, style and diff-hook
fingerprints unchanged.

* fix(work-items): scope the workspace lifecycle hold per execution host on 1.4.205

Upstream v1.4.205 runs same-id workspace removals on different hosts
concurrently (a local row and an `ssh:*` row under the migrated spelling),
and its integration suites build partial runtime fakes. The reviewed R2 hold
(0fd77da) keyed the lifecycle lock by worktree id alone, so on the 1.4.205
base the remote removal waited on the local one until `worktree_lifecycle_busy`
and three upstream integration suites failed with
`holdWorktreeLifecycle is not a function`.

- `runtimeWorktreeLifecycleKey` keys the hold per execution host; no host is
  the local machine, which is what an unscoped hold always meant.
- The create path holds the host the admitted target names (or the resolved
  location's host); the removal path holds the host it routes to, resolved
  from the explicit host or the owner repo before the hold is taken.
- The removal hold helper receives the scope it needs instead of a bare id.
- Upstream's three integration fixtures declare the hold, as the reviewed
  fixtures already did.
- Regression: a remote exclusive hold neither waits for nor blocks the local
  create, and the local scope is still the one the create held.

Verified on this tree: `pnpm tc`; the previously failing suites plus their
neighbours (orca-runtime, orca-runtime-tests, structured-session
integration/replay/claude, create-lifecycle, precommit refusal, worker seed
options, worktree ps: 8 files, 1320 tests) pass; oxlint on changed files;
`check:code-quality:changed` against 11aba8b: 0 findings; `git diff --check`.
The one remaining failure in the targeted desktop run,
`hourly-build-version.test.mjs` "stays on the already-shipped hourly base",
fails identically on pristine v1.4.205 (upstream's own stale expectation) and
is not part of this delta.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix: repair v1.4.205 port CI and Windows provenance paths

Re-decided on 1.4.209: the legacy adapter freeze hunk is dropped — 1.4.209 (stablyai#20872) already
reads own-property descriptors, which is the lint fix this worked around; the added freeze
assertions are kept and pass. The mobile admission read keeps its typed operation.

* test: check Windows target watcher in architecture packaging guard

* test: use retrying cleanup for Windows package fixtures

* fix(work-items): preserve scoped Start authority at production callers

* fix(work-items): retain strict delivery across incomplete settings refreshes

* fix(work-items): preserve legacy settings wire behavior outside strict Start

* fix(codex): preserve yolo semantics in structured app-server

Re-decided on 1.4.209: stablyai#20944 deleted `codex-structured-app-server-args.ts` and stopped
passing configured CLI arguments to app-server. The Yolo posture now travels as the
thread's explicit `approvalPolicy: never` / `sandbox: danger-full-access` on thread/start
and thread/resume, derived from the bypass flag wherever it sits in the arguments, and
conflicting `-c approval_policy` / `--sandbox` tokens never reach app-server. So the patch's
`-c` overrides are dropped with the deleted file, and its guarantee (Yolo is final regardless
of argument order) is pinned against `codexStructuredPermissionPolicyForSettings`.

* fix(mobile): restore the 1.4.209 web client context seam

The 1.4.205 CI repair moved the RPC client context into a native-only
rpc-client-context.ts and pointed host-client-hooks at it. 1.4.209 ships a
client-context.web.tsx with its own context, so on the web page every
useHostClient read a context the web provider never fills: 27 bridge-parity
goldens failed (base: 793/793). Back to upstream's import layout.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(work-items): adapt direct and composer Start to 1.4.209

Split the strict submit-after-ready half of launchWorkItemDirect into
launch-work-item-direct-strict-start.ts and the mobile composer Start into
composer-work-item-start.ts, with unchanged behaviour. The strict pre-create
preflight keeps reading the settings snapshot taken at launch rather than
re-reading the store after the awaits.

1.4.209 keeps structured launches with configured agent arguments and refuses
only a command override, so the unavailable copy now says "custom launch
command" and the folder composer test pins arguments as non-blocking. Test
fixtures follow 1.4.209 APIs (retired-name registry, agentLaunchSupported,
repoName, provisional-tab store fields, cancelStructuredAgentLaunch).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(mobile): read a missing support method as a verdict

The Start's probeFailed marks an unanswered createSupport probe so a strict
Start reports unconfirmed instead of refused. It also swallowed
method_not_found, which is a host answering that it has no structured route:
structured-launch-support-refused diverged from its golden. Definitive codes
(isDefinitiveAgentSessionCreateRefusal) are plain unsupported again; other
ok:false replies stay probeFailed, so the two reply-matrix goldens whose
generic refusal partitions now settle probeFailed are re-recorded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(work-items): pin the closures and capabilities Start changes on 1.4.209

- The structured RPC module imported the native-chat write floor from
  mobile-native-chat-send, dragging terminal-write families into the Tasks
  and Files pages. The constant moves to a leaf module.
- The layout's composer now opens the Start's structured session, so C1
  (and C5, which inlines it) pins agentSession.structured-create/launch with
  their replay verdicts; C2/C3/C5 censuses move by +2 families, +9 goldens.
- The session route closure is 4329 modules (+6 Start modules).
- The desktop renderer advertises the structured capabilities to paired
  remotes too, which the port added so a paired Start is not refused; the
  divergence test records that decision.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(daemon): reap terminal descendants during shutdown on 1.4.209

Port of upstream 632ae13 ("fix(daemon): reap terminal descendants
during shutdown (stablyai#22232)") onto the v1.4.209 tree. The release's pr.yml
already runs config/scripts/run-daemon-shutdown-descendants-docker.mjs,
but the harness was never shipped and the leak it guards was live: a
setsid'd, SIGTERM-ignoring child of a live PTY shell survived daemon
exit.

Host dispose now captures each live session's descendants, escalates
them with identity-checked SIGTERM/SIGKILL and waits for verification
(bounded to 2.5s inside the daemon's 5s budget) before force-killing the
shell and releasing its PTY. In-flight teardowns are upgraded to
immediate and awaited instead of being disposed a second time.

Carried as-is: the teardown/verification runtime, the per-pid partial
read verifier, the process-table parser split, tests, and the Docker
oracle plus its PR-scope classification. Not carried: the
daemon-cgroup-scope busctl hunk (the module was removed in 1.4.209) and
the pr.yml step (already present in the release).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(work-items): scope Start session reach to its owner on 1.4.209

- hold, reveal, restartResumable/Resume/Continue authorize each concrete
  sessionId: a Work Item Start session is reachable by its owner alone,
  even with global structured chat off; cross-owner access is refused.
- Add the Start-only client capability agent-session.work-item-start.client.v1.
  Paired desktop and web clients advertise it instead of the generic
  structured-session capability, so Start no longer grants reach into
  ordinary structured chats. Hosts still accept the generic capability.
- Ordinary structured tab restore stays behind the v1.4.209 setting; with
  it off, a Start caller restores only its own visible Start tabs, latched
  per record so owners never spend each other's sweep.
- worktree.ps drops sessionId and provider thread ids from structured rows
  the caller cannot reach.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(work-items): close the remaining 1.4.209 Start review findings

Settings wire: settings.update accepts the optional workItemStartPromptDelivery
('draft' | 'submit-after-ready') that paired web clients already send; legacy
payloads are unchanged and an unknown value is refused.

Prompt refusal: only a strict Work Item Start (submit-after-ready) settles a
definitively refused prompt, and it names one fresh retry id. Every other
structured launch keeps the v1.4.209 requeue.

Desktop strict retry: a recovery whose staged prompt is gone restages it under
the SAME operation id, so the host ledger replays an accepted send instead of
delivering twice. A strict refusal leaves one retry id and keeps Retry enabled.
A refused create retries with its never-dispatched prompt, and a thrown
unadmitted refusal is definitive rather than unknown.

Quick create: the strict preflight checks the host the workspace runs on
(ephemeral VM or run target), not the source repo's, before any worktree exists.

Mobile: a strict Start refuses SSH, runtime-hosted and WSL repos before
worktree.create. A durable attempt journal, written after worktree.create and
before the session request, makes every retry re-enter the same session id and
create operation. The composer returns the created workspace with a warning
instead of an error that left Create armed for a second workspace; the workspace
screen offers Retry Start; the Tasks create path has a synchronous in-flight
guard. Support probes are bounded to 10 s on mobile and desktop.

Build provenance: the hourly, daily and adhoc mac version scripts emit the full
commit sha (display names still slice seven), and provenance refuses any
ORCA_BUILD_COMMIT/ORCA_BUILD_TREE that is not a full 40-hex id.

Certification evidence: the 1.4.203 pin is gone and evidence binds to the
candidate manifest's version, commit, tree and build id, with an optional
ORCA_CANDIDATE_EXPECTED_COMMIT pin. The runtime attestation adds optional
appContent (the app.asar hash) and attestationId, and packaging records the
asar hash per artifact in the manifest. The paired spec runs its contract steps
in PR CI against a new inert Codex app-server fixture; candidate evidence runs
only with ORCA_WORK_ITEM_START_CERTIFICATION=1, where nothing may skip.

Also: the terminal-paste path is fenced from strict Start and pinned by a test,
mobile/pnpm-lock.yaml is restored to its v1.4.209 bytes (a format-only rewrite
with no package.json change), and three parity pins plus six mobile RPC goldens
are refreshed for the intended probe-timeout and create-guard changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(work-items): close the 1.4.209 Start CI findings outside the RPC pin

- runtime-executable-attestation: read the original-fs shape and
  process.resourcesPath through typed property access, not Reflect.get.
- write-candidate-manifest: rpm names x64 `x86_64`, which the bare `x86`
  match read as ia32; the package job refused the manifest for want of an
  ia32 app-content sidecar. Covered by a linux x64 binding test.
- Session route closure 4329 -> 4333: the four Start modules added in
  8ecea75 (attempt journal, prompt delivery, retry banner, execution host).
- Web runtime capability tests assert the Start-scoped structured capability
  is advertised and the generic structured capabilities are not.
- The e2e workflow contract finds its Windows launcher by listing rather than
  naming a batch-shim literal; it only reads the file, never spawns it.
- en.json gains the retryable prompt-refusal copy the recovery path reads.

The RPC recording pin stays red: the 9 structured goldens record the
support probe's new 10s budget, which the pinned baseline tree does not
send. Only a repin plus full re-record satisfies both replays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(rpc): repin recordings to final 1.4.209 Start candidate

* fix(work-items): refuse unsupported strict Start repos before worktree.create

A strict Work Item Start now asks the host for the source repo's
structured-session verdict (agentSession.createSupport with a repo
selector) after admission and before any setup or worktree.create.
A remote/wsl/agent refusal, an older host that rejects the repo params,
or an unanswered probe stops the Start with nothing created. Draft mode
never sends the probe.

The host derives the would-be location from the repo row, the project
runtime's configured WSL distro and a WSL UNC path, so a C:\ repo whose
project runs in WSL is refused before a workspace exists.

Also binds mac zips to the macos app-content sidecar in the candidate
manifest, refusing zips whose platform is ambiguous.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(mobile): expect the pre-create repo probe in the Start submit hook

ef2ff81 made a strict Work Item Start ask agentSession.createSupport with a
repo selector before worktree.create, and updated the Tasks-side tests, but
not the new-workspace submit hook test. Its expected call sequence still
omitted the repo probe, failing Mobile Checks deterministically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(work-items): deliver strict quick-create Start as submit-after-ready

Quick-create derived its structured prompt delivery as `auto-submit` whenever
no draft prompt existed, which is always the case for a strict Work Item
Start. The launch carried `launchOrigin: work-item-start` but not
`submit-after-ready`, so the strict send guard never applied and a refused
send was requeued into the legacy chat outbox behind the Start.

The route resolver now owns the delivery decision: a strict Start always
delivers `submit-after-ready`, legacy quick-create keeps `draft` /
`auto-submit`. The worktree-creation launch boundary also pins strict delivery
from `workItemStartPromptDelivery`, so a stale request cannot reach the legacy
requeue. A strict Start with an empty prompt is refused before any workspace
exists instead of completing or reconciling on an empty send. Direct and
folder Start are unchanged; both already sent `submit-after-ready`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(work-items): report managed Claude account ineligibility as agent, not wsl

`agentSession.createSupport` answered a managed Claude account mismatch with
reason `wsl`, even for a native repo, so mobile told the user the repository
runs inside WSL and orchestration downgraded with "this workspace runs under
WSL". The verdict now uses the existing `agent` reason, which every client
already maps, so no schema or wire change is needed. Mobile's `agent` copy,
before and after create, now names the selected agent or its active account.
Real WSL workspace / project-runtime refusals keep reason `wsl`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(work-items): add strict Start empty-prompt localization

* fix(work-items): single-flight strict Start and Retry per workspace

A mobile Start and a Retry Start (or two Retry taps) for the same workspace
could overlap. The attempt and send journals only make a sequential
re-entry safe: the first flow cleared the send record when it settled, the
overlapping one minted a new send operation, and the prompt reached the
session twice.

Start and Retry now share one module-level in-flight promise per workspace.
Every overlapping caller awaits that same promise, so there is one create,
one session and one prompt. The slot is released only by the promise that
holds it, on success or on a throw, so a later Retry after a settled
outcome runs normally. Retry reads the attempt journal inside the flight,
so a stale banner tap after a settled Start finds nothing and sends
nothing. The banner also guards double taps with a ref.

A phone paired with mobile scope is now told that the refusal is about its
pairing scope instead of being blamed on an older host. Pairing authority
is unchanged.

The pre-create route decision moves unchanged to work-item-start-route.ts
(plus its tests) to keep both files under max-lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(work-items): pin strict Start mobile route closure

* chore(work-items): preserve reviewed mobile Start sources

---------

Co-authored-by: salvadorgu7 <206022846+salvadorgu7@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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.

Structured Claude chat session can never be closed: eviction fails at stop-provider-child

1 participant