Skip to content

feat(pwd): add rtk pwd for execution-context validation (#2148)#2990

Open
oluies wants to merge 5 commits into
rtk-ai:developfrom
oluies:task/i-03f211
Open

feat(pwd): add rtk pwd for execution-context validation (#2148)#2990
oluies wants to merge 5 commits into
rtk-ai:developfrom
oluies:task/i-03f211

Conversation

@oluies

@oluies oluies commented Jul 14, 2026

Copy link
Copy Markdown

Summary

Adds rtk pwd — a cheap execution-context validation command, motivated by #2148 (see my investigation comment).

Agent harnesses (Claude Code, Cursor, …) track their shell cwd out-of-band and can silently drift to the wrong checkout when git worktrees are involved — commands the agent believes run in a linked worktree actually run in the main checkout, and rtk faithfully reports the "wrong" checkout's state (or commits there). rtk pwd gives agents and hooks a one-shot way to assert where commands will actually execute before writing:

$ rtk pwd                       # in a linked worktree
/repo/.worktrees/feature
branch: feature
worktree of: /repo (branch develop)

$ rtk pwd                       # in a main checkout, subdirectory
/repo/src/cmds
branch: develop
repo root: /repo

$ rtk pwd                       # outside git
/some/dir
(not in a git repository)

It also surfaces logical $PWD vs physical getcwd() mismatches — the innocent explanation behind "pwd and rtk proxy pwd disagree" reports (shell builtins print the symlink-preserving $PWD; child processes only ever see the physical cwd):

note: shell $PWD is a symlinked path to the same directory: /tmp/link
warn: shell $PWD differs from physical cwd: /other/path

Design

  • Pure in-process FS walk — no child process, no dependency on a git binary; parses .git dir/file, the gitdir: pointer, commondir (with a path-based fallback when absent), and HEAD (branch or detached short hash) directly. Keeps startup well within the <10ms budget.
  • Never fails the user — malformed metadata degrades to (unknown) / (unresolved gitdir pointer); only an unreadable cwd is an error.
  • Token-light — 2–4 short lines; tracked via TimedExecution like other info commands.
  • Not added to is_operational_command (diagnostic command, invoked explicitly — fail-open per the whitelist comment), and no hook rewrite rule (pwd builtin output must stay untouched).

Tests

10 hermetic unit tests with hand-crafted .git fixtures (no git binary needed): main checkout, subdirectory, linked worktree, missing commondir fallback, detached HEAD, malformed gitdir pointer, symlink note / differing-$PWD warn / equal-paths cases.

cargo fmt --all ✓ · cargo clippy --all-targets ✓ (0 warnings) · cargo test --all2006 passed, 0 failed.

Verified end-to-end against the real binary in a linked worktree, main checkout, subdirectory, non-repo dir, and through a symlinked cd.

@oluies

oluies commented Jul 14, 2026

Copy link
Copy Markdown
Author

Salut @aeppling ! Petit bonjour de Suède 🇸🇪

Tu peux jeter un œil à cette PR (et à l'analyse dans #2148) quand tu as un moment ?

Question de fond : est-ce que cette dérive de cwd avec les worktrees est un problème déjà résolu ailleurs (un autre outil, une autre approche qu'on devrait reprendre), ou est-ce que c'est simplement — comme on dit en Suède, « skit bakom ratten » — de la merde derrière le volant, c'est-à-dire le conducteur (l'agent) qui se trompe de répertoire, et pas rtk ?

Mon enquête penche pour la deuxième option : rtk ne touche jamais au cwd (audit du code + 14/14 checks sur un repro déterministe), d'où ce rtk pwd comme garde-fou léger pour que l'agent puisse valider son contexte avant d'écrire.

oluies added 5 commits July 14, 2026 23:26
Agent harnesses track shell cwd out-of-band and can silently drift to
the wrong checkout when git worktrees are involved (rtk-ai#2148). rtk pwd
prints the physical cwd, branch, and worktree linkage in one compact
block so agents/hooks can validate where commands will execute:

  /repo/.worktrees/feature
  branch: feature
  worktree of: /repo (branch develop)

Also surfaces logical $PWD vs physical getcwd() mismatches (symlinked
paths), the innocent explanation for pwd != rtk proxy pwd reports.

Pure in-process FS walk (no child process, no git dependency): parses
.git dir/file, gitdir pointer, commondir, and HEAD directly. Degrades
to '(unknown)' on malformed metadata, never fails the user.
- Classify .git files pointing into .git/modules/ as submodules
  ('submodule of: <super>') instead of mislabeling them as worktrees
  with an unresolved pointer.
- Make the branchless 'worktree of:' arm reachable: main_branch now
  comes from read_branch_opt, so an unreadable main HEAD prints the
  link without an awkward '(branch (unknown))' suffix.
- Parse HEAD honestly: non-heads symbolic refs print verbatim, detached
  formatting only applies to plausible object hashes, anything else
  degrades to '(unknown)' instead of 'detached @ ref: refs' garbage.
- Check worktree layout before submodule layout so a linked worktree of
  a submodule keeps its worktree linkage (the drift signal) and gains a
  'submodule of:' attribution line instead of losing the linkage.
- When the shared gitdir is not a checkout's .git (submodule gitdir),
  point 'worktree of:' at the gitdir itself rather than fabricating a
  checkout path from its parent.
- Label nested submodules 'submodule (nested) of:' since attribution is
  always to the outermost superproject, and document that in the code.
- Require a commondir file for worktree classification unless the
  gitdir is provably not submodule-layout: a plain submodule at path
  'worktrees/<name>' no longer gains a bogus 'worktree of:' line.
- Document that the 'nested' submodule label is best-effort: the disk
  layout cannot distinguish a nested submodule from a submodule whose
  own path contains a 'modules' component.
develop's new classification test requires every Commands variant to be
listed as meta or passthrough. rtk pwd is RTK-native (in-process, does
not wrap /bin/pwd), so it fails closed instead of falling back to the
system pwd on bad args.
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