feat(agent-status): add the pane agent identity resolver by brennanb2025 · Pull Request #16157 · stablyai/orca · GitHub
Skip to content

feat(agent-status): add the pane agent identity resolver - #16157

Merged
brennanb2025 merged 2 commits into
mainfrom
brennanb2025/agent-run-generation
Aug 24, 2026
Merged

brennanb2025 merged 2 commits into
mainfrom
brennanb2025/agent-run-generation

Conversation

@brennanb2025

@brennanb2025 brennanb2025 commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​278 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​278
Prod 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​163 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​163

ELI5

Four different places in Orca each work out "which agent is in this pane" their own way, and they disagree with each other. Two of them read the terminal title before the launch record — so a string Orca parsed outranks a fact Orca owns.

This adds the one function that answers it. Nothing calls it yet.

What Changed

resolvePaneAgentIdentity ranks evidence by how directly it observes the process:

live-hook > process > launch > completed-hook > sleeping-session > sibling > title

Two rules, and only one of them is an ordering:

  1. A display title is last. It is a decoration channel — anyone can type an agent's name into a task description.
  2. Every observation carries the runId of the agent run it describes. Evidence from a superseded run is ineligible, not merely outranked.

Why rule 2 exists

Rule 1 alone cannot fix this, and it is worth being precise about why.

A completed hook naming A plus a title naming B is either:

  • the reported bug — nothing new started, the hook is right and the title is stale; or
  • a legitimate pane reclaim — the pane was reused, and B really is there now.

Identical signals. Opposite correct answers. No precedence order can separate them, which is what an earlier review of this problem established.

Run ids make them different facts: in the bug both belong to the current run, and in the reclaim the hook belongs to a previous one. That pair ships as an explicit assertion — the same evidence resolving two ways given only the run ids.

A run advances only on positive evidence that a new agent started: an accepted launch, a recognized command at a shell prompt, a host-confirmed foreground change, a new provider session. Never because a title changed, and never on transport loss.

Mixed-version behavior

runId: undefined is eligible. Absence means "this peer does not publish run ids", not "this is stale" — treating unknown as superseded would blank every row arriving from an older host. currentRunId: undefined disables run filtering entirely.

Sibling evidence is opt-in (allowSibling), so pane-scoped consumers — routing, native chat, keyboard encoding — cannot inherit a neighbouring pane's agent. Tab-level surfaces opt in.

Returns null rather than guessing. An absent icon is recoverable; at the action surfaces (orchestration routing, mailbox delivery, prompt-cache timers) a wrong one is a misdelivery.

Testing

18 assertions: title-is-last against every stronger source, the bug/reclaim pair, mixed-version absence in both directions, sibling scoping, input-order independence.

Verified non-vacuous by mutation, not by reading:

  • reversing the authority order → 10 of 18 fail
  • removing the run filter → 3 of 18 fail
  • restored → 18 pass

typecheck, oxlint, oxfmt clean.

Linked Issue

Part of the agent-identity rearchitecture tracked by #9236. No single issue is closed by this PR on its own — it is one stage of a staged change, and behavior only moves once consumers migrate.

Visual Proof

N/A — this module has zero importers and is a pure function, so there is no user-visible behavior to capture. Screenshotting it would imply coverage that does not exist. The manual-validation arm was deliberately skipped for that reason.

Testing

pnpm exec vitest run --config config/vitest.config.ts src/shared/pane-agent-identity-resolver.test.ts — 18 passed.

Verified non-vacuous by mutation rather than by reading: reversing the authority order fails 10 of 18 assertions, and removing the run filter fails 3. Restored, 18 pass.

  • I manually tested these changes locally
  • Automated tests added/updated

Agent skill upstream boundary

  • Not applicable.

Notes

  • Security: no new input is trusted. The change reduces what a terminal title is allowed to decide.
  • Cross-platform: pure TypeScript in src/shared, no platform branches, no paths, no shell.
  • Remote SSH / backwards compatibility: no wire change in this PR. Nothing new is published, and nothing existing is read differently, because no consumer calls it yet.
  • Mobile: unaffected — no importers.
  • Performance: pure function over a short string, called by nobody yet; no allocation on any hot path.

Checklist

  • This PR is small and focused
  • I explained what changed and why (including ELI5)
  • Before/after screenshots or videos attached for UI changes, or N/A with reason
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered
  • typecheck, oxlint, oxfmt and focused tests pass locally; CI covers the rest

@coderabbitai

coderabbitai Bot commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Added a shared resolvePaneAgentIdentity resolver with ranked evidence sources. The resolver filters sibling and below-floor evidence, excludes superseded same-authority runs, preserves unknown and cross-authority run evidence, and reports ambiguity and superseded-source diagnostics. Added exported input, evidence, result, run-key, and source types. Added tests for precedence, fallbacks, run handling, mixed-version behavior, sibling opt-in, null results, authority scoping, ambiguity, minimum-source filtering, diagnostics, and input-order independence.

Merge Risk: 🔵 Low · up to ce834

The new resolver’s diagnostic output can vary with evidence ordering even when the selected agent identity is unchanged. Because the module has no consumers yet, this is a bounded, non-blocking risk that should be addressed with deterministic ordering before dependent code adopts it.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the added pane agent identity resolver.
Description check ✅ Passed The description covers the required sections, explains the change and rationale, links the tracking issue, documents testing, and addresses compatibility and scope.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 2 files.
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
📝 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.

@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

🧹 Nitpick comments (1)
src/shared/pane-agent-identity-resolver.ts (1)

3-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reduce the implementation-history comment.

Keep the durable resolver contract in this file. Move the detailed history of prior ladders to design documentation or an issue.

As per coding guidelines: “Comments must be concise, non-obvious, and brief—prefer one line; do not explain obvious behavior or walk through code.”

Source: Coding guidelines


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 878f80a2-c183-4c35-90e8-a4ce391fe951

📥 Commits

Reviewing files that changed from the base of the PR and between ab3b1d0 and c5f9a63.

📒 Files selected for processing (2)
  • src/shared/pane-agent-identity-resolver.test.ts
  • src/shared/pane-agent-identity-resolver.ts

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

Comment thread src/shared/pane-agent-identity-resolver.ts Outdated
@brennanb2025

Copy link
Copy Markdown
Contributor Author

QA evidence (captured against PR #16237, brennanb2025/agent-group-routing-identity, stacked on this resolver; #16157 itself has zero importers and renders no behavior). The rendered Orca terminal ran the real group-routing tests: @claude does not route to a Codex pane whose task title mentions Claude, while agent-identity routing still reaches real Claude panes.

qa-groups-routing-exact.png

@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-run-generation branch from c5f9a63 to 8ac06a2 Compare August 24, 2026 18:30
@brennanb2025

Copy link
Copy Markdown
Contributor Author

Codex review loop - status

Reviewed head: 8ac06a2540

Verdict: no release-blocking defects found. The resolver’s process/live-hook/launch/completed-hook ordering is defensible, run generations correctly separate stale hooks from pane reclaim, unknown run IDs preserve mixed-version rows, and the deliberately unwired module leaves migration behavior attributable. The residual design risk is that title fallback remains available to action consumers; the stacked caller fails closed on null, and a future trusted-source floor would make misuse impossible.

Checks: resolver + observation tests 26 passed; mutation checks were non-vacuous (reversed rank failed 10/18, run filter removed failed 3/18); pnpm typecheck and targeted oxlint passed. Electron validation was run against stacked PR #16237: the real rendered Orca terminal showed @claude does not route to a Codex pane whose task title mentions Claude, while identity routing reaches real Claude panes; screenshot evidence is in the preceding QA comment.

No additional implementation commits were needed after rebasing onto current main; final branch head is 8ac06a2540.

Four ladders answer "which agent is in this pane" independently — the tab icon, the
open-tab/search occupant, the sidebar title rows, and the sidebar hook-row fallback — and they
disagree. Two consult the terminal title before the launch record, so a string Orca parsed
outranks a fact Orca owns.

resolvePaneAgentIdentity is the single ranked answer. Two rules, one of which is not an ordering:

1. Evidence is ranked by how directly it observes the process; a display title is last.
2. Each observation carries the runId of the agent run it describes. Evidence from a superseded
   run is INELIGIBLE, not merely outranked.

Rule 2 is the part reordering could never supply. A completed hook naming A plus a title naming
B is either a bug (hook right, title stale) or a legitimate pane reclaim (title right) —
identical signals, opposite correct answers. Run ids make them different facts: in the bug both
belong to the current run; in the reclaim the hook belongs to a previous one. That pair ships as
a test asserting the two produce opposite answers from the same evidence.

Missing run ids are treated as eligible. Absence means "this peer does not publish them", not
"this is stale", so an old host's rows are never blanked. Sibling evidence is opt-in so
pane-scoped consumers cannot inherit another pane's agent.

No consumer imports this yet; each migrates separately with its own evidence.

Verified non-vacuous: reversing the authority order fails 10 of 18 assertions and removing the
run filter fails 3.
**Duplicate evidence of one source resolved by array order.** `eligible.find(...)` returned the
first match, so two live hooks naming different agents were settled by input position — the exact
property this resolver exists to remove. The original order-independence test only used DISTINCT
sources, so it never exercised it. Conflicting same-class evidence now returns null with
`ambiguousAt`, and does NOT fall through to a weaker source: letting a title answer whenever two
hooks disagree is worse than saying nothing.

**A bare numeric runId collided across authority restarts.** `incarnation` is a total order only
within one `authorityId` (agent-status-observation.ts states this), and the id is regenerated per
authority instance, so a restarted host counting from its own floor would report `1` and match an
unrelated live run 1. The run key now carries its authority, and evidence from a DIFFERENT
authority is treated as incomparable — kept, like an absent key — rather than as stale.

**Title stayed reachable by consumers that authorize writes.** Ranking it last makes misuse
unlikely; `minimumSource` makes it impossible. An action consumer passes `'launch'` and weaker
evidence is dropped before ranking, so routing or delivery cannot name a target from a parsed
string even by reordering its inputs. Display surfaces omit it and are unaffected.

Also restores the generic agent-vocabulary parameter, which lives on the routing branch and was
lost when this branch was rebased.

Each fix is mutation-verified: first-match restored fails 3, ignoring authority fails 1, dropping
the floor fails 2. The authority test was itself vacuous on the first attempt — both sides used
`incarnation: 1`, so a resolver ignoring authority still passed on the numeric compare. It now uses
differing incarnations.

The remaining review finding, that `process > launch` has no freshness bound, is NOT fixed here:
it needs an observation timestamp the evidence type does not yet carry. Recorded rather than
silently dropped.
@brennanb2025
brennanb2025 force-pushed the brennanb2025/agent-run-generation branch from 8ac06a2 to ce83494 Compare August 24, 2026 21:41
@coderabbitai

coderabbitai Bot commented Aug 24, 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.

@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: Pro Plus

Run ID: 665fa7b6-0aa8-4da7-9fe7-318dd085854c

📥 Commits

Reviewing files that changed from the base of the PR and between b085e67 and ce83494.

📒 Files selected for processing (2)
  • src/shared/pane-agent-identity-resolver.test.ts
  • src/shared/pane-agent-identity-resolver.ts

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

Comment on lines +144 to +145
superseded.push(item.source)
return false

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make supersededSources deterministic.

Lines 144-145 append sources in input order. Reversing two stale evidence items changes the returned diagnostics, even when the resolved identity is unchanged. Return the diagnostics in SOURCE_RANK order and add a reversed-input stale-run test.

Proposed fix
   const eligible = input.evidence.filter((item) => {
     // ...
   })
+  const supersededSources = [...superseded].sort(
+    (left, right) => SOURCE_RANK.indexOf(left) - SOURCE_RANK.indexOf(right)
+  )
 
   for (const source of SOURCE_RANK) {
     // ...
-      return { agent: null, source: null, ambiguousAt: source, supersededSources: superseded }
+      return { agent: null, source: null, ambiguousAt: source, supersededSources }
     }
-    return { agent: matches[0].agent, source, supersededSources: superseded }
+    return { agent: matches[0].agent, source, supersededSources }
   }
-  return { agent: null, source: null, supersededSources: superseded }
+  return { agent: null, source: null, supersededSources }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change

brennanb2025 added a commit that referenced this pull request Aug 24, 2026
…r routing

Rebuilt on current main so this carries the hardened parser from #16148 and the corrected
resolver from #16157 (authority-scoped run keys, no order-dependent duplicate resolution).

Applies the resolver's new `minimumSource` floor at the publisher. What this publishes authorizes
an action — routing decides which real agent pane receives a message — so ranking title last is
not enough; the floor removes it from consideration entirely, and no amount of reordering by a
caller can bring it back.

The trade, stated because it is a real capability loss: a hook-less agent over SSH that Orca did
not launch, and whose foreground process the host cannot read, is no longer addressable by @agent.
Accepted because a message delivered into the wrong agent's prompt is unrecoverable while an
undelivered one is visible — the sender sees zero recipients. Whether real panes actually carry
launch/foreground evidence is the open question, and is what live validation must answer.
@brennanb2025
brennanb2025 merged commit 4f525f1 into main Aug 24, 2026
47 checks passed
paidaxingyo666 pushed a commit to paidaxingyo666/Manta that referenced this pull request Aug 25, 2026
)

* feat(agent-status): add the pane agent identity resolver

Four ladders answer "which agent is in this pane" independently — the tab icon, the
open-tab/search occupant, the sidebar title rows, and the sidebar hook-row fallback — and they
disagree. Two consult the terminal title before the launch record, so a string Orca parsed
outranks a fact Orca owns.

resolvePaneAgentIdentity is the single ranked answer. Two rules, one of which is not an ordering:

1. Evidence is ranked by how directly it observes the process; a display title is last.
2. Each observation carries the runId of the agent run it describes. Evidence from a superseded
   run is INELIGIBLE, not merely outranked.

Rule 2 is the part reordering could never supply. A completed hook naming A plus a title naming
B is either a bug (hook right, title stale) or a legitimate pane reclaim (title right) —
identical signals, opposite correct answers. Run ids make them different facts: in the bug both
belong to the current run; in the reclaim the hook belongs to a previous one. That pair ships as
a test asserting the two produce opposite answers from the same evidence.

Missing run ids are treated as eligible. Absence means "this peer does not publish them", not
"this is stale", so an old host's rows are never blanked. Sibling evidence is opt-in so
pane-scoped consumers cannot inherit another pane's agent.

No consumer imports this yet; each migrates separately with its own evidence.

Verified non-vacuous: reversing the authority order fails 10 of 18 assertions and removing the
run filter fails 3.

* fix(agent-status): close three resolver contract holes found in review

**Duplicate evidence of one source resolved by array order.** `eligible.find(...)` returned the
first match, so two live hooks naming different agents were settled by input position — the exact
property this resolver exists to remove. The original order-independence test only used DISTINCT
sources, so it never exercised it. Conflicting same-class evidence now returns null with
`ambiguousAt`, and does NOT fall through to a weaker source: letting a title answer whenever two
hooks disagree is worse than saying nothing.

**A bare numeric runId collided across authority restarts.** `incarnation` is a total order only
within one `authorityId` (agent-status-observation.ts states this), and the id is regenerated per
authority instance, so a restarted host counting from its own floor would report `1` and match an
unrelated live run 1. The run key now carries its authority, and evidence from a DIFFERENT
authority is treated as incomparable — kept, like an absent key — rather than as stale.

**Title stayed reachable by consumers that authorize writes.** Ranking it last makes misuse
unlikely; `minimumSource` makes it impossible. An action consumer passes `'launch'` and weaker
evidence is dropped before ranking, so routing or delivery cannot name a target from a parsed
string even by reordering its inputs. Display surfaces omit it and are unaffected.

Also restores the generic agent-vocabulary parameter, which lives on the routing branch and was
lost when this branch was rebased.

Each fix is mutation-verified: first-match restored fails 3, ignoring authority fails 1, dropping
the floor fails 2. The authority test was itself vacuous on the first attempt — both sides used
`incarnation: 1`, so a resolver ignoring authority still passed on the numeric compare. It now uses
differing incarnations.

The remaining review finding, that `process > launch` has no freshness bound, is NOT fixed here:
it needs an observation timestamp the evidence type does not yet carry. Recorded rather than
silently dropped.

(cherry picked from commit 4f525f1)
brennanb2025 added a commit that referenced this pull request Aug 26, 2026
* feat(agent-status): measure identity evidence before migrating any consumer

PR 1 of the identity migration. It changes no displayed or routed identity — it only measures.

Why measure first: the hierarchy shipped in #16148/#16157 has zero consumers, while ~31 sites still
derive identity independently. Every migration decision after this is currently a guess, including
the one that matters most — how often a real pane has no evidence at all. A live P0 reports "No
Claude status shown", and this design trades toward showing nothing when uncertain, so the blank
rate has to be a number before any surface moves.

- `pane-agent-identity-evidence.ts` — one assembler that gathers a pane's evidence, so consumers
  stop each inventing their own ladder.
- `pane-agent-identity-census.ts` — shadow-only counters keyed by host kind (native / wsl-host /
  wsl-distro / ssh / relay) and launch mode (typed / orca-launch / resume). Records a bitmask of
  which sources were present and whether the resolver returned null or ambiguous. No titles,
  prompts, paths, handles, or agent text.
- `pane-agent-identity-inventory.test.ts` — a ratchet that fails when a legacy identity helper
  gains a new production caller, so the surface cannot grow while the migration runs.

Three review findings are encoded rather than deferred: launch stays above run-key-less completed
hooks (promoting the hook lets a stale record hijack a pane); OMP/Pi evidence is owner-normalized
before assembly, since OMP emits Pi-compatible frames and a wrapper's hook would otherwise be read
as the agent it wraps; and Windows-side `wsl.exe` is rejected as process evidence, because the host
observes the distro wrapper rather than the agent inside it.

The census cannot be completed from a worktree. It needs representative native, SSH, WSL and relay
cohorts collected from real use, and that review is the gate on PR 3 — not this PR.

* test(agent-status): keep identity migration inventory-only

* test(agent-status): reuse reliable source scanner

* test(agent-status): bound inventory scan work

* test(agent-status): avoid inventory path false negatives

* test(agent-status): refresh identity inventory after base repair

* test(agent-status): correct inventory classifications

* test(agent-status): correct action boundary inventory

* test(agent-status): pin inventory occurrence counts

* test(agent-status): fail closed on scanner desync
dallascrilley pushed a commit to dallascrilley/orca that referenced this pull request Aug 27, 2026
)

* feat(agent-status): add the pane agent identity resolver

Four ladders answer "which agent is in this pane" independently — the tab icon, the
open-tab/search occupant, the sidebar title rows, and the sidebar hook-row fallback — and they
disagree. Two consult the terminal title before the launch record, so a string Orca parsed
outranks a fact Orca owns.

resolvePaneAgentIdentity is the single ranked answer. Two rules, one of which is not an ordering:

1. Evidence is ranked by how directly it observes the process; a display title is last.
2. Each observation carries the runId of the agent run it describes. Evidence from a superseded
   run is INELIGIBLE, not merely outranked.

Rule 2 is the part reordering could never supply. A completed hook naming A plus a title naming
B is either a bug (hook right, title stale) or a legitimate pane reclaim (title right) —
identical signals, opposite correct answers. Run ids make them different facts: in the bug both
belong to the current run; in the reclaim the hook belongs to a previous one. That pair ships as
a test asserting the two produce opposite answers from the same evidence.

Missing run ids are treated as eligible. Absence means "this peer does not publish them", not
"this is stale", so an old host's rows are never blanked. Sibling evidence is opt-in so
pane-scoped consumers cannot inherit another pane's agent.

No consumer imports this yet; each migrates separately with its own evidence.

Verified non-vacuous: reversing the authority order fails 10 of 18 assertions and removing the
run filter fails 3.

* fix(agent-status): close three resolver contract holes found in review

**Duplicate evidence of one source resolved by array order.** `eligible.find(...)` returned the
first match, so two live hooks naming different agents were settled by input position — the exact
property this resolver exists to remove. The original order-independence test only used DISTINCT
sources, so it never exercised it. Conflicting same-class evidence now returns null with
`ambiguousAt`, and does NOT fall through to a weaker source: letting a title answer whenever two
hooks disagree is worse than saying nothing.

**A bare numeric runId collided across authority restarts.** `incarnation` is a total order only
within one `authorityId` (agent-status-observation.ts states this), and the id is regenerated per
authority instance, so a restarted host counting from its own floor would report `1` and match an
unrelated live run 1. The run key now carries its authority, and evidence from a DIFFERENT
authority is treated as incomparable — kept, like an absent key — rather than as stale.

**Title stayed reachable by consumers that authorize writes.** Ranking it last makes misuse
unlikely; `minimumSource` makes it impossible. An action consumer passes `'launch'` and weaker
evidence is dropped before ranking, so routing or delivery cannot name a target from a parsed
string even by reordering its inputs. Display surfaces omit it and are unaffected.

Also restores the generic agent-vocabulary parameter, which lives on the routing branch and was
lost when this branch was rebased.

Each fix is mutation-verified: first-match restored fails 3, ignoring authority fails 1, dropping
the floor fails 2. The authority test was itself vacuous on the first attempt — both sides used
`incarnation: 1`, so a resolver ignoring authority still passed on the numeric compare. It now uses
differing incarnations.

The remaining review finding, that `process > launch` has no freshness bound, is NOT fixed here:
it needs an observation timestamp the evidence type does not yet carry. Recorded rather than
silently dropped.
dallascrilley pushed a commit to dallascrilley/orca that referenced this pull request Aug 27, 2026
…#16575)

* feat(agent-status): measure identity evidence before migrating any consumer

PR 1 of the identity migration. It changes no displayed or routed identity — it only measures.

Why measure first: the hierarchy shipped in stablyai#16148/stablyai#16157 has zero consumers, while ~31 sites still
derive identity independently. Every migration decision after this is currently a guess, including
the one that matters most — how often a real pane has no evidence at all. A live P0 reports "No
Claude status shown", and this design trades toward showing nothing when uncertain, so the blank
rate has to be a number before any surface moves.

- `pane-agent-identity-evidence.ts` — one assembler that gathers a pane's evidence, so consumers
  stop each inventing their own ladder.
- `pane-agent-identity-census.ts` — shadow-only counters keyed by host kind (native / wsl-host /
  wsl-distro / ssh / relay) and launch mode (typed / orca-launch / resume). Records a bitmask of
  which sources were present and whether the resolver returned null or ambiguous. No titles,
  prompts, paths, handles, or agent text.
- `pane-agent-identity-inventory.test.ts` — a ratchet that fails when a legacy identity helper
  gains a new production caller, so the surface cannot grow while the migration runs.

Three review findings are encoded rather than deferred: launch stays above run-key-less completed
hooks (promoting the hook lets a stale record hijack a pane); OMP/Pi evidence is owner-normalized
before assembly, since OMP emits Pi-compatible frames and a wrapper's hook would otherwise be read
as the agent it wraps; and Windows-side `wsl.exe` is rejected as process evidence, because the host
observes the distro wrapper rather than the agent inside it.

The census cannot be completed from a worktree. It needs representative native, SSH, WSL and relay
cohorts collected from real use, and that review is the gate on PR 3 — not this PR.

* test(agent-status): keep identity migration inventory-only

* test(agent-status): reuse reliable source scanner

* test(agent-status): bound inventory scan work

* test(agent-status): avoid inventory path false negatives

* test(agent-status): refresh identity inventory after base repair

* test(agent-status): correct inventory classifications

* test(agent-status): correct action boundary inventory

* test(agent-status): pin inventory occurrence counts

* test(agent-status): fail closed on scanner desync
rahuljangirworks pushed a commit to rahuljangirworks/veer that referenced this pull request Aug 27, 2026
…#16575)

* feat(agent-status): measure identity evidence before migrating any consumer

PR 1 of the identity migration. It changes no displayed or routed identity — it only measures.

Why measure first: the hierarchy shipped in stablyai#16148/stablyai#16157 has zero consumers, while ~31 sites still
derive identity independently. Every migration decision after this is currently a guess, including
the one that matters most — how often a real pane has no evidence at all. A live P0 reports "No
Claude status shown", and this design trades toward showing nothing when uncertain, so the blank
rate has to be a number before any surface moves.

- `pane-agent-identity-evidence.ts` — one assembler that gathers a pane's evidence, so consumers
  stop each inventing their own ladder.
- `pane-agent-identity-census.ts` — shadow-only counters keyed by host kind (native / wsl-host /
  wsl-distro / ssh / relay) and launch mode (typed / orca-launch / resume). Records a bitmask of
  which sources were present and whether the resolver returned null or ambiguous. No titles,
  prompts, paths, handles, or agent text.
- `pane-agent-identity-inventory.test.ts` — a ratchet that fails when a legacy identity helper
  gains a new production caller, so the surface cannot grow while the migration runs.

Three review findings are encoded rather than deferred: launch stays above run-key-less completed
hooks (promoting the hook lets a stale record hijack a pane); OMP/Pi evidence is owner-normalized
before assembly, since OMP emits Pi-compatible frames and a wrapper's hook would otherwise be read
as the agent it wraps; and Windows-side `wsl.exe` is rejected as process evidence, because the host
observes the distro wrapper rather than the agent inside it.

The census cannot be completed from a worktree. It needs representative native, SSH, WSL and relay
cohorts collected from real use, and that review is the gate on PR 3 — not this PR.

* test(agent-status): keep identity migration inventory-only

* test(agent-status): reuse reliable source scanner

* test(agent-status): bound inventory scan work

* test(agent-status): avoid inventory path false negatives

* test(agent-status): refresh identity inventory after base repair

* test(agent-status): correct inventory classifications

* test(agent-status): correct action boundary inventory

* test(agent-status): pin inventory occurrence counts

* test(agent-status): fail closed on scanner desync
brennanb2025 added a commit that referenced this pull request Aug 27, 2026
…erminal title (#16237)

* feat(agent-status): add the pane agent identity resolver

Four ladders answer "which agent is in this pane" independently — the tab icon, the
open-tab/search occupant, the sidebar title rows, and the sidebar hook-row fallback — and they
disagree. Two consult the terminal title before the launch record, so a string Orca parsed
outranks a fact Orca owns.

resolvePaneAgentIdentity is the single ranked answer. Two rules, one of which is not an ordering:

1. Evidence is ranked by how directly it observes the process; a display title is last.
2. Each observation carries the runId of the agent run it describes. Evidence from a superseded
   run is INELIGIBLE, not merely outranked.

Rule 2 is the part reordering could never supply. A completed hook naming A plus a title naming
B is either a bug (hook right, title stale) or a legitimate pane reclaim (title right) —
identical signals, opposite correct answers. Run ids make them different facts: in the bug both
belong to the current run; in the reclaim the hook belongs to a previous one. That pair ships as
a test asserting the two produce opposite answers from the same evidence.

Missing run ids are treated as eligible. Absence means "this peer does not publish them", not
"this is stale", so an old host's rows are never blanked. Sibling evidence is opt-in so
pane-scoped consumers cannot inherit another pane's agent.

No consumer imports this yet; each migrates separately with its own evidence.

Verified non-vacuous: reversing the authority order fails 10 of 18 assertions and removing the
run filter fails 3.

* fix(agent-status): close three resolver contract holes found in review

**Duplicate evidence of one source resolved by array order.** `eligible.find(...)` returned the
first match, so two live hooks naming different agents were settled by input position — the exact
property this resolver exists to remove. The original order-independence test only used DISTINCT
sources, so it never exercised it. Conflicting same-class evidence now returns null with
`ambiguousAt`, and does NOT fall through to a weaker source: letting a title answer whenever two
hooks disagree is worse than saying nothing.

**A bare numeric runId collided across authority restarts.** `incarnation` is a total order only
within one `authorityId` (agent-status-observation.ts states this), and the id is regenerated per
authority instance, so a restarted host counting from its own floor would report `1` and match an
unrelated live run 1. The run key now carries its authority, and evidence from a DIFFERENT
authority is treated as incomparable — kept, like an absent key — rather than as stale.

**Title stayed reachable by consumers that authorize writes.** Ranking it last makes misuse
unlikely; `minimumSource` makes it impossible. An action consumer passes `'launch'` and weaker
evidence is dropped before ranking, so routing or delivery cannot name a target from a parsed
string even by reordering its inputs. Display surfaces omit it and are unaffected.

Also restores the generic agent-vocabulary parameter, which lives on the routing branch and was
lost when this branch was rebased.

Each fix is mutation-verified: first-match restored fails 3, ignoring authority fails 1, dropping
the floor fails 2. The authority test was itself vacuous on the first attempt — both sides used
`incarnation: 1`, so a resolver ignoring authority still passed on the numeric compare. It now uses
differing incarnations.

The remaining review finding, that `process > launch` has no freshness bound, is NOT fixed here:
it needs an observation timestamp the evidence type does not yet carry. Recorded rather than
silently dropped.

* fix(orchestration): route @agent messages by resolved identity, not terminal title

`@claude` picked its recipients with `buildAgentNameRe('claude').test(title)`, so any pane whose
TITLE contained the word received Claude's messages. Terminal titles carry task text, and people
describe agent work in them, so this is the ordinary case rather than a contrived one: the
recorded title "Switch Claude and Codex off the load balancer… - grok" is a Grok pane that
received both @claude and @codex. Misdelivered instructions, not a cosmetic slip.

The cause is that `RuntimeTerminalSummary` carried no identity at all — `title` was the only
identity-ish field on it, so routing by title was the only option available. Fix the input:

- `RuntimeTerminalSummary.agentIdentity?: TuiAgent` — optional, host-resolved from launch and
  foreground-process evidence the host owns, with the title ranked last and contributing only
  when the evidence parser finds an unambiguous name. A title that merely mentions an agent
  yields no evidence, which is the whole point.
- `resolvePublishedPaneAgentIdentity` in `src/shared` rather than inside the runtime class, so
  the decision is testable without a runtime and so routing, delivery and the UI cannot drift.
- Groups match `agentIdentity`; the title matcher and its bespoke Cursor predicate are deleted.

Unknown fails closed. `agentIdentity` is absent when the host predates the field or had no
evidence beyond the title, and delivery is an action: not delivering is visible and recoverable
(the sender sees no recipients), while delivering to the wrong agent is neither. The optional
field is additive, so an old client simply ignores it (wire rule 1).

This is also the first real caller of the evidence parser and the identity resolver.

Tests: 27 in groups, 8 for the publisher, 3 RPC fan-out cases updated to the new contract. The
`@cursor`-must-not-match-"text cursor blink" hazard is now excluded structurally instead of by a
per-agent predicate.

Verified non-vacuous by mutation: swapping the process/title ranks fails 2 publisher assertions,
and reverting groups to title matching fails 15 of 27. One earlier mutation silently failed to
apply after formatting reflowed the block — the file was checked before trusting the result.

* perf(runtime): reuse terminal title during summary build

* fix(orchestration): refuse title evidence when publishing identity for routing

Rebuilt on current main so this carries the hardened parser from #16148 and the corrected
resolver from #16157 (authority-scoped run keys, no order-dependent duplicate resolution).

Applies the resolver's new `minimumSource` floor at the publisher. What this publishes authorizes
an action — routing decides which real agent pane receives a message — so ranking title last is
not enough; the floor removes it from consideration entirely, and no amount of reordering by a
caller can bring it back.

The trade, stated because it is a real capability loss: a hook-less agent over SSH that Orca did
not launch, and whose foreground process the host cannot read, is no longer addressable by @agent.
Accepted because a message delivered into the wrong agent's prompt is unrecoverable while an
undelivered one is visible — the sender sees zero recipients. Whether real panes actually carry
launch/foreground evidence is the open question, and is what live validation must answer.

* fix(pty): preserve agent identity on daemon reattach

* fix(runtime): retire stale pane agent identity

* chore: normalize runtime types formatting

* fix(agent-status): identify a pane from its own hook, not from how it was started

Two defects, one cause: identity was inferred from the outside instead of read from the agent.

**Hook evidence was never plumbed in.** The publisher considered `process`, `launch` and `title`
and contained zero hook references — while the resolver ranks `live-hook` first. The top rung of
the ladder was never connected.

That made identity depend on Orca having launched the agent. Most agents are started by typing
`claude` or `codex` at a shell, which leaves no launch record. On macOS the foreground process
still names them, so the gap was invisible. On WSL the Windows host reads the foreground process
as `wsl.exe` — the distro wrapper, not the agent inside it — so those panes had no signal at all
and became unaddressable by `@agent`.

A hook is the agent reporting itself, so it survives both: no launch record needed, and no
dependency on reading a process across the WSL boundary.

**`launch` outranked `completed-hook`.** Ranking is now by TENSE rather than by how authoritative
a source sounds:

    present: live-hook > process
    past:    completed-hook > launch > sleeping-session > sibling > title

A launch record is an event, not a state — it stays true after the agent exits, which is why a
pane reused after closing its agent kept reading as the old one. A completed hook at least proves
the agent actually ran in that pane; a launch record only proves Orca tried to start one.

Neither rank was covered: all 392 existing tests passed unchanged after reordering. Mutation now
fails 2 on the old order and 4 with hook evidence removed.

Known remaining gap, deliberately not papered over: a hand-started WSL agent with no managed hooks
has no identity signal at all. Restoring a title guess there would reinstate the misdelivery this
PR exists to prevent.

* fix(orchestration): restore title as the last resort, not a forbidden source

An earlier revision passed `minimumSource: 'launch'` so routing could not see a title at any rank,
reasoning that a display string must never authorize a write. That conflated the evidence parser
with the raw substring match it replaced.

`buildAgentNameRe('claude').test(title)` was the misdelivery. `collectAgentTitleEvidence` returns
null on exactly those shapes: "Review the Claude session-history fix" on a Codex pane yields
nothing, and "Switch Claude and Codex off the load balancer… - grok" yields grok from its owner
suffix. Ranking title last is therefore sufficient; refusing it is not necessary.

Refusing it had a real cost. An agent a user starts by hand inside an Orca WSL terminal has no
launch record, no readable foreground process (the Windows host sees `wsl.exe`, not the agent in
the distro), and — until managed Codex hooks install there — no hook either. An unambiguous title
was the only thing left, and dropping it made that pane unaddressable by @agent where the previous
code could reach it. That is a regression, and most agents are started that way.

End-to-end coverage added at the routing layer with title allowed: @claude still does not reach a
Codex pane whose task text names Claude, @codex still does not reach a Grok pane whose task text
names Codex, and a pane identified only by an unambiguous title is reachable again.

* revert(agent-status): keep launch above completed-hook until run keys exist

Reverts the tense-based reorder from this branch. The reasoning behind it was sound as far as it
went — a launch record is a past event, not an observation, which is why a reused pane kept reading
as its previous agent — but it fixed one staleness by opening a worse one.

A completed hook is past tense too, and without an agent-run key it never expires at all. Ranking
it above `launch` lets a stale hook from a previous agent outrank the launch record Orca stamped
for the process running NOW. pane-agent-owner.ts already says this in its own comment: "Ranking
launch/live-hook above the completed/sleeping records keeps a genuine pane on its real agent and
stops a stale record from hijacking it."

The reorder belongs with authority-scoped run generation, which is what makes any past-tense
evidence expire. It is staged in the migration plan rather than shipped here.

What this branch keeps: hook evidence feeding pane identity (so an agent a user starts by hand is
identified from its own report rather than needing a launch record), and title restored as a
genuine last resort behind the evidence parser.

* fix(runtime): guard the pane key so terminal.list survives a non-UUID leaf

`makePaneKey` throws on a leaf id that is not a UUID. The hook-evidence lookup called it unguarded
inside `buildTerminalSummary`, so a single such leaf took down `terminal.list` for the whole list
rather than degrading that one pane — 136 tests across 5 files, and the native code-quality gate
tripped separately on a duplicate test title.

Both were mine, and both were caught by CI rather than by me: I ran the focused suites before
pushing instead of the affected directories.

* fix(runtime): declare published terminal agent identity

* fix(runtime): demote completed hook identity evidence
paidaxingyo666 pushed a commit to paidaxingyo666/Manta that referenced this pull request Aug 28, 2026
…#16575)

* feat(agent-status): measure identity evidence before migrating any consumer

PR 1 of the identity migration. It changes no displayed or routed identity — it only measures.

Why measure first: the hierarchy shipped in stablyai#16148/stablyai#16157 has zero consumers, while ~31 sites still
derive identity independently. Every migration decision after this is currently a guess, including
the one that matters most — how often a real pane has no evidence at all. A live P0 reports "No
Claude status shown", and this design trades toward showing nothing when uncertain, so the blank
rate has to be a number before any surface moves.

- `pane-agent-identity-evidence.ts` — one assembler that gathers a pane's evidence, so consumers
  stop each inventing their own ladder.
- `pane-agent-identity-census.ts` — shadow-only counters keyed by host kind (native / wsl-host /
  wsl-distro / ssh / relay) and launch mode (typed / orca-launch / resume). Records a bitmask of
  which sources were present and whether the resolver returned null or ambiguous. No titles,
  prompts, paths, handles, or agent text.
- `pane-agent-identity-inventory.test.ts` — a ratchet that fails when a legacy identity helper
  gains a new production caller, so the surface cannot grow while the migration runs.

Three review findings are encoded rather than deferred: launch stays above run-key-less completed
hooks (promoting the hook lets a stale record hijack a pane); OMP/Pi evidence is owner-normalized
before assembly, since OMP emits Pi-compatible frames and a wrapper's hook would otherwise be read
as the agent it wraps; and Windows-side `wsl.exe` is rejected as process evidence, because the host
observes the distro wrapper rather than the agent inside it.

The census cannot be completed from a worktree. It needs representative native, SSH, WSL and relay
cohorts collected from real use, and that review is the gate on PR 3 — not this PR.

* test(agent-status): keep identity migration inventory-only

* test(agent-status): reuse reliable source scanner

* test(agent-status): bound inventory scan work

* test(agent-status): avoid inventory path false negatives

* test(agent-status): refresh identity inventory after base repair

* test(agent-status): correct inventory classifications

* test(agent-status): correct action boundary inventory

* test(agent-status): pin inventory occurrence counts

* test(agent-status): fail closed on scanner desync

(cherry picked from commit a624e7c)
paidaxingyo666 pushed a commit to paidaxingyo666/Manta that referenced this pull request Aug 30, 2026
…erminal title (stablyai#16237)

* feat(agent-status): add the pane agent identity resolver

Four ladders answer "which agent is in this pane" independently — the tab icon, the
open-tab/search occupant, the sidebar title rows, and the sidebar hook-row fallback — and they
disagree. Two consult the terminal title before the launch record, so a string Orca parsed
outranks a fact Orca owns.

resolvePaneAgentIdentity is the single ranked answer. Two rules, one of which is not an ordering:

1. Evidence is ranked by how directly it observes the process; a display title is last.
2. Each observation carries the runId of the agent run it describes. Evidence from a superseded
   run is INELIGIBLE, not merely outranked.

Rule 2 is the part reordering could never supply. A completed hook naming A plus a title naming
B is either a bug (hook right, title stale) or a legitimate pane reclaim (title right) —
identical signals, opposite correct answers. Run ids make them different facts: in the bug both
belong to the current run; in the reclaim the hook belongs to a previous one. That pair ships as
a test asserting the two produce opposite answers from the same evidence.

Missing run ids are treated as eligible. Absence means "this peer does not publish them", not
"this is stale", so an old host's rows are never blanked. Sibling evidence is opt-in so
pane-scoped consumers cannot inherit another pane's agent.

No consumer imports this yet; each migrates separately with its own evidence.

Verified non-vacuous: reversing the authority order fails 10 of 18 assertions and removing the
run filter fails 3.

* fix(agent-status): close three resolver contract holes found in review

**Duplicate evidence of one source resolved by array order.** `eligible.find(...)` returned the
first match, so two live hooks naming different agents were settled by input position — the exact
property this resolver exists to remove. The original order-independence test only used DISTINCT
sources, so it never exercised it. Conflicting same-class evidence now returns null with
`ambiguousAt`, and does NOT fall through to a weaker source: letting a title answer whenever two
hooks disagree is worse than saying nothing.

**A bare numeric runId collided across authority restarts.** `incarnation` is a total order only
within one `authorityId` (agent-status-observation.ts states this), and the id is regenerated per
authority instance, so a restarted host counting from its own floor would report `1` and match an
unrelated live run 1. The run key now carries its authority, and evidence from a DIFFERENT
authority is treated as incomparable — kept, like an absent key — rather than as stale.

**Title stayed reachable by consumers that authorize writes.** Ranking it last makes misuse
unlikely; `minimumSource` makes it impossible. An action consumer passes `'launch'` and weaker
evidence is dropped before ranking, so routing or delivery cannot name a target from a parsed
string even by reordering its inputs. Display surfaces omit it and are unaffected.

Also restores the generic agent-vocabulary parameter, which lives on the routing branch and was
lost when this branch was rebased.

Each fix is mutation-verified: first-match restored fails 3, ignoring authority fails 1, dropping
the floor fails 2. The authority test was itself vacuous on the first attempt — both sides used
`incarnation: 1`, so a resolver ignoring authority still passed on the numeric compare. It now uses
differing incarnations.

The remaining review finding, that `process > launch` has no freshness bound, is NOT fixed here:
it needs an observation timestamp the evidence type does not yet carry. Recorded rather than
silently dropped.

* fix(orchestration): route @agent messages by resolved identity, not terminal title

`@claude` picked its recipients with `buildAgentNameRe('claude').test(title)`, so any pane whose
TITLE contained the word received Claude's messages. Terminal titles carry task text, and people
describe agent work in them, so this is the ordinary case rather than a contrived one: the
recorded title "Switch Claude and Codex off the load balancer… - grok" is a Grok pane that
received both @claude and @codex. Misdelivered instructions, not a cosmetic slip.

The cause is that `RuntimeTerminalSummary` carried no identity at all — `title` was the only
identity-ish field on it, so routing by title was the only option available. Fix the input:

- `RuntimeTerminalSummary.agentIdentity?: TuiAgent` — optional, host-resolved from launch and
  foreground-process evidence the host owns, with the title ranked last and contributing only
  when the evidence parser finds an unambiguous name. A title that merely mentions an agent
  yields no evidence, which is the whole point.
- `resolvePublishedPaneAgentIdentity` in `src/shared` rather than inside the runtime class, so
  the decision is testable without a runtime and so routing, delivery and the UI cannot drift.
- Groups match `agentIdentity`; the title matcher and its bespoke Cursor predicate are deleted.

Unknown fails closed. `agentIdentity` is absent when the host predates the field or had no
evidence beyond the title, and delivery is an action: not delivering is visible and recoverable
(the sender sees no recipients), while delivering to the wrong agent is neither. The optional
field is additive, so an old client simply ignores it (wire rule 1).

This is also the first real caller of the evidence parser and the identity resolver.

Tests: 27 in groups, 8 for the publisher, 3 RPC fan-out cases updated to the new contract. The
`@cursor`-must-not-match-"text cursor blink" hazard is now excluded structurally instead of by a
per-agent predicate.

Verified non-vacuous by mutation: swapping the process/title ranks fails 2 publisher assertions,
and reverting groups to title matching fails 15 of 27. One earlier mutation silently failed to
apply after formatting reflowed the block — the file was checked before trusting the result.

* perf(runtime): reuse terminal title during summary build

* fix(orchestration): refuse title evidence when publishing identity for routing

Rebuilt on current main so this carries the hardened parser from stablyai#16148 and the corrected
resolver from stablyai#16157 (authority-scoped run keys, no order-dependent duplicate resolution).

Applies the resolver's new `minimumSource` floor at the publisher. What this publishes authorizes
an action — routing decides which real agent pane receives a message — so ranking title last is
not enough; the floor removes it from consideration entirely, and no amount of reordering by a
caller can bring it back.

The trade, stated because it is a real capability loss: a hook-less agent over SSH that Orca did
not launch, and whose foreground process the host cannot read, is no longer addressable by @agent.
Accepted because a message delivered into the wrong agent's prompt is unrecoverable while an
undelivered one is visible — the sender sees zero recipients. Whether real panes actually carry
launch/foreground evidence is the open question, and is what live validation must answer.

* fix(pty): preserve agent identity on daemon reattach

* fix(runtime): retire stale pane agent identity

* chore: normalize runtime types formatting

* fix(agent-status): identify a pane from its own hook, not from how it was started

Two defects, one cause: identity was inferred from the outside instead of read from the agent.

**Hook evidence was never plumbed in.** The publisher considered `process`, `launch` and `title`
and contained zero hook references — while the resolver ranks `live-hook` first. The top rung of
the ladder was never connected.

That made identity depend on Orca having launched the agent. Most agents are started by typing
`claude` or `codex` at a shell, which leaves no launch record. On macOS the foreground process
still names them, so the gap was invisible. On WSL the Windows host reads the foreground process
as `wsl.exe` — the distro wrapper, not the agent inside it — so those panes had no signal at all
and became unaddressable by `@agent`.

A hook is the agent reporting itself, so it survives both: no launch record needed, and no
dependency on reading a process across the WSL boundary.

**`launch` outranked `completed-hook`.** Ranking is now by TENSE rather than by how authoritative
a source sounds:

    present: live-hook > process
    past:    completed-hook > launch > sleeping-session > sibling > title

A launch record is an event, not a state — it stays true after the agent exits, which is why a
pane reused after closing its agent kept reading as the old one. A completed hook at least proves
the agent actually ran in that pane; a launch record only proves Orca tried to start one.

Neither rank was covered: all 392 existing tests passed unchanged after reordering. Mutation now
fails 2 on the old order and 4 with hook evidence removed.

Known remaining gap, deliberately not papered over: a hand-started WSL agent with no managed hooks
has no identity signal at all. Restoring a title guess there would reinstate the misdelivery this
PR exists to prevent.

* fix(orchestration): restore title as the last resort, not a forbidden source

An earlier revision passed `minimumSource: 'launch'` so routing could not see a title at any rank,
reasoning that a display string must never authorize a write. That conflated the evidence parser
with the raw substring match it replaced.

`buildAgentNameRe('claude').test(title)` was the misdelivery. `collectAgentTitleEvidence` returns
null on exactly those shapes: "Review the Claude session-history fix" on a Codex pane yields
nothing, and "Switch Claude and Codex off the load balancer… - grok" yields grok from its owner
suffix. Ranking title last is therefore sufficient; refusing it is not necessary.

Refusing it had a real cost. An agent a user starts by hand inside an Orca WSL terminal has no
launch record, no readable foreground process (the Windows host sees `wsl.exe`, not the agent in
the distro), and — until managed Codex hooks install there — no hook either. An unambiguous title
was the only thing left, and dropping it made that pane unaddressable by @agent where the previous
code could reach it. That is a regression, and most agents are started that way.

End-to-end coverage added at the routing layer with title allowed: @claude still does not reach a
Codex pane whose task text names Claude, @codex still does not reach a Grok pane whose task text
names Codex, and a pane identified only by an unambiguous title is reachable again.

* revert(agent-status): keep launch above completed-hook until run keys exist

Reverts the tense-based reorder from this branch. The reasoning behind it was sound as far as it
went — a launch record is a past event, not an observation, which is why a reused pane kept reading
as its previous agent — but it fixed one staleness by opening a worse one.

A completed hook is past tense too, and without an agent-run key it never expires at all. Ranking
it above `launch` lets a stale hook from a previous agent outrank the launch record Orca stamped
for the process running NOW. pane-agent-owner.ts already says this in its own comment: "Ranking
launch/live-hook above the completed/sleeping records keeps a genuine pane on its real agent and
stops a stale record from hijacking it."

The reorder belongs with authority-scoped run generation, which is what makes any past-tense
evidence expire. It is staged in the migration plan rather than shipped here.

What this branch keeps: hook evidence feeding pane identity (so an agent a user starts by hand is
identified from its own report rather than needing a launch record), and title restored as a
genuine last resort behind the evidence parser.

* fix(runtime): guard the pane key so terminal.list survives a non-UUID leaf

`makePaneKey` throws on a leaf id that is not a UUID. The hook-evidence lookup called it unguarded
inside `buildTerminalSummary`, so a single such leaf took down `terminal.list` for the whole list
rather than degrading that one pane — 136 tests across 5 files, and the native code-quality gate
tripped separately on a duplicate test title.

Both were mine, and both were caught by CI rather than by me: I ran the focused suites before
pushing instead of the affected directories.

* fix(runtime): declare published terminal agent identity

* fix(runtime): demote completed hook identity evidence

(cherry picked from commit 6ba6d58)
enrick-dev pushed a commit to enrick-dev/orca that referenced this pull request Oct 15, 2026
)

* feat(agent-status): add the pane agent identity resolver

Four ladders answer "which agent is in this pane" independently — the tab icon, the
open-tab/search occupant, the sidebar title rows, and the sidebar hook-row fallback — and they
disagree. Two consult the terminal title before the launch record, so a string Orca parsed
outranks a fact Orca owns.

resolvePaneAgentIdentity is the single ranked answer. Two rules, one of which is not an ordering:

1. Evidence is ranked by how directly it observes the process; a display title is last.
2. Each observation carries the runId of the agent run it describes. Evidence from a superseded
   run is INELIGIBLE, not merely outranked.

Rule 2 is the part reordering could never supply. A completed hook naming A plus a title naming
B is either a bug (hook right, title stale) or a legitimate pane reclaim (title right) —
identical signals, opposite correct answers. Run ids make them different facts: in the bug both
belong to the current run; in the reclaim the hook belongs to a previous one. That pair ships as
a test asserting the two produce opposite answers from the same evidence.

Missing run ids are treated as eligible. Absence means "this peer does not publish them", not
"this is stale", so an old host's rows are never blanked. Sibling evidence is opt-in so
pane-scoped consumers cannot inherit another pane's agent.

No consumer imports this yet; each migrates separately with its own evidence.

Verified non-vacuous: reversing the authority order fails 10 of 18 assertions and removing the
run filter fails 3.

* fix(agent-status): close three resolver contract holes found in review

**Duplicate evidence of one source resolved by array order.** `eligible.find(...)` returned the
first match, so two live hooks naming different agents were settled by input position — the exact
property this resolver exists to remove. The original order-independence test only used DISTINCT
sources, so it never exercised it. Conflicting same-class evidence now returns null with
`ambiguousAt`, and does NOT fall through to a weaker source: letting a title answer whenever two
hooks disagree is worse than saying nothing.

**A bare numeric runId collided across authority restarts.** `incarnation` is a total order only
within one `authorityId` (agent-status-observation.ts states this), and the id is regenerated per
authority instance, so a restarted host counting from its own floor would report `1` and match an
unrelated live run 1. The run key now carries its authority, and evidence from a DIFFERENT
authority is treated as incomparable — kept, like an absent key — rather than as stale.

**Title stayed reachable by consumers that authorize writes.** Ranking it last makes misuse
unlikely; `minimumSource` makes it impossible. An action consumer passes `'launch'` and weaker
evidence is dropped before ranking, so routing or delivery cannot name a target from a parsed
string even by reordering its inputs. Display surfaces omit it and are unaffected.

Also restores the generic agent-vocabulary parameter, which lives on the routing branch and was
lost when this branch was rebased.

Each fix is mutation-verified: first-match restored fails 3, ignoring authority fails 1, dropping
the floor fails 2. The authority test was itself vacuous on the first attempt — both sides used
`incarnation: 1`, so a resolver ignoring authority still passed on the numeric compare. It now uses
differing incarnations.

The remaining review finding, that `process > launch` has no freshness bound, is NOT fixed here:
it needs an observation timestamp the evidence type does not yet carry. Recorded rather than
silently dropped.
enrick-dev pushed a commit to enrick-dev/orca that referenced this pull request Oct 15, 2026
…#16575)

* feat(agent-status): measure identity evidence before migrating any consumer

PR 1 of the identity migration. It changes no displayed or routed identity — it only measures.

Why measure first: the hierarchy shipped in stablyai#16148/stablyai#16157 has zero consumers, while ~31 sites still
derive identity independently. Every migration decision after this is currently a guess, including
the one that matters most — how often a real pane has no evidence at all. A live P0 reports "No
Claude status shown", and this design trades toward showing nothing when uncertain, so the blank
rate has to be a number before any surface moves.

- `pane-agent-identity-evidence.ts` — one assembler that gathers a pane's evidence, so consumers
  stop each inventing their own ladder.
- `pane-agent-identity-census.ts` — shadow-only counters keyed by host kind (native / wsl-host /
  wsl-distro / ssh / relay) and launch mode (typed / orca-launch / resume). Records a bitmask of
  which sources were present and whether the resolver returned null or ambiguous. No titles,
  prompts, paths, handles, or agent text.
- `pane-agent-identity-inventory.test.ts` — a ratchet that fails when a legacy identity helper
  gains a new production caller, so the surface cannot grow while the migration runs.

Three review findings are encoded rather than deferred: launch stays above run-key-less completed
hooks (promoting the hook lets a stale record hijack a pane); OMP/Pi evidence is owner-normalized
before assembly, since OMP emits Pi-compatible frames and a wrapper's hook would otherwise be read
as the agent it wraps; and Windows-side `wsl.exe` is rejected as process evidence, because the host
observes the distro wrapper rather than the agent inside it.

The census cannot be completed from a worktree. It needs representative native, SSH, WSL and relay
cohorts collected from real use, and that review is the gate on PR 3 — not this PR.

* test(agent-status): keep identity migration inventory-only

* test(agent-status): reuse reliable source scanner

* test(agent-status): bound inventory scan work

* test(agent-status): avoid inventory path false negatives

* test(agent-status): refresh identity inventory after base repair

* test(agent-status): correct inventory classifications

* test(agent-status): correct action boundary inventory

* test(agent-status): pin inventory occurrence counts

* test(agent-status): fail closed on scanner desync
enrick-dev pushed a commit to enrick-dev/orca that referenced this pull request Oct 15, 2026
…erminal title (stablyai#16237)

* feat(agent-status): add the pane agent identity resolver

Four ladders answer "which agent is in this pane" independently — the tab icon, the
open-tab/search occupant, the sidebar title rows, and the sidebar hook-row fallback — and they
disagree. Two consult the terminal title before the launch record, so a string Orca parsed
outranks a fact Orca owns.

resolvePaneAgentIdentity is the single ranked answer. Two rules, one of which is not an ordering:

1. Evidence is ranked by how directly it observes the process; a display title is last.
2. Each observation carries the runId of the agent run it describes. Evidence from a superseded
   run is INELIGIBLE, not merely outranked.

Rule 2 is the part reordering could never supply. A completed hook naming A plus a title naming
B is either a bug (hook right, title stale) or a legitimate pane reclaim (title right) —
identical signals, opposite correct answers. Run ids make them different facts: in the bug both
belong to the current run; in the reclaim the hook belongs to a previous one. That pair ships as
a test asserting the two produce opposite answers from the same evidence.

Missing run ids are treated as eligible. Absence means "this peer does not publish them", not
"this is stale", so an old host's rows are never blanked. Sibling evidence is opt-in so
pane-scoped consumers cannot inherit another pane's agent.

No consumer imports this yet; each migrates separately with its own evidence.

Verified non-vacuous: reversing the authority order fails 10 of 18 assertions and removing the
run filter fails 3.

* fix(agent-status): close three resolver contract holes found in review

**Duplicate evidence of one source resolved by array order.** `eligible.find(...)` returned the
first match, so two live hooks naming different agents were settled by input position — the exact
property this resolver exists to remove. The original order-independence test only used DISTINCT
sources, so it never exercised it. Conflicting same-class evidence now returns null with
`ambiguousAt`, and does NOT fall through to a weaker source: letting a title answer whenever two
hooks disagree is worse than saying nothing.

**A bare numeric runId collided across authority restarts.** `incarnation` is a total order only
within one `authorityId` (agent-status-observation.ts states this), and the id is regenerated per
authority instance, so a restarted host counting from its own floor would report `1` and match an
unrelated live run 1. The run key now carries its authority, and evidence from a DIFFERENT
authority is treated as incomparable — kept, like an absent key — rather than as stale.

**Title stayed reachable by consumers that authorize writes.** Ranking it last makes misuse
unlikely; `minimumSource` makes it impossible. An action consumer passes `'launch'` and weaker
evidence is dropped before ranking, so routing or delivery cannot name a target from a parsed
string even by reordering its inputs. Display surfaces omit it and are unaffected.

Also restores the generic agent-vocabulary parameter, which lives on the routing branch and was
lost when this branch was rebased.

Each fix is mutation-verified: first-match restored fails 3, ignoring authority fails 1, dropping
the floor fails 2. The authority test was itself vacuous on the first attempt — both sides used
`incarnation: 1`, so a resolver ignoring authority still passed on the numeric compare. It now uses
differing incarnations.

The remaining review finding, that `process > launch` has no freshness bound, is NOT fixed here:
it needs an observation timestamp the evidence type does not yet carry. Recorded rather than
silently dropped.

* fix(orchestration): route @agent messages by resolved identity, not terminal title

`@claude` picked its recipients with `buildAgentNameRe('claude').test(title)`, so any pane whose
TITLE contained the word received Claude's messages. Terminal titles carry task text, and people
describe agent work in them, so this is the ordinary case rather than a contrived one: the
recorded title "Switch Claude and Codex off the load balancer… - grok" is a Grok pane that
received both @claude and @codex. Misdelivered instructions, not a cosmetic slip.

The cause is that `RuntimeTerminalSummary` carried no identity at all — `title` was the only
identity-ish field on it, so routing by title was the only option available. Fix the input:

- `RuntimeTerminalSummary.agentIdentity?: TuiAgent` — optional, host-resolved from launch and
  foreground-process evidence the host owns, with the title ranked last and contributing only
  when the evidence parser finds an unambiguous name. A title that merely mentions an agent
  yields no evidence, which is the whole point.
- `resolvePublishedPaneAgentIdentity` in `src/shared` rather than inside the runtime class, so
  the decision is testable without a runtime and so routing, delivery and the UI cannot drift.
- Groups match `agentIdentity`; the title matcher and its bespoke Cursor predicate are deleted.

Unknown fails closed. `agentIdentity` is absent when the host predates the field or had no
evidence beyond the title, and delivery is an action: not delivering is visible and recoverable
(the sender sees no recipients), while delivering to the wrong agent is neither. The optional
field is additive, so an old client simply ignores it (wire rule 1).

This is also the first real caller of the evidence parser and the identity resolver.

Tests: 27 in groups, 8 for the publisher, 3 RPC fan-out cases updated to the new contract. The
`@cursor`-must-not-match-"text cursor blink" hazard is now excluded structurally instead of by a
per-agent predicate.

Verified non-vacuous by mutation: swapping the process/title ranks fails 2 publisher assertions,
and reverting groups to title matching fails 15 of 27. One earlier mutation silently failed to
apply after formatting reflowed the block — the file was checked before trusting the result.

* perf(runtime): reuse terminal title during summary build

* fix(orchestration): refuse title evidence when publishing identity for routing

Rebuilt on current main so this carries the hardened parser from stablyai#16148 and the corrected
resolver from stablyai#16157 (authority-scoped run keys, no order-dependent duplicate resolution).

Applies the resolver's new `minimumSource` floor at the publisher. What this publishes authorizes
an action — routing decides which real agent pane receives a message — so ranking title last is
not enough; the floor removes it from consideration entirely, and no amount of reordering by a
caller can bring it back.

The trade, stated because it is a real capability loss: a hook-less agent over SSH that Orca did
not launch, and whose foreground process the host cannot read, is no longer addressable by @agent.
Accepted because a message delivered into the wrong agent's prompt is unrecoverable while an
undelivered one is visible — the sender sees zero recipients. Whether real panes actually carry
launch/foreground evidence is the open question, and is what live validation must answer.

* fix(pty): preserve agent identity on daemon reattach

* fix(runtime): retire stale pane agent identity

* chore: normalize runtime types formatting

* fix(agent-status): identify a pane from its own hook, not from how it was started

Two defects, one cause: identity was inferred from the outside instead of read from the agent.

**Hook evidence was never plumbed in.** The publisher considered `process`, `launch` and `title`
and contained zero hook references — while the resolver ranks `live-hook` first. The top rung of
the ladder was never connected.

That made identity depend on Orca having launched the agent. Most agents are started by typing
`claude` or `codex` at a shell, which leaves no launch record. On macOS the foreground process
still names them, so the gap was invisible. On WSL the Windows host reads the foreground process
as `wsl.exe` — the distro wrapper, not the agent inside it — so those panes had no signal at all
and became unaddressable by `@agent`.

A hook is the agent reporting itself, so it survives both: no launch record needed, and no
dependency on reading a process across the WSL boundary.

**`launch` outranked `completed-hook`.** Ranking is now by TENSE rather than by how authoritative
a source sounds:

    present: live-hook > process
    past:    completed-hook > launch > sleeping-session > sibling > title

A launch record is an event, not a state — it stays true after the agent exits, which is why a
pane reused after closing its agent kept reading as the old one. A completed hook at least proves
the agent actually ran in that pane; a launch record only proves Orca tried to start one.

Neither rank was covered: all 392 existing tests passed unchanged after reordering. Mutation now
fails 2 on the old order and 4 with hook evidence removed.

Known remaining gap, deliberately not papered over: a hand-started WSL agent with no managed hooks
has no identity signal at all. Restoring a title guess there would reinstate the misdelivery this
PR exists to prevent.

* fix(orchestration): restore title as the last resort, not a forbidden source

An earlier revision passed `minimumSource: 'launch'` so routing could not see a title at any rank,
reasoning that a display string must never authorize a write. That conflated the evidence parser
with the raw substring match it replaced.

`buildAgentNameRe('claude').test(title)` was the misdelivery. `collectAgentTitleEvidence` returns
null on exactly those shapes: "Review the Claude session-history fix" on a Codex pane yields
nothing, and "Switch Claude and Codex off the load balancer… - grok" yields grok from its owner
suffix. Ranking title last is therefore sufficient; refusing it is not necessary.

Refusing it had a real cost. An agent a user starts by hand inside an Orca WSL terminal has no
launch record, no readable foreground process (the Windows host sees `wsl.exe`, not the agent in
the distro), and — until managed Codex hooks install there — no hook either. An unambiguous title
was the only thing left, and dropping it made that pane unaddressable by @agent where the previous
code could reach it. That is a regression, and most agents are started that way.

End-to-end coverage added at the routing layer with title allowed: @claude still does not reach a
Codex pane whose task text names Claude, @codex still does not reach a Grok pane whose task text
names Codex, and a pane identified only by an unambiguous title is reachable again.

* revert(agent-status): keep launch above completed-hook until run keys exist

Reverts the tense-based reorder from this branch. The reasoning behind it was sound as far as it
went — a launch record is a past event, not an observation, which is why a reused pane kept reading
as its previous agent — but it fixed one staleness by opening a worse one.

A completed hook is past tense too, and without an agent-run key it never expires at all. Ranking
it above `launch` lets a stale hook from a previous agent outrank the launch record Orca stamped
for the process running NOW. pane-agent-owner.ts already says this in its own comment: "Ranking
launch/live-hook above the completed/sleeping records keeps a genuine pane on its real agent and
stops a stale record from hijacking it."

The reorder belongs with authority-scoped run generation, which is what makes any past-tense
evidence expire. It is staged in the migration plan rather than shipped here.

What this branch keeps: hook evidence feeding pane identity (so an agent a user starts by hand is
identified from its own report rather than needing a launch record), and title restored as a
genuine last resort behind the evidence parser.

* fix(runtime): guard the pane key so terminal.list survives a non-UUID leaf

`makePaneKey` throws on a leaf id that is not a UUID. The hook-evidence lookup called it unguarded
inside `buildTerminalSummary`, so a single such leaf took down `terminal.list` for the whole list
rather than degrading that one pane — 136 tests across 5 files, and the native code-quality gate
tripped separately on a duplicate test title.

Both were mine, and both were caught by CI rather than by me: I ran the focused suites before
pushing instead of the affected directories.

* fix(runtime): declare published terminal agent identity

* fix(runtime): demote completed hook identity evidence
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