bundlewrap/docs/superpowers/specs/2026-05-10-agent-friendliness-design.md
CroneKorkN c6bf2e0fc8
spec: banner stale sections so partial readers see the pivot
§0 Revisions notes that §3 and §7 Phase 2 are pre-pivot, but a reader
deep-linking into either section bypasses §0. Add a section-level
banner at the top of each that points back to §0 and to bundles/AGENTS.md
for the current per-bundle convention. Content is preserved as a record
of the original design.

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

19 KiB
Raw Blame History

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.mddocs/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

Status: replaced — pre-pivot intent only. Per-bundle docs are plain README.md with no fixed structure. See §0 Revisions and the "Per-bundle README" section in bundles/AGENTS.md for the current convention. The template below is kept as a record of the original design.

One balanced doc serving both audiences. Prose where prose helps, structure where structure helps. Sections in order:

# <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.mdeval() 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)

Status: dropped — pre-pivot intent only. Phase 2 didn't ship. After Phase 1 landed, the maintainer pulled the per-bundle AGENTS.md migration: the rigid template proved a poor fit for the heterogeneous existing READMEs. See §0 Revisions. The seed list and migration plan below are kept as a record of how the work was scoped.

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):

  1. telegraf (9 cross-bundle refs, 6 recent commits) — monitoring snippets ripple across bundles.
  2. backup (7 refs, cross-node coordination).
  3. letsencrypt (6 refs, cross-cutting).
  4. 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.