From c1a4fa756a2f44289d53b1430f2debbb6bf3e7bb Mon Sep 17 00:00:00 2001 From: claude Date: Tue, 2 Jun 2026 15:50:43 +0000 Subject: [PATCH] =?UTF-8?q?docs(prd):=20add=20PRD=200044=20=E2=80=94=20pri?= =?UTF-8?q?nt=20parity=20across=20backends?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../prds/0044-print-parity-across-backends.md | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 docs/prds/0044-print-parity-across-backends.md diff --git a/docs/prds/0044-print-parity-across-backends.md b/docs/prds/0044-print-parity-across-backends.md new file mode 100644 index 0000000..20fa1f3 --- /dev/null +++ b/docs/prds/0044-print-parity-across-backends.md @@ -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.