Repository navigation
fix(studio-server): history never files a hidden file as a change - #4966
Conversation
Edit accuracy: accurate 1556 (base branch 1556), smooth 1419 of thoseThe gate passes. Quarantined, measured but not gated (0) |
…ptied one still lands
…mes, not comments
terencecho
left a comment
There was a problem hiding this comment.
Approving ce0fe990. History no longer files hidden paths as changes, an older log is cleaned once on read with every entry id kept, and Undo/Redo step over the emptied entries. It is server-side only and touches 2 files (projectHistory.ts, projectHistory.test.ts). It is not stacked: the 6 commits are all this PR, and main...ce0fe990 shows the same 2 files.
What I verified (head tarball, deps built, NODE_ENV=test)
src/history: 143 pass / 2 skipped, 3 runs in a row, matching the PR body.tsc --noEmit,oxfmt --checkandoxlintare clean.- I ran the five new tests against the base
projectHistory.ts: all five fail there, so they exercise the fix. - I mutated the head 12 ways. 8 are caught: dropping the two-manifest allowance, not cleaning the baseline, not cleaning the entries, not cleaning on read, the sweep using the unfiltered walk, removing
undoesEmptiedincommit, removing thefiles.length > 0step predicate, and dropping emptied entries (breaks the kept-ids rule). - The 4 survivors are listed below. Three are redundant with another filter, and one is a real untested path.
- Probe with hidden files already in the project at first open (
.DS_Store,.gitignore,.github/ci.yml,sub/.turn-record):- Base: they land in the baseline, and an edit to
.gitignoreor.github/ci.ymlis filed and reverted by Undo. - Head: the baseline is
index.htmlonly, and no spurious entry appears.
- Base: they land in the baseline, and an edit to
- An emptied entry is still undoable and still lands, and Undo steps past it. Older-log restore does not write the hidden file (test 1).
Non-blocking
- The filter is broader than the PR text says. The walk skips only
SIGNATURE_EXCLUDED_DIRS(.git,.cache,.hyperframes, ...), not "hidden folders", so before this PR.github/*,.claude/*and.gitignorewere tracked and undoable.isHistoryPathdrops any path with a dot-segment, so those stop being tracked. In my probe an edit to.gitignore/.github/ci.ymlfollowed by Undo revertsindex.htmlbut leaves both dotfile edits. If that is intended, the body's "hidden folders" wording should say so. If not, narrow the rule to the basename plus the specific tool records. - No test pins the "last segment only" variant (a mutant that checks only the basename survives), which is the thing that decides item 1. A one-line
.github/xcase would pin whichever you choose. - Survivor, real path:
firstOpenusing the unfilteredlistProjectFiles. With hidden files present at first open, the next sweep files them all as "Changed outside the app" deletions. Every new test starts with no hidden file, so this is untested. A project with a pre-existing.DS_Storeplus the first sweep would pin it. - Survivors, redundant: removing the
noteChangeskip and removing thehearWritesskip are each backed up by the filtered sweep, so no behavior shows. They are fine as defense in depth.
CI. All checks at this head finished green (0 failing, 0 pending), including every edit-accuracy shard (the gate comment reports accuracy 1556, same as the base branch). CI is a reference here; the verdict rests on the evidence above.
Reviewed on the PR head ce0fe990; no other review exists on it. This is a review verdict, not authorization to merge or deploy beyond what the gate already does.
— Review by tai (pr-review)
What changes
Project history no longer records hidden files as someone's change. A tool's own record at the project root (
.turn-record,.spawn-record) or a.DS_Storein any folder used to land in the history entry. Then an agent run that wrote only such a file got an Undo step with nothing a person could see. Studio's two manifests under.hyperframes/are still kept.Why here
History tracks the set
listProjectFilesreturns: the preview-signature walk. That walk skips hidden folders but not hidden file names, so root-level dotfiles got in. The filter goes in history, the single owner of what it tracks, so the preview signature stays as it is:historyFilesfilters the walk for the first open and every sweep.noteChangeandhearWritesskip hidden paths.keptPaths): hidden paths leave the baseline and every entry. Every entry keeps its id, emptied or not, because pins, undos, restores and callers name entries by id. Undoing an emptied entry still lands as a step, so no Undo, Redo or caller stalls on it, and Cmd+Z steps over emptied entries.Test
projectHistory.test.ts, five tests:.turn-recordandsub/.DS_Store).Each older-log test fails with its part of the fix removed. All pass 3 runs in a row, and the history suites pass (143).