/** * Graph links from accepted verification evidence (T13950). * * When `cleo verify` accepts evidence atoms it has already validated them * against git, the filesystem or GitHub. Those atoms are the most reliable * statement of what a task touched, so they become graph edges: * * | atom | graph write | * |-------------|----------------------------------------------------------------| * | `commit` | `task: --touches_code--> commit:` (`ensureCommitNode`) | * | `pr` | the same, for the PR's merge commit | * | `files` | `task: --touches_code--> file:` | * | `decision` | `decision: --applies_to--> task:` | * * Other atoms (tool runs, test runs, notes, `satisfies` bindings that already * live in the tasks store) add no edge. Writes are explicit (they do not depend * on `brain.autoCapture`) and idempotent: nodes are created when absent and an * existing edge is left unchanged, so re-verifying adds nothing. * * Node ids are permanent and sync between devices, so a file node is keyed by * its POSIX path relative to the checkout that holds it. A path outside both * the evidence repository and the store root gets no node and is reported in * `skipped`. File and commit ids of every repository but the store's primary * one carry a repository key (`file:::`, `commit:::`; * T14012, {@link createEvidenceRepoResolver}), so same-named files in two child * repositories stay separate nodes. * * Code placed in `packages/core/` per Package-Boundary Check — verified against AGENTS.md. * * @module memory/evidence-graph-links * @task T13950 */ import type { EvidenceAtom } from '@cleocode/contracts'; /** Provenance stamped on every edge this module writes. */ export declare const EVIDENCE_EDGE_PROVENANCE = "evidence:verify"; /** Graph writes from one accepted verification. */ export interface EvidenceLinkResult { /** Commits linked (from `commit` and `pr` atoms). */ commits: number; /** Files linked. */ files: number; /** Decisions linked. */ decisions: number; /** `files:` paths given no node because they lie outside the evidence repository. */ skipped: string[]; } /** A file's node key: its repository (`null` = the store's own) and repo-relative path. */ export interface EvidenceFileKey { /** Repository key, or `null` for the store's own repository. */ repo: string | null; /** POSIX path relative to the checkout holding the file. */ path: string; } /** * Resolves which repository an evidence file or commit belongs to (T14012), * caching every git probe by directory for the resolver's lifetime (one link * call or one migration run). */ export interface EvidenceRepoResolver { /** * Node key of an evidence file, or `null` when it lies outside both the * execution and the store root, or its path contains the id separator. */ file(path: string, executionRoot: string): EvidenceFileKey | null; /** * Repository key of the checkout holding `dir`: `null` for the primary * repository, for a directory in no checkout, and for an outer repository * that tracks nothing under the store root. */ repoOf(dir: string): string | null; /** * Repository of a commit, by the same rule as files: the key of the * checkout that contains it (`repo: null` for the primary repository), or * `null` when no known checkout, or checkouts of more than one repository, * contain it. */ commit(sha: string): { repo: string | null; } | null; /** The checkouts the store knows: primary, its children, and their submodules. */ checkouts(): readonly string[]; } /** * Repository key of an `origin` URL, the same on every device for the same * repository: `github.com//` through the github.com-anchored * {@link extractRepoOwnerAndName} the PR evidence uses, else `/`. * Scheme, user, port (only a `scheme://` URL has one; in scp form the digits * after the colon are an owner) and `.git` are dropped and the key is * lowercased, so `git@github.com:O/R.git`, `https://github.com/o/r` and * `ssh://git@github.com:22/o/r` agree. A local-path remote has no key. * * @param url - Raw `origin` URL. * @returns The key, or `null`. * @example * ```ts * originRepoKey('ssh://git@github.com:22/O/R.git'); // 'github.com/o/r' * ``` * @task T14012 */ export declare function originRepoKey(url: string): string | null; /** * Create an {@link EvidenceRepoResolver} for one CLEO store. * * The primary repository is the store root's own: the checkout whose toplevel * is the store root, or an outer repository that tracks files under it (a * store inside a monorepo); failing that, the declared evidence git root * (`evidence.gitRoot` or `CLEO_EVIDENCE_GIT_ROOT`). Its files and commits, in * any of its worktrees, keep unqualified ids, so single-repo stores never * change. Without either, nothing is primary: the id of a child repository * never depends on how many repositories the store holds, so adding one later * moves nothing. Every other checkout (a child repository, a submodule) is * keyed by {@link originRepoKey}; without a usable origin, by its main * checkout's directory relative to the store root. An outer repository that * tracks nothing under the store root (a dotfiles repository at `~`) is * ignored: files under the store root are keyed against the store root. * * @param storeRoot - CLEO store root. * @returns The resolver. * @example * ```ts * const repos = createEvidenceRepoResolver('/work'); * repos.file('api/src/x.ts', '/work'); // { repo: 'github.com/o/api', path: 'src/x.ts' } * ``` * @task T14012 */ export declare function createEvidenceRepoResolver(storeRoot: string): EvidenceRepoResolver; /** * The POSIX path of an evidence file relative to the checkout that holds it, * or `null` when it lies outside both the execution and the store root. * * The file is located as `validateFiles` located it: the execution root when it * exists there, else the store root, else (validated at a commit only) the * execution root. Keying by the holding checkout makes a file verified from a * worktree, from the main checkout, or through a store that parents several * repositories (T14005) one node with the commit-anchored form; the repository * itself is {@link EvidenceRepoResolver.file}'s `repo` (T14012). * * @param path - Path as the `files:` atom stored it. * @param executionRoot - Root of the repository the evidence is about. * @param storeRoot - CLEO store root. * @returns The normalised path, or `null`. * @example * ```ts * repoRelativeEvidencePath('./src/a.ts', '/repo', '/repo'); // 'src/a.ts' * ``` */ export declare function repoRelativeEvidencePath(path: string, executionRoot: string, storeRoot: string): string | null; /** * Link a task to the commits, files and decisions its accepted evidence names. * * @param projectRoot - Project whose brain graph is written. * @param taskId - Task the evidence was accepted for. * @param atoms - Validated atoms the verification stored. * @returns Counts of linked targets (repeat links count again; writes are * idempotent) and the paths skipped as outside the repository. * @example * ```ts * await linkVerifiedEvidence(root, 'T123', evidenceStored); * ``` */ export declare function linkVerifiedEvidence(projectRoot: string, taskId: string, atoms: readonly EvidenceAtom[]): Promise; /** One evidence edge re-pointed by {@link migrateEvidenceRepoIds}. */ export interface EvidenceRepoMove { /** Source node of the edge (`task:`). */ from: string; /** Unqualified node id the edge pointed at. */ oldTarget: string; /** Repo-qualified node id it points at now. */ newTarget: string; } /** What {@link migrateEvidenceRepoIds} moved and left. */ export interface EvidenceRepoMigrationResult { /** True when nothing was written (the default; `apply` writes). */ dryRun: boolean; /** Receipt an apply wrote before its first write, or `null` for a dry run. */ receiptPath: string | null; /** Evidence edges re-pointed (or, in a dry run, that would be). */ moved: EvidenceRepoMove[]; /** Repo-qualified nodes created, each from the old node it copies. */ nodes: Array<{ oldId: string; newId: string; }>; /** Evidence edges left unchanged because their repository cannot be told. */ unresolved: Array<{ from: string; target: string; reason: string; }>; } /** Receipt of an applied {@link migrateEvidenceRepoIds} run: every old id → new id. */ export interface EvidenceRepoMigrationReceipt { /** Receipt format version. */ version: 1; /** ISO timestamp of the apply. */ migratedAt: string; /** `started` before the first write; `complete` once the transaction committed. */ status: 'started' | 'complete'; /** Edges re-pointed, old target → new target. */ moved: EvidenceRepoMove[]; /** Nodes created, old id → new id. Old nodes are kept for a later prune. */ nodes: Array<{ oldId: string; newId: string; }>; /** Edges left unchanged. */ unresolved: EvidenceRepoMigrationResult['unresolved']; } /** * Default receipt path of an applied evidence repo-id migration. * * @param projectRoot - Project root. * @param migratedAt - ISO timestamp of the apply. * @returns Absolute receipt path under `.cleo/backups/migrations/`. * @example * ```ts * defaultEvidenceRepoReceiptPath(root, '2026-10-24T00:00:00.000Z'); * ``` * @task T14012 */ export declare function defaultEvidenceRepoReceiptPath(projectRoot: string, migratedAt: string): string; /** * Forward migration of evidence file and commit node ids to repo-qualified ids * (T14012), by the rule {@link createEvidenceRepoResolver} applies to new links. * * Only `evidence:verify` edges move. A commit belongs to the repository whose * checkout contains it. A file belongs to the one repository whose checkout has * its path, or, when several do, to the one the same task's commits belong to. * Anything still ambiguous or absent everywhere is reported in `unresolved` * and left as it is. Old nodes are never deleted here (other tables and * writers may still reference them; a later prune owns that); a copied commit * node records its `repo` in its metadata. Re-running is a no-op. * * A dry run is the default; only `apply` writes, after recording a receipt * (status `started`, every old id → new id) and completing it once the single * transaction commits. It never runs automatically. * * @param projectRoot - CLEO store root. * @param options - `apply` writes; `receiptPath` overrides the default; `now` for tests. * @returns The moves, the created nodes, the unresolved edges and the receipt path. * @example * ```ts * const plan = await migrateEvidenceRepoIds(root); * const done = await migrateEvidenceRepoIds(root, { apply: true }); * ``` * @task T14012 */ export declare function migrateEvidenceRepoIds(projectRoot: string, options?: { apply?: boolean; receiptPath?: string; now?: Date; }): Promise; //# sourceMappingURL=evidence-graph-links.d.ts.map