Documentation
¶
Overview ¶
Package resolve turns a discovered session file into the project it belongs to. It peeks the file header for the working directory, then resolves that directory's git origin remote to a canonical project key. A folder with no usable git remote is standalone, and a folder that no longer exists on disk is orphaned. Operational Git failures are retried briefly and returned as errors if they persist, so they cannot be mistaken for definitive repository state or retained in the project cache. A file is skipped only when its header carries no recognizable session signature, so an arbitrary *.jsonl that merely shares the suffix (a tool-output log, an event feed) under a custom extra_root is rejected rather than ingested as a junk session.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type GitRunner ¶
GitRunner runs a git subcommand in dir and returns its trimmed stdout. The default shells out to the system git; tests inject their own.
type Header ¶
type Header struct {
Cwd string
GitBranch string
SourceID string
// contains filtered or unexported fields
}
Header is the minimum the client reads from a session file before deciding where it belongs. The full parse is the server's job.
func PeekHeader ¶
PeekHeader reads only as much of the file as it needs to extract cwd, the git branch, and a stable, unique source session id for the file. The id has to be unique per file: the server keys sessions on (user, agent, source_session_id), so two files that share an id fold into one row and clobber each other. Codex and pi files are already one-id-per-file, but Claude records the same sessionId in a main session file and in every subagent and workflow file under it, so those need an id derived from the file's location, not just its in-file sessionId.
It also performs the positive session test: a file is a session only if at least one peeked line matches its agent's session signature. A parseable *.jsonl that never does (a tool-output log, an event feed under a custom extra_root) returns errNotSession so Resolve can skip it with a clear reason rather than upload it as a junk standalone/orphaned session.
Reading is Lstat-then-Open-then-verify rather than the simpler Stat-then-Open: discover.Discover already rejected a symlink at f.Path when it found the file, but a stat-and-open a moment later reopens a time-of-check-to-time-of-use window, since both Stat and Open follow a symlink. If f.Path was swapped for one in between (deliberately, or by an ordinary rename-based file replace racing the walk), a plain Stat+Open would silently read through it and upload whatever it points to. Lstat here rejects a symlink outright before ever opening it, and comparing the Lstat and the opened file's own Stat with os.SameFile closes the remaining gap: if the path was swapped for a different file of any kind between the two calls, the file identity no longer matches and the read is refused rather than silently served from the wrong file.
type Kind ¶
type Kind string
Kind classifies how a session resolves to a project.
const ( // KindRemote is a session whose working directory resolves to a canonical git // remote. ProjectKey holds that remote. KindRemote Kind = "remote" // KindStandalone is a session whose working directory exists but yields no // usable git remote: not a repository, no origin, multiple origin URLs, or an // unrecognized origin URL. It is backed up and keyed by its local location. KindStandalone Kind = "standalone" // KindOrphaned is a session whose working directory is unknown or no longer // exists on disk, so its remote can never be resolved. It is still backed up. KindOrphaned Kind = "orphaned" )
type Resolver ¶
type Resolver struct {
// contains filtered or unexported fields
}
Resolver resolves files to projects. Its bounded in-memory cache avoids repeat Git processes, invalidates direct repository config changes immediately, and caps the age of results that depend on other Git config sources.
func New ¶
func New() *Resolver
New builds a Resolver with the default system-git runner and ssh alias map.
func (*Resolver) Resolve ¶
Resolve peeks the file header and classifies the session. A session with a resolvable git remote is KindRemote; one whose folder exists but has no usable remote is KindStandalone; one whose folder is unknown or gone is KindOrphaned. All three are returned ready to upload. Header I/O failures and exhausted transient Git failures are returned in Result.Err; a readable non-session is the only skipped result.
type Result ¶
type Result struct {
File discover.File
Header Header
Kind Kind
ProjectKey string
LocalRoot string
Skipped bool
Reason string
Err error
}
Result is the outcome of resolving one file. Kind classifies the session; ProjectKey is the canonical remote only when Kind is KindRemote. Reason carries the human-readable detail behind a standalone or orphaned classification (and a positive non-session verdict). Skipped is true only when readable content is not a session for its declared agent. Err carries I/O and structural read failures so a backup command cannot silently count an unreadable session as a successful skip.
LocalRoot is set only for a standalone session whose folder is a live git worktree: it holds the main worktree shared by every worktree of the repo, so the server can collapse a local-only repo's worktrees into one project the same way a canonical remote collapses a remote-backed repo's. It is empty for remote sessions, for orphaned sessions (the worktree is gone, so git cannot report it), and for non-git standalone folders.