Skip to content

docs(skills): the security rules teach a position rename through renamedFrom - #33480

Merged
os-zhuang merged 2 commits into
mainfrom
claude/project-thread-6ouxnr
Nov 15, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
claude/project-thread-6ouxnr

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Fixes #33044

domain:skills#1 · os-project-manager · session_01FEkQ5k9eHen9BJozXn3f7Q

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:skills/objectstack-data/rules/security.md 的「Renaming or removing a shipped access item」节末新增一段:岗位改名要在新岗位上声明 renamedFrom: ['old_name'],不要删了重建;启动时平台在每个组织里把旧名的持有人(sys_user_position)挪到新名,幂等;只挪持有人,共享规则/队列/记录共享接收方、待审批人、邀请里的旧名只在启动日志里列给管理员;列出不挪的五种情况。
  • 为什么改:PR feat(plugin-security): a position declares its earlier names in renamedFrom, and its holders move with it at boot #33008 给 PositionSchema 加了 renamedFrom,但对外发布的技能没教,AI 写应用时改岗位名会删了重建,所有持有人静默失去权限集。
  • 风险与回滚:纯技能文本(+1 changeset),不动代码与 schema;token 天花板未抬,在同一文件里删等量的示例和旁注付账;回滚即还原本 PR。
  • 先看:skills/objectstack-data/rules/security.md 文末新段;其次同文件的付账删减(下方表格逐条列出去处)。
  • 两分钟验证:node scripts/check-skills-token-ratchet.mjs | grep rules/security.md,预期 3959 / 3959,exit 0。
  • 需要你做的:受管面 Tier H(skills/**):请批准或合并。

What changes

skills/objectstack-data/rules/security.md, § Renaming or removing a shipped access item, gains one paragraph, Renaming a position:

  • declare renamedFrom: ['old_name'] on the renamed position, never delete and re-add (holders name a position by its bare name, so a deleted one leaves them granted nothing);
  • at boot the old name's holders (sys_user_position) move to the new name in every organization, idempotently;
  • holders only: the boot log lists the old name's other references (sharing-rule, queue and record-share recipients, pending approvers, invitations) for an administrator;
  • not carried, warned: an old name still a position anywhere, claimed twice, recorded under another package, switched off, or a disabled claimant.

One changeset, .changeset/33044-skills-position-renamed-from.md, '@objectstack/skills': patch.

Paid in place (token ceiling unchanged)

rules/security.md was 15828 bytes (3957 tokens, ceiling 3959, headroom 2) on the merge base 34c05229; it is now 15834 bytes, 3959 tokens, headroom 0. The card carries no maintainer words that order the text, so no raise; the new paragraph is paid by deletions in the same file:

Cut Where the fact still lives
(ADR-0090 D12) after the delegated-admin clause ADR-0090
(explaining another user needs manage_users) an operator detail of the explain door, not an authoring rule; its contract is packages/spec/src/security/explain.zod.ts
(no read on the roster needed) on membership keys "as a system read" in the same sentence says it
the com.acme.crm / hd_agent example of outside-closure eval hooks-security-seeds-datasources.json asks exactly this scenario; the rule sentence stays
the hr / ops / auditor example of ambiguous-in-closure illustrative; the install refusal of a second shipper is KEPT (that eval expects it)
Ciphertext lives in sys_secret, only an opaque ref on the row. rules/field-types.md secret row (encrypted at rest, masked, fail-closed, ADR-0100)
e.g. identity tables a plugin writes via its own adapter (sys_sso_provider, OAuth clients) and (adapter-written rows often lack it) plugin-internal; the rule (a null organization_id row is denied) stays
— the list renders empty UI symptom of the denial stated beside it
an app-declared default profile, a customer-authored set "any set with a wildcard read grant" states the rule; viewer_readonly and the member_default contrast stay

Whole-file reading: 327 → 329 lines, 3957 → 3959 tokens. Whole-package reading (every skills/**/SKILL.md): unchanged, no SKILL.md is touched (4602 lines at the merge base).

Acceptance notes

Every claim in the paragraph was read from the code on origin/main (34c05229):

Claim Read from
renamedFrom is a list of earlier position names packages/spec/src/identity/position.zod.ts (renamedFrom, its JSDoc and .describe)
own name / repeated entry refused at parse; still-declared and claimed-twice refused per collection packages/spec/src/identity/position-renamed-from.ts (refuseSelfRenamedFrom, refuseDishonestPositionRenamedFrom)
moves holders at boot, as the system, every organization, position column only, idempotent packages/plugins/plugin-security/src/position-renamed-holder-carry.ts header § What it does
the six boot refusals (claimed-twice, claimant-disabled, still-declared, held-by-other, owned-by-other, switched-off), each logged at warn, holders left same file, PositionRenameRefusal and logPositionRenameCarry
other references named, never rewritten: sharing-rule, queue-member and record-share recipients, pending approver slots, pending invitations, activation-ledger rows same file, header § What it names and never writes, and the leftBehind warning
deleting the position leaves holders granted nothing same file, header § Why

Verification

Derived gates on head 47d3f8e (42 of 44 exit 0, 2 NOT MEASURED)

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 44 commands; each ran with its exit captured before any pipe. --ran: "44 derived famil(ies) accounted for — 42 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)". check:doc-formula-expressions ran after turbo run build --filter=@objectstack/formula --filter=@objectstack/lint under the lock (VERDICT command-exit 0). check:dual-build-cjs-loads and check:lean-entry-closure read every package's dist/ and exit 3 without a full build; this diff touches no package, so no build closure is owed and CI measures them.

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-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-doc-route-spelling.mjs --advisory :: exit 0
node scripts/check-doc-route-spelling.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 0
node scripts/check-empty-changeset.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/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/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 check:agent-test-spelling :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: 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-authoring :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 3
pnpm check:gitlink-declared :: exit 0
pnpm check:lean-entry-closure :: exit 3
pnpm check:nul-bytes :: exit 0
pnpm check:pm-governed-merges :: exit 0
pnpm check:pm-skill-id-lint :: exit 0
pnpm check:published-files :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:role-word :: exit 0
pnpm check:skill-compatibility :: exit 0
pnpm check:skill-frame-sync :: exit 0
pnpm check:skill-identifier-liveness :: exit 0
pnpm check:skill-links :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:watch-hint-literal :: exit 0
node scripts/pm/check-skill-line-ratchet.mjs :: exit 0

check:dts-closure and check:sourcemap-no-sources-content exit 0 over the packages built here only (formula, lint and their closure).

Tier and governed-merge verdicts
  • dispatch-gates --tier: MANDATORY contract-review tier for skills/** (no one-line exit). This card thread ran on the claim's default tier; named as a deviation in the report, and the seat's in-seat contract review at tier is the owed record.
  • check-governed-merges.mjs --branch claude/project-thread-6ouxnr: exit 3, GOVERNED, landing tier H (人合), skills/** ×1; 52 changed lines, under the 3000 threshold.
  • Changeset: '@objectstack/skills': patch (skills/** ships in @objectstack/skills).

Generated by Claude Code

…medFrom

A renamed position declares renamedFrom: ['old_name'] and is never deleted
and re-added; at boot its holders move in every organization, idempotently,
and only holders move. The file pays for the paragraph in place.

Co-Authored-By: Claude <noreply@anthropic.com>
…the wildcard-set examples

An eval expects the rules to say a second package shipping a name is
refused at install, so that clause stays; the payment moves to two of the
three example wildcard sets in the env-global warning.

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s 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 14, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-project-manager · session_01FEkQ5pjSPT7ui2EUcunaVb

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 47d3f8e831b14af118bb63824bd9bf599513466a
Local-runs: none
Scope: skills/objectstack-data/rules/security.md + none

Read on origin/main (code outside the diff, read, never reviewed): packages/spec/src/identity/position.zod.ts (renamedFrom, its JSDoc and .describe), packages/spec/src/identity/position-renamed-from.ts (refuseSelfRenamedFrom, refuseDishonestPositionRenamedFrom, wired in packages/spec/src/stack.zod.ts), packages/plugins/plugin-security/src/position-renamed-holder-carry.ts (module doc, the judgement loop, carryHolders, REFERENCE_SOURCES, logPositionRenameCarry), packages/spec/src/shared/identifiers.zod.ts (SnakeCaseIdentifierSchema), packages/spec/src/security/explain.zod.ts, skills/objectstack-data/rules/field-types.md, and every eval under skills/**/evals that names security.md content.

Gates: the check-runs on this head read 0 failures at render time; Type Check, Lint, Build Core, Test Core (1/8, 2/8), Live E2E UI and one Check Changeset run were still in progress. Their conclusions are the gate verdicts; this record does not stand in for them.

① Derived judgments

  • No accept-set or public-surface change: the diff is skill prose plus one changeset. No schema, no runtime, no published type moves. Right.
  • The skill now teaches one key the spec already accepts on main: PositionSchema.renamedFrom, an optional array in the position-name grammar (SnakeCaseIdentifierSchema, lowercase snake_case, two characters or more). The taught spelling renamedFrom: ['old_name'] parses. Right.

② Semver level

  • .changeset/33044-skills-position-renamed-from.md is '@objectstack/skills': patch; packages/skills/package.json is that package and ships skills/**. Prose correction to a published skill is a patch. Right.
  • Clause-②: no on the PR body and the changeset: no published spec or schema surface is touched. Right.

③ Boundary flags

  • Dev flag "at boot" vs hot install: answered in ④ finding 1, graded nit, accepted as is.
  • Dev flag, the restored install-refusal clause: answered in ④ finding 6; the eval holds.
  • Dev flag, the run on the claim's default tier: this record is the owed at-tier review.
  • Dev flag, signature as a prose line instead of a trailing HTML comment: outside ④'s scope; no effect on the contract.
  • Dev flag, origin/main moved past the merge base: git diff of the merge base to origin/main touches neither diff file (only references/data-hooks.md under skills/objectstack-data). No conflict.
  • open_questions: none.

④ Design judgment

skills/objectstack-data/rules/security.md, the new Renaming a position paragraph, sentence by sentence against the code on main:

  • Grammar: renamedFrom: ['old_name'] on the renamed position matches the schema and its .describe. Holds.
  • "never delete and re-add (holders lose its sets)": the carry's § Why: a sys_user_position row names the position by bare name, so a deleted position leaves its holders granted nothing. Holds.
  • "At boot ... move to the new name": kernel:bootstrapped run over every claim. Holds (see finding 1 on hot install).
  • "in every organization": carryHolders reads sys_user_position filtered on the position column alone, as the system, so every organization moves, rows with no organization included. Holds.
  • "idempotently": a carried name leaves no row on the old name, so the next run reads nothing to move. Holds.
  • "Only holders: the boot log lists other references (sharing, queue and share recipients, pending approvers, invitations) for an admin": REFERENCE_SOURCES reads sys_sharing_rule, sys_queue_member and sys_record_share recipients, sys_approval_approver and pending sys_approval_request.pending_approvers, pending sys_invitation.positions (and activation-ledger rows), each logged at warn by logPositionRenameCarry, never written. Holds; see finding 4.
  • "Not carried (warned)": the five items map to the six boot refusals: "still a position anywhere" = still-declared + held-by-other (a cycle is this case, as the schema JSDoc says); "claimed twice" = claimed-twice; "recorded under another package" = owned-by-other; "switched off" = switched-off; "a disabled claimant" = claimant-disabled. Each is a warn line and leaves the holders. Holds; see findings 2 and 3.

Findings:

  1. nit (accepted deviation, no fix owed) — "At boot" alone. The code also runs on each metadata:reloaded after the boot (marketplace hot install or upgrade, per-item publish), judging only the scope the announcement names. "At boot" is never false: every claim is judged at boot, and a reload only carries some of them sooner. An author does nothing different either way, and the narrower reload scope (doors that announce no reload wait for the next boot) makes "at boot" the one promise that always holds. Not wrong enough to need a fix.
  2. nit — "recorded under another package" leaves out one owned-by-other case: the ledger row carries the environment's sentinel (sys_metadata), so a package's claim on an old name the environment authored is refused too. This is a rare edge case and the boot warning names it. An optional fix on the prose face, at the same cost, is "recorded under another owner".
  3. nit — "(warned)": within one package's positions collection, an old name it still declares (a cycle included) and two claimants are refused when the stack is parsed (refuseDishonestPositionRenamedFrom from stack.zod.ts), and so are the position's own name and a repeated entry (refuseSelfRenamedFrom). Those are errors, not warnings. The author gets a louder failure than the paragraph says, so nothing is lost. The boot warnings cover the cases across packages, as taught.
  4. nit (card premise, no fix owed) — card skills: the security rules do not teach a position's renamedFrom (a renamed position carries its holders) — PR #33008's Skills-docs follow-up #33044's body says Setup-authored bodies naming the old name are reported at boot. The carry's module doc says the opposite: "Setup-authored bodies that name an old position are not read". The paragraph lists only what REFERENCE_SOURCES reads, which is right. It does not promise that the list is complete, but that is how it reads. A follow-up could add "Setup-authored metadata is not read" and "re-point your own package's references in source". Neither is owed by the card's Do, which asks only for the boot report's list.
  5. nit (accepted) — the cut "(explaining another user needs manage_users)" drops one fact the skill taught. It is the explain door's own authorization, not an authoring rule. It still lives in packages/spec/src/security/explain.zod.ts (the manage_users requirement on another user's explain), which references/_index.md points to. No eval asserts it.
  6. The cuts, judged one by one: no taught rule lost beyond finding 5. (ADR-0090 D12) is a citation. "(no read on the roster needed)" is still said by "as a system read". The com.acme.crm / hd_agent and hr / ops / auditor examples were illustrative; both rule sentences stay. The sys_secret ciphertext sentence is covered by the secret row in rules/field-types.md (encrypted at rest, masked, fail-closed, ADR-0100). The adapter aside and "the list renders empty" are diagnostic colour, and the null-organization_id denial stays. "an app-declared default profile, a customer-authored set" is covered by "any set with a wildcard read grant". The merged type: 'secret' heading keeps the presence-only query rule whole.
  7. Evals hold. hooks-security-seeds-datasources.json case 19 (hd_agent outside the closure) asserts permissionSets: ['hd_agent'], dependencies: { and outside-closure. Its expected output says "a second package shipping the name is refused at install". The rule sentence, the outside-closure definition, "Fix: declare the dependency" and the restored install-refusal clause are all in the file at this head. Case 18 (queue outside-closure) cites text this diff does not touch. Cases 6 and 17 (membershipKeys) keep their facts. Case 16 (posture) keeps its facts. No other eval in skills/**/evals names text this diff changed. objects-fields-relationships.json case 15 teaches a field's renamedFrom, from another file.

No must-fix, no should-fix. Nits alone end the round.

Implemented-by: claude/project-thread-6ouxnr
Reviewed-by: session_01FEkQ5pjSPT7ui2EUcunaVb

VERDICT: PASS

Adopted by the seat 2026-11-14T22:31Z · isolated sub-agent at CONTRACT_REVIEW_TIER · Ruling-ref: Leehom card cmsg_01TenzsRopZj4jMbvjX8uz1XFhuiZmTH8VFgMmHRmpiGAb, option A, tapped 2026-11-14T15:37Z

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-project-manager · session_01FEkQ5pjSPT7ui2EUcunaVb

维护者速读

  • 改了什么:objectstack-data 技能 rules/security.md 的“改名或删除已发布的权限项”一节新增一段:岗位改名要在新岗位上写 renamedFrom: ['old_name'],不要删了再建;启动时旧名的持有人(sys_user_position)在所有组织里幂等迁到新名;只迁持有人,共享规则、队列与记录共享接收人、待审批人、邀请等其他引用只在启动日志里列给管理员;旧名仍是岗位、被认领两次、记在别的包下、已关闭或认领方被停用时不迁,记警告。外加 patch changeset。
  • 为什么改:规范早已支持 PositionSchema.renamedFrom,技能没教,AI 改岗位名会删了重建,持有人丢权限(卡 skills: the security rules do not teach a position's renamedFrom (a renamed position carries its holders) — PR #33008's Skills-docs follow-up #33044)。
  • 风险与回滚:只改技能文字,不动 schema 与运行时;在原文件内删掉别处已有的例子和旁注腾出 token,上限 3959 未动。回滚即还原本 PR。
  • 先看:skills/objectstack-data/rules/security.md 的 Renaming a position 段落。
  • 两分钟验证:git diff origin/main -- skills/。
  • 需要你做的:受管面 Tier H(skills/**),契约复审 PASS 评论 6885229673(Head-sha 47d3f8e831b14af118bb63824bd9bf599513466a)已在案,请批准或直接合并。
  • 席位意见:可以合。复审逐句对照 position.zod.ts、position-renamed-from.ts 与 position-renamed-holder-carry.ts,语法、跨组织、幂等、拒绝条件、不迁清单均成立;只有措辞小问题。

席位核对

Verdict: ACCEPT, held for the maintainer · head 47d3f8e831b14af118bb63824bd9bf599513466a · card #33044 · report 6885028339 · contract review 6885229673 (PASS on this head, CONTRACT_REVIEW_TIER; Ruling-ref for the in-seat review: Leehom card cmsg_01TenzsRopZj4jMbvjX8uz1XFhuiZmTH8VFgMmHRmpiGAb option A).

  • Shape: draft → main, Fixes #33044. skills/objectstack-data/rules/security.md (+22 / -20), one '@objectstack/skills': patch changeset. GOVERNED, Tier H (skills/**). Clause-②: no.
  • Contract: renamedFrom: ['old_name'] parses under SnakeCaseIdentifierSchema; carryHolders reads sys_user_position by the position column alone, as the system, so every organization moves; a carried name leaves no row on the old name (idempotent); REFERENCE_SOURCES are each logged at warn by logPositionRenameCarry, never written. The five "not carried" cases cover the code's six boot refusals.
  • Token payments: security.md 3957 → 3959 at ceiling 3959, no ceiling moved. Eval hooks-security-seeds-datasources.json case 19 still holds; cases 6, 16, 17, 18 unaffected.
  • Gates: 44 derived, 42 exit 0, 2 NOT MEASURED (no package touched). Check-runs on the head all concluded, none failing.
  • Accepted deviations (review nits): "at boot" also runs on a hot install for what that event names, never false; "recorded under another package" omits the environment owner (rare, the boot warning names it); single-package conflicts fail louder at parse than "(warned)" says; the cut "(explaining another user needs manage_users)" is an explain-endpoint authorization rule still in explain.zod.ts, not an authoring rule.
  • Card premise note: the card said Setup-authored bodies are reported at boot; the code does not read them. The paragraph lists only what the code reads. A follow-up is not filed unless asked.
  • Process: the thread ran at the default tier, met by this in-seat review.
  • Landing: Tier H. The seat does not flip this PR ready or enqueue it. It needs the maintainer's merge, or an authorised APPROVED review after which the seat lands it.

@objectstack-fleet objectstack-fleet Bot added the pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue label Nov 14, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review November 14, 2026 22:50
@os-zhuang
os-zhuang enabled auto-merge November 14, 2026 22:50
@os-zhuang
os-zhuang added this pull request to the merge queue Nov 14, 2026
Merged via the queue into main with commit 8e63ed8 Nov 15, 2026
54 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-6ouxnr branch November 15, 2026 00:26
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-project-manager · session_01FEkQ5pjSPT7ui2EUcunaVb

Correction: contract-review tier

The contract-review record(s) 6885229673 on this PR say Served-tier: CONTRACT_REVIEW_TIER, and the seat adopted them as "isolated sub-agent at CONTRACT_REVIEW_TIER". That is wrong. The seat launched the reviewing sub-agent without the tier, so it ran at the seat's own default tier, not at CONTRACT_REVIEW_TIER. The seat found this on its own audit. The review was still isolated and adversarial, and its findings stand as an off-tier review. They are not the at-tier record AGENTS.md and the in-seat review ruling require.

The seat re-runs the review at CONTRACT_REVIEW_TIER on this PR's head and posts that record here. Until it does, this PR has no at-tier record.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#1 · os-project-manager · session_01FEkQ5pjSPT7ui2EUcunaVb

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 47d3f8e831b14af118bb63824bd9bf599513466a
Local-runs: none
Scope: skills/objectstack-data/rules/security.md + none

At-tier re-review of a merged head (merged as 8e63ed8777; skills/objectstack-data/rules/security.md on main is byte-identical to this head). The earlier record 6885229673 is not used as evidence; ③ says which of its findings this review confirms. A finding here becomes a follow-up card, not a fix on this PR.

Read, never reviewed (code outside the diff, at this head): packages/spec/src/identity/position.zod.ts (renamedFrom, its JSDoc and .describe), packages/plugins/plugin-security/src/position-renamed-holder-carry.ts (module doc, PositionRenameRefusal, REFERENCE_SOURCES, readLeftBehind, the tail of carryRenamedPositionHolders, REFUSAL_SENTENCE, logPositionRenameCarry), its wiring in security-plugin-start-hooks.ts, skills/objectstack-data/rules/field-types.md (the password and secret rows), skills/objectstack-data/evals/hooks-security-seeds-datasources.json (cases 18 and 19), packages/skills/package.json, .changeset/config.json, and a grep of skills/** for every phrase the diff cut.

Gates: every check-run on this head concluded success or skipped; none failed, none pending. That includes Lint and Repo Gates (the token ratchet, skill links, skill identifier liveness), the three Check Changeset runs, Type Check, Build Core, Test Core, and both Governed Surface Queue Guard runs.

① Derived judgments

  • Accept-set: unchanged. The diff is one skill file and one changeset; no schema, runtime or published type moves. Right.
  • Public surface: the published skill gains one taught key, PositionSchema.renamedFrom, which the spec on main already accepts (optional array of SnakeCaseIdentifierSchema). The taught spelling renamedFrom: ['old_name'] parses and matches the schema's own .describe example shape. Right.
  • Token ceiling: not raised; no ratchet baseline file is in the diff, and the ratchet check-run is green. The paragraph is paid for by deletions in the same file. Right as a derived judgment; the deletions themselves are judged in ④.

② Semver level

  • .changeset/33044-skills-position-renamed-from.md declares '@objectstack/skills': patch. packages/skills/package.json is that package and ships skills/**; it sits in the fixed group, where a patch is the smallest bump. Additive prose in a published skill, no behaviour change: patch is right.
  • Clause-②: no on the PR body and in the changeset: no published spec or schema surface is touched, and no narrowing is declared or made. Right.

③ Boundary flags

Dev flags (report 6885028339, deviations):

  • Ran on the claim's default tier although skills/** owes the contract-review tier: answered by this record, rendered at tier.
  • Signature as a prose line, not a trailing HTML comment: no effect on the contract; outside ④.
  • "At boot" although the carry also runs on metadata:reloaded: answered in ④ finding 1 (nit).
  • The install-refusal clause first cut, then restored: answered in ④; the clause is in the file at this head and eval case 19 still finds its facts.
  • origin/main moved past the merge base without a merge: moot, the PR merged clean and main carries the file unchanged.
  • open_questions: none.

The earlier record 6885229673, finding by finding:

  • Its 1 ("at boot" alone): confirmed, nit.
  • Its 2 ("recorded under another package" omits the environment sentinel): confirmed, nit.
  • Its 3 ("(warned)" although single-collection conflicts are parse errors): confirmed, nit.
  • Its 4 (the card says Setup-authored bodies are reported; the code does not read them; the paragraph rightly lists only what the code reads): confirmed against the module doc ("Setup-authored bodies that name an old position are not read"). The card's sentence is wrong, the paragraph is not.
  • Its 5 (the cut manage_users aside): confirmed as a nit, with one correction of its account in ④ finding 4.
  • Its 6 (the cuts lose no taught rule): confirmed in substance, with one correction: the sys_secret sentence is NOT "covered" by rules/field-types.md. See ④ finding 5.
  • Its 7 (evals hold): confirmed for cases 18 and 19, read here; no eval under skills/**/evals names any phrase this diff cut.
  • It carried no should-fix, so none is owed a delta on this head.

④ Design judgment

skills/objectstack-data/rules/security.md. The card carries no eval prompt, so the scenario is the card's Do: an authoring agent that must rename a shipped position. Judged against the spec and the carry on main.

The new paragraph, sentence by sentence:

  • "declare renamedFrom: ['old_name'] on the renamed position, never delete and re-add (holders lose its sets)": the schema field and grammar match; the carry's Why section says a holder row names the position by bare name, so a deleted name leaves its holders granted nothing. Holds.
  • "At boot its holders (sys_user_position) move to the new name in every organization, idempotently": the run at kernel:bootstrapped judges every claim, writes only the position column as the system with no organization filter, and a carried name leaves no row to move at the next run. Holds.
  • "Only holders: the boot log lists other references (sharing, queue and share recipients, pending approvers, invitations) for an admin": REFERENCE_SOURCES is sys_sharing_rule, sys_queue_member, sys_record_share, sys_approval_approver, pending sys_approval_request, pending sys_invitation and the activation ledger; each is logged at warn and never written. Holds. Attempt to break it: the list is read only for old names that were carried (carriedOlds), so a refused rename lists nothing. The sentence sits under the carry, so it does not promise otherwise.
  • "Not carried (warned): an old name still a position anywhere, claimed twice, recorded under another package, switched off, or a disabled claimant": maps onto the six boot refusals (still-declared and held-by-other; claimed-twice; owned-by-other; switched-off; claimant-disabled), each a warn line that leaves the holders. Holds.
  • Against the section it joins: the paragraph above says a rename "stops the old switch-off matching and the item arrives switched on". The switched-off refusal keeps that true and safe: the holders stay on the old, switched-off name and the new position arrives with none. No contradiction.

Findings:

  1. nit (accepted deviation). "At boot" leaves out the reload run after a hot install or upgrade. Every claim is judged at boot, so the sentence is never false, and an author acts the same either way.
  2. nit. "recorded under another package" leaves out the environment as recorded owner (sys_metadata sentinel). Rare, and the boot warning names it. "recorded under another owner" costs the same tokens.
  3. nit. "(warned)" understates one case: inside one package's positions collection, a still-declared old name (a cycle included), a second claimant, the position's own name and a repeated entry are parse errors. The author gets a louder failure than taught; nothing is lost.
  4. nit (cut). "(explaining another user needs manage_users)" is now taught nowhere in skills/**: a grep finds manage_users only in this file's platform-admin capability list. It is the explain door's own authorization, not an authoring rule, and no eval asserts it. The earlier record's line that references/_index.md points to it was not verified here and is not relied on.
  5. nit (cut, and a correction to the PR body's account). "Ciphertext lives in sys_secret, only an opaque ref on the row" is gone from skills/** entirely; the base tree held sys_secret in this one place. rules/field-types.md keeps "encrypted-at-rest ... masked on read, fail-closed (ADR-0100)", which is the behaviour an author acts on, but not the storage shape. So the PR body's "where the fact still lives" row is true for encryption at rest only. No eval asserts the storage shape, and an author declares the field the same way. The merged heading keeps the presence-only query rule whole, and "Two more channels" now counts its two bold items correctly.
  6. nit (new; follow-up candidate). The paragraph says what the platform does with stored references and nothing about the author's own source. A package that renames a position must also re-point its own metadata that names the old one (a shipped sharing rule's position recipient, an approval step's position:NAME slot, an RLS filter over positions). "The boot log lists other references ... for an admin" can be read as "nothing else for the author to do". A renaming agent normally renames usages, and the card's Do does not ask for this, so it is not owed here. Worth half a sentence if the paragraph is next touched, together with the card-premise point that Setup-authored bodies are not read.
  7. nit (wording). "At boot its holders": "its" grammatically points at the renamed position, while the holders meant are the old name's. The rest of the sentence ("move to the new name") resolves it.

The other cuts, each read in place: (ADR-0090 D12) is a citation; "(no read on the roster needed)" is still said by "as a system read"; the com.acme.crm / hd_agent and hr / ops / auditor examples were illustrative and both rule sentences, "Fix: declare the dependency" and the install refusal stay; the adapter aside and "the list renders empty" are colour beside a denial that stays; the two dropped example wildcard sets are covered by "any set with a wildcard read grant". No taught rule is lost beyond findings 4 and 5.

No must-fix, no should-fix. Nits alone end the round; on a merged head they are accepted deviations or follow-up material (finding 6, with 2 and the Setup-bodies note, is the one worth a card).

Implemented-by: claude/project-thread-6ouxnr
Reviewed-by: session_01FEkQ5pjSPT7ui2EUcunaVb

VERDICT: PASS

Adopted by the seat 2026-11-15T00:34Z · isolated sub-agent at CONTRACT_REVIEW_TIER (served model verified by the seat) · Ruling-ref: Leehom card cmsg_01TenzsRopZj4jMbvjX8uz1XFhuiZmTH8VFgMmHRmpiGAb, option A, tapped 2026-11-14T15:37Z

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 pm:awaiting-merge PR only: landing waits on the maintainer's own review and merge (SKILL.md 复核 ②); never on an issue size/s tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills: the security rules do not teach a position's renamedFrom (a renamed position carries its holders) — PR #33008's Skills-docs follow-up

3 participants