Files
bot-bottle/tests/unit
didericis-claude a5c8b4e7b2
test / unit (pull_request) Successful in 27s
test / integration (pull_request) Successful in 39s
feat(manifest): bottle composition via \extends:\ resolver (PRD 0025, #88)
Add an optional `extends: <bottle-name>` field to bottle
frontmatter. Two-pass load:

  1. Collect raw frontmatter for every bottle file.
  2. Recursively resolve each name into a merged Bottle via
     `_resolve_one_bottle` + `_merge_bottles`.

Merge rules (per PRD 0025):

- env: dict merge, child wins on key collision
- git: full replace if child declares `git:`
- git_user: per-field overlay (child's non-empty fields win)
- egress: full replace if child declares `egress:`
- supervise: full replace if child declares `supervise:`

List-valued fields full-replace because partial merge is
ambiguous (ordering matters, name collisions ambiguous); env is
dict-merge because dict-keyed override is the natural shape.
git_user overlays per-field so a parent can declare just the
name and a child can add just the email.

Cycles / self-extends / missing-parent / non-string `extends:`
all die at parse with a pointer that includes the chain (cycles)
or the available names (missing parent). Resolution is cached
per-name so a diamond reference graph doesn't reparse the same
parent N times.

Both load paths threaded:
- `_load_bottles_from_dir` (md files) — collect raws, then
  resolve.
- `Manifest.from_json_obj` (JSON / test fixtures) — same.

Tests (24, in `test_manifest_extends.py`):
- Leaf without extends parses unchanged
- Child inherits parent unchanged when child only declares
  `extends:`
- env: disjoint union, collision (child wins), child-omits
- git: replace, omit, explicit-empty-clears-parent
- egress: same shape (replace, inherit)
- git_user: parent-only, child-overrides-both, partial fields
- 3-step chain (grandparent → parent → child)
- Errors: missing parent, self-extends, 2-node cycle, 3-node
  cycle, non-string extends

685 unit tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 23:30:40 -04:00
..