vendors ~/.claude/plans/btw-are-you-sure-crystalline-balloon.md into docs/superpowers/plans/2026-05-10-agent-friendliness-plan.md so the plan lives alongside its spec and handoff. tagged with a top-of-file note flagging it as a frozen pre-pivot artifact (the per-bundle-doc section, the AGENTS.template.md reference, and the Phase 2 seed-list all reflect original intent, not what shipped). handoff's pointer updated to the in-repo path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
25 KiB
Implementation plan — agent-friendliness docs
Note (post-execution): This plan is a frozen pre-pivot artifact. It captures the approach as designed before Phase 1 landed and the per-bundle convention pivoted from
AGENTS.mdtoREADME.md. Sections describing per-bundleAGENTS.md,bundles/AGENTS.template.md, and the Phase-2 seed-bundle migration reflect the original intent, not what shipped. For the current shape, read the spec's §0 Revisions (../specs/2026-05-10-agent-friendliness-design.md) and the handoff status note (../handoffs/2026-05-10-implementation-handoff.md). Kept in tree as a record of how the work was scoped and what validation findings (workflow + user-story) shaped Phase 1.
Context
This BundleWrap config repo (ckn-bw, ~22 nodes / 103 bundles) currently has
no agent-facing orientation: no CLAUDE.md / AGENTS.md, only ~10 of 103
bundles have a README, and the root README.md is a personal TODO. Agents
landing cold spelunk to figure out conventions (demagify magic strings,
metadata_reactor patterns, lib helpers, what's safe to run).
Goal: ship one PR that scaffolds the documentation surface described in
docs/superpowers/specs/2026-05-10-agent-friendliness-design.md, plus a
second PR seeding 10 high-value bundle docs. After this work, an agent can
land useful work in nodes / groups / bundles / libs without trial-and-error,
and never invokes a state-mutating bw command without explicit user request.
Corrections since the spec was written.
- Verification on 2026-05-10 found the active venv ran upstream
bundlewrap 4.24.0, the README's fork install was stale, and the user has since upgraded the venv to PyPIbundlewrap 5.0.3. - The user refreshed
/Users/mwiegand/Projekte/bundlewrap-forkto track upstream master at commita97cdb13(also version 5.0.3), and decided to make the fork agent-friendly first (separate session, separate plan), then return here. The ckn-bw plan therefore drops thedocs/agents/bundlewrap/folder it originally planned — bundlewrap-language docs live in the fork now.
This plan reflects the reduced ckn-bw scope. It is gated on the fork's
AGENTS.md existing, since this repo's root AGENTS.md links out to it.
See the handoff document delivered separately for the fork-session work.
Scope
Two PRs.
- PR1 — scaffolding. Root
AGENTS.md,CLAUDE.mdsymlink, thedocs/agents/tree (justconventions.mdandcommands.md— nobundlewrap/folder; that lives in the fork), all eight per-areaAGENTS.mdfiles, the per-bundle template, the docstring/header pass onlibs/*.py/hooks/*.py/bin/*, and the README cleanup (remove the stale fork section, keep everything else). Spec correction in the same PR. - PR2 — seed bundles. Per-bundle
AGENTS.mdfor the 10 seed bundles; fold and remove any existing per-bundleREADME.mdin those.
Phase 3 from the spec ("leave-as-you-go") is a convention, not a code task —
captured in bundles/AGENTS.md as a contribution rule.
Approach (recommended)
PR1 — scaffolding
Order is dependency-respecting; each step's output is referenced by later steps' link targets.
Precondition: the fork's root AGENTS.md exists at
/Users/mwiegand/Projekte/bundlewrap-fork/AGENTS.md. PR1 links out to it,
so PR1 cannot land cleanly until the fork session has produced that file.
-
Spec correction. In
docs/superpowers/specs/2026-05-10-agent-friendliness-design.md:- Replace "fork" framing with the actual reality: venv runs
bundlewrap 5.0.3(PyPI install today), the user maintains a separate fork atgithub.com/CroneKorkN/bundlewrapwhosemastertracks upstream and whoseAGENTS.mdis the canonical bundlewrap-language reference. - Replace the
docs/agents/bundlewrap/folder content (Section 2 IA, Section 6) with a single bullet: "bundlewrap-language reference lives in the fork at<URL>/AGENTS.md; ckn-bw links to it from rootAGENTS.mdandconventions.md." - Update Section 4 quickstart and Section 6
conventions.mdaccordingly. - Single commit before any other ckn-bw docs are written.
- Replace "fork" framing with the actual reality: venv runs
-
docs/agents/conventions.md(~80–120 lines): demagify magic strings; bundlewrap version note (5.0.3, link to the fork'sAGENTS.mdfor language reference); group inheritance order; node/group naming conventions; theeval()idiom innodes.py/groups.pyand what that constrains for editors/agents (no top-level imports, etc.); do-not-touch file list (.secrets.cfg*,.venv,.cache,.bw_debug_history,.envrc). -
docs/agents/commands.md(~30–50 lines, slimmed). The fork'sAGENTS.md(athttps://github.com/CroneKorkN/bundlewrap/blob/main/AGENTS.md) now carries the canonical bw runbook — tiers, after-change table, hash-diff workflow,bw debugsketch — verified against 5.0.3 source. This file shrinks to ckn-bw-specific deltas:- One-line lead pointing at the fork's
AGENTS.mdfor the canonical read-only allowlist + after-change runbook. - Apt-key after-change row (S7 finding): 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 withbw nodes: 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.
- One-line lead pointing at the fork's
-
Per-area
AGENTS.md— eight files, mechanism-focused, no enumeration. Order:bundles/,nodes/,groups/,libs/,hooks/,data/,items/,bin/. Each ~30–80 lines, same five-section shape from spec Section 5. -
bundles/AGENTS.template.md— the per-bundle template (spec Section 3 verbatim, with placeholder text in each section). -
Root
AGENTS.md— written so all link targets exist. Spec Section 4, with the "Conventions you must know" first bullet now pointing at the fork'sAGENTS.md(canonical bundlewrap language) instead of an internalbundlewrap/folder. Quickstart bullet about the fork updated to: "bundlewrap reference lives in<fork-URL>— read first if new to bundlewrap." Then createCLAUDE.mdas a symlink:ln -s AGENTS.md CLAUDE.md. -
Docstring/header pass — for every
libs/*.pyandhooks/*.pywithout a top-of-file module docstring, add a one-liner. For everybin/*script without a header comment, add a# purpose: …line. Do not touch files that already have one. Mechanical: read each file's first ~10 lines, decide, edit only if missing. -
Root
README.mdcleanup — remove the stale "install bw fork" section (thepip3 install --editable git+file:///…/bundlewrap-fork…block) since the venv runs PyPI 5.0.3. Leave the rest of the README untouched.
PR2 — seed bundles
For each of the 10 seed bundles, write bundles/<name>/AGENTS.md from
bundles/AGENTS.template.md. Where a bundle already has README.md, fold
its content into the new AGENTS.md and remove the old README.md in the
same commit.
Seed list (from spec Section 7, validated empirically 2026-05-10):
| # | Bundle | Existing README? | Notes |
|---|---|---|---|
| 1 | monitored |
check | meta-bundle, often misunderstood |
| 2 | postgresql |
check | foundational |
| 3 | wireguard |
check | own lib + bin script |
| 4 | routeros-monitoring |
no | most-churned bundle (15 commits / 18mo); replaces php per user-story rebalance |
| 5 | apt |
check | own lib |
| 6 | nginx |
check | web foundational |
| 7 | telegraf |
check | high cross-bundle ripple |
| 8 | backup |
check | cross-node coordination |
| 9 | letsencrypt |
check | cross-cutting |
| 10 | nextcloud |
yes (verified) | complex, recent activity |
For each: derive Metadata dict by reading the bundle's metadata.py and
running bw metadata <one-node-with-the-bundle> to confirm the resolved
shape. Derive Produces from items.py. Derive Depends on by checking
which other bundles' artifacts (apt packages, systemd services) the
bundle's reactors and items reference. Use the ccc index (built 2026-05-10)
for fast cross-bundle lookups when filling Depends on.
Workflow validation findings (2026-05-10) — content additions
Traced the "implement a new bundle" workflow against the planned docs.
Natural agent path is root → bundles/AGENTS.md → example bundle → write
files → wire to a node → verify. Seven gaps found; six are one-line
additions to bundles/AGENTS.md, one is a rewrite of root §6's example
pointers (~8 lines). All low-cost; fold into the corresponding files when
written in PR1.
-
bundles/AGENTS.md— "Before you start" header.conventions.mdis off the natural path; call it out as required reading at the top, not just in "see also." This repo's idioms (vault calls in reactors,repo.libs.hashable.hashable(...), demagify) live there. An agent who skips it will write subtly wrong code (e.g. dict-in-setTypeError, vault calls in the wrong place). -
Root
AGENTS.md§6 — use-case keyed example pointers. Replace the spec's "small bundle / complex bundle / node file" with one-line pointers per pattern: vault usage, templated files, cross-bundle reactor writing,downloadcustom item. Pick concrete bundles at write time (grep for the patterns to find good exemplars). ~8 lines. -
bundles/AGENTS.md"How to add" — explicit wiring step. Add a numbered step: "(4) Wire to nodes — seegroups/AGENTS.mdfor application-style group wiring ornodes/AGENTS.mdfor direct attachment via a node'sbundleslist." Currently the wiring step is implicit; agent has to discover by cross-reading two area docs. -
bundles/AGENTS.mdConventions — bundle naming. One line: bundle directory names are lowercase with hyphens (e.g.backup-server,bind-acme,dm-crypt); avoid underscores. Verify the convention byls bundles | grep _before writing — if the convention is mixed, document the actual rule. -
bundles/AGENTS.md"See also" — items and templates. Cross-link toitems/AGENTS.md(for thedownloadcustom item and how to write new custom item types) and to the fork'sdocs/content/guide/item_file_templates.md(template syntax). Both are common needs an agent writing a new bundle hits early. -
bundles/AGENTS.md— first-thing-to-run after writing. One-liner pointing atcommands.mdwith the canonical sequence:bw test(sanity) →bw items <node>(do items show up?) →bw hash <node>(changed as expected?). Saves the agent from hunting the runbook. -
bundles/AGENTS.template.md— empty-section guidance. Add a note at the top of the template: "For a brand-new bundle without consumers yet, leaveDepends onandProducesempty or marked TBD; fill in after the first verify run."
User-story validation findings (2026-05-10) — additional content adds
Empirical user-story extraction from 1169 commits (full history, with
detailed analysis of the last 222 commits / 18 months) is at
docs/superpowers/specs/2026-05-10-user-stories-from-history.md. It
identified 21 recurring user stories. Coverage assessment vs the planned
docs: 5 ✅ / 13 ⚠ / 3 ❌. Below are the 16 additional content adds, grouped
by target file. Each is one paragraph or less. The ❌ items shape an
agent's judgment (highest value); the ⚠ items shape lookups.
Root AGENTS.md
- F9 — Personal TODO callout. "Note:
README.mdis the maintainer's personal scratchpad, not project documentation. Onboarding lives here inAGENTS.md." One sentence in §1 ("What this repo is").
docs/agents/conventions.md
- S4 ❌ — Iterative-commit workflow style. "User commits are
iterative checkpoints, not landing-ready snapshots. Terse messages
(
+,fix,whitespace,dowsnt exist) and successive 'fix' commits on the same file are normal. Don't rebase WIP without asking. As an agent, prefer to land complete-feeling commits rather than mimic the iterative style." - S5 ❌ — Burst-state awareness. "Before writing into a subsystem,
check
git log --since='1 month ago' bundles/<x>(ornodes/<x>.py, etc.). If it shows ≥10 recent commits, the subsystem is in flux and your assumptions about its metadata shape may already be stale. Read the most recent diffs first." - S11 ❌ — Suspension idiom ("for now / disable / dummy / offline").
"Commits with these markers indicate deliberate suspension, not bugs.
If you encounter a stub or commented-out block, check
git log -- <file>for the suspension reason before re-enabling. The user reverses these manually when ready." - F6 —
_old/_old2soft-delete pattern. "Suffixed-with-_olddirectories are the user's recovery buffer during big refactors. Don't delete them without asking, even if they look orphaned." - F8 — Branch naming for PRs. "PRs go through self-hosted
Gitea/Forgejo. Branch names are lowercase-snake_case descriptive
(
debian-13,htz.mails_debian_13_squash,l4d2_the_next)." - S20 — Bundlewrap version-migration recipe (optional). "When the
next major bw version lands: read upstream migration guide → grep for
affected reactor patterns → rewrite each → bump
requirements.txtlast." Captures the pattern from186d503(bw 4 → 5). Useful given the maintainer's tool-design pivot.
docs/agents/commands.md
- S7 — Apt-keys verification. Add a row to the after-change table:
data/apt/keys/*.{asc,gpg}→ first check isgpg --show-keys <newkey>locally + visual diff against expected fingerprint. Trial viabw applyis the failure path (a wrong key blocks unattended upgrades cluster-wide). S21 —Resolved upstream: the fork'sbw debugcontent sketch.AGENTS.mdnow carries the canonicalbw debugcontent sketch (probes forrepo.get_node(...).metadata,repo.libs.<x>,repo.path). No ckn-bw addition needed — Approach step 3 already points at the fork.
bundles/AGENTS.md
- S3 — Template recognition. One paragraph: "Files under
bundles/<x>/files/are static unless thefile:item declarescontent_type='mako'or the file extension triggers templating (see fork'sdocs/content/guide/item_file_templates.md). To check: read the matchingfile:entry initems.py." - S13 — How to remove a bundle. 5-line section, symmetric to "How
to add": "(1)
git grep '<name>'to find references in nodes/groups/ other bundles; (2) remove those references; (3)rm -rf bundles/<x>/; (4)bw testandbw nodesto confirm clean." - S18 — README transition state. "If a bundle has both
README.mdandAGENTS.md,AGENTS.mdis canonical; the README is being phased out. ~23 bundle READMEs remain after the seed PR — Phase 3 leave- as-you-go folds them in over time."
bundles/AGENTS.template.md
- S12 — Optional
## Writes intosection. Add to template (after## Depends on): "List other namespaces this bundle'sdefaultsor reactors write into (e.g. nextcloud writes intoapt.packagesandarchive.paths). Skip section if none — most bundles don't write cross-namespace, but the ones that do create the highest-blast- radius surprises."
nodes/AGENTS.md
- S2 — Silent eval-load-failure pitfall. "Node files are
eval()'d. A syntax error or top-levelimportcauses the loader to silently drop the node. Ifbw nodesreports fewer nodes than expected, checkgroups.py(the user added explicit error printing in commitdc40295after being bitten)." - S9 —
*.py_suspend convention. "Appending_to a node filename (e.g.htz.l4d2.py_) parks it without loading. Used to keep decommissioned-but-not-deleted node configs in tree." - S9 — Symmetric "How to add a node" workflow. Numbered steps
parallel to
bundles/AGENTS.md"How to add": (1) createnodes/<location>.<role>.pywitheval()-safe expression syntax, (2) populateid,hostname,groups,bundles,metadata, (3) add to relevantgroups/<area>/<x>.pyif group membership is the attachment point, (4) verify withbw nodes,bw nodes <node> -a groups,bw metadata <node>. - S19 — Node-rename failure mode. "Renaming a node file renames
the node. Vault entries (via
!password_for:<node>),bw hashrecords, and ssh known_hosts associations all key on node name — search and replace before renaming, or vault lookups silently return new (wrong) values."
groups/AGENTS.md
- S10 — Family-file pattern. "OS variants commonly share a
*-common.pyparent (e.g.debian-13-common.pyshared bydebian-13.pyanddebian-13-pve.py). Use this when introducing related-but-distinct OS group families." - S10/S15 — New-OS-variant recipe. "To introduce a new OS major:
(1) add
groups/os/debian-N.py+debian-N-common.pyparallel to the existing files (don't edit in place); (2) adddata/apt/keys/debian-N-*.{asc,gpg}; (3) bump dependent bundles that branch on OS string (e.g.bundles/bind/items.py); (4) bump affected nodes'groupslists one at a time; (5) delete the old OS file when no node references it."
data/AGENTS.md
- S7 — Two distinct content models. "
data/apt/keys/holds binary GPG keys consumed bybundles/apt;data/grafana/rows/holds Python modules for Mako-templated dashboards. Same directory shape, different content models — when adding a new data subdir, declare which model it follows." - F4 —
data/vsbundles/<x>/files/heuristic. "If a data asset is read by exactly one bundle, preferbundles/<x>/files/. Usedata/for shared/multi-consumer artifacts. Single-instance evidence:78a8abcmovedmikrotik.mibfrom data/ into the bundle for this reason."
hooks/AGENTS.md
- S17 — Broken-hook failure mode. "A hook that errors at load time
breaks every
bwcommand that fires that lifecycle (includingbw test, defeating the obvious diagnostic). Test new hooks in isolation first:bw debugthenimport sys; sys.path.insert(0, 'hooks'); import <hookmodule>. Iterate there until the import is clean."
libs/AGENTS.md
- S16 — Find-consumers snippet. Add as a one-liner under
Pitfalls: "Before changing a lib's API, find consumers:
git grep -l 'repo.libs.<x>'. Lib changes have repo-wide blast radius — every bundle that imports the lib re-evaluates."
bin/AGENTS.md
- S14 —
bin/script_template. "When introducing a new operator script, start frombin/script_template(the user maintains it as the canonical starter)."
Phase 2 seed list rebalance
User-story analysis (story 5, subsystem-burst evidence) found that 3 of 5 recent burst targets are unseeded. Strongest single swap:
- Drop
php(8 node refs but ~zero recent commits — usage hub that doesn't change). Addrouteros-monitoring(15 recent commits, the most-touched bundle in 18 months; not a dependency hub but high churn).
Optional second swap (judgment call):
- Drop
apt(6 refs, has own lib, foundational but stable) and addbootshorn(recent burst target, has its own subsystem). Or keepaptfor usage-hub coverage and accept that the seed misses bootshorn — Phase 3 covers it lazily.
Default this plan to the single swap (php → routeros-monitoring);
note apt → bootshorn as a second-swap option if the user wants it.
Critical files
New:
AGENTS.md(root)CLAUDE.md(symlink →AGENTS.md)docs/agents/conventions.mddocs/agents/commands.mdbundles/AGENTS.md,bundles/AGENTS.template.mdnodes/AGENTS.md,groups/AGENTS.md,libs/AGENTS.md,hooks/AGENTS.md,data/AGENTS.md,items/AGENTS.md,bin/AGENTS.mdbundles/{monitored,postgresql,wireguard,routeros-monitoring,apt,nginx,telegraf,backup,letsencrypt,nextcloud}/AGENTS.md
Modified:
docs/superpowers/specs/2026-05-10-agent-friendliness-design.md(fork → upstream correction).README.md(remove stale "install bw fork" section).libs/*.py,hooks/*.py(add module docstrings where missing).bin/*(add# purpose:header where missing).
Deleted:
bundles/<name>/README.mdfor each of the ~10 seed bundles that have one (content folded intoAGENTS.md).
Existing utilities & assets to reuse
- Spec at
docs/superpowers/specs/2026-05-10-agent-friendliness-design.md— the source of truth for what each file contains. Sections 3, 4, 5, 6 of the spec are nearly copyable into the actual files. - User-story validation doc at
docs/superpowers/specs/2026-05-10-user-stories-from-history.md— 21 stories grounded in git history with concrete commit evidence. When writing per-bundle docs, look up the relevant story for evidence of typical changes (e.g. fornextcloud, see Story 1 + Story 5 l4d2-style burst comparison). The "Implications for agent docs" paragraphs in each story map directly to the additions in §"User- story validation findings" above. - ccc index built at
.cocoindex_code/on 2026-05-10 (768 chunks, 340 files) — useful in PR2 for finding bundles that consume a metadata key or import a particular lib. - Bundlewrap CLI for verification:
bw metadata <node>,bw items <node>,bw test,bw hash. Read-only; safe to run during writing. - Existing bundle READMEs at
bundles/{freescout,influxdb2,dm-crypt,gcloud,flask,nextcloud,build-server,raspberrymatic-cert,letsencrypt,nodejs}/README.md(and any others — verify withfind bundles -name README.md) — content to fold into the matchingAGENTS.md. These are the only existing human-prose source for those bundles; do not lose information when migrating.
Verification
Run after PR1:
bw test— repo-level sanity (passes today; should still pass).bw nodes,bw groups,bw bundles— sanity that loaders work after the docstring/header additions.- Check every internal link in
AGENTS.mdanddocs/agents/**.mdresolves to a real file. A tiny shell loop withgrep -oE '\]\([^)]+\.md[^)]*\)'thentest -feach path. readlink CLAUDE.mdresolves toAGENTS.md.grep -L '"""' libs/*.py hooks/*.pyreports zero files (every lib/hook has a module docstring).grep -L '^# purpose' bin/*reports zero non-binary scripts.git grep -i "bw fork\|bundlewrap-fork"reports only the corrected docs locations (no leftover fork references inREADME.md).- Workflow walk-through. Trace the "implement a new bundle" path
end-to-end against the written docs: root →
bundles/AGENTS.md→ example pointer → can the writer locate vault/hashable/template guidance from there without spelunking? Confirms the §"Workflow validation findings" fixes actually closed the gaps.
Run after PR2:
bw test— still green.- For each seed bundle
<x>: pick one node that has it, runbw metadata <node>and confirm the keys listed inbundles/<x>/AGENTS.mdMetadatasection actually appear in the resolved metadata. Catches drift between docs and reality. find bundles -name README.md— confirm none exist for the 10 seed bundles (folded intoAGENTS.md).- Each new
bundles/<x>/AGENTS.mdfollows the template structure (grep -L '^## Metadata' bundles/*/AGENTS.mdreports zero).
Non-goals (re-asserted from spec)
- No tooling changes (no
bwwrapper, no Makefile, no lint, no CI). - No code refactoring, renaming, or splitting bundles.
- No mass-fill of the remaining ~93 bundles up front.
- No upstream contribution to bundlewrap (acknowledged future work).
- The root
README.mdkeeps everything except the fork section.
Risks & mitigations
- Drift between docs and code. Mitigation: per-bundle docs are short (low maintenance), the area docs are mechanism-focused (changes less often than enumerations), and PR2 verification step 2 catches metadata drift at write time.
- PR1 is gated on the fork's
AGENTS.mdexisting. If you want to start ckn-bw work before the fork session lands, you can stub the link to the fork'sAGENTS.md(e.g. point at the repo URL even if the file isn't there yet) and merge PR1, but that risks broken links if the fork URL or filename changes. Cleaner: do the fork session first, then PR1. - PR1 is now a moderate writing chunk (~800–1000 lines instead of ~1500–2000), since the bundlewrap folder moved out. Single PR is comfortable. The user-story validation findings (16 small adds) push it to ~1000–1200; still manageable as one PR.
- README undercount. The plan/spec estimated ~10 existing bundle
READMEs to fold; actual is 33 (verified 2026-05-10 from git
history). The seed list intersects with only 4–5 of those (nextcloud
has one verified; others —
find bundles -name README.mdwill enumerate). PR2 only folds READMEs in seed bundles; ~28 remain untouched, addressed lazily by Phase 3.bundles/AGENTS.mdnotes this transition state explicitly (per user-story finding §S18) so agents reading both don't get confused.