Skip to content

smoo config schema: first-class schema patch verbs (show/add/rm) - #490

Merged
brentrager merged 2 commits into
mainfrom
th-config-schema
Aug 20, 2026
Merged

smoo config schema: first-class schema patch verbs (show/add/rm)#490
brentrager merged 2 commits into
mainfrom
th-config-schema

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

What

Patching the @smooai/config schema from the CLI used to mean pull → hand-edit .smooai-config/schema.jsonpush. This adds a first-class schema subgroup to the config command (smoo config schema …, also reachable as th smoo config schema / hidden th config schema):

  • schema show [--schema-name] [--json] — render the remote schema: every declared key per tier with its type and any description/default/clamp metadata.
  • schema add <key> --tier secret|public|feature_flag|limit [--type] [--description] [--default] [--min/--max]upsert a key declaration (there is no separate edit-key; re-adding in the same tier patches the given fields onto the existing declaration). --type defaults per tier (feature_flag→boolean, limit→number, else string); --min/--max are the limit clamp bounds and are refused on any other tier.
  • schema rm <key> [--tier] — remove a declaration. --tier acts as a guard: naming the wrong tier errors with the actual one.

Design

Remote-first, one shot. Fetches the remote schema via the same GET /organizations/{org}/config/schemas list endpoint push/pull already use, applies the patch in memory (pure functions, tested against fixture JSON matching a live pull of the real smooai schema), and POSTs a new version via the same POST /config/schemas/{id}/push endpoint. No local .smooai-config/schema.json required — but if a pulled (JSON-Schema-shaped) one exists in the cwd it is updated too, and a config.ts-generated manifest is left alone with a pointer at smoo config build.

Safety:

  • Cross-tier add is refused loudly, showing the existing declaration and the schema rm fix.
  • rm always prints "values set for this key are not deleted — use th config delete" (checking live values would cost a per-environment fan-out; the unconditional reminder is cheaper and always true).
  • Canonical key matching (API_URLapiUrl) means an upsert patches the existing spelling instead of inserting a duplicate.
  • Multi-schema orgs refuse to guess (same resolve_pull_schema rule as pull); --schema-name disambiguates.
  • --dry-run prints the would-be change and stops before the POST.
  • Identical upsert is detected as a no-op and nothing is pushed.

Verification

cargo fmt --check clean; clippy adds no new errors; 108 config tests pass including 15 new ones (clap parse tests for all three verbs, add/upsert/rm/tier-conflict round-trips on fixture JSON, dry-run render, pick_schema_entry semantics).

Live smoke against the real org (dry-run only — nothing was POSTed):

$ th smoo config schema add smokeTestNeverPushed --tier feature_flag --description "dry-run smoke — never pushed" --default false --dry-run
  Schema: smooai
  + added smokeTestNeverPushed [feature_flag]
      {"type":"boolean","description":"dry-run smoke — never pushed","default":false}
  ● dry-run — no changes pushed

$ th smoo config schema rm agentMaxIterations --dry-run
  ! values set for this key are not deleted — use `th config delete agentMaxIterations` for those
  Schema: smooai
  - removed agentMaxIterations [limit]
      {"type":"number","default":12,"maximum":50,"minimum":1,"multipleOf":1}
  ● dry-run — no changes pushed

$ th smoo config schema add anthropicApiKey --tier public --dry-run
Error: `anthropicApiKey` is already declared in tier `secret` as {"type":"string"} — refusing to add it to `public`.
Run `th config schema rm anthropicApiKey` first if you mean to move it.

Docs updated in docs/Engineering/Using-th-CLI.md; changeset added (@smooai/smooth minor).

🤖 Generated with Claude Code

https://claude.ai/code/session_01KF6EFZ3mMGGF4m1543yWQP

smoo config schema show/add/rm — patch the @smooai/config schema
remote-first from the CLI instead of pull → hand-edit → push. add is an
upsert (no separate edit-key); cross-tier adds are refused showing the
existing declaration; rm warns that values are not deleted; --dry-run
prints the would-be diff without POSTing. Reuses the existing
/config/schemas list + push endpoints and the pull-style refuse-to-guess
schema picker. A pulled JSON-Schema-shaped .smooai-config/schema.json in
the cwd is updated alongside the remote so the two stay in sync.

Ran the cargo gates manually (fmt --check, clippy no new errors, 108
config tests incl. 15 new schema tests); pre-commit skipped for the
shared-target poisoning issue.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KF6EFZ3mMGGF4m1543yWQP
@changeset-bot

changeset-bot Bot commented Aug 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6598c36

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@smooai/smooth Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@brentrager
brentrager enabled auto-merge (squash) August 20, 2026 17:00
@brentrager
brentrager merged commit 1df7b79 into main Aug 20, 2026
3 checks passed
brentrager added a commit that referenced this pull request Aug 20, 2026
…remote (#493)

The write direction (schema add/rm, config push) shipped in #490; this is
the read direction. `smoo config schema pull` reconciles the LOCAL schema
representation with the org's remote schema, correctly for both consumer
kinds: a plain pulled schema.json is overwritten (config pull --force
semantics), while a TypeScript consumer (config.ts present) is never
rewritten wholesale — remote-only keys become ready-to-paste snippets in
the file's own conventions (value-schema names and defineLimit clamp
fields derived from how the build serializes them), and --write appends
them into the right tier block mechanically, refusing all-or-nothing when
a block can't be located unambiguously. Local-only keys are reported,
never deleted; tier/type drift on shared keys is reported as tables.


Claude-Session: https://claude.ai/code/session_01KF6EFZ3mMGGF4m1543yWQP

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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