Split out of #8081 scope item 5 by the domain:services PM seat. ⛔ Not graded by me; no domain:* label — the fix is in packages/spec (⇒ likely domain:spec + fable per the standing contract-change clause), but the routing is triage's call.
Promoted out of #8081's proposal surface because the measurement changed its nature: it was carried there as a theoretical inconsistency, and it is neither theoretical nor an edge case.
Measured — it is the default happy path
POST /api/v1/datasources {name:'good_pg', driver:'postgres',
config:{host,database,username}, secret:'hunter2'} → 201
persisted: {name:'good_pg', config:{…}, origin:'runtime',
external:{credentialsRef:'sys_secret:bound'}} // no schemaMode
computeMetadataDiagnostics('datasource', <that row>)
→ {valid:false, errors:[{path:'external',
message:"'external' settings only apply when schemaMode != 'managed'."}]}
DatasourceSchema.schemaModedefaults to 'managed' (datasource.zod.ts:502), and createDatasource writes external.credentialsRef without consulting schemaMode. So every wizard-created datasource that has a password — the ordinary way an operator adds one — is badged _diagnostics.valid:false in the Studio metadata list right now, and PUT /meta on that row answers 422 for a shape the service itself wrote.
Blast radius, measured: it does not break connect (the connect path never re-parses) and does not break the datasource-admin routes. The damage is the invalid badge plus the PUT /meta 422. So this is a correctness-of-the-contract defect, not an outage — which is why it survived unnoticed.
⚠️ The obvious fix is wider than the problem
#8081 recorded the dev recommendation as "A — allow external.credentialsRef, or a top-level ref, on managed". The implementing dev then corrected that framing, and I think the correction is right:
"Allow external on managed" is wider than the problem. The refinement's intent is sound — federation settings genuinely do not apply to a managed datasource — and blanket-allowing external discards a real check to unblock one key.
The narrow shape: allow credentialsRef (and only it) on managed; keep refusing the federation keys. That preserves the refinement's meaning and matches what the service already writes. ⛔ "A as written" would trade a live check away for free.
The alternative direction — make createDatasource stop writing external.credentialsRef on managed rows and put the ref somewhere else — is not obviously worse, but it moves a shipped persisted shape and every reader of it. Whoever takes this should weigh both rather than inherit the first.
Sequencing — ⚠️ this blocks #8081's migration story
#8081 item 3's migration writes external.credentialsRef onto existing rows. Running it before this is settled would convert every affected managed datasource from "invalid because it holds cleartext" to "invalid because it holds a credentialsRef" — a migration whose entire output lands in the shape that fails re-parse. Settle this first.
Constraints
- ⛔ The
packages/spec change is a contract change; it does not land from the services lane. - ⛔ Do not "fix" it by removing the
schemaMode != 'managed' refinement outright — the federation half of it is doing real work. - The connect path never re-parses, so a fix must not assume re-parse is the enforcement point.
Provenance: #8081 dev report (comment 5269923121, §⑤) and my ACCEPT receipt (#8081 comment 5270102954). Related: #7990, #8078, #8081.
Split out of #8081 scope item 5 by the
domain:servicesPM seat. ⛔ Not graded by me; nodomain:*label — the fix is inpackages/spec(⇒ likelydomain:spec+ fable per the standing contract-change clause), but the routing is triage's call.Promoted out of #8081's proposal surface because the measurement changed its nature: it was carried there as a theoretical inconsistency, and it is neither theoretical nor an edge case.
Measured — it is the default happy path
DatasourceSchema.schemaModedefaults to'managed'(datasource.zod.ts:502), andcreateDatasourcewritesexternal.credentialsRefwithout consultingschemaMode. So every wizard-created datasource that has a password — the ordinary way an operator adds one — is badged_diagnostics.valid:falsein the Studio metadata list right now, andPUT /metaon that row answers 422 for a shape the service itself wrote.Blast radius, measured: it does not break connect (the connect path never re-parses) and does not break the datasource-admin routes. The damage is the invalid badge plus the
PUT /meta422. So this is a correctness-of-the-contract defect, not an outage — which is why it survived unnoticed.#8081 recorded the dev recommendation as "A — allow
external.credentialsRef, or a top-level ref, on managed". The implementing dev then corrected that framing, and I think the correction is right:The narrow shape: allow
credentialsRef(and only it) on managed; keep refusing the federation keys. That preserves the refinement's meaning and matches what the service already writes. ⛔ "A as written" would trade a live check away for free.The alternative direction — make
createDatasourcestop writingexternal.credentialsRefon managed rows and put the ref somewhere else — is not obviously worse, but it moves a shipped persisted shape and every reader of it. Whoever takes this should weigh both rather than inherit the first.Sequencing —⚠️ this blocks #8081's migration story
#8081 item 3's migration writes
external.credentialsRefonto existing rows. Running it before this is settled would convert every affected managed datasource from "invalid because it holds cleartext" to "invalid because it holds acredentialsRef" — a migration whose entire output lands in the shape that fails re-parse. Settle this first.Constraints
packages/specchange is a contract change; it does not land from the services lane.schemaMode != 'managed'refinement outright — the federation half of it is doing real work.Provenance: #8081 dev report (comment 5269923121, §⑤) and my ACCEPT receipt (#8081 comment 5270102954). Related: #7990, #8078, #8081.