Skip to content

fix(#360): command handlers honour ctx.cwd, not process.cwd() - #361

Merged
randomm merged 1 commit into
mainfrom
fix/issue-360-ctx-cwd
Aug 6, 2026
Merged

fix(#360): command handlers honour ctx.cwd, not process.cwd()#361
randomm merged 1 commit into
mainfrom
fix/issue-360-ctx-cwd

Conversation

@randomm

@randomm randomm commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Closes #360.

What

/work (commands.ts:191) and /work-status (work-status.ts:362) resolved the repo root from process.cwd() instead of the ctx.cwd Pi hands the handler. ExtensionContext.cwd is first-class API and resolveRepoRoot(cwd) already took the parameter — the plumbing existed, it was just fed the wrong value.

Why it mattered

Two ways:

  1. Production: whenever Pi's process cwd differs from the session cwd, /work writes its state file into the wrong repo.

  2. Observable today: because production ignored the context it was given, test-command-flow.ts had no seam to isolate through. Every run of the §1 pre-push gate wrote fixtures into the live state dir:

    .pi/work-state/547.json 548.json 549.json 551.json 561.json 789.json
    

    These sat at "status":"running" with an empty eventLog, so a real /work 547 would have collided with a fixture. 561.json carried "issues":[561,562,563] — the exact multi-issue shape work-driver-context.ts:139 cites as the historical false-MERGED incident. (Correction to the issue text: .pi/work-state/ is gitignored, so these were written-by-accident, never committed.)

Test changes beyond passing a temp cwd

Passing a temp dir alone turned the suite red, for two reasons worth recording:

  • The temp dir is git init-ed. resolveRepoRoot falls back to the raw cwd outside a repo, which makes the driver throw early and emit its crash-report sendUserMessage — perturbing the message counts asserted downstream.
  • Count assertions now read a promptMessages() view that filters the driver's own pi-ensemble: status lines. The driver is fire-and-forget and shares the sendUserMessage transport with prompt bodies, so every hardcoded count in this file was a latent race on how fast gh issue view failed. Against a real repo gh was slow enough to hide it; against a local temp repo it fails instantly and the race surfaced.

Verification

  • Full §1 gate green: bun run build, tsc --noEmit, bun run check, 60/60 offline smoke tests.
  • test-command-flow.ts run 3× consecutively — stable, no residue.
  • The new guard was proven to fail. With ctx.cwd reverted to process.cwd(), the closing assertion reports ✗ /work cases honour ctx.cwd — repo .pi/work-state/ is unchanged by the suite and the file exits 1. A gate nobody has watched fail is not a gate.
  • Lint diagnostics in the touched smoke test: 24 before, 24 after (all pre-existing; bun run check scopes to src/, which is clean).

Out of scope

spawn.ts:162 (spec.cwd ?? process.cwd() — correct fallback, no ctx), and permission-*.ts / session-autosave.ts, which run outside command-handler context where the process cwd is genuinely right.

/work and /work-status both resolved the project root from process.cwd()
while ignoring the ctx.cwd Pi hands the handler. ExtensionContext.cwd is
first-class API and resolveRepoRoot() already took the parameter — the
plumbing existed, it was just fed the wrong value. Wherever Pi's process
cwd differs from the session cwd, /work silently retargeted its state
file at the wrong repo.

The visible symptom was test residue: because production ignored the
context it was given, test-command-flow.ts could not isolate itself, so
every run of the standard pre-push gate wrote 547/548/549/551/561/789
.json into the real .pi/work-state/ alongside genuine cycle records.
Those sat at status "running" with an empty eventLog, so a real
/work 547 would have collided with a fixture.

Test changes beyond passing a temp cwd:

- The temp dir is `git init`-ed. resolveRepoRoot falls back to the raw
  cwd outside a repo, which makes the driver throw early and emit a
  crash-report sendUserMessage, perturbing the message counts.
- Count assertions now read a `promptMessages()` view that filters the
  driver's own "pi-ensemble:" status lines. The driver is fire-and-forget
  and shares the sendUserMessage transport with prompt bodies, so every
  hardcoded count was a latent race on how fast `gh issue view` failed.
- A closing assertion compares the repo's state dir against a snapshot
  taken at startup. Verified it fails (exit 1) with the bug reintroduced.

Fixes #360
@randomm
randomm merged commit 9f8fc40 into main Aug 6, 2026
1 check passed
@randomm
randomm deleted the fix/issue-360-ctx-cwd branch August 6, 2026 08:37
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.

fix(commands): /work and /work-status read process.cwd() instead of ctx.cwd — offline smoke tests write fixtures into the live .pi/work-state/

1 participant