Make spacetime describe support human-readable versions - #5947
Merged
krisajenkins merged 3 commits intoSep 22, 2026
Merged
Conversation
spacetime describe print a human-readable schema by defaultspacetime describe support human-readable versions
krisajenkins
force-pushed
the
describe-human-readable
branch
from
September 16, 2026 08:31
3e48393 to
e1ca570
Compare
krisajenkins
marked this pull request as ready for review
September 16, 2026 09:28
krisajenkins
enabled auto-merge
September 16, 2026 09:53
aasoni
reviewed
Sep 21, 2026
aasoni
reviewed
Sep 21, 2026
…uired
flag, and its help text promises that "in the future, omitting this will give
human-readable output". This patch brings that glorious future. 😁
`spacetime describe [db]` now prints the module as readable text, in the
sections Tables, Views, Reducers, Procedures, and Types:
```
page_previews (public)
Columns:
title String primary key
page_id U64
rev_id U64
display_title String
description Option<String>
extract String
thumbnail Option<Thumbnail>
fetched_at Timestamp
Indexes:
page_previews_title_idx_btree btree (title)
```
You can narrow it to every entity of one kind, or to one entity by name. The
entity types are `tables`, `reducers`, `procedures` and `types`, matching the
section headings:
```
$ spacetime describe wikiwatch procedures
fetch_edits(timer: FetchEditsSchedule) [private]
fetch_previews(timer: FetchPreviewsSchedule) [private]
$ spacetime describe wikiwatch types Thumbnail
Thumbnail = { url: String, width: U32, height: U32 }
```
The singular forms (`table person`) still work, since that's what the
JSON-only command accepted. `--format json`, or the existing `--json`
shorthand, prints the raw definitions instead: the whole module, an array for
a listing, or a single def. Text is styled like the migration plan that
`spacetime publish` prints, and is coloured only when stdout is a terminal and
`NO_COLOR` is unset.
The renderer is a new `describe` module in the schema crate, next to the
migration printer whose look it shares.
- `StyledWriter`: the colour scheme, colour/no-colour buffer and indent
helpers move out of `TermColorFormatter` into a `pub(crate)` writer that
both printers use. Migration output is unchanged, and its snapshots pass
untouched.
- `type_name` spells types language-neutrally: `U64`, `Array<T>`,
`Option<T>`, `Timestamp`, and named types by their scoped name joined with
`.`. It reads the "for generate" typespace, which keeps special types and
refs intact.
- Field and variant names come from the case-converted typespace, because
`typespace_for_generate` keeps source names (`imageUrl`, not `image_url`).
Column defaults are formatted through `WithTypespace`, so a sum prints as
`(red = ())` rather than `( = ())`.
- Reducer names arrive already qualified with their submodule
(`lib.end_session`), while table, view, procedure and type names are local
and have the prefix added. Prefixing reducers too would print
`lib.lib.end_session`, and a test pins that.
- Types lists only named types reachable from a column or a signature, and
leaves out table row types, which their table's block already shows.
`types <name>` searches every named type, so a row type seen in a signature
(`FetchEditsSchedule`) can still be looked up.
- Each listing (`describe_tables` and friends) shares its sorted source with
the whole-module renderer, and tests pin each one to its module section.
- CLI: `--format text|json` defaults to `text`, using a `Format` enum now
shared with `sql` and `logs` in `common_args`. `--json` conflicts with an
explicit `--format`. Whole-module JSON is still the raw, unvalidated def,
returned before validation, so a module that fails validation can still be
dumped for debugging. Existing JSON output is byte-for-byte unchanged.
- Tests: insta snapshots of a fixture covering every section, submodules,
indexes, constraints, defaults and schedules. The `describe` smoketest now
checks the text output, exact single-entity output, `--json` against
`--format json`, and the flag conflict.
- Docs: the regenerated CLI reference, the cheat sheet, and the CLI agent
skill, plus its codex-plugin copy, which must match byte for byte.
- Known gap: inside a submodule table, a column whose type is defined in that
submodule prints the bare type name, while Types prefixes it with `lib.`.
# API and ABI breaking changes
None. `spacetime describe <db>` without `--json` used to be an error, so no
existing invocation changes meaning, and JSON output is byte-for-byte
unchanged. `--json` combined with an explicit `--format` is now rejected as a
conflict, but `--format` is new to this command.
# Rollback safety impact
n/a
# Expected complexity level and risk
2. The diff is large, but most of it is the new, self-contained renderer and
its snapshots. The parts that touch existing code are the `StyledWriter`
extraction from the migration printer (its snapshots pass untouched) and
the shared `Format` enum now used by `sql` and `logs`.
# Testing
- [x] Insta snapshots for the renderer: whole module (colour and no colour), a single table, a single reducer.
- [x] Unit tests pinning each listing to its module section, and submodule reducer names not being double-prefixed.
- [x] `describe` smoketest covers text output, exact single-entity output, `--json` vs `--format json`, and the flag conflict.
- [ ] Reviewer: run `spacetime describe` against a real module of your own and check the output reads well.
Review feedback on clockworklabs#5947: the comment on the whole-module JSON branch said the output was the raw def "exactly as before". A reader who comes to this code fresh has no idea what "before" was, so the phrase tells them nothing. The comment now just says what the code does.
…, HTTP routes and environment variables. Review feedback on clockworklabs#5947 pointed out that `describe` predates views, environment variables and HTTP handlers, and was never taught about any of them. The human-readable output from the previous change already had Views and HTTP routes sections, but that was as far as it went. Say a module declares `API_KEY` and `MODE` with `#[spacetimedb::env]`. Run `spacetime describe mydb` and they appear nowhere. Ask for them directly with `spacetime describe mydb env`, or for a view with `spacetime describe mydb views top_player`, and the command refuses: the only entity types it knows are tables, reducers, procedures and types. The only way to see a module's environment declarations was to read the raw `--json` dump. The module output gains an Environment variables section, between HTTP routes and Types. Each declaration is a row with the key, its type, and an `optional` flag, aligned like table columns. The type is `String`, or the allowed values as quoted literals joined by `|`: Environment variables API_KEY String LOG_LEVEL "debug" | "info" optional MODE "development" | "production" Optional is a flag rather than `Option<...>` because the value is always a string; the flag only says it may be absent. Literals are quoted with Rust string escaping, so an empty string or one containing spaces stays readable. Three new entity types can be selected, in text or JSON like the existing ones: - `views [NAME]`, with submodule views qualified as usual (`lib.active`). - `routes [PATH]`. A path can be routed once per method, so naming one shows every matching route. - `env [KEY]`, accepting `environment` as well. `env` matches `ctx.env` and `publish --env-only`. Supporting changes: - `crates/schema`: `describe_view(s)`, `describe_http_route(s)`, `describe_env_var(s)` and `sorted_views`, alongside the existing per-entity renderers. - `From<HttpRouteDef> for RawHttpRouteDefV10`, used for the routes JSON and by the module-def conversion that previously built it inline. - The describe smoketest module now declares a view, an HTTP route and an optional environment variable, and the smoketest checks each new selector in text and JSON, plus that a missing entity is an error. - The `describe_parts` help lists the entity types; the CLI reference and CLI skill docs are updated to match.
krisajenkins
force-pushed
the
describe-human-readable
branch
from
September 22, 2026 08:45
e1ca570 to
7e1f792
Compare
aasoni
approved these changes
Sep 22, 2026
Merged
via the queue into
clockworklabs:master
with commit Sep 22, 2026
398c9eb
66 of 67 checks passed
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.
Run
spacetime describe mydbtoday and it refuses:--jsonis a requiredflag, and its help text promises that "in the future, omitting this will give
human-readable output". This patch brings that glorious future. 😁
spacetime describe [db]now prints the module as readable text, in thesections Tables, Views, Reducers, Procedures, and Types:
You can narrow it to every entity of one kind, or to one entity by name. The
entity types are
tables,reducers,proceduresandtypes, matching thesection headings:
The singular forms (
table person) still work, since that's what theJSON-only command accepted.
--format json, or the existing--jsonshorthand, prints the raw definitions instead: the whole module, an array for
a listing, or a single def. Text is styled like the migration plan that
spacetime publishprints, and is coloured only when stdout is a terminal andNO_COLORis unset.The renderer is a new
describemodule in the schema crate, next to themigration printer whose look it shares.
StyledWriter: the colour scheme, colour/no-colour buffer and indenthelpers move out of
TermColorFormatterinto apub(crate)writer thatboth printers use. Migration output is unchanged, and its snapshots pass
untouched.
type_namespells types language-neutrally:U64,Array<T>,Option<T>,Timestamp, and named types by their scoped name joined with.. It reads the "for generate" typespace, which keeps special types andrefs intact.
typespace_for_generatekeeps source names (imageUrl, notimage_url).Column defaults are formatted through
WithTypespace, so a sum prints as(red = ())rather than( = ()).(
lib.end_session), while table, view, procedure and type names are localand have the prefix added. Prefixing reducers too would print
lib.lib.end_session, and a test pins that.leaves out table row types, which their table's block already shows.
types <name>searches every named type, so a row type seen in a signature(
FetchEditsSchedule) can still be looked up.describe_tablesand friends) shares its sorted source withthe whole-module renderer, and tests pin each one to its module section.
--format text|jsondefaults totext, using aFormatenum nowshared with
sqlandlogsincommon_args.--jsonconflicts with anexplicit
--format. Whole-module JSON is still the raw, unvalidated def,returned before validation, so a module that fails validation can still be
dumped for debugging. Existing JSON output is byte-for-byte unchanged.
indexes, constraints, defaults and schedules. The
describesmoketest nowchecks the text output, exact single-entity output,
--jsonagainst--format json, and the flag conflict.skill, plus its codex-plugin copy, which must match byte for byte.
submodule prints the bare type name, while Types prefixes it with
lib..API and ABI breaking changes
None.
spacetime describe <db>without--jsonused to be an error, so noexisting invocation changes meaning, and JSON output is byte-for-byte
unchanged.
--jsoncombined with an explicit--formatis now rejected as aconflict, but
--formatis new to this command.Rollback safety impact
n/a
Expected complexity level and risk
its snapshots. The parts that touch existing code are the
StyledWriterextraction from the migration printer (its snapshots pass untouched) and
the shared
Formatenum now used bysqlandlogs.Testing
describesmoketest covers text output, exact single-entity output,--jsonvs--format json, and the flag conflict.spacetime describeagainst a real module of your own and check the output reads well.