Skip to content

plugin-sharing: the two mounts of the share-link messages door disagree for a signed-in caller of a signed_in conversation link — the plugin mount answers 404, the dispatcher 200 — because the plugin mount passes no signed-in user to resolveToken #23371

Closed

Description

@objectstack-fleet

Filing gate ① a product defect with a named producer, finding class (b), a seam between two mounts of one route family. reach: a named producer, the console. objectstack-ai/objectui ShareDialog offers the signed_in audience (packages/components/src/share/ShareDialog.tsx, about :133), and SharedRecordPage fetches a shared conversation's messages (apps/console/src/pages/SharedRecordPage.tsx, about :149). It was measured in-process on both mounts by #23328's dev (PR #23369; route-level readings are held by the filing seat). Filed by domain:services seat 2 (#21118) · zhuangjianguo · session session_01FzQy26KznSa5j2gnf3AzsE. ⛔ Not a claim; grade and lane are triage's.

Reader who acts: triage grades and routes it, most likely domain:services (plugin-sharing's share-link-routes.ts).

Dedupe words: share link messages signed_in 404; plugin-sharing messages signedInUserId; share link conversation messages mount divergence; shared conversation no messages signed in.

What happens

  • A signed-in caller of a signed_in link to an AI conversation asks for the conversation's messages.
  • The plugin-sharing mount answers 404 NOT_FOUND, and the dispatcher twin answers 200.

The seam

  • Contract: share-link-routes.ts's own presentedPassword doc says the two mounts of the same routes must not answer the same request differently.
  • The plugin mount's messages serve passes no signedInUserId to IShareLinkService.resolveToken, and ignores the context the family door already resolved. So resolveToken refuses the signed_in audience as if the caller were anonymous.
  • The twin passes the signed-in user, and serves.

Ask

The plugin mount's messages serve passes the signed-in user the family door resolved, as the twin does. One test pins both mounts to the same answer for a signed_in conversation link, signed in and anonymous.

Related: #23328 / PR #23369 · #15194.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, bug · priority:p2 · domain:services · area:access, into pm:queue (finding removed). The plugin mount's messages serve passes the signed-in user, as its dispatcher twin does

    Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-14T02:55Z. ⛔ Not a claim, ⛔ not a dispatch.

    • Why p2: a named producer, the console. Its ShareDialog offers signed_in, and SharedRecordPage fetches a shared conversation's messages. On the plugin mount, a signed-in recipient gets 404 where the twin serves 200.
    • Lane: plugin-sharing's share-link-routes.ts is domain:services.
    • Clause ②: no. The mount reaches the answer its own contract (the presentedPassword doc: the two mounts must not differ) and its twin already give. No published accept set changes.
    • Direction: the card's ask. Pass the signed-in user the family door already resolved to resolveToken. ⛔ No second resolution of the session.
      • Pin: both mounts give the same answer for a signed_in conversation link, signed in (200) and anonymous (refused).
  2. added
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    bugSomething isn't working
    and removed on Oct 14, 2026
  3. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Family member folded in: the /messages door forwards neither audience input /resolve honours, an email allowlist included

    domain:services seat 1 (#6021) · marchtian · session_01LxHPTb1LxL7a6WkQbALKnD · 2026-10-14T11:48Z. ⛔ Not a claim, ⛔ no label change. Source: the os-dev report for #23372 (comment 6167578994 on #23372, PR #23471, measured in-process on both mounts' production handlers on SqlDriver).

    This card becomes the family's closing card, so every location of the family goes here and no single-point card is opened. The family is: the share-link /messages serve calls resolveToken without an audience input that /resolve passes.

    Enumeration (the nail; each row is a cell the fix's pin covers):

    Audience input plugin-sharing mount (share-link-routes.ts, /messages) dispatcher twin (runtime/src/domains/share-links.ts, /messages)
    the signed-in user (signed_in audience), this card's original ask not forwarded: 404 where /resolve serves forwarded
    the recipient email (email audience), new not forwarded: 404 where /resolve serves not forwarded: 404 where /resolve serves

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 14, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 · 2026-10-14T23:56Z
    Session: session_017WLmd5in9RXu7AjyHR8fKQ
    Account: os-bill (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-23371-messages-audience-inputs
    Worktree: objectstack-issue-23371
    Domain: domain:services
    Seat: domain:services#1 (seat post #6021)
    File surface: packages/plugins/plugin-sharing/src/share-link-routes.ts (the /:token/messages serve only) and its tests; packages/runtime/src/domains/share-links.ts (the /messages arm's resolveToken call only, for the email row folded at 6167657863) and its tests; one changeset .changeset/23371-*.md. The runtime file is domain:cli's; it is declared to that lane in this act, and its edit is limited to the input forwarding. Stop on breach; explain in the report.
    Container & model: M, mode:subagent, model: default tier (dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"). Two mounts and a parity pin across them, so not the floor.
    Clause-②: no
    Responsibility: plugin-sharing's /messages serve (this lane) passes resolveToken neither the signed-in user nor the recipient email, and the runtime twin passes no recipient email | /resolve on both mounts already forwards both inputs, and share-link-routes.ts's presentedPassword doc says the two mounts must not answer one request differently | a signed-in recipient of a signed_in conversation link opened in the console's SharedRecordPage gets 404 on the plugin mount (measured in-process by #23328's dev); the email row is reached by API and SDK only
    Thread-read: 6167657863
    Serial constraints cleared: #23372 (PR #23471, 0ecac9613) and #23474 (PR #23506) have landed on these files, so the dev works on the merged code; 28e294f45 (#23369) and 0f059078e (#23287) also touched share-link-routes.ts. No open PR touches either file (the open PRs read at this write; #23461 and #23550 list neither). This seat's in-flight claims (#23347, #23354) are disjoint.


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 23371,
    "status": "done",
    "branch": "claude/issue-23371-messages-audience-inputs",
    "pr": "#23572",
    "session": "session_017WLmd5in9RXu7AjyHR8fKQ — the parent PM session's id (in-session subagent)",
    "premise_still_valid": true,
    "summary": "Both /messages call sites now hand resolveToken every audience input their own mount's /resolve hands it, from the same sources: the plugin-sharing mount adds the signed-in user (ctx.userId from the family door, no second session read) and the recipient email (?email=); the runtime dispatcher twin adds the recipient email. Mechanism assumption 1 held on main b572289 (plugin /messages passed only the password; the twin passed the user and the password, no email); /resolve, resolveToken and share-link-service.ts are unchanged. Boundary invariant (assumption 2) measured, not assumed: resolveToken's verdict is binary and its redaction set is the object default plus the link's redact_fields, with no probe input reaching it, so /resolve serves no caller-specific reduced view and no cell opens /messages beyond /resolve; the pin asserts /messages and /resolve on the same mount give one verdict per row. Clause-② copied as dispatched (no); for the fold's email row the supporting reading is that /resolve already serves those callers on both mounts (the twin did not serve them on /messages before either).",
    "tests": "New pin packages/runtime/src/domains/share-links-messages-audience.test.ts: 85 passed (28-row table x 2 mounts, each row asserting /messages AND /resolve status+error.code; 28 cross-mount /messages equality cases; 1 audience-coverage case typed against ShareLinkAudience, reverse-checked once: dropping 'email' from the list gives TS2322 at the assertion). Ablation, each leg via scripts/ablation-replace.mjs (anchor x1 -> x0, blob changed, restored blob == HEAD, git diff HEAD empty), plugin-sharing rebuilt, scripts/ablation-dist-preflight.mjs marker present in dist/index.js + dist/index.mjs: BEFORE (both forwardings reverted) 18 failed / 67 passed = exactly the enumeration's not-forwarded cells (plugin signed_in signed-in 5 rows, plugin email allowlisted 4, twin email allowlisted 4, cross-mount signed_in 5); NEG1 (resolveToken signed_in guard removed) 10 failed, every anonymous signed_in row on both mounts, failure text 'plugin route /messages: {success:true,data:[...]}: expected 200 to be 404'; NEG2 (allowlist check passes everyone) 12 failed, every no-email/off-list email row on both mounts; NEG3 (twin /messages forges a user for an anonymous caller) 10 failed, twin anonymous signed_in rows + their cross-mount equality; RESTORE rebuilt, preflight --absent 'marker absent from all 6 built files' + 'working tree clean', 85 passed. Suites at c127cb8 (pre-merge): runtime vitest --project local 382 files, 5717 passed, 19 skipped (Duration 644.55s); plugin-sharing pnpm test 46 files, 1061 passed; plugin-sharing typecheck exit 0; runtime typecheck exit 0 with check:test-typecheck OK (new file confirmed in the tsconfig.test.json program via --listFilesOnly, no new debt). After merging origin/main 7ffc578 (metadata-protocol + one objectql test only), at 3135008: runtime src/domains/share-links* + domain-floor-anonymous-deny 7 files / 291 passed; plugin-sharing src/share-link* 12 files / 271 passed. Runtime 'repo' vitest project not run locally, declared to CI.",
    "gates": "Local subset at HEAD 3135008: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 68 commands (4-path change set vs merge base 7ffc578); all 68 run, exit captured before any pipe, all exit 0 at final reading. pnpm check:i18n and pnpm check:dual-build-cjs-loads first exited 3 PREREQUISITE NOT MET; re-run after the builds they name (turbo i18n closure FULL TURBO 61/61 cached; full turbo build 76/76, 74 cached): check:i18n 'OK (10 package(s) — all bundles in sync)' exit 0, check:dual-build-cjs-loads exit 0. Slowest: check:query-options-erasure 263s, check:slot-lookup 127s, check-comment-mask-corpus 108s, check:type-check-debt 106s. --ran reconciliation with per-family exit codes: 'Run reconciliation — 68 derived, 68 run, 0 NOT-MEASURED, 0 UNRUN' (a derived zero). Local-only families check:dev-prereqs / check:skill-frame-freshness not owed (no packages/spec or framework-file change). CI check-runs on 3135008 at report time: 'No other open PR may claim the same issue' success; 'Packed-tarball smoke (opt-in)' skipped; in_progress: Check Changeset, Check Documentation Links, Check PR Size, Flag docs affected by code changes, Governed Surface Queue Guard, Lint · CI, release & engine gates, Lint · ESLint, Lint · PM dispatch-gates, Lint · docs & code guards, Lint · repo & tooling gates, Lint · scoped · query-options erasure, Lint · scoped · slot lookup & corpus, Lint · scoped · tooling self-tests, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, The card this PR closes must claim this branch, Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, Type Check · workspace, filter. Test Core / Build Core not yet listed. Not waited on, per contract.",
    "mcp_calls": "0",
    "api_writes": "3 endpoint writes over 2 relay strokes (fleet-write, as objectstack-fleet[bot]): stroke 1 POST /repos/objectstack-ai/objectstack/dispatches -> relay POST /repos/objectstack-ai/objectstack/pulls (draft #23572) + POST /repos//issues/23572/assignees (os-bill), read back 9119 bytes sent = 9119 stored; stroke 2 POST /repos/objectstack-ai/objectstack/dispatches -> relay POST /repos//issues/23371/comments (this report). label-write: zero writes — the dispatch names no label and skip-changeset does not apply (the PR carries a changeset). git push x4 (not REST).",
    "open_questions": [],
    "out_of_scope_findings": [
    "none in class a/b/c. Four observations are in the PR's Acceptance notes, each 承接者:无, noted not filed: /messages returns ai_messages unredacted by the link's redactFields (pre-existing for public/link_only, no contract states it); the console SharedRecordPage renders any non-OK /messages as 'This conversation has no messages yet.' (after this fix reachable only by a revoke/expiry race between its two calls); /resolve and /messages still spell the audience inputs inline per mount (a shared builder would touch /resolve, outside the surface); twin refusal bodies carry error.httpStatus where the plugin mount's do not (pre-existing, every twin refusal)."
    ],
    "line_budget": "n/a — no skills/** or ratcheted ledger touched; diff +457/-2 over 4 files (459 changed lines, under the 3000 human-merge threshold).",
    "deviations": [
    "The full runtime local suite (Duration 644.55s) exceeds the ~10-minute foreground cap, so it ran detached under os-verify-lock (slot dev-23371) and was waited on in the foreground with tail --pid; it held the lock 10m47s.",
    "One plugin-sharing run was spelled 'pnpm --filter @objectstack/plugin-sharing test -- --maxWorkers=2' (the bare -- trap): vitest dropped --maxWorkers; the intended whole-package run happened (46 files, 1061 passed), so the reading stands.",
    "Commits carry the model-free trailer pair per AGENTS.md, not the model-named Co-Authored-By line the harness reminder suggested."
    ],
    "files_changed": [
    ".changeset/23371-share-link-messages-audience-inputs.md",
    "packages/plugins/plugin-sharing/src/share-link-routes.ts",
    "packages/runtime/src/domains/share-links.ts",
    "packages/runtime/src/domains/share-links-messages-audience.test.ts"
    ],
    "cleanup": "root node_modules removed before this comment; the worktree is removed right after it is posted (tree clean, head 3135008 pushed)."
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #23572 comment 6182018833 · domain:services seat 1 · session_017WLmd5in9RXu7AjyHR8fKQ · 2026-10-15T01:07Z. Landing waits on every check green at the then-current head.


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 15, 2026

    @objectstack-fleet
    ContributorAuthor

    Landing note · domain:services seat 1 · os-bill · session_017WLmd5in9RXu7AjyHR8fKQ · 2026-10-15T02:11Z

    Reading PR #23572 (Fixes #23371)
    Queue membership (timeline) added_to_merge_queue 2026-10-15T01:30Z by the fleet relay → merged 2026-10-15T02:10Z
    origin/main by content c980d114d3. runtime/src/domains/share-links.ts forwards signedInUserId, recipientEmail, providedPassword on both arms (2 hits; 1 before the merge). share-link-routes.ts carries the [#23371] /messages block. share-links-messages-audience.test.ts and .changeset/23371-share-link-messages-audience-inputs.md are present

    State change: the card was closed completed by Fixes #23371. This write clears pm:dispatched and the assignee. domain:services, area:access and the type labels stay. The family's two rows (signed_in, email) both landed in this PR; the four Acceptance notes in PR #23572 stay noted, not filed.


    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

No one assigned

    Labels

    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:servicespriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions