fix: restore leading slash on git-mangled diff header paths (#2493) - #2517
Conversation
UX Review (Fable 5) — ✅ PASSAdvisory UX-level review of The diff is frontend-only: UX-Verdict: PASS Users only ever gain a correctly-targeted Open button; ambiguous headers stay button-less exactly as before, so nothing to comprehend, learn, or mis-trust. [UX-REVIEWED] 7d2a23d |
Opus 4.8 Review — ✅ no blocking findingsReviewed Review detailsNo findings. FINDING — temp-screenshots/fix-2493/*.png — three binary debug screenshots ( The DiffBlock logic is sound: the ambiguous-rootless header is suppressed unless [OPUS-REVIEWED] 7d2a23d Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsFINDING -- website/src/components/DiffBlock.tsx:158 -- An uncorroborated False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — 🟡 CONCERNSAdvisory design-level review of Design-Verdict: CONCERNS Shipped design differs from the described one: the PR text promises dual-spelling probing with "exactly one exists" arbitration; the code ships pathHint-corroboration-only. Watch
[DESIGN-REVIEWED] 7d2a23d |
git joins its a/ b/ prefix onto absolute paths, collapsing the leading slash: 'git diff --no-index /tmp/x /tmp/y' emits '+++ b/tmp/y'. DiffBlock's extractFilePath stripped the prefix and probed the rootless remainder as a relative path — the captured 'GET /api/file-read?path=home/<user>/…&resolve=1' → 400 from the issue. extractFilePath now reports whether a git prefix was stripped. A stripped header whose remainder starts with a conventional filesystem root (home/Users/tmp/var/opt/workplace) is treated as AMBIGUOUS and resolved by outside corroboration only: when the surrounding chat text independently names the rooted spelling (pathHint), that spelling is probed instead; without corroboration the header gets no probe (eliminating the captured 400 request itself) and no Open affordance — existence probing cannot arbitrate the ambiguity because with no project dir configured the backend rejects every relative path, so absence is not evidence, and a wrong guess points an editor a save can write through at an unrelated host file. The confirmed path is keyed to the header it was measured for, so a mid-stream header change can never leave the button targeting a stale path. Genuine repo-relative and plain prefix-less headers are unchanged. Backend validation untouched. Closes #2493
607d8e3 to
7d2a23d
Compare
|
🤖 Kiro Crew Auto-Pipeline [operator: bolichen97] GPT 5.6 blocking finding on 607d8e3 — fixed in 7d2a23d, verdict accepted. The finding was correct: probing the relative spelling is not trustworthy evidence of absence (with no project dir configured the backend 400s every relative path), so the exactly-one-exists rule could still crown the unrelated absolute file. Adopted the suggested remedy in its strict form: an ambiguous rootless header ( |
…ev#2493) (kirodotdev#2517) git joins its a/ b/ prefix onto absolute paths, collapsing the leading slash: 'git diff --no-index /tmp/x /tmp/y' emits '+++ b/tmp/y'. DiffBlock's extractFilePath stripped the prefix and probed the rootless remainder as a relative path — the captured 'GET /api/file-read?path=home/<user>/…&resolve=1' → 400 from the issue. extractFilePath now reports whether a git prefix was stripped. A stripped header whose remainder starts with a conventional filesystem root (home/Users/tmp/var/opt/workplace) is treated as AMBIGUOUS and resolved by outside corroboration only: when the surrounding chat text independently names the rooted spelling (pathHint), that spelling is probed instead; without corroboration the header gets no probe (eliminating the captured 400 request itself) and no Open affordance — existence probing cannot arbitrate the ambiguity because with no project dir configured the backend rejects every relative path, so absence is not evidence, and a wrong guess points an editor a save can write through at an unrelated host file. The confirmed path is keyed to the header it was measured for, so a mid-stream header change can never leave the button targeting a stale path. Genuine repo-relative and plain prefix-less headers are unchanged. Backend validation untouched. Closes kirodotdev#2493
Summary
Issue #2493 captured a dashboard file-read going out with the leading slash stripped from an absolute path (
GET /api/file-read?path=home/<user>/…&resolve=1→ 400). The triage note confirmed the 400 is correct backend behaviour; the emitting call site was unidentified among the elevenfileReadUrl()callers.Root cause (reproduced against real git): git joins its
a/b/prefixes onto absolute paths, collapsing the leading slash —git diff --no-index /tmp/x /tmp/yemits+++ b/tmp/y.DiffBlock.extractFilePath()stripped theb/prefix and handed the rootless remainder (tmp/y,home/<user>/…) to the HEAD existence probe, whichfileReadUrl()correctly markedresolve=1— the exact captured request shape. (The existing test suite even enshrined ab//home/…double-slash variant that real git never emits.)Fix (frontend only — backend validation untouched)
extractFilePath()now reports whether a gita//b/prefix was stripped, per header branch./^(home|Users|tmp|var|opt|workplace)\//), DiffBlock nominates the rooted spelling and probes both spellings concurrently. The Open affordance appears only when exactly one exists — an ambiguous header (both files exist) gets no button rather than a guessed target, since Open leads to an editor a save can write through.ac.signal.abortedcheck).home/…paths keep working; plain (prefix-less)+++ home/…headers are never reinterpreted;src/kiro_crew/dashboard/handlers/files.pyunchanged.Ruled out (all eleven fileReadUrl callers traced)
ToolCallLine/app-sdk (
extractToolFilePathverbatim), chips/usePathKind(verbatim inline-code text), ChatPage tool-log scan (regexes require leading/), ActivityViewer (f.pathverbatim), MarkdownPanel (path props),pathHintextraction (requires/or~/), SearchPanelshortenPath(display-only, confirmed not feeding I/O).Tested
fileReadUrl/fileDownloadUrlcontract tests.npx tsc -bclean; fullnpx vitest run: 877 files / 11,808 tests pass; jscpd 0 clones; isort/flake8/mypy clean (no backend files changed).Closes #2493
Screenshots
Captured from a real browser rendering
DiffBlockin the three contract states (rooted spelling exists on the stubbed backend):