refactor(manifest): consolidate the manifest modules into a bot_bottle.manifest package
tracker-policy-pr / check-pr (pull_request) Failing after 11s
test / integration-docker (pull_request) Successful in 20s
lint / lint (push) Successful in 59s
test / unit (pull_request) Successful in 2m10s
test / integration-firecracker (pull_request) Successful in 3m29s
test / coverage (pull_request) Successful in 38s
test / publish-infra (pull_request) Has been skipped

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>
This commit is contained in:
2026-07-24 13:04:39 -04:00
parent d7a58e52fd
commit a845cba925
15 changed files with 47 additions and 47 deletions
+1 -1
View File
@@ -380,7 +380,7 @@ def _peek_agent_bottle(manifest: ManifestIndex, agent_name: str) -> str:
return manifest.agents[agent_name].bottle return manifest.agents[agent_name].bottle
return "" return ""
from ..manifest_loader import scan_agent_names from ..manifest.loader import scan_agent_names
from ..yaml_subset import YamlSubsetError, parse_frontmatter from ..yaml_subset import YamlSubsetError, parse_frontmatter
home_agents = scan_agent_names(manifest.home_md / "agents") home_agents = scan_agent_names(manifest.home_md / "agents")
+1 -1
View File
@@ -49,7 +49,7 @@ def get_provisioner(
GiteaDeployKeyProvisioner, GiteaDeployKeyProvisioner,
) )
return GiteaDeployKeyProvisioner(token=token, api_url=api_url) return GiteaDeployKeyProvisioner(token=token, api_url=api_url)
from .manifest_util import ManifestError from .manifest.util import ManifestError
raise ManifestError( raise ManifestError(
f"unknown provisioned_key provider: {provider!r}; " f"unknown provisioned_key provider: {provider!r}; "
f"available: gitea" f"available: gitea"
@@ -63,25 +63,25 @@ from dataclasses import dataclass, field, replace
from pathlib import Path from pathlib import Path
from typing import Mapping from typing import Mapping
from .log import warn from ..log import warn
from .manifest_util import ManifestError, as_json_object from .util import ManifestError, as_json_object
from .manifest_agent import ManifestAgent, ManifestAgentProvider from .agent import ManifestAgent, ManifestAgentProvider
from .manifest_bottle import ManifestBottle from .bottle import ManifestBottle
from .manifest_egress import ( from .egress import (
EGRESS_AUTH_SCHEMES, EGRESS_AUTH_SCHEMES,
ManifestEgressConfig, ManifestEgressConfig,
ManifestEgressRoute, ManifestEgressRoute,
) )
from .manifest_extends import merge_bottles_runtime, resolve_bottles from .extends import merge_bottles_runtime, resolve_bottles
from .manifest_git import ManifestGitEntry, ManifestGitUser, ManifestKeyConfig from .git import ManifestGitEntry, ManifestGitUser, ManifestKeyConfig
from .manifest_loader import ( from .loader import (
check_stale_json, check_stale_json,
load_bottle_chain_from_dir, load_bottle_chain_from_dir,
scan_agent_names, scan_agent_names,
scan_bottle_names, scan_bottle_names,
) )
from .manifest_schema import validate_agent_frontmatter_keys from .schema import validate_agent_frontmatter_keys
from .yaml_subset import YamlSubsetError, parse_frontmatter from ..yaml_subset import YamlSubsetError, parse_frontmatter
# Re-export everything that callers currently import from this module. # Re-export everything that callers currently import from this module.
__all__ = [ __all__ = [
@@ -5,10 +5,10 @@ from __future__ import annotations
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import cast from typing import cast
from .agent_provider import PROVIDER_TEMPLATES from ..agent_provider import PROVIDER_TEMPLATES
from .manifest_util import ManifestError, as_json_object from .util import ManifestError, as_json_object
from .manifest_git import ManifestGitUser from .git import ManifestGitUser
from .manifest_schema import AGENT_MODEL_KEYS, is_valid_entity_name from .schema import AGENT_MODEL_KEYS, is_valid_entity_name
@dataclass(frozen=True) @dataclass(frozen=True)
@@ -1,13 +1,13 @@
"""The `ManifestBottle` value type. """The `ManifestBottle` value type.
Split out of `manifest.py` so the `extends:`/loader resolvers can import it Split out of the package facade (`manifest/__init__.py`) so the `extends:`/
without a circular dependency: `manifest.py` imports those resolvers, while loader resolvers can import it without a circular dependency: the facade
they only need this value type. Everything here depends on leaf modules imports those resolvers, while they only need this value type. Everything here
(`manifest_util`, `manifest_agent`, `manifest_egress`, `manifest_git`, depends on leaf modules (`util`, `agent`, `egress`, `git`, `schema`), so this
`manifest_schema`), so this module sits at the bottom of the manifest layer. module sits at the bottom of the manifest layer.
`manifest.py` re-exports `ManifestBottle`, so existing The facade re-exports `ManifestBottle`, so existing
`from .manifest import ManifestBottle` callers are unaffected. `from bot_bottle.manifest import ManifestBottle` callers are unaffected.
""" """
from __future__ import annotations from __future__ import annotations
@@ -15,11 +15,11 @@ from __future__ import annotations
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Mapping from typing import Mapping
from .manifest_util import ManifestError, as_json_object from .util import ManifestError, as_json_object
from .manifest_agent import ManifestAgentProvider from .agent import ManifestAgentProvider
from .manifest_egress import ManifestEgressConfig from .egress import ManifestEgressConfig
from .manifest_git import ManifestGitEntry, ManifestGitUser, parse_git_gate_config from .git import ManifestGitEntry, ManifestGitUser, parse_git_gate_config
from .manifest_schema import BOTTLE_KEYS from .schema import BOTTLE_KEYS
__all__ = ["ManifestBottle"] __all__ = ["ManifestBottle"]
@@ -6,7 +6,7 @@ import re
from dataclasses import dataclass from dataclasses import dataclass
from typing import cast from typing import cast
from .manifest_util import ManifestError, as_json_object from .util import ManifestError, as_json_object
EGRESS_AUTH_SCHEMES = ("Bearer", "token") EGRESS_AUTH_SCHEMES = ("Bearer", "token")
@@ -2,10 +2,10 @@
from __future__ import annotations from __future__ import annotations
from .manifest_bottle import ManifestBottle from .bottle import ManifestBottle
from .manifest_egress import ManifestEgressConfig, validate_egress_routes from .egress import ManifestEgressConfig, validate_egress_routes
from .manifest_git import ManifestGitUser, parse_git_gate_config from .git import ManifestGitUser, parse_git_gate_config
from .manifest_util import ManifestError, as_json_object from .util import ManifestError, as_json_object
def _overlay_declared_bool( def _overlay_declared_bool(
@@ -5,7 +5,7 @@ from __future__ import annotations
import re import re
from dataclasses import dataclass from dataclasses import dataclass
from .manifest_util import ManifestError, as_json_object from .util import ManifestError, as_json_object
# Shell-safe characters for git-gate repo names. Names are embedded in # Shell-safe characters for git-gate repo names. Names are embedded in
# the generated entrypoint shell script (shlex.quote is the primary # the generated entrypoint shell script (shlex.quote is the primary
@@ -4,15 +4,15 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
from .log import warn from ..log import warn
from .manifest_bottle import ManifestBottle from .bottle import ManifestBottle
from .manifest_extends import resolve_bottles from .extends import resolve_bottles
from .manifest_schema import ( from .schema import (
entity_name_from_path, entity_name_from_path,
validate_bottle_frontmatter_keys, validate_bottle_frontmatter_keys,
) )
from .manifest_util import ManifestError from .util import ManifestError
from .yaml_subset import YamlSubsetError, parse_frontmatter from ..yaml_subset import YamlSubsetError, parse_frontmatter
def check_stale_json(dir_path: Path, md_dir: Path, label: str) -> None: def check_stale_json(dir_path: Path, md_dir: Path, label: str) -> None:
@@ -68,7 +68,7 @@ def _validate_frontmatter_keys(
keys: object, keys: object,
allowed_keys: frozenset[str], allowed_keys: frozenset[str],
) -> None: ) -> None:
from .manifest_util import ManifestError from .util import ManifestError
key_set = set(keys) # type: ignore key_set = set(keys) # type: ignore
unknown = key_set - allowed_keys # type: ignore unknown = key_set - allowed_keys # type: ignore
+1 -1
View File
@@ -336,7 +336,7 @@ class TestManifestToYaml(unittest.TestCase):
agent_provider_template: str = "claude", agent_provider_template: str = "claude",
): ):
from bot_bottle.manifest import Manifest, ManifestBottle from bot_bottle.manifest import Manifest, ManifestBottle
from bot_bottle.manifest_agent import ManifestAgent, ManifestAgentProvider from bot_bottle.manifest.agent import ManifestAgent, ManifestAgentProvider
agent = ManifestAgent(skills=tuple(skills)) agent = ManifestAgent(skills=tuple(skills))
bottle = ManifestBottle( bottle = ManifestBottle(
+1 -1
View File
@@ -21,7 +21,7 @@ from bot_bottle.git_gate import (
git_gate_render_gitconfig, git_gate_render_gitconfig,
revoke_git_gate_provisioned_keys, revoke_git_gate_provisioned_keys,
) )
from bot_bottle.manifest_git import ManifestGitEntry, ManifestKeyConfig from bot_bottle.manifest.git import ManifestGitEntry, ManifestKeyConfig
def _entry(**kw: Any) -> ManifestGitEntry: def _entry(**kw: Any) -> ManifestGitEntry:
+2 -2
View File
@@ -14,7 +14,7 @@ import unittest
from pathlib import Path from pathlib import Path
from bot_bottle.manifest import ManifestBottle, ManifestError, ManifestIndex from bot_bottle.manifest import ManifestBottle, ManifestError, ManifestIndex
from bot_bottle.manifest_extends import merge_bottles_runtime from bot_bottle.manifest.extends import merge_bottles_runtime
def _index(bottles: dict[str, object], agents: dict[str, object]) -> ManifestIndex: def _index(bottles: dict[str, object], agents: dict[str, object]) -> ManifestIndex:
@@ -44,7 +44,7 @@ class TestMergeBottlesRuntime(unittest.TestCase):
self.assertEqual("y", result.env["ONLY_OVERRIDE"]) self.assertEqual("y", result.env["ONLY_OVERRIDE"])
def test_egress_routes_concatenated(self): def test_egress_routes_concatenated(self):
from bot_bottle.manifest_egress import ManifestEgressConfig, ManifestEgressRoute from bot_bottle.manifest.egress import ManifestEgressConfig, ManifestEgressRoute
r1 = ManifestEgressRoute(Host="api.a.com") r1 = ManifestEgressRoute(Host="api.a.com")
r2 = ManifestEgressRoute(Host="api.b.com") r2 = ManifestEgressRoute(Host="api.b.com")
base = ManifestBottle(egress=ManifestEgressConfig(routes=(r1,))) base = ManifestBottle(egress=ManifestEgressConfig(routes=(r1,)))
+2 -2
View File
@@ -14,12 +14,12 @@ from unittest.mock import patch
from bot_bottle.env import resolve_env from bot_bottle.env import resolve_env
from bot_bottle.errors import MissingEnvVarError from bot_bottle.errors import MissingEnvVarError
from bot_bottle.manifest import Manifest, ManifestBottle, ManifestIndex from bot_bottle.manifest import Manifest, ManifestBottle, ManifestIndex
from bot_bottle.manifest_agent import ( from bot_bottle.manifest.agent import (
ManifestAgent, ManifestAgent,
ManifestAgentProvider, ManifestAgentProvider,
_parse_provider_settings, _parse_provider_settings,
) )
from bot_bottle.manifest_util import ManifestError from bot_bottle.manifest.util import ManifestError
def _idx(obj: dict[str, object]) -> ManifestIndex: def _idx(obj: dict[str, object]) -> ManifestIndex: