Skip to content

glossary "Record Share" names two recipient kinds that no longer exist (role, group) #80

Closed

Description

@os-zhuang

Found while working #78. Out of that card's scope — it is pre-17.0 vocabulary drift (ADR-0090 / #52 era), not a 17.0 alignment defect — so it is filed rather than folded in.

content/docs/resources/glossary.mdx:180-182:

Record Share

A direct grant of access to a specific record for a specific user /
role / group. Stored as sys_record_share rows. Different from
sharing rules (declarative criteria).

Neither role nor group is a recipient kind. ShareRecipientType in packages/spec/src/security/sharing.zod.ts on objectstack@origin/main is exactly:

'user', 'team', 'position', 'unit_and_subordinates', 'business_unit'

and the file records both renames explicitly — role → position (ADR-0090 D3, the whole Roles-and-Profiles-converge-on-Positions change) and, in the schema's own words, "the group / guest recipients (group renamed → team; anonymous access is the public-form grant / share-link surface)".

Cost of leaving it: a reader who takes the glossary at its word authors recipient_type: 'role' or 'group' and it matches nothing. The glossary is also the page a newcomer reads first, so it teaches the retired vocabulary before any other page can correct it.

Suggested fix — one line, plus a check of whether the entry should name the two recipient kinds the sentence omits entirely (unit_and_subordinates, business_unit):

A direct grant of access to a specific record for a specific user, team,
position, or business unit (optionally including its subordinate units).
Stored as `sys_record_share` rows.

Worth grepping the rest of content/docs/ for the same two words while in there — this scan only covered the glossary.

Verified against: packages/spec/src/security/sharing.zod.ts (ShareRecipientType and the removal note above it) on objectstack@origin/main.

Activity

  1. self-assigned this
    on Aug 18, 2026
  2. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    ContributorAuthor

    Claim: PM loop round 6
    Session: session_01CJPxtTxoxTUnjNdTbiEaRa
    Branch: claude/issue-80-record-share-recipient-kinds
    Worktree: objectos-issue-80
    Domain: domain:devx (objectstack-ai/objectstack#9551, Option B). Dispatched autonomously under the maintainer's standing instruction, 原话: 「你自主派发」.
    File surface: content/docs/resources/glossary.mdx (English only)
    Container & model: S, mode:subagent, model: opus
    Clause-②: no
    Serial constraints cleared: batch siblings #84 (configure/runtime.mdx), #87 (operate/observability.mdx, resources/faq.mdx), #88 (quickstart.mdx) — disjoint. ⚠️ #89 also edits this file and is deliberately not dispatched: it is governed by the #79 naming decision and must be executed with it.

    Scope measured rather than assumed

    The card asks for a corpus grep for the same two words. I ran it: no other English page uses role or group as a recipient kind. The other hits are unrelated senses — "recipient" in configure/notifications.mdx, "Record Shares" as a nav label in configure/index.mdx. So the surface really is this one file, and the sweep the card asked for is already done.

    If the dev finds a hit I missed, report it rather than widening — a sibling card owns faq.mdx this round.

    First-touch grading

    Graded → dispatched. The fix is a transcription with a spec-verified answer (ShareRecipientType is exactly user / team / position / unit_and_subordinates / business_unit), so there is no decision in it. Promoted because the glossary is what a newcomer reads first: it teaches the retired vocabulary before any other page gets a chance to correct it, and a reader who authors recipient_type: 'role' matches nothing.


    Generated by Claude Code

  3. added a commit that references this issue on Aug 18, 2026
    b091442
  4. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    ContributorAuthor
    {
      "issue": 80,
      "status": "done",
      "branch": "claude/issue-80-record-share-recipient-kinds",
      "pr": "https://github.com/objectstack-ai/objectos/pull/92",
      "premise_still_valid": true,
      "summary": "The glossary Record Share entry named `role` and `group` (neither is a recipient kind) and omitted three that are, so it named 1 of the 5 real kinds. Rewrote it to name all five values of `ShareRecipientType` as read from `packages/spec/src/security/sharing.zod.ts` on objectstack@origin/main — user, team, position, business_unit, unit_and_subordinates — each paired with its canonical `recipient_type` spelling, and stated that the `sys_record_share.recipient_type` column takes exactly those five. Adjudication 2 (all five vs a readable subset): named ALL FIVE, because the defect being fixed *is* a subset, because the full set already fits one readable clause in house phrasing at `configure/permissions/record-access.mdx:66`, and because the card's stated cost is an author writing `recipient_type: 'role'` — prose without the canonical spellings would still leave them guessing at `unit_and_subordinates`. Nothing was left out, so there is no omission to justify. The duplicated `Console` entry in the same file (#89 / #79) was not touched; locale siblings left alone per AGENTS.md and now report stale, which is by design and non-blocking.",
      "tests": "All verification ran at b091442 = the pushed head (committed first, then verified; `git status` clean apart from build output). `pnpm turbo run type-check --force` → pass, `@objectos/docs:type-check: cache bypass, force executing 4652eecd91dea0a7` (not FULL TURBO), `Types generated successfully`, `1 successful, 1 total, 0 cached`. `pnpm turbo run build --force` → pass, `cache bypass, force executing 50e5be14a3f9c414`, `Compiled successfully in 55s`, `Generating static pages (740/740) in 12.3s`. All three translation checks run: (1) `check-translation-ownership.mjs --actor os-zhuang --files changed.txt` → exit 0, `touches 0 translation artifact(s) and 1 other file(s)` (also reports TRANSLATION_BOT_LOGIN unset, i.e. inert — that is #68, pre-existing); (2) `check-translations.mjs` → exit 0, `✓ translations gate passed`, 79 English pages / 256 translations, and the six `glossary.*.mdx` siblings listed under Stale exactly as designed; (3) `check-translation-output.mjs --self-test` → exit 0, `20 case(s) ... every rule demonstrated able to fail`, plus the PR-scoped `--files` run → exit 0, `blocking on 0 changed translation(s)`, `✓ translation output gate passed (110 pre-existing finding(s) reported)` with zero findings on this page. Rendered HTML read, not just the diff: `apps/docs/.next/server/app/en/docs/resources/glossary.html` shows the entry with all five values as `code` elements, `h3 id=\"record-share\"` and its 3 TOC/anchor references intact, and 0 occurrences of `role` or `group` as a recipient kind on the page. Control-byte self-scan on the edited file: no match. Heavy commands ran under `flock -E 99 -w 540 /tmp/os-heavy-verify.lock` with `NODE_OPTIONS=--max-old-space-size=4096`; no queueing contention.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #91: `content/docs/build/interface/actions.mdx:170` glosses the built-in `share` action as \"Direct share with a user / role\" — the identical retired-vocabulary defect in a second file, i.e. a real hit the pre-dispatch corpus grep missed. Reported, not edited, per the dispatch's do-not-widen clause. #91 also records two weaker adjacent observations for triage rather than filing them separately: `content/docs/index.mdx:43` calls ObjectOS's own model one of \"objects, fields, roles, sharing rules\" (retired `roles` vocabulary, but comparative prose), and `content/docs/configure/permissions/record-access.mdx:15` glosses record shares as \"a specific user, position, or unit (with subordinates)\" — three of five, dropping `team` and plain `business_unit`; no dead spelling, so it is the incomplete-subset pattern, and the same page gets all five right at `:66`."
      ]
    }

    Footer form used: the session-URL form, https://claude.ai/code/session_01CJPxtTxoxTUnjNdTbiEaRa, on both GitHub bodies (PR #92 and issue #91) rather than the bare https://claude.ai/code — the bare form is stripped on every subsequent edit of a PR/issue body. Read back after creation: PR #92's body retains it.


    Generated by Claude Code


    Generated by Claude Code

  5. os-zhuang commented on Aug 18, 2026

    @os-zhuang
    ContributorAuthor

    ACCEPT — #92.

    My scope measurement was wrong, and the dev caught it

    In the claim comment above I wrote: "I ran it: no other English page uses role or group as a recipient kind… the sweep the card asked for is already done." That was false. content/docs/build/interface/actions.mdx:170 glosses the built-in action as:

    | `share` | Direct share with a user / role |
    

    My grep searched for pages containing recipient or Record Share; that page contains neither word, so it never appeared. A sweep is only as wide as its pattern, and I declared one finished on the strength of a pattern narrower than the defect.

    The dispatch's do-not-widen clause is what made the miss cheap: the dev reported it as #91 instead of quietly editing a file outside its declared surface, so the finding landed as a card rather than as a scope argument in review. That clause earned its place here.

    #91 also records two weaker adjacent observations rather than filing them separately, which is the right calibration — index.mdx:43 uses roles in comparative prose, and record-access.mdx:15 names three of five recipient kinds while line 66 of the same page names all five. That second one is worth flagging to whoever takes #91: it is not retired vocabulary, it is a page disagreeing with itself fifty lines apart.

    The adjudication I asked for

    I asked the dev to decide between naming all five kinds or a readable subset, and to justify a subset if it chose one. It named all five, with the strongest available argument: the defect being fixed is itself a subset. Supporting reasons that hold up — the full set fits one clause in house phrasing already used at record-access.mdx:66, and the card's stated cost is an author writing an invalid recipient_type, which prose without the canonical spellings would not prevent (nobody guesses unit_and_subordinates).

    Pairing each kind with its literal value is the part that actually closes the card. The old entry's failure mode was an author copying a word that matches nothing; a reader can now copy the right one.

    Other checks

    Item Reading
    Scope 1 file, +6/−3, Record Share entry only
    Console entry untouched Confirmed — it is #89's, governed by the #79 naming decision, and editing it here would have produced exactly the "Setup … Distinct from Setup" nonsense that card warns about
    CI build and Ownership & freshness both green
    --force cache bypass, force executing on both tasks — real runs
    Anchors h3 id="record-share" and its three references intact
    Freshness Six glossary.* siblings move to stale, gate passes — by design

    Landing

    Direct merge, same recorded deviation as #83/#85/#86: build is required, ci.yml still lacks the merge_group: trigger (#76), #77 is open. Queueing would strand the PR rather than serialize it. Back to the queue once #77 lands.


    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

pm:dispatchedDispatched to a dev agent by /pm-dispatch

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions