Files
bot-bottle/docs/prds/0044-print-parity-across-backends.md
T
didericis-claude c1a4fa756a
test / unit (pull_request) Successful in 35s
test / integration (pull_request) Successful in 40s
docs(prd): add PRD 0044 — print parity across backends
2026-06-02 15:50:43 +00:00

4.7 KiB

PRD 0044: Print Parity Across Backends

  • Status: Draft
  • Author: didericis-claude
  • Created: 2026-06-02
  • Issue: #96

Summary

Hoist git_gate_plan, egress_plan, agent_provision, and supervise_plan from the concrete BottlePlan subclasses up to BottlePlan, and implement print concretely there. This eliminates the two per-backend output divergences and ensures any future backend gets correct preflight rendering for free.

Problem

BottlePlan.print is @abstractmethod, so each backend provides its own implementation. The two current implementations have drifted:

Field Docker smolmachines
git gate lines upstream_host:upstream_port from resolved git_gate_plan.upstreams Name → Upstream from manifest bottle.git
egress lines host [auth:scheme] host only (auth dropped)

The smolmachines docstring says "same shape as the Docker backend's so operators see one format across backends" — that intent is real but nothing enforces it.

The env_names divergence previously noted in this issue was resolved by PRD 0038 (smolmachines env contract): resolved.forwarded is now merged into agent_provision.guest_env at prepare time on both backends, so displayed env names are equivalent.

Goals / Success Criteria

  • BottlePlan carries git_gate_plan, egress_plan, agent_provision, and supervise_plan as concrete fields; subclasses no longer declare them independently.
  • BottlePlan.print is a concrete method; subclasses have no print implementation of their own.
  • Both backends render git gate lines as name → upstream_host:upstream_port (using git_gate_plan.upstreams), not the manifest-level URL.
  • Both backends render egress lines as host [auth:scheme] (dropping the annotation only when auth_scheme is empty).
  • Unit tests assert the unified output for both backends from a single shared test helper.

Non-goals

  • No changes to the Docker or smolmachines launch, prepare, or cleanup paths.
  • No changes to how env values are resolved or injected (that is PRD 0038).
  • No changes to the manifest schema or GitEntry.
  • No new CLI flags or dashboard changes.

Scope

In scope:

  • bot_bottle/backend/__init__.py — add git_gate_plan, egress_plan, agent_provision, and supervise_plan fields to BottlePlan; replace @abstractmethod print with a concrete implementation.
  • bot_bottle/backend/docker/bottle_plan.py — remove the four hoisted fields and the print method.
  • bot_bottle/backend/smolmachines/bottle_plan.py — remove the four hoisted fields and the print method.
  • tests/unit/ — add or update tests asserting unified preflight output; a shared helper can build a minimal plan fixture for each backend and assert the same lines appear.

Out of scope:

  • Changes to bot_bottle/backend/print_util.py beyond what the new print implementation requires.
  • Changes to BottleCleanupPlan.print or any other print method.
  • Integration tests that launch a real bottle.

Design

Move the four fields that both concrete subclasses already declare — git_gate_plan: GitGatePlan, egress_plan: EgressPlan, agent_provision: AgentProvisionPlan, supervise_plan: SupervisePlan | None — up to BottlePlan. Both backends' prepare paths already produce these with the same types, so no prepare-time changes are needed.

Replace the @abstractmethod print with a concrete implementation on BottlePlan that:

  1. Builds env_names from bottle.env.keys() | agent_provision.guest_env.keys() filtered through agent_provision.hidden_env_names.
  2. Builds git gate lines from git_gate_plan.upstreams as f"{u.name} → {u.upstream_host}:{u.upstream_port}".
  3. Builds egress lines from egress_plan.routes as f"{r.host} [auth:{r.auth_scheme}]" when r.auth_scheme is non-empty, else r.host.
  4. Renders the standard two-column preflight block (leading blank line, agent, provider, env, skills, bottle, git identity, git gate, egress, trailing blank line).

Docker's forwarded_env keys are already merged into agent_provision.guest_env via the agent_provision_plan builder, so no special handling is needed for env_names.

Testing Strategy

  • Add a shared fixture builder (e.g. make_plan(backend)) in a new or existing unit test module that constructs a minimal DockerBottlePlan and SmolmachinesBottlePlan from the same spec and plan fields.
  • Assert that plan.print(remote_control=False) produces identical git gate and egress lines for both backends given the same git_gate_plan and egress_plan.
  • Test the auth_scheme annotation: present when non-empty, absent otherwise.
  • Test git gate rendering: name → host:port format.

Run:

  • python3 -m unittest discover -s tests/unit

Open Questions

None.