Skip to content

Pools & cross-pooling

What it is

A pool is the smallest part of the org hierarchy that owns people — every employee has a home pool. Cross-pooling lets the allocation engine staff one pool's work with another pool's employees, but only when an admin has created an explicit, dated pool-lending rule that permits it.

Why it exists

Work doesn't always line up with team boundaries: one pool is short, another has spare capacity. Cross-pooling lets a planner borrow across pools without dissolving the org structure — the lending stays controlled, directional, and time-bounded, and every cross-pool decision the engine makes is explainable.

Key concepts & terms

  • Pool — a group of workers under a department; an employee's home pool is their primary pool.
  • Default pool of a department — the demand for a department is "owned" by its default pool (derived as the lowest-id active pool in that department; it is not a stored flag).
  • Pool-lending rule — an admin grant that says "source pool A may lend to target pool B," between two dates, optionally requiring approval.
  • Directional — a rule only grants A→B; B→A needs its own separate rule.
  • Requires-approval — a flag on a lending rule; in v1 a value of true blocks the lending.
  • Cross-pool eligibility rule — the hard rule in the allocation engine that enforces lending at run time.

How it works

There are two halves, joined at the hip: configuration (pools and lending rules) and enforcement (the engine).

An admin manages pools and, in a pool's detail page, the lending rules under the source pool ("Pool A lends to…"). That CRUD surface only stores the grants. The enforcement happens inside an allocation run: when the engine considers an employee from pool A for demand owned by pool B, the cross-pool eligibility hard rule decides:

flowchart TD
  E["Candidate from Pool A (home)"] --> Q{"Demand owned by Pool B"}
  Q -->|A = B| Same["Same pool → passes (no cross-pool check)"]
  Q -->|A ≠ B| L{"Effective A→B lending rule?"}
  L -->|no rule / outside its dates| F1["Fails hard — not eligible"]
  L -->|rule exists, requires approval| F2["Fails hard — needs approval (no workflow in v1)"]
  L -->|rule exists, auto-approved, in window| Pass["Passes — cross-pool eligible"]

This is the most foundational gate in the engine — it runs first (before certification, leave, rest, and so on), so an ineligible cross-pool candidate is rejected fast, with a precise reason. Same-pool candidates skip the check entirely.

Cross-pool lending passes — a borrowed employee shows as eligible under an effective lending rule.

Default cross-pool block — with no lending rule, the candidate fails the hard cross-pool gate.

Rules & what's enforced

  • Lending is directional and dated. A rule grants source→target between an effective-from and an (optional) effective-to; bounds are inclusive. "A lends to B" does not imply "B lends to A."
  • No lending rule means no cross-pool allocation. By default an employee can only be used for their own pool's demand.
  • Requires approval = true blocks in v1. There is no approval workflow yet, so a rule that requires approval rejects the cross-pool candidate. Only requires approval = false rules actually let someone through. (The flag defaults to true.)
  • Max concurrent borrows is stored but not enforced — the engine cannot currently cap how many people are borrowed at once.
  • Deleting a pool is blocked while any active employee still has it as their primary pool.
  • Lending-rule identity (source, target) is fixed — to re-target, delete and recreate.

What's live vs planned

  • Live (v1): pools and pool-lending rules (full create/edit and web UI), the default-pool-per-department invariant, and runtime enforcement of lending via the cross-pool eligibility hard rule (snapshot-backed, with a matching database fallback).
  • Planned / deferred: the per-rule concurrency cap (max concurrent borrows), the approval workflow that would clear a "requires approval" rule, and a lending-request ledger.

One operational gotcha: if a department is missing its default pool, the cross-pool check passes rather than fails (a deliberate "never reject without a citation" safeguard) — so a missing default pool surfaces as checks silently passing, not as an error.