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>
2.5 KiB
2.5 KiB
libs/
What's here
Shared Python helpers reachable from any bundle as
repo.libs.<modulename>.<symbol>. One module per file (no packages).
Discovery is by ls libs/ plus the one-line module docstring at the
top of each file.
head -1 libs/*.py
Conventions
- One file per module. Filename (without
.py) is the importable name.libs/wireguard.pyexposesrepo.libs.wireguard. - One-line module docstring. Every
libs/*.pystarts with"""<name>: <one-line purpose>."""sols + headis a working index. Add one when introducing a new lib; the rule is enforced by the Phase-0 baseline (grep -L '"""' libs/*.pyshould report zero files). - Pure helpers, no I/O at import. Libs are imported on every
bwinvocation. Heavy work or filesystem reads at module scope slow the whole repo. Use functions, not module-level side effects. - No magic-string demagification here. Libs receive already-
resolved values from bundles or nodes. Don't pass
!password_for:strings into libs — they won't be resolved. repoandvaultare globals available at runtime. A few libs (wireguard.py, etc.) reachrepo.vault.<verb>(...)directly. That's intentional inside libs, since libs run in the same loader scope as bundles.
How to add a helper
- Either extract from a bundle that's growing complex, or add a new
libs/<name>.pyfile. - Top line:
"""<name>: <one-line purpose>.""". - Pure functions only; document side effects in the docstring if any slip through.
- Use it from
bundles/<x>/items.pyormetadata.pyasrepo.libs.<name>.<symbol>(...).
Pitfalls
-
Lib changes have repo-wide blast radius. Every bundle that imports the lib re-evaluates on the next
bw test/bw apply. Before changing a lib's API, find consumers:git grep -l 'repo.libs.<x>' -
@cacheis your friend for deterministic key derivations (wireguard,rsa,ssh); without it,bwrecomputes per call, which is slow. -
Hashable wrappers.
libs/hashable.pyexists because raw dicts and sets aren't hashable, so you can't put them inside metadata sets. Wrap withrepo.libs.hashable.hashable(...)before nesting.
See also
bundles/AGENTS.md— when to extract a helper intolibs/instead of duplicating across bundles.docs/agents/conventions.md— vault, demagify, naming.- The fork's
AGENTS.md—repo.libsmechanism.