feat(004): governance verbs (tenants, stamps, fleet) over the API client#3
Merged
Conversation
Turn the spec 002 stub verbs into real control-plane calls on the spec
003 client. Each verb has a stable JSON envelope the MCP face (005)
reuses; guards are product surface, not options.
New stagecraft_cli::verbs module (spec 004 establishes it):
- tenants list/show/install-url (with --open)
- stamp new/status (with --watch, 2s->10s backoff, terminal green|failed)
- fleet list/deploy/update/backup/remove
Human output is aligned tables; --output json emits the {ok,data|error}
envelope, wrapping the plane's payload verbatim (passthrough) so the
contract survives field growth.
The spec 003 api module is generalized to GET/POST/DELETE returning
serde_json::Value with a body-carrying request path; an empty 2xx
decodes to JSON null; GET-only retry is unchanged (POST/DELETE are
never retried). The typed whoami path routes through the same pipeline.
Guards, per spec: --posture is required with no default (the platform
rejects defaulted postures); fleet remove echoes the confirm name in
the DELETE body, no --force/--yes. A missing service's 404 surfaces as
"not enabled on this control plane" (exit 1, not a crash), per §1.
JSON-mode failures render {ok:false,error} on stdout via a new
AppError::Rendered (exit 1, no doubled stderr line). A stamp or fleet
record with no readable id/status is a decode error, not a blank
(§5.3), so --watch cannot spin on a statusless response.
Endpoint shapes are inlined into spec 004 §5.1 from stagecraft specs
004/005/006. One is CLI-inlined: fleet list -> GET /tenants/:id/fleet,
since platform spec 006 defines no list endpoint; §1 governs divergence.
Tests: 72 (60 unit + 12 integration) covering per-verb happy path,
api-error surfacing, missing-service 404, watch terminal states, the
statusless-response guard, and JSON envelope snapshots. The release
binary was driven end-to-end against a mock plane: every verb renders
in both formats with correct exit codes, DELETE is not retried, and
--debug never leaks the token.
Stays implementation: in-progress: the §3 live-plane e2e cannot run
(stagecraft specs 004/005/006 are all implementation: pending, so no
plane serves these services yet). Dated Status note added (§6).
Includes the regenerated .derived/ shards.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Spec 004: the CLI face becomes useful. The stub tenants/stamp/fleet
commands from spec 002 become real calls to the Stagecraft control
plane over the spec 003 API client, each with a stable JSON envelope
the MCP face (spec 005) will reuse.
What landed
stagecraft_cli::verbsmodule (spec 004'sestablishessymbol): tenants (list/show/install-url, with
--open), stamp(new/status, with
--watch), fleet (list/deploy/update/backup/remove). Human output is aligned tables;
--output jsonemits the{ok, data|error}passthrough envelope, wrapping the plane's payloadverbatim so the contract stays stable as fields grow.
apimodule): GET/POST/DELETE returning
serde_json::Value, a body-carrying request path,an empty 2xx decoding to JSON null, GET-only retry unchanged. The
typed
whoamipath routes through the same pipeline.--postureis required with nodefault (the platform rejects defaulted postures);
fleet removecarries the confirm name in the DELETE body, with no
--force/--yesshortcut.
plane" (exit 1, not a crash), per spec 004 §1.
{ok:false,error}on stdout via a newAppError::Rendered(exit 1, no doubled stderr line). A stamp orfleet record with no readable
id/statusis a decode error, not ablank (§5.3), so
--watchcannot spin on a statusless response.Design decisions (spec 004 §5, 2026-07-15 amendment)
Endpoint shapes are grounded in stagecraft specs 004/005/006 and
inlined into §5.1 as the CLI's expectation. One endpoint is
CLI-inlined:
fleet listmaps to GET/tenants/:id/fleet, becauseplatform spec 006 defines deploy/status/update/backup/remove but no
list; the CLI adds the symmetric collection GET (mirroring
GET /tenants/:id/stamps). §1 governs if a live plane diverges.Testing
72 tests (60 unit + 12 integration): per-verb happy path, api-error
surfacing, missing-service 404 mapping, watch-mode terminal states, the
statusless-response guard, and a JSON envelope snapshot per verb family.
The release binary was driven end-to-end against a mock plane: every
verb renders correctly in both formats, exit codes are right, the
non-idempotent DELETE is not retried, and
--debugnever leaks thetoken.
Status: stays
implementation: in-progressThe §3 manual e2e against a live plane cannot run: stagecraft's
tenants/factory/fleet services (its specs 004/005/006) are all
implementation: pending, so no reachable plane serves theseendpoints. All mock-testable acceptance holds; the live check unblocks
when a plane exists. Dated Status note added to the spec (§6).
Spec-Drift-Waiver: this PR implements the spec 004 verb surface but
necessarily edits files under the spec 002 crate scaffold and the spec
003 api module (src/api.rs, src/cli.rs, src/commands.rs, src/error.rs,
src/main.rs, tests/cli.rs). The crate's [package.metadata.spec-spine]
key couples the whole crate to spec 002, and spec 003 owns the API
client this builds on; the new logic lives in the spec-004-owned
stagecraft_cli::verbs module, while the edits to 002/003 territory are
the wiring: command arguments, dispatch, the client's new GET/POST/
DELETE verbs, and the JSON error-envelope exit path. Waiving rather
than editing the completed 002/003 specs, per the precedent set by
spec 003's PR. Approved by the repo owner.