Repository navigation
Hand the projects store its clock, ids and disk (#115) - #127
Merged
Merged
Conversation
createProjectsStore read the clock and randomness itself (new Date() in
addProject and touchProject, randomUUID() for a new id), read
process.platform to fold case, and checked the filesystem directly. That
was against the rule that I/O is made at the composition root, and it
meant the tests could not pin an id or a date, or test the case rule for
any platform but the one they ran on.
The store now takes { disk, now, newId, pathKey } beside the database. It
declares the disk it needs as Disk: canonicalize, isDirectory and probe.
Case folding is a pure pathKeyOn(platform). The real disk is disk/disk.ts,
the one place under projects/ that touches the filesystem, made once in
index.ts beside one shared clock.
open-project.ts no longer asks the store for availability by id. It
declares a Probe of a path, and index.ts hands it disk.probe. A write
typed just before its project was removed now lands in that project's
folder instead of being refused, the same "lands where it was typed" rule
requireProject already keeps for a change of project.
New tests pin the id and the dates, check the case rule for macOS, Windows
and Linux on one machine, report an unreachable share as unknown without a
two-second wait, cover not-a-directory, unreadable and relocation on a disk
that holds only what a test says, check that requireProject probes the open
project's own folder, and check the real disk through a symlink and ..
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Closes #115.
What changed
The store (
app/src/main/features/projects/projects-store.ts) now takes{ disk, now, newId, pathKey }beside the database:diskis aDiskit declares itself, with only the three calls it makes:canonicalize,isDirectoryandprobe.nowdates an add and an open, andnewIdnames a new project.pathKeyis the rule that makes two paths one directory. It comes from a purepathKeyOn(platform), which replaces the oldtoPathKeythat readprocess.platform.The store no longer imports
node:fsornode:crypto, and never callsnew Date()itself.The real disk (
app/src/main/features/projects/disk/disk.ts, new) holdsrealpath,stat, and the probe with its 2-second timeout, all moved out of the store unchanged. It's the one place underprojects/that touches the filesystem.Which project is open (
open-project.ts):RegistrylosescheckAvailability.createOpenProject(registry, probe, onMoved)takes aProbeof a path, andrequireProjectprobes the open project's own folder.index.tsmakes onecreateDisk()and onenow, and hands them to the store, tocreateOpenProject(asdisk.probe) and to the pages session. It also passesrandomUUIDandpathKeyOn(process.platform).app/CLAUDE.mdnames the new signatures. It also adds the rule that a module declares only the part of a dependency it calls: the store'sDiskandopen-project'sProbeare both met by the one disk.For the reviewer
requireProjectchecked availability by id, through the store. If the project had been removed betweencurrentProject()and that check, the project was no longer found and the write was refused. Now it probes the project's path directly, so a write typed just before its project was removed lands in that project's folder. That's the same "lands where it was typed" rulerequireProjectalready keeps when the open project changes. Nothing else about what is accepted or refused changes.projects:availabilitychannel still goes throughstore.checkAvailability(ids), which now probes through the disk it was handed.path.basenamestays in the store. It's string handling, not I/O.Tests
disk.test.ts(3 tests, new): canonicalizing through a symlink and.., a folder vs. a file vs. a missing path, and what the probe says for each.projects-store.test.ts(4 existing tests kept on the real disk, 6 new on a disk that holds only what a test says):pathKeyOnfordarwin,win32andlinux, and a second spelling being "already tracked" on the first two and "added" on Linux, all on one machineunknownwith no 2-second wait, and an untracked id left outopen-project.test.ts: the stub registry now carries its ownprobe, whichcreateOpenProjecttakes as aRegistryall the same. One new test checks thatrequireProjectprobes the open project's folder and nothing else, and probes nothing when no project is open.--user-data-dirand scratch folders), throughwindow.pbnotes:lastOpenedAt, availability said available, andshowand a write went throughrequireProjectnpm test(178 tests across all workspaces),npm run typecheckandnpm run lintpass.🤖 Generated with Claude Code