fix(config): reject a config body that forgot the parameters wrapper (#605) - #614
Merged
Conversation
…605) A component's configurationSchema describes the CONTENTS of configuration.parameters, so a flattened body -- the component's own fields at the configuration root -- matched it, validated ok, and was POSTed verbatim, creating a live configuration with no parameters key while --push reported success. A body with no parameters key is now validated as an EMPTY parameters section, and the whole-body exemption is keyed on the component (flows) rather than on the body's shape.
padak
added a commit
that referenced
this pull request
Aug 20, 2026
…85.1 The version bump to 0.86.0 already landed on main (#615), but the release notes it produces were incomplete in two ways. Missing entries. PR #616 (`token list`, plus the retry-policy and exceptionId changes) carried no changelog note at all -- its commit message says "No version bump: this lands in a stack of PRs released as one version. The (since v0.86.0) doc tags assume 0.86.0 and the bump PR must confirm that", and the bump PR did not. `make changelog-check` cannot catch this: it verifies every published GitHub release has an entry, not that every merged PR has a note. #556/#606 (merge-request endpoints, Layer 3) and #610 (winget job disabled) were likewise unannounced. All four are added. Phantom 0.85.1. pyproject went 0.85.0 -> 0.85.1 (#614) -> 0.86.0 (#615) without a tag in between, so 0.85.1 exists only as a changelog bucket -- no release, no artifact, nobody running it. `format_whats_new` shows the notes of the *target* version only, so every user upgrading 0.85.0 -> 0.86.0 would have silently missed those four fixes (Azure ciphertext prefix, the `parameters` wrapper, GCP/Azure sync ciphertext, the encrypt-values docs). The bucket is folded into 0.86.0 verbatim. The same phantom leaked into the agent-facing version gates, which is the worse half: `keboola-expert.md` told users to "upgrade to 0.85.1+" and four gotchas.md entries were tagged `(since v0.85.1)` -- a version nobody can install. Retagged to 0.86.0, along with two source comments. Three of the new notes had to lead with a shorter sentence to satisfy `test_newest_release_notes_are_not_truncated` (the headline is the note's first sentence, capped at 160 chars). No behaviour change; documentation and release metadata only.
padak
added a commit
that referenced
this pull request
Aug 20, 2026
…85.1 (#619) * chore(release): complete the 0.86.0 changelog and drop the phantom 0.85.1 The version bump to 0.86.0 already landed on main (#615), but the release notes it produces were incomplete in two ways. Missing entries. PR #616 (`token list`, plus the retry-policy and exceptionId changes) carried no changelog note at all -- its commit message says "No version bump: this lands in a stack of PRs released as one version. The (since v0.86.0) doc tags assume 0.86.0 and the bump PR must confirm that", and the bump PR did not. `make changelog-check` cannot catch this: it verifies every published GitHub release has an entry, not that every merged PR has a note. #556/#606 (merge-request endpoints, Layer 3) and #610 (winget job disabled) were likewise unannounced. All four are added. Phantom 0.85.1. pyproject went 0.85.0 -> 0.85.1 (#614) -> 0.86.0 (#615) without a tag in between, so 0.85.1 exists only as a changelog bucket -- no release, no artifact, nobody running it. `format_whats_new` shows the notes of the *target* version only, so every user upgrading 0.85.0 -> 0.86.0 would have silently missed those four fixes (Azure ciphertext prefix, the `parameters` wrapper, GCP/Azure sync ciphertext, the encrypt-values docs). The bucket is folded into 0.86.0 verbatim. The same phantom leaked into the agent-facing version gates, which is the worse half: `keboola-expert.md` told users to "upgrade to 0.85.1+" and four gotchas.md entries were tagged `(since v0.85.1)` -- a version nobody can install. Retagged to 0.86.0, along with two source comments. Three of the new notes had to lead with a shorter sentence to satisfy `test_newest_release_notes_are_not_truncated` (the headline is the note's first sentence, capped at 160 chars). No behaviour change; documentation and release metadata only. * fix(changelog): correct the serve route and give every 0.86.0 note a recognised prefix Two findings from Devin's review of this PR, plus one they could not see. The serve route for `token list` was cited as `GET /tokens/{project}`. Both halves are wrong: the router carries `prefix="/token"` (singular) and the operation is registered at `/{project}/list`, so the real path is `GET /token/{project}/list` -- confirmed against the runtime OpenAPI schema, not the source, because that is what a caller actually hits. Worth noting the review's proposed correction (`/tokens/{project}/list`) is itself wrong on the prefix; taking it verbatim would have swapped one 404 for another. `CI:` is not a recognised note prefix. `_PREFIX_STYLES` / `_PREFIX_RE` in commands/changelog.py define the set, the module docstring states the contract, and an unrecognised label renders unhighlighted. Retitled to `Note:`, which also reads better: the winget job being disabled has a user-facing consequence (WinGet users stay on the last published version), so burying it under a dim `Internal:` would understate it. The finding Devin could not report: the four notification notes carried by #615/#618 have no prefix at all. They were outside this PR's diff, so no reviewer looking at the diff would flag them -- but they ship in the same release block and break the same contract, leaving half of v0.86.0 rendering flat. Prefixed `New:` / `Note:` with no change of meaning. Every 0.86.0 note now matches `_PREFIX_RE`, verified by asserting over the live CHANGELOG rather than by reading. Each replacement is written to disk on its own. Running several in one script means a later failed assert discards the earlier successful writes, which is precisely how #618's stale "server-side ?event=" claim survived its own fix pass.
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.
Closes #605.
The half of #587 that #589 left standing
A component's
configurationSchemadescribes the CONTENTS ofconfiguration.parameters, while the Storage API POST takes the wholeconfigurationobject. #589 (v0.84.1) fixed one direction of that mismatch: a correctly nested body is no longer rejected. The other direction survived and is what #605 reports.A flattened body -- the component's own fields sitting at the configuration root, no
parameterswrapper -- matched the parameters-level schema, validatedok, and was POSTed verbatim. The result was a live configuration with noparameterskey at all, which the Keboola UI and the component runtime both read as empty (blank boilerplate), while--pushreported success and returned a config id. Silent shape corruption: nothing errors, andconfig detaillooks superficially fine.Reproduced on
mainbefore touching anything:Root cause
The whole-body exemption was decided from the body itself:
"Body carries no
parameterskey" was read as "this is a flow-style config whose root IS what the schema describes". But that condition is also exactly what a forgotten wrapper looks like, so the one thing under test was also the thing granting itself the exemption -- the two cases are indistinguishable from the body alone.Fix
Decide the exemption from the COMPONENT and the SCHEMA, never from the body:
parameterskey is validated as an empty parameters section, so the schema's required fields fail it.ROOT_LEVEL_CONFIG_COMPONENTS(new, inconstants.py) carries the genuine root-level cases:keboola.flow/keboola.orchestrator, whosephases/tasksreally are the configuration root.hint:line when the wrapper is missing -- otherwise the report reads'db' is a required propertyto a caller who DID supplydb, one level too high.--no-validatefor a component that legitimately takes a root-level body.The POSTed body is still never altered -- unwrapping affects validation only, so
runtime/storage/authorizationsiblings survive (the data loss that prompted #587).Tests
New
TestMissingParametersWrapper(4 cases), written failing-first -- 3 of the 4 failed before the fix:ConfigErrorand nothing reaches the Storage APIparametersagainst a schema with no required fields still creates (guards against inventing a failure for a legitimately storage-only config)tests/test_e2e.py::_test_config_new_push_schema_validationextended with the flattened-body case, mirroring how #589 covered its own fix.Live verification
Against a real stack with a real AI Service schema (project
conditional-flow-testing,keboola.ex-db-mysql), everything--dry-run-- no configuration created:{"parameters": {"db": ...}, "runtime": ...}{"db": ...}(no wrapper)keboola.flow{"phases": [], "tasks": []}Docs / release
gotchas.md: the existing bullet documented this hole as expected behaviour ("a parameters-level body posted by mistake ... still validatesok, because it is indistinguishable from a flow-style config") -- rewritten, taggedsince v0.85.1.make version-syncrun.make checkgreen (lint, format, typecheck, skill, version, command-sync, changelog, error-codes, sentinel-guards, file-size, 5575 tests).Note for the reporter
The first symptom in #605 -- a correctly nested body failing validation with
<root>: 'venv' is a required property-- was #587, fixed in v0.84.1. The report was filed from v0.79.0, so upgrading resolves that half on its own; this PR covers the second half.