Repository navigation
fix(metadata-protocol): publish-drafts widget probes run as the publishing caller - #25533
Conversation
…shing caller The dashboard-widget build probe called queryDataset with no execution context, so read admission refused every dataset over a permission-gated object and a correct widget was reported as an error-tier widget_query_failed. The publish-drafts door now always hands the protocol the caller's execution context, the protocol threads it into the probes, and the widget probe queries (and judges "object has rows") as that caller. Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
…ller Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
… changeset Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
…dicate; pin it Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 31 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 72687f23f33c82863024254c2cf85c0650c1d481 && git checkout 72687f23f33c82863024254c2cf85c0650c1d481
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 74496c64b0637103f87cceec3f37932a48813e65 1bb247c7b6a2a311172b8a2bebd71970571975d8 && git checkout -B drift-repro 74496c64b0637103f87cceec3f37932a48813e65 && git merge --no-ff 1bb247c7b6a2a311172b8a2bebd71970571975d8
node scripts/docs-audit/affected-docs.mjs --json 74496c64b0637103f87cceec3f37932a48813e65
|
…now carry Claude-Session: https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX Co-authored-by: Claude <noreply@anthropic.com>
席位核对ACCEPT ·
Generated by Claude Code |
Requested by Sales · project thread
Fixes #25484
Clause-②: no
Metadata-protocol: none
What was wrong
POST /api/v1/packages/:id/publish-draftsruns the ADR-0038 L3 build probes after it publishes. The dashboard-widget probe calledanalytics.queryDataset(dataset, selection, undefined), with no execution context. The analytics service's object-level read admission refuses a read that has no context. So every widget whose dataset reads a permission-gated object came back as an error-tierwidget_query_failed("[Analytics] Access denied: reading ... is not permitted for this user"), and the fix text told the author to change a dataset that renders correctly.The probe's docblock says it takes "the SAME
queryDatasetpath the dashboard renderer hits". The renderer passes the caller's context, and now the probe does too.The change
packages/runtime/src/domains/packages.ts(the publish-drafts door): the caller'sexecutionContextnow goes topublishPackageDraftson every request. Before, it went only when the request named abackfill. The request type already declaredcontext?: unknown, so no interface changes.packages/metadata-protocol/src/protocol.ts(publishPackageDrafts): passesrequest.contexttorunBuildProbes. One added line plus a docblock sentence.packages/metadata-protocol/src/build-probes.ts:RunBuildProbesOptionsgains an optionalcontext, and the widget probe runsqueryDatasetwith it. The probe never falls back to a system context. A probe that read as the system would report a widget healthy even when its own publisher cannot open it.empty_query("returns NO data although object X has rows") is now judged over the rows that caller can see. Before, a system count sat beside a caller-scoped query, and that would report a row-scoped empty answer as broken. With no context, the plane behaves exactly as before: a context-less query and a system count.What a publisher who cannot read the dataset sees: the same error-tier
widget_query_failed, with the access-denied message. Its severity is unchanged on purpose, because that widget will not render for that caller either. Only the false error caused by the missing context goes away.Tests
packages/runtime/src/domains/packages-publish-drafts-probe-caller-context.test.ts(new) runs the real path:HttpDispatcher.handlePackages→ the realObjectStackProtocolImplementation.publishPackageDrafts→runBuildProbes→ a realAnalyticsServicewhoseadmitObjectReadadmits only the reader on the gated object. The draft store and the phase-1/phase-2 publish seams are the doublespackages/objectql/src/build-probes.test.tsalready uses.widget_query_failed.packages/metadata-protocol/src/build-probes-caller-context.test.ts(new):queryDatasetreceives the caller context, and the rows-exist count runs as that caller. With no context, a context-less query and a system count still produceempty_query.packages-publish-drafts-response-conformance.test.ts: the pin "without the key, the protocol is sent neither a backfill nor a context" now reads "no backfill, but THIS caller's context (not a system one)".scripts/ablation-replace.mjs):queryDataset(dataset, selection, context)was changed to pass an undefined global marker.ablation-dist-preflightfound the marker present in 2 built files ofmetadata-protocol/dist. Both new runtime cases went red. The reader case was the expected red. The non-reader case went red too, but only on its "admission saw this caller" assertion. The issue it pins was still produced, because a context-less read is refused too. Restore: blob back equal to HEAD (d23a96693a7e) andgit diff HEADempty, rebuild,--absentpreflight clean on all 24 built files, and the 2 cases green again.Acceptance notes
widget_query_failedfix text ("Fix the dataset definition ... so the query compiles") also prints for a real access denial, where the dataset is fine and the publisher lacks read access. This change leaves that text alone, as the card scoped it. Recorded, not filed.protocol.tsis being split under [maintainer] refactor(metadata-protocol): splitsrc/protocol.ts(33,065 lines, +10,169 in 30 days) along itsObjectStackProtocolImplementationseams, one move-only slice per PR #25467. The one hunk here sits insidepublishPackageDrafts(around line 27146). The open slice PR refactor(metadata-protocol): move getMetaDiagnostics out of protocol.ts into protocol-meta-diagnostics.ts (#25467 cut 2) #25518 edits lines 301, 2301 and 9254-9450 only, so the two diffs do not overlap.scripts/engine-double-contract.pinned.jsongains the new test's engine double, written bycheck-engine-double-contract.mjs --writeas that gate asked (new pinned coverage).🤖 Generated with Claude Code
https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX