bundlewrap/docs/superpowers/plans/2026-05-10-agent-friendliness-plan.md
CroneKorkN d4dedde0ad
add implementation plan to repo
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>
2026-05-10 16:07:35 +02:00

25 KiB
Raw Blame History

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.md to README.md. Sections describing per-bundle AGENTS.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 PyPI bundlewrap 5.0.3.
  • The user refreshed /Users/mwiegand/Projekte/bundlewrap-fork to track upstream master at commit a97cdb13 (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 the docs/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.md symlink, the docs/agents/ tree (just conventions.md and commands.md — no bundlewrap/ folder; that lives in the fork), all eight per-area AGENTS.md files, the per-bundle template, the docstring/header pass on libs/*.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.md for the 10 seed bundles; fold and remove any existing per-bundle README.md in 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.

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.

  1. 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 at github.com/CroneKorkN/bundlewrap whose master tracks upstream and whose AGENTS.md is 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 root AGENTS.md and conventions.md."
    • Update Section 4 quickstart and Section 6 conventions.md accordingly.
    • Single commit before any other ckn-bw docs are written.
  2. docs/agents/conventions.md (~80120 lines): demagify magic strings; bundlewrap version note (5.0.3, link to the fork's AGENTS.md for language reference); group inheritance order; node/group naming conventions; the eval() idiom in nodes.py / groups.py and what that constrains for editors/agents (no top-level imports, etc.); do-not-touch file list (.secrets.cfg*, .venv, .cache, .bw_debug_history, .envrc).

  3. docs/agents/commands.md (~3050 lines, slimmed). The fork's AGENTS.md (at https://github.com/CroneKorkN/bundlewrap/blob/main/AGENTS.md) now carries the canonical bw runbook — tiers, after-change table, hash-diff workflow, bw debug sketch — verified against 5.0.3 source. This file shrinks to ckn-bw-specific deltas:

    • One-line lead pointing at the fork's AGENTS.md for the canonical read-only allowlist + after-change runbook.
    • Apt-key after-change row (S7 finding): 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 with bw nodes: 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.
  4. Per-area AGENTS.md — eight files, mechanism-focused, no enumeration. Order: bundles/, nodes/, groups/, libs/, hooks/, data/, items/, bin/. Each ~3080 lines, same five-section shape from spec Section 5.

  5. bundles/AGENTS.template.md — the per-bundle template (spec Section 3 verbatim, with placeholder text in each section).

  6. 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's AGENTS.md (canonical bundlewrap language) instead of an internal bundlewrap/ folder. Quickstart bullet about the fork updated to: "bundlewrap reference lives in <fork-URL> — read first if new to bundlewrap." Then create CLAUDE.md as a symlink: ln -s AGENTS.md CLAUDE.md.

  7. Docstring/header pass — for every libs/*.py and hooks/*.py without a top-of-file module docstring, add a one-liner. For every bin/* 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.

  8. Root README.md cleanup — remove the stale "install bw fork" section (the pip3 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.

  1. bundles/AGENTS.md — "Before you start" header. conventions.md is 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-set TypeError, vault calls in the wrong place).

  2. 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, download custom item. Pick concrete bundles at write time (grep for the patterns to find good exemplars). ~8 lines.

  3. bundles/AGENTS.md "How to add" — explicit wiring step. Add a numbered step: "(4) Wire to nodes — see groups/AGENTS.md for application-style group wiring or nodes/AGENTS.md for direct attachment via a node's bundles list." Currently the wiring step is implicit; agent has to discover by cross-reading two area docs.

  4. bundles/AGENTS.md Conventions — bundle naming. One line: bundle directory names are lowercase with hyphens (e.g. backup-server, bind-acme, dm-crypt); avoid underscores. Verify the convention by ls bundles | grep _ before writing — if the convention is mixed, document the actual rule.

  5. bundles/AGENTS.md "See also" — items and templates. Cross-link to items/AGENTS.md (for the download custom item and how to write new custom item types) and to the fork's docs/content/guide/item_file_templates.md (template syntax). Both are common needs an agent writing a new bundle hits early.

  6. bundles/AGENTS.md — first-thing-to-run after writing. One-liner pointing at commands.md with 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.

  7. bundles/AGENTS.template.md — empty-section guidance. Add a note at the top of the template: "For a brand-new bundle without consumers yet, leave Depends on and Produces empty 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.md is the maintainer's personal scratchpad, not project documentation. Onboarding lives here in AGENTS.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> (or nodes/<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 / _old2 soft-delete pattern. "Suffixed-with-_old directories 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.txt last." Captures the pattern from 186d503 (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 is gpg --show-keys <newkey> locally + visual diff against expected fingerprint. Trial via bw apply is the failure path (a wrong key blocks unattended upgrades cluster-wide).
  • S21 — bw debug content sketch. Resolved upstream: the fork's AGENTS.md now carries the canonical bw debug content sketch (probes for repo.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 the file: item declares content_type='mako' or the file extension triggers templating (see fork's docs/content/guide/item_file_templates.md). To check: read the matching file: entry in items.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 test and bw nodes to confirm clean."
  • S18 — README transition state. "If a bundle has both README.md and AGENTS.md, AGENTS.md is 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 into section. Add to template (after ## Depends on): "List other namespaces this bundle's defaults or reactors write into (e.g. nextcloud writes into apt.packages and archive.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-level import causes the loader to silently drop the node. If bw nodes reports fewer nodes than expected, check groups.py (the user added explicit error printing in commit dc40295 after 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) create nodes/<location>.<role>.py with eval()-safe expression syntax, (2) populate id, hostname, groups, bundles, metadata, (3) add to relevant groups/<area>/<x>.py if group membership is the attachment point, (4) verify with bw 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 hash records, 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.py parent (e.g. debian-13-common.py shared by debian-13.py and debian-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.py parallel to the existing files (don't edit in place); (2) add data/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' groups lists 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 by bundles/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/ vs bundles/<x>/files/ heuristic. "If a data asset is read by exactly one bundle, prefer bundles/<x>/files/. Use data/ for shared/multi-consumer artifacts. Single-instance evidence: 78a8abc moved mikrotik.mib from data/ into the bundle for this reason."

hooks/AGENTS.md

  • S17 — Broken-hook failure mode. "A hook that errors at load time breaks every bw command that fires that lifecycle (including bw test, defeating the obvious diagnostic). Test new hooks in isolation first: bw debug then import 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 from bin/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). Add routeros-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 add bootshorn (recent burst target, has its own subsystem). Or keep apt for 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.md
  • docs/agents/commands.md
  • bundles/AGENTS.md, bundles/AGENTS.template.md
  • nodes/AGENTS.md, groups/AGENTS.md, libs/AGENTS.md, hooks/AGENTS.md, data/AGENTS.md, items/AGENTS.md, bin/AGENTS.md
  • bundles/{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.md for each of the ~10 seed bundles that have one (content folded into AGENTS.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. for nextcloud, 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 with find bundles -name README.md) — content to fold into the matching AGENTS.md. These are the only existing human-prose source for those bundles; do not lose information when migrating.

Verification

Run after PR1:

  1. bw test — repo-level sanity (passes today; should still pass).
  2. bw nodes, bw groups, bw bundles — sanity that loaders work after the docstring/header additions.
  3. Check every internal link in AGENTS.md and docs/agents/**.md resolves to a real file. A tiny shell loop with grep -oE '\]\([^)]+\.md[^)]*\)' then test -f each path.
  4. readlink CLAUDE.md resolves to AGENTS.md.
  5. grep -L '"""' libs/*.py hooks/*.py reports zero files (every lib/hook has a module docstring).
  6. grep -L '^# purpose' bin/* reports zero non-binary scripts.
  7. git grep -i "bw fork\|bundlewrap-fork" reports only the corrected docs locations (no leftover fork references in README.md).
  8. 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:

  1. bw test — still green.
  2. For each seed bundle <x>: pick one node that has it, run bw metadata <node> and confirm the keys listed in bundles/<x>/AGENTS.md Metadata section actually appear in the resolved metadata. Catches drift between docs and reality.
  3. find bundles -name README.md — confirm none exist for the 10 seed bundles (folded into AGENTS.md).
  4. Each new bundles/<x>/AGENTS.md follows the template structure (grep -L '^## Metadata' bundles/*/AGENTS.md reports zero).

Non-goals (re-asserted from spec)

  • No tooling changes (no bw wrapper, 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.md keeps 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.md existing. If you want to start ckn-bw work before the fork session lands, you can stub the link to the fork's AGENTS.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 (~8001000 lines instead of ~15002000), since the bundlewrap folder moved out. Single PR is comfortable. The user-story validation findings (16 small adds) push it to ~10001200; 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 45 of those (nextcloud has one verified; others — find bundles -name README.md will enumerate). PR2 only folds READMEs in seed bundles; ~28 remain untouched, addressed lazily by Phase 3. bundles/AGENTS.md notes this transition state explicitly (per user-story finding §S18) so agents reading both don't get confused.