120 lines
4.7 KiB
Markdown
120 lines
4.7 KiB
Markdown
# PRD 0044: Print Parity Across Backends
|
|
|
|
- **Status:** Active
|
|
- **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.
|