bundlewrap/docs/superpowers/handoffs/2026-05-10-implementation-handoff.md
CroneKorkN 136313e9c3
add implementation handoff for the next session
Self-contained handover covering: Phase 0 commits already landed,
Phase 0 remainder (docstring/header pass), Phase 1 scaffolding order,
Phase 2 seed bundle list, captured decisions, pitfalls (bw-syntax
corrections, /etc/hosts macOS quirk, sandbox), and verification
criteria. Cross-references the spec, the user-stories validation, the
plan, and the fork's AGENTS.md.

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

389 lines
17 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.

# Handoff — implement agent-friendly docs for ckn-bw
Date written: 2026-05-10. Working directory for the next session:
`/Users/mwiegand/Projekte/ckn-bw`.
This handoff is self-contained. Read top-to-bottom. Source documents
referenced are tracked in this repo unless noted.
## Why this work exists
This BundleWrap config repo (`ckn-bw`, ~22 nodes / 103 bundles) had no
agent-facing orientation: no `CLAUDE.md` / `AGENTS.md`, only ~33 of 103
bundles have a `README`, and the root `README.md` is the maintainer's
personal TODO list. Agents landing cold spelunked through magic-string
secrets, eval()-loaded node files, vault helpers, lib helpers, and a custom
bundlewrap fork.
The work over the last few sessions was: brainstorm → spec → plan → fork
work (separate session) → user-story validation → Phase 0 prep → handoff
(this file). The next session implements Phase 1 (PR1 scaffolding) and
Phase 2 (seed bundle docs).
The maintainer is also using this work as design exploration for a future
agent-friendly config-management tool. Concrete bundlewrap-language
gotchas surfaced here (silent eval-load, magic strings, reactor
namespace-write side effects) inform that future tool's design.
## Where we are right now (commit checkpoint)
`git log --oneline` (4 commits ahead of origin/master, none pushed):
```
1da7097 README: drop stale 'install bw fork' instruction
3daf70d spec: incorporate fork pivot and bw-syntax corrections
b804350 add user-stories validation doc
7486c78 switch bundlewrap install to editable from CroneKorkN/bundlewrap@main
8ec99db add agent-friendliness design spec ← prior session
```
Working tree at handoff:
```
?? .claude/ # local Claude settings, leave alone
?? bundles/left4dead2/files/scripts/overlays/test # pre-existing, unrelated, leave alone
```
The venv runs **bundlewrap 5.0.3 editable** from
`https://github.com/CroneKorkN/bundlewrap.git@main`, cloned at
`.venv/src/bundlewrap/`. `bw test` passes through the (full-network)
sandbox.
## Source documents (read in this order)
1. **Spec**`docs/superpowers/specs/2026-05-10-agent-friendliness-design.md`.
Section §0 documents revisions; the rest describes target file shapes
in detail. **Treat as source of truth for what each file must contain.**
2. **User-stories validation**
`docs/superpowers/specs/2026-05-10-user-stories-from-history.md`.
21 stories grounded in 1169 commits of git history. Each story's
"Implications for agent docs" section maps to specific content adds
that the plan (below) records as "User-story validation findings."
3. **Implementation plan**
`~/.claude/plans/btw-are-you-sure-crystalline-balloon.md`. Contains the
Approach (numbered steps), Workflow validation findings (7 content
adds), User-story validation findings (16 content adds), seed list,
and verification. **Read it.** Lives outside the repo because it was
produced under Claude's plan-mode workflow.
4. **Fork's `AGENTS.md`** — at
`https://github.com/CroneKorkN/bundlewrap/blob/main/AGENTS.md`. The
canonical bundlewrap-language reference: safety envelope (3 tiers),
after-change runbook, hash-diff workflow, dep-keyword cheat-sheet,
metadata.py pitfalls. ckn-bw's docs link out to it instead of
duplicating.
## Phase 0 — already done (don't redo)
`requirements.txt` swapped from `bundlewrap ~=5.0, >=5.0.3` to the
editable github reference. Don't change back.
✓ User-stories doc added at `docs/superpowers/specs/2026-05-10-user-stories-from-history.md`.
✓ Spec corrected at `docs/superpowers/specs/2026-05-10-agent-friendliness-design.md`:
fork pivot, slim `commands.md` scope, bw-syntax corrections, seed-list
rebalance, and a new `§0. Revisions` log. Section numbering is unchanged
otherwise.
✓ Root `README.md` cleaned: stale "install bw fork" section removed.
Personal TODO above and the `# monitor timers` / `# git signing` blocks
below are untouched.
## Phase 0 — what's left
**Docstring/header pass on `libs/`, `hooks/`, `bin/`** — partially started
in this session, then reverted to a clean state at user request to land
this handoff. The next session does the whole pass cleanly.
Spec §5 corollary defines the rule: "Every `libs/*.py` and `hooks/*.py`
starts with a one-line module docstring. Every `bin/*` script starts with
a `# purpose:` header comment."
Files that need a docstring/header (verified absent at handoff time):
```
libs/ — 19 files: apt, bind, derive_string, grafana, hashable, hmac,
ini, ip, local, nextcloud, nginx, postgres, rsa, ssh, systemd,
tools, version, wireguard, wol (all .py)
hooks/ — 5 files: known_hosts, skip_local_nodes, test_ptr_records,
unique_node_ids, wake_on_lan (all .py)
bin/ — 9 scripts: mikrotik-firmware-updater, passwords-for, rcon,
script_template, sync_1password, timestamp_icloud_photos_for_nextcloud,
upgrade_and_restart_all, wake, wireguard-client-config
```
For each: read the file, write a concise one-line description capturing
**what it's for**, not how it works. Place the docstring at the very top
of the file (after the shebang, if present), followed by a blank line,
then the existing first line. Example shapes:
```python
# libs/wireguard.py
"""wireguard: deterministic WireGuard private/public key + PSK derivation backed by repo.vault."""
import base64
...
```
```bash
# bin/wake
#!/usr/bin/env python3
# purpose: wake one node via WoL by name — usage: wake <node>.
from bundlewrap.repo import Repository
...
```
For files with `#!/usr/bin/env python` shebang in `libs/` (only
`derive_string.py` currently), put the docstring on the line immediately
after the shebang.
For files with a leading comment in `libs/` (only `rsa.py` has the
`# https://stackoverflow.com/a/18266970` link), place the docstring above
that comment so it's still the first statement.
Commit message style for this pass: `libs/hooks/bin: add one-line module
docstrings and # purpose: headers`. Single commit for the whole pass.
Use the file headers / function names / surrounding code to derive the
descriptions; don't trust git log alone — terse commit messages won't
explain a lib's purpose. The user-stories doc gives indirect signal for
some (`libs/version.py` was introduced for `bin/mikrotik-firmware-updater`,
etc.).
## Phase 1 — main scaffolding (PR1)
The core writing work, ~8001000 lines total per the plan's scope estimate.
### Order
Per plan Approach:
1. `docs/agents/conventions.md`
2. `docs/agents/commands.md`
3. The 8 per-area `AGENTS.md` files (`bundles/`, `nodes/`, `groups/`,
`libs/`, `hooks/`, `data/`, `items/`, `bin/`)
4. `bundles/AGENTS.template.md`
5. Root `AGENTS.md` (last so all link targets exist)
6. `ln -s AGENTS.md CLAUDE.md` (the symlink)
Each file's exact target content is in the spec (see paths above).
Augmentations beyond the spec are recorded in the plan as
"Workflow validation findings" (7 items) and "User-story validation
findings" (16 items). Examples of augmentations:
- `bundles/AGENTS.md` gets a "Before you start" header pointing at
`conventions.md` as required reading (workflow validation §1).
- `nodes/AGENTS.md` gets the silent-eval-load pitfall (user-story §S2),
the `*.py_` suspend convention (§S9), and the rename-failure-mode
pitfall (§S19).
- `conventions.md` gets the suspension idiom ("for now / disable / dummy"
— §S11), the iterative-commit context (§S4), the burst-state awareness
(§S5), and the `_old` soft-delete pattern (§F6).
- Per-bundle template gets an optional `## Writes into` section for
bundles whose reactors write cross-namespace (§S12).
**Read both validation-findings sections in the plan in full before
starting Phase 1.** They're scoped, file-keyed, and small individually,
but cumulatively they're the difference between "docs that look right" and
"docs that an agent can actually use."
### Style guardrails
- One balanced doc per audience boundary. The per-bundle `AGENTS.md`
template (spec §3) is the canonical shape: prose at top, structured
Python dict for metadata schema, optional sections only when useful.
- Mechanism over enumeration. Area docs say *how the area works*, not
*what's currently in it*. Specifics live in self-describing files
(per the docstring rule above).
- No editorializing. Don't write advice; write conventions.
- No comments unless they explain a non-obvious *why*. Same for prose:
short, factual.
- Use the path conventions the spec uses (`docs/agents/...`, not
`docs/agents/...md` repeatedly). Use full URLs to the fork
(`https://github.com/CroneKorkN/bundlewrap/blob/main/AGENTS.md`) so
links work in any rendered context.
### Verification before committing
Run all of these (some need network sandbox; sandbox is configured for
full-network access — see "Operating environment" below):
1. `bw test` — must pass (exit 0).
2. `bw nodes`, `bw groups`, `bw bundles` — repo loaders still work.
3. Internal-link integrity: `grep -oE '\]\([^)]+\.md[^)]*\)' AGENTS.md
docs/agents/**/*.md bundles/AGENTS*.md` then `test -f` each path.
4. `readlink CLAUDE.md` resolves to `AGENTS.md`.
5. `grep -L '"""' libs/*.py hooks/*.py` reports zero files.
6. `grep -L '^# purpose' bin/*` reports zero non-binary scripts.
7. `git grep -i "bw fork\|bundlewrap-fork"` reports only legitimate
fork-as-source references (no leftover stale install instructions).
8. **Workflow walk-through.** Trace the "implement a new bundle" path
end-to-end against the written docs as if you were a fresh agent.
Confirms the workflow-validation fixes actually closed the gaps.
## Phase 2 — seed bundle docs (PR2)
Ten bundles, each gets a `bundles/<x>/AGENTS.md` from the template.
| # | Bundle | Existing README? | Notes |
|---|---|---|---|
| 1 | `monitored` | check via `find bundles/<x> -name README.md` | 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) |
| 5 | `apt` | check | own lib |
| 6 | `nginx` | check | web foundational |
| 7 | `telegraf` | check | high cross-bundle ripple (writes into many namespaces) |
| 8 | `backup` | check | cross-node coordination |
| 9 | `letsencrypt` | check | cross-cutting |
| 10 | `nextcloud` | yes | complex, recent activity |
For each: derive the **Metadata** section 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 what other bundles' artifacts (apt packages,
systemd services) the bundle's reactors and items reference — use
`ccc search` (cocoindex; index already built at `.cocoindex_code/`) for
cross-bundle lookups when filling this in.
Where a bundle has an existing `README.md`, fold its content into the new
`AGENTS.md` and remove the old `README.md` in the same commit. Do not
lose information — those READMEs are the only existing human-prose
description for those bundles.
For bundles whose reactors write into other bundles' namespaces (notably
`telegraf`, `monitored`, `archive`, `wol-waker`, `apt`, `nextcloud`),
fill in the optional `## Writes into` section.
About 23 other bundles also have a `README.md` (not on the seed list).
Those stay in tree, untouched, until Phase 3 leave-as-you-go folds them.
Document this transition state in `bundles/AGENTS.md` so an agent
reading both isn't confused.
## Phase 3 — leave-as-you-go convention
Not a code task. The rule lives in `bundles/AGENTS.md`: any time an agent
or the maintainer materially edits a bundle, top-up or create its
`AGENTS.md`. The remaining ~93 bundles are filled lazily as they're
touched.
## Decisions captured (do not relitigate)
- **Doc audience.** One balanced doc per artifact, serving both humans
and agents. No separate `README.md` + `AGENTS.md` split per bundle.
- **Fork hosts bundlewrap-language docs.** Don't duplicate items / metadata
semantics in ckn-bw. Link out.
- **Read-only by default.** Agents do not run `bw apply`, `bw run`, or
`bw lock` autonomously. Never. Even with `-i`. The fork's AGENTS.md
spells this out as Tier 3.
- **No tooling work in this scope.** No `bw` wrapper, no Makefile, no CI,
no lint. The plan and spec both call this out as a non-goal.
- **Per-bundle metadata as Python dict** (not bullet list). Matches how
`metadata.py` actually looks; trailing comments carry type / required /
default.
- **Bundle naming**: lowercase-hyphenated (`bind-acme`, `routeros-monitoring`,
`wol-waker`). Underscores were used in deleted `_old` bundles, none
remain.
- **Node naming**: `<location>.<role>.py` (e.g. `home.server.py`, `htz.mails.py`).
- **`*.py_` suspend convention** is real and intentional — never delete.
- **Commit-message style**: lowercase, terse subject; multi-line body
with bullets when relevant; `Co-Authored-By: Claude Opus 4.7 (1M context)
<noreply@anthropic.com>` trailer when Claude assisted. Look at recent
commits for tone.
- **Don't push without asking.** Local commits only unless the user
explicitly says push.
## Pitfalls — things that bite
- **`bw hash` does NOT accept selectors.** Only literal node/group names.
Selectors (`bundle:<x>`, `group:<x>`) work for `bw apply`, `bw run`,
`bw nodes`, but `bw hash` is the exception. To scope to a bundle:
`bw nodes bundle:<x>` to enumerate, then `bw hash <node>` per result.
- **`bw items <node> <id> -p` is wrong** — the flag doesn't exist. Use
bare form for expected state, `--preview` (`-f`) for rendered file
content, `--attrs` for internal attributes.
- **`bw groups -n <node>` doesn't exist** — use `bw nodes <node> -a groups`.
- **Sandbox + macOS `localhost` quirk.** If you hit `errno 47
EAFNOSUPPORT` on a localhost-proxy connection, `/etc/hosts` may have
collapsed `127.0.0.1 localhost` and `::1 localhost` onto malformed
lines. Verify with `python3 -c "import socket;
print(socket.getaddrinfo('localhost', 80, type=socket.SOCK_STREAM))"`
must include both `('127.0.0.1', 80)` and `('::1', 80, 0, 0)`. (This
was already fixed in this session; documenting in case it regresses.)
- **`bw test` makes outbound HEAD requests** to verify download URLs.
Sandbox needs `allowedDomains: ["*"]` (or equivalent) for it to pass.
Fall back to `dangerouslyDisableSandbox: true` if sandbox network
blocks unexpectedly.
- **Eval-loaded node files.** `nodes/*.py` and `groups/*.py` are
`eval()`'d as Python expressions. No top-level imports allowed. A
syntax error or import causes the loader to silently drop the node
from `bw nodes` (vanilla bundlewrap behavior). The maintainer's
`groups.py` was patched in `dc40295` to print errors — a real foot-gun
to document in `nodes/AGENTS.md`.
- **Don't restore the 9 partially-edited libs from the prior session.**
They were intentionally reverted to give the next session a clean
starting point; redo from scratch using your own judgment + file
contents.
## Operating environment
- **Working dir**: `/Users/mwiegand/Projekte/ckn-bw`.
- **venv path**: `.venv/`. `bundlewrap` is editable at `.venv/src/bundlewrap/`
on branch `main`, currently at commit `42dabfc2`. To pull upstream changes:
`(cd .venv/src/bundlewrap && git pull)`.
- **Sandbox**: enabled, `network.allowedDomains: ["*"]`, network passes
after `/etc/hosts` was un-corrupted. `bw test` works under the sandbox.
- **ccc index** at `.cocoindex_code/` — already built (768 chunks, 340
files). Use `ccc search "<query>"` for semantic codebase exploration.
Particularly useful in Phase 2 for finding what consumes a given
metadata key or imports a given lib.
## What is intentionally NOT in scope
Re-listing so the next session doesn't drift:
- No tooling: no Makefile, no `bw` wrapper, no lint, no CI, no
pre-commit hooks.
- 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) stays as-is now that the stale
fork-install section is gone. Don't add a project-readme on top.
- No upstream contribution to bundlewrap (acknowledged future work, not
this scope).
- No edits to the bundlewrap fork — that's a separate repo. The fork
agent already shipped its `AGENTS.md`.
## Success criteria
Phase 0 docstring pass complete + Phase 1 scaffolding done + Phase 2 seed
bundle docs done. Verifiable by:
- `find . -name 'AGENTS.md' -not -path './.venv/*' -not -path './.git/*'`
lists root, 8 area docs, the per-bundle template, and 10 seed bundle
docs. (~20 files.)
- `readlink CLAUDE.md``AGENTS.md`.
- All internal links resolve (verification step 3 above).
- All `libs/*.py`, `hooks/*.py`, `bin/*` self-describe.
- `bw test` passes.
- A fresh agent can trace "implement a new bundle" through root → bundles
→ example → write → wire → verify, without spelunking.
## Quick start for the new session
If the new session has zero prior context:
```bash
cd /Users/mwiegand/Projekte/ckn-bw
git log --oneline -5 # see Phase 0 commits
cat docs/superpowers/handoffs/2026-05-10-implementation-handoff.md # this file
cat docs/superpowers/specs/2026-05-10-agent-friendliness-design.md # the spec
cat ~/.claude/plans/btw-are-you-sure-crystalline-balloon.md # the plan
.venv/bin/bw test # confirm baseline still green
```
Then start with the docstring pass (Phase 0 remainder), commit, then move
into Phase 1 file-by-file in the order listed above.