Repository navigation
_diagnostics.valid=true and editable/deletable are asserted for metadata rows no consumer reads — the envelope has no "in effect" axis #8169
Description
Restart-when: closed #7893 — re-read on the merged ref; if inert-but-valid rows remain representable, promote via a maintainer decision card covering this + #7656 + #8154 together
Split from #7893. Filed by the PM on behalf of the dev agent, which measured it but could not file it: GitHub REST returned 403 on every call from its container (git transport works, the API does not). Full text preserved verbatim from its handover; the wording below is the dev's.
Unassigned, finding — grading belongs to the findings-triage round, not to the filer.
The observation
GET /api/v1/meta/field/<object>.<name> on a runtime-created field returns:
{"type":"field","name":"showcase_task.zz_probe",
"item":{"name":"zz_probe","label":"Probe","type":"text",
"_diagnostics":{"valid":true}},
"lock":"none","editable":true,"deletable":true}for a row that reaches no object's fields, and therefore no ObjectQL query, no physical column, and no GET /meta/object/:name.
⛔ valid is correctly computed and should NOT simply be flipped
computeMetadataDiagnostics (packages/metadata-protocol/src/metadata-diagnostics.ts) resolves the type's Zod schema and .safeParse()s the document, so valid: true asserts this document is well-formed — which is true.
The same function deliberately backs both the save path's 422 and the read path's diagnostics, so that — per its own doc comment — "a document's verdict cannot depend on whether it was being saved or being opened." That property is worth preserving, and it is why the fix here is not "make it say false".
The actual defect
Well-formed is the only axis the envelope has, and consumers read it as in-effect.
MetadataValidationResult already models a third state on a different axis: computeMetadataDiagnostics returns undefined when a type has no registered schema, and its contract requires callers to treat that as "no opinion — not as 'valid'". There is no equivalent state for "well-formed but consumed by nothing."
lock / editable / deletable compound it: they are computed elsewhere (not traced in this investigation) and positively advertise the row as a live, editable, deletable item.
Ask
Whichever way #7893 is decided, decide separately what a read-time diagnostic owes a caller about effectivity. If a type can persist rows that no read path consumes, the envelope should either distinguish "well-formed" from "in effect", or state plainly that it does not model effectivity at all.
This is the same false-compliance shape ADR-0049 forbids, one layer down from #7893 — and it survives #7893 either way: under the implement-write-through option it disappears for field but remains for any future fragment type; under the retire-the-channel option the rows already written stay in sys_metadata, still reporting valid: true.
Reproduction
Rebuild the four-arm probe from packages/runtime/src/meta-field-overlay-lock.test.ts's harness — PUT /field/showcase_task.zz_probe, then GET the same path.
body.data.item. The dev's first run asserted against body.item, which returns [], and a defect assertion passed against an empty read. Any arm here needs an anti-vacuity control.
Dedup
Searched open issues for _diagnostics and for valid/effectivity wording before filing; the only hit is #7656, which is a distinct defect in the same envelope and worth reading alongside this one:
- finding: ?preview=draft self-inflicts an invalid diagnostic — the injected _draft key is validated, so the read carries _diagnostics.valid:false #7656 —
_diagnostics.valid:**false**when it should be true (the read injects_draft, then validates it). - this card —
_diagnostics.valid:**true**when nothing consumes the row.
Both say the envelope's verdict is computed over the wrong subject; they point in opposite directions and have different fixes. computeMetadataDiagnostics is fed could easily fix one and deepen the other.
Related
#7893 (parent) · #7656 (same envelope, opposite direction) · #7743 · ADR-0049
Generated by Claude Code
Activity
huangyiirene commentedon Aug 12, 2026
Checked against PR #8179 (#7656), which lands in
computeMetadataDiagnostics— the exact function this card is about. Verdict: it does not deepen this finding, and it slightly strengthens the case for it.I attached a warning to this card an hour ago: "a change to what
computeMetadataDiagnosticsis fed could fix one and deepen the other." #8179 is that change, so here is the check rather than the assumption.What #8179 does: the function previously stripped
_diagnosticsonly, via a private one-key helper that predated_draftjoiningMETADATA_READ_DECORATIONS. It now calls the sharedstripReadDecorations, removing every declared read decoration before the re-parse.Why that does not touch this card:
field, read by name) carries no read decoration other than_diagnosticsitself. Stripping more keys leaves its verdict exactly where it was —valid: true. The reported shape is unchanged._draftbadge #8179 corrects values; it adds no axis.Why it strengthens the case:
#8179 adds a drift guard pinning that every member of
METADATA_READ_DECORATIONSis invisible to the verdict, and an anti-loosening pin keeping the item schema closed against_draft. Together those make the function's contract sharper and more explicit: the verdict is computed purely over the document, deliberately and now test-enforced. That is precisely the premise this card rests on —validis document-scoped by design and correctly computed, so ⛔ the fix here is not to make it returnfalse. After #8179 that design intent is pinned rather than merely commented, which makes "add a separate effectivity signal" the clearly correct shape and "flip the boolean" clearly wrong.Unchanged for whoever grades this card: the ask is still to decide what a read-time diagnostic owes a caller about effectivity, and it still survives #7893 either way. The
lock/editable/deletablehalf is untouched by #8179 and remains untraced.Generated by Claude Code
hotlong commentedon Aug 13, 2026
Finding-grading round: held as
finding, type Task, with a named re-grade trigger. Rationale: the ask — a new "in effect"/effectivity axis on the meta read envelope — is a public-surface expansion, which is maintainer-floor if pursued; but its sharpest motivating instance (a runtime-created field storedvalid=trueyet never folded into the object) is exactly what queued card #7893 exists to fix. Re-grade trigger: when #7893's fix lands, re-read this card on the merged ref — if inert-but-valid rows remain representable after it, this promotes toneeds-user-decisionwith the four-prong block (the #8179 pins have already settled that flippingvalidis the wrong shape); if the write path fix makes the state unreachable, close as superseded. Sequencing note already on record from the seat comment: any implementation lands incomputeMetadataDiagnosticsand must read #8179's diff first. (Process note: this card's body renders complete on GitHub; an API read truncates it at the<name>token — double-read before treating it as sanitizer-damaged.)本评论来自分诊座位 Routine。
Generated by Claude Code
os-zhuang commentedon Aug 13, 2026
Findings-triage round — HELD as
finding. Recording why, so the hold is a decision rather than a card nobody got to.Premise re-checked on
origin/main:deletableis still asserted at 4 sites inprotocol.ts, so the observation stands. It is held anyway, for three reasons that are about shape, not doubt.1. It is a contract-shape question, not a defect. The ask — an "in effect" axis on the
_diagnosticsenvelope — widens a published response surface. Under this repo's mechanical border test that is not a bug fix and not PM-delegable; it needs a maintainer ruling with a four-cornered card face. ⛔ Promoting it topm:queuewould hand a dev a decision it is not allowed to make, and the honest exits (needs_decision) would just spend a dispatch to arrive back here.2. It is one of three cards on one envelope, pointing different directions — and they must be read together.
valid: trueis wrong because nothing consumes the rowvalid: falseis wrong because the read injects_draftand then validates itvalid:falseflips tovalid:trueand destroys [services half of #7990] Close the datasource/connector credential write/read paths: scrubgetDatasource().config, fix the false "credential-stripped" claim, and write the stored-cleartext-rows migration story #8081 item 3's inventory badge⇒⚠️ A change to what
computeMetadataDiagnosticsis fed could fix one of these and deepen another. Ruling them one at a time is how this lane produced three cards from one seam twice already. Whoever writes the decision card should carry all three.3.
valid:trueis correctly computed and must not simply be flipped.computeMetadataDiagnosticsresolves the type's Zod schema andsafeParses the document, sovalidmeans "this isolated document is well-formed" — nothing more. That document-scoping is a deliberate good property: the same function backs the save path's 422 and the read path's diagnostics, so a document's verdict cannot depend on whether it was being saved or opened. The gap is a missing signal, not a wrong value.Release condition: promote when a maintainer decision card covering the envelope as a whole (this + #7656 + #8154's ordering constraint) is written and ruled. ⛔ Do not promote it alone, and ⛔ do not fold it into an unrelated card that happens to touch
metadata-diagnostics.ts.Generated by Claude Code
hotlong commentedon Aug 13, 2026
State-machine migration, maintainer-authorized (2026-08-13, live triage session: 「我授权你先迁标签」; semantics on #8449): graded hold — release condition stands (promote only via a maintainer decision card covering this + #7656 + #8154's ordering constraint together) —
finding→pm:on-hold. Verdict, release condition, domain unchanged.Generated by Claude Code
os-zhuang commentedon Aug 17, 2026
Daily executable-criteria batch (triage seat): the
Restart-when:fired — #7893 closed completed via merged PR #8496 (thefieldruntime-create door was retired). Per the card's own instruction, the firing action is a merged-ref re-read: are inert-but-valid rows still representable through other doors after that retirement?That re-read needs a code pass and is scheduled into the next triage round's first-touch budget (recorded here so the next round executes rather than re-derives). If inert-but-valid rows remain representable, the promotion shape is a maintainer decision card covering this + #7656 + #8154 together, per the card. Hold stands until the re-read lands.
Generated by Claude Code
os-zhuang commentedon Aug 17, 2026
Restart-whenre-read executed (triage seat, merged-ref pass) — verdict: inert-but-valid rows remain representable after #7893 / PR #8496, so per the card's own instruction this promotes to the maintainer decision card covering this + #7656 + #8154 together.pm:on-hold→needs-user-decision.Evidence, all against
origin/main:field's runtime-create door — an accepted PUT never reached the object (#7893) #8496 retired exactly one producing door (field→allowRuntimeCreate: false). The registry still declaresallowRuntimeCreate: trueon 16 types (object, hook, seed, mapping, view, page, dashboard, app, action, report, dataset, flow, external_catalog, translation, email_template, doc —packages/spec/src/kernel/metadata-plugin.zod.ts:653ff).computeMetadataDiagnosticsstill judges the isolated document only, and the fix(metadata-protocol): draft preview no longer reports itself invalid over its own_draftbadge #8179 pins deliberately enforce that — so any stored row whose consumer never reads it still reportsvalid: true, with no effectivity axis.MetadataService.list(), so some consumers never read runtime rows) andfindReferencesToMetaanswers{references: []}for every target type absent fromREFERENCE_PATHS— the "Used by" panel is silent by construction for 20+ declared types #9190 (reference panel blind by construction to 20+ declared types). The class survives the single-door retirement.Decision asked of the maintainer — what a read-time diagnostic owes a caller about effectivity, ruled once for the envelope as a whole (this card + #7656's document-scoping pins + #8154's ordering constraint that diagnostics are computed on the raw stored body BEFORE redaction):
fieldPUT never reaches the object — a runtime-created field is storedvalid=trueand is absent fromfieldsforever #7893 route): no new surface; each remainingallowRuntimeCreate: truetype gets its consumer verified or its door closed.validas document-scoped and say so in docs.Four-prong card face:
field) is already retired; remaining pull is Studio/operator diagnostics fidelity. No user-blocking failure measured today.valid: trueon a row nothing consumes teaches an agent its authoring took effect; A is the loud signal, B removes the trap at the door (louder still), C preserves the trap.Recommendation: Option B (three of four prongs; A wins only on breadth of signal). Standing constraints either way: ⛔ flipping
validis settled wrong (#8179 pins); any implementation lands incomputeMetadataDiagnosticsand must read #8179's diff first; #8154's compute-before-redaction ordering is load-bearing.本评论来自分诊座位 Routine。
Generated by Claude Code
os-zhuang commentedon Aug 17, 2026
Maintainer ruling (2026-08-17, recorded by PM session
session_01NYgmGheCzM6NrHZN436Cxf). The maintainer accepted the decision-inbox report's recommendations in full, verbatim: 「其他接受你的所有建议」. For this card (ruled once for the envelope family: this + #7656 + #8154):Ruled: Option B — no effectivity axis is added to the meta read envelope. The route is continued per-door retirement (ADR-0049 enforce-or-remove, the #7893 pattern).
allowRuntimeCreate: truetype (16 today) gets its consumer verified or its door closed, one card at a time, as the practiced route — no new published envelope field, no declare-and-maintain surface with zero consumers.validis settled wrong (fix(metadata-protocol): draft preview no longer reports itself invalid over its own_draftbadge #8179 pins);computeMetadataDiagnosticsstays document-scoped; [security] GET /api/v1/meta/datasources still serves stored cleartext credentials — the metadata read path has no per-type redaction hook #8154's compute-before-redaction ordering is load-bearing. A short docs sentence statingvalidis document-scoped (the honest half of option C) may ride any nearby docs change.This card's decision is made and its route is the existing practice — nothing further is dispatchable from it. Closing as completed; #7656 / #8154 pins stand as constraints of record.
Generated by Claude Code