Repository navigation
feat(core): archive projects whose directories are gone - #54354
Conversation
There was a problem hiding this comment.
Works as described, and I found nothing blocking. I checked it against a real server: after deleting a project's directory and restarting, GET /api/project hides the project, ?archived=true returns it with time.archived set, and opening the directory again brings it back with time.updated unchanged. The TUI open dialog (Ctrl+O) no longer lists the deleted project (screenshots below). Core project/location tests pass, and core, server and client type-check.
Public API: all changes are additive, with one change to default behaviour.
GET /api/projectgets an optionalarchivedquery parameter ("true"/"false"). It also now excludes archived projects by default, so any outside caller that expected every known project has to passarchived=true.Project.Info.time.archivedis a new optional field.- The client's
project.list(input?)accepts{ archived?: boolean }. project.updatedis reused with no new event types.- There is a new nullable
project.time_archivedcolumn, with a migration.
One gap worth closing: packages/app (web and desktop) gets its project list from the now-filtered API, but its project.updated handler (applyProjectUpdate in packages/app/src/runtime/server/sync.tsx) only patches the projects it already has. A project archived by the hourly sweep therefore stays in the app's lists until they are fetched again, and a project brought back by opening it doesn't reappear until then either. The Solid client store change only covers the TUI side.
041ee48 to
256f7eb
Compare


Issue for this PR
Closes #
Type of change
What does this PR do?
Keeps the project list from growing forever by archiving projects whose folders no longer exist.
project.time_archivedcolumn (migration included), exposed asProject.Info.time.archived.Project.sweepruns at server startup and then hourly (project-archive-sweepnode). For each unarchived, non-global project:resolvealready does lazily;Filesystem errors count as "exists", so a transient failure never archives anything.
Project.resolveclearstime_archived, so opening any directory of the project brings it back.GET /api/projecthides archived projects by default;?archived=trueincludes them.project.updated, sent only when the value actually changes. Archiving leavestime_updatedalone, likeactivate.project.list()filters archived projects so they disappear live whenproject.updatedarrives.There is intentionally no manual archive action.
How did you verify your code works?
packages/core/test/project.test.ts: list filtering, archive on sweep plus unarchive on resolve (including the events sent and preservedtime_updated), moving the canonical directory to a surviving clone, and never archiving the global project.bun run checkpasses; core project/location/layer-node/projector/database tests, the server suite, and the TUI data tests pass;bun run migration --checkis clean.Screenshots / recordings
N/A
Checklist
— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊
Added by the opencode bot (AI-generated).
Fixes #54320
Related: #49442, #39903, #51197
Public API: GET /api/project hides archived projects by default and adds the optional
archivedquery parameter; adds optionalProject.Info.time.archived(additive; callers that want every project needarchived=true)