Skill packs, and the update cycle
What a pack is and why nobody writes one: the inputs, the input manifest, how a compile runs, when a pack goes stale and who recompiles it, what a pinned depth means, how a hub is exported and imported, and the bounds every one of those operations is checked against.
markdownguidev1R4RF-5GZ5area: packsaudience: agentaudience: maintainerskill-packscompilestalenessmanifestexportimportbounds
Skill packs, and the update cycle
A pack is an output of the hub, not an input to it. Development teams ship features, fix bugs, publish release notes and update documentation; nobody sits down to write a skill pack, and a platform that asks them to will get stale packs. So the hub compiles one, from documents that already exist, and the compile is repeatable.
This guide covers the pack side of that cycle and the hub side beside it: export and import, which are how a whole hub moves. Every figure is read from the compile artefact or the record named at the figure.
1. What a pack is
Inputs: one workflow document and the documents its steps name, each taken at a
depth. Output: the ordered documents, inlined, plus an input manifest listing
every document by short id, version, content hash, depth and freshness at compile time,
together with the workflow's own version.
A transient bundle and a released pack are the same object at two lifetimes. A search answer or a workflow step composes a bundle on the fly and discards it. A pack is compiled by an explicit call, stored, versioned, and served at the three depth rungs.
Why the manifest is the interesting half: a production agent that loads one pack and
never calls back is the intended mode of use, and the manifest is what makes that
silent run auditable afterwards. The agent's logs name the pack version, the manifest
names every document by id and version, and the documents name their own sources. A
pack carries freshness as the minimum tier of its inputs, so one asserted document
makes the whole pack asserted — which is the honest answer.
2. The worked example, and the figures it produced
This hub has one compiled pack, from the workflow guides/bootstrap-a-hub.md. Its
input manifest is committed at docs/hub/packs/bootstrap-a-hub.manifest.json and the
figures below are read from that file rather than from any sentence about it:
| figure | value |
|---|---|
| documents compiled | 5 |
| files in the pack | 6 — the five inputs plus the manifest |
| rendered bytes | 8,910 |
| token estimate | 2,228 |
| pack freshness | asserted, the minimum tier of the inputs |
| excluded | 1, as a duplicate: the workflow names one reference document twice |
inputs at depth lite | 4 of 5; the fifth is normal |
| inputs with a pinned depth | 5 of 5 |
Two readings of that table matter more than the numbers.
Four of five inputs are lite because the workflow chose it, not because the
compiler economised. A lite input renders the document's front matter and the call
that returns the body, which is what the navigate rung is for. The compiler's own depth
policy decides the rung only for a step that pins none.
Every input is depth-pinned, so no amount of usage can make this pack stale. Only a change to a named document's bytes, to the set of documents, or to the workflow itself can. That is the rule working as designed for a workflow that makes its own reading decisions, and it is the clearest illustration of what a pinned depth means.
The compile ran as a service call on a developer cluster, not over the MCP surface.
Six of the eighteen declared tools are on the wire on the release this hub was tested
against, and skills is not one of them; the pack above was compiled by calling the
service directly. The pack mechanism is built; the door to it is not open yet.
3. Staleness, and who recompiles
A pack is marked stale when its inputs move by more than the recompile threshold. The default threshold is: any input document version change, or a learned-depth change on two or more inputs.
Crossing the threshold does not recompile the pack. It does three things:
- marks the pack stale;
- reports that staleness in the envelope of every read, alongside the manifest, naming
the inputs that moved and their new versions or depths;
- files a problem entry for the hub's maintainer with the recompile call pre-filled as
the next action.
The compile call remains the only writer of a pack version. That is deliberate: the promise that a pack is correct and current is kept by staleness being *visible on every read*, not by an unnamed background process silently changing what an agent loads. So the answer to "who recompiles" is: a maintainer, or an agent acting as one, on the problem entry the hub filed. Nobody else, and nothing automatically.
A pinned depth edit is a workflow change, not a usage event. If you edit a workflow to pin a step's depth, you have changed the workflow document, which moves its version and marks every pack compiled from it stale. The distinction is worth keeping straight because the two look alike in a manifest and are not alike at all: a learned depth moves because readers behaved differently, and a pinned depth moves because an author decided. The manifest carries a flag per input saying which it was, and that flag was added precisely because a review measured a pinned edit being counted as learned movement.
Release notes for a pack are the diff of two manifests: which documents changed, which were added or dropped, which moved depth, and why. Nobody writes them.
4. Export and import: moving a hub
The same two commands serve an agent and an operator. Export produces a bundle from the whole hub, a folder or one document. Import reads a bundle into a hub at a path, in merge or replace mode, with a dry run that writes nothing.
The import is bounded, because it is a multiple of a bounded operation: one import is N document writes in one transaction with N chosen by the caller. So it carries two bounds of its own — a file count (2,000 by default, bounds 1 to 20,000) and a byte total (50 MiB by default, bounds 1 MiB to 500 MiB) — and both are checked before any file is read, on the directory walk's own counters. An oversized bundle costs one directory pass and no file reads, and the refusal names both the bound and the measured value. A hub may tighten either bound and may never exceed the platform ceiling.
The dry run is the whole safety net. It performs every write the real run would and
rolls the transaction back, so its report is the real answer discarded rather than a
prediction. It returns one verdict per entry — created, changed, unchanged or refused —
and it refuses an oversized _boot/ entry with the measured tokens and the budget
named, exactly as an authored write does. Fix every refusal, then run for real.
Two properties a reader should know before trusting a dry run's output. The counts and the per-entry verdicts are real. The catalog digest is not comparable with the real run's, because the dry run mints the short ids the catalog names and then rolls them back; the runner prints that caveat beside the digest.
The import requires maintainer on the hub, one rung above the contributor floor
a document write needs. That rung is named in this hub's own bootstrap runbook, in the
runner's header and in the runner's output, because it was named in no document at all
until a first end-to-end run was refused by it.
Front matter is derived from structure, never from content, and only where it is
absent. Derivation is per field: a file with partial front matter keeps every field it
declares, and every derived field is reported per entry so nothing silently acquires a
role. role comes from the first path segment, shape is always markdown, summary
is the first real body line, status is draft, and covers, facets and
depends_on are left empty rather than invented. Freshness is asserted and is never
raised by an import.
One consequence worth planning for: a document left to the derivation is in the hub
and in no pack, because a compile never inlines a draft. That is the right default for
an unreviewed file and the wrong one for a curated corpus, which is why this hub's own
import manifest declares status: published on every mapped file.
5. The bounds, and what is honestly not there
| operation | bound | where it is checked |
|---|---|---|
| import | file count and byte total | the directory walk, before any file is read |
| import | archive limits | the zip guard, on an archive source |
any _boot/ entry | the whole-file and hand-written token budgets | the shape validator, identically on a write and on an import |
| a document body | the ingest split cap | at ingest: a body above the embed cap is split into parts, and a split that would exceed the part cap is refused with both numbers named |
| folder pull | a token cap, and an explicit per-call budget | the pull path; the per-call budget is the only truncation and it is reported |
Three gaps, each recorded rather than smoothed over:
- The export pays one of its four promises. The content tree is produced; the
version history and change log for a narrowed path, the signed expiring bundle, and
the federated effective-scope format are named in the bundle's own manifest under what
is not included, so a reader of the artefact is told rather than misled
(tracking/backlog/hub.md item 27).
- An archive source, a URL source and replace mode are not served. The import
answers a typed not-implemented refusal for each, before it touches the hub.
- **The generated folder card and source manifest can be neither generated nor
authored** today: nothing writes them, and the path rule refuses an underscore-prefixed file name, so a bundle cannot carry them either.
6. The cycle, in order
- Write or edit the documents. Front matter per
reference/standard/hub-standard.md; status: published on anything you want a
pack to inline.
- Write or edit the
workflowdocument: ordered steps, each naming its documents and
pinning a depth where the step knows what it needs.
- Compile. Read the manifest the compile returns, and keep it — it is the audit record
of every silent run that follows.
- Watch for staleness on read. The envelope names the inputs that moved.
- Recompile when a maintainer acts on the problem entry, and read the diff of the two
manifests as the release note.
- Export when the hub has to move; import with a dry run first, always.
Next reads
guides/bootstrap-a-hub.md— the workflow this hub's one pack was compiled from.reference/standard/hub-standard.md— the front-matter contract a compile depends on.guides/sdk-and-transport.md— which of these calls are on the wire today.guides/runbooks/register-in-commander.md— the publisher side of a hub's content.