nextv2.0.0
Configuration

Repo libraries

A connected repository's .kraft/ folder as a library: its layout, names, where Kraft reads it from, how a person accepts it, automatic updates, the default chain, and repo-libraries.lock.

A repo library is the chains, library components, skills and agent profiles a connected repository keeps in .kraft/ on its default branch. Every instance that connects the repository is offered them. There is no collection to add and no add-on to install, and the repository's own id is the namespace.

A repo library holds what a Kraft add-on holds, under the same limits, in a lighter shape: no manifest and one library per repository. Share chains with add-ons walks through it.

The layout

my-repo/
  .kraft/
    library.yaml
    profiles.yaml
    chains/default.yaml
    chains/hotfix.yaml
    skills/release-notes/SKILL.md

These four things sit directly in .kraft/, each optional:

PathHolds
library.yamlThe tasks, steps, nodes and steering sections, as in the local file.
profiles.yamlA profiles: section of agent profiles.
chains/<name>.yamlOne chain per file. The chain is named by its id, or by <name> when it has none.
skills/<name>/SKILL.mdOne method per directory.

A .kraft/ with none of them is not a repo library. A repository that only holds .kraft/collection.json is a collection and is unaffected, and one repository may be both.

An add-on's addon.json is derived, never written:

Add-on fieldRepo library
name, namespaceThe repository's id in repos.yaml. There is no alias.
versionThe first 12 hex digits of the digest, wherever a version is shown. A change is a different digest.
requires.harnessesEvery harness: its tasks and agent profile fallbacks name.
requires.profilesEvery bare profile: its tasks name.
requires.kraftNone. A file this Kraft cannot read fails the check.

Names

Outside the files, everything is <repo-id>:<name>, such as my-repo:hotfix. The repository id differs between machines, so no file writes it. Inside the files, references resolve as in an add-on:

KeyA bare name meansQualified
extends, steering, skillThe library's own declaration.library:<name> only. A skill may also name kraft: or another tool's plugin.
profileThe instance's agent profile.library:<name>, for a profile in its profiles.yaml.
harnessThe instance's harness profile.Never qualified.

Any other qualifier is refused, the repository's own id included. A repo id counts as another namespace when an add-on or another repo library is checked.

Accepting is refused when the id is kraft, is an installed add-on's namespace, or is longer than 64 characters. The message says to set another id on the entry in repos.yaml. Accepting writes an id when the entry has none.

What it may hold

Exactly what an add-on may hold: limits and never permissions, no subprocess task, no harnesses section, no reference into an add-on or another repo library, and the same size and content limits. Harness profiles stay per machine.

Where it is read from

The server asks the remote which commit to trust, then reads that commit's files from the connected checkout's object store:

  1. git ls-remote --symref origin HEAD gives the default branch's name and commit. It runs in the checkout, with its git config and credentials.
  2. When the checkout lacks that commit, git fetch origin <commit> fetches it.
  3. Kraft reads .kraft/ from that commit as blobs.
  • The checkout's branches, remote-tracking refs, working tree and worktrees are never read, so moving one changes nothing.
  • The server does this, never the CLI, so a check works at connect exactly when it works later.
  • A checkout that lacks the blobs, a partial clone, is refused with a message naming the commit. Nothing is fetched lazily.
  • A repository with no origin has no repo library.
  • The origin URL is recorded with credentials removed.

Accepting

A repo library is not loaded until a person accepts it.

  • kraft repo connect, run in a terminal, prints the review and asks whether to take it.
  • -y, a connect with no terminal, --json, the web UI and an agent's ensure_repo never ask. The library waits for kraft repo library.
  • A library that was never accepted is not loaded, and is no fault: health stays ok.
  • After a first load is accepted at a prompt, Kraft asks once whether to take later text changes without asking, and says what that trusts. See Automatic updates.

The first review shows everything as new, names the remote URL and commit, and says so when the library's default chain becomes the chain new items in this repo run. kraft repo library shows a review and takes it.

An accept is refused with a 409 and writes nothing when the remote or the lock changed since the review a person read.

Changes

The server asks the remote about every accepted library after each start and once a day. A library nobody accepted is not asked about, except when a person runs kraft repo library.

  • A change is reported as waiting, and nothing is applied, unless the entry opted in to automatic updates. kraft repo library shows its review and takes it.
  • The accepted version keeps serving, and health is not degraded.
  • A change is refused, and the accepted version kept, for the reasons an add-on update is: a broken content rule, a harness or profile the instance lacks, a chain that does not resolve, or a chain or reference of yours that would stop resolving.

Each outcome is kept in run/addons/status.json beside the add-ons'. The repo libraries row of kraft admin doctor names a waiting or held change and a failed check. A library that was accepted and no longer loads is listed by GET /api/health under invalid_templates as repo library <id>.

One repo library or add-on change runs at a time.

Automatic updates

A repository's entry in repos.yaml can set library_auto_update: true, default false, to take changes to its accepted repo library without a person when nothing but text changed. It is set in three ways:

  • by answering yes to the question kraft repo connect or kraft repo library asks once, in a terminal, after a first load is accepted;
  • by editing repos.yaml;
  • with PATCH /api/repos, which marks the entry managed: true as every such save does.

A change is applied unattended only when all three rules hold:

  1. The review has no Reach line.
  2. No chain differs other than in its prompt, message, description or icon, and no chain is added or removed. A chain here is every one that resolves through the library, the library's own and yours.
  3. No changed skill or steering text is one that a gate's auto_review, a fix loop's judge or an escalation names.

A change that fails rule 2 or 3 is held with the reason, such as chain my-repo:ship changes more than text or skill my-repo:verdict is read by a task that decides a gate. A person may still take it: those reasons are not Reach lines, and the review reads as it did.

OutcomeMeaning
up to dateThe remote's files are the accepted ones.
waitingA change, or the remote no longer has a library, and the entry has not opted in. Nothing is applied.
appliedThe entry opted in and the change passed the three rules. It is loaded, and the server logs its review.
heldThe entry opted in and a Reach line or a rule above stopped the change. The message says why. Run kraft repo library.
failedThe check could not be made, the candidate was refused, or the lock was busy (kind: busy). The accepted version keeps loading.

The key is read again just before a change is applied, so clearing it during a slow check is honoured.

A repo library adds four Reach lines to a change of an already accepted one. Each holds an unattended apply:

LineWhen
origin's URL changed: <was> -> <now>The remote's URL is not the one that was accepted.
the default branch changed: <was> -> <now>The remote's default branch is not the one that was accepted.
becomes the chain new items in this repo run: <id>:defaultThe change adds a default chain, and the entry's default_chain is unset or default.
new items in this repo go back to the instance's default chainThe change removes it.

Two things differ from add-on auto-update:

  • A repo library has no version. Every merge to the default branch that changes the files is a candidate, which rule 2 is there to make safe.
  • A candidate whose files are the accepted ones is up to date even from a moved URL or branch, and the lock keeps the old ones. The next change is then held on the URL or branch line until a person takes it, which rewrites the lock.

A first load and a removed library always wait for a person. A remote that no longer has a library is reported waiting and never dropped. Opting in trusts whoever can merge to the default branch with what the repository's agents are told: see Security.

The default chain

An item filed with no chain runs, in order:

  1. the default_chain of the repository's entry, when it is set and is not default;
  2. <repo-id>:default, when the repository's library is loaded and has that chain;
  3. the instance's default.

default counts as unset because connecting through the API writes it into every entry. To keep the instance's own chain after accepting a library with a default chain, name another chain in default_chain, or decline the library.

CallerBehaviour
kraft item create, POST /api/work-items, POST /api/triggers, auto-intakeFollow the order above.
A cron schedule in intake.yamlUnchanged: it names its own chain.
Duplicating an itemThe copy runs the chain the original ran. An item that ran the instance's default stays on it.
Settings draftsShow and count the chain step 2 would pick.

repo-libraries.lock

config/repo-libraries.lock records the accepted version of each repo library, keyed by repository id. config/addons.lock is not touched. Only kraft repo library and an automatic update write it.

config/repo-libraries.lock
# Written by `kraft repo library`. Do not edit.
lock_version: 1
repos:
  my-repo:
    git: https://github.com/acme/my-repo.git
    branch: main
    commit: f4a11422cc8377a6ae61353897431dafa72b8ef5
    tree: de1b745c4020ff2d468c14451da2f28a5fb56207
    digest: sha256:f915f6266d319e9c0bc2bb2483683d74f307839138258901919e7d071fd37da4
    updated_at: '2026-10-11T13:23:58.690123Z'
FieldMeaning
gitorigin's URL when it was accepted, with credentials removed.
branchThe remote's default branch then.
commitThe commit of that branch that was read.
treeThe git tree of .kraft/ in that commit.
digestThe digest of the library's files, derived manifest included.
updated_atWhen the entry was written.

An entry whose repository id is no longer in repos.yaml is not loaded and is no fault. The next accept or disconnect drops it.

Work items, restore and the store

A work item records the repo library its chain resolved through as it does an add-on: id (library@repo:<repo-id>), commit, tree, source (./.kraft) and digest. It reads that version's skills and agent profiles until it ends.

A missing store is restored by fetching the commit into the connected checkout and extracting again. The files are kept only if their digest matches. A repository with open items cannot be disconnected, so the checkout is there. When the commit is gone from the remote, the item stops as one with an unrestorable add-on does. The extracted files sit in the content-addressed store add-ons use.

Disconnecting

kraft repo disconnect drops the lock entry. It is refused while local configuration names the namespace, and lists each reference: a default_chain of another repository, a chain in intake.yaml, or an extends in your own chains. Running items finish on their pinned version.

Saving a repos.yaml that removes or renames the id of an accepted library is reported before it is applied: would leave repo library my-repo out: repository id 'my-repo' is no longer in repos.yaml.

Copyright © 2026