You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Filed by the director seat out of #16678's Phase 2 ruling (decision batch #127 item 1, maintainer ruling 2026-09-13; recorded on #16678). ⛔ Not claimed, ⛔ not dispatched. domain:spec because the landing point is packages/spec/src/automation/approval.zod.ts; Clause-②: yes is expected — this widens an authorable enum and adds a sibling key — and the domain:spec seat declares it at claim time.
What is ruled (verbatim scope from the #16678 Phase 2 design §8.2, adopted)
Extend the approval node's empty-slate policy so it can name people: onEmptyApprovers gains a 'fallback' member, with a sibling fallbackApprovers reusing the existing approver-entry shape (ApproverEntrySchema, ⛔ not a new shape). This belongs on the node, ⛔ not on the manager rung: the #3807 warning already shows all five graph approver types (manager / team / department / position / org_membership_level) share the dead end, and the node is where the platform already decided emptiness is handled (admin_rescue | fail | auto_approve). Take the capability the measured platforms bind to the manager rung (Entra "Add fallback"; Odoo "If empty, the approval is done by an Administrator or Approver") at the node level.
Measured facts the implementer inherits (#16678 design §8.1, verified by the services seat on origin/main)
The slot then falls back to the literal `${a.type}:${a.value}` and value is omitted for manager ⇒ pending_approvers carries the literal manager:undefined. That literal is in this card's blast radius: with a fallback declared, the slate holds the fallback approvers instead.
packages/spec: onEmptyApprovers enum + fallbackApprovers (required iff 'fallback', refined with a remedy naming both keys); .describe() text and the generated reference page; authorable-surface gains the entries; minor changeset.
packages/plugins/plugin-approvals (openNodeRequest empty-slate block): the 'fallback' branch resolves fallbackApprovers through the same resolver as ordinary approver entries and opens the request on them; ⛔ never the manager:undefined literal.
packages/lintvalidate-approval-approvers.ts: the MANAGER_ONLY_REMEDY / MANAGER_ONLY_ROUTES text gains "or declare onEmptyApprovers: 'fallback'"; the finding keeps firing on shape (a static check cannot read the column); stackWiresManagerChain stays the silencer. ⛔ Do not delete the rule. The docs callout content/docs/automation/approvals.mdx:66-87 follows.
Cross-domain declaration: packages/spec (designated lane) + plugin-approvals + packages/lint faces declared in the claim comment, per the cross-domain exception path.
Relations
Parent ruling: #16678 (Phase 2 design adopted as the Phase 3 baseline, minus §4). This card is independent of the manager write surface (design §8.2: "a fallback does not make manager resolve; it makes the failure survivable. The two are independent and both are wanted") ⇒ ⛔ no Blocked-by:. #17579 (the generated reference page's manager line) is closed in substance by #17640 and is re-verified here.
Filed by the director seat out of #16678's Phase 2 ruling (decision batch #127 item 1, maintainer ruling 2026-09-13; recorded on #16678). ⛔ Not claimed, ⛔ not dispatched.
domain:specbecause the landing point ispackages/spec/src/automation/approval.zod.ts;Clause-②: yesis expected — this widens an authorable enum and adds a sibling key — and thedomain:specseat declares it at claim time.What is ruled (verbatim scope from the #16678 Phase 2 design §8.2, adopted)
Extend the approval node's empty-slate policy so it can name people:
onEmptyApproversgains a'fallback'member, with a siblingfallbackApproversreusing the existing approver-entry shape (ApproverEntrySchema, ⛔ not a new shape). This belongs on the node, ⛔ not on themanagerrung: the #3807 warning already shows all five graph approver types (manager/team/department/position/org_membership_level) share the dead end, and the node is where the platform already decided emptiness is handled (admin_rescue|fail|auto_approve). Take the capability the measured platforms bind to the manager rung (Entra "Add fallback"; Odoo "If empty, the approval is done by an Administrator or Approver") at the node level.Measured facts the implementer inherits (#16678 design §8.1, verified by the services seat on
origin/main)managerIS inGRAPH_APPROVER_TYPES(approval-service.ts:831-833); an empty manager fires the approvals: adepartmentapprover never resolves when the business unit hasorganization_id = null(every seeded BU) #3807 "expanded to nobody" warning (:1730) — ⛔ the card body's and PR docs(approvals): qualify themanagerapprover as a directory-sync dependency #17575's "silently" is stale.`${a.type}:${a.value}`andvalueis omitted formanager⇒pending_approverscarries the literalmanager:undefined. That literal is in this card's blast radius: with a fallback declared, the slate holds the fallback approvers instead.departmentapprover never resolves when the business unit hasorganization_id = null(every seeded BU) #3807 pin table (approval-service.test.ts:3152-3157) omitsmanager; add the row.Scope
packages/spec:onEmptyApproversenum +fallbackApprovers(required iff'fallback', refined with a remedy naming both keys);.describe()text and the generated reference page;authorable-surfacegains the entries;minorchangeset.packages/plugins/plugin-approvals(openNodeRequestempty-slate block): the'fallback'branch resolvesfallbackApproversthrough the same resolver as ordinary approver entries and opens the request on them; ⛔ never themanager:undefinedliteral.packages/lintvalidate-approval-approvers.ts: theMANAGER_ONLY_REMEDY/MANAGER_ONLY_ROUTEStext gains "or declareonEmptyApprovers: 'fallback'"; the finding keeps firing on shape (a static check cannot read the column);stackWiresManagerChainstays the silencer. ⛔ Do not delete the rule. The docs calloutcontent/docs/automation/approvals.mdx:66-87follows.fail/auto_approve/admin_rescueunchanged (negative controls); the newmanagerrow in the approvals: adepartmentapprover never resolves when the business unit hasorganization_id = null(every seeded BU) #3807 table.Cross-domain declaration:
packages/spec(designated lane) +plugin-approvals+packages/lintfaces declared in the claim comment, per the cross-domain exception path.Relations
Parent ruling: #16678 (Phase 2 design adopted as the Phase 3 baseline, minus §4). This card is independent of the manager write surface (design §8.2: "a fallback does not make
managerresolve; it makes the failure survivable. The two are independent and both are wanted") ⇒ ⛔ noBlocked-by:. #17579 (the generated reference page'smanagerline) is closed in substance by #17640 and is re-verified here.Generated by Claude Code