Repo libraries
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:
| Path | Holds |
|---|---|
library.yaml | The tasks, steps, nodes and steering sections, as in the local file. |
profiles.yaml | A profiles: section of agent profiles. |
chains/<name>.yaml | One chain per file. The chain is named by its id, or by <name> when it has none. |
skills/<name>/SKILL.md | One 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 field | Repo library |
|---|---|
name, namespace | The repository's id in repos.yaml. There is no alias. |
version | The first 12 hex digits of the digest, wherever a version is shown. A change is a different digest. |
requires.harnesses | Every harness: its tasks and agent profile fallbacks name. |
requires.profiles | Every bare profile: its tasks name. |
requires.kraft | None. 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:
| Key | A bare name means | Qualified |
|---|---|---|
extends, steering, skill | The library's own declaration. | library:<name> only. A skill may also name kraft: or another tool's plugin. |
profile | The instance's agent profile. | library:<name>, for a profile in its profiles.yaml. |
harness | The 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:
git ls-remote --symref origin HEADgives the default branch's name and commit. It runs in the checkout, with its git config and credentials.- When the checkout lacks that commit,
git fetch origin <commit>fetches it. - 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
originhas no repo library. - The
originURL 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'sensure_reponever ask. The library waits forkraft 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 libraryshows 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 connectorkraft repo libraryasks once, in a terminal, after a first load is accepted; - by editing
repos.yaml; - with
PATCH /api/repos, which marks the entrymanaged: trueas every such save does.
A change is applied unattended only when all three rules hold:
- The review has no Reach line.
- No chain differs other than in its
prompt,message,descriptionoricon, and no chain is added or removed. A chain here is every one that resolves through the library, the library's own and yours. - No changed skill or steering text is one that a gate's
auto_review, a fix loop'sjudgeor anescalationnames.
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.
| Outcome | Meaning |
|---|---|
up to date | The remote's files are the accepted ones. |
waiting | A change, or the remote no longer has a library, and the entry has not opted in. Nothing is applied. |
applied | The entry opted in and the change passed the three rules. It is loaded, and the server logs its review. |
held | The entry opted in and a Reach line or a rule above stopped the change. The message says why. Run kraft repo library. |
failed | The 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:
| Line | When |
|---|---|
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>:default | The 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 chain | The 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 dateeven 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:
- the
default_chainof the repository's entry, when it is set and is notdefault; <repo-id>:default, when the repository's library is loaded and has that chain;- 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.
| Caller | Behaviour |
|---|---|
kraft item create, POST /api/work-items, POST /api/triggers, auto-intake | Follow the order above. |
A cron schedule in intake.yaml | Unchanged: it names its own chain. |
| Duplicating an item | The copy runs the chain the original ran. An item that ran the instance's default stays on it. |
| Settings drafts | Show 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.
# 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'
| Field | Meaning |
|---|---|
git | origin's URL when it was accepted, with credentials removed. |
branch | The remote's default branch then. |
commit | The commit of that branch that was read. |
tree | The git tree of .kraft/ in that commit. |
digest | The digest of the library's files, derived manifest included. |
updated_at | When 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.
Related
- Add-ons and collections: the same content, published through a collection.
- Repo verbs:
kraft repo library. - Security: what accepting trusts.
Add-ons and collections
The files of a Kraft add-on and its collection, addons.yaml and addons.lock, the naming rules, what an add-on may set, and when an add-on loads, updates and is restored.
Overview
Every key a chain file accepts, from the chain down to each kind of task, plus read_only, extends, icons and canonical paths.