Skip to content

Graph concepts

The graph is deliberately split into a machine-local half (the catalog) and a committed half (the relations manifest). This page explains each model and how resolution ties them together.

The catalog: identity, location, lifecycle

Every repository daft touches gets a catalog entry:

FieldMeaning
identityThe repo's daft-id (a UUID created on first contact)
nameShort handle used by daft go <repo>, daft start <repo> <branch>, --repo, daft clone <name>
pathProject root on this machine
remote URLAs configured; also stored in normalized form for matching
default branchWhere bare daft go <repo> lands
removedTombstone timestamp — see below

Identity lives in the repository's git directory (daft-id), so it survives moves and dies with the repo; the catalog is the index that survives outside the repo. That asymmetry drives the lifecycle rules:

  • Registration is ambient. Clone, init, adopt, and everyday commands running inside a repo upsert its entry. daft repo add exists only for repositories daft has never operated in, and for renaming (--name).
  • Names are unique among live entries. Two clones that would derive the same name get suffixed (api, api-2) with a notice; rename with daft repo add --name.
  • Removal is a tombstone, not a deletion. daft repo remove marks the entry removed and keeps it: the repo's hook-job logs stay addressable (daft hooks jobs --repo <name>) and daft clone <name> restores the repo from its recorded remote. Re-cloning at the same path creates a fresh identity that takes over the live name; the old identity stays as a removed entry.

The relations manifest: committed, URL-keyed edges

Relations are declared in daft.yml, next to hooks:

yaml
relations:
  - url: git@github.com:acme/api-client.git
    name: client # optional friendly label
    kind: consumer # optional, free-form

You rarely write this by hand. daft repo link resolves a catalog name, a repo path, or a URL to the portable remote URL and appends a well-formed, deduped entry; daft repo unlink removes one. Both edit only the relations: block — the rest of daft.yml is untouched — and leave the file for you to commit.

Three properties are load-bearing:

  • Keyed by remote URL, not by path or name. Paths and catalog names are machine-local; the remote URL is the one identity every teammate shares. URL forms are normalized before matching, so git@github.com:acme/x.git, ssh://git@github.com/acme/x, and https://github.com/acme/x all name the same repo.
  • Directed. Declaring the client in the service's manifest does not make the service appear in the client's. If both sides drive coordinated changes, declare both edges.
  • Open semantics. kind is a free-form label (client, library, deploy, …) for humans and tooling to grow into; daft does not interpret it.

Resolution: manifest → catalog → path

When a command needs a related repo (daft exec --related, daft start --with-related, daft repo info), each manifest edge resolves in one step: normalize the edge's URL and look it up among the catalog's live entries' normalized remotes. A hit yields the local path — wherever that teammate happened to clone the repo. A miss means "not cloned here", and daft says so with the exact daft clone <url> to fix it.

The same live-first rule applies everywhere the catalog resolves anything: live entries win over removed ones, and within daft go, anything resolvable in the current repository (worktrees, local branches, remote branches) always wins over a catalog repo of the same name — --repo addresses a shadowed repo explicitly.

Where to next

Released under MIT or Apache-2.0.