Skip to content

fix(session): explain selector misses instead of protocol-validation-failed - #1149

Merged
elucid merged 2 commits into
mainfrom
fix/session-selector-lookup-errors
Oct 6, 2026
Merged

elucid merged 2 commits into
mainfrom
fix/session-selector-lookup-errors

Conversation

@elucid

@elucid elucid commented Oct 6, 2026

Copy link
Copy Markdown
Member

Problem

hunk session commands that select a window with --repo or --session-path fail with a bare hunk: protocol-validation-failed when the path matches no live window. A user running Claude Code and Hunk side by side in herdr hit this on every reload. Claude's shell was in a different git worktree from the Hunk window, so --repo . matched nothing. With no reason in the error, Claude reported that "the Hunk session disconnected during the reload", even though the window was fine.

Reproduced on 0.23.0 and main. Open the window on a main checkout, then run from a sibling worktree:

$ hunk session reload --repo . -- diff
hunk: protocol-validation-failed

The same happens for every selector command, including get, review, and comment list, and with no live sessions at all. Selecting by session ID already gives a proper No active session matches sessionId ….

Cause

A path selector names its session only after a lookup, so sessionApiAuthorizationFacts resolves the session before authorization. When the lookup threw, handleAuthenticatedControl treated it like a malformed body and returned the redacted protocol-validation-failed, dropping the real message.

Approach

  • A selector miss is now authorized as list. The lookup error (no match, or which sessions match ambiguously) never reveals more than list does.
    • Callers allowed to list (the Hunk CLI) get the real message.
    • A session-scoped caller can't list, so it is denied and still learns nothing about other windows.
  • A request authorized this way only reports the lookup error and never dispatches its action. If a matching window registers between authorization and handling, the request gets a 409 retry error instead of running a command it was never authorized for.
  • Malformed bodies still throw, so they keep the redacted protocol failure.

After:

$ hunk session reload --repo . -- diff
hunk: No active session matches repoRoot /Users/me/wt/eco-sibling.

Non-goals: no change to selection semantics. Worktrees still don't match each other's windows, and the explicit-ID path is unchanged. Skill guidance for worktree workflows is a separate follow-up PR.

Tests

  • brokerServer.test.ts, at the HTTP level with real signed callers:
    • get and reload with a missing repo selector return the lookup error and dispatch nothing.
    • A miss that becomes a match between authorization and handling returns 409 and dispatches nothing.
    • Malformed bodies stay redacted.
    • Against the old daemon code, the first two fail and the redaction test passes.
  • test/session/cli.test.ts, black-box CLI against a live window: get --repo and reload --repo for a path no window shows explain the miss. This suite is Linux-only (util-linux script). I ran it in oven/bun:1.4.2: all 8 pass, and the new test fails on the old code with hunk: protocol-validation-failed.
  • Manual check in a real TTY (herdr, macOS): main checkout plus sibling worktree. From the sibling, get, review, comment list and reload with --repo . all print the new message, and from the main checkout reload --repo . still works. With no live sessions, reload --repo . now prints the miss as well.

Commands run:

  • bun run typecheck, bun run lint, bun run deps:check: pass.
  • bun run test: 4558 pass, 1 fail. The failure is Jujutsu source reading > logs unexpected source failures…, which fails identically on main here because jj isn't installed on this machine.
  • bun run test:session-broker-node: pass.
  • bun run format:check: changed files pass; only untracked local scratch/ files are flagged.
  • bun run knip: flags the same 3 findings as main, none in changed files.

Platforms: macOS (unit and manual), Linux in Docker (session CLI suite). Windows not tested; the change uses no platform-specific paths.

Follow-ups

  • Skill guidance for agents working in a different checkout than the Hunk window (separate PR).
  • Separate bug found during the investigation: the CLI's 5s HTTP timeout is shorter than the daemon's 30s reload timeout. A reload slower than 5s reports Timed out waiting for the Hunk session daemon… while the window still finishes the reload.

…failed

A hunk session command that selects its window with --repo or --session-path looks the session up before authorization. When that lookup matched no single session, the daemon collapsed the error into a redacted protocol-validation-failed, so callers in a different checkout (for example another git worktree) could not tell why the command failed.

Authorize a selector miss as list instead, since the lookup error reveals nothing list would not, and answer it with the lookup error without dispatching the action. Session-scoped callers still learn nothing, and malformed bodies stay redacted.
@vercel

vercel Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
hunk-web Ignored Ignored Preview Oct 6, 2026 3:03pm UTC

Request Review

@greptile-apps

greptile-apps Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

[Medium risk] Changes session selector error reporting.

The PR appears safe to merge; the remaining concern is non-blocking test coverage for the restricted-caller boundary.

Findings

  1. P2 Restricted caller behavior lacks coverage ▶
Fix with agent prompt
### Issue 1
packages/hunk/src/session/broker/brokerServer.test.ts:943
These tests use the full-access CLI credential, so they do not check the new authorization path for a caller restricted to one session. Add a signed restricted-caller test that verifies a selector miss is denied before any lookup message about other windows is returned. Without it, a future change could expose session details while these tests still pass.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

The PR changes broker authorization of unresolved path selectors so authorized CLI callers receive the selector lookup error without dispatching the requested action.

  • Adds a retry response if the selector starts matching after authorization.
  • Adds broker and CLI regression tests for repository-selector misses and malformed requests.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Targeted session request] --> B{Selector resolves?}
  B -->|Yes| C[Authorize selected session and action]
  C --> D[Handle request]
  B -->|No| E[Authorize as list]
  E -->|Denied| F[Authorization denied]
  E -->|Allowed| G{Selector resolves on recheck?}
  G -->|No| H[400 with lookup explanation]
  G -->|Yes| I[409 retry; do not dispatch]
Loading

Reviews (1) · Last reviewed commit: "fix(session): explain selector misses in..."

Comment thread packages/hunk/src/session/broker/brokerServer.test.ts
…on failure

Move selector-miss handling from Hunk's adapter into the generic broker.
`resolve` now throws SessionBrokerTargetResolutionError, and
handleAuthenticatedControl authorizes only its disclosure (against `list` by
default), answers with the message, and never runs `handle`.

This removes the implicit "operation is list but action is not" check, the
second lookup in the handler, and the 409 retry path that existed only
because the lookup error was lost between resolve and handle. Audit events
now record the disclosure as outcome `target-resolution-failed` instead of
an ordinary authenticated `list`.

The response a caller receives is unchanged (a signed, target-specific 400
carrying the lookup error), so no session wire or broker protocol revision
bump is needed.
@elucid
elucid merged commit 252f59d into main Oct 6, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant