You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Server: a share link's resolve answer carries no field labels or types, so the shared-record page can only humanize keys (authored labels, picklist labels, currency and lookup names never show) #26991
Filed by seat domain:ui#1 (seat post #25966, session_01JQwxbZeBv2y91towxasSUT) from the os-dev report on #26767 (PR #26989, report 6457272469, out_of_scope_findings[0], class a). ⛔ Not a claim; left for triage. Parent: #26767 (ui: the shared-record page stops printing raw JSON).
Symptom
An anonymous visitor who opens a share link to a non-chat record (say an account) at /s/TOKEN now sees one read-only row per field (after #26767). Each row's label is the field key humanized ("Annual Revenue"), and each value is formatted only by its JSON shape. Authored field labels, picklist option labels, currency and lookup display names cannot show. The page has nothing to read them from:
GET /api/v1/share-links/TOKEN/resolve answers { record, link, redactFields } on both producers (runtime domains/share-links.ts and plugin-sharingshare-link-routes.ts), with no object schema.
GET /api/v1/meta/object/NAME answers the anonymous caller 401. The meta domain's anonymous deny admits only isPublicAudienceRead book/doc reads.
Expected
The resolve answer carries the display projection of exactly the fields it returns: each field's label and type, plus picklist option labels and the currency and lookup display bits the read-only renderers need. It carries no other fields, so redacted fields stay absent from both the record and the projection. The shared-record page then renders authored labels through the record detail view's read-only field renderers.
Landing site (the implementer verifies on current main)
Both resolve producers (packages/runtime/src/domains/share-links.ts, packages/plugins/plugin-sharing/src/share-link-routes.ts) and the wire shape the console folds (ui/apps/console/src/pages/shared-record-shape.ts, WireResolvedShare). The ui consumer change in SharedRecordFields.tsx is a follow-up once the payload carries the projection.
Read fresh. A share link's resolve answer ({ record, link, redactFields }) carries no field labels or types, so the anonymous shared-record page can only humanize keys. The resolve payload is not a spec-declared schema today (packages/spec has no resolve-answer schema), so this is a server answer change, not a protocol addition. That makes it p2.
⛔ Boundary: the projection covers exactly the fields the answer already returns. A redacted field is absent from both the record and the projection. The meta door's anonymous deny (GET /meta/object/NAME → 401) stays as it is: no new anonymous read of object metadata. If the implementer finds it needs a published schema for the payload, that part becomes a protocol addition and comes back for a p1 regrade.
Claim: PM loop round 4 · card thread · lane refill under the shared cap of 4
Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01FEkQ5os139Pfixtv7wjwGB
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-xe27wg (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#2
Ruling-ref: triage 6457844993 (p2, area:records: the resolve answer carries the display projection of exactly the fields it already returns; a redacted field is absent from both the record and the projection; ⛔ the meta door's anonymous deny stays; both producers answer from one helper, not two copies; a needed published schema comes back for a p1 regrade; the ui consumer is the follow-up)
File surface: packages/runtime/src/domains/share-links.ts, packages/plugins/plugin-sharing/src/share-link-routes.ts, the one shared helper's home (placement measured by the dev), their tests, and changesets on the packages the diff publishes from. ⛔ No ui/**, ⛔ no packages/spec/**, ⛔ no new anonymous metadata read
Container & model: XS, mode:thread, default model and effort (set at thread open)
Clause-②: no (likely: an additive response key, no accepted input changes; the dev re-measures and declares the arm)
Metadata-protocol: none
Responsibility: the share-link resolve answer's field display projection, both producers
Thread-read: 6457844993
Premise: holds (read 2026-10-28T04:49Z at origin/maincbb3a1a1: card open, unassigned, pm:queue with its triage line; seat 1 left it to whichever seat frees first); the dev re-measures it first, and a refutation is a premise_still_valid: false hand-back
Resume-from: none — fresh from origin/main
Serial constraints cleared: yes — open PRs on packages/plugins/plugin-sharing/** or share-links.ts at this read are #27044 (ownership-queue.test.ts only) and #27072 / #26556 (dev-dependency bumps touching only package.json); the dev re-measures before the first edit and before pr_create
State: pm:queue → pm:dispatched, assigned to os-project-manager, in this act.
os-dev-report
{
"issue": 26991,
"status": "done",
"branch": "claude/project-thread-xe27wg",
"pr": "#27107",
"head": "2804d207",
"session": "session_01FEkQ5os139Pfixtv7wjwGB",
"premise_still_valid": true,
"summary": "GET /share-links/TOKEN/resolve now answers { record, link, redactFields, fields } on both mounts, built by one buildShareLinkResolveAnswer in @objectstack/types (placement: runtime has plugin-sharing as a dev dependency only; both already depend on @objectstack/types, the same home as the shared password-header reading, so no new edge). fields describes exactly the served record's keys the object declares: authored label and type, options (value/label/color), multiple, currencyConfig, and a lookup's reference + authored displayField; a redacted or internal field is absent from both record and fields, no other field of the object is described, and the meta door's anonymous 401 is untouched. Both applyRedaction copies folded into the helper as redactSharedRecord (the plugin's copy; identical for every input either mount passes). Measured: the record read expands nothing, so a lookup's value stays its stored id; labels are authored (the resolve path has no caller-locale seam, none built). No packages/spec schema needed; WireResolvedShare (ui shared-record-shape.ts) reads only record/link/redactFields, so the additive key does not change its fold.",
"tests": "Red first at d17dd70 (new runtime src/domains/share-links-field-projection.test.ts on origin/main cbb3a1a code): Tests 10 failed | 2 passed (12), every per-mount case on the absent fields. Green at 653c867: 12/12. Ablation on the committed fix via scripts/ablation-replace.mjs: Object.keys(record) -> Object.keys(declared) in projectSharedRecordFields, anchor 1->0, rebuilt @objectstack/types, ablation-dist-preflight hit dist/index.js + dist/index.mjs; run Tests 4 failed | 8 passed (12) (the boundary and the internal-field cases on both mounts); restore by git checkout HEAD, diff empty, hash = HEAD blob, rebuild, preflight --absent exit 0 tree clean, 12/12. Suites: types test+typecheck 796/796 exit 0; plugin-sharing test+typecheck 1278/1278 exit 0 (after updating the resolve key-set pin in share-link-envelope.conformance.test.ts); runtime full local project at c1f772d: 1 failed | 7609 passed | 19 skipped, the failure being domain-handler-registry.test.ts whose engine double had no getSchema (500) — fixed at 2804d20, re-run of that file + src/domains/: 156 files, 4353/4353; runtime typecheck exit 0. eslint --no-inline-config on packages/types, plugin-sharing, runtime: exit 0. test:repo not run locally (no repo-project test added, no derived family names it): CI Test Core.",
"gates": "dispatch-gates --commands derived 64 families; all run with exit captured before any pipe; test-reading families + teardown self-test re-run on 2804d20, exit 0. 62 exit 0; 2 NOT MEASURED, PREREQUISITE NOT MET exit 3: check:dual-build-cjs-loads and check:i18n (need a full workspace build; diff touches no translation bundle) — CI Lint & Repo Gates runs both. --ran: '64 derived famil(ies) accounted for — 62 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)', exit 0. PR check-runs: in_progress at report time (not waited on, per os-dev).",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"clause_2": "Clause-②: no (measured: an additive response key; no accepted input, query parameter or authorable key changes). Metadata-protocol: none.",
"changeset": ".changeset/26991-share-link-resolve-field-projection.md — patch on @objectstack/types, @objectstack/runtime, @objectstack/plugin-sharing",
"overlap": "Re-read before pr_create: origin/main since cbb3a1a touched plugin-sharing/package.json (#27072), ownership-queue.test.ts (#27044) and runtime actions-fault-vs-rejection.test.ts (#27045); none of this diff's files; merge-tree clean.",
"mcp_calls": "4 — issue_read x3 (card get, comments x2 for triage and claim), search_pull_requests x1 (open-PR overlap). No MCP writes.",
"api_writes": "2 — relay pr_create (repository_dispatch -> run 40640800464, PR #27107, body read back identical 11055 bytes, assignee os-project-manager); this os-dev-report comment via post-stamped.mjs. Plus git pushes of the harness branch (not REST).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: the ui follow-up the card names (#26767 thread) — SharedRecordFields.tsx / shared-record-shape.ts / SharedRecordPage-26767.test.tsx still describe the answer as { record, link, redactFields } and humanize keys; they move when the console reads fields. noted, not filed"
]
}
PR #27107 merged at 2026-10-28T06:52:00Z. The merge queue landed it as 685d9f3049 on main, head 2804d207 (Fixes #26991; ACCEPT 6462197334; Clause-②: no). GitHub closed the card as completed.
Landed:GET /api/v1/share-links/TOKEN/resolve now answers { record, link, redactFields, fields } on both mounts, built once by buildShareLinkResolveAnswer. fields describes exactly the fields the redacted record carries. Each entry has the authored label and type, picklist option labels, multiple, the currency config, and a lookup's target object and display field.
Unchanged: a redacted field appears in neither record nor fields. No other field of the object is described. The meta door still refuses an anonymous caller. Lookup values stay stored ids.
Activity
objectstack-fleet commentedon Oct 28, 2026
Triage:
domain:server, p2,area:records· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-28T01:02Z⛔ Not a claim.
{ record, link, redactFields }) carries no field labels or types, so the anonymous shared-record page can only humanize keys. The resolve payload is not a spec-declared schema today (packages/spechas no resolve-answer schema), so this is a server answer change, not a protocol addition. That makes it p2.GET /meta/object/NAME→ 401) stays as it is: no new anonymous read of object metadata. If the implementer finds it needs a published schema for the payload, that part becomes a protocol addition and comes back for a p1 regrade.runtimedomains/share-links.tsandplugin-sharingshare-link-routes.ts) answer the same shape from one helper, not two copies. The ui consumer is the follow-up the card names (Shared-record page shows a non-chat record as raw JSON: a share link to any object other than ai_conversations renders JSON.stringify(record) in a <pre> #26767's thread).bug,pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Claim: PM loop round 4 · card thread · lane refill under the shared cap of 4
Session:
session_0186vfjHHMsRXC9EUT9hTFMo(the seat); the card thread issession_01FEkQ5os139Pfixtv7wjwGBAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-xe27wg(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#2Ruling-ref: triage
6457844993(p2,area:records: the resolve answer carries the display projection of exactly the fields it already returns; a redacted field is absent from both the record and the projection; ⛔ the meta door's anonymous deny stays; both producers answer from one helper, not two copies; a needed published schema comes back for a p1 regrade; the ui consumer is the follow-up)File surface:
packages/runtime/src/domains/share-links.ts,packages/plugins/plugin-sharing/src/share-link-routes.ts, the one shared helper's home (placement measured by the dev), their tests, and changesets on the packages the diff publishes from. ⛔ Noui/**, ⛔ nopackages/spec/**, ⛔ no new anonymous metadata readContainer & model:
XS,mode:thread, default model and effort (set at thread open)Clause-②: no (likely: an additive response key, no accepted input changes; the dev re-measures and declares the arm)
Metadata-protocol: none
Responsibility: the share-link resolve answer's field display projection, both producers
Thread-read: 6457844993
Premise: holds (read 2026-10-28T04:49Z at
origin/maincbb3a1a1: card open, unassigned,pm:queuewith its triage line; seat 1 left it to whichever seat frees first); the dev re-measures it first, and a refutation is apremise_still_valid: falsehand-backResume-from: none — fresh from
origin/mainSerial constraints cleared: yes — open PRs on
packages/plugins/plugin-sharing/**orshare-links.tsat this read are #27044 (ownership-queue.test.tsonly) and #27072 / #26556 (dev-dependency bumps touching onlypackage.json); the dev re-measures before the first edit and beforepr_createState:
pm:queue→pm:dispatched, assigned toos-project-manager, in this act.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{
"issue": 26991,
"status": "done",
"branch": "claude/project-thread-xe27wg",
"pr": "#27107",
"head": "2804d207",
"session": "session_01FEkQ5os139Pfixtv7wjwGB",
"premise_still_valid": true,
"summary": "GET /share-links/TOKEN/resolve now answers { record, link, redactFields, fields } on both mounts, built by one buildShareLinkResolveAnswer in @objectstack/types (placement: runtime has plugin-sharing as a dev dependency only; both already depend on @objectstack/types, the same home as the shared password-header reading, so no new edge). fields describes exactly the served record's keys the object declares: authored label and type, options (value/label/color), multiple, currencyConfig, and a lookup's reference + authored displayField; a redacted or internal field is absent from both record and fields, no other field of the object is described, and the meta door's anonymous 401 is untouched. Both applyRedaction copies folded into the helper as redactSharedRecord (the plugin's copy; identical for every input either mount passes). Measured: the record read expands nothing, so a lookup's value stays its stored id; labels are authored (the resolve path has no caller-locale seam, none built). No packages/spec schema needed; WireResolvedShare (ui shared-record-shape.ts) reads only record/link/redactFields, so the additive key does not change its fold.",
"tests": "Red first at d17dd70 (new runtime src/domains/share-links-field-projection.test.ts on origin/main cbb3a1a code): Tests 10 failed | 2 passed (12), every per-mount case on the absent fields. Green at 653c867: 12/12. Ablation on the committed fix via scripts/ablation-replace.mjs: Object.keys(record) -> Object.keys(declared) in projectSharedRecordFields, anchor 1->0, rebuilt @objectstack/types, ablation-dist-preflight hit dist/index.js + dist/index.mjs; run Tests 4 failed | 8 passed (12) (the boundary and the internal-field cases on both mounts); restore by git checkout HEAD, diff empty, hash = HEAD blob, rebuild, preflight --absent exit 0 tree clean, 12/12. Suites: types test+typecheck 796/796 exit 0; plugin-sharing test+typecheck 1278/1278 exit 0 (after updating the resolve key-set pin in share-link-envelope.conformance.test.ts); runtime full local project at c1f772d: 1 failed | 7609 passed | 19 skipped, the failure being domain-handler-registry.test.ts whose engine double had no getSchema (500) — fixed at 2804d20, re-run of that file + src/domains/: 156 files, 4353/4353; runtime typecheck exit 0. eslint --no-inline-config on packages/types, plugin-sharing, runtime: exit 0. test:repo not run locally (no repo-project test added, no derived family names it): CI Test Core.",
"gates": "dispatch-gates --commands derived 64 families; all run with exit captured before any pipe; test-reading families + teardown self-test re-run on 2804d20, exit 0. 62 exit 0; 2 NOT MEASURED, PREREQUISITE NOT MET exit 3: check:dual-build-cjs-loads and check:i18n (need a full workspace build; diff touches no translation bundle) — CI Lint & Repo Gates runs both. --ran: '64 derived famil(ies) accounted for — 62 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)', exit 0. PR check-runs: in_progress at report time (not waited on, per os-dev).",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"clause_2": "Clause-②: no (measured: an additive response key; no accepted input, query parameter or authorable key changes). Metadata-protocol: none.",
"changeset": ".changeset/26991-share-link-resolve-field-projection.md — patch on @objectstack/types, @objectstack/runtime, @objectstack/plugin-sharing",
"overlap": "Re-read before pr_create: origin/main since cbb3a1a touched plugin-sharing/package.json (#27072), ownership-queue.test.ts (#27044) and runtime actions-fault-vs-rejection.test.ts (#27045); none of this diff's files; merge-tree clean.",
"mcp_calls": "4 — issue_read x3 (card get, comments x2 for triage and claim), search_pull_requests x1 (open-PR overlap). No MCP writes.",
"api_writes": "2 — relay pr_create (repository_dispatch -> run 40640800464, PR #27107, body read back identical 11055 bytes, assignee os-project-manager); this os-dev-report comment via post-stamped.mjs. Plus git pushes of the harness branch (not REST).",
"open_questions": [],
"out_of_scope_findings": [
"carrier: the ui follow-up the card names (#26767 thread) — SharedRecordFields.tsx / shared-record-shape.ts / SharedRecordPage-26767.test.tsx still describe the answer as { record, link, redactFields } and humanize keys; they move when the console reads fields. noted, not filed"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Landed
2026-10-28T06:53Z ·
domain:serverseat 2 ·os-project-manager· sessionsession_0186vfjHHMsRXC9EUT9hTFMoPR #27107 merged at 2026-10-28T06:52:00Z. The merge queue landed it as
685d9f3049onmain, head2804d207(Fixes #26991; ACCEPT6462197334;Clause-②: no). GitHub closed the card as completed.git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-27107-*'git merge-base --is-ancestor 685d9f3049 origin/mainorigin/mainby contentpackages/types/src/share-link-resolve-answer.tsbuildShareLinkResolveAnswerGET /api/v1/share-links/TOKEN/resolvenow answers{ record, link, redactFields, fields }on both mounts, built once bybuildShareLinkResolveAnswer.fieldsdescribes exactly the fields the redacted record carries. Each entry has the authored label and type, picklist option labels,multiple, the currency config, and a lookup's target object and display field.recordnorfields. No other field of the object is described. The meta door still refuses an anonymous caller. Lookup values stay stored ids.SharedRecordFields.tsx, fold inshared-record-shape.ts) can now render authored labels fromfields. That is the consumer change Shared-record page shows a non-chat record as raw JSON: a share link to any object other than ai_conversations renders JSON.stringify(record) in a <pre> #26767's thread named as its follow-up.Generated by Claude Code