devantler-tech / devantler-tech/ksail
fix(env): the multi-cluster scaffold's initial environment is invisible to env list, add and rm
- Dominant language
- Go
- Stars
- 165
- Forks
- 12
- Avg merge
- 5h 41m
- Merged PRs (30d)
- 337
Description
> 🤖 Generated by the Agentic Engineer
## Evidence
Reproduced end to end with a binary built from `main`, in an empty directory. `project init --multi-cluster prod` scaffolds an environment named `prod`, and the env verbs then disagree about whether it exists:
```
$ ksail project init --multi-cluster prod
✚ created 'k8s/clusters/base/kustomization.yaml'
✚ created 'k8s/clusters/prod/kustomization.yaml'
✔ initialized project
$ ksail project env reconcile --experimental
ENVIRONMENT OVERLAY STATE
prod clusters/prod Present
$ ksail project env list
ℹ no environments declared; scaffold one with `ksail project env add --from `
$ ksail project env add staging --from prod
✗ failed to load source environment config (ksail.prod.yaml): ... no such file or directory
```
`reconcile` reports `prod` as a first-class declared environment; `list` reports none; `add --from prod` cannot find it. Same workspace, same moment.
The plain `ksail project init` path lands in the same place: it writes only `ksail.yaml`, so `env list` prints the identical hint, and there is no environment to pass to `--from`.
## Root cause
The two verbs resolve "what is an environment" through different functions, and only one of them knows about the multi-cluster scaffold's initial environment:
- `environment.DerivePlan` (used by `env reconcile`) calls `appendBaseSyncedEntry`, which reads the base `ksail.yaml`'s `kustomizationFile: clusters/prod` and adds `prod` as a first-class entry. Its own comment names this case exactly: *"the initial environment `project init --multi-cluster` scaffolds without a ksail..yaml"*.
- `environment.DeriveEnvironments` (used by `env list`, by `env add`'s `--from` load, by its available-environments hint, and by `env rm`) enumerates `ksail..yaml` files only, so the base-synced environment is invisible to all of them.
So the codebase already recognises this shape in one verb and not its siblings.
## Affected audience and impact
Anyone following the multi-cluster path — the flow epic #5441 exists to support. Concretely:
1. **The advertised next step cannot be taken.** `env add --from` is the only way to create a second environment, and the initial environment cannot be named as its source. The multi-cluster scaffold therefore stops at one environment.
2. **The CLI's own remediation hint is unfollowable** in exactly the state where it is printed: there is no `` to pass to `--from`, in either init mode. A guardrail that names a fix that cannot be applied trains people to distrust the message.
3. **`env list` contradicts `env reconcile`** on the same workspace, so neither can be trusted as the answer to "what environments do I have?".
There is a workaround, but it is undocumented and requires knowing the file convention: hand-write a `ksail.prod.yaml` next to `ksail.yaml`, after which `env list` and `env add --from prod` both work.
## Expected behaviour
The env verbs agree on what an environment is. The environment that `project init --multi-cluster ` creates is visible to `env list`, usable as `env add --from `, and removable by `env rm`, without hand-writing a config file.
## Proposed direction
Give `DeriveEnvironments` the same base-synced awareness `DerivePlan` already has — most cheaply by extracting the `appendBaseSyncedEntry` resolution so both call sites share it, which also guarantees the two verbs cannot drift apart again. Two details worth settling while implementing:
- What `env list` should show for `DISTRIBUTION`/`PROVIDER` of a base-synced environment (they come from the base `ksail.yaml`, so they are known).
- Whether `env add --from ` should also write the missing `ksail..yaml` for the source as it clones, so the workspace converges on one representation.
An alternative worth considering instead: have `project init --multi-cluster ` write `ksail..yaml` up front, so every environment has the same shape from the start. That is a smaller change but alters the scaffold's output, so it deserves an explicit decision rather than being assumed.
## Acceptance criteria
- [ ] After `project init --multi-cluster prod`, `env list` reports `prod`.
- [ ] After `project init --multi-cluster prod`, `env add staging --from prod` succeeds and produces a working `staging` environment.
- [ ] `env list` and `env reconcile` report the same environment set for that workspace, pinned by a test that would fail if either resolver changes alone.
- [ ] The "no environments declared" hint is only shown when there is genuinely nothing to clone from, and names an action that works from that state.
- [ ] Regression coverage for both init modes (plain and `--multi-cluster`).
## Rough size
S–M. The resolver change is small and the seam already exists; most of the work is deciding the two questions above and covering both init modes.
Contributor guide
Research direction
Start with the env list, add, rm, and reconcile entry points, then compare environment.DeriveEnvironments with environment.DerivePlan and its appendBaseSyncedEntry handling. Reproduce both plain and --multi-cluster project init flows, and run the related environment tests; done means list, reconcile, add --from, and rm agree on the scaffolded environment without an unfollowable hint.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 65/100