Maintainer-action: rule how a domain:services card obtains the CONTRACT_REVIEW_TIER clause-② verdict its carrier needs — route it to the spec seat (naming how), or rule that this seat's default-judgment-tier review plus the gates suffices — done when node scripts/pm/check-clause2-carriers.mjs --pair 18046 exits 0 with the carrier cleared on both carriers
Split from #16678's Phase 3 landing (5653051072), which flagged it in its own words:
⚠️ Ruling row 7 (admit manager_id to the bulk-import tier, resolved in a second pass keyed on email/phone) is ruled but not delivered here — admin-import-users.ts is untouched and its 0-match reading still holds. That split is this seat's to carry, ⛔ not lost.
This is the card that carries it. The work is already ruled — ⛔ it does not go back to the decision box.
The ruling, verbatim
Ruling 5651634638 (director seat, decision batch #127 item 1, option B), §9 row 7:
7 Bulk import — manager_id admitted to the import tier, resolved in a second pass keyed on the importer's identity key (email / phone), every row-level refusal from row 2 applied, unresolved key = per-row error
Measured — the gap is still open
On origin/main @ 8261ff717:
| reading |
result |
packages/plugins/plugin-auth/src/admin-import-users.ts |
632 lines |
case-insensitive manager in that file |
1 — and it is AuthManagerOptions in a comment, a word match, ⛔ not the field |
⇒ field-level manager_id hits |
0 |
POSITIVE CONTROL email in the same file |
73 |
The control is what makes the zero a reading rather than a broken search.
⛔ Consume the landed endpoint; do NOT re-derive its refusals
Phase 3 landed packages/plugins/plugin-auth/src/admin-set-user-manager.ts (476 lines) and already exports the reusable parts:
:131 export const MAX_MANAGER_CHAIN_DEPTH = 20
:134 export type SetUserManagerRefusalReason =
'self_assignment' | 'cycle' | 'max_depth_exceeded' | 'cross_organization' | 'idp_provisioned'
:146 export interface SetUserManagerEngine
:151 export interface SetUserManagerDeps
:165 export interface SetUserManagerResult
:333 export async function runSetUserManager(...)
The ruling says "every row-level refusal from row 2 applied". The file already carries that table, verbatim from its header:
| refusal |
error |
status |
reason |
| self-assignment |
INVALID_FIELD |
400 |
self_assignment |
| cycle |
RESOURCE_CONFLICT |
409 |
cycle |
| depth cap |
VALUE_OUT_OF_RANGE |
400 |
max_depth_exceeded |
| cross-organization |
INVALID_REFERENCE |
400 |
cross_organization |
| directory-owned identity |
PERMISSION_DENIED |
403 |
idp_provisioned |
⇒ the second pass routes each row through the existing derivation. ⛔ A second copy of these five predicates in the importer is the drift this platform keeps paying for — it is the same shape as #15706 (a mirrored copy of auth rules) and the reason #16678's own design insisted on one derivation. If runSetUserManager cannot be called row-wise as it stands, say so with the measurement and propose the smallest seam; ⛔ do not fork the predicates.
What the card is
- Admit
manager_id to the importer's accepted tier.
- Resolve it in a second pass, after all rows exist — the ruling's wording, and it is load-bearing: a manager named in row 40 may be created by row 90, so a single-pass resolve would refuse valid input.
- Key the resolve on the importer's identity key (email / phone) — the same key the importer already uses, ⛔ not a new one.
- Unresolved key ⇒ per-row error, not a whole-import failure and ⛔ not a silent skip.
- Apply every row-2 refusal per row, through the existing derivation.
Open questions the taker must answer with a measurement, not a guess
- Does the importer today have a place for a second pass at all, or does it commit row-by-row? That decides whether this is a small addition or a restructure — unmeasured here.
- What does the importer's existing per-row error channel look like (
rows[].delivery is the shape its header mentions for delivery)? The per-row manager error should ride that channel rather than invent one.
runSetUserManager's signature is written for one HTTP call with SetUserManagerDeps; whether N row-wise calls are acceptable or a batch seam is owed is a real design question — answer it with the file open.
Fences
Grading
Inherited from the parent: priority:p2, domain:services (landing site packages/plugins/plugin-auth). ⚠️ Triage may re-rank — the parent's p2 was set against the whole card, and this is its last row.
Refs
#16678 (parent, closed) · 5651634638 (the ruling) · 5653051072 (Phase 3 landing, which flagged this row) · #17995 (domain:spec, the error-vocabulary question for the same five refusals) · objectui#9361 (the Console affordance)
Generated by Claude Code
Maintainer-action: rule how a
domain:servicescard obtains theCONTRACT_REVIEW_TIERclause-② verdict its carrier needs — route it to the spec seat (naming how), or rule that this seat's default-judgment-tier review plus the gates suffices — done whennode scripts/pm/check-clause2-carriers.mjs --pair 18046exits 0 with the carrier cleared on both carriersSplit from #16678's Phase 3 landing (
5653051072), which flagged it in its own words:This is the card that carries it. The work is already ruled — ⛔ it does not go back to the decision box.
The ruling, verbatim
Ruling
5651634638(director seat, decision batch #127 item 1, option B), §9 row 7:Measured — the gap is still open
On
origin/main@8261ff717:packages/plugins/plugin-auth/src/admin-import-users.tsmanagerin that fileAuthManagerOptionsin a comment, a word match, ⛔ not the fieldmanager_idhitsemailin the same fileThe control is what makes the zero a reading rather than a broken search.
⛔ Consume the landed endpoint; do NOT re-derive its refusals
Phase 3 landed
packages/plugins/plugin-auth/src/admin-set-user-manager.ts(476 lines) and already exports the reusable parts:The ruling says "every row-level refusal from row 2 applied". The file already carries that table, verbatim from its header:
INVALID_FIELDself_assignmentRESOURCE_CONFLICTcycleVALUE_OUT_OF_RANGEmax_depth_exceededINVALID_REFERENCEcross_organizationPERMISSION_DENIEDidp_provisioned⇒ the second pass routes each row through the existing derivation. ⛔ A second copy of these five predicates in the importer is the drift this platform keeps paying for — it is the same shape as #15706 (a mirrored copy of auth rules) and the reason #16678's own design insisted on one derivation. If
runSetUserManagercannot be called row-wise as it stands, say so with the measurement and propose the smallest seam; ⛔ do not fork the predicates.What the card is
manager_idto the importer's accepted tier.Open questions the taker must answer with a measurement, not a guess
rows[].deliveryis the shape its header mentions for delivery)? The per-row manager error should ride that channel rather than invent one.runSetUserManager's signature is written for one HTTP call withSetUserManagerDeps; whether N row-wise calls are acceptable or a batch seam is owed is a real design question — answer it with the file open.Fences
packages/specis not this lane's and must stay untouched; if a newerror.codelooks owed, STOP and route it — spec(approvals):ApproverType.describe()still says the manager column has no product write surface — PR #17993 gives it one, and #17640 left the sentence behind #17995 already carries the closed-vocabulary question for this endpoint's five refusals.content/docs/releases/**untouched.manager_idfrom unit membership would let a membership edit change a security scope as a side effect), ⛔ not on cost.{ type: 'manager' }resolves a column no product surface can write:sys_user.manager_idis refused by the data API and absent from the auth admin endpoints #16678's delivery pinned the ABSENCE of aSYS_USER_PROFILE_EDIT_FIELDSchange with a test. The importer reaches the column by system context, the same way the endpoint does.Grading
Inherited from the parent:⚠️ Triage may re-rank — the parent's p2 was set against the whole card, and this is its last row.
priority:p2,domain:services(landing sitepackages/plugins/plugin-auth).Refs
#16678 (parent, closed) ·
5651634638(the ruling) ·5653051072(Phase 3 landing, which flagged this row) · #17995 (domain:spec, the error-vocabulary question for the same five refusals) · objectui#9361 (the Console affordance)Generated by Claude Code