OPENPROGRAM / PROJECT MANAGEMENT DESIGN

Move projects while retaining history

Store application data under the user directory, grouped by stable project IDs. Review visible usage states below; implementation rules are collapsed at the end.

Portable project identity · Implementation contract and acceptance requirements.

01

Where conversations live

First open

OpenProgramProposed interface
Projects
Paper notes
/Projects/paper
Start a conversation
Registration remains in the picker and management page.

Start a conversation

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Projects/paper
Summarize this paper
Saved conversations and history remain available.

Reopen

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Projects/paper
Summarize this paper
Saved conversations and history remain available.

Application data stays under the local user directory. Each project has its own directory and each conversation retains its Git history. No new chat records are written inside working folders.

02

Move or rename a folder

Before

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Projects/paper
Summarize this paper
Saved conversations and history remain available.

Move in the file manager

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Research/paper
Summarize this paper
Saved conversations and history remain available.

Return to the conversation

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Research/paper
Summarize this paper
Saved conversations and history remain available.

Update the location automatically only when directory identity is established. Project ID, title, conversations and archive status stay unchanged; chat files do not move. Otherwise offer Locate folder.

03

Move while the app is closed

Before closing

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Projects/paper
Summarize this paper
Saved conversations and history remain available.

Move while closed

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Research/paper
Summarize this paper
Saved conversations and history remain available.

Reopen

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Research/paper
Summarize this paper
Saved conversations and history remain available.

At startup or access, find the portable project marker within the bounded search roots. If the destination is outside those roots, reopen or locate the folder once. History stays readable; no idle polling is used.

04

Disconnect and reconnect

Connected

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Volumes/Research/paper
Summarize this paper
Saved conversations and history remain available.

Disconnected

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Volumes/Research/paper
Summarize this paper
Saved conversations and history remain available.

Reconnected

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Volumes/Research/paper
Summarize this paper
Saved conversations and history remain available.

Projects with unarchived conversations remain in the sidebar. History stays readable; working files, attachments referring to them and new tasks are unavailable until reconnection and identity validation. History is also unavailable if application storage itself is offline.

05

Copy or move across drives

Original

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Projects/paper
Summarize this paper
Saved conversations and history remain available.

Copy elsewhere

OpenProgramProposed interface
Projects
Paper notes
/Volumes/Other/paper
Start a conversation
Registration remains in the picker and management page.

Explicitly locate replacement

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Volumes/Other/paper
Summarize this paper
Saved conversations and history remain available.

When the original still exists, opening a copy creates a separate project. A copy-then-delete move retains the portable marker and can reconnect the original project. Multiple discovered copies require explicit Locate; the marker cannot distinguish a single surviving copy from a move.

06

Another folder occupies the old path

Original

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Projects/paper
Summarize this paper
Saved conversations and history remain available.

Folder replaced

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Projects/paper
Summarize this paper
Saved conversations and history remain available.

Before repair

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Projects/paper
Summarize this paper
Saved conversations and history remain available.

An identical path does not establish project identity. Keep history, block new tasks, and locate the original or explicitly confirm a replacement. Do not take over a location already registered to another project.

07

Empty projects and archives

No conversation

OpenProgramProposed interface
Projects
Paper notes
/Projects/paper
Start a conversation
Registration remains in the picker and management page.

Has conversations

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Projects/paper
Summarize this paper
Saved conversations and history remain available.

All archived

OpenProgramProposed interface
Projects
Paper notes
/Projects/paper
Archived conversations · 12
Registration remains in the picker and management page.

The sidebar shows only projects with unarchived conversations. The picker and management page retain registrations; management includes archived conversations. Archiving keeps records; permanent conversation deletion removes their storage.

08

Existing data migration

Before upgrade

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Projects/paper
Summarize this paper
Saved conversations and history remain available.

During migration

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Projects/paper
Summarize this paper
Saved conversations and history remain available.

Old drive unavailable

OpenProgramProposed interface
Projects
Paper notes
Summarize this paper
Paper notes
/Projects/paper
History content unavailable
Only registration information is available.

Verify messages, Git history, branches, recovery backups and stored attachments together. Retain the source on failure and resume after restart. Keep offline-source registrations; never imply a local copy exists or replace history with an empty session.

Try the resulting states

This simulation does not modify real projects.

Storage, relocation, migration and acceptance (expand)

Adopted choices

Adopt application-owned conversation storage shared by Claude Code, Codex and DeepSeek Harness. Group by project as Claude and DeepSeek do, but use stable project IDs rather than path encodings. Keep existing OpenProgram Git repositories, branches and recovery semantics instead of rewriting to JSONL.

Codex filters against recorded cwd and recent turn context. DeepSeek workspace attachment validates real paths against immutable session header cwd and rejects unavailable or mismatched directories. These paths do not establish automatic tracking of arbitrary folder moves; they also do not imply that moving a folder deletes history.

Further source verification: Codex Project stores id separately from roots; update_project updates roots by id and threads reference project_id. Adopt this separation. DeepSeek Workspace also has an independent ID, but its current path is explicitly immutable; do not adopt that restriction.

Storage and identity

Use the existing profile/state root, default ~/.openprogram. Preserve registered project IDs; generate opaque unique IDs for new projects. Session IDs remain globally unique. Names are display metadata. Location records carry path, revision and a portable folder identity token. Existing default-session placement remains compatible.

~/.openprogram/
  projects/
    projects.json                    # registry / location / identity
    <project-id>/                    # existing project settings
  sessions/
    index.json                       # rebuildable listing
    locations.json                   # legacy compatibility
    projects/
      <project-id>/
        <session-id>/                # complete session Git repository
        .file-recovery/<session-id>/ # external rewind data
    <legacy-session-id>/             # existing default sessions
  memory/                            # existing memory store, unchanged

Portable project identity

A working folder contains .openprogram/project.json with a versioned random identity token. The registry maps this token to the existing project ID. Paths are replaceable location data; conversation repositories remain under the application state directory. Renames, parent-directory moves and copy-then-delete moves preserve the token.

Startup, project-list requests and file/execution access reconcile unavailable locations. Search registered project parents, or their nearest surviving ancestor when a parent moved, up to three directory levels and 4,000 visited entries; exclude the home directory and its ancestors, hidden/dependency directories and directory symlinks. An incomplete search or multiple matching folders does not relocate. Opening a moved folder anywhere checks its token directly and restores the original project when its previous location no longer has that identity. Arbitrary unknown destinations require opening or locating the folder once. There is no background recursive home scan or platform-specific observer.

If the original location still has the token, opening a copy registers a separate project and writes a fresh token in the copy. If the original is gone and only one candidate is found, retain the original project, including for cross-volume copy-then-delete moves. A copy followed by deletion of the original is indistinguishable from a move under this portable model. Multiple candidates retain explicit Locate. A marker is identity metadata, not an authorization credential; file permissions and execution authorization are revalidated at the new path.

Ordinary metadata edits and portable location checks never create or overwrite a marker. New registration and explicit Locate create it atomically, refuse symlinked metadata and malformed files, and do not overwrite a marker owned by another registered project. Legacy records with an exactly matching standard-library device/inode pair can be upgraded once at their recorded path; other unverified legacy records need Locate once. Device/inode values and native bookmarks are not used for ongoing identity after upgrade. Existing history and project IDs are retained.

Adopt independent identity and location from the inspected Codex/OpenCode designs, and explicit Locate from GitHub Desktop. Keep Claude-style application-owned session storage. Reject platform-specific bookmarks/events as a required identity mechanism and reject Git remote equality as proof that two checkouts are the same working folder. The existing platform links below are comparison references only.

Acceptance: public registration plus rename/restart, moving a parent, copy-then-delete, opening a destination outside discovery roots, simultaneous copies, conflicting markers, incomplete discovery, read-only or symlinked metadata, missing folders and explicit legacy Locate. Verify project/session IDs, persisted history and execution refusal at a wrong directory. No native framework or platform event API is invoked.

Execution and references

History reads do not require the working folder. Block new project tasks when it is unavailable, without falling back to HOME, server cwd or session workdir. Do not retarget or replay running subprocesses; validate before subsequent file operations. Check independent worktrees separately.

Do not replace absolute paths inside historical messages or checkpoint manifests. New project file references use project ID plus relative path; unavailable legacy attachment paths remain explicitly unavailable. Audit uploaded attachments, session resources and external recovery storage for migration. Preserve memory text/history; permanent source-conversation deletion removes its clickable link or marks it deleted, without creating an independent source copy. Archived sources stay accessible.

Migration and deletion

Quiesce every session writer before migration, including model tasks, fork children, jobs, checkpoints and attachment writers. A Python lock or singleton worker lock alone is insufficient. Initial migration runs before execution starts; reconnecting legacy sources require the same quiescent boundary, never a hidden move during ordinary reads.

Journal source, destination, identities and a full inventory. Copy into private staging, verify content and unchanged source, durably publish session and external recovery data, then atomically publish authoritative placement before cleaning old storage. Publication failure never authorizes source deletion. Resume after interruption without overwriting conflicting destinations. Serialize deletion with migration and retain durable deletion intent so recovery cannot resurrect deleted sessions. Maintain one authoritative writable location.

Rebuild indexes from both layouts. Never prune a temporarily unavailable migrating session as empty. Project relocation updates only location revision, not conversation placement. Registration removal, archiving, permanent conversation deletion and working-folder deletion remain distinct operations; never cascade into user working files.

Implementation and acceptance

  1. Probe portable identity and bounded location lookup with temporary folders.
  2. Implement home storage, compatible reads, index reconstruction and correct deletion.
  3. Implement quiescent, recoverable migration and failure/restart/delete tests.
  4. Use shared resolution, identity validation, execution refusal and visible states for startup, access failure, user Locate and explicit folder reopening.
  5. Independent specification review, then quality review, affected and full gates, default App refresh and actual interaction checks.

Acceptance covers same history after move/restart, migrated history while offline, no execution in a wrong directory, copies and same-path replacements, archives, failure at every migration stage with intact messages/branches/Git/recovery data, no idle scanning and bounded lookup.

Exact compatibility and operation rules

“Chat files do not move” and offline readability apply to new or migrated conversations. Initial legacy migration physically copies data. Location checks run on startup, project-list requests, access failure, Locate and opening a folder. They do not poll while idle.

Preserve existing path-derived IDs without recomputation. New registrations generate UUIDs after checking validated canonical-path registrations; retry ID collisions, never merge conversations. Existing projects keep their IDs throughout upgrade.

Automatic relocation requires a matching portable token and an unambiguous discovered location. Manual location permits an explicit replacement after showing old and new paths, without requiring platform-specific file identity. Both revalidate location revision, permissions and destination ownership at commit; reject another project's destination. Legacy placement repairs belong in a durable operation journal: show “Updating location”, withhold full success and new execution until all compatibility mappings recover. New-layout relocation updates only project location.

All updated session writers, including CLI, worker, fork children and rewind, share an interprocess lock keyed by session ID under application data. Re-read storage revision and location after acquiring it. Stop accepting tasks and wait for registered writers; defer on timeout instead of killing tasks or copying concurrently. Migration holds exclusive access through copy and publication. Stop pre-upgrade processes through lifecycle management first; mixed-version writes to one profile are unsupported. An empty in-process task list is insufficient.

The project location overrides stale session work_dir. For registered additional directories and source_folders inside the confirmed old project root, derive the new path from the relative suffix, then validate identity and permissions. External additional directories keep absolute paths and independent checks. Unavailable required directories block dependent file operations with a repair affordance. Do not rewrite historical messages, authorization snapshots or rewind manifests. If rewind still targets an unavailable old location, report it rather than creating or overwriting files there. Revalidate permissions at the new location.

Sidebar visibility derives from the current valid session set after filtering archived conversations. Management total counts include archives and show archived counts separately; total session_count is not the sidebar predicate. Refresh the same derived set after archive, restore or deletion. Hiding or removing registration does not delete working files.

Implementation status

The source entry points are store/project/identity.py, discovery.py, location.py and the execution guard in agent/internals/_workdir.py. Public-entry acceptance lives in tests/component/store/test_project_portable_identity.py, with project, file, migration and execution regression suites. Release acceptance requires independent specification and quality review, the complete affected verification manifest, and a default-App refresh. Review reports and release logs identify the exact verified revision; this design does not certify an installed App merely from its source state.

Source and platform references

The inspected official Claude Code native distribution embeds JavaScript selecting ~/.claude/projects/encoded-path/session-id.jsonl. This evidence comes from a local distribution inspection, not a claim that original TypeScript source is public.