- drop the docs/agents/bundlewrap/ folder; bundlewrap-language docs now live in the personal fork's AGENTS.md (canonical reference). ckn-bw links out instead of duplicating - slim commands.md scope from ~80-120 to ~30-50 lines (fork carries the generic bw runbook; ckn-bw keeps only repo-specific deltas: apt-key verification, *.py_ suspended-node behavior, vault-echo guidance) - sync bw command syntax against 5.0.3 source (no -p flag; use bare or --preview; bw hash takes only literal node/group names; replace bw groups -n with bw nodes -a groups) - rebalance phase 2 seed list: php -> routeros-monitoring (highest-churn bundle in 18mo per user-story analysis) - update fork install pointer to editable github reference - new section 0 documents revisions inline so a reader sees current shape Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
18 KiB
Agent-friendly repo — design
Date: 2026-05-10
0. Revisions
Material revisions since this spec was first written, kept here so anyone reading the spec sees the current shape rather than the original intent.
- Fork pivot. Originally the spec planned a
docs/agents/bundlewrap/folder (README.md+items.md+metadata.md) explaining bundlewrap-the-language inside ckn-bw. That folder is gone; the maintainer maintains a personal bundlewrap fork atgithub.com/CroneKorkN/bundlewrapwhose rootAGENTS.mdcarries the canonical agent-oriented bundlewrap-language reference. ckn-bw's docs link out to the fork instead. The venv installs editable from the fork (-e git+https://github.com/CroneKorkN/bundlewrap.git@main). commands.mdslimmed from ~80–120 lines to ~30–50 lines: the fork'sAGENTS.mdcarries the canonical bw runbook (read-only allowlist, after-change table, hash-diff workflow,bw debugsketch); ckn-bw'scommands.mdshrinks to repo-specific deltas (apt-key verification,*.py_suspended-node behavior, vault-echo guidance).- Phase 2 seed-list rebalance.
phpswapped out forrouteros-monitoringbased on user-story analysis: php is a low-churn usage hub (8 refs but ~zero recent commits); routeros-monitoring is high-churn (15 commits in 18 months), exactly where seeded docs pay off most. See plan for empirical justification. - bw-syntax corrections found by per-task code-review during the fork's
AGENTS.md implementation, synced in:
bw items <node> <id> -pdoes not exist (use bare or--preview);bw hashaccepts only literal node / group names (selectors likebundle:<x>work forbw nodesetc., but not forbw hash);bw groups -n <node>does not exist (usebw nodes <node> -a groups). - Workflow + user-story validation findings (16 small content adds
across area docs, the per-bundle template,
commands.md, andconventions.md) are recorded in the implementation plan rather than back-fitted into this spec — they're additions to file content, not scope changes.
1. Goals & non-goals
Goal. Make this BundleWrap config repo legible to agents (and humans) so an
agent can land useful work — adding/modifying a bundle, configuring a node,
running read-only bw introspection, doing a cross-cutting refactor — without
spelunking and without unsafe side effects.
In scope.
- Root entry point:
AGENTS.md(withCLAUDE.mdas a symlink to it). - Per-area
AGENTS.mdforbundles/,nodes/,groups/,libs/,hooks/,data/,items/,bin/. Mechanism-focused; no enumeration of contents. - Per-bundle
AGENTS.md: one balanced doc per bundle, replacing existing bundleREADME.mdfiles. Template provided. docs/agents/conventions.md: repo-specific idioms (vault magic strings, bundlewrap-fork install pointer, files-not-to-touch).docs/agents/commands.md: ckn-bw-specific deltas to the fork's bw runbook (apt-key verification, suspended-node behavior, vault-echo guidance). Canonical bw command reference lives in the fork'sAGENTS.md.- A docstring/header pass on
libs/*.py,hooks/*.py,bin/*so each individual file self-describes. - Phase 2 seed: per-bundle
AGENTS.mdfor 10 bundles selected empirically.
Out of scope (explicitly).
- No tooling changes: no
bwwrapper, no Makefile, no lint, no CI. - No code refactoring, renaming, or splitting bundles.
- No mass-fill of all 103 bundles'
AGENTS.mdup front. Phase 3 is leave-as-you-go. - The root
README.md(personal TODO list) stays untouched. - Contributing
AGENTS.mdupstream to bundlewrap is acknowledged but out of scope for this work.
2. Information architecture
ckn-bw/
├── AGENTS.md # root entry point (agents + humans)
├── CLAUDE.md # → symlink to AGENTS.md
├── README.md # untouched (personal TODO)
├── docs/
│ └── agents/
│ ├── conventions.md
│ └── commands.md # ckn-bw deltas; canonical bw runbook is in
│ # the fork's AGENTS.md (linked from here)
├── bundles/
│ ├── AGENTS.md # what bundles are, how they compose
│ ├── AGENTS.template.md # template for per-bundle docs
│ └── <bundle>/
│ └── AGENTS.md # per-bundle doc (replaces existing READMEs)
├── nodes/
│ └── AGENTS.md
├── groups/
│ └── AGENTS.md
├── libs/ AGENTS.md
├── hooks/ AGENTS.md
├── data/ AGENTS.md
├── items/ AGENTS.md
└── bin/ AGENTS.md
Reading order an agent should follow. Root AGENTS.md → relevant area
AGENTS.md → specific bundles/<x>/AGENTS.md → docs/agents/conventions.md
when a repo-specific idiom is in play → fork's AGENTS.md (at
https://github.com/CroneKorkN/bundlewrap/blob/main/AGENTS.md) for any
bundlewrap-language question (item types, dep keywords, metadata reactor
semantics).
Per-area files (not just root). An agent editing bundles/nextcloud/items.py
already has bundles/AGENTS.md and bundles/nextcloud/AGENTS.md adjacent in
the file tree. Locality of reference matters more than centralization.
Existing per-bundle README.md files (~10 of them). Folded into the new
AGENTS.md and removed in the same change, so there is exactly one doc per
bundle.
3. Per-bundle AGENTS.md template
One balanced doc serving both audiences. Prose where prose helps, structure where structure helps. Sections in order:
# <bundle-name>
<1–3 sentences: what this bundle does and when you'd use it.>
## Usage
<How to apply: which group(s) typically include it, or how a node opts in.
Minimal example of node metadata if any keys are required.>
## Metadata
Keys read from `node.metadata`:
```python
{
'nextcloud': {
'domain': 'nc.example.com', # str, required — public hostname
'admin_user': 'admin', # str, default 'admin'
'apps': [], # list[str], default [] — apps to enable
'preview': {
'enabled': True, # bool, default True
},
},
}
```
## Produces
<Brief list of items created: files, services, packages, users, etc.
One line each. Skip if trivially obvious from items.py.>
## Depends on
<Other bundles required, or "none". Note ordering quirks if any.>
## Gotchas
<Non-obvious behavior, manual steps, known pitfalls. Omit the section if none.>
Design choices.
- Free-form prose at the top (purpose, usage) keeps it readable for humans skimming.
- Metadata is a Python dict literal — matches how
metadata.pyactually looks, shows nested structure at a glance. Trailing comment per leaf carries type, required/default, and a short note. ProducesandGotchasare optional — skip when there is nothing useful to say.- No version/changelog/author fields — git already covers that.
4. Root AGENTS.md content
Target ~150 lines. Sections in order:
- What this repo is. 2–3 sentences. BundleWrap config-management for personal/family infra (~22 nodes), Python-defined nodes/groups/bundles, applies to real machines.
- Quickstart for agents. Five bullets, the operating envelope:
- Default to read-only
bwcommands; neverbw apply/bw run/bw lockwithout explicit user request. Seedocs/agents/commands.md. - Never paste/echo decrypted secret values; respect the demagify magic-string
convention. See
docs/agents/conventions.md. - Do not modify
.secrets.cfg*,.venv,.cache,.bw_debug_history,.envrc. Everything else is editable, but treathooks/anditems/(custom item types) with extra care — they affect bw's behavior or item resolution across the whole repo. - Repo runs editable from the maintainer's bundlewrap fork
(
github.com/CroneKorkN/bundlewrap, branchmain); behavior tracks upstream main but the fork'sAGENTS.mdis the canonical bundlewrap-language reference. Seedocs/agents/conventions.mdfor install detail. - Prefer adding helpers to
libs/over duplicating logic across bundles.
- Default to read-only
- Layout map. Terse, link-rich. One line per top-level dir, each linking
to that area's
AGENTS.md. - How nodes/groups/bundles fit together. 5–10 lines: nodes pick up bundles
via groups; metadata flows from groups → node → metadata processors;
nodes.pyandgroups.py(root) are the loaders that walk the dirs and rundemagify. - Conventions you must know. One-line summary + link for each:
- Fork's
AGENTS.md(https://github.com/CroneKorkN/bundlewrap/blob/main/AGENTS.md) — read first if new to bundlewrap. Carries the safety envelope, the after-change runbook, and cheat-sheets for item dep keywords +metadata.pypitfalls. docs/agents/conventions.md#secrets— secrets / demagify magic strings.docs/agents/conventions.md#bundlewrap-version— install pointer (editable from the fork'smain).docs/agents/conventions.md#groups— group inheritance order.docs/agents/commands.md— ckn-bw-specific deltas to the bw runbook (apt keys, suspended nodes, vault-echo guidance).- Lib helpers — see top-of-file docstrings in
libs/*.py.
- Fork's
- Where to look for examples. Pointers to a small bundle, a complex bundle, and a node file.
- Where this doc lives. Note that
CLAUDE.mdis a symlink to this file.
5. Per-area AGENTS.md content
Rule. An area AGENTS.md describes how that area works — mechanisms,
conventions, how to add or modify. It does not enumerate contents.
Specifics live with the thing itself (docstrings, top-of-file comments) or
in a per-subdir AGENTS.md only when that subdir has its own non-obvious
conventions.
Corollary. Every libs/*.py and hooks/*.py starts with a one-line
module docstring. Every bin/* script starts with a # purpose: header
comment. Discovery is by ls + reading those headers, not by an index page.
Each area AGENTS.md has the same five-section shape (target 30–80 lines):
- What's in this directory (one paragraph).
- Conventions (naming, file structure, what each kind of file does).
- How to add / modify (concrete steps for the most common change).
- Pitfalls (area-specific gotchas).
- See also (links to relevant
docs/agents/*and example files).
Per-area specifics:
bundles/AGENTS.md— bundle anatomy (items.py,metadata.py,files/,templates/), where helpers go, when to extract tolibs/. Links out to the fork'sAGENTS.md(item types, dep keywords, metadata reactor semantics) for language-level detail.nodes/AGENTS.md—eval()loading mechanism vianodes.py, demagify magic-string syntax, naming convention pattern (<location>.<role>.py). Pitfall: because node files areeval()'d, no top-level imports — only expression-level constructs.groups/AGENTS.md— sameeval()mechanism viagroups.py, subdir purpose convention (applications/,locations/,machine/,os/), howall.pyinteracts, group-membership rules, inheritance order.libs/AGENTS.md— what libs are (importable from bundles viarepo.libs.<x>), conventions for adding a helper, contribution rule (one-line module docstring required).hooks/AGENTS.md— bw hook lifecycle, when each event fires, how to write a hook.data/AGENTS.md— whatdata/is for (data sources templated and consumed by bundles), conventions for adding a new data source.items/AGENTS.md— what custom item types are, how to write one, when to use a custom item type vs afileitem.bin/AGENTS.md— whatbin/is for (operator tooling, not invoked by bundlewrap).
6. docs/agents/ content
conventions.md (~80–120 lines)
- Secrets / demagify.
!password_for:,!decrypt:,!decrypt_file:,!32_random_bytes_as_base64_for:. What each does, where they're allowed (node files, evaluated throughnodes.py), why agents must never echo decrypted values. - Bundlewrap version / install. Repo runs editable from the maintainer's
personal fork:
pip install -e git+https://github.com/CroneKorkN/bundlewrap.git@main#egg=bundlewrap. Captured inrequirements.txt. The fork'smaintracks upstream main; the fork'sAGENTS.mdis the canonical bundlewrap-language reference. - Group inheritance order & how metadata merges
(
all.py→ location → os → machine → applications → node). - Naming conventions for nodes (
<location>.<role>.py) and groups (subdir purpose). - Files agents must not modify.
.secrets.cfg*,.venv,.cache,.bw_debug_history,.envrc.
commands.md (~30–50 lines)
The fork's AGENTS.md is the canonical bw runbook — read-only command
allowlist, after-change table, hash-diff workflow, bw debug sketch,
verified against 5.0.3 source. ckn-bw's commands.md carries only
repo-specific deltas:
- One-line lead pointing at
https://github.com/CroneKorkN/bundlewrap/blob/main/AGENTS.mdfor the full runbook. - Apt-key after-change row. Editing
data/apt/keys/*.{asc,gpg}→ first verify withgpg --show-keys <newkey>locally + fingerprint diff against the expected source. Trial viabw applyis the failure path (a wrong key blocks unattended upgrades cluster-wide). Not in the fork's runbook because it's repo-specific. *.py_suspended-node interaction. A node file ending in.py_is silently excluded from the loader;bw nodeswon't list it. Document this so an agent doesn't think a node is missing when it's actually parked.- Vault magic-string handling. Never echo decrypted output, even in
bw debugexploration. Cross-link toconventions.md#secrets.
7. Seed work & rollout
Phase 1 — scaffolding
Gated on: the fork's AGENTS.md exists and is reachable at the URL above
(verified 2026-05-10).
docs/agents/conventions.md(Section 6).docs/agents/commands.md(Section 6).- Per-area
AGENTS.mdforbundles/,nodes/,groups/,libs/,hooks/,data/,items/,bin/(Section 5). bundles/AGENTS.template.mdso future bundle docs have something to copy.- Root
AGENTS.md(Section 4) +CLAUDE.mdsymlink →AGENTS.md(written last so all internal link targets exist). - Docstring/header pass: add a one-line module docstring to any
libs/*.pyandhooks/*.pylacking one;# purpose:header to anybin/*script lacking one.
Order rationale: build link targets bottom-up (conventions → commands → area docs → template), then root last, then the docstring pass last. Each piece can be reviewed in isolation; the work bisects cleanly.
Honest scope: ~800–1000 lines of focused writing total now that
bundlewrap-language docs live in the fork. Area docs + conventions land
in 30–120 lines each; root AGENTS.md is ~150 lines.
Phase 2 — seed bundles (10)
Bundles selected empirically (node+group references and recent commit activity, validated 2026-05-10):
Usage hubs (6):
monitored(12 node refs) — meta-bundle, often misunderstood.postgresql(9 refs, 3 cross-bundle).wireguard(8 refs, has own lib + bin script).routeros-monitoring(15 commits in 18 months — most-churned bundle).apt(6 refs, has own lib).nginx(4 refs, web foundational).
Recently active or complex (4):
telegraf(9 cross-bundle refs, 6 recent commits) — monitoring snippets ripple across bundles.backup(7 refs, cross-node coordination).letsencrypt(6 refs, cross-cutting).nextcloud(5 recent commits, complex, actively edited).
(Original §0 noted: php was originally seeded but swapped for
routeros-monitoring after user-story analysis showed it's a low-churn
hub, while routeros-monitoring is the highest-churn target in the repo.)
For each: write one AGENTS.md from the template — purpose, usage, metadata
dict, produces, depends-on, gotchas. Migrate any existing
bundles/<x>/README.md content into it and remove the old README.md.
Phase 3 — leave-as-you-go
- Convention from this point: any time an agent (or you) materially edits a
bundle, top-up or create its
AGENTS.md. Documented as a rule inbundles/AGENTS.md. - No mass-fill of the remaining ~95 bundles up front — most are simple enough
that
items.py/metadata.pyare self-explanatory.
8. Future work (not this spec)
- Tooling: a read-only
bwwrapper or lint that nudges new bundles toward having anAGENTS.md. Worth considering only after Phase 1+2 reveal which conventions actually drift. - Pushing the fork's
AGENTS.mdupstream tobundlewrap/bundlewrap— it's written in a style that allows it; a follow-up the maintainer may pursue.
9. Open questions / risks
- Risk: docs drift. As the repo evolves,
AGENTS.mdfiles lag behind code. Mitigations: per-bundle docs are short (low maintenance); Phase 3 rule attaches doc updates to material code edits; the area docs are mechanism-focused, which changes less often than enumerations. - Risk: fork drifts from upstream. ckn-bw's docs link to the fork's
AGENTS.md; if the fork falls far behind upstream main, the linked semantics might not match what real bundlewrap users see. Mitigation: the fork tracks upstream main via periodic merges; ckn-bw'srequirements.txtpins@mainso the venv stays aligned with the fork's documented behavior. - Open: seed bundles. Phase 2 list is empirically grounded but not
rigid —
zfs(8 refs),bind(4 refs, own lib), andbootshorn(recent burst target) are honourable mentions if a swap is wanted later.