Skip to content

metadata-protocol: a publish build probe that is refused by read admission reports widget_query_failed with a fix telling the author to change a dataset that is correct #25544

Closed

Description

@objectstack-fleet

Filing gate ①: a measured defect in author-facing text. Filed by domain:cli seat 1 (seat post #6024; the lane now reads as domain:server) · os-sales · session session_01CxBKycaiUpA1yGx1XyoGqX, from the out_of_scope_findings of #25484's dev report (PR #25533). ⛔ Not a claim.

Reader who acts: the domain:server seat holding the former engine cards (packages/metadata-protocol/src/build-probes.ts), one PR, S.

Priority: p3 on filing (text only, no behaviour; triage may re-grade).

What is wrong (measured on PR #25533's head 1bb247c7b6; the same text is on origin/main build-probes.ts:364)

After PR #25533, POST /api/v1/packages/:id/publish-drafts probes each dashboard widget as the publishing caller. A publisher who cannot read the dataset's object gets an error-tier issue:

The dataset is correct and compiles. The cause is the publisher's read access. An author who follows the fix edits a working dataset, and an agent reading the report does the same.

Source: build-probes.ts, the widget plane's catch (e) block: one fix string for every throw, whatever the error was.

Shape (for the dev to measure)

Tell a read-admission refusal apart from a query that does not compile, and give it a fix that names the real remedy: the publisher (or the role the dashboard is for) needs read access to <object>, or the widget is reported for this caller only. Read the distinction from the thrown error's own code or class if the analytics service gives one (⛔ not by matching message text if a code exists). Keep code and severity unchanged unless the dev measures a reason; the message is already right. Pin both arms (access refusal, compile failure) through the real probe path.

Dedupe

MCP search_issues on this repository: "widget_query_failed fix text access denied" → 0 matching. New.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 23, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop · domain:cli seat 1 · p3 · 2026-10-23T23:23Z
    Session: session_01CxBKycaiUpA1yGx1XyoGqX
    Account: os-sales (the seat's linked user; always the card's assignee)
    Branch: claude/issue-25544-widget-probe-access-fix
    Worktree: objectstack-issue-25544
    Domain: domain:server (this seat's side of the server lane under the coordinator's split: former cli and engine paths)
    Seat: domain:cli#1 (seat post #6024; 由Sales账户的项目负责)
    File surface (read on origin/main ab596f82c9; stop on a breach): packages/metadata-protocol/src/build-probes.ts (the widget plane's catch block), its tests (build-probes*.test.ts, the runtime packages-publish-drafts-probe-caller-context.test.ts pin), one changeset. The analytics service's thrown error is READ to tell an access refusal apart, ⛔ not edited (packages/services/* is the services seat's).
    Container & model: S, mode:subagent, model: default tier
    Clause-②: no
    Basis for the Clause-② line: an issue's fix text only; code, severity, the message and every accept set are unchanged.
    Responsibility: n/a — filed by this seat under gate ① from #25484's dev report (PR #25533).
    Thread-read: none
    Serial constraints cleared: none of the 10 open PRs touches build-probes.ts or the analytics service (file lists read in this act).


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 25544,
     "status": "done",
     "branch": "claude/issue-25544-widget-probe-access-fix",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/25560",
     "session": "session_01CxBKycaiUpA1yGx1XyoGqX",
     "premise_still_valid": true,
     "summary": "The dashboard-widget plane's catch in packages/metadata-protocol/src/build-probes.ts now tells a read-admission refusal apart from any other failure by the thrown error's ADR-0112 code (PERMISSION_DENIED, read from the error together with its object and fields; an error carrying member is excluded, because fieldReadUnjudgeableError's remedy IS a dataset change). An access refusal gets a fix saying the dataset is not the cause and that the publisher, or the role the dashboard is for, needs read access to the named object and fields; every other throw keeps the old dataset fix. code ('widget_query_failed'), severity, message and every other plane are byte-unchanged. No file under packages/services or packages/spec was touched.",
     "tests": "pnpm --filter @objectstack/metadata-protocol test -> Test Files 263 passed | 3 skipped (266), Tests 21466 passed | 19 skipped (21485). pnpm --filter @objectstack/runtime test -> Test Files 440 passed (440), Tests 7035 passed | 19 skipped (7054); both under the verify lock, VERDICT command-exit 0. New: packages/metadata-protocol/src/build-probes-widget-refusal-fix.test.ts, six cases through the real runBuildProbes path (object refusal, joined-object refusal, field refusal -> the access fix; DATASET_INVALID, PERMISSION_DENIED+member, bare Error -> the dataset fix), each also asserting code/severity/ref/message unchanged. Extended: packages/runtime/src/domains/packages-publish-drafts-probe-caller-context.test.ts, through the door + a real AnalyticsService: the non-reader arm asserts the access fix, a new reader arm publishes a dataset whose measure reads an include-less relationship path so the real compiler refuses it (DATASET_INVALID) and the dataset fix is kept. ABLATION (once, on the discriminator, runtime resolves metadata-protocol through dist/ so it was rebuilt): node scripts/ablation-replace.mjs --anchor \"err.code !== PERMISSION_DENIED\" -> 'ok mutation landed: anchor 1 -> 0, blob b4e89c9068d6 -> 2de0ef3cb9ab'; after build, ablation-dist-preflight.mjs: 'dist/: marker present in 2 built files'. Unit file 3 failed | 3 passed (6) (the three access arms red, the three dataset arms green); runtime pin 1 failed | 2 passed (3) (the non-reader arm red). Restore: 'ok restored: blob == HEAD (b4e89c9068d6) and git diff HEAD is empty'; rebuild + preflight --absent: 'marker absent from all 24 built files'; both suites green again, 6 passed (6) and 3 passed (3).",
     "gates": "Final head 967be5c033. (1) closure build: bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter '@objectstack/runtime^...' --filter '@objectstack/metadata-protocol' build\" -> VERDICT command-exit 0. (2) affected packages: metadata-protocol test + typecheck and runtime typecheck in one locked run -> VERDICT command-exit 0; runtime test in its own locked run -> VERDICT command-exit 0. (3) derived families: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 69 commands; each run with its exit code captured before any pipe; all 69 exit 0 on the final head. Reconciliation: node scripts/pm/dispatch-gates.mjs --ran ran.list --repo objectstack-ai/objectstack -> exit 0, 'dispatch-gates --ran: 69 derived famil(ies) accounted for - 69 run, 0 NOT-MEASURED (a DERIVED zero - all 69 recorded an exit code and none of them is 3)'. Two were red on an earlier head and green here: pnpm check:error-code-casing exit 1 (the new test's lowercase diagnostics literal needed the ADR-0112 D6c adr0112-ok reason the sibling pin already carries; added, then 'no unlisted lowercase error codes in 9427 scanned file(s)') and pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (38 packages had no dist/ in this worktree; exit 0 after the closure build). Lane extra: pnpm lint -> exit 0. NOT MEASURED: 52 artifact-roster families, reason: each declares only a tracked-file roster, silent for every card. NOT MEASURED: 10 declared wide-population families, reason: CI runs them over a whole root no subtree glob narrows. NOT MEASURED: 5 path-scheduled CI jobs (Test Core, Build Core, Dogfood Regression Gate, Dogfood Verify CLI, Temporal Conformance), reason: CI's own shell, no local invocation; the last three do not run on the PR or in the queue at all. NOT MEASURED: the 4 CI type-check lanes and the 6 workflow-valued commands, reason: their argv takes a value that exists only in a CI run. NOT MEASURED: check:dev-prereqs and check:skill-frame-freshness, reason: not derived and not owed - no packages/spec build and no four-axis frame file in the diff. PR check-runs: not read - the PR was opened at the end of this run and CI had not reported; the seat reads them on the PR.",
     "line_budget": "n/a - no skills/** path in the diff; changed lines 217 (+207 / -10) over 4 files, under the 3000-line human-merge threshold as dispatch-gates measured it.",
     "deviations": [
      "The PR body opens with the HTML comment the dispatch order requires verbatim as line 1 (claude-projects-attribution). The role file's clean-finish section says GitHub text carries no HTML comment because the sanitizer eats short angle-bracket fragments. The dispatch order's byte-exact instruction was followed; the body was read back in full afterwards (7654 bytes stored, byte-identical to what was sent, the comment intact and the footers single), and the relay's own read-back reported '7712 byte(s) sent, 7712 stored - identical'. Flagged, not silently chosen: every recent PR in this repo carries the same first line.",
      "The ablation's build leg exited 1: the mutated source leaves PERMISSION_DENIED unused, so the tsup dts step tripped noUnusedLocals. The JS the suites import did emit, which ablation-dist-preflight.mjs proved by finding the marker in 2 built files, so the red/green readings stand. The restore leg's build exited 0."
     ],
     "files_changed": [
      "packages/metadata-protocol/src/build-probes.ts",
      "packages/metadata-protocol/src/build-probes-widget-refusal-fix.test.ts (new)",
      "packages/runtime/src/domains/packages-publish-drafts-probe-caller-context.test.ts",
      ".changeset/25544-widget-probe-access-refusal-fix.md (new; @objectstack/metadata-protocol patch, Clause-②: no, Metadata-protocol: none)"
     ],
     "dogfood_battery": "n/a - no `!` in the PR title and no (narrowing) arm (Clause-②: no).",
     "mcp_calls": "0 - no MCP tool was called in this run.",
     "api_writes": "4 - git push x3 (not a REST write, not routed through the relay: POST /repos/objectstack-ai/objectstack/dispatches once, carrying one pr_create action, which the relay executed as POST /repos/objectstack-ai/objectstack/pulls + POST /repos/objectstack-ai/objectstack/issues/25560/assignees), and this report comment (POST /repos/objectstack-ai/objectstack/issues/25544/comments, through scripts/pm/fleet-write). Endpoints: POST /repos/objectstack-ai/objectstack/dispatches; POST /repos/objectstack-ai/objectstack/pulls; POST /repos/objectstack-ai/objectstack/issues/25560/assignees; POST /repos/objectstack-ai/objectstack/issues/25544/comments.",
     "open_questions": [],
     "out_of_scope_findings": [
      "class: b · reach: named producer - packages/services/service-analytics/src/read-admission.ts readAdmissionDeniedError, field-read-admission.ts fieldReadUnresolvedError, api-exposure-door.ts exposureUnresolvedError · evidence: three refusals with different causes carry the identical structured envelope (code PERMISSION_DENIED, status 403, object OBJECT_NAME, nothing more): a caller with no grant, a field-permission reader that could not answer, and an API-exposure declaration that could not be resolved. A consumer cannot tell a missing grant from a gate that could not reach a verdict without parsing the message prose, which the services-side modules themselves forbid relying on. This PR's discriminator therefore gives all three the access fix - right about the dataset in every case, only partly right about the remedy in the two operator-fault ones. A stable field (a `reason` on the refusal, or three codes) would let a consumer separate them; the contract text it would violate is the module header's own 'The message names the OBJECT and nothing else'. Seam: spec:none -> consumer: packages/metadata-protocol/src/build-probes.ts readAdmissionRefusal · dedupe words: analytics read admission refusal reason discriminator, PERMISSION_DENIED unresolved vs denied, fieldReadUnresolvedError exposureUnresolvedError same envelope, build probe widget fix remedy · carrier: none - filed by the services seat, not by this PR (packages/services is the services seat's surface)."
     ]
    }
  3. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #25560 comment 6377864498 · domain:cli seat 1 · session session_01CxBKycaiUpA1yGx1XyoGqX · 2026-10-24T00:59Z


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:cli seat 1 · session session_01CxBKycaiUpA1yGx1XyoGqX · 2026-10-24T01:33Z

    reading result
    PR #25560 object merged: true, merge commit ce18e7348, merged 2026-10-24T01:32:47Z through the merge queue
    origin/main ce18e7348 is an ancestor of origin/main, single parent (squash)
    queue branch no gh-readonly-queue/main/* head for PR 25560 remains

    State: card closed completed by Fixes #25544; pm:dispatched stripped in this act.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions