30: resolveBaseCwd's outside callers still pass an email

The history layer itself checks out — claudeHome gone, ChatIdentity carries both halves,
chatIdentity throws rather than falling back, identity resolved before cwd, and the opencode
path keeping getOwnerHomeDir is correct and documented.

But resolveBaseCwd's first parameter changed meaning from email to home, and three callers
outside the commit still pass an email: pipeline-executor.ts:499 and :591, and
agent-handoff/deliver.ts:36. Both parameters are string, so tsgo had nothing to say — exactly
the wrong-but-well-typed case flagged as uncertainty (2).

Before, the function resolved its own root via getOwnerHomeDir(email) and passing an email was
correct. Now the argument IS the home, so any task step or handoff with a tilde, a relative
cwd, or no cwd gets a relative path built from an email address, resolved against the platform
process's working directory — the repo. Absolute paths still work, which will make it look
intermittent.

Live tonight on the owner's own features, not a member issue. Fix is to pass
getOwnerHomeDir(email) at those three sites, the way agent-runner.ts now does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-12 00:50:10 +00:00
co-authored by Claude Opus 5
parent 95951fbe5a
commit 2bb8128619
@@ -0,0 +1,53 @@
# 30 — your uncertainty (2) was right, and it is broken today on the owner's paths
Commit read: `95951fbe`. The history layer itself is correct as far as I can check it — `claudeHome` is gone,
`ChatIdentity` carries both halves without one standing in for the other, `chatIdentity` throws rather than
falling back, and resolving identity before cwd is the right order for the reason you give. The opencode path
at `:469` keeping `getOwnerHomeDir` is correct and documented.
**But `resolveBaseCwd` has three callers outside this diff that still pass an email.**
```
src/servers/api/tasks/pipeline-executor.ts:499 resolveBaseCwd(email, cwdRelative)
src/servers/api/tasks/pipeline-executor.ts:591 resolveBaseCwd(email, cwd)
src/servers/api/agent-handoff/deliver.ts:36 resolveBaseCwd(user.email, target.cwd ?? undefined)
```
Neither file is in the commit. This is precisely the case you named — *"the meaning of a wrong-but-well-typed
string would not be caught by anything"* — and both parameters are `string`, so `tsgo` had nothing to say.
**Before**, the function resolved its own root: `getOwnerHomeDir(email)` inside. Passing an email was correct.
**After**, the first argument *is* the home:
```ts
const resolveCwd = (home: string, cwd?: string) => {
if (!cwd || cwd === '~') return home; // → returns the EMAIL as a cwd
if (cwd.startsWith('~/')) return join(home, ); // → "pastilhas@officer.dev/foo"
if (cwd.startsWith('/')) return cwd; // → fine
return join(home, cwd); // → "pastilhas@officer.dev/foo"
};
```
So every task pipeline step and every agent handoff with a `~`, a relative cwd, or no cwd now gets a
**relative path built from an email address**, which resolves against the platform process's own working
directory — the repo. Absolute paths still work, which is what will make this look intermittent.
This is not a member problem. It is live tonight, on the owner's own features, and it is the kind that
misbehaves quietly: an agent handoff that should run in the owner's home instead runs somewhere under the
checkout and writes there.
**Fix:** those three are owner-only paths, so pass `getOwnerHomeDir(email)` explicitly, the way
`agent-runner.ts` now does. Same reasoning you used there — state that it is the owner's home rather than
inherit it.
Worth considering a nominal type or an options object for that first parameter. Two `string`s meaning
"identity" and "filesystem path" sat next to each other for the whole of this refactor and the compiler was
never going to help.
## The rest
I could not fault (1) or (3). `chatIdentity` does introduce a failure mode where none existed, and I agree it
is the right trade — a 403 that says why beats a silent read of somebody else's transcripts — but it is worth
the owner knowing that a database blip now surfaces as a forbidden chat screen rather than an empty one.
`tsgo` clean, 78 tests, gates up.