Skip to content

fix(core): canonicalize location directories on every platform - #56325

Open
Finn763 wants to merge 3 commits into
anomalyco:devfrom
Finn763:canonical-location-paths
Open

Finn763 wants to merge 3 commits into
anomalyco:devfrom
Finn763:canonical-location-paths

Conversation

@Finn763

@Finn763 Finn763 commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #55909

Type of change

  • Bug fix

What does this PR do?

Config discovery walked location.directory as given, so <base>/src/../proj loaded <base>/src/opencode.json — a directory that is not an ancestor of the real placement — and applied its permission rules to the session.

Normalization now happens where the location comes in, not only at the resource-cache key:

  • Session.create stores LocationServiceMap.canonical(location), so the session row, the location-service key, bus event filtering and SessionContext.select all compare the same directory.
  • LocationServiceMap.canonical normalizes on every platform — it was win32-only.

The existing test follows the directory's project identity established after creation pinned the aliased spelling (alias/../packages/app) on the stored location; it now expects the canonical form, which is the point of this fix.

How did you verify your code works?

  • New test normalizes a location that arrives with dot segments fails on base (stored location.directory keeps src/..) and passes with the fix.
  • bun test test/session-create.test.ts — 44 pass, 1 pre-existing Windows failure (runs a shell command and projects the started/ended shell message, fails identically on the pristine base).
  • bun test test/location-service-map.test.ts test/location.test.ts test/location-layer.test.ts — 22 pass, 5 pre-existing skips.
  • bun run typecheck clean.

Screenshots / recordings

N/A.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Added by the opencode bot (AI-generated).

Related: #53735

@github-actions github-actions Bot added the needs:compliance This means the issue will auto-close after 2 hours. label Oct 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

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

This stops the permission bypass, but it breaks the session: a session created with a .. directory never runs its prompts.

I ran the #55909 repro from source on the base and on this branch. On the base, the <base>/src/../proj session runs the tool without asking, which is the bug. With this change, that session is still created and stored as …/src/../proj and the prompt request is accepted. But the model is never called, the session has no messages (not even the user's), and nothing is logged. The bypass is gone only because nothing runs.

Only the instance key is normalized. The session row and its events keep the raw path, and a few places compare the two with ===:

  • bus.ts:734 routes events to the instance, so this session's events are filtered out.
  • session/context.ts:124 interrupts select when the directories differ.
  • plugin/host.ts:92 and session/move.ts:136 compare the same way.

Please normalize the directory where it comes in (when the Location.Ref / location.directory is decoded, or in session create, move and import), so the stored session, its events and the instance all use the same path. A regression test should go through session create and prompt with an a/../b directory, rather than only call canonical(). The current test passes even though the session is broken.

Public API: no schema change. Callers that send a .. directory to POST /api/session currently get a session that silently never runs.

Related: #53735

export function canonical(ref: Location.Ref) {
return Location.Ref.make({
directory: AbsolutePath.make(process.platform === "win32" ? path.normalize(ref.directory) : ref.directory),
directory: AbsolutePath.make(path.normalize(ref.directory)),

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.

Normalizing only here makes the instance key differ from the session's stored location.directory, which stays raw (…/src/../proj). bus.ts:734 filters events with ref.directory === location.directory, and session/context.ts:124 interrupts on the same mismatch. In the #55909 repro, the .. session's prompt never reaches the model and no messages are recorded. Normalizing where the location comes in (when it is decoded, or in session create, move and import) would keep the session, its events and the instance on the same path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch — moved the normalization to the entry boundary. Session.create now stores LocationServiceMap.canonical(location), so the session row, the location-service key, bus filtering and SessionContext.select all compare the same directory, and canonical itself now normalizes on every platform (it was win32-only). New test creates a session with /project/src/.. and asserts the stored directory is the canonical one; it fails on base and passes with the fix. The existing 'follows the directory's project identity' test pinned the aliased spelling on the stored location — updated to expect the canonical form, since that is the behavior the issue asks for.

@github-actions github-actions Bot removed the needs:compliance This means the issue will auto-close after 2 hours. label Oct 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

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

My earlier concern is resolved: Session.create now stores the normalized location, so the session row, the location-service key, bus routing and SessionContext.select all use the same path. I re-ran the #55909 repro from source. Both <base>/proj and <base>/src/../proj now run the prompt and stop at one pending shell permission, with no marker.txt (the base runs the tool without asking). The related packages/core tests pass (70 pass, 2 skip) and typecheck is clean. The updated project-identity test expectation is correct for this fix.

Public API: no change in shape. POST /api/session now returns and stores location.directory normalized (e.g. /a/src/../b → /a/b). This is backwards compatible.

Still worth fixing:

  • SessionTransfer.import (session/transfer.ts, around lines 74–109) still stores input.location as given. Now that the instance key is normalized on every platform, an imported session with a .. directory gets the problem I described last time: its events are filtered out and its prompts never run. Normalizing it the same way as create would fix this. Normalizing once in a shared place (where the location is decoded, or a helper both paths use) would cover create and import together.
  • Sessions saved before this change with a .. directory will hit the same mismatch after upgrading. This is probably rare, but worth knowing about.

Related: #53735

@Finn763

Finn763 commented Oct 17, 2026

Copy link
Copy Markdown
Contributor Author

Added the import path: SessionTransfer.import now stores LocationServiceMap.canonical(input.location), so imported sessions match the instance key like created ones. New test imports with /project/src/.. and asserts the canonical stored directory (fails without the change). session-create suite: 45 pass, 1 pre-existing Windows failure; typecheck clean. Pre-existing sessions saved with .. are acknowledged as a known gap of the upgrade (rare; would need a one-off backfill).

@askaramirov

Copy link
Copy Markdown

@Finn763 thanks for your contributions and on this specific case also :3

This branch has not been deployed

No deployments
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.

A session directory containing .. loads project config from the un-normalized path: another directory's opencode.json (and its permissions) applies

2 participants