Skip to content

fix(agent-session): refuse a pre-commit structured create with an envelope - #18697

Merged
brennanb2025 merged 8 commits into
mainfrom
brennanb2025/bug2-refusal-envelope
Sep 5, 2026
Merged

brennanb2025 merged 8 commits into
mainfrom
brennanb2025/bug2-refusal-envelope

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 7 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​509 $\color{#cf222e}{\Huge{\mathbf{−}}}$​14 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​495
Prod 5 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​255 $\color{#cf222e}{\Huge{\mathbf{−}}}$​69 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​186

ELI5

When Orca can't start a Codex chat in a workspace, the host used to fail in a way the app couldn't recognise as a refusal — it looked identical to "the connection broke."

So the app did nothing. You clicked New tab → Codex and got neither a chat nor a terminal. No error, no toast, nothing.

Now the host says "no, and here's the reason code" in a form the app understands, so it falls back and gives you a terminal.

Before / after

Before After
Refused Codex launch Nothing happens — no chat, no terminal, no message Falls back to a working Codex terminal
What the user sees Silent failure — zero errors, zero breadcrumbs A terminal opens
Successful launch Structured chat Structured chat, unchanged
Genuinely uncertain outcome (timeout, dropped connection) — Still treated as unknown; no duplicate session created

Visual proof

Corrected — this PR does change user-visible behaviour, and an earlier draft of this description wrongly said N/A. Captured as a runtime differential on an isolated dev instance:

  • Before (fix ablated): New tab → Codex leaves the user with nothing at steady state (~18s) — only the pre-existing terminal, no new tab, no toast. A silent failure with zero console errors and zero main-process breadcrumbs.
  • After (PR head): the same click falls back to a live Codex terminal, with the breadcrumb [agent-session] create refused before it committed anything.
  • No regression: with the fault removed, a real structured session is still created — new chat tab, no new PTY.

The failure was triggered without editing product code — a regular file placed where the session store expects a directory makes the store's mkdir fail. That is a genuine code-less pre-commit failure, exactly the class this PR is about.

What changed

The whole side-effect-free pre-attach span of agentSession.create now returns a refusal envelope carrying a code, instead of throwing. Converting the span rather than a list of throw sites matters: alongside the code-carrying throws there is a code-less class (unknown worktree, store/host-install failure) that per-site conversion misses entirely.

Also ships src/shared/agent-session-definitive-refusal.ts — the definitive-vs-unknown allowlist, exported for the follow-up fix and deliberately not consumed here.

Deliberately left outside the span: the capability gate (it must keep hiding the surface from capability-less clients, per its own contract) and the fence throw (a programming-error passthrough). Both verified correct in review.

Cross-version

Safe. The envelope shape has been on the create wire since the capability shipped — no new opcode, no new field. Shipped mobile lacks the capability entirely; desktop is same-build. Anything not on the allowlist stays unknown, so no client creates a duplicate.

Evidence

Ablation red for the right reason: reverting only the changed file fails exactly the 4 refusal-conversion tests, while all 5 boundary tests stay green. 15 new tests; independent review returned CLEAN.

…elope

The create route refused by throwing, which reaches a client as a generic
transport error indistinguishable from a lost answer — so desktop parked the
launch as visibility-unknown with no chat and no terminal. Convert the whole
pre-commit span, everything before `attach`, into a refusal envelope carrying a
code, and name the definitive-refusal allowlist the fallback decision needs.
@coderabbitai

coderabbitai Bot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Team

Run ID: 932a6a83-4da2-46d4-b2d8-65edd2d1468a

📥 Commits

Reviewing files that changed from the base of the PR and between 8491e84 and a5d6f96.

📒 Files selected for processing (3)
  • mobile/src/session/mobile-structured-agent-session-launch.test.ts
  • mobile/src/session/mobile-structured-agent-session-launch.ts
  • mobile/src/session/use-mobile-session-terminal-create-actions.test.ts

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


📝 Walkthrough

Walkthrough

The change adds definitive refusal classification for agent-session creation. It converts pre-attachment preparation failures into refusal envelopes with wire codes. The agentSession.create route prepares attachment data before attachment and returns prepared refusals directly. Failures during attachment or tab publication remain unknown outcomes. Renderer launch handling preserves this distinction. New tests cover both boundaries and refusal classification.

Merge Risk: ⚪ Minimal · up to d8794

Pre-attach structured-session failures now return a refusal that can safely fall back to a Codex terminal, while ambiguous outcomes continue to avoid duplicate-session fallback. The covered behavior is ready to merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.32% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 19 functions across 12 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: structured agent-session creation now returns a refusal envelope for pre-commit failures.
Description check ✅ Passed The description explains the problem, implementation, user-visible behavior, cross-version impact, visual evidence, and test evidence. It does not include the required Linked Issue entry or the templa…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

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.

@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

  • structured-agent-session-precommit-refusal.ts — new resolveUncommittedStructuredCreate() wrapper that turns any pre-attach throw into a { ok: false, refusal } envelope; code-carrying throws keep their code, code-less ones become structured_agent_session_unsupported with the cause preserved and logged.
  • structured-agent-session.ts — agentSession.create now runs the fingerprint check through ensureHostInstalled/requireHost inside the wrapper, leaving attach and tab publication outside it.
  • agent-session-definitive-refusal.ts — new isDefinitiveAgentSessionCreateRefusal() allowlist (structured_agent_session_unsupported + RPC method_not_found), exported but deliberately unconsumed until bug 1 wires it in.

Verified the refactor is behavior-preserving (both branches still call ensureHostInstalled before requireHost, in the same order; the post-attach agent_session_operation_unknown path is untouched), that structured_agent_session_unsupported is absent from AGENT_SESSION_RPC_ERROR_CODES so the old thrown path genuinely flattened to runtime_error, and that the boundary tests assert attach is uncalled on refusal and stays unknown on attach/publish failure. All 15 new tests and the 24 existing structured-agent-session.test.ts tests pass locally.

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

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Visual proof — refused structured launch now falls back to a terminal

Driven through the real UI on an isolated dev instance (own CDP port, own user-data dir, own scratch repo), Playwright over CDP. Tested at PR head 730e4c9ab0e72183ef6d3f3d2eaf9fbca8aa98b3 (git rev-parse HEAD confirmed).

How the pre-commit failure was triggered without touching product code: placed a regular file at <userData>/agent-sessions, the path hardenAgentSessionStorePermissions calls mkdir on. That throws EEXIST: file already exists, mkdir '…/agent-sessions' inside ensureStructuredAgentSessionHost — a code-less pre-commit failure, exactly the class this PR's comment names ("a store that will not open"), and it happens strictly before host.attach, so nothing was created.

Flow in both runs: New tab → Codex, structured native chat enabled, local execution host.


BEFORE — fix ablated (the resolveUncommittedStructuredCreate hunk reverted in structured-agent-session.ts)

BEFORE — fix ablated: New tab → Codex leaves the user with nothing

Steady state ~18s after the click. The user has nothing: only the pre-existing "Terminal 1", no chat tab, no terminal tab, no toast. Status-bar terminal count stayed at 1.

  • Wire: THROW RuntimeRpcCallError :: EEXIST: file already exists, mkdir '/tmp/o97b/agent-sessions'
  • Renderer: a thrown error arrives as RuntimeRpcCallError, not StructuredAgentSessionCreateRefusalError, so trackLaunchSettlement never calls settleDefinitiveRefusalFallback and claimDefinitiveRefusalFallback never fires.
  • Zero app-level console errors and zero main-process breadcrumbs — the failure is entirely silent.

AFTER — fix restored (PR head, unmodified)

AFTER — fix restored: the refused launch falls back to a live Codex terminal

Same sabotage, same click. The refused launch falls back to a real Codex terminal: a second tab running codex '--dangerously-bypass-approvals-and-sandbox' in the workspace. Status-bar terminal count went 1 → 2; ptyIdsByTabId gained a second entry; the new tab carries launchAgent: 'codex'.

  • Wire: THROW StructuredAgentSessionCreateRefusalError :: Orca could not prepare a Codex chat for this workspace: EEXIST: file already exists, mkdir '/tmp/o97a/agent-sessions'
  • Main log breadcrumb now present: [agent-session] create refused before it committed anything Error: EEXIST: …

No regression — happy path still opens a structured chat

No regression — with the sabotage removed, the same click opens a structured Codex chat with no PTY

Sabotage file removed, same build, same click: a third tab "Codex Chat" opens with no new PTY (2 tabs' worth of PTYs across 3 tabs) — a genuine structured session — and agent-sessions/agent-sessions.json + .bak are written. The fallback does not fire when the host succeeds.


Finding: the PR body's "Visual Proof: N/A — desktop's user-visible behaviour is unchanged" is not accurate for this flow

The renderer today already converts a refusal envelope into StructuredAgentSessionCreateRefusalError (launch-structured-codex-session.ts) and already runs claimDefinitiveRefusalFallback (launch-agent-in-new-tab.ts). So shipping the envelope alone changes user-visible desktop behaviour immediately: the screenshots above are the same build, the same click, differing only by this PR's hunk. Worth correcting in the PR body.

Isolated rig: dev instance on private CDP + renderer ports, ORCA_DEV_USER_DATA_PATH under /tmp, scratch repo at /tmp/pr18697-vp/ws. The user's desktop Orca was never touched. Renderer console was collected for both runs; the only two entries in the BEFORE run were 404s from my own probe imports, not app errors.

Merge Sim added 2 commits September 4, 2026 16:27
@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@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

  • De-branded refusal copy — precommitRefusal now emits "structured agent chat" rather than "Codex", with test assertions pinning the agent-agnostic wording.
  • Restored the supportsCreate gate on attach-shaped entries — extracted resolveClientSuppliedAttach and routed both agentSession.create (client-supplied location) and agentSession.ensure through it, re-applying the host capability check the prior commit dropped; create now answers an unsupported client location with a definitive refusal envelope while ensure keeps its top-level error.
  • Restored per-agent tab publication — the worktree branch publishes the tab with resolved.agent again, dropping the hardcoded 'codex' agent the prior commit introduced.
  • Gated the terminal fallback on definitive refusals — launchStructuredAgentSession now throws StructuredAgentSessionCreateRefusalError (carrying code) only for definitive refusals; unknown refusals and unclassified transport failures throw plain errors that launchAndReconcile routes through recover/retry instead of a sibling terminal, and old-host method_not_found / structured_agent_session_unsupported transport errors are reclassified as definitive.

Verified all 78 tests across the five touched suites pass locally, and that the net structured-agent-session.ts change faithfully restores main's supportsCreate and per-agent tab behavior rather than regressing it.

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

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Visual proof — structured create refusal fallback (HEAD 8491e84262455c63b8e0e92781597f68d1281fa5)

Electron CDP on this worktree only (Orca: brennanb2025/pr18697-merge, renderer 127.0.0.1:5178). Launch was the visible New tab → Codex action.

Injected (refusal path only): agentSession.create returned a pre-commit { ok:false, refusal.code: structured_agent_session_unsupported } envelope. The UI still opened a fallback Codex session (pr18697-qa-repo tab, contentType: terminal, launchAgent: codex) instead of failing silently.

Refusal fallback: New tab Codex after injected unsupported create

Not injected (happy path): the same New tab → Codex action with the intercept off. Host agentSession.create succeeded and a structured Codex Chat pane opened (contentType: agent-session, session options High / GPT-5.6-Sol). Resource manager stayed at 2 terminal sessions — the structured pane is not a third PTY.

Happy path: structured Codex Chat pane

@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

  • Mobile refusal classification aligned with desktop — createMobileStructuredCodexSession now routes both the top-level error and envelope-refusal paths through a new classifyCreateRefusal helper backed by the shared isDefinitiveAgentSessionCreateRefusal allowlist, so only structured_agent_session_unsupported / method_not_found fall back to a legacy terminal and every other outcome (agent_session_ownership_unknown, agent_session_operation_unknown, runtime_error, unknown future codes) stays unknown with no sibling session.
  • Tests pin the definitive-vs-unknown boundary — added mobile tests covering top-level and envelope classification, updated the fallback test to a genuinely definitive code, and added a no-sibling assertion for unknown top-level outcomes.

Verified the mobile change closes the same duplicate-session gap the desktop renderer already fixed: agent_session_ownership_unknown previously classified as failed (fall back to terminal), now correctly unknown. Ran the three touched suites — 23 mobile tests plus 6 shared allowlist tests — all pass.

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

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Engineering review complete at 194d01ca6b77cf8aa38bc38c975e8a27a38eabb2.

  • Merged the latest origin/main into the branch and resolved structured-agent-session.ts while preserving both the generalized Claude/Codex support work and this PR’s refusal-envelope boundary.
  • Hardened desktop and mobile fallback so only definitive no-create outcomes (structured_agent_session_unsupported / old-host method_not_found) can start the legacy path. Runtime, transport, attach, tab-publication, ownership, and future unknown outcomes cannot create a sibling session.
  • Completed iterative same-model review/fix loops plus start/end readiness audits. Final readiness verdict: PASS, with no P0/P1/P2 findings and no performance, dependency, lockfile, cross-platform, or scope regressions.
  • Local verification passed: 78 focused desktop/shared tests, 23 focused mobile tests, pnpm tc, and changed-code quality.
  • CI is fully green: 19 successful, 9 intentionally skipped, 0 failing/pending. GitHub reports the PR as mergeable.
  • Electron/CDP visual proof covers the real New tab → Codex fallback and structured-chat happy path: fix(agent-session): refuse a pre-commit structured create with an envelope #18697 (comment)
  • Mobile bundle validation succeeded in the Orca-managed simulator; the live paired-host ambiguity flow could not be exercised because the available simulator was unpaired, so that exact native flow remains a documented QA gap covered by focused regression tests.

From the code-review and validation side, this PR is ready to merge.

@brennanb2025

brennanb2025 commented Sep 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Final refresh: merged the latest origin/main through e2b70a5 and pushed exact head d8794b9.

Post-merge validation is green: 72 focused desktop tests, 23 focused mobile tests, pnpm tc, and all GitHub checks (20 passed, 9 intentionally skipped). The PR is MERGEABLE / CLEAN, and the worktree is clean.

@brennanb2025
brennanb2025 merged commit cc07249 into main Sep 5, 2026
29 checks passed
brennanb2025 pushed a commit that referenced this pull request Sep 5, 2026
The rebase onto #18697 brought its definitive-refusal tests in cleanly, but they
call the pre-rename createMobileStructuredCodexSession, and mobile tsc excludes
test files so nothing caught it. Retarget them and give the agent-copy test a
code that is actually in the definitive allowlist - agent_session_refused now
correctly stays unknown, so it never reached the failure copy it asserted.
brennanb2025 pushed a commit that referenced this pull request Sep 6, 2026
The rebase onto #18697 brought its definitive-refusal tests in cleanly, but they
call the pre-rename createMobileStructuredCodexSession, and mobile tsc excludes
test files so nothing caught it. Retarget them and give the agent-copy test a
code that is actually in the definitive allowlist - agent_session_refused now
correctly stays unknown, so it never reached the failure copy it asserted.
brennanb2025 added a commit that referenced this pull request Sep 6, 2026
* feat(mobile): structured native Claude chat

Mobile already spoke the structured agent-session protocol for Codex, and the
host already had a Claude capability gate — mobile just never advertised it, so
`projectAgentSessionTabsOut` stripped every Claude tab before it left the
desktop. The structured lane in mobile/ turned out to be agent-agnostic already
(shared reducer, message projection, option catalog, prompt tokens), so this
opens the gate rather than building a second lane:

- advertise `agent-session.structured.claude.v1`
- resolve any structured provider in `resolveMobileNativeChat` via the shared
  `isAgentSessionHandleProvider`, instead of a `'codex'` literal
- widen the `agent-session` route type off `'codex'`
- route bare Claude launches through `agentSession.createSupport` like Codex,
  which still degrades to a terminal when the host refuses (remote, WSL, win32,
  managed-account mismatch, or structured chat switched off)

Deduplicate the create envelope. Renderer and mobile each assembled the
`agentSession.create` params by hand; the fingerprint has to be computed over
the same fields the host recomputes, so both now build it in one shared
`structuredAgentSessionCreateParams`. Mobile's Codex-only launcher becomes
`createMobileStructuredAgentSession(client, worktreeId, agent)` and reuses the
shared display-name map; two copies of a random-UUID fallback collapse into one.

Answer grouped Claude questions. A Claude AskUserQuestion carrying more than one
question — or one multi-select question — is emitted with the real content in
`body.questions` and the flat `options` left EMPTY, so mobile rendered a card
with nothing to tap and the turn stalled with no way out. Codex never emits this
shape. The phone has room for one question at a time, so the group is answered
as steps and submitted once, reusing the shared
`encodeAgentSessionQuestionAnswers` / `isValidAgentSessionQuestionAnswers`
rather than a second encoding.

Prompt responses move into `useMobileStructuredPromptResponses` because grouped
questions carry a multi-step draft the rest of the session does not touch, and
the session hook was at the 300-line cap.

Pin the mobile capability list against the host's parser bounds: it fails closed
to NO capabilities when the array exceeds 64 entries, which would look exactly
like an old client.

Re-pin mobile-session-route-parity: the create-actions edit drops one runtime
string literal and changes one nested function body. Ablated to confirm that
file is the sole cause.

* fix(mobile): derive the grouped-question draft instead of clearing it in an effect

The React Doctor gate flagged the session-change reset as a state adjustment
after a prop change, which renders the stale draft for a frame. Store the
session the answers were collected in alongside them and check it on read, so a
session switch drops the draft during render with no effect at all.

* test(mobile): pin that grouped steps key apart when the questions read identically

Claude can ask the same text twice in one group (once per file, say). The view
keys the question card by its projected content, so identical wording must still
key apart or step 1's checkboxes would be submitted as step 2's answer.

* fix(mobile): harden grouped Claude question answers

* fix(mobile): retry transient structured support probes

* fix(mobile): preserve grouped prompt response compatibility

* fix(mobile): preserve tokenless duplicate choice identity

* fix(mobile): point the launch tests at the generalized create API

The rebase onto #18697 brought its definitive-refusal tests in cleanly, but they
call the pre-rename createMobileStructuredCodexSession, and mobile tsc excludes
test files so nothing caught it. Retarget them and give the agent-copy test a
code that is actually in the definitive allowlist - agent_session_refused now
correctly stays unknown, so it never reached the failure copy it asserted.

* test(mobile): re-pin route parity after the rebase onto main

Main moved its own runtime-string pin to 547; this branch drops the 'codex'
literal from the create-actions gate. Ablated against main's pins to confirm
that file is the sole cause before re-deriving.

---------

Co-authored-by: Merge Sim <sim@local>
enrick-dev pushed a commit to enrick-dev/orca that referenced this pull request Oct 15, 2026
…elope (stablyai#18697)

* fix(agent-session): refuse a pre-commit structured create with an envelope

The create route refused by throwing, which reaches a client as a generic
transport error indistinguishable from a lost answer — so desktop parked the
launch as visibility-unknown with no chat and no terminal. Convert the whole
pre-commit span, everything before `attach`, into a refusal envelope carrying a
code, and name the definitive-refusal allowlist the fallback decision needs.

* fix(agent-session): gate legacy fallback on definitive refusals

* fix(mobile): preserve unknown structured create outcomes

---------

Co-authored-by: Merge Sim <sim@local>
enrick-dev pushed a commit to enrick-dev/orca that referenced this pull request Oct 15, 2026
* feat(mobile): structured native Claude chat

Mobile already spoke the structured agent-session protocol for Codex, and the
host already had a Claude capability gate — mobile just never advertised it, so
`projectAgentSessionTabsOut` stripped every Claude tab before it left the
desktop. The structured lane in mobile/ turned out to be agent-agnostic already
(shared reducer, message projection, option catalog, prompt tokens), so this
opens the gate rather than building a second lane:

- advertise `agent-session.structured.claude.v1`
- resolve any structured provider in `resolveMobileNativeChat` via the shared
  `isAgentSessionHandleProvider`, instead of a `'codex'` literal
- widen the `agent-session` route type off `'codex'`
- route bare Claude launches through `agentSession.createSupport` like Codex,
  which still degrades to a terminal when the host refuses (remote, WSL, win32,
  managed-account mismatch, or structured chat switched off)

Deduplicate the create envelope. Renderer and mobile each assembled the
`agentSession.create` params by hand; the fingerprint has to be computed over
the same fields the host recomputes, so both now build it in one shared
`structuredAgentSessionCreateParams`. Mobile's Codex-only launcher becomes
`createMobileStructuredAgentSession(client, worktreeId, agent)` and reuses the
shared display-name map; two copies of a random-UUID fallback collapse into one.

Answer grouped Claude questions. A Claude AskUserQuestion carrying more than one
question — or one multi-select question — is emitted with the real content in
`body.questions` and the flat `options` left EMPTY, so mobile rendered a card
with nothing to tap and the turn stalled with no way out. Codex never emits this
shape. The phone has room for one question at a time, so the group is answered
as steps and submitted once, reusing the shared
`encodeAgentSessionQuestionAnswers` / `isValidAgentSessionQuestionAnswers`
rather than a second encoding.

Prompt responses move into `useMobileStructuredPromptResponses` because grouped
questions carry a multi-step draft the rest of the session does not touch, and
the session hook was at the 300-line cap.

Pin the mobile capability list against the host's parser bounds: it fails closed
to NO capabilities when the array exceeds 64 entries, which would look exactly
like an old client.

Re-pin mobile-session-route-parity: the create-actions edit drops one runtime
string literal and changes one nested function body. Ablated to confirm that
file is the sole cause.

* fix(mobile): derive the grouped-question draft instead of clearing it in an effect

The React Doctor gate flagged the session-change reset as a state adjustment
after a prop change, which renders the stale draft for a frame. Store the
session the answers were collected in alongside them and check it on read, so a
session switch drops the draft during render with no effect at all.

* test(mobile): pin that grouped steps key apart when the questions read identically

Claude can ask the same text twice in one group (once per file, say). The view
keys the question card by its projected content, so identical wording must still
key apart or step 1's checkboxes would be submitted as step 2's answer.

* fix(mobile): harden grouped Claude question answers

* fix(mobile): retry transient structured support probes

* fix(mobile): preserve grouped prompt response compatibility

* fix(mobile): preserve tokenless duplicate choice identity

* fix(mobile): point the launch tests at the generalized create API

The rebase onto stablyai#18697 brought its definitive-refusal tests in cleanly, but they
call the pre-rename createMobileStructuredCodexSession, and mobile tsc excludes
test files so nothing caught it. Retarget them and give the agent-copy test a
code that is actually in the definitive allowlist - agent_session_refused now
correctly stays unknown, so it never reached the failure copy it asserted.

* test(mobile): re-pin route parity after the rebase onto main

Main moved its own runtime-string pin to 547; this branch drops the 'codex'
literal from the create-actions gate. Ablated against main's pins to confirm
that file is the sole cause before re-deriving.

---------

Co-authored-by: Merge Sim <sim@local>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant