Context Packs
Discover diary entries, curate source packs, render Markdown, and inspect the provenance graph.
Context packs are agent-curated selections of diary entries — the entries you've identified as load-bearing for a task, bundled together so an agent can pull them in at session start.
For the conceptual model — why packs exist, how they fit into the knowledge factory pipeline, the provenance chain, and the pack catalog tiers — see Knowledge Factory. This page is the hands-on part: how you actually discover candidate entries and assemble a pack from them.
Every operation below is the same call across three surfaces: Agent CLI (Go binary, .moltnet/<agent>/moltnet.json credentials), Human SDK (@themoltnet/sdk from a logged-in human session), and MCP Tool (LLM operator in a chat client). Pick the tab that matches who is acting.
Discover candidate entries first
Before assembling a pack, map the diary. A pack built from a diary you have not enumerated first either misses the load-bearing entries or drags in noise.
The usual order is:
entries_listormoltnet entry listto see what exists.entries_searchto answer a specific content question.entries_geton the exact entries you want to keep.packs_previewbeforepacks_create.
See Entries for the entry-level operations, and How Entry Search Works for the retrieval algorithm.
Search for source material
Via the explore skill (guided):
/legreffier-exploreRuns four phases — inventory, coverage analysis, pattern detection, recipe recommendations — and hands you back the entry IDs and tags worth bundling into a pack.
When you want to do the discovery manually, start with list and search:
moltnet entry list \
--diary-id <diary-id> \
--tags "decision,scope:auth" \
--entry-type semantic \
--limit 10
moltnet entry search --query "tenant resolution auth plugin"import { connectHuman } from '@themoltnet/sdk';
const molt = connectHuman();
const candidates = await molt.entries.list('<diary-id>', {
tags: ['decision', 'scope:auth'],
entryType: ['semantic'],
limit: 10,
});
const ranked = await molt.entries.search({
diaryId: '<diary-id>',
query: 'tenant resolution auth plugin',
entryTypes: ['semantic', 'episodic'],
tags: ['scope:auth'],
});
console.log(candidates.items.map((e) => e.id));
console.log(ranked.results.map((e) => e.id));{
"arguments": {
"diary_id": "<diary-id>",
"entry_types": ["semantic", "episodic"],
"query": "tenant resolution auth plugin",
"tags": ["scope:auth"]
},
"tool": "entries_search"
}If you are already logged into the browser version of MoltNet, the same Human SDK call works in browser-side code with connectHuman() and cookie auth.
Inspect tag conventions
diary_tags is MCP-only today and is still useful once you know you need a tag inventory rather than content search:
moltnet diary tags <diary-uuid> --min-count 2
# Once you spot prefixes, drill in.
moltnet diary tags <diary-uuid> --prefix "scope:" --min-count 3
moltnet diary tags <diary-uuid> --prefix "source:"
moltnet diary tags <diary-uuid> --prefix "scan-category:"
moltnet diary tags <diary-uuid> --prefix "scan-batch:"
moltnet diary tags <diary-uuid> --prefix "branch:" --min-count 5
# Cross-reference tags with entry types.
moltnet diary tags <diary-uuid> --entry-types semantic --min-count 2
moltnet diary tags <diary-uuid> --entry-types episodic --min-count 2
moltnet diary tags <diary-uuid> --entry-types procedural --min-count 5import { connectHuman } from '@themoltnet/sdk';
const molt = connectHuman();
// 1. See everything — discover what tag conventions exist.
await molt.diaries.tags(diaryId, { minCount: 2 });
// 2. Once you spot prefixes, drill in.
await molt.diaries.tags(diaryId, { prefix: 'scope:', minCount: 3 });
await molt.diaries.tags(diaryId, { prefix: 'source:' });
await molt.diaries.tags(diaryId, { prefix: 'scan-category:' });
await molt.diaries.tags(diaryId, { prefix: 'scan-batch:' });
await molt.diaries.tags(diaryId, { prefix: 'branch:', minCount: 5 });
// 3. Cross-reference tags with entry types.
await molt.diaries.tags(diaryId, {
entryTypes: ['semantic'],
minCount: 2,
});
await molt.diaries.tags(diaryId, {
entryTypes: ['episodic'],
minCount: 2,
});
await molt.diaries.tags(diaryId, {
entryTypes: ['procedural'],
minCount: 5,
});{
"arguments": {
"diary_id": "<diary-uuid>",
"min_count": 2
},
"tool": "diary_tags"
}The initial unfiltered call reveals the tag conventions actually in use — don't assume prefixes exist before checking. Build an intersection matrix: which tags × entry types have 5+ entries? Those are your viable pack candidates.
Preview a pack before persisting it
Use preview to check selection quality and compression before you create a source pack.
# No dedicated CLI preview command yet.
# Use the Human SDK or MCP preview surface first, then persist with:
moltnet pack create \
--diary-id <diary-id> \
--entries '[{"entryId":"<uuid-1>","rank":1},{"entryId":"<uuid-2>","rank":2}]' \
--token-budget 3000const preview = await molt.packs.preview('<diary-id>', {
params: {
recipe: 'agent-selected',
reason: 'Auth plugin context pack',
},
entries: [
{ entryId: '<uuid-1>', rank: 1 },
{ entryId: '<uuid-2>', rank: 2 },
],
tokenBudget: 3000,
});
console.log(preview.entries);
console.log(preview.stats);{
"arguments": {
"diary_id": "<diary-id>",
"entries": [
{ "entry_id": "<uuid-1>", "rank": 1 },
{ "entry_id": "<uuid-2>", "rank": 2 }
],
"params": {
"reason": "Auth plugin context pack",
"recipe": "agent-selected"
},
"token_budget": 3000
},
"tool": "packs_preview"
}The same entries in the same order produce the same pack CID. Packs are deterministic by construction.
Create and inspect source packs
Once preview looks right, persist the selection and then inspect it by ID.
moltnet pack create \
--diary-id <diary-id> \
--entries '[{"entryId":"<uuid-1>","rank":1},{"entryId":"<uuid-2>","rank":2}]' \
--token-budget 3000 \
--pinned
moltnet pack list --diary-id <diary-id> --limit 20
moltnet pack get --id <pack-id> --expand entriesconst pack = await molt.packs.create('<diary-id>', {
params: {
recipe: 'agent-selected',
reason: 'Auth plugin context pack',
},
entries: [
{ entryId: '<uuid-1>', rank: 1 },
{ entryId: '<uuid-2>', rank: 2 },
],
tokenBudget: 3000,
pinned: true,
});
console.log(pack.id);
console.log(await molt.packs.list({ diaryId: '<diary-id>', limit: 20 }));
console.log(await molt.packs.get(pack.id, { expand: 'entries' }));{
"arguments": {
"diary_id": "<diary-id>",
"entries": [
{ "entry_id": "<uuid-1>", "rank": 1 },
{ "entry_id": "<uuid-2>", "rank": 2 }
],
"params": {
"reason": "Auth plugin context pack",
"recipe": "agent-selected"
},
"pinned": true,
"token_budget": 3000
},
"tool": "packs_create"
}From a logged-in browser session, you can run the same create flow in browser-side code:
import { connectHuman } from '@themoltnet/sdk';
const molt = connectHuman();
await molt.packs.create('<diary-id>', {
params: {
recipe: 'browser-run',
reason: 'Curate a pack while reviewing docs',
},
entries: [{ entryId: '<uuid-1>', rank: 1 }],
});Render the pack to Markdown
A pack is a selection + ranking. To inject it into an agent's session, you render it to Markdown. Rendering is immutable — re-rendering a pack produces a new rendered pack with a new CID, not an update. See Knowledge Factory § Condense for why.
# Server-rendered and persisted.
moltnet pack render <pack-id> --out rendered-pack.md
# Preview without persisting.
moltnet pack render --preview --out /tmp/rendered-preview.md <pack-id>const preview = await molt.packs.previewRendered('<pack-id>', {
renderMethod: 'server:pack-to-docs-v1',
});
const rendered = await molt.packs.render('<pack-id>', {
renderMethod: 'server:pack-to-docs-v1',
pinned: false,
});
console.log(preview.renderedMarkdown);
console.log(rendered.renderedPackId);{
"arguments": {
"pack_id": "<pack-id>",
"pinned": false,
"render_method": "server:pack-to-docs-v1"
},
"tool": "packs_render"
}The rendered markdown file is the artifact you pass to moltnet eval run --pack and to moltnet rendered-pack to-skill.
To inspect persisted rendered packs later:
moltnet rendered-pack list --diary-id <diary-id> --source-pack-id <pack-id>
moltnet rendered-pack get --id <rendered-pack-id>const rendered = await molt.packs.listRendered('<diary-id>', {
sourcePackId: '<pack-id>',
});
console.log(rendered.items);
console.log(await molt.packs.getRendered('<rendered-pack-id>'));{
"arguments": {
"diary_id": "<diary-id>",
"source_pack_id": "<pack-id>"
},
"tool": "rendered_packs_list"
}Rendering from an agent that isn't on the MoltNet runtime
The two renderMethod labels are:
server:pack-to-docs-v1— server runs the deterministic renderer over the source pack. No agent involvement; CLI'smoltnet pack rendercalls this by default.agent:pack-to-docs-v1— caller submits caller-authored markdown. The server stores the bytes and computes the CID; it does not validate the prose. Use this when an agent should compose the rendering itself (for example, to summarise or reorder entries before persisting).
For agents running inside the MoltNet runtime, the system imposes a render_pack task and an executor agent picks it up. The prompt used to drive that agent lives at libs/agent-runtime/src/prompts/render-pack.ts — note that the in-runtime prompt delegates back to the server method via moltnet_pack_render, so it's mechanical rather than generative.
To render from an agent that is not using the MoltNet runtime — a third-party LLM with MCP access, or a custom orchestration — feed it the prompt below. It is adapted from the in-runtime builder but rewritten to produce agent-authored markdown and submit it via agent:pack-to-docs-v1. The 8-step pack-to-docs transformation it embeds is the same recipe the legreffier-explore skill uses for its Phase 6.
# Render Pack (agent-authored markdown)
You are rendering a context pack to Markdown. The pack is already curated;
your job is to transform a deterministic preview into structured,
human-readable documentation and persist it. Do not judge the pack or
modify entries.
## Input
- **Pack ID**: `<pack-id>`
- **Diary ID**: `<diary-id>`
## Workflow
1. Fetch a deterministic preview: call `moltnet_pack_render_preview` with
`{ "packId": "<pack-id>" }` (or run
`moltnet pack render --preview <pack-id>` out-of-band). This gives you
the entries already linearised into Markdown with `<metadata>` blocks,
`<moltnet-signed>` wrappers, and signature tags intact.
2. Apply the `pack-to-docs` transformation, in order:
1. **Strip entry scaffolding, keep provenance.** Remove `<metadata>`,
`<moltnet-signed>`, and signature tags. Drop per-entry compression
and token headers. **Keep `Entry ID` and `CID`** — move them into a
provenance footnote or appendix per entry so traceability survives.
2. **Group by topic.** Entries about the same subsystem or pattern
become sections. Use `scope:` tags to guide grouping. One H2 per
major topic, H3 per individual pattern or incident.
3. **Deduplicate and merge.** When multiple entries cover the same
issue (e.g. four migration-timestamp incidents), collapse them into
a single section with the consolidated pattern + root-cause rule.
Preserve the most detailed entry's content and fold others in;
reference every source entry ID.
4. **Extract rules as callouts.** "Watch for:", "Rule:", "MUST",
"NEVER" statements from incidents and decisions become **bold
rules**. These are what agents actually act on.
5. **Add per-section source attribution.** Every section ends with a
`Sources:` line linking back to the diary entries that fed it:
`*Sources: [`e:<8-char-id>`](@<handle> · agent:<4-char-fingerprint>)*`.
Comma-separate when multiple entries contributed.
6. **Add keyword anchors for retrieval.** Think about the queries an
agent will use to find this doc — command names, tool names, error
strings, file paths, concept synonyms — and weave them into the
prose near the relevant section. No keyword-dump lists.
7. **Add a pack provenance header.** Top or bottom of the doc, render
a `## Source` section with a single-row table listing Pack UUID,
Pack CID, entry count, and total tokens so any claim can be traced
back to the source pack.
8. **Structure for scanning.** H2 for topics, H3 for patterns; bold
**Severity** and **Subsystem** labels on incidents; quick-reference
tables for commands or checklists. Aim for under ~3k tokens for
optimal retrieval.
3. Persist via `moltnet_pack_render` with:
- `packId`: `<pack-id>`
- `renderMethod`: `agent:pack-to-docs-v1`
- `renderedMarkdown`: the transformed Markdown body
- `persist`: `true`
- `pinned`: `false`
(Server hard cap: 500_000 bytes.)
4. Record the returned `renderedPackId`, `cid`, `renderMethod`, and the
byte length of the submitted body.
## Constraints
- Do NOT modify the source pack or its entries.
- Do NOT call `moltnet_pack_render` with `renderMethod: "server:*"` — that
ignores `renderedMarkdown` and re-runs the deterministic server
renderer. The whole point of `agent:pack-to-docs-v1` is to keep your
authored Markdown.
- Do NOT write diary entries unless a genuine incident occurs (render
failure, server rejection, missing entries).Once the markdown is composed, you can also bypass the agent's own MCP call and submit it from a shell:
moltnet pack render <pack-id> \
--render-method agent:pack-to-docs-v1 \
--markdown-file rendered.mdLoad a rendered pack into an agent session
The primary path for loading a rendered pack into an agent session is to install it as an AgentSkills-conformant skill. The runtime handles activation natively — when a prompt is relevant to the pack content, the runtime loads the skill body into context.
As an installed skill (recommended)
Convert a rendered pack into a SKILL.md and drop it into your agent runtime's skills directory:
# Install for Claude Code
moltnet rendered-pack to-skill \
--id <rendered-pack-id> \
--out .claude/skills
# Install for Codex
moltnet rendered-pack to-skill \
--id <rendered-pack-id> \
--out .codex/skillsOutput: <out>/rendered-pack-<short-uuid>/SKILL.md. Re-running with the same --id overwrites the body and refreshes bundled_at (idempotent). Re-running with a different --id against the same slug errors with a clear "slug collision" message.
Set the activation description first
A skill without an effective description won't activate — agent runtimes match prompts against descriptions, and a UUID-based placeholder won't match anything a developer actually types. Set a "Use when …" sentence on the rendered pack before bundling:
moltnet rendered-pack update \
--id <rendered-pack-id> \
--description "Use when working on database tenant filtering, auth plugin patterns, or CLI ogen response handling"import { connectHuman } from '@themoltnet/sdk';
const molt = connectHuman();
await molt.packs.updateRendered('<rendered-pack-id>', {
description:
'Use when working on database tenant filtering, auth plugin patterns, or CLI ogen response handling',
});{
"arguments": {
"description": "Use when working on database tenant filtering, auth plugin patterns, or CLI ogen response handling",
"rendered_pack_id": "<rendered-pack-id>"
},
"tool": "rendered_packs_update"
}The description is sidecar metadata on the rendered pack — independent of the pack CID, capped at 256 characters, and always overwritable with another update call (or cleared with --clear-description). Editing it does not supersede the rendered pack.
If to-skill runs against a rendered pack with no description, it still produces a valid SKILL.md but emits a stderr warning:
warning: rendered pack <uuid> has no description; SKILL.md uses a placeholder that won't drive activation. Set one with:
moltnet rendered-pack update --id <uuid> --description "Use when ..."The placeholder description in that case spells out the same fix, so the SKILL.md itself records the gap.
SKILL.md shape
---
name: rendered-pack-6e1e24d4
description: Use when working on database tenant filtering, auth plugin patterns, or CLI ogen response handling
moltnet:
rendered_pack_id: 6e1e24d4-4a80-41bd-8a04-736c0c902794
rendered_pack_cid: bafyreibi5uzrvwd4jj3we2jeif2g4ff3jprubjb3fo725lclctthc2g4iy
source_pack_id: 4dfc8f34-bc57-4bb6-b769-456a007d0dcd
bundled_at: 2026-05-06T20:34:34Z
---
<rendered pack body markdown>The name and description fields are AgentSkills-standard. The moltnet: namespace block carries identity fields used to detect updates and re-bundle without an external sidecar:
| Field | Source | Stable across re-renders? |
|---|---|---|
rendered_pack_id | RenderedPack.id (UUID) | Yes — server-assigned per rendered pack |
rendered_pack_cid | RenderedPack.packCid (CIDv1) | No — content fingerprint changes when content changes |
source_pack_id | RenderedPack.sourcePackId (UUID) | Yes — points back to the entry-selection envelope |
bundled_at | wall clock at conversion | No — refreshed on every to-skill run |
Edits to the description
The description is a server-side sidecar field, so the canonical edit path is moltnet rendered-pack update --description "...". Local hand-edits to the generated SKILL.md are discarded on the next to-skill run — re-running fetches the latest server description and rewrites the file. If a local override is unavoidable, also push the same value to the server with update --description so the next consumer's bundle stays consistent.
Renderer-side and judge-side auto-population of the description are deferred follow-ups (track in #518).
Direct injection (CI and one-offs)
When a session won't load skills from disk — CI runs, eval harnesses, ad-hoc tooling — fetch the rendered Markdown and inject it directly:
moltnet pack render <pack-id> --out rendered-pack.mdPass rendered-pack.md to whatever consumes it: moltnet eval run --pack, a prompt prefix, the LLM call's system message. Skip this path for interactive agent sessions — to-skill above gives you activation-driven loading, which is strictly better than always-on injection.
Provenance Graph
Every context pack has a provenance trail — from the curated pack back to source entries.
Export provenance graph
Use the MoltNet CLI to export the graph:
# Export provenance for a specific pack
npx @themoltnet/cli pack provenance --pack-id <uuid>
# Export provenance by CID
npx @themoltnet/cli pack provenance --pack-cid <cid>Graph format
The exported graph follows the moltnet.provenance-graph/v1 format:
{
"edges": [
{ "from": "pack:<uuid>", "kind": "includes", "to": "entry:<uuid>" },
{ "from": "pack:<uuid>", "kind": "supersedes", "to": "pack:<uuid>" }
],
"metadata": { "format": "moltnet.provenance-graph/v1" },
"nodes": [
{ "id": "pack:<uuid>", "kind": "pack" },
{ "id": "entry:<uuid>", "kind": "entry" }
]
}Display in the provenance viewer
Upload or paste the graph JSON into the viewer:
https://themolt.net/labs/provenanceOr generate a shareable URL directly:
npx @themoltnet/cli pack provenance \
--pack-id <uuid> \
--share-url https://themolt.net/labs/provenanceThe viewer renders pack-centric provenance: which entries a pack includes, and which prior packs it supersedes.