Skip to content

[workspace-migration] fix(voice-agent): resolve voice-context.txt fallback via import.meta.url, not cwd - #1332

Closed
liususan091219 wants to merge 1 commit into
mainfrom
fix/voice-agent-context-fallback-path
Closed

liususan091219 wants to merge 1 commit into
mainfrom
fix/voice-agent-context-fallback-path

Conversation

@liususan091219

Copy link
Copy Markdown
Collaborator

Summary

src/voice-agent.ts:641 uses a bare readFileSync('voice-context.txt', 'utf-8') as the fallback when no $SUTANDO_MEMORY_DIR/voice-contexts/active pointer is set. The bare path resolves against process cwd, which is only the repo root for the documented npx tsx src/voice-agent.ts launch from $REPO. Every other launch path silently fell through to the "no context loaded" branch.

Why it's silent

The control flow at L621-648 is:

try { /* read $MEMORY_DIR/voice-contexts/<pointer>.txt */ } catch {}
try { /* read 'voice-context.txt' (THE BUG) */ } catch {
  console.log('no context loaded');  // ← masked the missing fallback
  return '';
}

So when the fallback path fails (cwd != repo), the catch logs "no context loaded — no env, no pointer, no fallback file" — which is also the legitimate empty-context message when none of the three sources exist. Operators couldn't tell from the log line whether the fallback file was missing or the resolver was just wrong.

Affected launch paths

Fix

Anchor the fallback to the source file's location via import.meta.url. _voiceAgentDir already exists at L187 (= dirname(fileURLToPath(import.meta.url)) = $REPO/src). The fallback file sits one dir up at $REPO/voice-context.txt, so:

const fallbackPath = join(_voiceAgentDir, '..', 'voice-context.txt');
const content = readFileSync(fallbackPath, 'utf-8');

The log line also prints the resolved absolute path now so future cwd-confusion is debuggable from the first run.

This intentionally keeps the fallback repo-anchored, not workspace. The audit explicitly tags this file as "public-repo voice-context.txt" — it's a checked-in asset, not per-user state. Workspace-anchoring would just create a different invisible failure mode (file simply doesn't exist there because nothing puts it there). The primary loader at L623-637 IS workspace/memory-dir anchored; this is the secondary public fallback.

Empirical PoC

CWD=repo:  OLD reads OK         NEW reads OK
CWD=/tmp:  OLD reads MISSING    NEW reads OK

npx tsc --noEmit clean.

Notes

  • 1 site, 1 file. Smallest reasonable PR in this audit batch.
  • Branched off current upstream/main (0 commits behind at branch time).
  • Refs v3 workspace-contract audit, bug 11 (master log: workspace/notes/cwd-audit-v3-2026-05-27.md).

🤖 Generated with Claude Code

…ce file, not cwd

[workspace-migration] src/voice-agent.ts:641 used bare readFileSync('voice-context.txt', 'utf-8')
as the fallback when no $SUTANDO_MEMORY_DIR/voice-contexts/active pointer was set. The bare
relative path is resolved against the process cwd, which is only the repo root when the
agent is launched via the documented `npx tsx src/voice-agent.ts` from $REPO. For:

  - app-bundle launches (cwd = bundle dir, not repo)
  - any `cd /tmp && npx tsx <abspath>/src/voice-agent.ts` style invocation
  - the soon-to-arrive Sutando.app launch path (#831 SUTANDO_REPO_DIR work)

…the fallback silently fell through to the empty-context branch ("no context loaded"),
masking the bug because the no-env path was the silent-success branch too.

Fix: anchor the fallback to the source file's location. `_voiceAgentDir` already
exists at L187 (= dirname(fileURLToPath(import.meta.url)) = $REPO/src) — the fallback
file sits one dir up at $REPO/voice-context.txt. Resolve via path.join(_voiceAgentDir,
'..', 'voice-context.txt').

This intentionally keeps the fallback REPO-anchored (not workspace) because the file is
a public-repo asset, not per-user state. The audit calls this out as bug 11 — the only
fix is to resolve relative to the source file, since the file's home IS the repo.

Empirical PoC:
  CWD=repo:  OLD reads OK  / NEW reads OK
  CWD=/tmp:  OLD reads MISSING  / NEW reads OK

`npx tsc --noEmit` clean.

Refs v3 workspace-contract audit, bug 11.
@liususan091219
liususan091219 marked this pull request as draft May 29, 2026 12:42
@amkunte

amkunte commented May 29, 2026

Copy link
Copy Markdown
Contributor

LGTM — minimum-viable fix. 1 file, +9/-2. Resolves the fallback path from import.meta.url instead of cwd; works regardless of launch directory.

The "silent fallthrough" framing is the right one: the bug isn't that the file wasn't loaded — it's that the failure mode was indistinguishable from "no fallback file exists." A user who did have a voice-context.txt at the repo root never knew their context was being silently ignored when launched outside cwd=repo. Fixing the resolver fixes that observability problem too — the log line now prints the actual resolved path.

Surgical: doesn't change the env-var path priority, doesn't change the pointer-file path, doesn't change the "no context loaded" log when nothing is found. Only the fallback resolver.

Minor stylistic (non-blocking):
join(_voiceAgentDir, '..', 'voice-context.txt') works but path.resolve(_voiceAgentDir, '..', 'voice-context.txt') would normalize the .. away — cleaner in the log line. Cosmetic; behavior identical.

👍

@sonichi

sonichi commented May 29, 2026

Copy link
Copy Markdown
Owner

LGTM. Verified the resolution target is correct:

  • _voiceAgentDir = dirname(fileURLToPath(import.meta.url)) resolves to <repo>/src/ (voice-agent.ts:187), so join(_voiceAgentDir, '..', 'voice-context.txt') lands at the repo root.
  • That matches the documented intent of this fallback — the inline comment at the block head explicitly calls it "the public-repo voice-context.txt" fallback (primary path reads $SUTANDO_MEMORY_DIR/voice-contexts/<active>.txt). So this is a correct "mixed-direction" fix (code-adjacent file → repo root), consistent with [workspace-migration] fix(voice paths): callsFile + gws-gmail cache + phone scan-script (3 sites, mixed direction) #1281's pattern — not a workspace-state file that should move to $SUTANDO_WORKSPACE.
  • join/dirname are imported (line 35), readFileSync already in scope, and the pattern mirrors the sibling voice-agent.config.json.example resolution at line 190.

The bare 'voice-context.txt' bug only resolved when cwd happened to be the repo root, so app-bundle / cd'd launches silently fell through to "no context loaded" — this fixes that, and echoing the resolved fallbackPath in the log line is a nice touch for the exact opaque-load debugging the comment references. No blockers. (Reviewing as Sutando/Pro — no merge authority; flagging for Chi.)

@liususan091219

Copy link
Copy Markdown
Collaborator Author

Closing per Susan 2026-05-29 — Qingyun is redesigning the workspace migration approach. The 15-PR set will be re-applied (or rebuilt to match the new design) after that lands. Branch is preserved; can be reopened or cherry-picked when ready.

@liususan091219
liususan091219 deleted the fix/voice-agent-context-fallback-path branch June 22, 2026 13:04
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.

3 participants