Skip to content

docs(skills,docs): teach "the person is a record", a membership key with valueField 'id' - #31266

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-h4z1pw
Nov 8, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/project-thread-h4z1pw

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Irene · project thread

Fixes #31176

Clause-②: no
Metadata-protocol: none

A skills/** diff ships in @objectstack/skills, so this PR adds .changeset/data-skill-person-is-a-record.md ('@objectstack/skills': patch, both declaration lines), not skip-changeset. dispatch-gates mandates the contract-review model tier for skills/** (clause ①); that mandate is waived by the maintainer's standing order in this project (2026-11-03), and this card ran on the project's default model. The dispatching seat (domain:skills#3) runs the contract review. The docs page content/docs/permissions/rls.mdx is in scope by the card itself (triage 6710238236 folded it into the domain:skills lane).

维护者速读

What it teaches, and where each sentence comes from

Taught sentence (site) Code on main
valueField is a reference column, or id (security.md § Membership keys; rls.mdx) packages/spec/src/security/rls.zod.ts L740-L741 ("F is a reference column on X … or id, the roster row's own primary key (#31129 ruling A)"); packages/lint/src/validate-membership-key-declarations.ts L245 isRosterOwnId; explanation membership-key-value-field-invalid (packages/lint/src/rule-explanations.ts L1783-L1794: "F may also be id, X's own primary key")
never the same column as userField (stated beside the code, not attributed to it) rls.zod.ts docblock "valueField naming the same column as userField" — refused by the schema's own superRefine, so membership-key-value-field-invalid never fires for it; rule-explanations L1793 "F may not be U's column: the schema refuses that"
roster employee, my_employee_ids: { userField: 'user', valueField: 'id' }, policy employee in current_user.my_employee_ids (security.md policy block; rls.mdx os:check block; eval 12) rls.zod.ts L742-L745; rule-explanations L1838-L1840; .changeset/31129-membership-key-own-rows.md example
with id the set is the ids of the caller's own roster rows rls.zod.ts L742 "With id the set is the caller's own rows of X"; rule-explanations L1789-L1790
it grants nothing by itself rls.zod.ts L747 "It grants nothing by itself: what a policy grants with it is the author's."
tested against a column not referencing the roster, or another object's id, the policy is refused (rls-predicate-membership-key-target) packages/lint/src/validate-rls-predicate-enforceability.ts L1868-L1872 (an id key's target is the declaring object), L1486-L1488 (id of another object faults), L1492-L1496 (non-reference / other-reference faults), L1667 severity: 'error'; rule-explanations L1830-L1834
compared with a column that references the roster, or the roster's own id (rls.mdx) rls.zod.ts L745-L746; enforceability hint L1674
"a named record (the caller's own roster row too) → a membership key"; "the user → owner scoping" (security.md Choose-by) re-scope of the existing choice rule so it does not contradict the new case ("the person → owner scoping" read as covering the person-as-record)

The diff, file by file

  • skills/objectstack-data/rules/security.md § Membership keys: a two-line variant in the existing policy block (comment naming the employee roster and its key, then an own_leave policy on leave_request); the policy block's comment says "in a permission set" (it now holds a non-portal policy); the valueField line says "or id (membership-key-value-field-invalid; never the same column)"; one new bullet on what an id key holds and does not do; the Choose-by bullet re-scoped. The os:check object block above is byte-identical.
  • Correction of a false claim in the claimed file: valueField "(never the same column, nor id: membership-key-value-field-invalid)" was false after PR feat(spec,lint,plugin-security): a membership key may yield the caller's own roster rows (valueField: 'id') #31174; the "nor id" is gone.
  • skills/objectstack-data/evals/hooks-security-seeds-datasources.json: case 17 (below). Swept the file: no case rewards the old "nor id" claim (case 6 is the account roster, unchanged).
  • content/docs/permissions/rls.mdx § Context variables: the valueField sentence names "a reference column, or id"; a paragraph When the person is a record, an os:check block (employee roster with my_employee_ids, leave_request (public_read) with an employee lookup, an employee_self_service permission set with own_leave), and a paragraph on what the key compares with, the refusal code and "grants nothing by itself", and why leave_request is public_read: RLS only narrows what the org-wide default already allowed (the same page, § How policies compose), so under private the policy would narrow the employee's own rows and a request someone else filed for them would stay hidden. Picked public_read (review S3) so the policy is what scopes the rows.
  • .changeset/data-skill-person-is-a-record.md: new, '@objectstack/skills': patch.
  • .changeset/data-skill-optional-master.md (deliberate touch, seat ruling): this unreleased note from this lane's PR docs(skills): teach controlled_by_parent with an optional master in the data skill, permission docs and ADR-0055 #31092 said "the membership-key notes say a valueField cannot be the roster's own id (membership-key-value-field-invalid)"; that clause is deleted so the next release notes do not print both claims. Nothing else in the file changes.
  • scripts/check-skills-token-ratchet.mjs: security.md's row only, 3698 → 3768 (below).

Token budget

skills/objectstack-data/rules/security.md, ceil(bytes / 4):

  • origin/main (bdefd49): 14783 bytes → 3696 tokens, ceiling 3698 (headroom 2).
  • After: 15069 bytes → 3768 tokens.
  • Deletions (each a whole line, never a rewrap), 208 bytes = 52 tokens:
    • D1 - Source: \node_modules/@objectstack/spec/src/security/permission.zod.ts`(§ RBAC). Fact held: that path only. Home:skills/objectstack-data/references/_index.md` L18 (same path, with its exports).
    • D2 - Source: \node_modules/@objectstack/spec/src/security/rls.zod.ts`.(§ RLS). Fact held: that path only. Home:references/_index.md` L47.
    • D3 - Source: \node_modules/@objectstack/spec/src/data/field.zod.ts`and its blank line (§ Sensitive fields). Fact held: that path only. Homes:references/_index.mdL13,rules/field-types.md` L31.
  • Remainder after the cuts: 3768 − 3698 = +70. Raised by the measured remainder, pinned at the measurement, under the standing ruling recorded as 6513306068 (maintainer ruling 2026-10-27, decision card 「skills 修复卡的教学和 eval 都撞 token 天花板,怎么付?」, tap A), verbatim: 「A:先削重复,余额按实测提高」. The seat ruled the cuts and the raise before the script was touched. No other row changes.
  • Eval fixtures and docs pages are not ratcheted.

Eval case

hooks-security-seeds-datasources.json case 17:

  • prompt: "In our HR app each employee record has a user lookup to the person who signs in. An employee may read only their own leave_request rows (each has an employee lookup), and we don't want an owner stamp or a junction table for it. Call the set my_employee_ids."
  • must_contain: membershipKeys, my_employee_ids, valueField: 'id', employee in current_user.my_employee_ids
  • must_not_contain: using: 'owner_id (matches an owner-scoped policy, not an answer that names owner scoping as the thing to avoid)

Other sites

  • content/docs/releases/v18/18-1.mdx L285 says a valueField "that is not a reference" is refused. It is the v18.1 release history, true when written; left alone (seat ruling).
  • No other skill names membershipKeys or valueField for a membership key. The generated reference pages (content/docs/references/security/rls.mdx L174, references/data/object.mdx L461) already say "or id".

Gates

Head ebb6bce9 = the card commit b9f4ec22, a merge of origin/main (42252a5a, reviewed: contract review PASS, no must-fix) and one review delta commit (S1, S2, S3, N4, N6, N7). node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived the same 87 commands at b9f4ec22 and at ebb6bce9, after a full pnpm build (exit 0). Each ran under timeout 900 with NODE_OPTIONS=--max-old-space-size=8192, exit captured before any pipe. At ebb6bce9: 86 exit 0, 1 exit 1 (check-empty-changeset, the deliberate correction below; it stays red by design until the seat's record confirms it). The same split held at b9f4ec22. origin/main has moved since (07559ce); none of its new paths is in this diff, so the branch was not re-merged.

node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-adr-links.mjs :: exit 0
node scripts/check-adr-links.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-declaration-mirrors.mjs :: exit 0
node scripts/check-declaration-mirrors.mjs --self-test :: exit 0
node scripts/check-doc-route-spelling.mjs --advisory :: exit 0
node scripts/check-doc-route-spelling.mjs --self-test :: exit 0
node scripts/check-docs-section-name.mjs :: exit 0
node scripts/check-docs-section-name.mjs --self-test :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 1
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-section-landing-index.mjs :: exit 0
node scripts/check-section-landing-index.mjs --self-test :: exit 0
node scripts/check-self-test-wired.mjs :: exit 0
node scripts/check-self-test-wired.mjs --self-test :: exit 0
node scripts/check-self-test-workflow-commands.mjs :: exit 0
node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0
node scripts/check-skills-token-ratchet.mjs :: exit 0
node scripts/check-skills-token-ratchet.mjs --self-test :: exit 0
node scripts/check-whole-set-label-write.mjs :: exit 0
node scripts/check-whole-set-label-write.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/ci/snapshot-next.mjs --self-test :: exit 0
node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
pnpm --filter @objectstack/lint run check:doc-security-posture :: exit 0
pnpm --filter @objectstack/spec run check:docs :: exit 0
pnpm --filter @objectstack/spec run check:empty-state :: exit 0
pnpm --filter @objectstack/spec run check:liveness :: exit 0
pnpm --filter @objectstack/spec run check:skill-examples :: exit 0
pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0
pnpm --filter @objectstack/spec run check:variant-docs :: exit 0
pnpm --filter @objectstack/spec run check:yaml-examples :: exit 0
pnpm check:agent-test-spelling :: exit 0
pnpm check:bash32-floor :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:cli-command-ids :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:corpus-claim-drift :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:doc-anchors :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:doc-frontmatter :: exit 0
pnpm check:docs-audit-scope :: exit 0
pnpm check:docs-redirects :: exit 0
pnpm check:docs-single-h1 :: exit 0
pnpm check:docs-spec-enumerations :: exit 0
pnpm check:docs-transcript-drift :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:entry-guard :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:merge-driver :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:parse-guard :: exit 0
pnpm check:pm-dispatch-gates :: exit 0
pnpm check:pm-governed-merges :: exit 0
pnpm check:pm-skill-id-lint :: exit 0
pnpm check:pnpm-filter-targets :: exit 0
pnpm check:pnpm-flag-spellings :: exit 0
pnpm check:published-files :: exit 0
pnpm check:published-readme-links :: exit 0
pnpm check:ratchet-remedy-authority :: exit 0
pnpm check:react-page-adapter-contract :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:role-word :: exit 0
pnpm check:scripts-symbol-anchors :: exit 0
pnpm check:skill-compatibility :: exit 0
pnpm check:skill-frame-sync :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:vendor-version-stamps :: exit 0
pnpm check:watch-hint-literal :: exit 0

Also named by the order: check:skill-examples exit 0 and check:skill-identifier-liveness exit 0 (both in the list above; liveness Leg 1: 490 citations over 71 files), check:skill-refs exit 0 (not derived, run separately: 9 generated files in sync), check-skills-token-ratchet.mjs exit 0 and --self-test exit 0.

Review delta (42252a5a → ebb6bce9)

  • S1: security.md's valueField line reads "or id (membership-key-value-field-invalid; never the same column)", so the code is no longer pinned on the same-column case (the schema refuses that itself). Same bytes: 15069 → 3768 tokens, ratchet row unchanged.
  • S2: eval 12's prompt names my_employee_ids; must_not_contain is now using: 'owner_id, which an answer naming owner scoping only as the thing to avoid does not match.
  • S3: rls.mdx's leave_request is public_read, with the reason stated (RLS only narrows the org-wide default).
  • N4: the rls.mdx valueField sentence reads "(valueField: a reference column, or id)". N6: table row cites L1667. N7: the new changeset says the platform change is in this same release.

Merge of origin/main (ebb6bce9 → 0a8ff459)

PR #31201 added eval cases 12 and 13 to the same fixture, so this card's case became case 14 (now 17, below), content unchanged; the merge (plain merge commit, parents ebb6bce9 and 85c01247) resolves only that file. At 0a8ff459: JSON parses, ids 1–14 unique; check-skills-token-ratchet exit 0 (security.md 3768 / 3768); check:skill-examples exit 0 (286 examples, after rebuilding spec/client/client-react on merged main); check:skill-identifier-liveness exit 0; check-commit-card-trailers exit 0.

Merge of origin/main (0a8ff459 → 1d88c9b8)

PR #31223 added eval cases 14–16 to the same fixture, so this card's case is now case 17, content unchanged; the merge (plain merge commit, parents 0a8ff459 and ae816c40) resolves only that file, keeping main's cases 1–16 byte for byte. At 1d88c9b8: JSON parses, ids 1–17 unique; check-skills-token-ratchet exit 0 (security.md 3768 / 3768); check:skill-examples exit 0 (286 examples, after rebuilding spec/client/client-react on merged main); check:skill-identifier-liveness exit 0; check-commit-card-trailers exit 0.

DELIBERATE CORRECTION of a pending release note

check-empty-changeset names .changeset/data-skill-optional-master.md (present on the merge base, changed here). This is not a collision: that note, from this lane's PR #31092, is unreleased and says a thing this PR's teaching makes false. Corrected sentence, before:

The relationship/master-detail-required lint row names the optional master, and the membership-key notes say a valueField cannot be the roster's own id (membership-key-value-field-invalid).

After:

The relationship/master-detail-required lint row names the optional master.

Nothing else in that file changes. Per the gate, the dispatching seat confirms this by its own record (ACCEPT / same-head contract-review PASS); it is not restored from the base, which would republish the false clause.

Commit trailers: node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD → exit 0 before every push.

🤖 Generated with Claude Code

https://claude.ai/code/session_01J8VVAmphavoevUax9bUVkY

…ith valueField 'id'

The data skill's security rule and the RLS guide now teach a membership key
whose valueField is the roster's own `id`: an `employee` roster's
`my_employee_ids` read by a `leave_request` policy as
`employee in current_user.my_employee_ids`. They say it grants nothing by
itself and that a policy testing it against a column not referencing the
roster, or another object's `id`, is refused
(rls-predicate-membership-key-target). The skill's false "nor `id`" clause on
valueField is corrected, as is the same claim in an unreleased changeset. A new
eval case exercises it. The security rule pays with three Source pointer lines
(paths kept in references/_index.md) and its ratchet row rises by the measured
remainder under the standing ruling.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAmphavoevUax9bUVkY
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 7, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

Contract-review-claim: session_01J8VVAj7CvjkGXvgXzd6RZ7 · head 42252a5 · 2026-11-07T21:03Z

Full contract review of PR #31266 (#31176, claim on the card, round 22). The seat review runs at CONTRACT_REVIEW_TIER in an isolated subagent.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 42252a5a8fb10a9fa10fa2a36d9f92432cea243d
Local-runs: probe — node scripts/check-skills-token-ratchet.mjs, run in a throwaway worktree of the head (/tmp/claude-0/seat3/wt-31266, since removed), because the brief asks for the token reading from the head's own ratchet (exit 0; security.md 3768 / 3768, headroom 0; 57 authored files within ceilings). In the same worktree I also ran node scripts/pm/check-governed-merges.mjs --pr 31266 (exit 3, GOVERNED, Tier H), node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD (exit 0, 2 commits) and node scripts/check-empty-changeset.mjs --base origin/main (exit 1, names only .changeset/data-skill-optional-master.md, the DELIBERATE CORRECTION class). Bytes were taken by hand (git show <ref>:skills/objectstack-data/rules/security.md | wc -c: 14783 on origin/main, on the merge base and on bdefd49f; 15069 at the head). Each cut was measured from the diff with a scratch script (total 208). The os:check gates and the lint tests were not re-run, because they need a built spec dist. The gate record shows check:skill-examples exit 0 at b9f4ec22, and the PR #31174 tests were read instead (see item 1).
Scope: the 6 files of git diff origin/main...refs/seat3/pr-31266 (merge base 455229e7, which is origin/main; card commit b9f4ec22 on bdefd49f, then merge 42252a5a): skills/objectstack-data/rules/security.md, skills/objectstack-data/evals/hooks-security-seeds-datasources.json, content/docs/permissions/rls.mdx, .changeset/data-skill-person-is-a-record.md, .changeset/data-skill-optional-master.md, scripts/check-skills-token-ratchet.mjs. bdefd49f..origin/main is 2 commits (c09ffc16d, 455229e7a). Neither touches these files or any code cited below. Cited code read on origin/main: packages/spec/src/security/rls.zod.ts (membership-key docblock L727-777, MembershipKeySchema L805-880 with its superRefine), packages/lint/src/validate-membership-key-declarations.ts (header, valueFieldFault, isRosterOwnId L245, columnOf, the judge loop L300-370), packages/lint/src/validate-rls-predicate-enforceability.ts (membershipTargetFaults L1459-1500, the refusal L1662-1675, membershipKeyContextOf L1840-1877), packages/lint/src/rule-explanations.ts L1783-1843, packages/plugins/plugin-security/src/object-membership-keys.ts (header, readDeclaredKey L440-540), packages/plugins/plugin-security/src/external-owd-grant-stamp.ts header, packages/spec/src/data/object.zod.ts L3745 (sharingModel), packages/spec/scripts/check-skill-examples.ts (docs root L379-438), packages/spec/scripts/build-skill-references.ts (SKILL_MAP), and PR #31174's merged diff (3c1eb635a), its tests included.

① Derived judgments

Inputs: card #31176 (body; triage 6710238236; claim 6712646214), blocker #31129 with ruling A (6708428544), PR #31174's merged diff, the PR body (the live body equals /mnt/project-files/card-31176/body-v1.md apart from one trailing blank line), the order at /mnt/project-files/skills-seat3/order-31176.md, the gate record gates-b9f4ec22.txt (87 commands, 86 exit 0, check-empty-changeset exit 1), the two commits and the net diff. The seat's mid-flight rulings (a) and (b) are taken as given. Each taught fact was checked against code:

  • Item 1, valueField: 'id'. TRUE, with one loose code citation (S1).
    • The docblock (rls.zod.ts L740-747) says F is a reference column on X, or id. With id the set is the caller's own rows of X. It holds ids of X, so a policy compares it with a column that references X, or with X's own id. It grants nothing by itself. The field's .describe says the same.
    • The resolver (readDeclaredKey) reads X where U equals the caller's user id, filter holds and the caller's own Layer 0 wall applies, as a system read. For id it projects ['id'] once, and collects row[valueField], which is row.id. So with id the set is the ids of the caller's own roster rows (in the caller's organisation, after filter). TRUE.
    • "Grants nothing by itself": the read returns only rows whose U is the caller. A policy decides what the set lets through. TRUE, and it is the docblock's own sentence.
    • Declaration lint: membership-key-value-field-invalid (MEMBERSHIP_KEY_VALUE_FIELD_INVALID) now accepts id through isRosterOwnId. That is only the driver-provisioned id (an injected column with no type). An authored text field named id is still refused (PR feat(spec,lint,plugin-security): a membership key may yield the caller's own roster rows (valueField: 'id') #31174 test).
    • Policy lint: rls-predicate-membership-key-target (RLS_PREDICATE_MEMBERSHIP_KEY_TARGET, severity error, L1667-1668). For an id key, membershipKeyContextOf sets the target to the declaring object. membershipTargetFaults then refuses: id of another object; a non-relationship column; a relationship whose reference is another object. It accepts a column that references the roster and the roster's own id. The PR feat(spec,lint,plugin-security): a membership key may yield the caller's own roster rows (valueField: 'id') #31174 tests pin all four refusals on hr_leave_request and the two accepts, at both the CLI door and the save door. So "tested against a column not referencing the roster, or another object's id, it is refused (rls-predicate-membership-key-target)" is TRUE. One bound is unstated: the rule judges only a key whose roster is in the stack and declared once (N2).
    • userField equal to valueField is still refused. But it is refused by MembershipKeySchema's superRefine, not by membership-key-value-field-invalid. The lint loop skips that case (decl.valueField !== decl.userField). The rule's own explanation says "F may not be U's column: the schema refuses that". See S1.
    • "or id (never the same column …)": the words are right. id can never be U (membership-key-user-field-invalid refuses id as U, PR feat(spec,lint,plugin-security): a membership key may yield the caller's own roster rows (valueField: 'id') #31174 test), and U = F is refused by the schema. Only the code attached to "never the same column" is off (S1).
  • Item 2, the examples. TRUE and consistent. One design point on the docs example's OWD (S3).
    • security.md: the os:check object block is byte-identical. The new lines sit in the unmarked policy fragment (a two-line comment and one policy), as the order preferred. A fragment is not type-checked. Its content matches the docs block.
    • rls.mdx: the new block is marked {/* os:check */}. check-skill-examples.ts scans every .mdx under content/docs except references/ (L379-438), so it covers this block. The gate record shows check:skill-examples exit 0 at b9f4ec22, after a full build. The merge brought no change to these files. The block uses the same imports and shapes as the page's existing portal block.
    • Consistency: employee declares user: { type: 'user' } and my_employee_ids: { userField: 'user', valueField: 'id' }. leave_request.employee is lookup to employee. employee_self_service grants allowRead on leave_request, and its own_leave select policy is employee in current_user.my_employee_ids. This is exactly the shape PR feat(spec,lint,plugin-security): a membership key may yield the caller's own roster rows (valueField: 'id') #31174's lint tests accept. Names are snake_case and both objects set sharingModel, so security-owd-unset does not fire.
  • Item 3, "Choose by". TRUE, and no neighbour contradicts it.
    • The old "the person → owner scoping" would have contradicted the new teaching. "the user → owner scoping" plus "a named record (the caller's own roster row too) → a membership key" is right. Rows keyed on a user column are owner scoping; rows keyed on the caller's own roster row are a membership key over id.
    • Neighbours checked: the § intro ("Rows keyed on a named record … declare the set on the roster object"), "Prefer the built-in member_default owner scoping … for the common cases", the external-tier bullet, and SKILL.md's ownership-queue and relationship text. None says owner scoping is the only answer for a person. relationships.md's junction object is many-to-many data modelling, not access, so it stays. No re-scope is owed.
  • Item 4, deletions and tokens. Each cut has a home. No re-wrap.
    • D1, § RBAC: "- Source: node_modules/@objectstack/spec/src/security/permission.zod.ts" (74 B). Fact: that path. Home: references/_index.md L18 (Core schemas). It is in SKILL_MAP for objectstack-data.
    • D2, § RLS: "- Source: node_modules/@objectstack/spec/src/security/rls.zod.ts." (68 B). Fact: that path. Home: references/_index.md L47 (Transitive dependencies). Both permission.zod.ts and object.zod.ts import it, so the entry is stable.
    • D3, § Sensitive fields: "- Source: node_modules/@objectstack/spec/src/data/field.zod.ts" and its blank line (65 + 1 B). Fact: that path. Homes: references/_index.md L13 and rules/field-types.md L31.
    • SKILL.md L462 links references/_index.md, so a reader can reach all three. check:skill-refs exit 0 shows the generated index does not depend on these lines.
    • Sum: 74 + 68 + 66 = 208 bytes. Net growth: 15069 − 14783 = 286. Additions: 286 + 208 = 494 bytes. "in the portal permission set" → "in a permission set" is a needed edit (the block now holds a non-portal policy), not a cut. The policy name portal_orders still says portal.
    • Readings: main 14783 B → ceil(14783/4) = 3696, ceiling 3698 (headroom 2). Head 15069 B → 3768. The head's ratchet exits 0 at 3768 / 3768. The remainder over the old ceiling is +70.
    • The ratchet diff moves one row (security.md 3698 → 3768) and adds a comment. It quotes the ruling verbatim and untranslated (「A:先削重复,余额按实测提高」, record 6513306068), names the three cuts and their generated home, and gives the byte readings. No other row moves.
    • No re-wrap. The two valueField lines both change in content. The Choose-by edit adds one short line, and the next line is untouched. Every other removed line is a cut.
  • Item 5, eval case 12. TRUE and consistent. The JSON parses. Ids run 1-12 with no gaps. Each must_contain appears in expected_output, and the must_not_contain does not.
    • The expected answer matches the skill: the roster key, the policy, "grants nothing by itself", and the refusal code.
    • Risk to a careful answer: the prompt never names the key, yet 2 of the 4 must_contain strings need the name my_employee_ids (S2). must_not_contain: "owner_id == current_user.id" could catch an answer that names it only as the thing to avoid (N5).
    • Sweep: no case in either data-skill eval file rewards "valueField can never be id". Case 6 (the account_member roster, valueField: 'account') stays true.
  • Item 6, rls.mdx. TRUE, and consistent with the skill.
    • The valueField sentence now says "or id". The new paragraph, block and closing paragraph match the docblock and both lint rules. They state the accept side too (a column that references the roster, or the roster's own id).
    • No other sentence on the page says valueField cannot be id. The only other valueField text is the portal block.
  • Item 7, process. Checked.
    • The changeset has '@objectstack/skills': patch. Clause-②: no and Metadata-protocol: none are line-initial in the changeset (L7-8) and in the body (L6-7). The body has Fixes #31176 (L4).
    • The model waiver is in the body and names no model ("ran on the project's default model"). No model identifier appears in the body, the diff or the two commit messages. The card commit's trailers are Co-authored-by: Claude <noreply@anthropic.com> and Claude-Session:. check-commit-card-trailers exits 0.
    • Files: all 6 are inside claim 6712646214. Both changesets fall under .changeset/*.md. The ratchet row is under 6513306068, after the seat's ruling (a). The optional-master edit is ruling (b).
    • Tier: check-governed-merges --pr 31266 exits 3. GOVERNED, Tier H, skills/** ×2. The other four paths are off the register. The size is +100 / −13. The PR is a draft with no assignees. Labels: documentation, size/m, tooling.
    • The merge brings nothing into these files: git diff b9f4ec22 42252a5a over the 6 paths is empty.
    • The DELIBERATE CORRECTION section is accurate. A word diff shows exactly one change: ", and the membership-key notes say a valueField cannot be the roster's own id (membership-key-value-field-invalid)." became ".". The before and after quotes in the body match the file. The note comes from PR docs(skills): teach controlled_by_parent with an optional master in the data skill, permission docs and ADR-0055 #31092 (aa3e21cca) and is still pending in .changeset/ on main. The clause became false with PR feat(spec,lint,plugin-security): a membership key may yield the caller's own roster rows (valueField: 'id') #31174. check-empty-changeset exits 1 on that file alone, with the DELIBERATE CORRECTION remedy. On the head, the CI job "Check Changeset" fails for that same annotation and nothing else. Check-runs read 2026-11-07T21:09Z: 44 runs, 29 success, 12 skipped, 2 in progress, 1 failure (that one).
    • The body's code-site table checks out, apart from one off-by-one (N6).
  • Item 8, other sites. Confirmed below (③).

② Semver level

'@objectstack/skills': patch is right. Skill prose and an eval fixture change, and @objectstack/skills ships both. The docs page is site content with no package. The ratchet script is repo tooling. The edit to data-skill-optional-master.md keeps its own patch line. No platform behaviour moves here (PR #31174 carries its own minors), so Clause-②: no and Metadata-protocol: none are right.

③ Boundary flags

  • Claim. All 6 paths are inside claim 6712646214, plus the seat's rulings (a) and (b). No breach.
  • Ratchet. One row moved, by exactly the measured remainder (+70), under standing ruling 6513306068. The ruling is quoted verbatim in the script and the body, after 208 bytes of real same-file deletion, each with a named home. Not a budget breach.
  • Tier. GOVERNED, Tier H. Landing needs the maintainer's merge or an APPROVED review from GOVERNED_APPROVERS. The body's 「需要你做的」 says so.
  • Changeset gate. check-empty-changeset stays red by design. This record confirms the DELIBERATE CORRECTION: the edit is exactly the false clause, nothing else in the file changes, and restoring it would republish a false sentence.
  • Other sites, confirmed for filing.
    1. content/docs/releases/v18/18-1.mdx L285 ("valueField that is not a reference … is refused"). This is release history and was true when written. Leave it (the seat already ruled).
    2. The generated pages already say "or id": content/docs/references/security/rls.mdx L174, references/data/object.mdx L461. Nothing is owed.
    3. No other published skill teaches membershipKeys or a membership valueField. The valueField in objectstack-ui (dashboards, optionsFrom) is a different concept.
    4. No other skill or docs page (outside release history and references/) teaches owner_id per row or a junction table for a person-as-record case. relationships.md's project_assignment junction is many-to-many data, not access. The owner_id == current_user.id examples in permissions/access-recipes.mdx, permission-metadata.mdx, protocol/objectql/security.mdx and concepts/index.mdx are plain owner scoping, which is still right. Nothing to file.
    5. Extra, not a teaching site: skills/objectstack-data/evals/objects-fields-relationships.json cases 1 and 2 already have a must_not_contain string inside their own expected_output (type: 'password', many_to_many). This is older than the PR and outside its files. It may be worth filing.

④ Design judgment

  • should-fix S1: the edited valueField line ties membership-key-value-field-invalid to the one refusal that code never raises.
    • New text: "valueField: the reference column whose values form the set, or id (never the same column: membership-key-value-field-invalid)".
    • U = F is refused by MembershipKeySchema's superRefine (rls.zod.ts), as a schema error at valueField. The lint loop skips that case. The rule's explanation (L1792-1794) says "the schema refuses that". The body's own table cites that line.
    • The old line had the same structure, but "nor id" gave the code something it did refuse. With that gone, the code now reads as the U = F refusal.
    • Fix at zero cost: "or id (membership-key-value-field-invalid; never the same column)". The code then governs which column F may be, and the U = F rule stands on its own. The byte count is the same, so the ceiling holds.
  • should-fix S2: eval 12 asks for a key name the prompt does not give. must_contain needs my_employee_ids and employee in current_user.my_employee_ids. An answer that is otherwise correct but calls the key self_employee_ids fails 2 of 4. Case 6 has the same pattern, so this follows precedent. But evals are not ratcheted, so the fix is free: add "call the set my_employee_ids" to the prompt.
  • should-fix S3: the rls.mdx example's leave_request declares sharingModel: 'private', which bounds the policy to rows the employee already owns.
    • The same page says RLS "narrows what the earlier layers already allowed; it never widens". sharing-rules.mdx says private is "owner + shares only". object.zod.ts L3745 says private is owner-only.
    • So under this example, a leave request filed for the employee by someone else stays hidden. The policy only narrows the employee's own-owned rows. That is weaker than the "no owner stamp" reason the card gives.
    • PR feat(spec,lint,plugin-security): a membership key may yield the caller's own roster rows (valueField: 'id') #31174's runtime test boots the security plugin without the sharing plugin. It shows the key and the policy, not the OWD composition.
    • No sentence the PR adds is false, so this is not a must-fix. Fix: give leave_request sharingModel: 'public_read', so the self-service policy is what narrows. Or keep private and add a clause saying the request's owner bound still applies. I did not run this end to end.
  • nit N1: the skill's new bullet reads too wide if taken literally. "Tested against a column not referencing the roster" literally includes the roster's own id, which is accepted. The "or another object's id" carve-out implies the exception, and rls.mdx states it outright.
  • nit N2: "is refused" leaves out the rule's bound. rls-predicate-membership-key-target judges only a key whose roster object is in the stack and declared once. The explanation says "not judged here" otherwise. Both the skill and the docs state it without that bound. This is a low risk.
  • nit N3: one ragged line. The new bullet's second line (" nothing by itself. Tested against a column not referencing the roster, or another") is 85 characters, longer than its neighbours. A re-flow would cost about 2 bytes, which the zero headroom cannot pay, so leave it.
  • nit N4: one awkward rls.mdx sentence. "the reference column whose values form the set, or id (valueField)" parses badly. The docs page is not ratcheted, so it could say "the column whose values form the set (valueField: a reference column, or id)".
  • nit N5: eval 12's must_not_contain (owner_id == current_user.id) could catch a careful answer that names it as the thing to avoid. This is a low risk.
  • nit N6: one stale citation in the PR body. The table cites enforceability L1666 for severity: 'error'. That is findings.push({; severity is at L1667.
  • nit N7: one changeset wording. The new changeset says the platform behaviour "shipped in" spec, lint and plugin-security. PR feat(spec,lint,plugin-security): a membership key may yield the caller's own roster rows (valueField: 'id') #31174's changeset is still pending in .changeset/, so the two ship in the same release. "ships in the same release" would be exact. Optional.

Verdict: PASS

- security.md: the refusal code on valueField no longer reads as the
  same-column refusal (the schema refuses that case itself); same bytes.
- eval: the prompt names the key the assertions require, and the
  must_not_contain matches only an owner-scoped policy, not a mention.
- rls.mdx: leave_request is public_read so the policy is what scopes its
  rows, with the reason; the valueField sentence reads cleanly.
- changeset: the platform change ships in the same release.

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

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: ebb6bce99cf7ff152a283afb8ae103a3bb265d14
Local-runs: probe — a byte count of skills/objectstack-data/rules/security.md at the head, read from git objects with no checkout, because the delta edits that file at zero headroom (15069 bytes, so 3768 of 3768, unchanged).
Scope: delta review. One commit, ebb6bce99 (only parent 42252a5a8, a normal push). git diff 42252a5a8 refs/seat3/pr-31266 touches 4 files: skills/objectstack-data/rules/security.md (one line), skills/objectstack-data/evals/hooks-security-seeds-datasources.json (case 12), content/docs/permissions/rls.mdx (two passages), .changeset/data-skill-person-is-a-record.md (one sentence). Also read: the PR body v2 (stored by the seat, read back identical) and the gate record gates-ebb6bce9.txt (87 commands, 86 exit 0, check-empty-changeset exit 1 by design). Cited code read on origin/main: packages/spec/src/data/object.zod.ts L3745 (sharingModel enum).

① Derived judgments

  • S1 is fixed. The valueField line now reads "or id (membership-key-value-field-invalid; never the same column)". The code is no longer pinned to the same-column case, which the schema's superRefine refuses. It costs the same bytes: security.md is 15069 bytes at both heads.
  • S2 is fixed. Case 12's prompt now names the set (Call the set my_employee_ids.), so the two must_contain strings that need it are fair. must_not_contain is now using: 'owner_id, which catches an owner-scoped policy but not a sentence that names owner_id == current_user.id as the thing to avoid (N5). The JSON parses.
  • S3 is fixed. leave_request is now public_read, a member of the sharingModel enum. The added sentence says why: RLS only narrows what the org-wide default already allowed, which matches the same page's § How policies compose. Under private the policy would narrow the employee's own rows, and a request someone else filed for them would stay hidden. That is true.
  • N4 is fixed. The rls.mdx sentence now reads "the column whose values form the set (valueField: a reference column, or id)".
  • N6 is fixed in the body (L1667).
  • N7 is fixed. The changeset now says the platform change is in this same release, and that this note covers only the teaching. That is true while PR feat(spec,lint,plugin-security): a membership key may yield the caller's own roster rows (valueField: 'id') #31174's note is still pending.

② Semver level

Unchanged: '@objectstack/skills': patch.

③ Boundary flags

The 4 files are all inside the claim and the seat's rulings. Nothing new is added. The ratchet row stays at 3768, equal to the measurement. check-empty-changeset stays red only on .changeset/data-skill-optional-master.md, the deliberate correction the seat ruled and confirms.

④ Design judgment

The delta does only what the first record asked for. N1–N3 stay as they are.

Verdict: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

维护者速读

席位核对 — ACCEPT · domain:skills#3 · head ebb6bce99cf7ff152a283afb8ae103a3bb265d14

  1. 形态: draft → main,Fixes #31176;'@objectstack/skills': patch,Clause-②: no,Metadata-protocol: none;作者 objectstack-fleet[bot];6 个文件,在认领面(认领 6712646214)与席位裁定之内。
  2. 前提实测: 复核逐项对照 main 代码:rls.zod.ts 成员键文档块与 superRefine(同列由 schema 拒绝)、validate-membership-key-declarations.ts(membership-key-value-field-invalid,接受 id)、validate-rls-predicate-enforceability.ts(rls-predicate-membership-key-target:拒绝别的对象的 id、非引用列、引用他对象的列;接受引用名册的列与名册自身 id)、plugin-security 的 readDeclaredKey(取 id 即本人名册行)、object.zod.ts 的 sharingModel 枚举。
  3. 门禁(钉 head ebb6bce9): dev 派生门禁 87 条中 86 条 exit 0,1 条 check-empty-changeset exit 1(设计如此,见第 4 条);trailers 0;ratchet 0;check-runs 49 条:33 success、14 skip、2 失败(均为 Check Changeset,同一原因);check-governed-merges --pr 31266:Tier H(skills/**)。
  4. 接受的偏差与更正确认: 本 PR 有意修改了一条待发布的变更说明 .changeset/data-skill-optional-master.md(来自本车道的 PR docs(skills): teach controlled_by_parent with an optional master in the data skill, permission docs and ADR-0055 #31092):只删去分句 "and the membership-key notes say a valueField cannot be the roster's own id (membership-key-value-field-invalid)",其余一字未改。原因:PR feat(spec,lint,plugin-security): a membership key may yield the caller's own roster rows (valueField: 'id') #31174 之后该句为假,不改则下次发布说明会同时印出两种相反说法。Check Changeset 与 check-empty-changeset 对此保持红,按 [finding] the skip-changeset label suppresses the DELIBERATE-CORRECTION refusal whose own text says 「no label and no diff shape makes that safe」 — declared contract against enforced behaviour #18375 的规定由本席在此确认,不加 skip-changeset。预算:删 3 行(D1–D3)后余量 +70 按 6513306068 上调 security.md 一行,席位事前裁定。S1–S3、N4、N6、N7 已在增量中改正;N1–N3 不改。
  5. 范围外去向: 无新增。v18.1 发布说明 L285 为历史页,不动。复核另见 objects-fields-relationships.json 案例 1、2 的 must_not_contain 与其 expected_output 自相冲突(早于本 PR),另行立卡。
  6. 落地条款: Tier H。保持 draft,挂 pm:awaiting-merge,请 os-zhuang、hotlong 审批;获批后本席过落地前检、清标、ready、auto-merge;维护者直合亦可(Check Changeset 红按上条确认)。

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

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 0a8ff459fa3846212aa308f8ec3918e083d72f7a
Local-runs: none
Scope: delta review: 0a8ff459 is a plain merge commit, with parents ebb6bce9 (the head reviewed in the previous record, PASS) and origin/main 85c01247. Against ebb6bce9, the five other PR files (security.md, rls.mdx, the two changesets, the ratchet script) are byte-identical, and 85c01247 touched none of them since the merge base. The one resolved file is skills/objectstack-data/evals/hooks-security-seeds-datasources.json. Also read: the PR body v3 (/mnt/project-files/card-31176/body-v3.md), which renumbers the case and adds a merge section.

① Derived judgments

  • The merge resolution is right. The fixture at the head keeps every case 85c01247 carries, byte for byte (PR docs(skills): teach the hook body first in data-hooks.md, drop hook ctx.api methods that do not exist, guard nullable hook and formula predicates #31201's cases 12 and 13 included), and appends this card's case as id 14. Its prompt, expected_output, must_contain and must_not_contain are identical to the reviewed case 12 (compared field by field). Ids 1-14 are unique. git diff 85c01247 on the file is exactly the added case.
  • Nothing else moved. The PR's net diff against main is the same six files as at ebb6bce9, and only the case id changes.
  • The body matches the head. v3 renames "case 12" to "case 14" in three places and adds the merge section. Nothing else changes.

② Semver level

Unchanged: '@objectstack/skills': patch. Check Changeset stays red only on the deliberate correction to .changeset/data-skill-optional-master.md, which the seat already ruled and confirmed.

③ Boundary flags

The merge adds no path and no new claim. The ratchet row stays at 3768, equal to the measurement.

④ Design judgment

The merge does only what the conflict needed. The previous record's findings stand.

Implemented-by: claude/project-thread-h4z1pw
Reviewed-by: session_01J8VVAj7CvjkGXvgXzd6RZ7

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

席位核对 — 增量 ACCEPT · domain:skills#3 · head 0a8ff459fa3846212aa308f8ec3918e083d72f7a

0a8ff459 是普通合并提交(父提交 ebb6bce9 即原 ACCEPT 6713452578 所钉 head,和 origin/main 85c01247),只为解开与 PR #31201 在 eval fixture 上的冲突。os-zhuang 在 ebb6bce9 的批准因这次推送失效,需重新批准。

  1. 形态: 不变。draft → main,Fixes #31176,'@objectstack/skills': patch,Clause-②: no,Metadata-protocol: none;assignee huangyiirene;PR 对 main 仍是同样 6 个文件。
  2. 合并实测: security.md、rls.mdx、两个 changeset、棘轮脚本与 ebb6bce9 逐字节相同,且 main 自合并基起未改动它们;hooks-security-seeds-datasources.json 保留 main 的全部用例(含 PR docs(skills): teach the hook body first in data-hooks.md, drop hook ctx.api methods that do not exist, guard nullable hook and formula predicates #31201 新增的 12、13),本卡用例内容不变,改号为 14,id 1-14 唯一。PR 正文已更新为 v3(用例号与合并说明)。
  3. 门禁(钉 head 0a8ff459): 线程实跑 JSON 解析、token ratchet(security.md 3768 / 3768)、skill-examples(286 例)、identifier-liveness、trailers 均 exit 0;check-runs 49 条全部完成:33 success、14 skip、2 failure,两条失败均为 Check Changeset,原因仍是本 PR 对 .changeset/data-skill-optional-master.md 的 DELIBERATE CORRECTION(席位已裁定,原 ACCEPT 第 3 条已确认,按 [finding] the skip-changeset label suppresses the DELIBERATE-CORRECTION refusal whose own text says 「no label and no diff shape makes that safe」 — declared contract against enforced behaviour #18375 保持红色、不加 skip-changeset)。增量复核 6715347692 PASS。
  4. 接受的偏差: 与原 ACCEPT 相同。
  5. 范围外去向: 无新增。
  6. 落地条款: Tier H。保持 draft,已重新请 os-zhuang / hotlong 批准;批准后由维护者翻 ready 入队。

@objectstack-fleet
objectstack-fleet Bot requested a review from os-zhuang November 8, 2026 01:49
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 8, 2026 07:22
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 8, 2026 07:22
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 8, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Nov 8, 2026
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J8VVAmphavoevUax9bUVkY
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

Contract-review-claim: session_01J8VVAj7CvjkGXvgXzd6RZ7 · head 1d88c9b · 2026-11-08T07:53Z

Delta contract review of PR #31266 (#31176) from 0a8ff459 (delta review 6715347692): one merge of origin/main resolving the eval-fixture conflict with PR #31223. The seat review runs at CONTRACT_REVIEW_TIER in an isolated subagent.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 1d88c9b8b9fd2e6d4bc6c4ae21546b7342110e23
Local-runs: probe — ran node scripts/check-skills-token-ratchet.mjs and a JSON.parse of the eval fixture in a detached worktree at 1d88c9b8, to confirm the merged ratchet and fixture hold on the new head.
Scope: delta review from 0a8ff459 (prior delta review 6715347692, PASS). 1d88c9b8 is a plain merge commit, with parents 0a8ff459 and origin/main ae816c40 (merge base 85c01247). Also read: the PR body v4 (/mnt/project-files/card-31176/body-v4.md), which renumbers the case to 17 and adds a second merge section.

① Derived judgments

  • Only the eval fixture conflicted. A re-run of the merge (git merge-tree 0a8ff459 ae816c40) conflicts on skills/objectstack-data/evals/hooks-security-seeds-datasources.json alone. Main's change there is PR docs(skills): objectstack-data carries the hosted build agent's metadata-authoring judgment (cloud#3659 A, port 1 of 3) #31223 (0e13561b4), which added cases 14-16. The head differs from that auto-merge tree only in this file.
  • The ratchet script merged cleanly. Main changed it since the merge base (0e13561b4, eight other rows). Main did not touch the security.md row. git diff ae816c40 1d88c9b8 on the script is exactly this PR's hunk (3698 -> 3768 and its comment), byte-identical to the hunk at 0a8ff459, only shifted in line number. The script at the head equals git's own auto-merge.
  • The other four files did not move. security.md, rls.mdx and the two changesets have the same blob at 0a8ff459 and at the head. Main did not touch them since the merge base.
  • The fixture resolution is right. git diff ae816c40 on the fixture is a pure append of one case. Main's 16 cases are kept byte for byte, and the top-level keys match. The new case is id 17. Its prompt, expected_output, files and assertions (must_contain, must_not_contain) are identical to case 14 at 0a8ff459 (compared field by field). Ids 1-17 are unique. The JSON parses (17 cases).
  • The ratchet passes on the head. Probe: exit 0. security.md is 15069 bytes, 3768 / 3768, headroom 0.
  • The body's merge claims hold. Parents, "resolves only that file", "PR docs(skills): objectstack-data carries the hosted build agent's metadata-authoring judgment (cloud#3659 A, port 1 of 3) #31223 added eval cases 14–16", "main's cases 1–16 byte for byte", JSON/ids and the ratchet result all match the head. The check:skill-examples, check:skill-identifier-liveness and check-commit-card-trailers results were not re-run here.

② Semver level

Unchanged: '@objectstack/skills': patch. Check Changeset is red only on the deliberate correction to .changeset/data-skill-optional-master.md. The CI log names that file alone, with the DELIBERATE CORRECTION remedy text. The seat has already ruled and confirmed it.

③ Boundary flags

The merge adds no path and no new claim. The ratchet row stays at 3768, equal to the measurement. Main's eight new ratchet rows are main's own, untouched by this PR.

④ Design judgment

The merge does only what the conflict needed. The previous records' findings stand.

Nit (not blocking): the body's "What it teaches" table still says "eval 12" in the roster/policy row. It is now case 17. The same stale label survived v3.

Gates: CI check-runs on 1d88c9b8 (REST read): 14 success, 14 skipped, 1 failure, 17 in progress. The one failure is Check Changeset, which is red by design on the deliberate correction. A second Check Changeset run is in progress. No other check has failed. The remaining runs (Lint, Type Check, Test Core 1-4/8, Build Docs, Live E2E UI) had not finished when this was read.

Implemented-by: claude/project-thread-h4z1pw
Reviewed-by: session_01J8VVAj7CvjkGXvgXzd6RZ7

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#3 · huangyiirene · session_01J8VVAj7CvjkGXvgXzd6RZ7

席位核对 — 增量 ACCEPT · domain:skills#3 · head 1d88c9b8b9fd2e6d4bc6c4ae21546b7342110e23

1d88c9b8 是普通合并提交(父提交 0a8ff459 即增量 ACCEPT 6715817688 所钉 head,和 origin/main ae816c40),只为解开与 PR #31223 在 eval fixture 上的冲突(07:45Z 被合并队列以 MERGE_CONFLICT 移出)。更正:上次增量 ACCEPT 说 os-zhuang 的批准因推送失效,不对。按 AGENTS.md,Tier H 的授权批准落在任一提交上、未被撤销即有效,每个 PR 用一次,之后的推送也由本席落地;os-zhuang 在 ebb6bce9 的 APPROVED 仍有效。

  1. 形态: 不变。ready → main,Fixes #31176,'@objectstack/skills': patch,Clause-②: no,Metadata-protocol: none;assignee huangyiirene;PR 对 main 仍是同样 6 个文件(+105/−14)。
  2. 合并实测: 只有 hooks-security-seeds-datasources.json 冲突;main 的用例 1-16 逐字节保留,本卡用例逐字段不变,改号为 17,id 1-17 唯一;棘轮脚本自动合并,本 PR 的改动不变,其余 4 个文件与 0a8ff459 同一 blob。PR 正文已更新为 v4。
  3. 门禁(钉 head 1d88c9b8): 线程实跑 JSON 解析、token ratchet(security.md 3768 / 3768)、skill-examples(286 例)、identifier-liveness、trailers 均 exit 0;Check Changeset 红仍是本 PR 对 .changeset/data-skill-optional-master.md 的 DELIBERATE CORRECTION(按 [finding] the skip-changeset label suppresses the DELIBERATE-CORRECTION refusal whose own text says 「no label and no diff shape makes that safe」 — declared contract against enforced behaviour #18375 保持红色);其余 check-runs 在跑,落地交给 auto-merge 与必需检查。增量复核 6718997968 PASS。
  4. 接受的偏差: 与原 ACCEPT 相同。增量复核的 nit(正文教学表仍写「eval 12」)不改。
  5. 范围外去向: 无新增。
  6. 落地条款: Tier H,批准已有。本席重挂 auto-merge。

@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 8, 2026 07:59
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 8, 2026
Merged via the queue into main with commit 37e2c14 Nov 8, 2026
47 of 49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-h4z1pw branch November 8, 2026 08:27
@objectstack-fleet objectstack-fleet Bot removed the pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue label Nov 8, 2026
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 size/m tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

3 participants