* fix(studio): the preview serves a same-size edit written right after the last one
* chore(studio): keep the comment share and complexity gates green
* refactor(studio): keep the project signature doc comment on the function it describes
* fix(studio): the preview re-reads files written in the last two seconds, whoever wrote them
* fix(studio-server): a folder rename past an unreadable folder or file answers ok
* fix(studio-server): a rename skips only folders and files it may not read
* fix(studio-server): the rename's skip helpers say it by name
* test(studio-server): the rename tests share one request
* fix(studio): open projects whose folder name has a colon
* fix(studio): open projects named with a colon or '#' in Studio and its preview
* fix(studio): refuse a Windows stream id reached through a session alias
One adapter rule, isServableProjectId, now guards the requested id, a
session's projectId and the folder listing. The bundler-failure preview
places its <base> inside an attributed <head>, and the media metadata
cache keys on the encoded URL so ids with ':' can't collide.
* test(studio-server): history prune tests allow a file time a moment past Date.now()
* ci: temporary probe for the history prune test flake
* fix(studio): timeline scroll-end timer no longer fires after unmount
* ci: remove the temporary history prune probe
* test(studio): check the virtualizer teardown with a spy and a 200 ms wait
* fix(parsers): moving a clip on the timeline moves its inner animations too
* fix(parsers): a timeline move keeps chained and partly known tweens where they belong
* fix(parsers): moving a clip leaves chained outside tweens and template aliases in place
* fix(studio): a clip timing edit syncs its animations once, not twice
* fix(studio): a timing edit falls back to the server when the SDK wrote no animation change
* fix(studio): the SDK sync check reads scripts inside a template too
* fix(studio): the SDK sync check finds template scripts through the DOM walker
* fix(core): a clip retime writes only its own timed tweens, never outside ones
* style(core): format the clip tween import
* fix: readers never see an empty or half-written project file
* test: pin the create routes and block install to whole-file writes
* fix(sdk): a write through a link to a missing file creates that file
* fix: saves through a linked folder reach the real file, atomic writes handle long names
* test(sdk): check for the stray file, not the folder listing
* fix(core): creates work on Windows FAT drives, and links through folders resolve as the system does
* fix: agree the helper move in the deletion check, and match temp paths without a path regex
* test: check POSIX file modes only off Windows
* fix(producer): render names a data-composition-src that points at a folder instead of crashing
* fix(lint): name a data-composition-src that points at a folder instead of crashing
* fix(cli): compositions, keyframes and Studio insertion survive a folder composition source
* fix(lint): tell a folder composition source to point at the HTML file inside it
* fix: read a composition source through one file handle so its type check and read cannot race
* fix(parsers): report a named pipe as a composition source instead of blocking on it
* revert(parsers): open composition sources read-only again, which CodeQL reads as no file creation
* fix(parsers): open composition sources with a private mode, skip pipes, and treat bad paths as missing
* fix(studio): undo and redo swap the restored scene in place instead of reloading
* fix(studio-server): a preview asked for right after an undo shows the undone state
* fix(studio): an undo that restores a page outside every scene still reloads in full
* fix(studio-server): a preview built across a write is not cached under the old signature
* test(studio): a soft undo keeps the scene hashes so the next edit still swaps
* feat(cli): add hyperframes clean to find and remove what HyperFrames left on disk
`hyperframes clean` lists leftover render work dirs, debug renders, the history of projects
that no longer exist, idle video proxies and extracted video frames, with sizes, and removes
them. `--dry-run` removes nothing, `--json` is for agents, `--snapshots` also removes
snapshot folders (otherwise listed as reclaimable). It never touches outputs, sources or
anything a running render or preview still owns.
* docs(cli): say that clean does not search a project's subfolders
* fix(cli): clean test compares folder names on Windows and the skills manifest is regenerated
* fix(studio-server): the proxy cache sweep removes only files the transcoder named
It removed any idle .mp4 or .webm in .transcode-cache, so a user file placed there, or reached through
a symlinked cache folder, could go. Proxies are always <sha256>.<ext> and their temp files
.tmp-<uuid>-<proxy name>; nothing else is swept.
* style(producer): keep a renderDirOwner doc comment within 120 columns
* fix(studio-server): the server loads where sharp's native binary is missing
* fix(studio-server): a missing sharp warns once and is checked before the image is read
* test(studio-server): a write landing between Studio's read and its patch survives undo
* test(studio-server): the outside write also survives a claim that arrives after receipts expire
* fix(studio-server): undo keeps an outside change even when Studio's history claim arrives late
* fix(studio-server): history forgets replaced bytes on delete and frees unused bytes past budget
Also recreates the history folder before storing replaced bytes, hears writes inside the queued start, and pins the agent-turn claim that arrives after the receipt expired.
* fix(studio-server): answer 413/415 instead of reading huge or binary files as text
* test(studio-server): prove the 413 path never reads the file via peak RSS
* perf(studio-server): light entries for safe paths, hf-id stamping and project history
* docs(studio-server): list the light entries a host can import
* fix(studio-server): a cold proxy copy answers 202 at once and the preview retries until it lands
* fix(studio): timeline filmstrips wait for a clip's proxy copy instead of failing on the 202
* fix(studio-server): a copy that cannot be made reports its failure instead of answering 202 forever
* test(studio-server): pin that a full queue is not remembered and a reset probe stops waiting
* test(studio-server): pin that a failure outside ffmpeg is remembered too
* style(studio-server): keep comment share at base in the transcoder and decoder
* fix(core): a clip's proxy URL goes on the video only once the copy is served, so every reader of it works
* fix(player): the parent media mirror follows a clip's src, so it plays the preview copy once it lands
* perf(player): the mirror re-points a clip only when the parent takes over playback
* style(core): shorten the sync-state comment in the proxy swap
* fix(studio): the timeline follows a clip re-pointed at its preview copy
* fix(core): a failed readiness check is retried instead of leaving a clip on its original
* fix(studio-server): a composition card rendered while preview copies are made is not cached
* fix(core): frame captures wait for a pending preview copy as long as for a loading video
* fix(studio-server): the preview-copy check uses the shared realpath from main
* fix(player): re-pointing a clip onto another clip's file no longer plays that file twice
* fix(player): a clip that replaces another on the same file keeps its sound
* fix(studio-server): a save never recreates a project folder renamed away
* style(cli): wrap imports after merging main
* fix(studio-server): proxy and upload answer that the project folder is gone instead of 502 or an empty 201
* test(studio-server): rename the project folder while an upload is being read
* fix(studio-server): a proxy or backup answers that a vanished project folder is gone
* test(studio-server): clean up the renamed folder after the proxy test
* refactor(studio-server): drop an unused re-export and share the folder-gone test setup
* fix(studio-server): any request failing after its project folder vanished says the folder is gone
* fix(studio-server): the folder-gone answer works on every host and drops the failed route's headers
* fix(studio-server): the folder-gone 404 keeps the host's own headers
* fix(core): no folder outside a project is created once the project folder is gone
* test(studio-server): a rename that makes the codec probe fail answers that the folder is gone
* test(studio-server): a refused render leaves no outside renders folder behind
* docs(core): wrap the mkdirWithinProject comment under the line limit
* refactor(studio-server): let the folder-gone middleware's names carry what its comments said
* fix(studio-server): say the folder is gone when a render names a composition
* fix(studio-server): check renders against the folder path and share one folder-gone answer
* test(studio-server): pass the project folder to the preview document store
* fix(studio-server): a save never recreates a project folder renamed away
* style(cli): wrap imports after merging main
* fix(studio-server): proxy and upload answer that the project folder is gone instead of 502 or an empty 201
* test(studio-server): rename the project folder while an upload is being read
* fix(studio-server): a proxy or backup answers that a vanished project folder is gone
* test(studio-server): clean up the renamed folder after the proxy test
* refactor(studio-server): drop an unused re-export and share the folder-gone test setup
* fix(core): no folder outside a project is created once the project folder is gone
* test(studio-server): a refused render leaves no outside renders folder behind
* docs(core): wrap the mkdirWithinProject comment under the line limit
* test(studio-server): pass the project folder to the preview document store
* fix(studio-server): remove the history of a project that no longer exists
* fix(studio-server): keep a project's history while its drive is disconnected
* fix(studio-server): keep an older history whose drive may only be unmounted
A canvas drag stores the element position as a global gsap.set above the
timeline declaration. A resize that followed it inserted its tl.set right
after that line, so the saved script referenced the timeline before it was
declared and every reload and render threw a ReferenceError.
Both GSAP writers now bound the "after the last statement" anchor by the
timeline declaration, whatever the timeline variable is named. A drag or a
resize on its own writes the same bytes as before.
* fix(studio-server): ffmpeg and ffprobe run from the configured binary; unchecked uploads say so
* fix(studio-server): the upload result type lists unchecked files
* fix(media-use): spawn the configured ffmpeg and ffprobe, and name a missing configured ffprobe
* style: format the ffprobe path test, doctor spawns and upload check
* fix(studio): show the not-checked upload notice as info, not an error
* fix: a configured ffmpeg or ffprobe path that cannot run fails loudly
* fix: refuse a folder as the ffprobe path, and report broken paths through --json
* test: reuse the file manager test setup and split the upload stream check
* fix(media-use): a configured ffmpeg or ffprobe must run -version, and a relative one runs itself
* fix(media-use): a configured ffprobe path must name ffprobe, not another tool
* perf(core): skip hidden clips' content so the browser stops decoding their images
Hidden timed clips get content-visibility:hidden while their inline visibility is hidden,
from script evaluation on. A clip due within the look-ahead (2 s by default) keeps its
content live, so its images load before it shows.
* perf(studio-server): load preview images lazily so hidden scenes fetch nothing
* fix(core): stop the readiness scan waiting on and decoding later scenes' images
* perf(studio-server): keep first-frame images eager so the preview opens as fast as before
Only images inside a clip with a numeric start after 0 go lazy; a start the route
cannot read stays eager.
* perf(core): keep clips that start at 0 laid out before the first pass so the first frame is on time
* perf(core): open the preview on time by hiding only lazy images before the first pass
GSAP reads the computed style of the elements it animates while timelines build. Inside a
content-skipped clip every read forces a style update, which cost about 65 ms at open on the
bench composition. Before the first pass the rule now takes only lazy images out of layout, so
hidden later scenes still do not load theirs; after it, the skip rule keeps their content skipped.
* fix(core): hold a paused jump on the previous picture until the next scene's images decode
* fix(studio): swap in a reloaded preview only after its restore seek has painted
* fix(core): apply a held paused jump after 1 s when an image never decodes
* fix(core): keep hidden-scene image skipping and held jumps to Studio's preview, never renders
* fix(studio): reapply manual edits once a held paused jump lands
* fix(studio): copy a clip without the preview's lazy-loading marks
* fix(studio-server): mark preview images lazy without re-encoding the document's entities
* fix(core): share one preview-mark constant between the runtime and the preview route
* fix(studio): reapply manual edits a second time only after a seek that was held
* refactor(core): share the hidden-ancestor walk between clip visibility and image holds
* fix(core): import the studio preview mark from its own subpath
* fix(studio-server): find preview images with the linear html scanner
* chore(core): drop two doc comments the scanner's names already carry
* fix(studio-server): serve the preview mark as its own meta, apart from the GSAP fallback script
* fix(studio-server): capture thumbnails and frames from the preview without lazy images or the mark
* fix(core): let the full readiness scan wait on every image except lazy ones off the first frame
* fix(studio-server): keep an authored loading attribute in any letter case on preview images
* fix(core): leave template content out of findStartTags so preview images match the DOM
* fix(studio): send Ask agent the live markup without the preview loading marks
* chore(core): drop the template note from findStartTags, its test carries it
* perf(studio-server): splice lazy attributes into the preview in one pass
* fix(studio): keep authored image loading on copy and Ask agent by marking preview-made lazy images
* test(studio-server): follow the capture preview URL back to its sub-composition for every project id
* refactor: name the upcoming-clip and GSAP fallback marks once
* fix(studio-server): keep capture builds out of the on-disk preview store
* fix(studio): read and write a clip's in-point the way playback does
* fix(studio): a group head trim keeps the minimum clip length off the grid
* fix(studio): a group head trim stops where a single-clip trim does and never goes below zero
* fix(studio): group members on the grid move together when a trim stops at its limit
* fix(studio-server): background removal writes only where it checked, even through planted links
* test(cli): read the cutout through a no-follow handle instead of checking then reading
* feat(studio-server): a host can make Cmd+Z undo the newest change whoever made it
* fix(studio-server): Cmd+Z for everyone leaves an agent turn that is still open alone
* style(studio-server): format the undo scope check
* refactor(studio-server): the undo scope option names itself
* fix(studio-server): an open turn's parts are known by id, and a new change ends redo for everyone
* style(studio-server): format the open turn check
* fix(studio-server): an open turn's part is known before its entry is announced
* fix(studio-server): history saved by 0.8.78 opens with every entry, and an unreadable id is never overwritten
* test(studio-server): a malformed history id is refused and kept, not replaced
* refactor(studio-server): name the 0.8.78 record check instead of commenting it
* docs(studio-server): hosts keep project histories in historyCache, which reopens a replaced folder
* fix(studio-server): an unreadable 0.8.78 record keeps its history, and another path to the folder is not a copy
* style(studio-server): format the 0.8.78 history tests
* fix(studio-server): name the history id error, and a recorded path that cannot be read is not a copy
* style(studio-server): format the 0.8.78 history fixture
* fix(studio-server): a write through a link lands on the file the system resolves
* fix(cli): resolve catalog install paths as the system does, matching the install root
* fix(cli): list installed files relative to the real project folder
* fix(studio): keep a clip in sync when its head is trimmed at non-1x speed
* fix(studio): keep head-trim sync at the media start, on snaps and through splits
* fix(studio): keep group head trims rigid and read in-points like the runtime
* fix(studio): reload the preview only when a file it loaded changes
* test(studio): read the owner id from the new change-owner shape
* refactor(studio): drop a comment the parser name already says
* fix(studio): keep reloading until the preview is built, and count what its document names
* style(studio): format the reference pattern and name the kept reload
* fix(studio-server): scan preview references in linear time and refresh all thumbnails on a merged reload
* fix(studio): keep a waiting film change when a file the preview never loaded arrives behind it
* test(studio): name the preview ownership test for what it checks
* chore(ci): allow the watchIgnore removals and trim the new comments
* fix(studio-server): reload the preview when a folder holding a file it loaded moves in or out
* perf(studio-server): check a changed folder in one lookup and cover every reference form
* fix(studio-server): record read folders for a project at the filesystem root
* fix(studio-server): reload the preview when the file a symlinked asset points at changes
* fix(studio-server): resolve symlinks on every preview read and key projects by their real folder
* fix(studio-server): record every link a preview read passes through, resolved as the system does
* fix(studio-server): a thumbnail no longer fails when a file it reads is removed mid-request
* fix(studio-server): a thumbnail survives its cache folder being removed mid-request
* fix(studio-server): a stray file where the manifests folder goes no longer fails a thumbnail
* fix(studio-server): drop a comment the code already says
* fix(studio): the dev server leaves catalog installs to hyperframes preview
* test(studio): name the dev server's install test for what it checks
* fix(studio): a finished install replaces its 'Adding' toast instead of sitting beside it
* test(studio): pin that the Adding toast goes after the install's own toast
* test: cli and studio-server tests remove the temp folders they create
* test(cli): each temp folder is removed where it is made, even when a test fails
* test(cli): the one-liner helper relies on its project's own cleanup
* fix(studio): avoid preview reloads for temporary and excluded files
* docs: capture public preview reload reproduction
* fix(studio-server): normalize watch paths in linear time
* fix(studio): register dynamically opened preview projects
* fix(studio-server): probe each proxied clip once instead of on every request
* fix(studio-server): retry a failed codec probe and use the server probe cache
* fix(studio-server): probe again when a clip symlink is repointed
* test(studio-server): pin equal mtimes in the symlink retarget test
* fix(core): clips that meet within float rounding count as meeting in edits and playback
* fix(engine): hand a shared clip boundary to the incoming clip in export, as preview does
* perf(studio): tag each scene's parts in the preview so one can be swapped
With sceneParts (Studio preview only) the bundler tags each top-level scene's host, styles and
inline scripts with data-hf-scene; nested scenes belong to their top-level scene. The preview
route then adds a manifest: a hash of everything outside the scenes and one hash per scene,
so the preview can tell when a rebuilt document differs only inside some scenes. Renders are
unchanged.
* fix(core): keep scene order in the preview manifest and mark unswappable scenes
A moved or reordered scene now changes the shared hash, a nested scene
keeps its @import first, and a scene whose script leaves work running
is marked so edits to it take the full reload.
* refactor(core): keep the scene swap side-effect check in the bundler
* fix(core): refuse swapping a scene that runs its own script file
A swap never re-runs external scripts, so a scene file keeps driving the
replaced nodes. Library URLs stay swappable.
* fix(core): refuse swaps for async scene setup and keep the shared @import first
Scenes that finish building after an await or a .then, animate elements
directly or touch the page head and body now take the full reload, and
splitting out scene styles no longer leaves the shared import late.
* fix(core): refuse swaps for scene scripts that throw when run again
Custom element and CSS property registration, sockets, draggables and
infinite tweens now refuse too, and ticker or animate inside a name no
longer does.
* fix(core): refuse swapping a scene that runs a module script or import map
* fix(core): run a scene's local script file in source order in the preview
* fix(core): refuse a scene swap only for defineProperty on a global
* refactor(core): cut scene swap comments to what names and types do not say
* fix(core): find the head tag for the scene manifest in linear time
* fix(core): leave a document with an unclosed head tag without a scene manifest
* fix(core): keep preview scene parts in render order and refuse scene-only script URLs
* fix(core): keep render @import precedence in the preview and swap only known libraries
* fix(core): keep preview scene styles where a render merges them and resolve library URLs
* fix(core): scene swap refuses standalone gsap tweens and root scripts that hold scene nodes
* fix(core): refuse to swap exactly the scenes a script outside them selects
* perf(core): the preview scene scan looks up ids and classes once instead of querying per string
* docs(core): trim three scene-parts comments to the facts a reader needs
* fix(core): refuse to swap a scene a script outside it reaches by tag name
* fix(core): keep scenes swappable when a script outside them only creates elements by tag
* test(core): check scene tags on elements, not in the inlined runtime's text
* fix(core): refuse swaps for chained scene timelines and any-case or universal root selectors
* fix(core): refuse swaps for root selectors through tags above the scenes
* fix(core): put the scene manifest inside the head when a head attribute holds a quoted >
* fix(core): place the scene manifest with the shared head scanner instead of a regex
* fix(core): an added sub-composition keeps its declared variable defaults when bundled
parseHTMLContent only treated input as a full document when it started with
a doctype or <html>. Files installed by `hyperframes add` start with a marker
comment, so they were wrapped as a fragment in a new <html> and lost their own
<html data-composition-variables>. Leading comments are now skipped first.
* fix(studio-server): an installed block's preview keeps its <html> variables
isFullHtmlDocument moves to core beside parseHTMLContent and both callers use
it, so the standalone preview of a file that `hyperframes add` installed (a
marker comment above the doctype) is treated as the full document it is. The
leading-comment skip also handles an empty `<!-->` comment, and `<html-card>`
style elements no longer count as a document.
* fix(core): a doctype written without a space still reads as a full document
* fix(studio-server): undo of a studio edit keeps a save that landed just before it
* fix(studio-server): undo keeps a save across several studio writes and a whole-file follow-up
* fix(studio-server): undo claims survive studio writes that loop back and stale write receipts
* fix(studio-server): a claim forgets only the write receipts it walked through
* chore(studio-server): drop a claim comment the comment ratchet counts against the stacked base
* docs(studio-server): keep the claim walk comment under the line limit
* refactor(studio-server): pull the pending-hash keep set out of the budget fold
* feat(studio-server): history can copy the files before or after any change into a folder
ProjectHistory.checkout(entryId, side, emptyDir) writes the project as entry entryId found it (before) or left it (after) into an empty folder, leaving the project and its history untouched, so a host can play a change's before and after side by side. The entry's own files come from its recorded change and the rest from the entries logged before it, so a change stays exact when entries were logged out of write order.
* fix(studio-server): checkout refuses an unreadable folder and every replay names its side
* fix(studio-server): checkout lays out only trees that existed and never writes into the project
An entry's tree now includes later-logged entries whose writes it continues (an outside change that turned a file into a folder before an agent turn edited inside it), each file replayed in the order its versions chain. Checkout refuses a folder inside the project (symlinks included), refuses a tree with a path that is both a file and a folder before writing anything, and empties the folder again if a copy fails. manifestAt is back to main's replay.
* fix(studio-server): history logs every change in the order it was written
A window now settles pending writes, the held claim and the outside group before it opens, and commits a held claim before its own entry when it closes, so the log order is the write order and every replay (checkout, restore, peek, reopen) sees files as they were. Removes the dependency replay added for checkout. Visible change: an outside burst that spans an agent turn is two entries, and a drag held across a turn ends at the turn.
* fix(studio-server): checkout recognises its project folder whatever letter case the path uses
* fix(studio-server): overlapping turns, operations and claims log every pending change in write order
settle() (a window opening, an undo, restore or step) and every claim commit the held claim, the outside group and each open window's pending changes as a part, oldest first; the windows stay open. A held drag ends when another write is pending. One commitPending owner replaces the claimed-paths-only cut.
* feat(studio-server): undo is per author, and a turn part undoes alone keeping later edits
* test(studio-server): pin per-author redo and name a person's open window on Cmd+Z
* refactor(studio-server): split Cmd+Z target lookup and claim steps under the audit's thresholds
* fix(studio-server): a file replaced by a folder logs its removal before the folder's files
* fix(studio-server): undo across a file/folder swap works, and a later swap stays the person's
* fix(studio-server): an undo blocked by a file added since refuses before touching any file
* fix(studio-server): an undo never writes through or replaces a link standing in its way
* fix(studio-server): undo and redo across a letter-case file/folder swap work in every direction
* fix(studio-server): a letter-case file/folder swap after an idle turn stays one checkable entry
* fix(studio-server): decide letter-case folding from the project's own disk
* docs(studio-server): keep three history comments under the line limit
* fix(studio-server): checkout checks and fills one resolved folder, so it never overwrites a nonempty one
* fix(studio-server): a Studio element edit keeps a change that lands while it is applied
applyFileMutations refuses a file that changed since the caller read it even without a version (FileChangedError). The element-patch routes, including the patch-element route every style, text and attribute save uses, re-read and re-fold their patches when another writer lands mid-edit, so both changes are kept, and answer 409 after three raced tries. Every other route that writes through writeMutationResult answers 409 instead of overwriting. The batch route now writes atomically.
* fix(studio-server): element edits re-check after their backup and insert-composition refuses a changed file
* fix(studio-server): an element edit refolds when another save lands during its backup
* test(studio-server): the insert race test uses a page with a composition to insert into
* fix(studio-server): an edit to a linked file updates the link's target and keeps the link
* fix(studio-server): id stamping never writes through a link, and link writes keep their receipt
* fix(studio-server): receipts match across a linked project folder, before and after a delete
* fix(studio-server): a restore into a folder deleted outside the app keeps its receipt
* docs(studio-server): say the real-path fallback climbs to the nearest existing folder
* fix(studio-server): edits pin their target when checked, so a retargeted link can't redirect the write
* fix(studio-server): split cuts pin their target and duplicates never follow a planted link
* docs(studio-server): keep the real-path helper's comment under the line limit
* fix(cli): timeline ids never writes over a save that lands while it stamps
runIds now passes each file's version to applyFileMutations, like every other timeline command, so a save that lands while ids are minted makes the command stop and roll back instead of being replaced by the stamped old content.
* fix(cli): timeline ids refuses a conflicting save with the same JSON refusal as other timeline edits
* fix(studio-server): a refused file batch never rolls back over another writer's save
applyFileMutations checks every file's version before its first write, so a batch that is already stale writes nothing, and a rollback restores only files that still hold this batch's write; a file whose own write failed is still restored.
* fix(studio-server): stamping ids at open never reverts a write that lands meanwhile
stampFileHfIds replaces the file only if it still holds the bytes it stamped, so a save or agent write landing while ids are minted is kept. walkDir skips a subfolder that is unreadable or removed mid-walk instead of aborting the whole project walk; an unreadable project folder still throws.
* fix(studio-server): a stamp never recreates a deleted file and a walk only skips unreadable folders
* fix(studio-server): a stamp keeps a permission change made while ids are minted
* feat(cli): hyperframes history lists, undoes and restores a project's recorded changes
* fix(cli): an agent's history turn ends after 10 idle minutes on every path
A turn left open no longer takes the person's later edits: the engine
files a CLI turn's writes by the same idle rule as a preview window.
Studio claims take their paths back from an agent's window; refusals,
--json shapes, --limit and time parsing follow the review.
* refactor(cli): the project-history opener lives in its own module
The history command and other commands open a project's history the same
way: through a running preview, or directly under the engine's lock.
* docs(skills): the history loop lines sit in one marked trial block per skill
* fix(cli): a turn marker without its last write time has ended
Idle-limit tests set file times instead of racing the clock, and the
restore refusal test picks a ref no entry id starts with.
* docs(cli): the history entry shape comment names no other app
* refactor(cli): history reuses the busy error class and its who helper
* fix(cli): undo --who reverts the agent's whole last turn and nothing older
When the open turn recorded nothing, undo fell back to the agent's
earlier turn; when a mid-turn command split the turn, it reverted only
the newest part. The turn now keeps the entries it filed, and undo --who
reverts exactly those or refuses. Engine tests pin the write timing
rules, and a date that rolls over (Feb 31) is refused.
* fix(cli): an undo --who that meets a conflict leaves the whole turn as it was
It redoes the parts it already undid, lists the newer entries as they
are now, and offers --just-this and --back-to-before for the whole turn.
The last turn is recorded atomically before its marker is cleared.
* chore(cli): history keeps each file's comment share at or under main's
* fix(cli): history keeps log order past a turn's idle limit and undoes a turn's cut parts
* fix(cli): history drops unused imports and splits undo and reopen steps