fix: surface nested scratchpad files and name published artifacts - #166
Merged
Merged
Conversation
A session's scratchpad listing read only the top level, so every file an
agent organized into a subdirectory was invisible. A session whose own
summary pointed at "scratchpad/kp/pr/pr.md" showed one unrelated file in
the preview — the listing looked complete while omitting the work.
The walk is now recursive, bounded three ways so it stays on the UI
thread:
* A git checkout in the scratchpad is listed as one row, not walked.
One cloned karpenter tree otherwise filled every listing slot and
pushed the session's own pr/issue.md out entirely.
* The file cap trims by mtime, not walk order — a walk is alphabetical,
so trimming mid-walk keeps whatever sorts first rather than whatever
the session last touched. Bodies are read after trimming, so the byte
budget is spent on files that survived.
* A visit budget bounds the walk itself, which the file cap cannot: the
cost is in traversing entries, paid before any file is selected. One
measured scratchpad holds 70k entries and took ~1.2s to walk fully —
long enough to freeze the preview. It now costs ~136ms.
When a bound bites, the listing says so rather than silently ending
short, so a missing file is not mistaken for proof it never existed.
Artifacts had the opposite problem: the URL was tracked but the row read
"artifact:6c6ced7d", naming nothing. The Artifact tool's result line
carries the source path, so its basename becomes the ref Title, which the
renderer already preferred. Multiple publishes on one line are paired by
URL — mislabeling an artifact is worse than leaving it unnamed.
|
| CVE | Package | Version | Fix |
|---|---|---|---|
| CVE-2026-56852 | golang.org/x/text |
v0.3.8 |
0.39.0 |
View full analysis in Upwind Console
Scan completed in 9s
Scan history (1 scan)
| Commit | Scanned at | New | Resolved | Net |
|---|---|---|---|---|
56d15e5 < |
2026-09-15 08:50 UTC | +1 | 0 | +1 |
Last scanned: 56d15e5 · 2026-09-15 08:50 UTC
|
| Commit | Scanned at | New | Resolved | Net |
|---|---|---|---|---|
56d15e5 |
2026-09-15 08:51 UTC | — | — | — |
56d15e5 < |
2026-09-15 08:51 UTC | 0 | 0 | 0 |
Last scanned: 56d15e5 · 2026-09-15 08:51 UTC
Kairo-Kim
approved these changes
Sep 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A session's scratchpad listing read only the top level, so every file an agent
organized into a subdirectory was invisible. A session whose own summary pointed
at
scratchpad/kp/pr/pr.mdshowed one unrelated file in the preview — thelisting looked complete while omitting the work.
Measured on that session before the change:
kp/pr/issue.mdandkp/pr/pr.mdexist on disk but were never scanned:LoadScratchpadFileswalked oneos.ReadDirandcontinued on every directory.After:
Artifacts had the opposite problem. The URLs were tracked correctly — across the
whole transcript corpus, every
Published-marked artifact URL was extracted —but the row read
artifact:6c6ced7d, naming nothing.SessionRef.Titleand therenderer that prefers it already existed; nothing filled it.
How
Recursion is the easy half. Measuring it surfaced two problems that the naive
version got wrong, and both are why this touches more than one line:
A cloned repo swallows the listing. The first version let a
kp/karpentercheckout fill all 300 slots, pushing the session's own
pr/issue.mdoutentirely — strictly worse than the bug being fixed. A git work tree is upstream
code, not session output, so it is listed as one row and never descended into.
The walk blocks the UI thread. A 70k-entry scratchpad took ~1.2s to walk
fully. This runs on the UI thread, so that is the freeze #161 fixed, reintroduced
by a different path. A file cap cannot bound it — the cost is in traversing
entries, paid before any file is selected — so the walk carries its own visit
budget. 3.2s → 346ms on the worst scratchpad measured.
Two smaller ordering decisions follow from the same measurements:
trimming mid-walk keeps whatever sorts first rather than whatever the session
last touched.
survived rather than on whatever the walk reached first.
When any bound bites, the listing appends a truncation row. A silently short list
reads as "this is everything", which turns a missing file into apparent proof it
never existed.
For artifacts, the
Published <path> at <url>tool-result line carries thesource path; its basename becomes the
Title. One line can announce severalpublishes, so names are paired to their own URL — mislabeling an artifact is
worse than leaving it unnamed.
Test
go vet ./...clean,go test ./...green across all packages.Six tests added, each pinning one of the failure modes above rather than the
happy path: recursion, cloned-repo containment, dependency-dir skipping,
truncation marking, artifact title extraction, and multi-publish URL pairing. All
isolate
HOMEwitht.Setenv("HOME", t.TempDir()).Verified against real transcripts and scratchpads on disk, not only fixtures —
that is where the cloned-repo and walk-cost problems showed up, neither of which
a fixture would have exposed.
Note
internal/tui/splitpane.gohas a pre-existing gofmt violation. Unrelated to thischange, so left alone.