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
glossary "Record Share" names two recipient kinds that no longer exist (role, group) #80
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:
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.
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.
{
"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.
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.
Activity
os-zhuang commentedon Aug 18, 2026
Claim: PM loop round 6⚠️ #89 also edits this file and is deliberately not dispatched: it is governed by the #79 naming decision and must be executed with it.
Session:
session_01CJPxtTxoxTUnjNdTbiEaRaBranch:
claude/issue-80-record-share-recipient-kindsWorktree:
objectos-issue-80Domain:
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: opusClause-②: no
Serial constraints cleared: batch siblings #84 (
configure/runtime.mdx), #87 (operate/observability.mdx,resources/faq.mdx), #88 (quickstart.mdx) — disjoint.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
roleorgroupas a recipient kind. The other hits are unrelated senses — "recipient" inconfigure/notifications.mdx, "Record Shares" as a nav label inconfigure/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.mdxthis round.First-touch grading
Graded → dispatched. The fix is a transcription with a spec-verified answer (
ShareRecipientTypeis exactlyuser/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 authorsrecipient_type: 'role'matches nothing.Generated by Claude Code
docs(glossary): name the real Record Share recipient kinds
os-zhuang commentedon Aug 18, 2026
{ "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 barehttps://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
os-zhuang commentedon Aug 18, 2026
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
roleorgroupas a recipient kind… the sweep the card asked for is already done." That was false.content/docs/build/interface/actions.mdx:170glosses the built-in action as:My grep searched for pages containing
recipientorRecord 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:43usesrolesin comparative prose, andrecord-access.mdx:15names 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 invalidrecipient_type, which prose without the canonical spellings would not prevent (nobody guessesunit_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
Consoleentry untouchedbuildandOwnership & freshnessboth green--forcecache bypass, force executingon both tasks — real runsh3 id="record-share"and its three references intactglossary.*siblings move to stale, gate passes — by designLanding
Direct merge, same recorded deviation as #83/#85/#86:
buildis required,ci.ymlstill lacks themerge_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