# The WorkQueues kit's floor — restricted queues, who may hold an item, and who moves whose work

> What holds in every app on `Osysharp.WorkQueues`, whatever the app grants: an item in a restricted queue is invisible — in lists and in counts — to staff outside it; only somebody who may hold an item is ever given it; only a lead moves a colleague's work; the holder, queue, priority and promise are written only by the kit's run, and every change is recorded; a requester reads none of the service's internals; and queues and promises are the inbox admins'.

<!-- id: security-inbox-kit · area: security · stability: preview · html: https://osysharp.com/reference/security/inbox-kit/ -->

## Summary        {#summary}
The WorkQueues kit's `WorkItem` is `[Authority(Extensible)]`: the app derives its kind (`Ticket : WorkItem`) and states that
kind's grants, and the kit's **denies** are the floor every grant stands on. The app says three things — who works the
inbox, who leads it, who administers it:

```osy syntax
policy WorksInbox       => IsAgent;
policy LeadsInbox       => IsLead;
policy AdministersInbox => IsAdmin;
```

## Signature      {#signature}
| invariant | what holds |
|---|---|
| `inbox-a-restricted-queue-is-read-by-its-members-only` | an item in a `Restricted` queue is unreadable to staff outside it — rows **and** counts; its holder and inbox admins read it, and the requester's read is the app's |
| `inbox-work-goes-only-to-who-may-hold-it` | every path that puts an item on a person — take, hand-on, a routing rule, the queue's distribution, a refill, an account offer, cover, follow-the-sun — asks `MayHold`: an active member of the item's queue (or on its organisation's account team, when the queue is not restricted) holding every skill it requires |
| `inbox-assignment-only-through-the-run` | holder, queue, priority, escalation level and promise are written only by the kit's runs; every change is a `Handover` (and every escalation an `Escalation`) |
| `inbox-moving-anothers-work-is-a-leads` | unless `AgentsHandOn`, only the holder or a lead moves a held item |
| `inbox-service-internals-are-staffs` | a requester reads no policy, budget, at-risk fraction, escalation level or history, assignment history, offer, clock record, queue or absence |
| `inbox-a-count-is-the-viewers-own` | every count is the viewer's own read, never a privileged aggregate |
| `inbox-a-view-grants-nothing` | a saved view only filters; a personal view is its owner's; shared views are the admins' |
| `inbox-only-you-or-a-lead-set-your-availability` | a person's status, cover, ceiling, hours and absences are set by that person or a lead — nobody marks a colleague away and collects their work |
| `inbox-cover-grants-no-access` | cover moves an item only to somebody who may hold it — a restricted item, or one needing a skill the cover lacks, goes back to its queue; `ActsFor` is asked beside the app's own rule, so a delegate never acts on their own request |
| `inbox-service-levels-are-the-admins` | queues (their distribution included), SLA policies and targets, routing rules, skills and shared views change only by an inbox admin |

## Description    {#description}

### The app grants; the kit's floor holds   {#floor}
Grants are never inherited, so a `Ticket : WorkItem` states its own reads and writes — staff work every ticket, a
customer reads their own. Whatever it grants, the kit's denies hold: an app that grants its agents every ticket still
cannot show one in a restricted queue to an agent outside it, and an app that grants `update` cannot write
`Assignee`, `Queue`, `Priority` or the promise. Those change only through the kit's verbs, which raise events on the
item's `WorkHandling` run.

### The lead's verbs are their own events   {#leads}
`TakeWork`, `HandWorkTo` and `MoveWork` raise an agent's event or a lead's, chosen by the caller's `LeadsInbox`; the
lead's events are gated `[Authorize(u => LeadsInbox)]`, so an agent cannot reach them by raising them herself. With
`AgentsHandOn` off, an agent may take only unheld work and hand on only what she holds. With it on (the default), any
agent may — the target must still be able to hold it.

### Routing gives work only to who may hold it   {#routing}
A routing rule's `AssignTo`, a queue's round robin or least loaded, and a refill all end in the same question the verbs
ask. A rule naming somebody who may not hold the item gives it to nobody, and the item's timeline says so; a queue
never gives an item to a member who lacks a skill it **requires**. Who holds which skills is a lead's to say
(`HeldSkill`); which skills exist, which rules route and how a queue hands its work out are an inbox admin's.

### Cover is not a way in   {#cover}
When somebody goes away, each item they hold goes to their cover **only** when the cover may hold it; otherwise it goes
back to its queue. So naming a colleague as cover never hands them a restricted queue's item, or work needing a skill
they lack — and never into a read they did not have. Delegation reads `ActsFor(u, p)` ("u is p, or covers for p while
p is away"), and it is always asked beside the app's own rule: an approver's delegate may approve, but never their own
request (`&& u != this.Item.Requester`). The cover ends with the absence.

### The collision sentence   {#collision}
A take or hand-on carries `expected` — the holder the page showed. If somebody else holds the item now, the verb is
refused with the sentence naming them ("Rahul took this just now."), and nothing moves. `[ConcurrencyCheck]` on
`WorkItem` turns a truly simultaneous double-take into one success and one refusal instead of a lost update.

### Known limit   {#limits}
"Only the kit's run writes the holder" means **any** workflow body today: an app's own lifecycle body writes as the
system too, so it could set `Assignee` past the kit's deny. Scoping that exemption to the package that declared the
deny is an open platform decision (D-INBOX1, with D-WFRUNAS1).

### An absolute due date   {#due-at}
`WorkItem.DueAt` is a promise no business-hours budget can say — "one calendar month from receipt". An app sets it when
it creates the item; when set, it IS the resolution promise: counted in real time from `ArrivedAt`, never paused by a
wait, at risk at `AtRiskAt` of that time and breached at the instant, recorded like any clock, and what the list counts
down to while the resolution governs. It moves only through `MoveDue(item, due, why)`, which is recorded on the item's
trail. The Privacy kit's requests are the first user.

## Examples       {#examples}

### A restricted queue, and the verbs that move work   {#example-restricted}
```osy title="a restricted queue's items are its members' alone; a lead hands work on" test app=security-inbox-kit
app SecureDesk {
  use Osysharp.Identity@0;
  use Osysharp.Organizations@0;
  use Osysharp.Organizations.ServicePlans@0;
  use Osysharp.Presence@0;
  use Osysharp.Observability;
  use Osysharp.WorkQueues@1;
  use Osysharp.Workflow;
  use Osysharp.ServiceHours;
  model "model/**/*.osy";
}

using Osysharp.Identity;
using Osysharp.WorkQueues;

[Role] enum DeskRole { Authenticator, Agent, Lead, Admin }
policy WorksInbox => RoleGrant.Any(g => g.Member == user
  && (g.Role == DeskRole.Agent || g.Role == DeskRole.Lead || g.Role == DeskRole.Admin));
policy LeadsInbox => RoleGrant.Any(g => g.Member == user && (g.Role == DeskRole.Lead || g.Role == DeskRole.Admin));
policy AdministersInbox => RoleGrant.Any(g => g.Member == user && g.Role == DeskRole.Admin);
policy ManagesOrganizations => AdministersInbox;
policy CollaboratesOnRecords => WorksInbox;

/// The app grants its agents EVERY incident — and the kit's floor still keeps the Security queue's to its members.
entity Incident : WorkItem {
  security { allow read, create, update when WorksInbox; }
}

/// Open an incident straight into the restricted queue.
public Incident Report(string title) {
  return new Incident { Title = title, Queue = QueueNamed("Security") };
}

/// A lead hands it to a member of that queue — anyone else is refused, by name.
public void Assign(Incident incident, Principal to) { HandWorkTo(incident, to); }
```

## See also       {#see-also}
- [The WorkQueues kit — queues, who holds what, and the service levels promised on any item](https://osysharp.com/reference/workflow/inbox-kit/) — the model, the clocks, the verbs, views and counts
- [[Authority]](https://osysharp.com/reference/security/contribution-authority/) — why an Extensible package's denies are a floor
- [The Organizations kit — companies, teams, and roles held within one](https://osysharp.com/reference/security/organizations-kit/) — tiers and account teams, read by the kit and never written
- **`kits/work-queues/tests/security.test.osy`** — one guard per invariant, each red with its guard removed
