import { Buffer } from "node:buffer"; import { ObjectStore } from "../store/objectStore.ts"; import { type ReductionResult } from "../reducer/reducer.ts"; import type { OwnerRule } from "../objects/types.ts"; import { Keyring, type KeyRecord } from "../core/identity.ts"; import { type LeaseConflict } from "../concurrency/lease.ts"; /** A capture whose only effect would be to remove (almost) every tracked file. */ export declare class MassDeleteError extends Error { readonly removed: string[]; readonly tracked: number; constructor(message: string, info: { removed: string[]; tracked: number; }); } import { Metrics } from "../observe/metrics.ts"; import { type Logger } from "../observe/logger.ts"; import type { Actor, AnyObject, EvidenceKind, EvidenceResult, Intent, IntentKind, Line, Membership, Operation, OperationBody, OperationTarget, Policy, Protection, RoleName, ScopeRef, Undo, View, ViewQuery, WorkLease } from "../objects/types.ts"; /** * Phase 14 (docs/17 §14.2): the four-way contract of `submitIntegration`. There is no * "pull and redo" outcome by design — ops are append-only, so no work is ever redone: * - advanced — the head moved to an integrated checkpoint containing your work * - conflict — a minimal repair packet (the ONLY human/agent decision point) * - needs_evidence — run validation ONCE against the integrated tree, resubmit the ticket * - queued — another ticket holds the needs_evidence reservation; retry later * - rejected — a hard gate (role/approvals/causal-incompleteness) refused it */ export type IntegrationResult = { verdict: "advanced"; head: string; integration: string; } | { verdict: "conflict"; packet: ConflictPacket; integration: string; } | { verdict: "needs_evidence"; integratedCheckpoint: string; treeHash: string; requiredChecks: EvidenceKind[]; missingLocally: string[]; ticketId: string; integration: string; } | { verdict: "rejected"; reason: string; } | { verdict: "queued"; behindTicket: string; retryAfterMs: number; }; /** The minimal repair packet a `conflict` verdict carries (docs/17 §14.2 step 5): * per-key counterpart ops + prior human rulings on the same key (decision memory), so * an agent can prepare a precedent-based decision proposal without re-reading the repo. */ export interface ConflictPacket { conflicts: { key: string; reason: string; /** The contending ops on this key (oid + who/why), for a targeted repair. */ options: { op: string; actor: string; purpose: string; }[]; /** Overlapping line regions when this is a text-merge conflict (merge3 shape). */ regions?: import("../merge/merge3.ts").ConflictRegion[]; /** Prior rulings on the same key — reuse instead of re-litigating (docs/17 §14.2). */ priorDecisions: { reason: string; futurePolicy?: string; decidedBy: string; }[]; }[]; } /** * The persisted needs_evidence reservation (`.avcs/queue/.json`, docs/17 §14.3): * survives a hub restart so an in-flight ticket keeps its slot until the TTL. * * EXPORTED because the file is not the only place this can live. A multi-instance consumer * cannot share a local aux file, so it keeps the reservation in its own store and translates * both ways. That translation was being written against `Record` — which * means a field added here would not have broken anything, it would just have stopped being * carried, silently. A name is the minimum that makes the translation type-checked. */ export interface IntegrationReservation { ticketId: string; submittedCheckpoint: string; integratedCheckpoint: string; treeHash: string; requiredChecks: EvidenceKind[]; by: string; expiresAt: string; } /** * How AVCS history relates to a co-located git repo (docs/14). The git tree git tracks * is always the materialized *projection* — a derived artifact AVCS can recompute. What * differs by mode is whether the rich `.avcs/` history travels with it: * * - `sidecar` (default): `.avcs/` is fully git-ignored. git sees ONLY the clean * projection, so a single developer adopts AVCS locally with zero team * friction — no team decision required. History stays local (or syncs via * the hub between adopters). * - `committed`: `.avcs/objects` + `refs` are committed alongside the projection, so the * full intent/decision history travels via `git push`. This is a team-wide * adoption decision; flip to it with `setGitMode("committed")` once agreed. */ export type GitMode = "sidecar" | "committed"; /** The trunk branch assumed when `.avcs/config.json` records none (docs/20 §3.1). */ export declare const DEFAULT_TRUNK = "main"; /** * The branch names that count as trunk when none is configured. The pre-trunk bridge * special-cased exactly this pair, so keeping both is what makes an unconfigured repository * — including a `master`-default one — behave as it always did (docs/20 W7). */ export declare const LEGACY_TRUNK_BRANCHES: readonly ["main", "master"]; /** * A named hub URL persisted in `.avcs/remotes.json` (Phase 13.1). Per-replica * configuration — an aux file, never an object, never gossiped. `autoSync` + * `freshnessMs` are read by the live-convergence layer (Phase 15): a materialize * older than the freshness window fires a background sync. */ export interface RemoteConfig { url: string; autoSync?: boolean; freshnessMs?: number; } /** * One shared build-environment path (docs/21 §3.1), persisted in `.avcs/shared-paths.json`. * * The core treats `path` as a PATH RULE and nothing else — it does not know that * `node_modules` is a dependency tree, or that `pnpm-lock.yaml` is a lockfile, exactly as * `.avcsignore` (#10) knows nothing about what it excludes. That ignorance is docs/16 §2-2, * and it is what keeps the core out of every build ecosystem. * * - `path` — relative to the projection root, forward slashes, no `..`, not absolute. * - `keyFrom` — the files whose PROJECTED content derives the cache key (§3.2). This is the * declarative answer to docs/16 §10 question 1 ("who names the shared key"): * the user declares *which files decide the environment*, and the core only * hashes their content, never reads their meaning. `[]` means one cache for * every workspace — allowed, because the user said so, and warned about. * - `mode` — `symlink` (default, cost 0) or `copy` for toolchains that refuse a symlinked * dependency tree (§R1). `copy` is the DANGEROUS one for capture: it puts a * real directory in the tree, so the ignore composition (§3.5) is its only * defence. */ export interface SharedPathEntry { path: string; keyFrom?: string[]; mode?: SharedPathMode; } export type SharedPathMode = "symlink" | "copy"; /** * What {@link Repo.linkSharedPaths} did for one entry (docs/21 §3.4). * * `populated` — whether the cache directory is non-empty — is the WHOLE interface for "does * this need an install?". The core creates the place and connects it; filling it belongs to * the caller (human/agent/CI). The moment the core knew how to install anything, docs/21 §2 * principle 2 would be broken, so there is deliberately no hook, no command template and no * package-manager guess anywhere near this type. */ export interface SharedPathLink { path: string; key: string; /** Absolute path of the store-local cache directory (`/shared//`). */ cache: string; /** Absolute path inside the projection that should resolve to `cache`. */ target: string; mode: SharedPathMode; /** Whether the projection now reaches the cache (false ⇒ `warning` says why not). */ linked: boolean; /** Cache directory is non-empty. The caller's only signal for "an install is needed". */ populated: boolean; warning?: string; } /** * Early conflict warning for one contended entity key (Phase 15.3, docs/17 §15.3). * `theirs` are operations by OTHER actors that (a) are outside the caller's causal * closure — concurrent work the caller has not built on, (b) have not been rejected by * a decision, and (c) have not been built upon by any later op on the same key (i.e. * not superseded). `leaseHolders` are other actors holding an active lease whose scope * overlaps the key. Leases gossip as ordinary objects, so combined with the sync-watch * daemon this warning works ACROSS machines, not just across local processes. */ export interface ContentionWarning { key: string; /** `line` is populated for an `acrossLines` check, so the caller can name the branch. */ theirs: { op: string; actor: string; lamport: number; purpose: string; createdAt: string; line?: string; }[]; leaseHolders: { actor: string; leaseOid: string; scope: string; expiresAt: string; }[]; } /** * What one {@link Repo.undo} call did (issue #91). * * `excluded` is what THIS call dropped from the view; `alreadyExcluded` is what a previous * undo had already dropped — reported rather than refused, so running undo twice converges * instead of erroring. `purged` are the blobs whose bytes were evicted, `retained` the * target blobs a still-selected op keeps alive (content-addressing means identical content * is one blob, so this is the normal, not the exotic, case). */ export interface UndoResult { /** The authored {@link Undo} record, or null when the call was a no-op. */ undoOid: string | null; view: string; excluded: string[]; alreadyExcluded: string[]; purged: string[]; retained: string[]; } /** * `dir` holds no AVCS repo — a distinct, expected outcome, not a failure. * * `open` used to say this with a plain `Error`, which forced every caller into `catch {}` to * implement "open it, or create one". That catch also swallows permission errors, corruption * and EMFILE, promoting each of them to "absent" — and a caller that then creates an empty * repo has hidden the real one. A type is what lets the caller be narrow. */ export declare class RepoNotFoundError extends Error { readonly dir: string; constructor(dir: string); } /** Which of the two private keystores a key lives in (issue #98). `"repo"` is the * per-checkout override, `"machine"` the default shared by every repo on the box. */ export type KeyScope = "repo" | "machine"; /** The actor kind an id implies, by the `kind:name` convention the CLI already uses. Key * files written before #98 carry no kind, and `actorKind` is stored on a trust record but * never consulted by a trust check (only `actorId` is), so a guess here cannot grant * authority — it only keeps the record readable. */ export declare function kindOfActorId(id: string): Actor["kind"]; export declare class Repo { #private; readonly dir: string; readonly store: ObjectStore; readonly keyring: Keyring; readonly metrics: Metrics; /** * One-line notices about the machine keystore (issue #98) — a key adopted out of a * repo-local store, or a repo-local key that differs from the machine one. Collected on * the instance rather than printed from here so the CLI/MCP decide how to surface them, * and so a test can assert the user was told without parsing stdout. Never contains key * material. */ readonly keystoreNotices: string[]; /** Structured logger (silent by default; CLI/hub/MCP wire a console/OTel sink). */ logger: Logger; static readonly AUTO_COMPACT_DELTA = 256; private constructor(); static init(dir: string, opts?: { configHome?: string; }): Promise; static open(dir: string, opts?: { configHome?: string; }): Promise; /** * Open the repo at `dir`, creating one if — and ONLY if — none is there. * * The distinction is the whole point. A consumer writing this itself reaches for * `try { open } catch { init }`, and that catch cannot tell "absent" from "present but * unreadable"; the second case then gets an empty repo written over it. Here only * {@link RepoNotFoundError} routes to creation and every other failure propagates. * * Those two states are not the whole space, though (#171). A directory can also be * **present but incompletely seeded** — `.avcs/objects` exists, so `isRepo` says yes and * this routes to `open`, but no `view:main` was ever written because nothing ever ran * `init` here. That is the normal end state for a store populated by object import or * restored from a backup, and the repo it yielded threw `no such view: main` from every * `materialize()`. So the opened path seeds too: the postcondition "you get a usable * repo" is what this method sells, and it cannot depend on which branch was taken. */ static openOrInit(dir: string, opts?: { configHome?: string; }): Promise; policy(): Promise; /** Replace the active policy (new version ⇒ a distinguishable checkpoint). */ setPolicy(policy: Policy): Promise; /** Set code-owner rules (Phase 5), bumping the policy version. */ setOwners(owners: OwnerRule[]): Promise; /** actorId → learned reliability nudge, from history. */ reliability(): Promise>; /** Persist a public key as trusted and load it into the keyring. */ registerPublicKey(rec: KeyRecord): Promise; /** * Mint a keypair for an actor, register the public half as trusted, and return * the private half for the caller to hold. (MVP: a real deployment keeps private * keys with the actor, never in the repo.) */ generateActorKey(actor: Actor, keyId?: string): Promise<{ keyId: string; privateKey: string; publicKey: string; }>; /** * Persist an actor's PRIVATE key, perms 0600. * * Defaults to the MACHINE keystore (issue #98): `key provision` mints an identity for a * person on a box, not for a checkout. `scope: "repo"` writes the repo-local override * instead — that is what `clone --key` uses, so importing a credential for one repo does * not silently install it machine-wide. */ saveLocalKey(actorId: string, privateKey: string, opts?: { scope?: KeyScope; actorKind?: Actor["kind"]; }): Promise; /** * Adopt an existing private key into THIS repo's keystore (issue #58). * * `clone` is the command that creates a repo, so a freshly init'd directory holds no key * and cannot sign the first read — which makes a private repository unreachable on a hub * that gates reads. The credential therefore has to come from outside, and be left behind * afterwards: a clone that worked once and whose later `sync` then 401s just moves the * problem somewhere less obvious. * * `source` is either a key file (the shape `saveLocalKey` writes) or a repo directory to * take one from. An ambiguous directory names the choice rather than picking silently — * signing as the wrong actor is worse than a stop, because the wrong identity ends up in * history where it cannot be quietly corrected. * * Defaults to `scope: "repo"` (issue #98): `clone --key` says "THIS repo signs as this * actor", and installing a credential machine-wide as a side effect of a clone flag would * be a surprising write to a shared resource that could also shadow the machine's default * identity. `avcs key import` passes `scope: "machine"` when that IS what the user asked * for. Either way the public half is registered as trusted here, or the import would leave * the actor able to sign and unable to be believed (issue #96). */ importLocalKey(source: string, actorId?: string, opts?: { scope?: KeyScope; }): Promise; /** * Actor ids this machine holds a PRIVATE key for — i.e. who it can sign as. Merged over * both sources (issue #98). Returns ids only: the key material must never travel with a * listing, or `key ls` becomes the disclosure it is meant to help avoid. */ listLocalKeys(): Promise; /** * The same listing, saying WHICH keystore each key would be read from — the winner under * repo → machine precedence. `key ls` needs this to stay honest: "signable on this * machine" and "signable only in this checkout" are different facts, and a user with a * repo override needs to be able to see it. * * `shadowed` marks a repo-local key that is hiding a machine key for the same actor id. * Present only when true, so an entry that shadows nothing keeps the plain shape. Without * it a listing cannot tell "this key exists only in this checkout" apart from "this * checkout is overriding your machine identity" — and after the #98 migration adopts a * key, every repo-local entry is the second kind. */ listLocalKeySources(): Promise<{ actorId: string; source: KeyScope; shadowed?: true; }[]>; /** Public keys this REPO trusts (shared/gossiped) — a different question from which * keys this machine can sign with. Returns actor ids only. */ listTrustedKeys(): Promise; /** * Mint a signing key for `actor` unless one is already held (issue #51). * * Idempotent on purpose: re-provisioning would orphan the previous key while any * signature already made with it stays in history, so a caller who runs this twice must * not silently lose the ability to be recognised as themselves. */ ensureOwnerKey(actor: Actor, keyId?: string): Promise<{ keyId: string; created: boolean; }>; /** * Load a locally-held private key for `actorId`, or null if neither keystore holds one. * * Repo-local override first, then the machine keystore (issue #98). A key found ONLY in a * repo-local store is also ADOPTED into the machine keystore here — that is the migration: * every repo holding a key today keeps signing with exactly that key (precedence), and the * identity becomes usable in the next repo the user creates without a second provision. */ loadLocalKey(actorId: string): Promise; /** The replica's local actor identity, resolved by the same order #resolveHubSigner * uses (explicit → AVCS_ACTOR → config.json → sole private key) but WITHOUT requiring * a private key to exist — a contention check (Phase 15.3) needs a perspective, not a * credential. Returns undefined when nothing resolves. */ localActorId(explicitActorId?: string): Promise; /** * The local author for a new operation — id plus git-style name/email. The id resolves by * the usual chain (explicit → AVCS_ACTOR → config.actorId → sole key); name and email * resolve independently by the SAME shape (explicit → AVCS_AUTHOR_NAME/EMAIL → * config.authorName/authorEmail), because a person's display name and contact are set once * per machine/repo like `git config user.name`/`user.email`, not passed per commit. * * name/email are attribution and contact only — they ride in the operation content so * blame and history can show and reach the author, and are NEVER consulted by a trust * check. Returns undefined id when nothing resolves (the caller decides if that is fatal). */ localAuthor(explicit?: { id?: string; name?: string; email?: string; }): Promise; /** Persist a config.json value (author identity, git mode, …). A thin, git-config-like * setter over the same aux file the readers use. Deleting is passing undefined. */ setConfigValue(key: "actorId" | "authorName" | "authorEmail", value: string | undefined): Promise; /** Read a config.json value (or undefined). */ getConfigValue(key: string): Promise; /** * Provision an owner key: mint a keypair, register the public half as trusted, and * store the private half in the LOCAL keystore so the MCP server can sign the * owner's elicitation-confirmed decisions (issue #15). Returns the keyId. */ provisionOwnerKey(actor: Actor, keyId?: string): Promise; readIntent(oid: string): Promise; listIntents(): Promise; createIntent(args: { title: string; owner: string; kind?: IntentKind; priority?: Intent["priority"]; constraints?: string[]; constraintKinds?: Intent["constraintKinds"]; successCriteria?: string[]; allowedScopes?: ScopeRef[]; }): Promise; startSession(args: { intentOid: string; actor: Actor; summary?: string; openedEntities?: ScopeRef[]; baseViewOid?: string | null; }): Promise; static readonly CHUNK_THRESHOLD: number; static readonly CHUNK_SIZE: number; putBlob(content: string | Uint8Array): Promise; readBlob(oid: string): Promise; /** * How many operation object reads a single op-log tail keeps in flight. * * High enough that latency stops dominating, low enough to stay well inside a default * file-descriptor limit while other work (blobs, packs) also has files open. */ static readonly OP_READ_CONCURRENCY = 64; proposeOperation(args: { sessionOid: string; intentOid: string; actor: Actor; target: OperationTarget; body: OperationBody; declaredPurpose: string; causalDeps?: string[]; effects?: Operation["effects"]; confidence?: number; line?: string; workspace?: string; derivedFrom?: string; revertOf?: string; coAuthors?: Actor[]; private?: boolean; signWith?: { keyId: string; privateKey: string; }; /** Phase 15.3: after authoring, run a contention check on the op's keys and emit * structured-log warnings + a metric. Additive only — the return type is unchanged * (surfaces that want the warnings themselves call {@link contention} directly). */ warnContention?: boolean; /** Make that check cross-line (see {@link contention}'s `acrossLines`). The capture * path uses this: the git bridge puts every parallel session on its own line, so a * line-scoped check is structurally blind to them. */ contentionAcrossLines?: boolean; /** Receive the warnings the check produced, so a caller can surface them to a human * without re-running the scan. Called only when `warnContention` is set. */ onContention?: (warnings: ContentionWarning[]) => void; }): Promise; /** Convenience: write file content as a blob + a put_file operation. */ proposeFileWrite(args: { sessionOid: string; intentOid: string; actor: Actor; path: string; content: string | Uint8Array; declaredPurpose: string; causalDeps?: string[]; effects?: Operation["effects"]; line?: string; workspace?: string; signWith?: { keyId: string; privateKey: string; }; warnContention?: boolean; contentionAcrossLines?: boolean; onContention?: (warnings: ContentionWarning[]) => void; }): Promise; /** * Language-neutral edit (docs/15): submit the FULL new content of a file together with * the base content it was derived from. Concurrent edit_file ops on the same file are * 3-way line-merged at materialization — disjoint hunks auto-merge, overlapping hunks * surface as a Conflict. No code-structure awareness: works for any text/language. * * `baseBlobOid`/`baseText` is the 3-way merge base (what the agent read before editing); * normally the content established by the causally-prior op. Omit ⇒ base is empty. */ proposeEdit(args: { sessionOid: string; intentOid: string; actor: Actor; path: string; newText: string; baseText?: string; baseBlobOid?: string; declaredPurpose: string; causalDeps?: string[]; effects?: Operation["effects"]; line?: string; workspace?: string; signWith?: { keyId: string; privateKey: string; }; warnContention?: boolean; contentionAcrossLines?: boolean; onContention?: (warnings: ContentionWarning[]) => void; }): Promise; attachEvidence(args: { forOps: string[]; kind: EvidenceKind; result: EvidenceResult; producedBy: Actor; command?: string; detail?: string; /** The materialized treeHash this evidence was produced against (docs/16 §5). */ treeHash?: string; /** Produced by a secret-less isolated runner over untrusted code (Phase 11). */ fromUntrustedRunner?: boolean; /** Sign the evidence so the trust gate can verify it cryptographically. */ signWith?: { keyId: string; privateKey: string; }; }): Promise; recordDecision(args: { conflictId: string; chosenOps: string[]; rejectedOps: string[]; reason: string; decidedBy: Actor; futurePolicy?: string; signWith?: { keyId: string; privateKey: string; }; }): Promise; activeLeases(): Promise; /** * Request a soft write-lease over scopes. Returns the granted lease oid, or the * conflicts that block it (overlapping active exclusive lease held by another). */ requestLease(args: { intentOid: string; sessionOid: string; actor: Actor; writeScopes: ScopeRef[]; mode?: "exclusive" | "shared"; ttlMs?: number; }): Promise<{ granted: true; leaseOid: string; } | { granted: false; conflicts: LeaseConflict[]; }>; /** * Report contention on entity keys BEFORE finalize would discover it: for each key, * the operations by other actors that the caller has not built on (outside the * caller's causal closure) and are still live (neither decision-rejected nor built * upon by a later op on the key), plus other actors' active leases overlapping the * key. Discovery is via the entity index — O(ops-on-key), no reduce. * * Perspective resolution ("mine"), first hit wins: * - `sessionOid`: that session's actor; its ops seed both the key set and the closure. * - `actorId` (+ optional `keys`): that actor's ops on the resolved keys seed the * closure; with no `keys` given, every key the actor has authored on is checked. * - `keys` alone: no closure filter — everything live by anyone on the key reports. * * `acrossLines` (default false — existing callers are untouched) drops the line-equality * filter. The git bridge maps each git branch to its own line (`lineFor()` in the CLI), so * with N parallel sessions on N branches a line-scoped check cannot see ANY of them; the * cross-line check does, and reports each competing op's `line` so the caller can name the * branch. Lines are intentionally divergent by design, so this stays opt-in. */ contention(args: { keys?: string[]; sessionOid?: string; actorId?: string; line?: string; acrossLines?: boolean; }): Promise; /** Build a minimal repair packet for ops whose validation failed. */ repairContext(opOids: string[]): Promise; getView(name: string): Promise; createView(name: string, query: ViewQuery, baseViewOid?: string | null): Promise; listLines(): Promise; /** * Fork a new line from `fromLine` at its current (or a given) checkpoint. The fork * checkpoint freezes what the new line inherits; everything the base line does * afterwards stays out of the new line. Also creates a same-named view selecting it. */ createLine(name: string, fromLine?: string, atCheckpointOid?: string): Promise; /** Frontier (accepted head ops) of a line — the causalDeps a new op should build on. */ lineFrontier(lineName: string): Promise; /** * Port (cherry-pick / backport) an operation onto another line: mint a NEW op on * the target line carrying the source's body, based on the target line's current * frontier, with `derivedFrom` provenance. edit_file 3-way merges (against the target * line's content) at materialize; put_file replaces on the target line. */ portOp(sourceOpOid: string, targetLine: string, actor?: Actor): Promise; static readonly ROLE_WEIGHT: Record; /** Issue a root-signed membership granting a role; registers the member's key. */ registerMembership(args: { actorId: string; publicKey: string; role: RoleName; actorKind?: "human" | "ai_agent" | "ci_bot"; scopes?: ScopeRef[]; root: { keyId: string; privateKey: string; }; }): Promise; membershipOf(actorId: string): Promise; roleOf(actorId: string): Promise; hasRole(actorId: string, min: RoleName): Promise; /** Revoke a membership (admin only): future ops/decisions by this actor lose trust. */ revokeMembership(actorId: string, byAdmin: string): Promise; setProtection(p: Omit): Promise; getProtection(view: string): Promise; /** Current protected head (a checkpoint oid) of a view, or null if never finalized. */ protectedHead(view: string): Promise; /** * Finalize (= PR merge): advance a view's protected head to `newCheckpoint` via a * compare-and-swap on `parentHead`. Rejects a stale (non-fast-forward) finalize * even for admins unless allowForcePush — this is the causal-currency guard (docs/08 * §6/§9): authority never licenses overwriting fresher history. */ finalize(args: { view: string; newCheckpoint: string; parentHead: string | null; by: string; }): Promise<{ finalized: true; head: string; } | { finalized: false; reason: string; }>; /** * Submit a draft checkpoint to the integration queue (docs/17 §14.2). Runs under the * same `finalize:` lock as finalize — the existing mkdir lock IS the serializer * (no separate queue structure in v1). The outcome is always one of the four verdicts; * "pull and redo" does not exist on any path. * * Idempotency: an `advanced` ticket replays its recorded verdict forever. Non-terminal * verdicts (conflict/needs_evidence/rejected/expired) re-evaluate on resubmission — * the world legitimately changes under them (a decision lands, evidence arrives, a * missing object syncs), and a frozen replay would wedge the ticket. */ submitIntegration(args: { view: string; checkpoint: string; by: string; ticketId?: string; signWith?: { keyId: string; privateKey: string; }; /** * Compute the verdict and write NOTHING (issue #79). * * The queue is the right authority for "may this land?", and the natural place to surface * that is a pre-merge check — a job reporting the verdict while the author is still * working. Such a job must not advance the protected head as a side effect of reporting. * * The decision path is not duplicated: steps 1–6 run unchanged, so a `conflict` still * carries its repair packet and a `needs_evidence` still names its required checks. * docs/17 §2 requires every queue decision to be a pure function of objects + Protection, * and a second implementation would break exactly that. What a dry run skips is the * mutation: no integrated Checkpoint, no `head:`, no reservation written or * cleared, no Integration audit object. * * `finalize:` is still taken — a consistent read needs it — so this is cheaper than * landing to find out, not free. */ dryRun?: boolean; }): Promise; /** * Redact (tombstone) a blob's bytes — for a leaked secret. Admin-only. The oid is * preserved so all references and the treeHash stay valid; the plaintext is evicted * from this store (and, once a real sync ships, propagated to every replica). */ redact(blobOid: string, reason: string, by: string, signWith?: { keyId: string; privateKey: string; }): Promise; /** * Undo local ops: drop them from a view's projection, and with `purge` evict the blob * bytes they uniquely reference. * * This is `redact`'s pre-share counterpart, and the split is the whole point. `redact` * is admin-gated because it evicts bytes from a repo other people hold — a governance * act. `undo` refuses the moment the ops have been pushed (see {@link pushedOps}), * so by construction it only ever operates on history no other holder has. Nothing to * co-ordinate ⇒ nobody's authority to ask for. * * Without `purge` this is fully reversible: the ops and their bytes stay in the store and * only the view's `excludeOps` grows. With `purge` the bytes go, which is why it is opt-in * and separately named. Both are append-only: the exclusion is a NEW view object and the * act itself is recorded as an {@link Undo}. */ undo(args: { /** Ops to undo. Mutually exclusive with `last`. */ ops?: string[]; /** Undo the ops of the most recent commit on this scope instead. */ last?: boolean; /** The view (line) to undo on. Default "main". */ view?: string; /** Resolve `last` inside a workspace's projection rather than the base view. */ workspace?: string; /** Also evict the bytes the undone ops uniquely reference. Irreversible. */ purge?: boolean; by: string; reason?: string; }): Promise; /** * Op oid → the hub URLs that accepted it (issue #91). Written by `pushToHub`; the record * of what has left this machine, which `undo` refuses to touch. * * It is a record of THIS replica's pushes, so it is honest about what it can see and no * more: a hub push (including the one inside `land`/`submit`) is recorded, while a peer * that ran `avcs pull ` copied objects without this side ever being asked. See * docs/23 §5 for that boundary. */ pushedOps(): Promise>; /** Every recorded local undo, oldest first. */ listUndos(): Promise; /** Break-glass: a maintainer/admin grants an expiring waiver of required checks. */ grantOverride(args: { view: string; waiveChecks: EvidenceKind[]; reason: string; by: string; ttlMs?: number; }): Promise; /** * Rollback a protected head to an earlier checkpoint — FORWARD-only: it advances the * head (a new finalize CAS) to point at a prior state, never rewriting history. */ rollbackTo(view: string, checkpointOid: string, by: string): Promise<{ finalized: true; head: string; } | { finalized: false; reason: string; }>; /** A reviewer approves (or requests changes on) a checkpoint. = PR approve. */ approve(checkpointOid: string, by: string, verdict?: "approve" | "request_changes", opts?: { reason?: string; signWith?: { keyId: string; privateKey: string; }; }): Promise; /** * Public read of the effective approval verdicts on a checkpoint (Phase 16 M4, docs/18 * §3): the same trust-gated view finalize uses, so a review surface cannot show an * approval the gate would not count — only actors who still hold the reviewer role are * included, and a later verdict from the same reviewer supersedes an earlier one. */ approvalsFor(checkpointOid: string): Promise<{ by: string; verdict: "approve" | "request_changes"; }[]>; /** * Pull objects from another repo's store into this one. Objects are append-only and * content-addressed, so sync is a conflict-free union of whatever the other side has * that we lack. `gate` (optional) lets a hub reject ops not signed by a known member. * Returns counts. Refs (governance) are NOT synced — those are hub-authoritative. */ pull(otherDir: string, opts?: { requireSignedMembers?: boolean; }): Promise<{ copied: number; rejected: number; }>; /** Apply all known redaction tombstones locally (evict bytes; oids preserved). */ applyRedactions(): Promise; /** Push objects this repo holds that a network hub lacks (M2 / docs/10 WS-B). */ pushHub(hubUrl: string, opts?: { as?: string; }): Promise<{ pushed: number; rejected: number; }>; /** Request a finalize (= PR merge) on a network hub via its CAS endpoint (E6). */ finalizeHub(hubUrl: string, args: { view: string; newCheckpoint: string; parentHead: string | null; by: string; signWith?: { keyId: string; privateKey: string; }; }): Promise<{ status: number; finalized: boolean; head?: string; reason?: string; }>; /** Pull objects a network hub holds that this repo lacks. */ /** * Take objects straight into this repo's store — no network. * * `pullHub` was the ONLY way in, which forces a consumer that already holds the objects * in its own process to speak HTTP to itself: a loopback listener, a secret route to hide * it behind, and the code that keeps that route from leaking. The cost is not only the * round trip. When the loopback address cannot be resolved (port not yet bound, no socket), * "cannot receive objects" surfaces as a `rejected` integration verdict — something that is * not a policy judgement wearing a policy judgement's face. * * This is `pullHub` with the transport removed and nothing else: same store writes, same * operation indexing, same Lamport advance, same redaction pass. Anything left out here * would just be rebuilt outside, which is the problem this exists to end. * * Forgery needs no check. `store.put` recomputes the content address, so an object whose * body was altered lands at ITS OWN oid and cannot displace the original — the same * property the HTTP path relies on rather than a weaker one for being closer to home. */ importObjects(objects: Iterable | AsyncIterable): Promise<{ imported: number; skipped: number; }>; pullHub(hubUrl: string, opts?: { as?: string; }): Promise<{ pulled: number; }>; /** Register (or update) a named remote hub. */ addRemote(name: string, url: string, opts?: { autoSync?: boolean; freshnessMs?: number; }): Promise; /** Remove a named remote. Returns whether it existed. */ removeRemote(name: string): Promise; /** All configured remotes, name → config. */ listRemotes(): Promise>; /** Public remote-name → hub-URL resolution (a literal URL passes through). */ remoteUrl(nameOrUrl: string): Promise; /** * Submit a draft checkpoint to a REMOTE hub's integration queue (Phase 14, docs/17 * §14.4), with capability detection: a hub advertising `integrate` on GET /version * gets the queue path (one judgment, no redo); an older hub falls back to the legacy * finalize + pull retry funnel (bounded) — the exact loop the queue exists to kill, * kept only for backward compatibility. */ integrateHub(remoteOrUrl: string, args: { view: string; checkpoint: string; by?: string; ticketId?: string; signWith?: { keyId: string; privateKey: string; }; }): Promise<{ verdict: IntegrationResult["verdict"]; legacy?: boolean; } & Record>; /** * Bidirectional convergence with a named remote (default "origin"): pull what the hub * has that we lack, then push what we have that it lacks. Pure object gossip — union * semantics, no rebase, no working-tree mutation beyond redaction propagation. */ sync(remote?: string, opts?: { as?: string; }): Promise<{ pulled: number; pushed: number; rejected: number; }>; /** Freshness window applied to an `autoSync` remote that doesn't set `freshnessMs`. */ static readonly DEFAULT_FRESHNESS_MS = 30000; /** Milliseconds since the last successful sync with `remote` (Infinity when never). */ syncAgeMs(remote?: string): Promise; /** * BLOCKING freshness sync (Phase 15.2): sync each named remote (default: every * `autoSync` remote) whose last successful sync is older than its freshness window. * For callers that must not read stale state (e.g. just before a submit). The read * path itself never calls this — materialize only ever fires a BACKGROUND revalidate. */ syncIfStale(remote?: string, opts?: { as?: string; }): Promise<{ synced: string[]; }>; /** * Await any in-flight background revalidation, resolving immediately when idle. The * quiesce handle for the fire-and-forget path, mirroring the promise `runSyncWatch` * returns for the daemon: call it before tearing a repo down (shutdown, teardown) so * no `.avcs` write is still outstanding. Never rejects — a failed revalidate is logged * and swallowed, exactly as it is on the read path. */ settleBackgroundSync(): Promise; /** List the workspaces that have landed onto their base line. */ landedWorkspaces(): Promise; /** * Every workspace that actually carries operations. A workspace is not a stored object — * it exists exactly as a tag on ops — so this is the only way to ask whether a NAME names * anything. The `post-merge` land seam uses it as a guard: landing is append-only and * irreversible, so a name it cannot corroborate is not landed (docs/20 §3.4, R1). */ workspaceNames(): Promise; /** * Land a workspace onto its base line (docs/16): its ops join the base view and merge * there. There is no "rebase" — reduce always 3-way-merges the full op set, and any * overlap surfaces as a Conflict via the normal materialize path. Idempotent. */ landWorkspace(name: string): Promise; materialize(viewName?: string, opts?: { includeStatuses?: ViewQuery["includeStatuses"]; workspace?: string; }): Promise; /** List currently-quarantined ops (outsider contributions awaiting review). */ quarantinedOps(line?: string): Promise; /** * Phase 11: a non-member (external contributor) submits an op. It self-signs and * lands quarantined. Admission control caps outstanding outsider ops per actor. */ proposeOutsider(args: Parameters[0] & { maxOutstanding?: number; }): Promise; /** A reviewer promotes quarantined outsider ops into the normal accepted flow. */ promote(opOids: string[], byActor: string, reason?: string): Promise; /** * Revert an op: a forward-only inverse. Restores the op's file to its pre-op content * (or deletes it if it didn't exist before) as a NEW op with `revertOf` provenance — * append-only, recorded, itself revertable. File-granular in the MVP. */ revert(opOid: string, actor: Actor, line?: string): Promise; static readonly REDUCE_CACHE_MAX = 64; /** * Minimum line similarity for the capture path to call a removed × added pair a MOVE * rather than an unrelated delete + create (docs/19 §3.1, and §6 R3 asks for exactly one * place to tune it). 0.5 is git's `-M` default, so a tree avcs captures and the same tree * `git diff -M` describes agree about what moved. Raising it makes capture more * conservative (more moves recorded as delete + create, which is the pre-Stage-0 * behaviour); lowering it risks attaching a wrong merge base, which is worse than none. */ static readonly RENAME_SIMILARITY = 0.5; /** * The configured shared paths, or `[]` when nothing is configured. A torn/undecodable * file reads as empty, exactly like `remotes.json` and `config.json`: an unreadable * cache configuration must never make a projection fail. * * Reading must not CREATE the file — "no `shared-paths.json`" is the backward-compatible * state (docs/21 S1) and the absence of the file is itself the signal. */ readSharedPaths(): Promise; /** Replace the shared-path configuration wholesale. */ setSharedPaths(entries: SharedPathEntry[]): Promise; /** Add (or replace, by `path`) one shared path. Read-modify-write, like `setTrunk`. */ addSharedPath(entry: SharedPathEntry): Promise; /** Remove one shared path. Returns whether it existed. The CACHE is left alone — that is * `gc --shared`'s call to make, because re-installing is expensive (docs/21 §3.6). */ removeSharedPath(path: string): Promise; /** * Derive a cache key from the PROJECTED content of the declared files (docs/21 §3.2): * * key = sha256( canonical( [[path, blobOidOfProjectedContent] for path in sorted(keyFrom)] ) )[:32] * * Projected content, not what is on disk. A tree entry is `path → blobOid`, and a blob * object is `{type,data,encoding}` — content and nothing else — so the oid IS the content * hash. Two workspaces that project the same view therefore get the SAME key by * construction, with no disk read and no clock in the way: determinism buys cache * correctness for free (S15). Conversely a declared file whose content changes moves the * key (S4), and an undeclared file cannot move it however much it changes. * * Pure and static: a key that decides which cache a workspace links to must be checkable * without a store, a projection, or a filesystem. * * - A declared file ABSENT from the view (a lockfile nobody has written yet) participates * as EMPTY content and is reported in `missing` — never silently keyed differently, * which would split the cache and leave nobody able to explain the extra install (S9). * - `keyFrom: []` (or absent) is the named constant `"unkeyed"`: the explicit choice that * every workspace shares one cache. Dangerous, and the user's to make (S10). */ static deriveSharedKey(keyFrom: string[] | undefined, tree: Map): { key: string; missing: string[]; unkeyed: boolean; }; /** * Throw away one cache directory by key (docs/21 R2). The core reports only "non-empty", * so a cache left broken by a half-finished install is not something it can detect — this * is the escape hatch for the caller who can. */ dropSharedCache(key: string): Promise; /** * Connect every configured shared path to its store-local cache (docs/21 §3.4). Runs * AFTER the tree has been written, because writing the tree can create directories. * * What the core does: derive the key, create the cache directory, connect it, and report * `populated`. What the core does NOT do: run an install. It does not know what * `node_modules` is, which package manager owns it, or whether the network is up — and the * moment it did, docs/21 §2 principle 1 would be gone. `populated` is the entire interface * between "the core made a place" and "somebody has to fill it". * * Existing content at a shared path is never destroyed. A real directory there is the * user's data and the core cannot recreate it (it does not know how to install), so it is * left alone with a warning. The one thing that IS re-pointed is a symlink the core itself * put inside this store's own cache tree, which is how a key change (S4) takes effect * instead of leaving the workspace wired to a stale environment. * * With `mode: "copy"`, a directory already at the target counts as materialized and is not * copied over — local edits inside it survive (S11). Re-materializing after a key change * therefore means removing that directory by hand; the core will not delete user data to * refresh a cache. */ linkSharedPaths(workDir: string, tree: Map): Promise; /** * Project a view into `workDir` AND connect its shared paths (docs/21 §3.4) — the full * physical checkout `avcs workspace project` performs. `checkoutInto` is this without the * shared report, kept as-is for every existing caller. * * `skipped` are tree entries that live INSIDE a shared path. Normally there are none — * capture cannot produce them (§3.5) — but a history contaminated before shared paths * existed can still be opened, and writing those files would spill recorded content over a * live build environment. So they are skipped and named rather than written. * * `at` pins the projection to a CHECKPOINT instead of the view's current frontier. A job * has to run on the tree it was triggered for: if the head advances between the trigger and * the claim, a job that checks out "now" examines a different tree, and its evidence is then * bound to a checkpoint nothing verified — which is what `Protection.requireBoundEvidence` * stands on. The store could already do this (`materializeAt(cp.headOps)`, which * `checkpointBytes` uses); only the physical checkout had no way to ask. */ projectInto(workDir: string, view?: string, opts?: { workspace?: string; at?: string; }): Promise<{ written: string[]; shared: SharedPathLink[]; skipped: string[]; /** path → blob oid, for the caller that records what it projected (see checkoutInto). */ tree: Map; }>; /** * Write a view's materialized files into `workDir` (alongside .avcs, like git), and * REMOVE the ones a previous projection put there that this view does not contain. * * The working tree is derived, so the view must decide what is in it. Writing without * removing makes two projections layer into their union — which is exactly what * `clone` (default view) followed by `checkout` (target view) produced. * * git does this with the index: it knows which files are its own. The equivalent here is * a record of what the last projection wrote, which {@link projectInto} already computes * and used to discard. `#readProjection`/`#writeProjection` persist it. * * Only the repo's own working tree is cleaned. `projectInto` stays a pure write so that * `materialize --out` / `workspace project` (exports into a caller's directory) neither * delete nor disturb the record. * * `at` pins the projection to a checkpoint — see {@link projectInto}. */ checkoutInto(workDir: string, view?: string, opts?: { workspace?: string; at?: string; }): Promise; /** * Commit a working tree: diff `workDir`'s files against the materialized view and * author edit_file / put_file / delete_file ops for the changes (the git `add`+`commit` * step, which agents do via operation.propose). Causally builds on the current frontier. * * A MODIFIED file is captured as `edit_file` with the previously projected content as its * 3-way merge base — that base is already in hand here, and blobs are content-addressed so * re-putting it is free (dedup). This is what lets two sessions editing disjoint regions of * one file auto-merge (L1) instead of colliding as two base-less `put_file`s (docs/15 §3). * ADDED files keep `put_file` (a create genuinely has no base), and so does any content that * is not losslessly UTF-8 text (see `#isMergeableText`). * * A MOVED file is recovered from the removed × added pair (`#detectRenames`, docs/19 §3.1) * and captured as `rename_file` — plus an `edit_file` at the NEW path, based on the content * from BEFORE the move, when it was edited on the way. Without this the reducer's whole * rename × edit commutativity is unreachable from real usage: a path-set diff turns every * move into a delete racing an edit and a create with nothing to merge against. Recovered * moves are reported in `renamed` and are NOT double-counted in `added`/`removed`. * * `workspace` scopes the capture to a converging workspace (docs/20 §3.3) — the git bridge * maps a topic branch to one. It has to reach BOTH ends: the authored ops carry the tag, and * the projection this diffs against is the WORKSPACE's view. Diffing disk against the base * view instead would re-capture the workspace's own earlier edits as brand-new changes on * every commit, and — since a move is recovered from a removal paired with an addition — * those stale removals could pair into renames that never happened. * * Capture also runs the early conflict warning with CROSS-LINE visibility: a competing * session may be on a different line, and a line-scoped check would never see it. The * warnings are returned (`contention`) so the CLI can put them in front of the human * running `git commit`. */ commitWorkingTree(workDir: string, opts: { message: string; actor: Actor; line?: string; workspace?: string; ignorePredicate?: (rel: string) => boolean; /** JOIN an intent that was already declared (issue #167) instead of opening a new one. * Intent is the object that carries WHY; minting a fresh one per capture collapses * that back into the commit message, which is the WHAT. Omitted, the behaviour is * unchanged: a capture with no declared intent opens one titled with its message. */ intentOid?: string; /** Author a deletion that covers (almost) the whole tree. Off by default — see * {@link MassDeleteError} for why that is not paranoia. */ allowMassDelete?: boolean; /** Stop authoring at the next op boundary once aborted (#181). Everything authored up * to that point is flushed and reported as usual; `partial.remaining` counts the * changes that were NOT authored, so the caller can say so and the next capture can * pick up from here instead of from zero. */ signal?: AbortSignal; }): Promise<{ ops: string[]; added: string[]; modified: string[]; removed: string[]; /** Moves recovered from the removed × added pairing (docs/19 §3.1). These paths do NOT * also appear in `added`/`removed`. */ renamed: { from: string; to: string; }[]; intent: string; contention: ContentionWarning[]; /** Present only when `signal` aborted mid-way: the lists above hold what WAS authored. */ partial?: { remaining: number; }; }>; /** Read the repo-local git-bridge mode (default `sidecar` for pre-existing repos). */ getGitMode(): Promise; /** Whether `git-sync --commit` injects AVCS provenance trailers (default on). */ gitTrailerEnabled(): Promise; /** Persist the git-bridge mode and (re)write `.avcs/.gitignore` to match it. */ setGitMode(mode: GitMode): Promise; /** * The git branch that carries the base view (docs/20 §3.1). The core stays git-agnostic: * this is a recorded NAME, and only the bridge ever compares it against a real branch. * Unset ⇒ `main`, which is what the bridge assumed before trunk existed. */ getTrunk(): Promise; /** * Every branch name that counts as trunk. With `trunk` configured it is the single * answer; with nothing configured BOTH `main` and `master` are trunk — exactly the pair * the pre-trunk bridge special-cased, so an unconfigured repository (a `master`-default * one included) keeps behaving as it always did (docs/20 W7). */ trunkBranches(): Promise; /** Record the trunk branch. Shares `config.json` with the git mode, so read-modify-write. */ setTrunk(branch: string): Promise; /** * Build the commit-message trailer block that links a git commit to its AVCS provenance * (the git→avcs half). A reader with the `.avcs/` history can resolve the checkpoint; * for a teammate without AVCS it is a harmless annotation (like `Co-authored-by`). */ gitTrailer(info: { checkpoint: string; treeHash: string; intent?: string; }): string; /** Record the git commit ↔ AVCS checkpoint back-link (the avcs→git half of provenance). */ recordGitCommit(sha: string, checkpointOid: string): Promise; /** The checkpoint a git commit was synced from, if a back-link was recorded locally. */ gitCheckpoint(sha: string): Promise; /** * A checkpoint's projection, as BYTES. * * This is the primitive shape. The storage layer is already all Buffer — `readBlob`, * `#treeEntryBytes` and `materializedBytes` all return one, and `putBlob` accepts * `string | Uint8Array`. String appeared in exactly one outermost layer, and that layer * destroyed binary content: a 12-byte PNG header comes back as 20 bytes with every invalid * UTF-8 sequence replaced by U+FFFD, which is not recoverable. * * Anything handing the projection back out — a git-plane comparison, `avcs show`, an op * re-authored from a previous state — takes bytes. The string view below is for line-wise * work (blame, diff hunks, merge3), which genuinely wants text. */ checkpointBytes(checkpointOid: string): Promise<{ treeHash: string; treeHashOk: boolean; files: { path: string; bytes: Buffer; }[]; }>; /** The utf8 text view of {@link checkpointBytes}. A convenience for line-wise callers, not * the primitive — a binary path read through here is lossy by construction. */ checkpointFiles(checkpointOid: string): Promise<{ treeHash: string; treeHashOk: boolean; files: { path: string; content: string; }[]; }>; /** * Persist the provenance handoff for the git-hook trio (pre-commit writes it; the * prepare-commit-msg and post-commit hooks consume it). Local working state, git-ignored. * `workDir` is the working tree being committed (the git worktree dir), defaulting to * the store dir for a plain repo. */ writeGitPending(info: { checkpoint: string; treeHash: string; intent?: string; }, workDir?: string): Promise; /** Read the pending provenance handoff for `workDir`, or null if none is staged. */ readGitPending(workDir?: string): Promise<{ checkpoint: string; treeHash: string; intent?: string; } | null>; /** Clear the pending provenance handoff for `workDir` (post-commit, after recording the back-link). */ clearGitPending(workDir?: string): Promise; /** * Rebuild every rebuildable cache from the object store: the entity index AND the * op-log/obj-log. This is the recovery path after objects arrive OUTSIDE the normal * authoring code path — e.g. a `git pull`/`merge` that unions committed-mode * `.avcs/objects` straight onto disk. Those logs are git-ignored, so without this the * op-LOG (which `materialize` reads its op SET from) would stay stale and silently miss * the pulled ops. Rebuilding the op-log is therefore essential, not just cosmetic. * Idempotent. */ reindex(): Promise<{ ops: number; }>; /** * One-shot "prepare the working tree for `git commit`" (docs/14). The bridge between * AVCS development and a `git add`/`commit`/`push`: * 1. capture any direct working-tree edits as ops (so nothing a human/agent typed is * lost — direct edits and agent-proposed ops converge into one history), * 2. gate: if the view has open (needs-human) conflicts, REFUSE — never let a * conflicted tree be committed; the caller routes the human to `avcs conflicts`, * 3. checkpoint the verified state vector (the git "commit unit"), and * 4. re-project so the working tree is EXACTLY reduce()'s output (folding in any * auto-merged concurrent ops), making git track the deterministic projection. * Git invocation (`git add`) is intentionally left to the caller/CLI so this core stays * git-agnostic; `.avcs/.gitignore` (ensured here) makes a plain `git add -A` mode-correct. */ gitSync(opts: { message: string; actor: Actor; line?: string; workspace?: string; workDir?: string; ignorePredicate?: (rel: string) => boolean; intentOid?: string; signal?: AbortSignal; }): Promise<{ mode: GitMode; captured: { ops: string[]; added: string[]; modified: string[]; removed: string[]; renamed: { from: string; to: string; }[]; intent: string; }; /** Cross-line early warnings the capture raised (docs/17 §15.3): another branch/session * has live concurrent work on a file this commit touches. Advisory — never blocking. */ contention: ContentionWarning[]; conflicts: ReductionResult["conflicts"]; checkpoint?: string; treeHash?: string; reprojected?: number; /** The capture was stopped by `signal` at an op boundary (#181): what `captured` lists is * durable, `remaining` changes are not yet authored, and no checkpoint/reprojection was * made — the next sync continues from here. */ partial?: { remaining: number; }; }>; /** Export the whole repo (all objects + refs) as a portable bundle for backup/transfer. */ exportBundle(): Promise<{ version: number; objects: AnyObject[]; refs: Record; }>; /** Import a bundle into this repo (idempotent, content-addressed). Rebuilds the entity index. */ importBundle(bundle: { objects: AnyObject[]; refs?: Record; }): Promise<{ objects: number; refs: number; }>; /** * Pack loose objects into a packfile (docs/11 B2) — a maintenance op that reduces inode * count and speeds full scans. Reads stay correct throughout (loose-first, then packs); * blobs are intentionally left loose so redaction can always scrub their bytes. */ pack(): Promise<{ packed: number; }>; /** * Compaction (docs/11 B3): persist the current reduction of `view` as a durable base * snapshot. A COLD materialize loads it BY DEFAULT (Phase 13.3) and `reduceIncremental`s * only the ops added since — folding settled history into the base instead of replaying * it — while the original ops stay on disk (append-only audit preserved). Correctness is * the same invariant as Track A: reduceIncremental(base, current) ≡ full reduce, gated by * the property harness and (with AVCS_VERIFY_INCREMENTAL=1) a per-call self-check. */ compact(view?: string): Promise<{ baseOps: number; }>; /** * Garbage-collect (docs/10 WS-C). Reclaims only objects UNREACHABLE from the * authoritative graph — never the append-only audit history of accepted ops: * - orphan blobs: stored blobs no remaining op references (incl. chunk blobs whose * manifest is gone); * - expired quarantine: outsider ops still quarantined (non-member, never promoted), * past `quarantineTtlMs`, that nothing else builds on — the one place append-only * yields (abandoned/spam contributions, docs/09 G5). * `dryRun` reports without deleting. * * `shared` opts IN to collecting shared-path caches (docs/21 §3.6). Plain `gc` never * touches them: re-installing a build environment is expensive, so the routine reclaim of * orphan blobs must not be able to cost somebody an install. */ gc(opts?: { quarantineTtlMs?: number; dryRun?: boolean; shared?: boolean; }): Promise<{ blobs: string[]; quarantinedOps: string[]; sharedKeys: string[]; }>; /** * Materialize the state AT a given frontier: reduce only the causal closure of * `headOps`. The basis for time-travel — history, bisect, and diff-at-point all * reduce over a prefix instead of the whole graph. (Phase 9 / Phase 10) */ materializeAt(headOps: string[], includeStatuses?: ViewQuery["includeStatuses"]): Promise; /** * History of one entity (file path or `#`) in causal order, via the * entity index — O(ops-on-that-entity), not a full-store scan. The basis for blame * and `log -p`. (Phase 9 / Phase 10) */ historyOf(entityKey: string): Promise; /** * Blame: who currently owns an entity and WHY — the accepted head op on its key, * with actor + intent + purpose. Stronger than git blame: the 'why' is first-class. */ blame(entityKey: string, line?: string): Promise<{ op: string; actor: Actor; purpose: string; intentTitle?: string; at: string; } | null>; /** `log -p` for one entity: each op with its before/after content reconstructed. */ logP(entityKey: string, filePath: string): Promise<{ op: string; purpose: string; before: string; after: string; }[]>; /** * Per-line provenance (`blame`, but for lines): who wrote each line of a file * and WHY. Entity-level {@link blame} answers "who owns this file now"; this * answers "why is THIS line here" — the operation that last wrote it, with * its actor, intent title and declared purpose. * * Derived, not stored: the entity's causal history is replayed and each op's * before→after line diff re-attributes the lines it changed (an insertion is * attributed to the inserting op, untouched lines keep their earlier owner). * No new object kind, no determinism impact. */ blameLines(entityKey: string, filePath: string, line?: string): Promise>; /** Diff two views (or, with materializeAt, two frontiers). */ diff(viewA: string, viewB: string): Promise; /** * Bisect: find the first operation (between a known-good and known-bad frontier) * that makes `isBad` true. Deterministic — re-reduces at each step with no checkout. */ bisect(goodHeads: string[], badHeads: string[], isBad: (res: ReductionResult) => boolean | Promise): Promise; /** * Decision memory: given a conflict key, recall prior human rulings on the same * key — their verdict, reason, and any distilled `futurePolicy`. The next agent * (and the conflict UI) can reuse them instead of re-litigating. */ recallDecisions(conflictKey: string): Promise<{ reason: string; futurePolicy?: string; decidedBy: string; }[]>; /** All distilled `futurePolicy` rules a human has left behind — learned constraints. */ learnedPolicies(): Promise; /** * Write the materialized tree to a directory. Refuses to clobber an existing * non-empty directory unless it carries our marker, so a stray `--out` can't * `rm -rf` someone's source tree. */ writeWorkspace(result: ReductionResult, targetDir: string): Promise; /** * Freeze a view's verified state. `workspace` freezes that WORKSPACE's projection instead * of the bare base view (docs/20 §3.3): a commit on a topic branch contains the workspace's * tree, so a checkpoint of the base view would describe a tree git does not hold and * `avcs verify-git` would report every such commit as a mismatch. The scope is recorded on * the checkpoint so it can never be mistaken for a base-view one (`finalize` refuses it). */ createCheckpoint(viewName: string, summary: string, opts?: { workspace?: string; }): Promise; /** Resolve the materialized tree into {path, bytes} entries (byte-preserving). */ materializedBytes(result: ReductionResult): Promise<{ path: string; bytes: Buffer; }[]>; /** Resolve the materialized tree into {path, content} entries (utf8 text view). */ materializedFiles(result: ReductionResult): Promise<{ path: string; content: string; }[]>; /** * Phase 6: cut a Release — a verified checkpoint + its evidence + an SBOM of what * shipped + signed-off artifacts. Refuses unless the view is conflict-free (no open * conflicts and no semantic contract breaks): you cannot release an unverified tree. */ cutRelease(viewName: string, opts?: { artifacts?: import("../objects/types.ts").ArtifactRef[]; signedBy?: string[]; signWith?: { keyId: string; privateKey: string; }; summary?: string; version?: string; supportStatus?: "supported" | "maintenance" | "eol"; }): Promise<{ released: true; releaseOid: string; } | { released: false; reason: string; }>; } //# sourceMappingURL=repo.d.ts.map