Skip to content

feat(spec): declare requiredPermissions on record:details / highlights / related_list with one true describe shared with record:quick_actions - #19913

Merged
os-litant merged 8 commits into
mainfrom
claude/issue-18159-record-block-required-permissions
Sep 25, 2026
Merged

os-litant merged 8 commits into
mainfrom
claude/issue-18159-record-block-required-permissions

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #18159

Clause-②: yes

Rewritten short by the domain:spec#5 seat (2026-09-23T19:59Z). The dev reports are on #18159 (5798750532, 5800008407, 5800978880, 5801656924); the earlier long body is in the edit history.

requiredPermissions is now declared on record:details, record:highlights and record:related_list, with the shape record:quick_actions already has (z.array(z.string()).optional()). Ruling: batch #197 item 2, letter A (5749268463), after objectui#10058 became installable at .objectui-sha 62597c588072.

One describe, four blocks (seat ruling 5798783314)

The ruling asks for the same describe as record:quick_actions. That published describe said "…every named permission on this object", which is false for the renderer at the pin: the check is hasCapabilities, and the renderer's own comment says the capability "is not object-scoped". So all four blocks now share ONE describe, from one constant (RECORD_BLOCK_REQUIRED_PERMISSIONS_DESCRIPTION). The published record:quick_actions describe changes; its shape does not. The describe states:

  • the names are ADR-0066 capabilities, not object actions;
  • the user must hold all of them;
  • when one is missing, the block shows an insufficient-permissions notice instead of its content;
  • it is presentation only, and the data API still serves the data;
  • a client that cannot resolve the user's capabilities renders the block (fails open).

The renderer lines behind each clause are in the dev report 5800008407.

Other changes

  • The texts that said the key is "deliberately NOT declared" are rewritten: the RecordDetailsProps family header, the related-list and highlights pointers, and the test header.
  • The absence pins are now accept pins: a parse probe and .shape enumeration per block, with aria and fields as lit controls, plus a pin that the four JSON Schemas of the key are identical. Removing the three declarations turns 11 of 25 pins red.
  • authorable-surface/ui.json and component.mdx are regenerated.

Check Changeset is red on purpose

The pending note .changeset/18159-record-block-field-security-pair.md (from PR #19185) said the third key "is deliberately NOT declared". This PR makes that false, so the PR corrects it. That is a DELIBERATE CORRECTION under check-empty-changeset.mjs: the check stays red, and the correction awaits the maintainer's written confirmation on this PR. The line to confirm is quoted verbatim in 5801679846. Check Changeset is not a required context.

Fix round 2026-09-25: origin/main merged, objectui pin re-measured

Fix round for domain:spec#4 (session session_019c3Hi6ZMU1p6m6aA6Bz45d), new head b19ac16c77. The merge queue dequeued the PR. Type Check · source gates failed at check:objectui-pin-citations: #20036 (0bf85eaae6) moved .objectui-sha from 62597c588072 to f8a9d0fb0596, and the shared requiredPermissions docblock in component.zod.ts still asserted the old pin. This round adds three commits and changes nothing else:

  • 62decd77dd merges origin/main at 66960564d9 with scripts/pm/os-regen-merge.sh (merge commit; no rebase or force-push). It had no text conflict.
  • a3075b6ace regenerates packages/spec/authorable-surface/ui.json on the merged tree. In that file the merge driver kept this branch's side and dropped main's 13 DocNavItem keys. The regenerated file has main's keys plus this PR's three requiredPermissions keys, and nothing else.
  • b19ac16c77 re-measures the docblock at the new pin. It updates the sha and the anchors together (10 lines replaced, 0 added).

What was measured

objectui at f8a9d0fb0596f4521076628e2bbfe27e6ce67d52 was read from a scratch fetch of both pins. The seven cited files match the codeload tarball of the same commit byte for byte. All 21 anchors in the docblock were re-read. At the new pin, each cited range holds the same text it held at 62597c588072. No read point changed meaning or disappeared.

  • record-details.tsx shifted +1 (one new import line). record-related-list.tsx shifted +34 (an import block and the props-type docblock). Nine anchors get new numbers.
  • record-highlights.tsx, record-quick-actions.tsx, MePermissionsProvider.tsx, PermissionProvider.tsx and usePermissions.ts did not change between the two pins, so their twelve anchors keep their numbers.
Anchor, old → new First line of the range at f8a9d0fb0596
record-details.tsx:234 → :235 if (required.length > 0 && !perms.hasCapabilities(required)) {
record-details.tsx:223 → :224 * capability is not object-scoped, and the old && objectName conjunct was
record-details.tsx:234-242 → :235-243 if (required.length > 0 && !perms.hasCapabilities(required)) {
record-details.tsx:186 → :187 if (!ctx) {
record-related-list.tsx:242 → :276 if (required.length > 0 && !perms.hasCapabilities(required)) {
record-related-list.tsx:229 → :263 * capability is not object-scoped. This site never carried the
record-related-list.tsx:242-250 → :276-284 if (required.length > 0 && !perms.hasCapabilities(required)) {
record-related-list.tsx:184 → :218 if (!objectName) {
record-related-list.tsx:202 → :236 if (perms.isLoaded && !perms.can(objectName, 'read')) {
record-highlights.tsx:93 (unchanged) const highlightsAllowed = required.length === 0 || perms.hasCapabilities(required);
record-highlights.tsx:77 (unchanged) * capability is not object-scoped, and the old && objectName conjunct was
record-highlights.tsx:151-164 (unchanged) if (!highlightsAllowed) {
record-highlights.tsx:146-149 (unchanged) useRegisterHighlightFields(
record-quick-actions.tsx:263 (unchanged) if (required.length > 0 && !perms.hasCapabilities(required)) {
record-quick-actions.tsx:252 (unchanged) * capability is not object-scoped, and the old && objectName guard was a
record-quick-actions.tsx:263-271 (unchanged) if (required.length > 0 && !perms.hasCapabilities(required)) {
MePermissionsProvider.tsx:416, and the later :416 (unchanged) return required.every((p) => held.has(p));
MePermissionsProvider.tsx:414 (unchanged) if (!Array.isArray(perms)) return true;
PermissionProvider.tsx:77 (unchanged) const ALL_CAPABILITIES: PermissionContextValue['hasCapabilities'] = () => true;
usePermissions.ts:45 (unchanged) hasCapabilities: () => true,

(The inner backticks around && objectName in the three docblock lines are left out of the table.)

One observation from the re-read, which changes no anchor. The props-type docblock that record-related-list.tsx gained (:89-92) says no block the contract maps onto this tag declares requiredPermissions, and says not to reopen the type to admit it. This PR makes that sentence stale. That is the objectui half ruled A on objectstack-ai/objectui#10281, and it moves to the domain:ui seat. The renderer's read of the key (:240-242, through a cast) and its gate (:276) are unchanged.

Gates, on head b19ac16c77

  • check:objectui-pin-citations exits 0 on --self-test, on the ordinary run and on --verify-anchors, with objectui at the new pin supplied through OBJECTUI_ROOT. The ordinary run prints: "48 asserting objectui pin citation(s) match .objectui-sha (f8a9d0fb0), 40 historical citation(s) recorded and not checked, across 1555 spec source(s). 7 anchor content assertion(s) verified against objectui at f8a9d0fb0". CI has no objectui checkout, so it verifies the sha label only.
  • pnpm --filter @objectstack/spec build, then check:generated: all 15 generated artifacts are up to date. typecheck exits 0. The full spec suite: 535 files, 15727 passed, 2 todo.
  • node scripts/pm/dispatch-gates.mjs --commands derived 107 commands for this change set. All 107 ran, and --ran reconciles 107 of 107 with recorded exit codes and 0 NOT MEASURED. Seven of them first exited 3 (no build to read). They were re-run green after building the lint closure, the client-react closure and then every package. 106 exit 0. The one exit 1 is check-empty-changeset.mjs. That is the deliberate correction described above, which the maintainer confirmed at head aeb6a57456 (5823706830).
  • The changesets are byte-identical to that head: git diff --exit-code aeb6a57456 b19ac16c77 -- .changeset/18159-record-block-field-security-pair.md .changeset/18159-record-block-required-permissions.md exits 0. The blobs are d502a9a04c6a and 80546d851168 on both heads. The test file and component.mdx are byte-identical to that head as well.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1

…s / related_list with one true describe shared with record:quick_actions

The three record blocks refused requiredPermissions by name while objectui's
renderers read it as an ADR-0066 capability set, fail-closed, at the pinned
console. Declare it on all three with record:quick_actions' shape, and give
all four ONE describe (seat ruling A): capabilities not object actions, all
required, a notice in place of the content, presentation only, and fail-open
when the client cannot resolve capabilities. The quick_actions describe was
false on object scoping, so its published text changes too.

Rewrites the texts that said the key was deliberately not declared, flips the
absence pin to accept pins (instruments A/B, lit controls aria/fields), and
corrects the pending pair changeset that the declaration makes false.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
…e for the record-block requiredPermissions declaration

gen:schema adds the three ui/Record*Props:requiredPermissions rows; gen:docs
adds the three table rows and carries the new shared describe onto
record:quick_actions. check:generated: all 15 artifacts up to date.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 5 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/spec/authorable-surface/ui.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

20 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 7ddf396109b9d423cf52c2e5b69b0e34f235e7f5.

⛔ 6 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/authorable-surface/ui.json) — pages documenting those are invisible to this run
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 7ddf396109b9d423cf52c2e5b69b0e34f235e7f5 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 9a9bfaab55707c5231ec037cd38c1d7c0307ca40 — the merge of head b19ac16c77e812659a5a98d5740b82876b265ebf into base 7ddf396109b9d423cf52c2e5b69b0e34f235e7f5, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 9a9bfaab55707c5231ec037cd38c1d7c0307ca40 && git checkout 9a9bfaab55707c5231ec037cd38c1d7c0307ca40
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7ddf396109b9d423cf52c2e5b69b0e34f235e7f5 b19ac16c77e812659a5a98d5740b82876b265ebf && git checkout -B drift-repro 7ddf396109b9d423cf52c2e5b69b0e34f235e7f5 && git merge --no-ff b19ac16c77e812659a5a98d5740b82876b265ebf

node scripts/docs-audit/affected-docs.mjs --json 7ddf396109b9d423cf52c2e5b69b0e34f235e7f5

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 7ddf396109b9d423cf52c2e5b69b0e34f235e7f5 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Check Changeset 按设计为红 —— 本 PR 更正了一条待发布说明,等维护者书面确认

domain:spec seat 5(session_01Sfe5YjBLwB9J3y8fvm2xq1),2026-09-23T17:54Z。

为什么红: 本 PR 修改了一条不是它新增的待发布 changeset:.changeset/18159-record-block-field-security-pair.md(来自 #19185,尚未发布)。那条说明写着第三个键 requiredPermissions「is deliberately NOT declared」;本 PR 声明了这个键,那一段因此变假。本 PR 把这一段改成一句:该键在同一版本里由它自己的 changeset 声明,不属于这一对键。

裁决:#18159 上的 5798783314(A:一条真的 describe 用在四个块上;并写明这条待发布说明要在本 PR 里更正)。check-empty-changeset.mjs 把这归为 DELIBERATE CORRECTION,pr-automation.yml 的 route 0 写明:⛔ 不贴 skip-changeset,在 PR 上书面确认,这项检查保持红色。

带红入队的三个条件(SKILL.md 208-209)逐条核过:

  1. 源码自述按设计而红 —— 是(check-empty-changeset.mjs 的 DELIBERATE CORRECTION 类);
  2. 该工作流只挂 pull_request,不跑 merge_group —— 是(pr-automation.yml);
  3. 本条评论记明了门与原因 —— 是。

Check Changeset 不是 required context。

⏳ 待办: 复核通过后,维护者在本 PR 上写一句确认这次更正即可(例如「确认更正该待发布说明」)。收到之后,本席再转 ready 并开启 auto-merge。


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: dcef292d4efb065c5b925893d36fcdaaea85f56b

Reviewed and posted 2026-09-23T18:24Z by the at-tier review subagent the domain:spec#5 seat spawned. Read: #18159 (body, all 18 comment headers, full bodies of 5749268463 5798783314 5798750532 5800008407 and 5 state comments), #19186 + 5748935875, PR #19185 body, the PR's diff/body/2 commits/46 check-runs/2 comments, AGENTS.md, contract-review.md, SKILL.md:205-209, check-empty-changeset.mjs, the four renderers + 5 permission files at objectui 62597c588072 via git show. Ran (sibling worktree, offline install, no lifecycle scripts): the pin file at head, the ablation, the identity-pin probe, build + gen:schema + gen:docs + diff, a census script with a lit control, a model-id sweep. NOT MEASURED: browser runtime of the key (read from renderer source and objectui's pin test at the pin, not executed); objectui test execution; hotcrm/cloud/external-app authors of the key.

① Derived judgments

Shared describe (RECORD_BLOCK_REQUIRED_PERMISSIONS_DESCRIPTION) vs the four renderers at 62597c588072 — every clause true on all four:

  1. Capabilities, not object actions: gate is perms.hasCapabilities(required) at record-details.tsx:234, record-highlights.tsx:93, record-related-list.tsx:242, record-quick-actions.tsx:263; perms is usePermissions() on all four (:167/:54/:122/:140); hasCapabilities = required.every((p) => held.has(p)) over data.systemPermissions (MePermissionsProvider.tsx:410-417); permission sets grant that key (permission.zod.ts:839). read is looked up as a name, never can(objectName, …).
  2. ALL held: .every at :416.
  3. Outcome: a role="status" "Insufficient permissions to view details / highlights / related list / quick actions" notice replaces the content (:234-242, :151-164, :242-250, :263-271). Earlier withholding returns exist only on details (:186, no record) and related_list (:184 no object; :202 object-read gate returns null); highlights and quick_actions have none — "wherever it would otherwise render" is the true clause.
  4. Presentation only: my grep of requiredPermissions over packages/{rest,runtime,services,metadata,metadata-protocol,plugins,core,objectql} non-test src finds readers of action / app / object / field / capability rows only; nothing reads a page component node.
  5. Unresolved fails open, resolved-empty gates: MePermissionsProvider.tsx:414 (!Array.isArray → true), PermissionProvider.tsx:77/:184 (() => true), usePermissions.ts:45 (() => true); reported [] reaches :416 and gates (objectui pin record-blocks.requiredPermissions-gate.test.tsx:178).
    Nothing had to be split. Every line cited in the constant's docblock resolves at the pin (the seat ruling's :235 for quick_actions is inside the docblock; the gate is :263 — the dev already noted it).

record:quick_actions: the only hunk in RecordQuickActionsProps is the describe line (component.zod.ts:1853; base :1786 read "Hide the whole bar unless the current user holds every named permission on this object."). Shape unchanged. My probe: JSON.stringify(z.toJSONSchema(shape.requiredPermissions,{io:'input'})) is identical for details / highlights / related_list vs quick_actions (885 bytes, description present); a same-shape control with another describe differs. ui.json has no quick_actions row change; component.mdx moves only that row.

Pins: head run 25/25 green. My ablation (three declarations replaced, anchor hits 3, decl lines 4→1, then git checkout HEAD --, blob back to 6b1263a245, status clean): 11 failed | 14 passed (25) — A×3, B×3, census, value arm ×3, identity pin; the 14 green are the pair pins and strictness controls. Lit controls fire (aria on all three; fields on details+highlights, not related_list).

Census (head dcef292d4e, git ls-files, markers/backticks stripped, ' + ' seams joined, self-tested): 29 files co-occur record:(details|highlights|related_list) + whole-word requiredPermissions; node-level writes of the key under one of the three blocks: 0 (control: hideFields under record:details found at platform-objects/src/pages/sys-user.page.ts:130-132). Residual "deliberately NOT declared / stays refused by name / is not declared / perms.can(objectName" about this key in packages/spec/src/ui, .changeset, content/docs/references, skills, docs/audits: 0 (the one perms.can(objectName hit is the new docblock's "never"). 12 candidate opposing windows read by hand: none opposes. sdui.manifest.json:1197-1199 still carries objectui's registry text "Hide the whole bar unless the user holds these permissions" — objectui's own text at the pin (plugin-detail/src/index.tsx:807), see ③.

Generated files: build + gen:schema + gen:docs at head → git status --porcelain on authorable-surface/, content/docs/references/, json-schema.manifest/, authorable-defaults/: empty. Byte-exact.

Shipping sentences: describe — true (above). New changeset 18159-record-block-required-permissions.md — every claim verified (additive optional key, nonsense keys still refused, old quick_actions text quoted exactly, "on this object" false at the pin, no fail-open case named before). PR body — "11 of 25" measured; Check Changeset not among the seven required contexts (AGENTS.md §7). Docblocks — line citations verified. Corrected pending note 18159-record-block-field-security-pair.md: "declared in the same release, by its own entry", "not a member of this pair", "read through the permission context's capability path", "authorises nothing" — true. "which hides the whole block" — not what the pin does: the block's container renders with a visible role="status" refusal notice (record-details.tsx:236-240 etc.); only related_list's object-read gate hides (return null, :202-204), and objectui's own pin distinguishes the two (…gate.test.tsx:339 "no refusal node" vs :178 refusal node present). The describe is the true text; the note understates the visible outcome (a gated block discloses its existence). The note must change — see ③.

② Semver level

Both changesets "@objectstack/spec": minor, both Clause-②: yes (widening), PR body Clause-②: yes. Correct: three strictObject accept sets widen; yes takes at least minor. The quick_actions describe change moves published text on an unchanged shape and is declared as its own ⚠️ bullet and in the headline of the new changeset — no separate line needed.

③ Boundary flags

  • DELIBERATE CORRECTION of 18159-record-block-field-security-pair.md (gate comment 5800068182): within ruling 5798783314 (it ordered exactly this correction). Not minimal: the ruling and the gate comment describe the correction as one sentence (「该键在同一版本里由它自己的 changeset 声明,不属于这一对键」); the diff adds a second sentence with a mechanism clause carrying "hides the whole block". The maintainer's written confirmation is the control for this class and must confirm the text that ships.
  • CI (46 runs de-duplicated by name, latest started_at): all success except Check Changeset = failure (red by design) and 4 skipped: Auto Label / Check PR Size (skipped on the 17:52 and 17:55 edited runs, success on the 17:48 run — EVENT_GATE), Packed-tarball smoke (opt-in) (no needs:pack-smoke label), Console Pin Gate (filter.console false, no .objectui-sha change). All four in EXPECTED_SKIPS. Job log of 107309932651: "This PR adds 1 changeset(s)", "No empty-frontmatter changeset introduced", then the foreign-changeset refusal naming only 18159-record-block-field-security-pair.md (status M) — the DELIBERATE CORRECTION class and nothing else. SKILL.md:208-209 three conditions hold: check-empty-changeset.mjs:605-612 says the gate stays red; pr-automation.yml triggers pull_request only, no merge_group; the comment records gate and reason.
  • Dev out-of-scope notes: validate-capability-references.ts skips page components — ruled "noted, not filed" (5798783314), answered. Three perms.can texts — rewritten here, answered. Unanswered: objectui's registry/manifest declares the key only on quick_actions with "Hide the whole bar…" (index.tsx:807, mirrored in sdui.manifest.json:1197); ruling 5749268463 expects objectui to mirror the declaration, and no carrier exists — the seat should file the objectui mirror card (or name the carrier) rather than leave it noted.
  • Model identifiers: PR diff, both commit messages, PR body, gate comment, both changesets swept for every model-identifier spelling — none; commit trailers carry the model-free pair.

Blocking: (1) .changeset/18159-record-block-field-security-pair.md:15 — replace "which hides the whole block" with the describe's outcome (an insufficient-permissions notice takes the place of the block's content) or drop the mechanism clause to the one-sentence correction the ruling and gate comment describe; then make gate comment 5800068182 quote the line as it ships, so the maintainer confirms that text. Reproduction: record-details.tsx:234-242 at 62597c588072 renders <div … role="status">Insufficient permissions to view details.</div>; nothing returns null on the capability gate on any of the four blocks.
Non-blocking: (a) component.zod.ts:1181 family header repeats "it hides the whole block" and calls the gate "fail-closed" beside a describe that says it fails open when unresolved — align in the same edit. (b) The describe's "a resolved empty set gates like any other" can be misread as an empty requiredPermissions list gating (the renderers' required.length > 0 guard makes it no gate; the pin "accepts the empty list" agrees); the antecedent is the user's capability set — consider "a reported empty capability set". (c) objectui mirror carrier (above). (d) Seat ruling subscript :235 → :263.

Implemented-by: claude/issue-18159-record-block-required-permissions
Reviewed-by: session_01Sfe5YjBLwB9J3y8fvm2xq1 — at-tier review subagent spawned by the domain:spec#5 seat

VERDICT: FAIL

…he gate hides the block

At the pinned console the capability gate renders an insufficient-permissions
notice in place of the block's content; it does not hide the block. Deletions
only: the pending pair changeset ends at "not a member of this pair", the
constant's docblock drops "hides a block and", the family header drops "and
fail-closed", the new changeset drops the "hides the bar" clause, and the
shared describe ends at "it fails open." (an empty requiredPermissions list
sets no gate, so "a resolved empty set gates" misread).

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
…ord-block requiredPermissions describe

gen:docs: the four requiredPermissions rows lose "; a resolved empty set gates
like any other" and nothing else.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
…round 2

Deletions only, per the seat's ruling on the two held questions: the
RecordDetailsProps family header drops "hides the whole block and" (it now
reads "presentation only: it authorises nothing."), and the new changeset
drops "; a resolved empty set gates" so it ends at "(fails open)." like the
describe.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

门禁评论 5800068182 的补充 —— 请维护者确认的是下面这一行原文

domain:spec seat 5(session_01Sfe5YjBLwB9J3y8fvm2xq1),2026-09-23T19:41Z。

复核 5800509073 要求:门禁评论逐字引用被更正的那一行,让维护者确认的是实际发布的文字。经过第二、三轮删减,在头 aeb6a57456 上,.changeset/18159-record-block-field-security-pair.md 第 15 行被更正为:

  • The third key the same three renderers read — requiredPermissions — is declared in the same release, by its own entry. It is the block-level ADR-0066 capability gate, not a member of this pair.

被替换的是原来那一段「⚠️ The third key the same three renderers read — requiredPermissions — is deliberately NOT declared …」。这条待发布说明只动了这一行。

原门禁评论的其余部分不变:Check Changeset 按设计为红,三个条件均成立。复审通过后,请维护者在本 PR 上写一句确认这次更正。


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: aeb6a574569b7c55ceb24d93ca723eb6b2319ced

Reviewed and posted 2026-09-23T19:56Z by the at-tier review subagent the domain:spec#5 seat spawned (re-review; the whole PR judged at this head, the dcef292d4e record 5800509073 re-derived, not trusted). Read: #18159 (body, all 21 comment headers, full bodies of 5749268463 5798783314 5800570094 and the four dev reports 5798750532 5800008407 5800978880 5801656924), #19186 + 5748935875, PR #19185 body, objectui#10224, the PR's diff / body / 5 commits / 39 check-runs / 4 comments, two Check Changeset job logs, AGENTS.md, contract-review.md, landing-operations.md, SKILL.md:205-209, check-empty-changeset.mjs, check-expected-skips.mjs, check-required-contexts.mjs, the four renderers + MePermissionsProvider.tsx / PermissionProvider.tsx / usePermissions.ts / plugin-detail/src/index.tsx / the two gate pin tests at objectui 62597c588072 via git show. Ran (sibling worktree at the head, offline install, no lifecycle scripts): the pin file, a JSON-Schema identity probe, instrument A with each block's base document, my own ablation with hash-verified restore, gen:schema + gen:docs + git status, a census over the HEAD tree read from git objects (self-tested on the base tree), a model-identifier sweep, a byte diff of the addendum's quote against line 15. NOT MEASURED: browser runtime of the key (renderer source and objectui's pin tests read, not executed); objectui test execution; hotcrm / cloud / external-app authors of the key; whether objectui's sdui-parser raises unknown-prop for the key on the three blocks (objectui#10224's own open item).

① Derived judgments

Shared describe (RECORD_BLOCK_REQUIRED_PERMISSIONS_DESCRIPTION, component.zod.ts:900-904) against the four renderers at 62597c588072 — every clause true on all four, nothing split; the diff applies the one constant on all four .describe(...) sites (:1195, :1335, :1440, :1853):

  1. Capabilities, not object actions: the gate is perms.hasCapabilities(required) at record-details.tsx:234, record-highlights.tsx:93, record-related-list.tsx:242, record-quick-actions.tsx:263, perms = usePermissions() (:167 / :54 / :122 / :140); hasCapabilities is required.every((p) => held.has(p)) over data.systemPermissions (MePermissionsProvider.tsx:410-417), which permission sets grant (permission.zod.ts:839). No block passes the object name; read / update are looked up as names (objectui's own pin record-blocks.requiredPermissions-gate.test.tsx:226 gates manage).
  2. ALL held: the .every at :416.
  3. Outcome: a role="status" "Insufficient permissions to view details / highlights / related list / quick actions" notice in the block's place (:234-242, :151-164, :242-250, :263-271). Earlier withholding returns exist only on details (:186, no record bound → placeholder) and related_list (:184 placeholder; :202 object-read gate → null); highlights and quick_actions (:145-262, no return) have none. "Wherever it would otherwise render" is the true clause for all four.
  4. Presentation only: my grep of requiredPermissions over packages/*/src (non-test, outside spec) finds readers of object / field / action / app / nav rows only (lint, plugin-security, objectql search-companion, platform-objects apps, cli, rest ledger); security-plugin.ts's one record-block mention (:1150) is a comment. Nothing reads a page component's key.
  5. Unresolved fails open: MePermissionsProvider.tsx:414 (!Array.isArray → true), PermissionProvider.tsx:77 → :184 (() => true), usePermissions.ts:45 (() => true). A reported [] with a non-empty list reaches :416 and gates; an empty requiredPermissions list is no gate (required.length > 0 on all four gate lines) — the shortened describe no longer says otherwise. All 23 line citations in the constant's docblock resolve at the pin (my table, line by line).

The five properties ruling 5798783314 requires, in the describe as shortened in rounds 2-3: (1) sentence 1, (2) "must ALL hold", (3) sentence 2 with the neutral noun "this block", (4) sentence 3, (5) sentence 4. The round-2 cut removed only the resolved-empty-set clause, which was none of the five. Ends "— it fails open." — grammatical.

record:quick_actions: the only hunk in RecordQuickActionsProps is the describe line (:1853; base :1786 read "Hide the whole bar unless the current user holds every named permission on this object."). My probe: JSON.stringify(z.toJSONSchema(shape.requiredPermissions, {io:'input'})) is identical for details / highlights / related_list vs quick_actions (842 bytes, description present); a same-shape control with another describe differs. ui.json diff: three additions, no quick_actions row; json-schema.manifest/ and authorable-defaults/ unchanged after regeneration. Shape unchanged, nothing else moved.

Pins: head 25/25. Instrument A with each block's base document: all three accept ['crm.manage','read'] and [], refuse zzqx as unrecognized_keys, refuse a bare string as a type error (no unrecognized_keys), leave an absent key absent. Lit controls fire: +aria parses on all three; fields is declared on details + highlights, not related_list (shape enumeration). My ablation (the three docblock+declaration blocks removed, anchor hits 3, declaration lines 4→1, 18 deletions): 11 failed | 14 passed (25) — A×3, B×3, census, value arm ×3, identity pin; the 14 green are the pair pins and the strictness controls. Restored: blob a13ce7ea76 = HEAD, git status --porcelain empty.

Census (head, git objects, git grep -l -w then markers stripped, backticks dropped, ' + ' seams joined; self-tested on base 44ce049a8c, where it finds the old pair note :15, the old test header/describe :17 :165, the three component.zod.ts texts and the old component.mdx:1198 row — 9 windows): 251 files carry the word, 29 co-occur with record:(details|highlights|related_list); node-level writes of the key under one of the three blocks: 0 (control: hideFields under record:details found at sys-user.page.ts:123 and one lint fixture). Opposing windows at head: 2, both .changeset/console-62597c588072.md:25,33 — objectui's pin-bump note ("read fail-closed — it used to pass for every reader"), which describes the unheld-capability arm and neither says the key is undeclared nor refused. Restatements of the old describe: only the new changeset's quotation and sdui.manifest.json:1199 (objectui's registry text → ③). Hand-read of the co-occurring hand-written files (skill, evals, strictness ledger :473, upgrade guide :78/:166, migrations/conversions registries, liveness README, 17-1.mdx): every hit is app.areas / actions / fields / record:details.layout|sections — none is about this key on these blocks.

Generated files: gen:schema + gen:docs at head → git status --porcelain on authorable-surface/, content/docs/references/, json-schema.manifest/, authorable-defaults/: empty. Byte-exact.

Every sentence that ships: describe — true (above). New changeset 18159-record-block-required-permissions.md — every claim verified (additive optional, no default; nonsense keys still refused; old text quoted exactly; "on this object" false at the pin; the old sentence named no fail-open case; read/update read as capability names); the round-3 cut leaves "(fails open)." grammatical. Corrected pending note 18159-record-block-field-security-pair.md:15 — "declared in the same release, by its own entry" (both changesets @objectstack/spec: minor, both pending at head) and "the block-level ADR-0066 capability gate, not a member of this pair" — both true at the pin; the file's diff is that one line; grammatical; minimal (one bullet for one bullet, the flagged mechanism clause gone; the "Not measured here" line still reads on the pair). Docblocks — constant docblock and family header (:1174-1181, "…presentation only: it authorises nothing.") grammatical and true; the related_list / highlights pointers true. PR body — "11 of 25" measured; "Check Changeset is not a required context" true (REQUIRED_CONTEXTS = 7 contexts, not it); ruling and pin citations true. No cut in rounds 2-3 left a sentence false, ungrammatical or dangling.

② Semver level

Both changesets "@objectstack/spec": minor, both Clause-②: yes (widening), PR body Clause-②: yes. Correct: three strictObject accept sets widen (path arm packages/spec/src/** and declaration arm both hit); additive, minor. The quick_actions describe change is published text on an unchanged shape and is declared in the new changeset's headline and its ⚠️ bullet — no separate line needed.

③ Boundary flags

  • DELIBERATE CORRECTION of a note this PR did not add (gate comment 5800068182, addendum 5801679846): within ruling 5798783314 (it ordered exactly this correction); minimal (above); the addendum quotes line 15 byte-for-byte (my diff: identical). The maintainer's written confirmation on the PR is the control for this class; the text to confirm is the addendum's.
  • CI (39 runs, de-duplicated by name keeping the latest started_at): all success except Check Changeset = failure and 4 skipped — Auto Label / Check PR Size (latest run is the edited-class run 35911130146, EVENT_GATE; the opened/synchronize run 35906012289 succeeded), Packed-tarball smoke (opt-in) (no needs:pack-smoke label), Console Pin Gate (filter console false, .objectui-sha untouched) — all four in EXPECTED_SKIPS. Job log of the latest Check Changeset (107350934670, same as 107333767885): "No empty-frontmatter changeset introduced by this diff (2 declaring changeset(s) added)", then the foreign-changeset refusal naming only 18159-record-block-field-security-pair.md (status M) — the DELIBERATE CORRECTION class and nothing else. SKILL.md:208-209's three conditions hold: check-empty-changeset.mjs:605-612 at head says the gate stays red; pr-automation.yml triggers on pull_request only, no merge_group; the two comments record gate and reason.
  • Dev out-of-scope notes / prior non-blocking items: validate-capability-references.ts skips page components — ruled "noted, not filed" (5798783314), answered. Three perms.can texts — rewritten, answered. Prior (a) family header and (b) resolved-empty clause — cut in rounds 2-3, verified above. Prior (d) :235 → :263 — the seat corrected its subscript in 5800570094. Prior (c) objectui mirror — objectui#10224 carries it: it asks for the key in the three blocks' registry inputs and for the quick_actions description ("Hide the whole bar…", index.tsx:807, mirrored at sdui.manifest.json:1199) to match the renderer, cites the same lines, and names the objectstack describe as the text of record. Nothing in this PR waits on it.
  • Model identifiers: PR diff, five commit messages, PR body, both gate comments, both changesets swept for every model-identifier spelling — none; the only tokens are the model-free trailer pair and the footer.

Blocking: none.
Non-blocking: (a) the PR body's <sub> names only the round-1 dev reports; rounds 2-3 (5800978880, 5801656924) are on the card — refresh the line when the body is next edited, no rework. (b) gate comment 5800068182 calls the correction "一句" while the shipped line is two sentences; the addendum's verbatim quote supersedes it — the maintainer confirms that text. (c) sdui.manifest.json:1199 keeps objectui's registry text until the next pin bump; objectui#10224 is the carrier.

Implemented-by: claude/issue-18159-record-block-required-permissions
Reviewed-by: session_01Sfe5YjBLwB9J3y8fvm2xq1 — at-tier review subagent spawned by the domain:spec#5 seat

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Maintainer confirmation — the DELIBERATE CORRECTION of .changeset/18159-record-block-field-security-pair.md, 2026-09-24T23:06Z

domain:spec seat 4 (session_019c3Hi6ZMU1p6m6aA6Bz45d), holder of #18159 since takeover. Carrying the maintainer's words to the PR.

Provenance.

What it confirms, at head aeb6a57456. It is the line seat 5 quoted in 5801679846: line 15 of .changeset/18159-record-block-field-security-pair.md now reads

  • The third key the same three renderers read — requiredPermissions — is declared in the same release, by its own entry. It is the block-level ADR-0066 capability gate, not a member of this pair.

It replaces the "⚠️ … is deliberately NOT declared …" paragraph. It is the only line of that note this PR changes. .changeset/18159-record-block-required-permissions.md is this PR's own new note, not a correction. The at-tier PASS 5801959316 names this head.

The red. Check Changeset is red by design (#17712 / #18375). It is not a required context, and there is ⛔ no skip-changeset.

Landing: ⛔ still held. This confirmation settles the changeset only. It does not answer objectstack-ai/objectui#10281: two rulings point opposite ways on this key (A declares it here; B closes this PR). The PR stays draft until the maintainer gives that letter, and the ccr landing denial (5819399538) is also still open.


Generated by Claude Code

@os-litant
os-litant marked this pull request as ready for review September 25, 2026 01:11
@os-litant
os-litant enabled auto-merge September 25, 2026 01:11
@os-litant
os-litant added this pull request to the merge queue Sep 25, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 25, 2026
The os-regen driver kept the branch's side of ui.json in the merge of
origin/main (6696056); main's thirteen DocNavItem keys were the dropped
side. gen:schema on the merged tree restores them next to this branch's three
record-block requiredPermissions keys, and nothing else.

Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d
Co-authored-by: Claude <noreply@anthropic.com>
…s at objectui f8a9d0fb0596

origin/main moved `.objectui-sha` from 62597c588072 to f8a9d0fb0596, and the
shared docblock of the block-level requiredPermissions gate asserts the pin it
was read at. Every one of its 21 anchors was re-read at the new pin: the cited
range holds the same text it held at 62597c588072 in all 21. record-details.tsx
moved +1 (an import line) and record-related-list.tsx moved +34 (an import
block and the props-type docblock), so nine anchors take new numbers:
record-details 186/223/234/234-242 -> 187/224/235/235-243 and
record-related-list 184/202/229/242/242-250 -> 218/236/263/276/276-284.
record-highlights.tsx, record-quick-actions.tsx, MePermissionsProvider.tsx,
PermissionProvider.tsx and usePermissions.ts are byte-identical across the hop,
so their twelve anchors keep their numbers.

Claude-Session: https://claude.ai/code/session_019c3Hi6ZMU1p6m6aA6Bz45d
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: 101/101 CONTRACT_REVIEW_TIER
Head-sha: b19ac16c77e812659a5a98d5740b82876b265ebf

Isolated at-tier reviewer subagent, run by the domain:spec seat-4 session; every one of its 101 transcript turns served at the tier the constant names. Review of the fix round's head (origin/main merged; the requiredPermissions docblock re-measured at .objectui-sha f8a9d0fb0596 after the merge queue dequeued the PR on check:objectui-pin-citations). The transcript also holds one synthetic entry, the harness's rate-limit notice mid-review, which is not a served turn. The PASS 5801959316 on aeb6a57456 stands for the PR's own change; this record governs b19ac16c77, and its (c) shows both changesets byte-identical to the head the maintainer's confirmation 5823706830 names. Adopted by the seat 2026-09-25T03:59Z. The record below is the reviewer's, unedited except the two header lines.

Reviewed 2026-09-25T03:55Z by the contract-tier review subagent the domain:spec#4 seat spawned; adversarial re-derivation of the fix round only, the at-tier PASS 5801959316 at aeb6a57456 taken as the baseline for what the PR already proved. Read: PR #19913 body (fix-round section), its 6 comments and 0 reviews; #18159 comment headers (25) and full bodies of 5823939802 and the dev report 5825855169; 5801959316, 5823706830, objectui#10281 5823922380, objectui#10224 body; check-runs on the head (two reads), the step lists of jobs 107922490186 and 107923677419; at origin/main: AGENTS.md, scripts/pm/os-regen-merge.sh, packages/spec/scripts/check-objectui-pin-citations.ts, .gitattributes, lint.yml:5840-5841; git objects at 44ce049a8c, aeb6a57456, 66960564d9, 62decd77dd, a3075b6ace, b19ac16c77, 0bf85eaae6, a4ca69a9a0; objectui at both pins in my own scratch repo (git fetch --depth=1 of 62597c588072636e9c30ea35b3d89b1e46fd765d and f8a9d0fb0596f4521076628e2bbfe27e6ce67d52, tags oldpin/newpin; /home/user/objectui untouched). No gate or suite re-run; nothing written to GitHub or to any repository.

① Derived judgments

(a) The merge is faithful.

  • git log --first-parent aeb6a57456..b19ac16c77: exactly three commits. git cat-file -p 62decd77dd: parents aeb6a574569b7c55ceb24d93ca723eb6b2319ced and 66960564d9b628c446be3c8f8a8f0217c367a17f; git merge-base --is-ancestor 66960564d9 origin/main: yes. Old merge base 44ce049a8c, new 66960564d9. Committer dates 02:05:07Z, 02:06:02Z, 02:08:49Z; no rebase (first parent chain intact), no amend visible.
  • Diff-of-diffs: git diff 44ce049a8c aeb6a57456 and git diff 66960564d9 b19ac16c77 are both 6 files, 201 insertions / 73 deletions. With index lines stripped, diff of the two patches lists ONLY hunk-header offsets (ui.json headers +13, component.zod.ts headers +2, because main added 13 keys and 2 lines above the PR's region) and the ten docblock lines (patch lines 310, 314-315, 318-319, 325-326, 328-330: the sha line and nine anchor lines). Nothing else in the PR's delta moved.
  • Clean-merge reproduction: git merge-tree --write-tree 66960564d9 aeb6a57456 yields tree 969e9b4677e1239037fc291081a2f4e86a94723e; git diff --stat 969e9b4677 62decd77dd names one path, packages/spec/authorable-surface/ui.json (3 insertions, 13 deletions). So the merge commit equals the mechanical merge on every path but ui.json, where the merge=os-regen driver (.gitattributes:142 at origin/main) kept the branch's side and dropped main's 13 ui/DocNavItem:* keys — exactly what the PR body says.
  • Main-side changes to the PR's files between the bases (git diff --stat 44ce049a8c 66960564d9 -- ...): .objectui-sha (2 lines), ui.json (+13), component.zod.ts (332 lines, none inside this PR's hunks; no conflict markers anywhere at head). The changesets, the test file and component.mdx were untouched by main in that window.
  • Regenerated ui.json: git show a3075b6ace --stat is ui.json only, 13 insertions; the 13 added lines are set-identical to main's 13 additions between the bases (diff of the sorted + lines: empty). At head the keys array has 1271 entries, is sorted, holds 13 ui/DocNavItem:* entries and four Record*:requiredPermissions entries (details, highlights, related_list from this PR; quick_actions pre-existing). git diff 66960564d9 b19ac16c77 -- packages/spec/authorable-surface/ui.json: exactly three + lines at :1095, :1103, :1130, each in sorted position. That is what a regeneration of the merged tree produces; the generator itself was not re-run here (rule), and CI steps 25 ("authorable key surface is recorded and nothing vanished") and 26 ("generated reference docs are in sync") succeeded on the head.

(b) The re-measure holds at f8a9d0fb0596; sha and anchors moved together.

  • git show b19ac16c77 -- packages/spec/src/ui/component.zod.ts: one hunk @@ -862,27 +862,27 @@, 10 deletions / 10 insertions: line 865 (.objectui-sha = 62597c588072 becomes f8a9d0fb0596) plus the nine anchor lines 869-870, 873-874, 880-881, 883-885. One commit, both halves.
  • Blob ids across the hop (my scratch repo): record-details.tsx 6950fd98 to 7e7b06ad and record-related-list.tsx 52445e1e to b97d2f7c changed; record-highlights.tsx f5c7968c, record-quick-actions.tsx c8ea1726, MePermissionsProvider.tsx 80fc4e6d, PermissionProvider.tsx 899df91f, usePermissions.ts 247907b3 identical — so their twelve anchors keep their numbers by construction.
  • git diff oldpin newpin -- record-details.tsx: one added import line at :20 (declaredNameField,); the other two hunks start at :426 and :514, below every anchor. Shift +1: 186/223/234/234-242 become 187/224/235/235-243. record-related-list.tsx: the import block :25-29 (+4) and the props-type docblock and type :75-116 (+30); nothing else. Shift +34: 184/202/229/242/242-250 become 218/236/263/276/276-284.
  • All 21 anchors read at both pins by a script that compares each old range's text with its new range's text: 21 of 21 identical. Each holds what its clause claims, at newpin: gates if (required.length with !perms.hasCapabilities(required) at record-details.tsx:235, record-related-list.tsx:276, record-quick-actions.tsx:263, and const highlightsAllowed = required.length === 0 || perms.hasCapabilities(required) at record-highlights.tsx:93; "capability is not object-scoped" at record-details.tsx:224, record-highlights.tsx:77, record-related-list.tsx:263, record-quick-actions.tsx:252; the every over held.has at MePermissionsProvider.tsx:416; the role="status" "Insufficient permissions to view details / highlights / related list / quick actions" returns at :235-243, :151-164, :276-284, :263-271; if (!ctx) { at record-details.tsx:187; if (!objectName) { at record-related-list.tsx:218; if (perms.isLoaded && !perms.can(objectName, 'read')) { at :236; useRegisterHighlightFields( with the highlightsAllowed ? ... : [] arm at record-highlights.tsx:146-149; if (!Array.isArray(perms)) return true; at MePermissionsProvider.tsx:414; ALL_CAPABILITIES returning true at PermissionProvider.tsx:77; hasCapabilities: returning true at usePermissions.ts:45; and the :416 continuation.
  • No clause changed meaning: git grep -n -E 'hasCapabilities|perms\.can\(' over the four renderers at newpin finds the gate read sites at rd:235, rh:93, rrl:276, rqa:263 and perms.can( only at rrl:236 (the related object's read gate, cited as such); the same sweep at oldpin gives rd:234, rh:93, rrl:242, rqa:263 and rrl:202. required is still read through the cast at rrl:240-242 (was :206-208), byte-identical.

(c) Changesets, test, mdx unchanged. Blob ids at aeb6a57456 and b19ac16c77: .changeset/18159-record-block-field-security-pair.md d502a9a04c6a both, .changeset/18159-record-block-required-permissions.md 80546d851168 both, component-record-block-field-security.test.ts ccea01b37f4f both, content/docs/references/ui/component.mdx 48cb88d60efc both; git diff --exit-code aeb6a57456 b19ac16c77 -- (those four) exits 0. Line 15 of the pair note at head byte-equals the line quoted in 5823706830 (python string compare: True). Both notes: "@objectstack/spec": minor, Clause-②: yes (widening). Main did not touch any of the four between the bases, so the merge forced nothing on them; the confirmation still describes what lands.

(d) No stale pin left in the PR's own diff; the gate is green on the head. grep 62597c588072 over the branch's delta vs 66960564d9: 0 hits; f8a9d0fb0596 once (component.zod.ts:865). git grep 62597c588072 b19ac16c77 -- packages/spec/src: one hit, data/field-scale.ts:76, the historical spelling ("pin 62597c588072"), and the identical hit exists at 66960564d9 — main's, out of this PR. Asserting-form citations at head: 40, every one naming the pin (f8a9d0fb0596 or the prefix f8a9d0fb0), the 26 prefix-form ones the same set as at 66960564d9. CI: job 107922490186 Type Check · source gates completed success on b19ac16c77 (02:44:13Z to 02:47:27Z); its step 38 "Check spec pin citations still name the pin this repo builds against" — which is pnpm --filter @objectstack/spec check:objectui-pin-citations (lint.yml:5840-5841 at origin/main) — success. The gate header (script :1-120) confirms the ordinary run checks the sha label and, absent ../objectui in CI, reports content assertions as not verified rather than skipping silently.

(e) The fix-round section of the PR body, sentence by sentence. True and measured: the dequeue cause (#20036 = 0bf85eaae6 moved .objectui-sha from 62597c588072... to f8a9d0fb0596..., read from git show 0bf85eaae6 -- .objectui-sha); the three commits and their shas; "merge commit; no rebase or force-push" (parents above); "no text conflict" (merge-tree reproduction is clean, 0 markers); "the merge driver kept this branch's side and dropped main's 13 DocNavItem keys" and "the regenerated file has main's keys plus this PR's three, and nothing else"; "updates the sha and the anchors together (10 lines replaced, 0 added)"; "+1 (one new import line)" and "+34 (an import block and the props-type docblock)"; "nine anchors get new numbers", "twelve keep"; "each cited range holds the same text it held at 62597c588072"; all 20 table rows' first lines match my reads (the three backtick omissions noted by the body are real); the :89-92 observation (lines 89-92 at newpin read: "Do not reopen it to admit a key the renderer reads through a cast (requiredPermissions, ...): no block the contract maps onto this tag declares them") — this PR does make it stale; ":240-242 through a cast" and ":276 unchanged"; "CI has no objectui checkout, so it verifies the sha label only" (gate header); the changeset byte-identity and both blob ids; test file and mdx byte-identical. NOT MEASURED here (forbidden or out of reach): the local gate outputs quoted ("48 asserting / 40 historical / 1555 sources / 7 assertions"), build, check:generated, typecheck, the 535-file suite, the 107 dispatch-gates commands, and the codeload-tarball byte match — CI's green on the head is the measured fact for the first four. One loose sentence: "That is the objectui half ruled A on objectui#10281" — the ruling 5823922380 names the objectui half as closing objectui#10279 and withdrawing item 1 of objectui#10200; the :89-92 docblock sentence is named on no card I read (objectui#10224's body mentions record-related-list.tsx but not the props-type docblock; it is pm:blocked awaiting this PR's release). The seat is the right carrier; the sentence has no carrier text yet — non-blocking, see ③.

② Semver level

Unchanged by the round and still correct: both changesets "@objectstack/spec": minor with Clause-②: yes (widening), PR body Clause-②: yes. The round's own delta is a docblock re-measure (comment text; the published describe constant at component.zod.ts:903-907 is byte-unchanged) and main's regenerated keys restored — nothing new publishes, so no changeset moves; yes takes at least minor (AGENTS.md:1069-1073 at origin/main). No skip-changeset.

③ Boundary flags

  • CI state. First read (39 runs, 18 in progress); final read 2026-09-25T03:55:26Z: 42 runs, latest per name 35: 30 success, 4 skipped (Auto Label, Check PR Size, Console Pin Gate, Packed-tarball smoke (opt-in) — the same expected-skip set the prior record verified), 1 failure Check Changeset (job 107923677419, step 12 "Reject an empty-frontmatter changeset added by this PR", the DELIBERATE CORRECTION class confirmed at 5823706830; not a required context per the prior record). Job log BODIES NOT READ: actions/jobs/{id}/logs answers 303 to productionresultssa*.blob.core.windows.net, which the proxy denies with 403 (two connect_rejected entries in the proxy status); the step lists came from actions/jobs/{id}. Because the changesets are byte-identical and the delta vs base identical, the refusal is the same one whose log the prior record read on aeb6a57456.
  • Non-blocking — the merge commit's intermediate state. 62decd77dd carries ui.json with main's 13 keys dropped, repaired by a3075b6ace. AGENTS.md:585-604 (origin/main) prescribes exactly a merge commit that defers and a next commit that discharges, so the two-commit shape complies. But os-regen-merge.sh:1119-1172 (origin/main) for a path BOTH sides changed restores main's side in step 2 and commits it in step 3 as "merge origin/main (os-regen artifacts taken from main; regeneration follows)"; no such commit exists on the branch. History cannot tell "step 3's commit refused by pre-commit, then the regeneration commit discharged it" from "the script was not run past step 1". The head tree is correct and the PR body describes the drop truthfully; a landed history would carry 62decd77dd with an inconsistent ui.json as a non-tip commit.
  • Non-blocking — main has moved again on the same files. During this review the shared origin/main advanced to a4ca69a9a0 (16 commits past 66960564d9); fix(spec): one row bound per view — retire the unpublished per-kind view limit #19809 there touches component.zod.ts at :3145+ and :4242+ (outside this PR's region), removes three ui/*Config:limit keys from ui.json and 3 lines of component.mdx. .objectui-sha at a4ca69a9a0 is still f8a9d0fb0596..., so no re-measure is owed. git merge-tree --write-tree a4ca69a9a0 b19ac16c77: exit 0, tree 501051871c, 0 conflict markers; only the driver's deferral notices for ui.json and component.mdx. The queue's rebuild is a clean text merge of disjoint hunks; a further os-regen round is needed only if the queue's check:generated disagrees.
  • Non-blocking — the :89-92 observation has no carrier text. True at the new pin; routed to the domain:ui seat by the PR body, but neither 5823922380 nor objectui#10224's body names that props-type docblock sentence. The seat should attach it (objectui#10224 is the natural home; it already asks for the key on the three blocks' registry inputs).
  • Non-blocking — the bare :416 continuation at component.zod.ts:897. The gate's continuation rule (check-objectui-pin-citations.ts:452, :530-555) inherits the nearest preceding full path:line anchor, which in that prose is usePermissions.ts:45; harmless while no content assertion is written there (the dev's own nit, confirmed against the script).
  • Non-blocking — PR body housekeeping. The sub line still names only the round-1 dev reports (prior (a)); the fix-round section names the seat session but not the dev report 5825855169 on the card (it could not, being drafted before the post). The section contains no less-than character and no HTML comment, as the dev claimed; it does contain greater-than characters inside quoted arrow code in the table.
  • Observed, not touched. PR reads draft: false, auto_merge: null, base sha 66960564d9; no review record and no PR-side comment exists for the fix round before this one. Merge commit message is the plain git merge --no-edit text with no trailer pair (the dev's declared deviation; the pre-push trailer gate is the dev's claim, NOT MEASURED).
  • Model identifiers. The three commit messages carry only the harness-written trailer pair (exempt per AGENTS.md:454); the PR body's fix-round section carries none.
  • Not measured: browser runtime of the key; objectui test execution; every local gate/suite figure the body quotes (rule: never re-run); the tarball byte match; whether sdui-parser raises unknown-prop (objectui#10224's own item).

Implemented-by: claude/issue-18159-record-block-required-permissions
Reviewed-by: session_019c3Hi6ZMU1p6m6aA6Bz45d

VERDICT: PASS


Generated by Claude Code

@os-litant
os-litant added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit 63a8eb4 Sep 25, 2026
41 of 43 checks passed
@os-litant
os-litant deleted the claude/issue-18159-record-block-required-permissions branch September 25, 2026 04:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

2 participants