Skip to content

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

Description

@randomm

Problem

/work and /work-status both resolve the project root from process.cwd() instead of the ctx.cwd that Pi hands the command handler:

// extension/src/commands.ts:191
const cwd = process.cwd();
const repoRoot = await resolveRepoRoot(cwd);

// extension/src/work-status.ts:362
const cwd = process.cwd();
const repoRoot = await resolveRepoRoot(cwd);

ExtensionContext.cwd: string is first-class Pi API (@earendil-works/pi-coding-agent core/extensions/types.d.ts:216), and resolveRepoRoot(cwd) already takes the parameter — the plumbing exists, it is just fed the wrong value. Whenever Pi's process cwd differs from the session cwd, /work writes its state file into the wrong repo.

Evidence — the offline smoke suite writes into the live state directory

Because the handler ignores the context it is given, smoke-tests/test-command-flow.ts cannot isolate itself. Its /work invocations (test-command-flow.ts:182,234,263,272,285,297) resolve to the real repo root, and every run of the standard pre-push gate leaves fixtures behind:

$ ls .pi/work-state/
547.json  548.json  549.json  551.json  561.json  789.json   <- test residue
5.json  208.json  277.json  279.json  ...                    <- real cycles

These sit at "status":"running" with an empty eventLog, so a real /work 547 would collide with a fixture. 561.json even carries "issues":[561,562,563] — the exact multi-issue shape work-driver-context.ts:139 cites as the historical false-MERGED incident.

Sibling tests already do this correctly with mkdtempSync (test-work-driver-pr5.ts:145); test-command-flow.ts is the outlier only because production gives it no seam to use.

Fix

  1. commands.ts:191 and work-status.ts:362 — use ctx.cwd instead of process.cwd().
  2. test-command-flow.tsmakeCtx() takes a cwd; the /work cases pass a mkdtempSync dir so the suite stops touching the real state directory.
  3. Delete the six committed-by-accident fixture files from .pi/work-state/.

Acceptance criteria

  • No process.cwd() remains in a registered command handler that has ctx in scope.
  • Running the full offline gate loop twice from a clean tree leaves git status --porcelain empty and creates no new .pi/work-state/*.json.
  • test-command-flow.ts asserts the work-state path it was given, not the ambient one.
  • Existing assertions (notify text, restart-tag parsing, multi-issue phrasing) still pass unchanged.

Out of scope

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions