Implements the manifest surface for the signed-commits & audit-attribution
PRD (#423, PR #480): a bottle opts into per-activation commit signing with
git-gate:
signing:
enabled: true
- New ManifestGitSigning value type (frozen, enabled: bool = False) parsed
under git-gate.signing; unknown sub-keys and non-bool `enabled` die with
targeted errors. parse_git_gate_config now returns (repos, user, signing).
- ManifestBottle gains git_signing; defaults to off, so bottles that omit
the block are unchanged.
- Bottle-only: git-gate.signing on an agent is rejected by the existing
agent-level non-`user` guard (like git-gate.repos).
- extends/runtime merges thread git_signing with an enable-wins overlay
(a child enabling signing wins; omitting inherits the parent), mirroring
the git_user non-empty-wins overlay.
- Facade exports ManifestGitSigning.
Tests: tests/unit/test_manifest_git_signing.py (parse, validation,
agent-level rejection, extends overlay). Full unit suite green.
This is the first stacked chunk; signing pipeline, gate signature check,
and control-plane verification + audit tables follow per the PRD.
Issue: #423
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
manifest/__init__.py was a facade that eagerly imported every piece (agent,
bottle, egress, git, loader, schema, util) and defined the aggregate Manifest /
ManifestIndex, so importing a leaf like `manifest.util` paid 19 modules.
Move the aggregate model — Manifest, ManifestIndex, and the bottle-resolution
helpers — into manifest/index.py (which keeps the framework imports it needs),
and make __init__ a thin __getattr__ facade over the 12 __all__ names (mapping
each to its submodule), with a TYPE_CHECKING block. The package docstring is
preserved.
`import bot_bottle.manifest.util` drops 19 -> 3; the package itself is 2. All
`from bot_bottle.manifest import Manifest/ManifestIndex/ManifestError/…`
call-sites keep working via the lazy facade; no importer changes needed.
Full unit suite green (2243).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the nine root-level manifest modules into a bot_bottle/manifest/ package,
dropping the redundant manifest_ prefix: the facade (manifest.py) becomes the
package __init__ and keeps re-exporting Manifest / ManifestIndex and the piece
types, while manifest_<x>.py become manifest/<x>.py (agent, bottle, egress,
extends, git, loader, schema, util).
Because the facade stays the package __init__, the ~13 callers that import
`from bot_bottle.manifest import ...` are unchanged; only direct-piece imports
move to `bot_bottle.manifest.<name>`. Inside the package, intra-manifest
imports drop the prefix and the non-manifest siblings (log, yaml_subset,
agent_provider) move to `..`.
No behavior change; full unit suite green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>