Skip to content

fix(omp): find transcripts in the store the pane's omp process moved - #1898

Merged
devswha merged 2 commits into
mainfrom
fix/omp-followups
Oct 24, 2026
Merged

devswha merged 2 commits into
mainfrom
fix/omp-followups

Conversation

@devswha

@devswha devswha commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

What it does for the user

An omp pane whose sessions live outside ~/.omp/agent now shows its chat. The bridge works out
where the pane's own omp process writes its sessions and terminal breadcrumbs, from that process's
argv and environment, never from the bridge's own:

  • --session-dir and PI_CODING_AGENT_SESSION_DIR (a flat session directory of its own),
  • PI_CODING_AGENT_DIR, PI_CONFIG_DIR, the process's HOME,
  • a profile (--profile, OMP_PROFILE, PI_PROFILE), and
  • the XDG data and state roots ($XDG_DATA_HOME/omp, $XDG_STATE_HOME/omp).

Both ways of finding the transcript use that layout: the path herdr names (the hook path), and the
file the omp process holds open or its terminal breadcrumb (the held fallback).

When the bridge can't prove where the store is, the pane shows no chat. It doesn't guess:

  • Decoys: herdr's path is accepted only inside the store the process writes to, with the shape
    omp writes there, after following links. In a session directory of omp's own, where every
    folder's sessions sit side by side, its header must also name the pane's live directory.
    Otherwise the held fallback answers, and it tries the absolute --cwd every omp process started
    with only after the live directory. A default-store session no longer passes for a moved store's
    session.
  • Symlink escapes: a link out of the store is refused through herdr's path, the held file and
    the breadcrumb. So is a subagent file whose parent session links out, which the old
    gjcSessionFile accepted.
  • Session versions: only versions omp writes or migrates are read (none, 1, 2 or 3; omp 18.8.7
    and 18.12.6 both write 3).
  • Conflicting or unreadable layouts: two omp processes in the pane with different stores, an
    omp whose environment can't be read, a variable given twice, a relative directory, an empty
    --session-dir, or a profile name omp refuses.

PI_CODING_AGENT_DIR set to the default agent directory keeps the XDG roots, as native omp does. The
old code skipped them. A value that names a profile's agent directory and was handed down by that
profile is ignored, as omp ignores it.

Why

Fixes #1613. The bridge only knew ~/.omp/agent and --profile stores, read from its own HOME,
and treated --session-dir as "unknown". A pane whose launch moved the store showed no chat. The
earlier local branch's review found three more gaps, fixed here:

  • herdr's path was accepted before the process layout was checked,
  • a nested custom root could follow a symlink out,
  • an unsupported session version got through.

How the layout is derived

server/omp.ts ompStoreLayout follows omp's own packages/utils/src/dirs.ts. I read it from the
installed 18.8.7 binary and from the published 18.12.6 source.
scripts/omp-store-layout-probe.ts starts the installed omp 18.8.7 in a temporary HOME for each
routing case (no prompt is sent; provider traffic goes to a closed port). It records where omp
writes its breadcrumb and session, checks that the resolver gives the same roots and finds that
session, and writes server/fixtures/omp-18.8.7-store-layouts.json. The probe exits 1 when any
supported case lacks that proof, when an unsupported one resolves, or when a secret reaches the
helper's answer.

The routing variables come from the process itself:

  • Linux: procfs, always through the existing bounded helper (deadline, output cap, refuses a
    process in D state), which now passes on the routing keys and nothing else. The bridge never
    reads /proc/<pid>/environ itself. A process the helper can't read, whether herdr supplied its
    argv or not, makes the store unproved.
  • macOS: the kernel's argument area (darwinProcess.environment, already used for Claude).
    omp reads the XDG roots on macOS only from 18.12, so an XDG omp root there counts as unproved.
  • Windows: shows no process's environment. It keeps the known default and profile paths and
    never takes a custom session directory.

Settings that omp loads at runtime from a .env file aren't visible. Such a pane shows no chat:
its files sit outside the store the bridge predicts. GJC's defaults are unchanged.
ompAgentDir (used by the composer's integration check) is untouched.

Verification

  • Reproduced on main with the installed omp 18.8.7 in a temporary HOME. For
    PI_CODING_AGENT_SESSION_DIR, --session-dir, PI_CODING_AGENT_DIR and XDG roots, main's
    hook-store check and held lookup both returned nothing. The default case was found.
  • Native probe: all 47 cases proved against the installed omp 18.8.7 (exit 0). Each supported case had the resolver produce omp's own sessions root, breadcrumb root and depth, and find the session omp wrote. Each unsupported case (relative PI_CODING_AGENT_DIR, relative --session-dir, empty values) resolved to nothing, and no secret reached the helper's answer. The committed fixture is that run's output; an earlier run produced the same bytes.
  • Unit and integration tests: server/omp.test.ts passes 34/34 and server/omp-process.test.ts 7/7 on 9d8b65f. In bun run check fast on 9d8b65f, the unit suite passed 6423, failed 0 and skipped 13 across 456 files; it covers omp, omp-process, gjc-runtime, omp-incremental and windows-native. server/omp.contract.test.ts passed 2/2 under bun run check run against the isolated herdr session.
  • Real surface: a real omp 18.8.7 in a pane of an isolated herdr session, with
    PI_CODING_AGENT_SESSION_DIR set. Two servers (this branch and origin/main) were queried with
    curl -i /api/pane/conversation. This branch found the moved session through the held file (on the second poll, once omp left the D state it sits in while paging in its binary) and through the hook path. With a decoy session of the same folder in the default store, it showed the moved session on 5/5 polls. origin/main showed scrollback on 5/5 polls for the held and hook cases, and the decoy on 5/5 polls.
  • Mutation checks: I removed each guard in turn, checked that its named test failed, and restored it: the routing allowlist in the helper and the direct read, an empty environ, the header-cwd check, the session-version check, the subagent parent-link check, the conflicting-layouts check, the XDG check with PI_CODING_AGENT_DIR naming the default agent dir, the handed-down profile rule, an unreadable environment treated as the default, a relative session dir, macOS XDG, refusing the moved-store hook path, the depth-1-only header check, and the unread-owner check. One mutation, accepting the default store, stays green alone because the canonical containment in ompRouteOwns refuses the decoy too. With both removed, the decoy test fails.
  • bun run check fast: passes on 9d8b65f: workflow syntax, generated types and themes, typecheck, build, unit tests and actionlint.

This changes which session paths the conversation route accepts, so the store-containment checks
need a real review.

Reviews

  • Codex (Sol 6.1 Fast, xhigh):
    • H1 (fixed): a header naming the launch directory could pass herdr's path over the live directory's session. The launch directory is now tried only through the held lookup, after the live one.
    • H3 (fixed): an agent process whose argv herdr left out and the helper could not read, beside a readable omp, was dropped silently. It now makes the route unproved, so the pane shows no chat.
    • H2 (declined): herdr's path is accepted when two omp processes share one store. herdr's own session report names the session; the ambiguity guard is for the held fallback, where nothing else identifies it. main behaves the same.
    • M5 (declined): an XDG omp directory created after omp started. This fails closed: the pane shows no chat, never a wrong one.
    • M4 (documented): .env routing values aren't seen.
  • omo-native-code-reviewer:
    • First pass: APPROVE. Its points were fixed. The header check now applies only in a session directory of omp's own, so default-store users keep resumed sessions of a deleted folder. The hook path got a test, dead code was removed, and the docs were corrected.
    • Second pass on the final diff: APPROVE, with no CRITICAL, HIGH or MEDIUM findings; it confirmed the H1, H3, M1, M3 and M4 fixes. Of its LOW notes, the one H1 test assertion that could not fail now names a session that isn't held (checked: it fails when ompRouteOwns refuses herdr's path). The others are left as follow-ups. A stale herdr path in a managed store and a pane whose only omp can't be read both behave as on main. server/core/composer.ts still finds the integration through the older ompAgentDir, which is outside this issue.
  • Overlap: fix(omp): wait for the integration when omp runs with --session-dir #1825 also edits server/omp.ts. git merge-tree merges the two cleanly.

Merge pass gate review (fixed in 9d8b65f)

  • A pane whose only omp process can't be read: this pane no longer falls back to herdr's path in the bridge's own default store. It shows no chat.
  • The Linux environment read: it always goes through the bounded helper, never as a direct synchronous /proc/<pid>/environ read.

Details and tests are in the reply.

@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 48 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

Run configuration
Commits

Reviewing files that changed from the base of the PR and between 3346b03 and 9d8b65f.

Files selected for processing: 11
  • changelog.d/omp-moved-session-store.md
  • docs/guide.md
  • scripts/omp-store-layout-probe.ts
  • server/conversation.ts
  • server/fixtures/omp-18.8.7-store-layouts.json
  • server/gjc-runtime.ts
  • server/omp-process.test.ts
  • server/omp-process.ts
  • server/omp.contract.test.ts
  • server/omp.test.ts
  • server/omp.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@devswha

devswha commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

Code review standing in for CodeRabbit

CodeRabbit hit its review limit on this PR, so no CodeRabbit review ran. This PR changes which session paths the conversation route accepts. The reviews below were run on this change, and each finding's resolution is listed.

omo-native-code-reviewer, second pass on the final diff (73dbbcd; same content as cd75a0d0c plus one test fix)

Verdict: APPROVE. No CRITICAL, HIGH or MEDIUM findings. It ran bun test server/omp.test.ts: 34 pass, 0 fail.

It confirmed the fixes made after the first pass and the Codex review:

  • H1: at depth 1, ompRouteOwns accepts a header only if it names the live directory. The launch directory is tried only through the held lookup, after the live one.
  • H3: an unreadable live agent process beside a resolved omp makes the route unproved.
  • M1: the header check applies only in a session directory of omp's own, so the managed store behaves as on main.
  • M3: the moved-store hook path has a test.
  • M4: the dead code is gone.

It found no symlink escape and no secret leaving the environment allowlist. GJC callers behave as before.

LOW notes and how each was resolved:

  • L1, a stale herdr path at depth 2 still wins over the held file: not changed. Main does the same (ompSessionStore accepted any path in the store). It's a follow-up if herdr can report a stale path.
  • L2, when the pane's only omp can't be read, the route is null and the old default and profile store check applies: not changed, also main's behaviour. herdr's path must still sit in the user's own default or profile store.
  • L3, one H1 test assertion couldn't fail: fixed in this commit. The assertion now names a session that isn't held. With ompRouteOwns mutated to refuse herdr's path, the test fails; restored, it passes.
  • L4, terminalBreadcrumb's plain-string form has only test callers: kept. It's a small surface, and GJC tests use it.
  • L5, server/core/composer.ts still uses ompAgentDir for the integration check: outside Server: find OMP transcripts when the pane's launch moved the session store #1613's scope. fix(omp): wait for the integration when omp runs with --session-dir #1825 is working in that area.

omo-native-code-reviewer, first pass (earlier version): APPROVE

All of its points were resolved:

  • M1, the header check regressed default-store users: fixed (depth 1 only).
  • M2: the docs were fixed.
  • M3: the hook-path test was added.
  • M4: the dead code was removed.
  • M5: the fixture is committed.
  • L1, per-poll cost: kept. A cache keyed on pid and argv would hide a process whose environment turned unreadable.
  • L3: the comment was fixed.

Codex (Sol 6.1 Fast, xhigh)

  • H1 (fixed): herdr's path to a session of the launch directory could outrank the live directory's. There's a test for it.
  • H3 (fixed): a live process with unread argv was dropped silently. It now makes the route unproved, and there's a test.
  • H2 (declined): herdr's path is accepted when two omp processes share one store. herdr's session report names the session, and main does the same. The ambiguity guard belongs to the held fallback, where nothing else identifies the session.
  • M5 (declined): an XDG omp directory created after omp started fails closed: no chat, never a wrong one.
  • M4 (documented): routing values loaded from omp's .env files aren't seen.

Reports are kept locally in the lane's worktree: .omo/evidence/omp-moved-session-store-code-review{,-2}.md and .omo/run/codex-review-2.md.

@devswha

devswha commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

Merge pass gate review of 73dbbcd: two blockers. The moved-store behaviour itself works, and 51 targeted tests pass.

1. When the only omp process can't be read, the decoy check is skipped (server/omp.ts:267,279-280, server/conversation.ts:906-917)

  • A real permission-denied read of the pane's only omp process returned live: true, route: null instead of "unproved".
  • A null route turns on the fallback to the bridge's own HOME for the hook path, so a path in the default store is accepted. That breaks the PR's own rule: if the store can't be proven, show no chat.
  • Fix: carry the unreadable state through as "unproved" before returning, and add a test with an unreadable process.

2. The native Linux environment read has no bound (server/omp.ts:225,260-269, server/omo.ts:164-165)

  • When herdr supplies argv, the bridge reads /proc/<pid>/environ with a synchronous readFileSync(..., "utf8"), with no deadline and no size limit. The probe saw one direct read and zero calls to the bounded helper.
  • Fix: use the bounded helper this repo already uses for other process reads, with a size cap, and fail closed to "unproved".

Holds

  • ../ traversal probes pass, and the existing tests reject symlink escapes.
  • A mutant of the history routing assertion is caught.
  • The 51 omp, omp-process and conventions tests pass, and so does the typecheck.

The merge pass re-checks the fix commit (changed range only).

mp added 2 commits October 25, 2026 00:54
The bridge only knew ~/.omp/agent and --profile stores, read from its own
HOME, so an omp pane started with --session-dir, PI_CODING_AGENT_SESSION_DIR,
PI_CODING_AGENT_DIR, PI_CONFIG_DIR or XDG data/state roots showed no chat.

ompStoreLayout now derives the store from the pane's omp process argv and
routing environment the way omp's own dirs.ts does (verified against the
real omp 18.8.7 by scripts/omp-store-layout-probe.ts, 47 cases, fixture
server/fixtures/omp-18.8.7-store-layouts.json). herdr's path and the held
fallback are both held to that store: links out of it, another folder's
session in a session directory of its own, unsupported session versions,
unreadable environments and conflicting stores show no chat.

Fixes #1613
… refuse a lone unread omp

Merge pass gate review of #1898:
- A pane whose only omp process could not be read got a null route, which
  fell back to the bridge's own default store for herdr's path. A live
  process whose argv and environment cannot be read now makes the route
  unproved whether or not another omp was read.
- On native Linux with herdr's argv, the route read /proc/<pid>/environ
  synchronously in the bridge. Every Linux read now goes through the
  bounded helper (timeout, size cap, refuses a process in D state), which
  fails closed to unproved.

Tests: every Linux read now goes through the real helper, so the chat
tests give it a deadline no busy PC reaches, and the routing-key test uses
#1862's started shell target, as omp-process.test.ts does.
@devswha

devswha commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

Both blockers from the gate review are fixed in 9d8b65f. The branch is also rebased onto main 3346b03, which brings in #1862, so this push is a force push.

1. When the only omp process can't be read, the decoy check was skipped. In server/omp.ts, ompPaneRoute, a live process whose argv and environment can't be read now makes the route "unproved" whether or not another omp was read. The check moved ahead of the layout === null return. ompTranscriptPath then answers no_session_path and no longer falls back to the bridge's own default store. There are two tests:

  • "an agent process whose argv cannot be read, alone or beside a readable omp, shows no chat" covers the lone unreadable omp with herdr naming a default-store path.
  • "a failed probe with supplied argv never falls back to a synchronous environment read, and shows no chat" covers the case where herdr supplies the argv.
    With the old ordering, the first one fails.

2. The native Linux environment read had no bound. On Linux every read of an omp process now goes through the bounded helper ompProcessMemory, including when herdr supplies the argv. The helper has a 1 s deadline and a 1 MB output cap, refuses a process in D state, and passes on only the routing keys. When it fails, the route is "unproved". ompPaneRoute no longer reads /proc/<pid>/environ itself; the direct read is left for macOS only (darwinProcess.environment). The test "a native Linux pane whose argv herdr supplies reads its environment through the bounded helper" makes the in-process /proc/*/environ read throw and checks that the helper answered. With the old direct read, it and the failed-probe test fail.

Because every Linux read now uses the real helper, the chat tests give it a 30 s deadline, as #1862 did for omp-process.test.ts. The routing-key test now uses #1862's shell target that reports when it is up.

Verification on 9d8b65f

  • bun run check fast passed: 6423 pass, 0 fail, 13 skip in 456 files, and typecheck and build are ok.
  • server/omp.contract.test.ts passed 2/2 under bun run check run.
  • server/omp.test.ts passed 34/34 and server/omp-process.test.ts 7/7.
  • The real-omp QA rerun on this commit is queued, and I'll post its result here.

Outside this PR: server/conversation.ts paneProcessRoots still reads the matched agent's environment with processEnviron. That code is shared by every agent and was on main before this PR. I left it alone; it can be a follow-up.

@devswha

devswha commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

I reran the real-omp QA on 9d8b65f, the same commit as before and after the run, and all 22 checks passed.

The setup was a real omp 18.8.7 in a pane of bun run check run's own isolated herdr session, with PI_CODING_AGENT_SESSION_DIR moving its sessions. I queried a server on this branch and one on origin/main with curl -i /api/pane/conversation:

  • Held file: this branch shows the moved session on its first poll. main shows scrollback on 5/5 polls.
  • herdr's hook path: this branch shows the moved session. main shows scrollback on 5/5 polls.
  • Default-store decoy: with a session of the same folder in the default store, this branch still shows the moved session on 5/5 polls. main shows the decoy on 5/5 polls.

On herdr's first answer, the helper couldn't read the fresh omp, which was still in D state while paging in its binary. Its read was not cached, and the next poll read it, so the pane resolved to the flat store at $ROOT/history. Cleanup stopped both servers, closed the workspace and removed the temp root; no omp was left running, and check run stopped its herdr session.

@devswha

devswha commented Oct 24, 2026

Copy link
Copy Markdown
Owner Author

Merge pass review-work report. Overall: PASSED.

Lane Verdict Confidence
Gate review of 73dbbcd (another model; programming + remove-ai-slops applied directly) REJECT: a lone unreadable omp process enabled a decoy; unbounded environ read HIGH
Gate re-check of 9d8b65f (the fix) the decoy is fixed; one read held, resolved below HIGH

Fixed in 9d8b65f

  • Unreadable omp process: a permission-denied read of the pane's only omp process is now "unproved". The pane shows no chat, and the hook fallback is never called (0 calls in the real probe).
  • Bounded reads: this PR's own environment reads go through the bounded helper. Its deadline and output cap held under probes.
  • Mutant: the new regression fails against the pre-fix code.
  • Tests: omp, omp-process, omo and conventions all pass (75), and so does the typecheck. A range-diff shows 47a97c4 changed only by the rebase.

The read the re-check held is not from this PR. paneConversation first asks paneRunsOmo, which reads the omp process's environment through processEnviron in server/omo.ts:165, a synchronous readFileSync. That code is unchanged on main, and this PR does not touch server/omo.ts. The merge pass's brief asked for every read to be bounded, which pulled main's existing path into this review. It is a follow-up: route processEnviron through the same bounded helper. It does not block this PR.

Holds from round 1

  • ../ traversal is refused.
  • Symlink escapes are refused.
  • The store layout is taken from the pane's own omp process argv and environment, with a fixture for omp 18.8.7.

All required checks are green and no review thread is open.

CodeRabbit did not review this PR (rate-limited or skipped). The reviews above stand in for it.

@devswha
devswha merged commit d44908b into main Oct 24, 2026
18 checks passed
@devswha
devswha deleted the fix/omp-followups branch October 24, 2026 17:23
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.

Server: find OMP transcripts when the pane's launch moved the session store

1 participant