/** * Portable project identity — the `projectId` in the committed * `/.auden/config.json`. * * This is deliberately NOT `repo-id.ts`. That module derives * `HMAC(machine-local key, absolute path)`, which identifies *this checkout on * this machine* and is the right key for **evidence** — runs, actions, rule * manifests — because the server never learns a path from it. It is the wrong * key for **identity**: the same repo on two machines, or cloned to two paths, * yields two digests and the server can never learn they are one project. So * this adds a second field rather than repurposing the first * (docs/plans/guide-write-side-identity-plan.md → "Do not overload `repoId`"). * * One rule, three cases: * - a `project.id` committed in the repo config wins, always; * - otherwise derive it from the git **root-commit sha**, which survives * renames, re-hosting and SSH-vs-HTTPS remotes, and which a fork keeps — * the right answer when the point is sharing standards; * - otherwise (no git history, or a shallow clone — see below) mint one and * write it, so the next checkout reads rather than re-derives. * * **A shallow clone must never derive.** Git treats a shallow boundary as a * root, so a `--depth=1` CI checkout would derive a *different* root commit * than a full clone of the same repo and silently key the same project two * ways. There the committed id is required; absent one we mint and write, and * the caller tells the user to commit it. * * Kept free of citty/console so it unit-tests directly against a temp dir. */ import { spawnSync } from 'node:child_process'; /** Shared prefix so a projectId is recognizable wherever it surfaces. */ export declare const PROJECT_ID_PREFIX = "proj_"; export type ProjectIdSource = /** Read from the committed repo config — the declaration always wins. */ 'declared' /** Derived from the repo's root-commit sha. */ | 'git-root-commit' /** Minted, because derivation was unavailable or refused. */ | 'generated'; export type GeneratedReason = /** `git rev-parse --is-shallow-repository` said true (or could not be proven false). */ 'shallow-clone' /** Not a git work tree, or a work tree with no commits yet. */ | 'no-git-history' /** Several root commits are reachable from HEAD — the set is branch-dependent. */ | 'multiple-roots'; export type ProjectIdResolution = { status: 'ok'; projectId: string; source: ProjectIdSource; /** Present only when `source` is `generated`. */ generatedReason?: GeneratedReason; } | { status: 'refused'; /** `cwd` is the home directory, where the repo-config path is the token store. */ reason: 'home-collision' | 'malformed-config'; message: string; }; export type EnsureProjectIdResult = (Extract & { /** True when the id was just written to the repo config (so it needs committing). */ wrote: boolean; /** True when a stray `token` key was stripped from the committed file on write. */ removedToken: boolean; }) | Extract; /** Injected in tests so the git branches are exercisable without a real repo. */ export type ProjectIdOptions = { spawn?: typeof spawnSync; /** Injected in tests to make a generated id deterministic. */ generateId?: () => string; }; /** * True when the checkout is shallow — and also when the probe itself fails. * A probe we cannot answer is not proof of a full history, and the cost of * being wrong is asymmetric: minting an id the user commits is recoverable, * deriving a shallow boundary's sha as if it were the root is the silent fork * this whole rule exists to prevent. */ export declare function isShallowRepository(cwd: string, options?: ProjectIdOptions): boolean; /** Every root commit reachable from HEAD, sorted so the order is git-independent. */ export declare function gitRootCommits(cwd: string, options?: ProjectIdOptions): string[]; /** * The repo's *single* root commit, or null when there is none (no commits, or * git failed) or **several** (a subtree or unrelated-history merge). * * Several is not a tie to break. The set is only the roots reachable from the * current HEAD, so a clone initialized before such a merge and one initialized * after it would pick different shas and key one project two ways — the same * silent disagreement the shallow rule exists to prevent. Ambiguous means mint * an id and have the user commit it, not guess. */ export declare function gitRootCommit(cwd: string, options?: ProjectIdOptions): string | null; /** * Resolve the project identity for `cwd` without writing anything. Callers that * want the id persisted (so every clone agrees) use `ensureProjectId`. */ export declare function resolveProjectId(cwd: string, options?: ProjectIdOptions): Promise; /** * Resolve the project identity and persist it when it is not already declared, * so the value is committed and every other clone reads the same one instead of * re-deriving it. A declared id keeps its value — the file overrides. * * One exception to "declared means don't write": a file that already carries a * `token`. We tell the user to commit this file, so leaving a bearer token in it * because the id happened to need no update would be handing them a credential * to commit. Rewriting it with the *same* id routes through the writer's strip * and surfaces `removedToken` so the caller can tell them to rotate. */ export declare function ensureProjectId(cwd: string, options?: ProjectIdOptions): Promise; //# sourceMappingURL=project-id.d.ts.map