docs(prd): add PRD 0044 — print parity across backends

This commit is contained in:
2026-06-02 15:50:43 +00:00
committed by didericis
parent f25fa589fe
commit 71ac555f25
@@ -0,0 +1,119 @@
# 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.