bundlewrap/docs/superpowers/specs/2026-05-10-agent-friendliness-design.md
CroneKorkN 7b44a8ad3a
spec/handoff: record per-bundle README pivot
- spec §0 gets a new revision bullet noting that per-bundle docs
  are README.md (not AGENTS.md), the rigid template is gone, and
  Phase 2 was dropped. flags §3 and §7 as pre-pivot intent only;
  doesn't back-fit them.
- handoff replaced with a short status note (~50 lines vs the
  original ~390): what landed, where current truth lives, and the
  fact that nothing is planned for a next session.

implementation plan in ~/.claude/plans/ is left as a frozen
pre-pivot artifact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 16:06:25 +02:00

402 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 at
`github.com/CroneKorkN/bundlewrap` whose root `AGENTS.md` carries 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.md` slimmed** from ~80120 lines to ~3050 lines: the fork's
`AGENTS.md` carries the canonical bw runbook (read-only allowlist,
after-change table, hash-diff workflow, `bw debug` sketch); ckn-bw's
`commands.md` shrinks to repo-specific deltas (apt-key verification,
`*.py_` suspended-node behavior, vault-echo guidance).
- **Phase 2 seed-list rebalance.** `php` swapped out for
`routeros-monitoring` based 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> -p` does not
exist (use bare or `--preview`); `bw hash` accepts only literal node /
group names (selectors like `bundle:<x>` work for `bw nodes` etc., but
not for `bw hash`); `bw groups -n <node>` does not exist (use
`bw nodes <node> -a groups`).
- **Workflow + user-story validation findings** (16 small content adds
across area docs, the per-bundle template, `commands.md`, and
`conventions.md`) are recorded in the implementation plan rather than
back-fitted into this spec — they're additions to file content, not
scope changes.
- **Per-bundle docs are `README.md`, not `AGENTS.md`** (revised
2026-05-10, after Phase 1 scaffolding landed). The spec originally
specified one balanced `AGENTS.md` per bundle (§3 template) plus a
Phase 2 seed migration that folded existing READMEs into new
AGENTS.md files (§7). After Phase 1 landed, the maintainer flagged
that the rigid template wouldn't survive contact with the existing
READMEs (which range from one-paragraph balanced docs to operational
scratchpads — see `bundles/{flask,dm-crypt,apt,nextcloud}/README.md`).
Resolution: one `README.md` per bundle, no fixed shape, no template;
Phase 2 dropped; existing READMEs stay in place under leave-as-you-go.
Current convention lives in `bundles/AGENTS.md` "Per-bundle README".
**Sections §3 and §7 are no longer authoritative — read them as
pre-pivot intent only.**
## 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` (with `CLAUDE.md` as a symlink to it).
- Per-area `AGENTS.md` for `bundles/`, `nodes/`, `groups/`, `libs/`, `hooks/`,
`data/`, `items/`, `bin/`. Mechanism-focused; no enumeration of contents.
- Per-bundle `AGENTS.md`: one balanced doc per bundle, replacing existing
bundle `README.md` files. 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's `AGENTS.md`.
- A docstring/header pass on `libs/*.py`, `hooks/*.py`, `bin/*` so each
individual file self-describes.
- Phase 2 seed: per-bundle `AGENTS.md` for 10 bundles selected empirically.
**Out of scope (explicitly).**
- No tooling changes: no `bw` wrapper, no Makefile, no lint, no CI.
- No code refactoring, renaming, or splitting bundles.
- No mass-fill of all 103 bundles' `AGENTS.md` up front. Phase 3 is leave-as-you-go.
- The root `README.md` (personal TODO list) stays untouched.
- Contributing `AGENTS.md` upstream 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:
````markdown
# <bundle-name>
<13 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.py` actually looks,
shows nested structure at a glance. Trailing comment per leaf carries type,
required/default, and a short note.
- `Produces` and `Gotchas` are 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:
1. **What this repo is.** 23 sentences. BundleWrap config-management for
personal/family infra (~22 nodes), Python-defined nodes/groups/bundles,
applies to real machines.
2. **Quickstart for agents.** Five bullets, the operating envelope:
- Default to read-only `bw` commands; never `bw apply`/`bw run`/`bw lock`
without explicit user request. See `docs/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 treat `hooks/` and `items/`
(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`, branch `main`); behavior tracks
upstream main but the fork's `AGENTS.md` is the canonical
bundlewrap-language reference. See `docs/agents/conventions.md` for
install detail.
- Prefer adding helpers to `libs/` over duplicating logic across bundles.
3. **Layout map.** Terse, link-rich. One line per top-level dir, each linking
to that area's `AGENTS.md`.
4. **How nodes/groups/bundles fit together.** 510 lines: nodes pick up bundles
via groups; metadata flows from groups → node → metadata processors;
`nodes.py` and `groups.py` (root) are the loaders that walk the dirs and
run `demagify`.
5. **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.py` pitfalls.
- `docs/agents/conventions.md#secrets` — secrets / demagify magic strings.
- `docs/agents/conventions.md#bundlewrap-version` — install pointer
(editable from the fork's `main`).
- `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`.
6. **Where to look for examples.** Pointers to a small bundle, a complex
bundle, and a node file.
7. **Where this doc lives.** Note that `CLAUDE.md` is 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 3080 lines):
1. What's in this directory (one paragraph).
2. Conventions (naming, file structure, what each kind of file does).
3. How to add / modify (concrete steps for the most common change).
4. Pitfalls (area-specific gotchas).
5. 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 to `libs/`. Links out to
the fork's `AGENTS.md` (item types, dep keywords, metadata reactor
semantics) for language-level detail.
- **`nodes/AGENTS.md`** — `eval()` loading mechanism via `nodes.py`, demagify
magic-string syntax, naming convention pattern (`<location>.<role>.py`).
**Pitfall:** because node files are `eval()`'d, no top-level imports — only
expression-level constructs.
- **`groups/AGENTS.md`** — same `eval()` mechanism via `groups.py`, subdir
purpose convention (`applications/`, `locations/`, `machine/`, `os/`),
how `all.py` interacts, group-membership rules, inheritance order.
- **`libs/AGENTS.md`** — what libs are (importable from bundles via
`repo.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`** — what `data/` 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 a `file` item.
- **`bin/AGENTS.md`** — what `bin/` is for (operator tooling, not invoked by
bundlewrap).
## 6. `docs/agents/` content
### `conventions.md` (~80120 lines)
- **Secrets / demagify.** `!password_for:`, `!decrypt:`, `!decrypt_file:`,
`!32_random_bytes_as_base64_for:`. What each does, where they're allowed
(node files, evaluated through `nodes.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 in `requirements.txt`. The fork's `main` tracks upstream main; the
fork's `AGENTS.md` is 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` (~3050 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.md` for the
full runbook.
- **Apt-key after-change row.** Editing `data/apt/keys/*.{asc,gpg}`
first verify with `gpg --show-keys <newkey>` locally + fingerprint diff
against the expected source. Trial via `bw apply` is 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 nodes` won'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 debug` exploration. Cross-link to `conventions.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).
1. `docs/agents/conventions.md` (Section 6).
2. `docs/agents/commands.md` (Section 6).
3. Per-area `AGENTS.md` for `bundles/`, `nodes/`, `groups/`, `libs/`, `hooks/`,
`data/`, `items/`, `bin/` (Section 5).
4. `bundles/AGENTS.template.md` so future bundle docs have something to copy.
5. Root `AGENTS.md` (Section 4) + `CLAUDE.md` symlink → `AGENTS.md` (written
last so all internal link targets exist).
6. Docstring/header pass: add a one-line module docstring to any `libs/*.py`
and `hooks/*.py` lacking one; `# purpose:` header to any `bin/*` 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: ~8001000 lines of focused writing total now that
bundlewrap-language docs live in the fork. Area docs + conventions land
in 30120 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):**
1. `monitored` (12 node refs) — meta-bundle, often misunderstood.
2. `postgresql` (9 refs, 3 cross-bundle).
3. `wireguard` (8 refs, has own lib + bin script).
4. `routeros-monitoring` (15 commits in 18 months — most-churned bundle).
5. `apt` (6 refs, has own lib).
6. `nginx` (4 refs, web foundational).
**Recently active or complex (4):**
7. `telegraf` (9 cross-bundle refs, 6 recent commits) — monitoring snippets
ripple across bundles.
8. `backup` (7 refs, cross-node coordination).
9. `letsencrypt` (6 refs, cross-cutting).
10. `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 in
`bundles/AGENTS.md`.
- No mass-fill of the remaining ~95 bundles up front — most are simple enough
that `items.py`/`metadata.py` are self-explanatory.
## 8. Future work (not this spec)
- Tooling: a read-only `bw` wrapper or lint that nudges new bundles toward
having an `AGENTS.md`. Worth considering only after Phase 1+2 reveal
which conventions actually drift.
- Pushing the fork's `AGENTS.md` upstream to `bundlewrap/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.md` files 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's
`requirements.txt` pins `@main` so 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), and `bootshorn`
(recent burst target) are honourable mentions if a swap is wanted
later.