Skip to content

fix(orchestration): call the Orca session ID orca_session_id everywhere agents see it - #24230

Merged
brennanb2025 merged 8 commits into
mainfrom
brennanb2025/orca-session-id-prefix
Oct 1, 2026
Merged

brennanb2025 merged 8 commits into
mainfrom
brennanb2025/orca-session-id-prefix

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 10 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​271 $\color{#cf222e}{\Huge{\mathbf{−}}}$​40 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​231
Prod 18 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​125 $\color{#cf222e}{\Huge{\mathbf{−}}}$​60 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​65

The problem

When one agent messages another through Orca orchestration, it names the recipient by its Orca session ID, the ID Orca mints for every agent session. Agents see and type that ID as session:<id> (for example orca orchestration send --to session:3f9c…).

"Session" alone means several different things in Orca: the Claude/Codex session ID (the "Copy Session ID" action), Session History, and terminal sessions. So an agent or user reading session:<id> can reasonably think it is the Claude or Codex session ID, which it is not, and paste the wrong one.

The database already calls this identity orca_session_id (the coordinator_orca_session_id, assignee_orca_session_id and creator_orca_session_id columns from #22522, which followed the team discussion that "sessionId itself is confusing"). Only the text form agents see still says plain session:. The goal is that every agent is referred to by its Orca session ID, spelled orca_session_id:<id>.

What this changes for you

Before After
Sending mail to an agent --to session:3f9c… --to orca_session_id:3f9c…
A caller flag naming a session --from session:3f9c… --from orca_session_id:3f9c…
Mail headers an agent reads (from_handle, to_handle) session:3f9c… orca_session_id:3f9c…
Typing the old --to session:3f9c… Reaches the agent Refused like any unknown address: "Terminal session:3f9c… has no live pane or durable Run/Dispatch mailbox."
Refusal when an agent passes its Claude/Codex session ID as a recipient "This session's Orca address is session:3f9c…" "This session's Orca session ID is orca_session_id:3f9c…; address it by that instead."
Refusal when a caller names itself by its Claude/Codex session ID "This session's Orca id is 3f9c…" "This session's Orca session ID is orca_session_id:3f9c…; set ORCA_AGENT_SESSION_ID=3f9c… instead."

A bare Orca session ID (no prefix) is still accepted wherever it was before. Terminal handles (term_…) and terminal-agent wording are unchanged.

Scope: Stored session:<id> addresses are migrated once to orca_session_id:<id>; the old prefix is no longer accepted as input. That is, session: no longer names an Orca session anywhere: --to session:<id> gets the generic refusal below, a chat's --from session:<id> is refused, and a terminal agent's --from session:<id> is treated like any other unrecognised sender name (as --from foo:bar is today), not as a session. Your existing chats stay reachable: they keep the mail sent to them before the upgrade, keep receiving mail for the Runs they coordinate, and replies to their old mail reach them. Typing --to session:<id> is refused like any unknown terminal handle ("Terminal session: has no live pane or durable Run/Dispatch mailbox."), and nothing is stored. There is no alias and no version handshake with remote hosts.

Rolling back to v1.4.214–v1.4.218 after this build has run: the older build skips migration on the v43 database and cannot see unread mail the migration already respelled to orca_session_id:<id> until you upgrade again.

Deferred: if you upgrade, roll back to v1.4.214–v1.4.218, then upgrade again, mail that older build stored at a session:<id> address during the rollback is not migrated, because the one-time migration has already run (the database is already at v43). Running the same rewrite on every open would close this; it is left out to keep the migration one-time.

What changed

The prefix constant ORCA_SESSION_ADDRESS_PREFIX (src/shared/orca-session-address-prefix.ts) is now orca_session_id:. Everything that formats or parses the address already derives it from that constant: the shared parse/format codec, the CLI's own-session check, the SQL helper that builds '<prefix>' || id, the mail-target and recipient resolvers, and the address injected into agent-facing text.

One place did not follow the constant on existing databases: the two triggers that remember a Run coordinator's session address (so mail to it is routed into the Run mailbox) have the prefix compiled into their SQL body, and migrate-v42 created them once. An existing v42 database would therefore keep remembering session:<id> for every new Run, so mail to the coordinator's orca_session_id:<id> would not route until the next app restart backfilled it. The triggers are now recreated on every open, right after migrate, the same way the coordinator mail routing trigger already is, so the trigger body always matches the running build. migrate-v42 keeps only the columns and indexes. The triggers need no schema version bump.

Stored addresses: schema v43 (src/main/runtime/orchestration/db/schema/migrate-v43.ts) rewrites session:<id> to orca_session_id:<id> once, in every column that holds an Orca session address: message sender and recipient (messages.from_handle, messages.to_handle), remembered Run coordinator addresses (run_coordinator_handles.terminal_handle), structured pointer operation keys (structured_pointer_operations.mailbox_handle) and coordinator loop handles (coordinator_runs.coordinator_handle). It matches the exact prefix, leaves subjects, bodies and payloads as written, and spells both prefixes as fixed literals rather than the live constant, because a migration rewrites what one particular build wrote. Where a keyed table already holds the new spelling, it keeps that row and drops the old one. Running it again changes nothing. It only rewrites data, so the schema version-skew check needs nothing new, and an older build that opens a v43 database skips migration, as it already does for any newer schema.

The two provider-ID refusals now use the same wording: "This session's Orca session ID is orca_session_id:". The recipient refusal adds "address it by that instead". The caller refusal adds "set ORCA_AGENT_SESSION_ID= instead", because that environment variable is where the caller's ID comes from and it takes only the bare ID. The refusal's machine-readable orcaSessionId field stays the bare ID. The malformed-recipient refusal says "does not name an Orca session ID".

Tests and code comments are updated to the new spelling. A new test stamps an existing database with triggers compiled for the old session: prefix, reopens it, and checks a new Run's coordinator is remembered at orca_session_id:<id>. It fails on main's migrate-v42-only trigger install (it records session:<id>) and passes with this change.

Left as session: on purpose, because they are not the Orca session address: the workspace-session IPC channels (session:get, session:set, …) and browser:session:* channels, the AI Vault list and search keys, the desktop-script cache key, the native-chat subscription tokens (claude:session:…), the structured pointer idempotency key (trusted-local:orchestration:session:<id>, an internal ledger key whose shape change would orphan in-flight entries), mobile test fixtures, and two historical comments quoting what an earlier build wrote.

The bundled skill guides and CLI help never spelled the address, so they are unchanged (the guide and skill-bundle manifest checks pass). Mobile does not parse or format the address.

Testing

  • Targeted vitest: src/main/runtime/orchestration, src/cli, src/shared, tests/e2e/cross-version-wire, the orchestration RPC suites and structured-chat coordinator mail tests.
  • New and tightened tests: both provider-ID refusal texts are asserted exactly, including the orca_session_id:<id> an agent would paste. The trigger regression test builds its old-prefix setup from ORCA_SESSION_ADDRESS_PREFIX and asserts the rewrite actually changed the trigger SQL, so a future prefix change cannot make it pass without testing anything.
  • Migration tests: a database seeded the way an earlier build left it (unread mail to session:<id>, a Run remembered under session:<id>, mail a chat sent as session:<id>, keyed rows in both spellings, a coordinator loop handle) goes through the real migrate. Then the chat reads the old mail through its own check; mail to the chat's new address lands in that Run's mailbox; a reply to the old mail reaches the original sender at its new address; --to session:<id> is refused with terminal_not_found and the generic text, including for the Run whose remembered address was session:<id>; subjects and bodies that spell session:<id> are untouched; no address column keeps the old spelling; running the step again changes nothing. On a fresh database --to session:<id> gets the same generic refusal, and the address codec still refuses to parse session:<id>.
  • Ablation: with the migration step disabled, the read, Run-routing, reply and refusal tests fail. On a database that is not migrated, --to session:<id> for a Run remembered under that spelling is accepted and routed to the Run, so the refusal depends on the migration rewriting that row.
  • Current main is merged in (at 3ab3c92). Locally tc:node is clean. 614 test files ran (612 passed, 2 skipped) across src/main/runtime/orchestration, src/main/runtime/rpc, src/cli, tests/e2e/cross-version-wire, orca-runtime, structured-chat coordinator mail and the address codec: 7027 tests passed, 16 skipped, 0 failed.
  • The round-2 migration review measured the one-time rewrite at about 0.15 s for 100k messages and 2.5 s for 1M. It runs once, on the first orchestration call after the upgrade (the database opens lazily).
  • One cross-version test, agent-session-resume-marker-downgrade (restart offers, unrelated to orchestration addresses), could not run locally: the harness's 45-second tar extraction of an old release timed out on a heavily loaded machine. CI covers it.
  • oxlint (full), anti-slop on the changed files, and check:code-quality:changed pass.
  • verify:bundled-skill-guides and verify:skill-bundle-manifest pass.

Screenshots

Real app on a Mac, restructured Claude chats, real Claude CLI, isolated profile.

Upgrade from the build before this PR, same profile. On the old build (base 5cda0f4), chat A mails chat B at session:<B> and B leaves the second mail unread:

Old build: chat B receives the first mail from session:<A> and is told to leave the next one unread

After relaunching the same profile on this PR (head cec36b3; the database goes from schema 42 to 43), B reads the mail it left unread, now from orca_session_id:<A>:

This PR: chat B's check lists the pre-upgrade unread mail with sender orca_session_id:<A>

Typing the old --to session:<B> is refused with the generic unknown-terminal error and nothing is delivered:

This PR: send --to session:<B> fails with terminal_not_found; nothing reaches B

The new spelling reaches B:

This PR: B receives mail sent to orca_session_id:<B>, sender orca_session_id:<A>

A still gets mail for the Run it coordinated before the upgrade, including B's reply to the old mail (shown after A acknowledges an older Run delivery it had read but never acknowledged before the upgrade; that wait is existing Run delivery behaviour, identical without the upgrade):

This PR: A's Run check returns B's reply to the old mail and new Run mail, senders orca_session_id:<B>

Fresh profile (head 7a685db; the address and refusal code is unchanged since):

Chat A sends to orca_session_id:<B>: ok, from_handle orca_session_id:<A>, to_handle orca_session_id:<B>

Chat B receives the mail turn; its check shows from=orca_session_id:<A>

Sending to B's Claude session id is refused: This session's Orca session ID is orca_session_id:<B>; address it by that instead

Terminal agents unchanged: chat A sends to a term_ handle, ok

…id:<id>

The database already names this identity orca_session_id, but agents saw and typed
session:<id>, which collides with the Claude/Codex session ID, Session History and
terminal sessions. The prefix is now orca_session_id:. No alias, migration or version
handshake for the old session: spelling: restructured native chat is experimental.

The coordinator-address triggers compile the prefix into their body, and migrate-v42
stamped them once, so an existing database would keep remembering session:<id> for
new Runs. They are now recreated on every open, like the mail routing trigger.

Provider-id refusals now name the "Orca session ID" instead of "Orca address"/"Orca id".
@greptile-apps

greptile-apps Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Critical risk] Database migration respells session addresses in stored data.

The PR appears safe to merge, with a non-blocking caller-refusal wording issue remaining.

Findings

  1. P2 Prefixed address mislabeled as ID ▶

Summary

The PR changes agent-facing Orca session addresses from session:<id> to orca_session_id:<id>, migrates stored addresses, and recreates coordinator-address triggers when the database opens.

  • Existing mail and Run coordinator addresses are respelled during the v43 migration.
  • The old address prefix is no longer accepted as input.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A["Stored session:<id> addresses"] --> B["v43 migration"]
  B --> C["orca_session_id:<id> addresses"]
  C --> D["Mail and Run routing"]
  E["Database open"] --> F["Recreate coordinator-address triggers"]
  F --> D
Loading

Reviews (7) · Last reviewed commit: "Merge origin/main into #24230"

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

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: f0770a79-50b0-4343-aad3-74a18277dbf3

📥 Commits

Reviewing files that changed from the base of the PR and between 809d623 and cec36b3.

📒 Files selected for processing (7)
  • src/cli/orchestration-session-caller-cli.test.ts
  • src/main/runtime/rpc/methods/orchestration/messaging/session-recipient.ts
  • src/main/runtime/rpc/orchestration-legacy-session-address-migration.test.ts
  • src/main/runtime/rpc/orchestration-session-recipient.test.ts
  • src/shared/orca-session-address-prefix.ts
  • src/shared/orca-session-address.test.ts
  • src/shared/orca-session-address.ts
💤 Files with no reviewable changes (1)
  • src/shared/orca-session-address-prefix.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The canonical Orca session address prefix changes from session: to orca_session_id:. Address parsing and recipient handling reject the retired spelling. Schema version 43 converts legacy addresses in stored address fields and handles keyed-row conflicts. Database opening now recreates run coordinator address triggers after migrations and before backfills. Tests cover address parsing, mail routing, migration behavior, and trigger recreation.

Priority: ➖ Normal

Merge Risk: ⚪ Minimal · up to cec36

Stored legacy addresses are upgraded to the canonical spelling, while new inputs using the retired prefix are rejected. The repository contract supports this behavior, leaving no actionable merge risk.

Security Architecture Review

Security architecture risk: 🔵 Low · up to cec36

The normal upgrade preserves stored mail and routing ownership without an identified expansion of caller authority. Remaining uncertainty concerns older-version rollback, mixed-version access, and subsequent recovery—not a demonstrated security vulnerability.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — The documented direct-addressing scope includes sessions on the current host even when they do not coordinate a Run. Durable Run and Dispatch mailbox ownership provide additional routing paths. These inspected controls do not establish tenant isolation or end-to-end authorization for every messaging transport.

Trust Boundaries and Controls

  • observed — Session caller claims from paired clients and SSH or WSL environments are refused. Local claims require a valid Orca ID, a matching session record, local host scope, and a writable lease. Direct session delivery separately refuses unverifiable, unknown, other-host, and ended recipients.

Resilience and Maintainability Implications

  • observed — Coordinator-address trigger replacement uses BEGIN IMMEDIATE with rollback on failure. Recreating triggers on every open prevents a successfully opened current-version database from retaining a trigger body compiled with an obsolete address prefix.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.96% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 27 functions across 29 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description explains the problem, user-visible behavior, implementation, migration, testing, and visual results. However, it omits the required Linked Issue with an actual issue number and does no… Add an actual issue reference after Fixes #. Add the missing required template sections, or explicitly mark them as not applicable where allowed. Include the required checklist confirmations and any relevant security, compatibility, platf…
✅ Passed checks (3 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly identifies the main change: replacing the agent-facing Orca session address with the orca_session_id format.
Full details: Description check

Explanation

The description explains the problem, user-visible behavior, implementation, migration, testing, and visual results. However, it omits the required Linked Issue with an actual issue number and does not include several required template sections, including Review, Agent skill upstream boundary, Notes, and Checklist.

Resolution

Add an actual issue reference after Fixes #. Add the missing required template sections, or explicitly mark them as not applicable where allowed. Include the required checklist confirmations and any relevant security, compatibility, platform, SSH, mobile, and performance notes.

✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


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

Renames the agent-facing Orca session address from session:<id> to orca_session_id:<id>, and makes the coordinator-address triggers follow the running build instead of the prefix compiled when the database first migrated.

  • Prefix constant renamed — ORCA_SESSION_ADDRESS_PREFIX is now orca_session_id:; the codec, CLI own-session check, injected-address helper, SQL address helper, mail-target/recipient resolvers, and agent-facing text all derive from it.
  • Coordinator triggers recreated on every open — createRunCoordinatorAddressTriggers is extracted from migrate-v42 and run after migrate on each OrchestrationDb open, so an existing v42 database stops remembering session:<id> for a new Run coordinator. migrate-v42 keeps only columns and indexes; no schema bump.
  • Refusal text — the two provider-id refusals and the malformed-recipient refusal now say "Orca session ID".
  • Tests — existing assertions moved to the new spelling; a new test stamps a database with old-prefix triggers, reopens, and asserts the new prefix is used.

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

…vider-id refusals

The send refusal handed back orca_session_id:<id> as the Orca session ID while
the caller refusal called the bare id by that name. Both now say "This
session's Orca session ID is orca_session_id:<id>"; the caller refusal also
names the bare value ORCA_AGENT_SESSION_ID accepts, since that input takes
only the bare id.

Tests pin both refusal texts, that session:<id> no longer parses as a session,
and that the older-build trigger rewrite actually changes the trigger SQL.
throw new OrchestrationError(
CODES.providerId,
`${sessionId} is the provider's own session id, which changes on /clear. This session's Orca id is ${found.orcaSessionId}; use that instead. No effects were applied.`,
`${sessionId} is the provider's own session id, which changes on /clear. This session's Orca session ID is ${ORCA_SESSION_ADDRESS_PREFIX}${found.orcaSessionId}; set ${ORCA_AGENT_SESSION_ID_ENV}=${found.orcaSessionId} instead. No effects were applied.`,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Prefixed address mislabeled as ID When the CLI supplies a Claude or Codex session ID as its caller identity, this refusal calls orca_session_id:<id> the “Orca session ID,” but ORCA_AGENT_SESSION_ID requires the bare <id>. An agent that copies the displayed value into that variable is refused again as an unknown session. Show the bare ID as the session ID and reserve the prefixed form for its address.

Suggested change
`${sessionId} is the provider's own session id, which changes on /clear. This session's Orca session ID is ${ORCA_SESSION_ADDRESS_PREFIX}${found.orcaSessionId}; set ${ORCA_AGENT_SESSION_ID_ENV}=${found.orcaSessionId} instead. No effects were applied.`,
`${sessionId} is the provider's own session id, which changes on /clear. This session's Orca session ID is ${found.orcaSessionId}; set ${ORCA_AGENT_SESSION_ID_ENV}=${found.orcaSessionId} instead. No effects were applied.`,

@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

Reviewed the delta since the prior pullfrog review: the authored commit 7a685dbc54 (the merge bb6ec0eb02 only brings origin/main forward and adds no authored change here).

  • Aligned the two provider-id refusals — session-recipient.ts and orchestration-session-caller.ts both now spell the identity as orca_session_id:<id>; the caller-side refusal points at the bare value ORCA_AGENT_SESSION_ID accepts because that path reads only a bare id from the env var the CLI sends as agentSessionId.
  • Pinned the refusal text exactly — orchestration-session-caller.test.ts and orchestration-session-recipient.test.ts now assert the full strings instead of stringContaining, and the recipient case covers both the orca_session_id:<provider id> and bare spellings.
  • Added the retired-spelling test — orca-session-address.test.ts asserts session:<id> no longer parses as an address nor satisfies isOrcaSessionId.
  • Guarded the trigger-rewrite fixture — run-coordinator-orca-session-address.test.ts now asserts its older-build rewrite actually changes the trigger SQL, so the reopen cannot silently prove nothing.

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

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (1)

🟠 Major · Preserve handle provenance before removing retired addresses. · orchestration-db.ts:29-37

src/main/runtime/orchestration/db/orchestration-db.ts:29-37
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Preserve handle provenance before removing retired addresses.

The open path recreates the triggers and refills the cache with INSERT OR IGNORE. It does not remove existing session: rows. Exact handle lookup can therefore continue routing a retired address to its historical run: mailbox.

Do not delete every session:% row. run_coordinator_handles does not record whether a row came from coordinator_handle or a generated session address. The binding path also accepts arbitrary handle strings, and the cache intentionally retains historical handles. Current run metadata cannot distinguish all of these cases safely.

Add provenance or enforce a disjoint handle namespace before removing only generated retired addresses. The migration must also define how to handle already ambiguous rows to preserve the no-alias contract.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: d42df367-b37b-41b6-b3a5-aff19d541165

📥 Commits

Reviewing files that changed from the base of the PR and between f37d879 and 7a685db.

📒 Files selected for processing (7)
  • src/main/runtime/orchestration/run-coordinator-orca-session-address.test.ts
  • src/main/runtime/rpc/methods/orchestration/messaging/session-recipient.ts
  • src/main/runtime/rpc/orchestration-session-caller.test.ts
  • src/main/runtime/rpc/orchestration-session-caller.ts
  • src/main/runtime/rpc/orchestration-session-recipient.test.ts
  • src/main/runtime/structured-chat-coordinator-mail.test.ts
  • src/shared/orca-session-address.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/runtime/rpc/orchestration-session-caller.ts
  • src/main/runtime/rpc/methods/orchestration/messaging/session-recipient.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 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.

✅ No new issues found.

Reviewed changes

The only commit since the prior pullfrog review (7a685dbc54) is 5d8a69c644, a merge of origin/main into the PR branch. A scoped git diff 7a685dbc54 5d8a69c644 -- <all 24 PR file paths> is empty, so the merge brought in base-branch churn only, with no PR-authored change and no conflict resolution touching the PR's files. The prior review stands unchanged.

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

…s after the rename

Existing native chats were addressed as session:<id>, so agents and stored
mail still use that spelling. Input now accepts session:<id> and treats it as
orca_session_id:<id> (the codec parses both; nothing writes the old one), and
schema v43 rewrites every stored session:<id> address once: message senders
and recipients, remembered Run coordinator addresses, structured pointer
operation keys, and coordinator loop handles. Subjects, bodies and payloads
are left as written.
… still migrate once

The old prefix is not accepted as input: no respelling helper, no special
case. A session:<id> recipient is refused like any unknown terminal handle
(terminal_not_found), including after v43 rewrote a Run's remembered
session:<id> coordinator address, so the old spelling no longer routes.
Schema v43 still rewrites stored session:<id> addresses once.

@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 is commit 809d6235 ("keep chats reachable at their session:<id> address after the rename"), which turns the one-way rename into a forward migration with read-only legacy aliases.

  • Legacy alias codec — orca-session-address-prefix.ts gains respellLegacyOrcaSessionAddress and isSpelledAsOrcaSessionAddress; parseOrcaSessionAddress respells first, so session:<id> still parses to the bare id while formatOrcaSessionAddress only ever emits the current prefix.
  • Alias acceptance on input — the CLI own-session check and sessionAddressForHost, the recipient resolver (mayNameSession/readSessionRecipient), and the party resolver all treat the legacy spelling as the same Session address, respelling it before lookup or storage.
  • Schema v43 — migrateV43 rewrites stored session:<id> addresses once in messages, coordinator_runs, and the two keyed tables (dedup via UPDATE OR IGNORE + DELETE); idempotent and inside the existing migrate transaction.
  • Coordinator triggers — createRunCoordinatorAddressTriggers now owns the trigger form and runs on every open, so the compiled prefix always matches the running build.
  • Tests — a new v42-seeded migration test asserts the rewrite, dedup, chat read, reply address, and rerun no-op; the trigger test asserts its own fixture actually changed the SQL.

I traced the migration's dedup against the real PKs (run_coordinator_handles(run_id, terminal_handle), structured_pointer_operations(mailbox_handle)), confirmed deliveries.mailbox_handle only holds run:/dispatch: role addresses so it is correctly not migrated, and verified runs.coordinator_handle never holds a session address (only mailboxAddressOf does, which the keyed table covers). The prior CodeRabbit provenance concern is superseded: every session: row in those tables is a generated address, so the rewrite cannot rename a literal handle. No new findings.

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

@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: 094f14af-6cfa-4b58-a925-c5fa0439568b

📥 Commits

Reviewing files that changed from the base of the PR and between 5d8a69c and 809d623.

📒 Files selected for processing (11)
  • src/cli/orchestration-session-caller-cli.test.ts
  • src/cli/session-caller-flags.ts
  • src/main/runtime/orchestration/db/contract-constants.ts
  • src/main/runtime/orchestration/db/schema/migrate-v43.ts
  • src/main/runtime/orchestration/db/schema/migrate.ts
  • src/main/runtime/rpc/methods/orchestration/messaging/session-recipient.ts
  • src/main/runtime/rpc/orchestration-legacy-session-address-migration.test.ts
  • src/main/runtime/rpc/orchestration-session-recipient.test.ts
  • src/shared/orca-session-address-prefix.ts
  • src/shared/orca-session-address.test.ts
  • src/shared/orca-session-address.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.

Comment on lines +34 to +42
for (const [table, column] of ADDRESS_COLUMNS) {
this.db.exec(`UPDATE ${table} SET ${column} = ${respelled(column)} WHERE ${isLegacy(column)}`)
}
for (const [table, column] of KEYED_ADDRESS_COLUMNS) {
this.db.exec(`
UPDATE OR IGNORE ${table} SET ${column} = ${respelled(column)} WHERE ${isLegacy(column)};
DELETE FROM ${table} WHERE ${isLegacy(column)};
`)
}

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
rg -nP "session:\\$\\{|'session:'|\"session:\"" src/main --type=ts -g '!**/*.test.ts' -C2
rg -nP '\b\w+_handle\b\s+TEXT' src/main/runtime/orchestration/db -C1

Repository: stablyai/orca

Length of output: 13181


🏁 Script executed:

#!/bin/bash
set -o pipefail
printf '%s\n' '--- migrate-v43 ---'
cat -n src/main/runtime/orchestration/db/schema/migrate-v43.ts
printf '%s\n' '--- migration references ---'
rg -n -C3 'ADDRESS_COLUMNS|KEYED_ADDRESS_COLUMNS|orca_session_id:|session:' src/main/runtime/orchestration
printf '%s\n' '--- orchestration schemas ---'
cat -n src/main/runtime/orchestration/db/schema/create-core-tables-sql.ts
printf '%s\n' '--- required diff stat ---'
git diff --stat 3ab3c9239fba5eaa36c3c4ce10aab4cf346ee812 809d6235bede57dd7dd1af990abaf73a44d7de3
printf '%s\n' '--- required diff for migration ---'
git diff --unified=30 3ab3c9239fba5eaa36c3c4ce10aab4cf346ee812 809d6235bede57dd7dd1af990abaf73a44d7de3 -- src/main/runtime/orchestration/db/schema/migrate-v43.ts

Repository: stablyai/orca

Length of output: 41582


🏁 Script executed:

#!/bin/bash
set -o pipefail
printf '%s\n' '--- legacy migration test fixture ---'
sed -n '600,715p' src/main/runtime/orchestration/orchestration-legacy-session-address-migration.test.ts
printf '%s\n' '--- v42 migration at head ---'
cat -n src/main/runtime/orchestration/db/schema/migrate-v42.ts
printf '%s\n' '--- relevant current address construction and consumers ---'
rg -n -C4 'ORCA_SESSION_ADDRESS_PREFIX|sessionAddress|SessionAddress|from_handle|to_handle|coordinator_handle|mailbox_handle|terminal_handle|caller_handle|recipient|owner' src/main/runtime/orchestration/db src/main/runtime/orchestration/messaging src/main/runtime/orchestration -g '*.ts' -g '!**/*.test.ts'
printf '%s\n' '--- legacy prefix occurrences at required base ---'
git grep -n -E 'session:|orca_session_id:' 3ab3c9239fba5eaa36c3c4ce10aab4cf346ee812 -- src/main/runtime/orchestration ':!**/*.test.ts'
printf '%s\n' '--- legacy prefix occurrences at reviewed head ---'
git grep -n -E 'session:|orca_session_id:' 809d6235bede57dd7dd1af990abaf73a44d7de3 -- src/main/runtime/orchestration ':!**/*.test.ts'

Repository: stablyai/orca

Length of output: 45665


🏁 Script executed:

#!/bin/bash
set -o pipefail
printf '%s\n' '--- locate legacy migration test ---'
git ls-files | rg 'legacy.*session.*address|session.*address.*migration'
printf '%s\n' '--- base v42 migration ---'
git show 3ab3c9239fba5eaa36c3c4ce10aab4cf346ee812:src/main/runtime/orchestration/db/schema/migrate-v42.ts | cat -n
printf '%s\n' '--- current test files mentioning earlier build ---'
rg -l -F 'earlier build of this step stamped v42' src/main
printf '%s\n' '--- fixture declaration and focused body ---'
file=$(rg -l -F 'earlier build of this step stamped v42' src/main | head -n1)
if [ -n "$file" ]; then
  rg -n -C8 'earlier build of this step stamped v42|CREATE TABLE|CREATE TRIGGER|session:<id>|coordinator_principal|coordinator_actor|principal|actor' "$file"
fi
printf '%s\n' '--- current schema table declarations ---'
rg -n -C3 '^CREATE TABLE|coordinator_runs|dispatch_contexts|tasks|messages|deliveries|structured_pointer_operations' src/main/runtime/orchestration/db/schema src/main/runtime/orchestration/db

Repository: stablyai/orca

Length of output: 45663


🏁 Script executed:

#!/bin/bash
set -o pipefail
testfile=src/main/runtime/rpc/orchestration-legacy-session-address-migration.test.ts
printf '%s\n' '--- dedicated migration test address references ---'
rg -n -C6 'session:|orca_session_id:|formatOrcaSessionAddress|ADDRESS|messages|coordinator_runs|run_coordinator_handles|structured_pointer_operations|dispatch_contexts|tasks' "$testfile"
printf '%s\n' '--- address formatter uses in non-test source ---'
rg -n -C3 'formatOrcaSessionAddress|ORCA_SESSION_ADDRESS_PREFIX|structuredSessionPointerCallerKey|insertMessage|createStructuredPointer|mailboxHandle|coordinatorHandle' src/main -g '*.ts' -g '!**/*.test.ts'
printf '%s\n' '--- coordinator-runs schema and stores ---'
rg -n -C5 'CREATE TABLE.*coordinator_runs|coordinator_runs|coordinator_handle' src/main/runtime/orchestration/db -g '*.ts' -g '!**/*.test.ts'
printf '%s\n' '--- migration orchestration ---'
cat -n src/main/runtime/orchestration/db/schema/migrate.ts | sed -n '1,100p'

Repository: stablyai/orca

Length of output: 45667


🏁 Script executed:

#!/bin/bash
set -o pipefail
printf '%s\n' '--- deliveries mailbox_handle ---'
rg -n -C5 '\bmailbox_handle\b' src/main/runtime/orchestration/db src/main/runtime/orchestration -g '*.ts' -g '!**/*.test.ts'
printf '%s\n' '--- dispatch and task handle declarations/writes ---'
rg -n -C4 'assignee_handle|creator_handle|created_by_terminal_handle|terminal_handle|from_handle|to_handle' src/main/runtime/orchestration/db/schema src/main/runtime/orchestration/db/dispatch src/main/runtime/orchestration/db/tasks src/main/runtime/orchestration -g '*.ts' -g '!**/*.test.ts' | head -n 500
printf '%s\n' '--- gate/question persistence ---'
rg -n -C5 'question_threads|decision_gate|requester|caller|asker_handle|created_by_terminal_handle' src/main/runtime/orchestration/db src/main/runtime/orchestration -g '*.ts' -g '!**/*.test.ts' | head -n 500

Repository: stablyai/orca

Length of output: 42725


🏁 Script executed:

#!/bin/bash
set -o pipefail
printf '%s\n' '--- mailbox delivery API and callers ---'
sed -n '1,125p' src/main/runtime/orchestration/db/messages/role-mailbox-delivery.ts
rg -n -C6 'enqueue|create|deliver|mailboxHandle|mailbox_handle' src/main/runtime/orchestration -g '*.ts' -g '!**/*.test.ts' | rg -C3 'delivery|mailboxHandle|mailbox_handle|enqueue|create'
printf '%s\n' '--- task and gate write boundaries ---'
rg -n -C8 'INSERT INTO tasks|created_by_terminal_handle|INSERT INTO question_threads|asker_handle|createDecision|decision_gates' src/main/runtime/orchestration/db src/main/runtime/orchestration -g '*.ts' -g '!**/*.test.ts'

Repository: stablyai/orca

Length of output: 45663


🏁 Script executed:

#!/bin/bash
set -o pipefail
printf '%s\n' '--- exact delivery callers ---'
rg -n -C8 'getOrCreateMailboxDelivery|requireMailboxConsumer' src/main -g '*.ts' -g '!**/*.test.ts'
printf '%s\n' '--- mailbox consumer implementation ---'
cat -n src/main/runtime/orchestration/db/messages/mailbox-consumer.ts
printf '%s\n' '--- task creation inputs ---'
rg -n -C10 'createTask\(|createdByTerminalHandle|created_by_terminal_handle' src/main/runtime/orchestration -g '*.ts' -g '!**/*.test.ts'
printf '%s\n' '--- gate creation implementation ---'
rg -n -C12 'export function createGate|function createGate|INSERT INTO decision_gates' src/main/runtime/orchestration -g '*.ts' -g '!**/*.test.ts'

Repository: stablyai/orca

Length of output: 41471


🏁 Script executed:

#!/bin/bash
set -o pipefail
printf '%s\n' '--- question-thread backfill call and ordering ---'
rg -n -C8 'backfillLegacyQuestionThreads|asker_handle' src/main/runtime/orchestration/db/schema src/main/runtime/orchestration/db -g '*.ts' -g '!**/*.test.ts'
printf '%s\n' '--- question-thread consumers and writers ---'
rg -n -C8 'asker_handle|question_threads' src/main/runtime/orchestration/db src/main/runtime/rpc src/main/runtime/orchestration -g '*.ts' -g '!**/*.test.ts' | head -n 700
printf '%s\n' '--- migration calls around question backfill and v43 ---'
rg -n -C10 'backfillLegacy|migrateV20|migrateV43|migrateV41' src/main/runtime/orchestration/db/schema -g '*.ts'

Repository: stablyai/orca

Length of output: 41987


Migrate legacy addresses in question_threads.asker_handle.

migrateV43 does not update question_threads.asker_handle. When an older database is replayed, backfillLegacyQuestionThreads copies messages.from_handle into this column before v43 runs. A legacy session:<id> sender can therefore remain in the question thread. legacy-reply-operation.ts uses that value as the reply recipient, so the reply can target the obsolete address.

Suggested fix
   ['messages', 'from_handle'],
   ['messages', 'to_handle'],
-  ['coordinator_runs', 'coordinator_handle']
+  ['coordinator_runs', 'coordinator_handle'],
+  ['question_threads', 'asker_handle']
 ] as const
🧰 Tools
🪛 OpenGrep (1.30.0)

[ERROR] 35-35: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)


[ERROR] 38-41: Dynamic command passed to child_process.exec/execSync. Use child_process.execFile or spawn with an argument array instead.

(coderabbit.command-injection.exec-js)

@greptile-apps

greptile-apps Bot commented Oct 1, 2026

Copy link
Copy Markdown

Comments Outside Diff

These findings could not be posted inline.

  • P1 Old session addresses stop working src/main/runtime/rpc/methods/orchestration/messaging/session-recipient.ts:50 ▶

    Existing chats and older clients can still supply the session:<id> address they were given. This check no longer recognizes that spelling, so a send treats it as a terminal handle and fails instead of reaching the chat or its Run mailbox. Caller flags using the old spelling are also rejected rather than respelled. The database migration updates stored addresses, but cannot update addresses supplied later by clients.

@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 is commit cec36b3022 ("refuse session:<id> input again; stored addresses still migrate once"), which reverses the read-only legacy aliases added in 809d6235 while keeping the one-time storage migration and the per-open trigger recreation.

  • Legacy input aliases removed — LEGACY_ORCA_SESSION_ADDRESS_PREFIX, respellLegacyOrcaSessionAddress, and isSpelledAsOrcaSessionAddress are gone; parseOrcaSessionAddress and the CLI's sessionAddressForHost accept only orca_session_id:<id>, so session:<id> falls through to the terminal-handle path and is refused (terminal_not_found). Grep confirms no remaining references to the removed helpers.
  • Storage migration retained — migrateV43 still respells every stored session:<id> once, so pre-upgrade mail, Run routing, replies, and remembered coordinator handles stay reachable at the current spelling.
  • Tests retargeted — the alias acceptance/reach tests are replaced by refusal assertions in the CLI, recipient, migration, and codec suites, with the provider-id refusal text pinned exactly. The four affected suites run clean locally (178 passed, 4 skipped).

ℹ️ Rollback-written session:<id> rows are now hard-refused, not aliased

The documented rollback gap changes character once the input alias is gone. If a user rolls back to a v1.4.214–v1.4.218 build that writes new session:<id> addresses, a later upgrade skips them (user_version is already 43), and --to session:<id> now refuses them as an unknown terminal instead of reaching the recipient through the alias 809d6235 briefly provided. This is a scope confirmation rather than a request — the PR body already defers the rewrite-on-every-open fix — but since the alias softened exactly this case, it is worth confirming the harder behavior is intended.

Technical details
# Rollback-written legacy rows become unreachable via direct `--to` input

## Affected sites
- `src/main/runtime/orchestration/db/schema/migrate-v43.ts:26-29` — `if (current >= 43) return`, so a v43 DB never re-runs the rewrite.
- `src/main/runtime/rpc/methods/orchestration/messaging/session-recipient.ts:50` — `session:<id>` no longer matches the current prefix, so it resolves as a terminal handle and is refused.

## Required outcome
- Confirm the no-alias contract is intended even when a rollback has written fresh `session:<id>` rows that the one-time migration cannot see. If not, either re-run the rewrite on open or retain a read-only alias.

## Open questions for the human
- Is the hardening acceptable, or does the rollback path need the migration to run on every open (as the PR body notes would close the gap)?

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

@brennanb2025

Copy link
Copy Markdown
Contributor Author

Review status: ready for your call (not merged)

The problem. Agents address each other by the Orca session ID, spelled session:<id>. In Orca, "session" alone also means the Claude/Codex session ID, Session History and terminal sessions, so an agent or user can easily paste the wrong ID. The database already calls this identity orca_session_id.

What changes for you. Agents now see and type orca_session_id:<id> everywhere: --to, --from, mail headers, and the refusal you get for passing a Claude/Codex session ID ("This session's Orca session ID is orca_session_id:; …"). Chats you created before upgrading stay reachable: a one-time migration (database schema 42 → 43) rewrites the stored old addresses, so unread mail, mail for the Runs they coordinate, and replies to their old mail still reach them. Typing the old session:<id> no longer works. It gets the same generic "no live pane or durable Run/Dispatch mailbox" refusal as any unknown address, with no special case. Terminal agents and term_ handles are unchanged.

What the review changed (head now cec36b3):

  • Scope, per your decisions: a one-time v43 migration of stored session:<id> addresses in every column that can hold one: message sender/recipient, remembered Run coordinator addresses, pointer operation keys, and coordinator loop handles. No input alias. An alias was briefly added and then removed when you ruled it out.
  • The two "provider session ID" refusals now hand back the same orca_session_id:<id> form, and the tests assert their exact text.
  • Hardened the trigger regression test so a future prefix change can't make it pass vacuously.
  • Brought the branch up to date with main (including test: stop main failing on three tests the store and close-cause changes crossed #24231, which fixed main's red typecheck), and corrected the PR body so it matches the code.

Verified:

  • 2 review rounds, 3 + 2 independent reviewers, plus 2 readiness-checklist passes: no P0–P2 findings left open.
  • The trigger rebuild on every database open (still needed for the trigger body) was checked against fresh, pre-v42, version-skew-replay and downgrade databases.
  • The migration's column list was checked against the old build's writers. Each part was ablated, and the matching tests fail without it.
  • Cost: about 0.15 s per 100k messages, run once.
  • Local: tc:node clean; broad vitest 612 files, 7027 tests, 0 failed.
  • CI on cec36b3: 23 pass, 0 failing.
  • Real app on the M4Air with real Claude and an isolated profile:
    • Fresh profile: new address delivers, the refusal text is right, terminal sends are unchanged.
    • Upgrade (chats created on the pre-rename build, relaunched on this PR, same profile): old unread mail readable, old spelling refused with nothing delivered, new spelling delivers, nothing printed after the upgrade says session:. Screenshots are in the PR body.

Not verified / deferred:

  • Opening a migrated database with a real older release binary (rollback): only code reading and the downgrade test cover it. After a rollback, the older build can't see mail the migration already respelled until you upgrade again.
  • Upgrade → rollback to v1.4.214–218 → upgrade again: mail the rollback build stored at session:<id> is not migrated, because the step is one-time. Re-running the rewrite on every open would close this.
  • Cached request replies (mutation receipts) can still echo the old spelling for a retried request ID. They are short-lived and not rewritten.
  • Wording nits: mixed "Orca session id" / "agent session" casing in other refusals.
  • Not caused by this PR, seen in QA: a reply to an agent that now coordinates a Run is silently filed into that Run's mailbox, and Run mail waits behind an earlier unacknowledged delivery. Both behave identically without the upgrade.
  • Merge order: open PRs Add durable per-message notification opt-out for orchestration send #23424 and feat(team): standing agent teams with an office view #23654 also add a migrate-v43.ts. Whichever lands second must renumber; git will flag the conflict.

@brennanb2025
brennanb2025 merged commit e42c0b3 into main Oct 1, 2026
40 checks passed
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