Skip to content

feat(rebind): foundation for RFC-0041 aliases#486

Draft
naxty wants to merge 2 commits into
mainfrom
feat/rfc-0041-foundation
Draft

feat(rebind): foundation for RFC-0041 aliases#486
naxty wants to merge 2 commits into
mainfrom
feat/rfc-0041-foundation

Conversation

@naxty

@naxty naxty commented May 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Lands the foundation for RFC-0041 (resource / stack rename via aliases). No behaviour change for existing formae — formae that do not declare alias see no DB writes from this code.

  • PKL: new Alias class with required label; optional alias: Alias? on Resource, Stack, Target.
  • Go: matching Alias struct (pkg/model/alias.go) mirrored as Alias *Alias on Resource, Stack, Target.
  • Datastore: new Tx marker interface and WithTx(ctx, fn) error method on Datastore. All impls (Aurora, Postgres, SQLite, mocks) pass-through; the marker carries no methods yet so later PRs can extend it without churn.
  • API: new OperationRebind = "rebind" constant.
  • Engine: new internal/metastructure/rebind package with Rebinder.Run / Validate / Execute. All no-op. Run is wired into the apply path ahead of FormaCommandFromForma; subsequent PRs add real rename behaviour without touching the wiring.

Why

Subsequent PRs in the rename-aliases stack add real rename logic on top of this skeleton. Landing the surface up-front lets each follow-up PR stay small and focused.

Why WithTx

Rebind is multi-row, atomic. A single rename like stack: prod → production writes one new stacks row plus N new resources rows (cascade-update of resources.stack). If the cascade partially fails the metastructure is torn — stack renamed, children still pointing at the old label, downstream apply phase creates bogus resources. WithTx wraps the whole rebind in one DB transaction:

  • All writes succeed → commit; renamed view is what the downstream apply phase sees.
  • Any write fails → rollback; metastructure unchanged; no plugin called; author retries from clean state.

The Tx interface is intentionally empty in this PR. PR 2 (resource rebind) adds the real methods (StoreResource, CreateStack, …) to Tx. Doing the Datastore-interface widening once now means PR 2 is purely additive on Tx. All five implementations currently call fn(d) pass-through; real BEGIN/COMMIT plumbing is scoped to PR 2 where it is first exercised by a non-empty RebindPlan.

Test plan

  • go test ./pkg/model/... — 25 passed.
  • go test ./internal/metastructure/rebind/... — 2 passed (no-op Run + Validate).
  • go test ./internal/datastore/... ./internal/metastructure/... — green; pre-existing Postgres collation flake unrelated.
  • make test-generator-pkl — 27/27 PKL tests pass after fixture refresh.

Adds the PKL `Alias` class (single `label` field) and the optional
`alias: Alias?` field on Resource, Stack, and Target. Mirrors the
surface in Go on the equivalent model structs.

Introduces the `datastore.Tx` marker interface and `WithTx` method on
`Datastore`; the marker has no methods yet so later PRs can extend it
without churn. All datastore implementations pass-through.

Adds `OperationRebind = "rebind"` to the API op-type enum.

Creates `internal/metastructure/rebind` with `Rebinder.Run`,
`Validate`, and `Execute`. PR 1 ships them as no-ops: Validate always
returns an empty `RebindPlan` and Run short-circuits before opening a
transaction. The rebinder is wired into the apply pipeline ahead of
`FormaCommandFromForma`, so once later PRs add real validation logic
the engine picks it up automatically.

No behaviour change for existing formae (no alias declared = no-op).

See RFC-0041 §Implementation Plan PR 1.
@naxty naxty closed this May 26, 2026
@naxty naxty reopened this May 26, 2026
@naxty naxty marked this pull request as draft May 26, 2026 12:15
@naxty naxty changed the title feat(rebind): foundation for RFC-0041 aliases (PR 1 / 10) feat(rebind): foundation for RFC-0041 aliases May 26, 2026
ResourceRebind now carries only Ksuid + NewLabel. Per-child writes
generated by a stack rename move into a new ResourceStackCascade
type with Ksuid + NewStack.

Nothing in the plan can express "move this resource to a different
stack via a ResourceAlias" because ResourceRebind has no NewStack
field. Cross-stack moves are only possible through StackRebind plus
the cascade slice, which is what the RFC scope allows.

Target equivalents intentionally absent; tracked for the follow-up
RFC.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant