Skip to content

fix(studio-server): history never files a hidden file as a change - #4966

Merged
miguel-heygen merged 6 commits into
mainfrom
dchatsolid/history-skip-hidden-files
Oct 4, 2026
Merged

miguel-heygen merged 6 commits into
mainfrom
dchatsolid/history-skip-hidden-files

Conversation

@miguel-heygen

@miguel-heygen miguel-heygen commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator

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_Store in 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 listProjectFiles returns: 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:

  • historyFiles filters the walk for the first open and every sweep.
  • noteChange and hearWrites skip hidden paths.
  • An older log is cleaned once when it is read (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:

  • "never files a hidden file name as a change, but keeps Studio's two manifests": fails without the fix (the entry lists .turn-record and sub/.DS_Store).
  • "never writes a hidden file an older log still names, on undo or restore".
  • "steps Undo past an older log's entry that changed only a hidden file".
  • "keeps an older log's Undo that only reverted a hidden file, so its target stays undone".
  • "keeps every entry an older log names, emptied ones too, and undoing one still lands".

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).

@github-actions

github-actions Bot commented Oct 3, 2026 •

Copy link
Copy Markdown

Edit accuracy: accurate 1556 (base branch 1556), smooth 1419 of those

The gate passes.
Smoothness is reported in the artifact, not gated. A case fails only if it fails 2 of 3 runs.

Quarantined, measured but not gated (0)

@miguel-heygen
miguel-heygen marked this pull request as ready for review October 4, 2026 11:03

@terencecho terencecho left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 --check and oxlint are 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 undoesEmptied in commit, removing the files.length > 0 step 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 .gitignore or .github/ci.yml is filed and reverted by Undo.
    • Head: the baseline is index.html only, and no spurious entry appears.
  • 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

  1. 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 .gitignore were tracked and undoable. isHistoryPath drops any path with a dot-segment, so those stop being tracked. In my probe an edit to .gitignore/.github/ci.yml followed by Undo reverts index.html but 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.
  2. 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/x case would pin whichever you choose.
  3. Survivor, real path: firstOpen using the unfiltered listProjectFiles. 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_Store plus the first sweep would pin it.
  4. Survivors, redundant: removing the noteChange skip and removing the hearWrites skip 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)

@miguel-heygen
miguel-heygen added this pull request to the merge queue Oct 4, 2026
Merged via the queue into main with commit cad3db4 Oct 4, 2026
94 checks passed
@miguel-heygen
miguel-heygen deleted the dchatsolid/history-skip-hidden-files branch October 4, 2026 11:55
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.

2 participants