Report the directory a session was spawned in #182

Merged
natsukium merged 1 commit from daemon/spawn-cwd into main 2026-10-19 02:11:59 +09:00
Owner

SessionInfo.cwd is the directory a program last announced with OSC 7. A session whose program never sends one, such as a TUI coding agent or a command spawned without a shell, lists no directory even though the daemon chose the one its child started in. A session manager that groups sessions by directory then has to record the path itself. Once that manager restarts, only the daemon still knows the path, and 128-byte tags are too short for long worktree paths.

  • SessionInfo.spawn_cwd reports where the child actually started, not SpawnArgs.cwd as sent:
    • An empty cwd reports the daemon's own directory.
    • A bare shell that falls back from an unusable cwd also reports the daemon's own directory.
    • A requested directory is reported as sent, not canonicalized.
  • The field is absent when the daemon's cwd is not UTF-8, and for a session carried over from a daemon that never recorded it.
  • The field joins the unreleased protocol minor 2 as skippable, so an older peer that skips it only lists no spawn directory. The ipc.md ledger row 2 is updated.
  • The field is carried through the in-place upgrade dump. It decodes as None when the key is absent, so DUMP_VERSION stays at 2.
  • sessions list / info --format json and the bridge emit spawn_cwd. sessions info prints it as a spawned: line, since info is the per-session view that shows every field.
  • Docs: ipc.md, cli.md, the felis skill, and CHANGELOG.

Tests:

  • Create acks for a named, empty, and fallback cwd.
  • A restore from a dump with the key present and with it removed from the JSON.
  • sessions list --format json and sessions info human and JSON output.
  • The bridge golden masks spawn_cwd like cwd.

just check passes except for a_daemon_write_failure_fails_and_clears_the_link (a known bridge hang) and an_idle_notification_subscriber_does_not_block_the_upgrade, which timed out under load and passes on its own. golden_daemon_loss and seven felis-render-wgpu paint_frame goldens fail on this machine without this change.

Closes #178

`SessionInfo.cwd` is the directory a program last announced with OSC 7. A session whose program never sends one, such as a TUI coding agent or a command spawned without a shell, lists no directory even though the daemon chose the one its child started in. A session manager that groups sessions by directory then has to record the path itself. Once that manager restarts, only the daemon still knows the path, and 128-byte tags are too short for long worktree paths. - `SessionInfo.spawn_cwd` reports where the child actually started, not `SpawnArgs.cwd` as sent: - An empty `cwd` reports the daemon's own directory. - A bare shell that falls back from an unusable `cwd` also reports the daemon's own directory. - A requested directory is reported as sent, not canonicalized. - The field is absent when the daemon's cwd is not UTF-8, and for a session carried over from a daemon that never recorded it. - The field joins the unreleased protocol minor 2 as `skippable`, so an older peer that skips it only lists no spawn directory. The ipc.md ledger row 2 is updated. - The field is carried through the in-place upgrade dump. It decodes as `None` when the key is absent, so `DUMP_VERSION` stays at 2. - `sessions list` / `info --format json` and the bridge emit `spawn_cwd`. `sessions info` prints it as a `spawned:` line, since info is the per-session view that shows every field. - Docs: `ipc.md`, `cli.md`, the `felis` skill, and CHANGELOG. Tests: - Create acks for a named, empty, and fallback `cwd`. - A restore from a dump with the key present and with it removed from the JSON. - `sessions list --format json` and `sessions info` human and JSON output. - The bridge golden masks `spawn_cwd` like `cwd`. `just check` passes except for `a_daemon_write_failure_fails_and_clears_the_link` (a known bridge hang) and `an_idle_notification_subscriber_does_not_block_the_upgrade`, which timed out under load and passes on its own. `golden_daemon_loss` and seven `felis-render-wgpu` `paint_frame` goldens fail on this machine without this change. Closes #178
protocol, daemon, cli, docs, skills: report the directory a session was spawned in
All checks were successful
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / detect relevant changes (pull_request) Successful in 9s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 1m11s
windows / detect relevant changes (pull_request) Successful in 14s
windows / windows docs-only no-op (pull_request) Has been skipped
bench / instruction-count regression gate (pull_request) Successful in 46s
windows / cargo nextest (Windows) (pull_request) Successful in 2m57s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 50s
pr / nix flake check (pull_request) Successful in 46s
pr / cargo build / clippy / test / deny (pull_request) Successful in 1m38s
pr / wire schema is compatible with the base (pull_request) Successful in 2s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 27s
windows / cargo clippy (Windows cross) (pull_request) Successful in 15s
windows / frontend smoke (Windows) (pull_request) Successful in 2m49s
09d595ceec
SessionInfo.cwd is the directory a program last announced with OSC 7,
so a session whose program never sends one (a TUI coding agent, a
command spawned without a shell) lists no directory, though the daemon
chose the one its child started in. A session manager that groups its
sessions by directory then has to record the path itself, and once it
restarts the daemon is the only place left that knows it; storing it in
128-byte tags forces long worktree paths to be split across several and
duplicates state the daemon already owns.

SessionInfo.spawn_cwd carries the directory the spawn resolved to, not
SpawnArgs.cwd as sent: an empty cwd and a bare shell's fallback from an
unusable one both start the child in the daemon's own directory, which
is what gets reported. A requested directory is reported as sent rather
than canonicalized, so a caller sees its own spelling back. The value is
fixed at creation, rides the in-place upgrade dump (a default-valued
field, so the dump version stands), and is absent where the daemon does
not know it: a non-UTF-8 daemon cwd, or a session carried over from a
daemon that never recorded it.

The field joins the unreleased protocol minor 2 as skippable: an older
peer that skips it only lists no spawn directory. `sessions info` prints
it as a `spawned:` line as well as in --format json, since info is the
per-session view that shows every field the row carries.

Assisted-by: Claude Code
natsukium force-pushed daemon/spawn-cwd from 09d595ceec
All checks were successful
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / detect relevant changes (pull_request) Successful in 9s
darwin / darwin docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 1m11s
windows / detect relevant changes (pull_request) Successful in 14s
windows / windows docs-only no-op (pull_request) Has been skipped
bench / instruction-count regression gate (pull_request) Successful in 46s
windows / cargo nextest (Windows) (pull_request) Successful in 2m57s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 50s
pr / nix flake check (pull_request) Successful in 46s
pr / cargo build / clippy / test / deny (pull_request) Successful in 1m38s
pr / wire schema is compatible with the base (pull_request) Successful in 2s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 27s
windows / cargo clippy (Windows cross) (pull_request) Successful in 15s
windows / frontend smoke (Windows) (pull_request) Successful in 2m49s
to c77ba7c89d
All checks were successful
fuzz / cargo fuzz nightly long-run (pull_request) Has been skipped
darwin / detect relevant changes (pull_request) Successful in 9s
darwin / darwin docs-only no-op (pull_request) Has been skipped
windows / detect relevant changes (pull_request) Successful in 9s
windows / windows docs-only no-op (pull_request) Has been skipped
darwin / build felis (aarch64-darwin) (pull_request) Successful in 1m13s
bench / instruction-count regression gate (pull_request) Successful in 1m6s
fuzz / cargo fuzz smoke (per target) (pull_request) Successful in 51s
pr / nix flake check (pull_request) Successful in 38s
pr / cargo build / clippy / test / deny (pull_request) Successful in 1m37s
pr / wire schema is compatible with the base (pull_request) Successful in 2s
pr / frontend smoke (x86_64-linux) (pull_request) Successful in 38s
windows / cargo clippy (Windows cross) (pull_request) Successful in 15s
windows / cargo nextest (Windows) (pull_request) Successful in 8m4s
windows / frontend smoke (Windows) (pull_request) Successful in 1m21s
2026-10-19 02:00:47 +09:00
Compare
natsukium scheduled this pull request to auto merge when all checks succeed 2026-10-19 02:03:32 +09:00
natsukium deleted branch daemon/spawn-cwd 2026-10-19 02:12:00 +09:00
Sign in to join this conversation.
No reviewers
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
felis-terminal/felis!182
No description provided.