유대선
프로젝트로
·트러블슈팅·3

Arch map fixes: a build-copy with no engine, dead Cursor links in a Tauri webview, and a smarter change feed

Three fixes to the in-app architecture map: the engine couldn't be found because the running app was a non-git build copy missing scripts/; clicking a file did nothing because ArchPanel used a bare cursor:// anchor (dead in a Tauri WKWebView) instead of the invoke() every other surface uses; and the change feed showed 'last 8 commits' instead of the branch delta you actually want when re-orienting on parallel worktree work.

The architecture map (⌘⇧A) is the self-orientation surface: scan the repo, group it into components, and surface what changed and where it lives. Three things were quietly wrong.

1. "engine not found"

The map renders nothing and shows engine not found at .../ddalkkak-main/.../scripts/archengine.py. The engine is a Python sidecar (ADR-019 boundary) that arch.rs resolves relative to the executable: <exe>/../../../scripts/archengine.py.

The catch: the running app was launched from ddalkkak-main — a build copy that isn't a git checkout (no .git, and scripts/ had been dropped entirely). The real checkout (ddalkkak, where editing happens) has the engine committed; the copy had silently drifted. Fix: sync scripts/ into the copy, and for testing, run dev straight from the real checkout so what you click is what you edited.

Lesson: a "build copy" that isn't a git checkout drifts without telling you. Either build from the real checkout or sync it deterministically.

2. Clicking a file did nothing

Once structure loaded, file names in the map were links — and clicking them opened nothing in Cursor. The engine emits cursor://file/<abspath>:<line> per file, and ArchPanel.tsx rendered that as a bare <a href={f.cursor}>.

A bare custom-scheme anchor is a no-op in a Tauri WKWebView — there's no global click handler routing it to the OS, and the opener plugin is never invoked. Meanwhile every other source jump in the app (terminal links, owner manual, mission control) goes through invoke("open_source_location"), which runs the cursor CLI from Rust behind the CR-002 path allowlist. ArchPanel was the one surface doing it differently.

Fix: the link now intercepts the click, parses the cursor://file/<abspath>:<line>, and calls the same invoke("open_source_location", { startupId, path, line }) as everyone else. startupId is threaded App.tsx → ArchPanel → FeedView.

Then a follow-up: it opened the lone file with no project context. open_source_location_for_startup ran cursor -r -g file:line — no folder. Adding the granted root (cursor -r <root> -g file:line) opens the repo as a workspace and then jumps, so you land in the file with the tree, search, and git right there. Because the command is shared, every source jump now opens with repo context.

Lesson: when one surface misbehaves but its siblings work, it's usually doing the job a different way. Grep for the shared primitive and converge.

3. "Changed" should mean this branch, not recent

The change feed answered the wrong question. git_changed() was git log -n 8 --name-onlygit status --porcelain — i.e. "what moved in the last 8 commits + what's dirty." That's recent activity, HEAD-relative. It is not "what did this branch/worktree change," which is exactly what you want when an AI agent just developed something in a parallel worktree and you're trying to see its footprint.

Fix: branch delta. On a feature branch or worktree, git diff --name-only $(merge-base main HEAD) HEAD — the files this branch added — plus the working tree. When you're directly on main (where main..HEAD is empty), fall back to recent commits so on-main AI work still shows. Each worktree has its own HEAD, so git -C <worktree> isolates that worktree's changes for free. (Churn/hotspots still uses the last 300 commits — that's a frequency metric, a separate question.)

Lesson: "changed" is a product decision, not a CLI default. For re-orientation and parallel work, merge-base..HEAD ("what this branch did") beats "last N commits."

All three verified statically (tsc / cargo check / py_compile green, branch-delta run on the live repo); the file-open was confirmed at runtime.