Repository navigation
fix(plugin-security): the position-name fold warning says the same-name grant is removed in the next major - #24988
Conversation
…me grant is removed in the next major The position_name_fold_grant warning is re-pointed as the 18.x loud half of the fold's retirement under the standing rule 甲: its text now says the grant is in force only because the names match, that the name-based grant is deprecated and removed in the next major version (after which holders of P do not receive set P unless P's definition names it), and the fix. The structured payload adds removedIn: 'next_major'; event, position and permissionSet are unchanged. Trigger, cadence and every resolution result are unchanged. Claude-Session: https://claude.ai/code/session_01UmqzD97e14iws6hmVhgZ5y Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 4 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 16 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 3eb3e6c98b5a68075780fe8b80969f50ad9e3808 && git checkout 3eb3e6c98b5a68075780fe8b80969f50ad9e3808
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 78ab6474ca30bbb7c4400bae548c814da8fa57c1 e41097d2334238f2bf3827241b19a89a6eaa5b11 && git checkout -B drift-repro 78ab6474ca30bbb7c4400bae548c814da8fa57c1 && git merge --no-ff e41097d2334238f2bf3827241b19a89a6eaa5b11
node scripts/docs-audit/affected-docs.mjs --json 78ab6474ca30bbb7c4400bae548c814da8fa57c1
|
席位核对ACCEPT · #24757 (its 18.x half) ·
|
Part of #24757
Clause-②: no (a runtime warning's wording plus one added field in its log payload; no accept set, served shape, resolution result or error code moves)
This is the card's 18.x half under the standing rule 「甲」: the existing
position_name_fold_grantruntime warning inplugin-security, re-pointed as the loud half of the position-name fold's retirement. The fold itself stays. Its removal is the card's 19.0 half, and #24757 remains open for it.What changed
Pis in force for holders of positionPonly because the two share a name;P's definition does not name it inpermissionSets. ② This name-based grant is deprecated and is removed in the next major version. From then on, holders ofPdo not receive setPunless the position's definition names it. ③ The way out:permissionSets: ['P']on positionPto keep the grant, or a rename if the shared name is a coincidence. The string carries no tracker id and no version number (check:doc-authoring; the docblock says why).{ event, position, permissionSet }gainsremovedIn: 'next_major'. The three existing fields are unchanged, because a log consumer may key on them.POSITION_NAME_FOLD_EVENTandreportNameFoldCollisionsnow say this is the 18.x loud half under 「甲」 and cite the ruling records. The stale "junction table is the governed channel" wording is replaced with ADR-0131's definitionpermissionSets. The "HotCRM NOT MEASURED" boundary note is replaced with the card's measurement (5 of 12 positions atf22dfa52, declared by Five positions reach their permission sets only through a same-name match: declarepermissionSetson them hotcrm#2200). A "When it fires" section records the cadence measured below.Pstill receives setPin 18.x, as 要点 5 of ruling B on Permission-set resolution binds by POSITION NAME while sys_position_permission_set sits near-empty — confirm name-based resolution is the intended mechanism, not an accident the empty junction hides #13419 requires: 「任何行为差异只能表现为拒绝/告警,⛔ 永不静默改变解析结果」.「甲」 reading
6292005854), verbatim: "No conversion can carry it: 18.x ships only the loud half (a warning, an explain line, a reported event). The refusal is filed for 19.0."6292072572), verbatim: "18.x: Five positions reach their permission sets only through a same-name match: declarepermissionSetson them hotcrm#2200 (HotCRM declarespermissionSetson its five positions), and the existing runtime warning re-pointed as the loud half. 19.0: the retirement itself: the fold is removed, and the delegation walk, explain and the collision check follow it."removedIn). The rule lists three loud-half forms; triage's derivation for this card names the warning.explain-engine.tsis fenced out of this claim, so no explain line changes here.permissionSetson them hotcrm#2200, HotCRM's half. It declarespermissionSetson its five same-name positions. That silences this warning for them (pinned below as the declared-pair case) and makes Setup's two panels show the binding.resolvePermissionSetsForContextUnmemoizedanddropDeactivatedFoldedSets, and the follow-ups in the delegated walk, explain and the collision gate. That half deletes this warning together with the fold.Measured (PM mechanism assumptions)
Every reading below is from
origin/mainb811c7369, this branch's base, by source reading.reportNameFoldCollisionsemitsposition_name_fold_grantthroughlogger.warnwith{ event, position, permissionSet }. It reports a pair(position P, set P)only when setPresolved,Pis not among the explicitly requested sets (the definition'spermissionSetsand direct grants, §6b), andPis not a baseline name. It shipped in 17.3.0 (changeset936aa2din the package CHANGELOG).nameFoldWarned). That means not per holder and not per organization, because the key is the name alone. A restart reports again.git grep position_name_fold_grantfinds only the pin and docblock/CHANGELOG prose. The core logger (packages/core/src/logger.tswrite) spreadsmetainto the JSON line, so the new field arrives in the log as a top-level key besideevent. No new channel, endpoint or gate is added.Tests
packages/plugins/plugin-security/src/position-name-fold-warning.test.ts, 29 cases (28 before):sales_rep,sales_manager, each bound tocrm_sales_user): the payload is exactly{ event, position, permissionSet, removedIn: 'next_major' }, and ⛔ the holder still resolves the same-name set.event,permissionSet,positionandremovedIn, so a renamed legacy field fails as loudly as a missing retirement field.sys_position_permission_set, and no#followed by a digit.sales_rep.org_adminnear-miss, the 13 declared bindings, a declared pair (permissions: ['sales_rep', …], the shape hotcrm#2200 produces), a baseline-named position, and no positions.Ablations, each through
scripts/ablation-replace.mjsin WRAP mode, run against the warning file. For each one the tool's own evidence readsok mutation landedandok restored: blob == HEAD (5db0a1f3c00a) and git diff HEAD is empty. The plugin's tests import./security-plugin.jsfromsrc, so nodist/leg applies.removedInfrom the payloadexpected [ 'crm_sales_user', 'member_default' ] to deeply equal [ …(3) ]andexpected [ 'member_default' ] to include 'sales_rep'A9's first attempt was a no-op: its replacement contained the anchor, and the tool refused with "the anchor count moved 1 -> 1" and restored. The leg was re-run with a replacement that does not contain the anchor. The table reports the second run.
Suite and typecheck, at
63246cedf(the merge oforigin/mainthat followed touched onlyplugin-approvals, outside this package's dependency and devDependency closure):pnpm --filter @objectstack/plugin-security exec vitest run --maxWorkers=2: 197 files passed, 4078 tests passed, 45 skipped.pnpm --filter @objectstack/plugin-security run typecheck(tsc, the scripts project andcheck:test-typecheck): exit 0, with0 file(s) / 0 error(s) / 0 pinned signature(s)intest-typecheck-debt.json. The test file is in thetsconfig.test.jsonprogram (--listFilesOnly: 1).Gates
These were run locally at
e41097d23, the head this PR opens on, from thenode scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderivation at that head: 69 commands, the same 69 the PM's reading at6440cf163listed. Each exit code was captured before any pipe.check:doc-authoring(sibling-package prose ids hold the empty baseline),check:nul-bytes,check:position-name-fold-loadersand its--self-test,check:issue-citations,check:changeset-no-majorandcheck:adr-0087-registration(both--base origin/main),check:empty-changeset,check:published-files,check:dts-closure,check:test-source-alias,check:type-check-coverageandcheck:type-check-debt.pnpm check:dual-build-cjs-loadsandpnpm check:i18nread every package'sdist/. This worktree built onlyplugin-securityand its dependency closure. CI's fresh build runs both.dispatch-gates --ranwith the exit-coded record: "69 derived famil(ies) accounted for — 67 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)".check-changeset-fixed.mjs,check:authz-resolver,check:error-code-casingandcheck:filter-alias-parity, all exit 0.pnpm lint, the type-check lanes, Test Core, Build Core).Acceptance notes
nameFoldWarnedkeys on the position name alone and the payload names no organization. On a multi-organization deployment, an undeclared pairPin a second organization is not reported until restart once the first organization'sPhas warned. This is not reproduced through a public door; it is a source reading. Carrier: Setup: a position's same-named permission set is in force but invisible — the set's Assigned Users panel is empty and the position reads as granting nothing #24757's 19.0 half, which removes the warning.Pmakes a request. A deployment where no such request has happened since boot has not been told. The readings report on the card noted that a boot-time pass over the registry would be the cheaper place for this. That would be a new trigger, outside this dispatch's "keep the trigger conditions". Carrier: Setup: a position's same-named permission set is in force but invisible — the set's Assigned Users panel is empty and the position reads as granting nothing #24757's 19.0 half.nameFoldWarneddocblock linksreportNameFoldCollision(singular), which names no member. It is outside this claim's file surface, so it is left as it is.explain-engine.tsis fenced out of this claim, so this PR ships the warning and its reported event.Generated by Claude Code