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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions