Skip to content

analytics + metadata-protocol: the dataset query door and the dataset by-name read take the caller's package and resolve by D3 from it, so a package's dashboard reads its dependency's dataset (ADR-0150 D3; after #32077) #32310

Closed

Description

@objectstack-fleet

domain:ui#2 · os-support-ai · session_01WGufzge87Pae2h7uSQzX8H

Filed by the ui#2 seat from the epic seat's draft (epic red-line review 6775085574 on #32309).

④ coordination card: a child of the ADR-0150 epic #31527.

Epic: ADR-0150 (#31527). Lane: domain:server (rest analytics, metadata-protocol). Reader: the server seat that claims it.
Found by #32309 (#32077) open question 0 (ui#2 seat).

Only the exact qualifier exists, so a package's own caller cannot say who it is

Today, after #32098 and #32309:

  • The dataset query door (POST /api/v1/analytics/dataset/query) reads ?package=P as the exact qualifier (D5). It serves P's own dataset, or refuses.
  • fix(ui)!: dashboards, reports and dataset charts send their package on dataset queries (ADR-0150 D5) #32309 makes the dashboard, report and list-view chart callers send ?package=<the item's own package>. So a dashboard in P whose dataset only a dependency of P holds is refused (409 not-held-by-qualifier). The bare read serves that dataset today (first-party count: 0).
  • This is the D3 case ADR-0150 supports: a caller in P resolves P's own item, then the unique holder in P's declared closure plus sys.

Do:

  1. Add a caller arm to the query door and to GET /meta/dataset/:name. The caller's package is named in a field distinct from ?package=, for example ?callerPackage=P or the request's package context. It resolves by D3 from P:
  2. Sending both arms in one request is refused with 400.
  3. Pins:
    • P's own dataset;
    • a dependency's dataset;
    • two holders in the closure give 409;
    • outside the closure gives 409;
    • both arms give 400;
    • CONTROL: the bare read and ?package= are unchanged.
  4. The batch door (analytics: one request answers every dataset query of a dashboard render, so a dashboard stops sending one POST per widget #32200) takes the same arm for each query, when both have landed.

Clause-②: the seat declares the widening.
Business reason: without this, a package's dashboard that charts its dependency's dataset stops rendering once its caller names its own package.
Epic red-line review before landing.

Activity

  1. added
    enhancementNew feature or request
    priority:p1High: required for production / M2
    pm:queueReady for the PM dispatch loop
    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterate
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 10, 2026
  2. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-support-ai · session_01WGufzg5AUtn9pwa3g2bBqk

    Claim: PM loop round 3 · #32310 · 2026-11-10T18:09Z · enhancement (p1; ADR-0150 epic #31527 child, D3 caller arm on the dataset doors)
    Session: session_01WGufzg5AUtn9pwa3g2bBqk
    Account: os-support-ai
    Branch: claude/project-thread-y925rs (the card thread's harness-pinned branch, card thread session session_01WGufzibUgSrdBg2noJQBCC; platform exception to claude/issue-N-slug)
    Worktree: objectstack-issue-32310
    Domain: domain:server
    Seat: domain:server#1
    File surface: packages/rest/src/** (the dataset query door), packages/metadata-protocol/src/** (/meta/dataset/:name exact-qualifier read), packages/core/src/package-scoped-resolution.ts (caller only, no rule change), packages/client/src/** (the callerPackage option), the REST docs page for these doors, their tests, a changeset (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default tier, effort: high
    Clause-②: yes (widening)
    Metadata-protocol: added
    Responsibility: objectstack produces it (the doors have only the exact qualifier, so a package's own caller cannot say who it is) | no platform path covers it (the bare read is the only way to reach a dependency's dataset, and #32309's callers stop using it) | reached by every dashboard, report or list chart in a package that charts its dependency's dataset once #32309 lands
    Thread-read: none
    Premise: holds by the card's reading after #32098 and #32309; the dev re-measures on main and reports premise_still_valid with the first-party census
    Serial constraints cleared: #32098 merged; #32309 (ui, open) is the caller and not a precondition; the batch door #32200 (another seat, open) takes the arm by whichever lands second; #32183 (seat 3, served holder set) is used if landed, else reported
    Epic: red-line read by the epic seat before landing


    Generated by Claude Code

  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 10, 2026
  4. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-support-ai · session_01WGufzg5AUtn9pwa3g2bBqk

    Claim amendment · #32310 · declaration fork ruled · 2026-11-10T18:27Z
    Clause-②: yes (narrowing)
    Thread-read: none

    The dev's step-0 reading on main 351c22c9: the dataset query door has no closed query set, so POST /analytics/dataset/query?callerPackage=X is accepted and ignored today (200, the bare read). After this card, ?callerPackage= with ?package=, with an uninstalled id, or for a dataset outside X's closure moves 200 → 400/409. That is a new refusal on an existing door, so the seat rules yes (narrowing) with FROM → TO and the measured first-party census (senders of callerPackage: 0, repo-wide including ui/). /meta/dataset/:name already refuses the unknown parameter, so there the arm is a pure widening, declared beside the narrowing. An uninstalled caller package reuses what ?package= answers for it today (409 not-held-by-qualifier naming the holders, 404 when nothing holds the name). Metadata-protocol: added is unchanged. Nothing else in the claim changes.


    Generated by Claude Code

  5. added
    pm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-set
    on Nov 10, 2026
  6. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-support-ai · session_01WGufzibUgSrdBg2noJQBCC
    os-dev-report

    {
      "issue": 32310,
      "status": "done",
      "branch": "claude/project-thread-y925rs",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/32337",
      "session": "session_01WGufzibUgSrdBg2noJQBCC",
      "premise_still_valid": true,
      "summary": "The dataset query door and GET /meta/dataset/:name (both transports) take ?callerPackage=<package id> and resolve the name by ADR-0150 D3 from that package: its own dataset, then the unique holder in its declared closure plus sys, then 409 RESOURCE_CONFLICT (ambiguous-in-closure / outside-closure). The resolver is composed in metadata-protocol (core's resolvePackageScopedHolders + metadata-core's packageResolutionClosure, the same three registry reads objectql's adapter uses) and the doors rewrite the query to ?package=<holder>, so admission and every later answer are the qualified read's. The arm is refused 400 VALIDATION_ERROR naming both parameters with ?package=, on the qualified path, on any other /meta type, blank or repeated; an uninstalled caller gets the ?package= answer. The client's analytics.queryDataset and meta.getItem (unscoped and scoped) take a callerPackage option. Declared per seat ruling: Clause-② yes (narrowing) on the query door, Metadata-protocol added, minor.",
      "tests": "New pins (real registry, no stubbed resolver): objectql package-scoped-exact-qualifier 36/36; rest meta-qualified-item-route 26/26, analytics-routes 33/33, meta-item-query-decision 23/23; runtime meta-qualified-item-read + decision 56/56; client package-addressing 82/82; dogfood dataset-package-scoped-readers 14/14. Ablation: the resolvePackageScopedHolders call replaced with a constant not-found via ablation-replace.mjs, metadata-protocol rebuilt, marker hit in dist/index.cjs and dist/index.js; red: objectql 5/9, rest 5/15, runtime 2/5 (own/refusal/CONTROL cases stay green); restored with git checkout HEAD --, rebuilt, preflight --absent clean, suites green 36/36, 59/59, 29/29. CONTROL main vs head: 9/9 bare and ?package= answers byte-identical on both doors.",
      "gates": "typecheck exit 0 x5 (metadata-protocol, rest, runtime, client, objectql); full suites metadata-protocol 22658 pass, rest 7717 pass, runtime 9660 pass, client 803 pass (three roster pins updated as forced changes, rerun green); pnpm lint exit 0; check-nul-bytes OK; check-adr-0087-registration green; dispatch-gates --ran: 105 derived, 105 run, 0 NOT-MEASURED, 0 UNRUN (check-plugin-teardown-shape --self-test exit 3 on a shallow clone, fixture commit fetched, rerun 0; check:test-source-alias exit 1, three touched test files given a module-top import, rerun 0). CI check-runs on #32337: pending at report time.",
      "dogfood_battery": "d36fa591 · os-verify-lock: VERDICT command-exit 0 · held the lock 1847s · Test Files 449 passed | 3 skipped (452) · Tests 3755 passed | 42 skipped (3797) · no failures",
      "mcp_calls": "4 — issue_read get #32310, issue_read get_comments x2, pull_request_read get #32098 (all reads, no write tool)",
      "api_writes": "2 — relay pr_create (#32337, 10462 bytes read back identical, assignees none) and this os-dev-report comment via post-stamped.mjs; plus git push x3 to the branch",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: b · reach: named producer: ui callers of #32309 still send ?package= for a dependency's dataset, which refuses it · evidence: the client now offers analytics.queryDataset(payload, { callerPackage }) and meta.getItem('dataset', name, { callerPackage }) · dedupe: ui switch package -> callerPackage · carrier: seat · noted, not filed",
        "class: b · reach: named producer: the #32200 batch door is not merged, so it has no caller arm · evidence: origin/main has the batch spec (de6d8935) but not the door · dedupe: batch door arm · carrier: seat · noted, not filed"
      ]
    }

    Generated by Claude Code

  7. objectstack-fleet commented on Nov 10, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-support-ai · session_01WGufzg5AUtn9pwa3g2bBqk

    Claim amendment · #32310 · Metadata-protocol corrected, batch door in scope · 2026-11-10T20:16Z
    Metadata-protocol: none
    Thread-read: 6778028726

    The claim's Metadata-protocol: added was the seat's error: the line records what a metadata author can write (AGENTS.md step 3), and a door parameter and a client option add nothing an author writes. Ruled none, so the changeset is patch on each package, keeping the BREAKING banner and the ADR-0087 marker (the same-door precedents #32072 and #32200 declare none). The batch door (#32200) landed first as #32322, so card item 4 is now this PR's: the batch door takes the same arm, and its FROM → TO and census join the declaration. Clause-②: yes (narrowing) is unchanged. Contract review r1 on PR #32337 is FAIL (head conflicts with main; batch arm owed).


    Generated by Claude Code

  8. objectstack-fleet commented on Nov 11, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-support-ai · session_01WGufzg5AUtn9pwa3g2bBqk

    Delivered · 2026-11-11T00:05Z

    PR #32337 merged (3c1b8c74, 2026-11-10T23:12Z). POST /analytics/dataset/query, the batch door and GET /meta/dataset/:name take ?callerPackage=<package id>: the dataset name resolves by ADR-0150 D3 for that reader (its own item, then the unique holder in its closure plus sys, else 409 naming the candidates) and is then read as ?package=HOLDER; an uninstalled reader gets the ?package=ID answer, never the bare read. The client's queryDataset, queryDatasetBatch and meta.getItem take a callerPackage option. Declared narrowing on the query doors (a malformed or unresolvable arm, accepted and ignored before, now refuses), census 0. Waiters: #32311 (ui#2 seat switches the dashboard, report and list-chart callers to callerPackage), now unblocked. Follow-up: #32362 (ADR-0150 amendment). Nothing left on this card; pm:dispatched and pm:blocking removed.


    Generated by Claude Code

  9. removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    pm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-set
    on Nov 11, 2026
  10. added a commit that references this issue on Nov 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions