fix(config): th config diff/push compares schemas across serializations#102
Merged
Conversation
…zations
`th config diff`/`push` flattened only the local manifest shape
(`{public:[NAMES], secret:[...], featureFlag:[...]}`, SCREAMING_SNAKE). The
config server stores + returns its schema as a JSON-Schema
(`properties.<tier>ConfigSchema.properties.<camelCaseKey>`), which flattened to
an EMPTY map — so every local key looked "added". For the smooai master-org
schema that meant a dry-run reporting 150 phantom additions / 0 removals, i.e.
`push` would have duplicated the entire schema in two casings. The local
`.smooai-config/schema.json` could never be safely pushed.
Fix: `flatten_schema` now parses BOTH serializations and compares keys
canonically (lowercased, non-alphanumerics stripped) so `ANTHROPIC_API_KEY`
(manifest) ≡ `anthropicApiKey` (JSON-Schema). The original display name + tier
are preserved for output. Against the real smooai schema the dry-run now reports
an honest 22 added / 3 removed (genuine local↔remote drift) instead of 150
phantom adds.
Tests: flatten handles the remote JSON-Schema shape; cross-serialization
identical schemas diff empty; a real rename (smooaiOrgBackendKey → smooaiLlmKey)
across serializations reports exactly one add + one remove. 5 diff + 3 flatten
tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
brentrager
added a commit
that referenced
this pull request
Jun 22, 2026
`pushConfigSchemaVersion` persists the POSTed `jsonSchema` verbatim. push was
POSTing the local *manifest* doc (`{public:[SCREAMING_SNAKE], …, types:{…}}`),
which would overwrite the remote's *JSON-Schema* form
(`properties.<tier>ConfigSchema.properties.<camelKey>`) — changing the shape every
consumer of the stored schema reads. So push could never safely carry a real
change, even after the diff was fixed (#102).
Add `manifest_to_json_schema`: converts the manifest to the server's JSON-Schema
shape before POST (camelCase property names from `types`, tier from matching each
key's canonical form against the tier arrays; bare type strings become
`{type: ...}`). Already-JSON-Schema input passes through. Both push paths (update +
create) now POST the converted form.
Tests: produces the server shape; passthrough for JSON-Schema input; and the key
invariant — the converted form flattens to the SAME key→tier map as the manifest
(`compute_diff(manifest, converted)` is empty), so a push is a key-set update, not
a format rewrite. 3 new tests pass (+ the #102 diff tests).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 22, 2026
brentrager
added a commit
that referenced
this pull request
Jul 8, 2026
`pushConfigSchemaVersion` persists the POSTed `jsonSchema` verbatim. push was
POSTing the local *manifest* doc (`{public:[SCREAMING_SNAKE], …, types:{…}}`),
which would overwrite the remote's *JSON-Schema* form
(`properties.<tier>ConfigSchema.properties.<camelKey>`) — changing the shape every
consumer of the stored schema reads. So push could never safely carry a real
change, even after the diff was fixed (#102).
Add `manifest_to_json_schema`: converts the manifest to the server's JSON-Schema
shape before POST (camelCase property names from `types`, tier from matching each
key's canonical form against the tier arrays; bare type strings become
`{type: ...}`). Already-JSON-Schema input passes through. Both push paths (update +
create) now POST the converted form.
Tests: produces the server shape; passthrough for JSON-Schema input; and the key
invariant — the converted form flattens to the SAME key→tier map as the manifest
(`compute_diff(manifest, converted)` is empty), so a push is a key-set update, not
a format rewrite. 3 new tests pass (+ the #102 diff tests).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
The bug
th config diff/th config pushflatten only the local manifest shape —{ public: [NAMES], secret: [...], featureFlag: [...] }withSCREAMING_SNAKEnames. But the config server stores + returns its schema as a JSON-Schema —properties.<tier>ConfigSchema.properties.<camelCaseKey>.flatten_schemareturned an empty map for that shape, so every local key looked "added".For the smooai master-org schema, a
push --dry-runreported 150 phantom additions / 0 removals — a realpushwould have duplicated the entire schema in two casings. The monorepo's.smooai-config/schema.jsoncould never be safely pushed. (Found while renamingsmooaiOrgBackendKey → smooaiLlmKey.)The fix
flatten_schemanow parses both serializations and compares keys canonically (lowercased, non-alphanumerics stripped), soANTHROPIC_API_KEY(manifest) ≡anthropicApiKey(JSON-Schema). Original display name + tier are preserved for output.Before: 150 added / 0 removed (all phantom).
After (same real schema): an honest 22 added / 3 removed — genuine local↔remote drift (config.ts keys never synced, e.g.
SMOOAI_LLM_KEY/MICROSOFT_AUTH/CRAWLER_SERVICE_URL, vs. stalepayload*keys still on the remote).Tests
flatten_schemahandles the remote JSON-Schema shape.Out of scope / follow-up
This makes the diff honest (and
pushcorrectly no-ops when in sync). Separate concern: thepushPOST sends the local manifest doc asjsonSchemawhile the server stores JSON-Schema form — the server ingest contract should be confirmed before relying on real (non-dry-run) pushes that carry changes.🤖 Generated with Claude Code