introduces a balanced set of agent + human docs: - root AGENTS.md (with CLAUDE.md symlink) — 5-rule quickstart, layout map, mental model, use-case keyed example pointers. - docs/agents/conventions.md — vault/demagify, eval-loader constraints, group inheritance, naming, do-not-touch list, suspension idioms, working-style notes. - docs/agents/commands.md — repo-specific deltas to the fork's bw runbook (apt-key offline-verify, *.py_ suspended-node visibility, vault-echo rule). - per-area AGENTS.md for bundles/, nodes/, groups/, libs/, hooks/, data/, items/, bin/ — mechanism-focused, no enumeration. - bundles/AGENTS.template.md — per-bundle doc template with optional `## Writes into` section for cross-namespace reactors. bundlewrap-language reference (item types, dep keywords, reactors, runbook, three-tier safety envelope) is not duplicated here; we link out to the fork's AGENTS.md instead. bw test still green. all internal links resolve. Phase 0 invariants preserved (libs/hooks docstrings, bin/* # purpose: headers). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.9 KiB
Conventions
Repo-specific idioms an agent has to know before editing this BundleWrap
config. For bundlewrap-the-language reference (item types, dep keywords,
metadata-reactor semantics, after-change runbook), see the fork's
AGENTS.md.
Secrets
Secrets live in .secrets.cfg and are referenced from node files by
"demagify" magic strings. The loader (nodes.py) walks node dicts and
resolves any leaf string of the form !<verb>:<arg>.
| Magic string | Resolves to |
|---|---|
!password_for:<id> |
repo.vault.password_for(id) |
!decrypt:<ciphertext> |
repo.vault.decrypt(ciphertext) |
!decrypt_file:<path> |
repo.vault.decrypt_file(path) |
!32_random_bytes_as_base64_for:<id> |
repo.vault.random_bytes_as_base64_for(id, length=32) |
Magic strings are only resolved inside node files (everything under
nodes/). They are not resolved in group files, bundle metadata
defaults, or item attributes — call repo.vault.<verb>(...) directly
there.
Never echo decrypted values. Don't print, log, or paste them, even
in bw debug exploration. If you need a sanity check, hash or
fingerprint instead. This applies to AI agents and humans equally.
Bundlewrap version
The venv runs editable from the maintainer's fork:
-e git+https://github.com/CroneKorkN/bundlewrap.git@main#egg=bundlewrap
Pinned in requirements.txt. The fork's main tracks upstream
bundlewrap/bundlewrap master; the fork's AGENTS.md
is the canonical agent-oriented bundlewrap-language reference.
To pull upstream changes into the venv:
(cd .venv/src/bundlewrap && git pull)
Eval-loaded node and group files
nodes/*.py and groups/*.py are loaded by eval() in nodes.py /
groups.py (top of the repo). Each file must be a single Python
expression — typically a dict literal.
Consequences:
- No top-level imports. No
from foo import bar, noimport os. Helpers go inlibs/and are reached viarepo.libs.<x>from bundle code, not from node files. - No statements. No
if/for/defat the top level. Use expressions (ternaries, comprehensions) instead. - Silent drop on parse error. Vanilla bundlewrap silently omits a
node/group whose file fails to eval. The maintainer patched
groups.py(commitdc40295) to print the error instead — but it still skips the file. Symptom:bw nodeslists fewer nodes than you expect. Cure: re-read the file and check for accidental imports or syntax errors.
Group inheritance order
Metadata merges along this chain (from the fork's docs):
all → location → os → machine → applications → node
groups/all.py is the universal base; groups/{locations,os,machine,applications}/
hold the per-axis groups; the node's own metadata wins last.
Naming conventions
| Where | Convention | Example |
|---|---|---|
nodes/*.py |
<location>.<role>.py |
home.server.py, htz.mails.py |
groups/*.py |
one file per group; subdir = purpose | groups/applications/nextcloud.py, groups/os/debian-13.py |
bundles/<name> |
lowercase, hyphen-separated | backup-server, bind-acme, routeros-monitoring |
| Custom items | items/<type>.py |
items/download.py |
Underscores in bundle names appear only in _old-suffixed leftovers
(see below); don't introduce new ones.
Suspension and soft-delete idioms
These conventions look like dead code; they aren't. Don't clean them up.
*.py_parked node. A node file whose name ends in.py_is silently excluded frombw nodes(the loader only matches*.py). Used to keep decommissioned-but-not-deleted node configs in tree. Example:nodes/htz.l4d2.py_.- "for now / disable / dummy / offline" markers. Commits or comments
containing these phrases mark deliberate suspensions, not bugs. Check
git log -- <file>before re-enabling — the maintainer reverses these manually when the underlying condition resolves. _old/_old2directories. Recovery buffers during big refactors. Don't delete them without asking, even if they look orphaned.
Files agents must not modify
| Path | Why |
|---|---|
.secrets.cfg* |
vault key material |
.venv/ |
local Python environment |
.cache/ |
bw runtime cache |
.bw_debug_history |
shell history for bw debug |
.envrc |
direnv local-environment config |
/.cocoindex_code/ |
local code index, not in git |
README.md (root) |
maintainer's personal TODO scratchpad — not project docs |
Treat hooks/ and items/ (custom item types) with extra care: they
affect bw's behavior for the whole repo, not a single bundle.
Working style
- Iterative commits are normal. The maintainer commits in small
checkpoints (
+,fix,whitespace, terse one-liners). Don't rebase WIP branches without asking. As an agent, prefer complete-feeling commits over mimicking the iterative style. - Burst-state awareness. Before writing into a subsystem, run
git log --since='1 month ago' bundles/<x>(ornodes/<x>.py). ≥10 recent commits means the subsystem is in flux; read the most recent diffs first — your assumptions about its metadata shape may already be stale. - Branch names. PRs go through self-hosted Gitea/Forgejo. Branch
names are lowercase snake_case, descriptive
(
debian-13,htz.mails_debian_13_squash,l4d2_the_next).
Bundlewrap-version migration recipe
When the next bw major lands:
- Read the upstream migration guide.
git grepfor affected reactor / item patterns.- Rewrite each (one commit per pattern is fine — see Working style).
- Bump
requirements.txtlast.
Pattern from commit 186d503 (bw 4 → 5).