Skip to content
15 changes: 9 additions & 6 deletions src/cli/orchestration-session-caller-cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ describe.each(CALLER_VERBS)('orchestration $command run as an agent session', (v
}
)

it.runIf(verb.callerFlag !== undefined).each([`session:${SESSION}`, SESSION])(
it.runIf(verb.callerFlag !== undefined).each([`orca_session_id:${SESSION}`, SESSION])(
'accepts a caller flag that restates the session (%s)',
async (restated) => {
await invoke(verb.command, flagMap({ ...verb.flags, [verb.callerFlag ?? 'from']: restated }))
Expand All @@ -326,11 +326,14 @@ describe.each(CALLER_VERBS.filter((verb) => verb.callerFlag !== undefined))(

it('sends the address it had before the clear for the host to place, instead of refusing it', async () => {
// The chat's address is its lineage root's, which only the host's session records know.
const flags = flagMap({ ...verb.flags, [verb.callerFlag ?? 'from']: `session:${ROOT}` })
const flags = flagMap({
...verb.flags,
[verb.callerFlag ?? 'from']: `orca_session_id:${ROOT}`
})
await invoke(verb.command, flags)

const [params] = callsTo(verb.method)
expect(params?.[verb.callerParam]).toBe(`session:${ROOT}`)
expect(params?.[verb.callerParam]).toBe(`orca_session_id:${ROOT}`)
expect(params?.terminalPaneKey).toBeUndefined()
expect(params?.senderPaneKey).toBeUndefined()
expect(getTerminalHandleMock).not.toHaveBeenCalled()
Expand All @@ -357,11 +360,11 @@ describe.each([
})

it('accepts a --from that restates the session', async () => {
await invoke(command, flagMap({ run: 'run_1', from: `session:${SESSION}` }))
await invoke(command, flagMap({ run: 'run_1', from: `orca_session_id:${SESSION}` }))
expect(callsTo(method)[0]?.[callerParam]).toBeUndefined()
})

it.each([`session:${ROOT}`, 'session:9d4c1b2a-3e5f-4a6b-8c7d-0e1f2a3b4c5d'])(
it.each([`orca_session_id:${ROOT}`, 'orca_session_id:9d4c1b2a-3e5f-4a6b-8c7d-0e1f2a3b4c5d'])(
'sends a session address it cannot place (%s) for the host to check, never dropping it',
async (declared) => {
// A /clear-ed chat's root is accepted by the host; anyone else is refused there, before any
Expand Down Expand Up @@ -441,7 +444,7 @@ describe('the identity a session presents', () => {
return callsTo('dispatchShow')[0]?.from
}
asSessionWithInheritedPane()
expect(await preview({})).toBe(`session:${SESSION}`)
expect(await preview({})).toBe(`orca_session_id:${SESSION}`)
// Not a caller flag: it names the text to preview, so it is never fenced.
expect(await preview({ from: 'term_sibling' })).toBe('term_sibling')
setEnv({ ORCA_AGENT_SESSION_ID: SESSION, ORCA_TERMINAL_HANDLE: 'structworker_self' })
Expand Down
3 changes: 2 additions & 1 deletion src/main/runtime/orchestration/db/contract-constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export const CURRENT_CONTRACT_VERSION = ORCHESTRATION_CONTRACT_VERSION
// Schema versions: v2 'heartbeat'+last_heartbeat_at, v3 delivered_at, v4 task-creator terminal, v5 task_title/display_name, v6 pane identity, v7 lightweight Runs, v8 crash-safe Run deliveries, v9 durable question threads, v10 Dispatch capabilities, v11 durable mutation receipts, v12 composed worker state, v18 post-v6 version-skew repair, v19 adopted legacy Runs and compatibility receipts, v20 legacy question backfill, v21 legacy scheduler-loss provenance, v22 dispatch assignee lookup, v23 worker terminal resource ownership, v24 creator-incarnation authority, v25 active Dispatch handle lookup, v26 indexed mutation receipt capacity, v27 durable federation acknowledgments, v28 durable local mutation caller identity, v31 dispatch/resource identity links, v32 bounded worker-terminal recovery metadata, v33 durable mailbox pointer Enter state, v34 role-addressed mailbox deliveries, v35 mailbox delivery default and index-predicate repair, v36 dispatch mailbox consumer generation, v37 recorded dispatch creator identity, v39 structured session journal archives.
// v41: derive outstanding deliveries from unread messages.
// v42: structured-session Orca session id columns.
export const SCHEMA_VERSION = 42
// v43: stored `session:<id>` addresses respelled `orca_session_id:<id>`.
export const SCHEMA_VERSION = 43
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ORCA_SESSION_ADDRESS_PREFIX } from '../../../../shared/orca-session-address'

/**
* The `session:<id>` address of a bare Orca session id column or expression, NULL when it is NULL.
* The `orca_session_id:<id>` address of a bare Orca session id column or expression, NULL when it is NULL.
* The only way SQL compares a stored id with a mail address: the id side is formatted, never the
* address side stripped, so a handle or `run:` address can never equal a bare id.
*/
Expand Down
2 changes: 2 additions & 0 deletions src/main/runtime/orchestration/db/orchestration-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { backfillFederatedStubHomeRuns } from './federation/federated-stub-home-
import type { OrchestrationDbMethods } from './orchestration-db-methods'
import {
createCoordinatorMailRoutingTrigger,
createRunCoordinatorAddressTriggers,
rememberCurrentRunCoordinatorHandles
} from './runs/run-coordinator-mail-routing'
import { createTables } from './schema/create-tables'
Expand All @@ -30,6 +31,7 @@ class OrchestrationDbCore {
this.db.pragma('busy_timeout = 5000')
createTables.call(this as unknown as OrchestrationDb)
migrate.call(this as unknown as OrchestrationDb)
createRunCoordinatorAddressTriggers(this.db)
backfillFederatedStubHomeRuns(this.db)
backfillStructuredWorkerOrcaSessionIds(this.db)
createCoordinatorMailRoutingTrigger.call(this as unknown as OrchestrationDb)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type Database from '../../../../sqlite/sync-database'
import type { OrchestrationDb } from '../orchestration-db'
import { currentRunCoordinatorSessionAddressSql } from './run-coordinator-orca-session'

Expand All @@ -24,8 +25,51 @@ export function rememberRunCoordinatorHandle(
}

const CURRENT_COORDINATOR_SESSION_ADDRESS_SQL = currentRunCoordinatorSessionAddressSql('runs')
const NEW_COORDINATOR_SESSION_ADDRESS_SQL = currentRunCoordinatorSessionAddressSql('NEW')

// Every address the coordinator has, its handle and its session address, as the migrate-v42 triggers.
/**
* Remembers every address the coordinator has, as bindRun does: its handle and its session address,
* so every reader of this cache matches either by string equality and neither takes precedence.
* Recreated on every open, after migrate: the address prefix is compiled into the trigger body,
* so a trigger stamped once would keep an older build's prefix. The static createTables SQL stays
* handle-only (see create-core-tables-sql), since it must compile against a pre-v42 runs table.
*/
export function createRunCoordinatorAddressTriggers(db: Database.Database): void {
db.exec('BEGIN IMMEDIATE')
try {
db.exec(`
DROP TRIGGER IF EXISTS trg_runs_remember_coordinator_insert;
DROP TRIGGER IF EXISTS trg_runs_remember_coordinator_update;
Comment thread
greptile-apps[bot] marked this conversation as resolved.
CREATE TRIGGER trg_runs_remember_coordinator_insert
AFTER INSERT ON runs
WHEN NEW.legacy = 0
BEGIN
INSERT OR IGNORE INTO run_coordinator_handles (run_id, terminal_handle)
SELECT NEW.id, NEW.coordinator_handle WHERE NEW.coordinator_handle IS NOT NULL;
INSERT OR IGNORE INTO run_coordinator_handles (run_id, terminal_handle)
SELECT NEW.id, ${NEW_COORDINATOR_SESSION_ADDRESS_SQL}
WHERE ${NEW_COORDINATOR_SESSION_ADDRESS_SQL} IS NOT NULL;
END;
CREATE TRIGGER trg_runs_remember_coordinator_update
AFTER UPDATE OF coordinator_handle, coordinator_orca_session_id,
coordinator_orca_session_id_generation ON runs
WHEN NEW.legacy = 0
BEGIN
INSERT OR IGNORE INTO run_coordinator_handles (run_id, terminal_handle)
SELECT NEW.id, NEW.coordinator_handle WHERE NEW.coordinator_handle IS NOT NULL;
INSERT OR IGNORE INTO run_coordinator_handles (run_id, terminal_handle)
SELECT NEW.id, ${NEW_COORDINATOR_SESSION_ADDRESS_SQL}
WHERE ${NEW_COORDINATOR_SESSION_ADDRESS_SQL} IS NOT NULL;
END;
`)
db.exec('COMMIT')
} catch (error) {
db.exec('ROLLBACK')
throw error
}
}

// Every address the coordinator has, its handle and its session address, as the triggers above.
export function rememberCurrentRunCoordinatorHandles(this: OrchestrationDb): void {
this.db.exec(`
INSERT OR IGNORE INTO run_coordinator_handles (run_id, terminal_handle)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function currentRunCoordinatorOrcaSessionIdSql(row: string): string {
THEN ${row}.coordinator_orca_session_id END)`
}

/** The coordinator's `session:<id>` address in SQL; NULL when it has no current Orca session id. */
/** The coordinator's `orca_session_id:<id>` address in SQL; NULL when it has no current Orca session id. */
export function currentRunCoordinatorSessionAddressSql(row: string): string {
return orcaSessionAddressSql(currentRunCoordinatorOrcaSessionIdSql(row))
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ CREATE TABLE IF NOT EXISTS run_coordinator_handles (
CREATE INDEX IF NOT EXISTS idx_run_coordinator_handles_handle
ON run_coordinator_handles(terminal_handle, run_id);

-- Handle-only on purpose; migrate-v42 replaces both triggers with a form that also remembers the
-- coordinator's session address. This SQL runs before migrate on every open, so it
-- Handle-only on purpose; createRunCoordinatorAddressTriggers replaces both on every open with a
-- form that also remembers the coordinator's session address. This SQL runs before migrate, so it
-- must compile against a pre-v42 runs table: a trigger naming coordinator_orca_session_id there makes
-- the next INSERT INTO runs fail to prepare mid-migration.
CREATE TRIGGER IF NOT EXISTS trg_runs_remember_coordinator_insert
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ CREATE TABLE IF NOT EXISTS dispatch_contexts (
assignee_handle TEXT,
assignee_pane_key TEXT,
-- Bare Orca session id the agent is addressed by, when it has one (today only structured
-- sessions); for a /clear'd chat, its lineage root's. Not its session:<id> address.
-- sessions); for a /clear'd chat, its lineage root's. Not its orca_session_id:<id> address.
assignee_orca_session_id TEXT,
capability_hash TEXT,
process_incarnation TEXT,
Expand Down
36 changes: 2 additions & 34 deletions src/main/runtime/orchestration/db/schema/migrate-v42.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { OrchestrationDb } from '../orchestration-db'
import { currentRunCoordinatorSessionAddressSql } from '../runs/run-coordinator-orca-session'

const ORCA_SESSION_ID_COLUMNS = [
['runs', 'coordinator_orca_session_id', 'TEXT'],
Expand All @@ -8,15 +7,14 @@ const ORCA_SESSION_ID_COLUMNS = [
['dispatch_contexts', 'creator_orca_session_id', 'TEXT']
] as const

const NEW_COORDINATOR_SESSION_ADDRESS_SQL = currentRunCoordinatorSessionAddressSql('NEW')

/**
* Orca session id columns (bare ids, see orca-session-address) on a Run's coordinator and a
* Dispatch's assignee and creator: the Orca session id the agent is addressed by, when it has one
* (today only structured sessions); for a `/clear`ed chat, its lineage root's, not the live one.
* Existing structured-worker rows get their id from `backfillStructuredWorkerOrcaSessionIds`, which
* runs after migrate on every open. A coordinator's id carries the consumer generation it was
* written at and counts only at that generation.
* written at and counts only at that generation. The triggers that remember a coordinator's session
* address are `createRunCoordinatorAddressTriggers`, recreated on every open after migrate.
*
* Dev databases stamped v42 by earlier builds hold `*_principal` or `*_actor` columns instead. They
* are unsupported: the version-skew probe finds a column missing and replays the chain, which adds
Expand All @@ -39,34 +37,4 @@ export function migrateV42(this: OrchestrationDb, current: number): void {
CREATE INDEX IF NOT EXISTS idx_dispatch_assignee_orca_session_id
ON dispatch_contexts(assignee_orca_session_id) WHERE assignee_orca_session_id IS NOT NULL;
`)
// Every address the coordinator has is remembered, as bindRun remembers them: its handle and its
// session address, `session:<id>`, so every reader of this cache matches either by string
// equality, unchanged, and neither address takes precedence. This step owns the trigger form: the
// static createTables SQL must stay handle-only (see create-core-tables-sql), and CREATE TRIGGER IF
// NOT EXISTS never replaces an existing database's triggers, so they are dropped and recreated by name.
this.db.exec(`
DROP TRIGGER IF EXISTS trg_runs_remember_coordinator_insert;
DROP TRIGGER IF EXISTS trg_runs_remember_coordinator_update;
CREATE TRIGGER trg_runs_remember_coordinator_insert
AFTER INSERT ON runs
WHEN NEW.legacy = 0
BEGIN
INSERT OR IGNORE INTO run_coordinator_handles (run_id, terminal_handle)
SELECT NEW.id, NEW.coordinator_handle WHERE NEW.coordinator_handle IS NOT NULL;
INSERT OR IGNORE INTO run_coordinator_handles (run_id, terminal_handle)
SELECT NEW.id, ${NEW_COORDINATOR_SESSION_ADDRESS_SQL}
WHERE ${NEW_COORDINATOR_SESSION_ADDRESS_SQL} IS NOT NULL;
END;
CREATE TRIGGER trg_runs_remember_coordinator_update
AFTER UPDATE OF coordinator_handle, coordinator_orca_session_id,
coordinator_orca_session_id_generation ON runs
WHEN NEW.legacy = 0
BEGIN
INSERT OR IGNORE INTO run_coordinator_handles (run_id, terminal_handle)
SELECT NEW.id, NEW.coordinator_handle WHERE NEW.coordinator_handle IS NOT NULL;
INSERT OR IGNORE INTO run_coordinator_handles (run_id, terminal_handle)
SELECT NEW.id, ${NEW_COORDINATOR_SESSION_ADDRESS_SQL}
WHERE ${NEW_COORDINATOR_SESSION_ADDRESS_SQL} IS NOT NULL;
END;
`)
}
43 changes: 43 additions & 0 deletions src/main/runtime/orchestration/db/schema/migrate-v43.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { OrchestrationDb } from '../orchestration-db'

// Frozen literals, never the live prefix constant: a migration rewrites what that build wrote.
const LEGACY_PREFIX = 'session:'
const CURRENT_PREFIX = 'orca_session_id:'

// Every column an earlier build wrote a session's mail address into. Bodies, subjects and payloads
// are text, not addresses, and are left as written.
const ADDRESS_COLUMNS = [
['messages', 'from_handle'],
['messages', 'to_handle'],
['coordinator_runs', 'coordinator_handle']
] as const

// Address columns in a key: where the current spelling already has its row, the legacy one is a duplicate.
const KEYED_ADDRESS_COLUMNS = [
['run_coordinator_handles', 'terminal_handle'],
['structured_pointer_operations', 'mailbox_handle']
] as const

/**
* Earlier builds addressed an Orca session as `session:<id>`. Respells every stored one as
* `orca_session_id:<id>`, so a chat still reads mail sent before the upgrade, a Run still routes
* mail to the chat that coordinated it, and a reply to old mail reaches its sender.
*/
export function migrateV43(this: OrchestrationDb, current: number): void {
if (current >= 43) {
return
}
const isLegacy = (column: string): string =>
`substr(${column}, 1, ${LEGACY_PREFIX.length}) = '${LEGACY_PREFIX}'`
const respelled = (column: string): string =>
`'${CURRENT_PREFIX}' || substr(${column}, ${LEGACY_PREFIX.length + 1})`
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)};
`)
}
Comment on lines +34 to +42

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)

}
2 changes: 2 additions & 0 deletions src/main/runtime/orchestration/db/schema/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { migrateV39 } from './migrate-v39'
import { migrateV40 } from './migrate-v40'
import { DERIVED_DELIVERY_SCHEMA_SQL, migrateV41 } from './migrate-v41'
import { migrateV42 } from './migrate-v42'
import { migrateV43 } from './migrate-v43'

// Why: CREATE TABLE IF NOT EXISTS won't alter existing DBs; migrate in a txn that bumps user_version only on success (atomic all-or-nothing).
export function migrate(this: OrchestrationDb): void {
Expand Down Expand Up @@ -40,6 +41,7 @@ export function migrate(this: OrchestrationDb): void {
// Why: older steps recreate the unique index; v41 must run after them.
migrateV41.call(this, current)
migrateV42.call(this, current)
migrateV43.call(this, current)
this.createMailboxDeliveryIndexesIfPossible()
// Why: rebuild steps above RENAME the table, which SQLite refuses while a view names it.
this.db.exec(DERIVED_DELIVERY_SCHEMA_SQL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { formatOrcaSessionAddress, type OrcaSessionId } from '../../../shared/or
* Who an orchestration caller is, as Run binding and mail routing match it.
*
* A PTY agent is its terminal: a handle and a pane key, no Orca session id. An agent that is a
* structured session is its Orca session id, addressed as `session:<id>`; a structured worker also
* structured session is its Orca session id, addressed as `orca_session_id:<id>`; a structured worker also
* has the handle and pane key it was minted, and an ordinary chat has neither. Methods pass this
* through whole and never branch on which fields are set; the lookups below own that.
*/
Expand All @@ -16,7 +16,7 @@ export type OrchestrationCallerIdentity = Readonly<{
address: string
terminalHandle: string | null
paneKey: string | null
/** The bare Orca session id the caller is addressed by; mail spells it `session:<id>`. */
/** The bare Orca session id the caller is addressed by; mail spells it `orca_session_id:<id>`. */
orcaSessionId: OrcaSessionId | null
}>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import {
ORCA_SESSION_ADDRESS_PREFIX,
formatOrcaSessionAddress,
parseOrcaSessionAddress
} from '../../../shared/orca-session-address'
Expand Down Expand Up @@ -70,7 +71,7 @@ describe('Run coordinator Orca session address', () => {
db = new OrchestrationDb(':memory:')
insertSessionCoordinatedRun(db, 'run_session')

// The column holds the bare id; only the remembered address carries the session: prefix.
// The column holds the bare id; only the remembered address carries the orca_session_id: prefix.
expect(db.getRunRaw('run_session')?.coordinator_orca_session_id).toBe(CHAT_SESSION_ID)
expect(addressesFor(db, 'run_session')).toEqual([CHAT_ADDRESS])
expect(parseOrcaSessionAddress(addressesFor(db, 'run_session')[0])).toBe(CHAT_SESSION_ID)
Expand Down Expand Up @@ -109,6 +110,31 @@ describe('Run coordinator Orca session address', () => {
expect(addressesFor(db, 'run_unbound')).toEqual([CHAT_ADDRESS])
})

it('recreates on open a coordinator trigger an older build compiled with another address prefix', () => {
const path = tempDbPath(tempRoots)
db = new OrchestrationDb(path)
const triggers = db.db
.prepare(`SELECT sql FROM sqlite_master WHERE name LIKE 'trg_runs_remember_coordinator_%'`)
.all()
.map((row) => String(row.sql))
expect(triggers).toHaveLength(2)
const olderBuild = triggers.map((sql) =>
sql.replaceAll(`'${ORCA_SESSION_ADDRESS_PREFIX}'`, "'session:'")
)
// Otherwise the reopen below would find current triggers and prove nothing.
olderBuild.forEach((sql, index) => expect(sql).not.toBe(triggers[index]))
db.db.exec(`
DROP TRIGGER trg_runs_remember_coordinator_insert;
DROP TRIGGER trg_runs_remember_coordinator_update;
${olderBuild.map((sql) => `${sql};`).join('\n')}
`)
db.close()

db = new OrchestrationDb(path)
insertSessionCoordinatedRun(db, 'run_after_upgrade')
expect(addressesFor(db, 'run_after_upgrade')).toEqual([CHAT_ADDRESS])
})

it('remembers a PTY coordinator written by insert, update and refill by exactly its handle', () => {
const path = tempDbPath(tempRoots)
db = new OrchestrationDb(path)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* A structured agent session as a mail address: `session:<id>`, the Orca-minted id every agent is
* A structured agent session as a mail address: `orca_session_id:<id>`, the Orca-minted id every agent is
* told is its public address. Recipient routing and pointer delivery both read these rules off the
* durable session record, so the two can never disagree about which sessions mail can reach.
*
Expand Down
Loading
Loading