Skip to content

How status, sign-off and approval are configured

Three separate admin screens, all under Admin, all following the same rule: you can configure the vocabulary, labels, thresholds, who signs what, but never the underlying doctrine. An arithmetic verdict stays arithmetic; a declared status still has to name a cause; a rejection still always shows.

All three screens require you to preview the effect of a change before you can save it, a dry run showing exactly which items would change verdict, or which recent real change-sittings would have needed an extra approval, before anything is committed. None of them silently rewrite history: a report or a sign-off already published stays exactly as it was, under the rules that were live when it was made, even after you retune something here.

Two different things live on this screen, and it’s worth keeping them apart:

  • Words: relabel any status (derived or declared), and add your own custom declared-status types alongside the four shipped ones (Blocked, On hold, Awaiting decision, Awaiting sign-off). A custom type needs a stable code and an escalation level, none, amber or red, which decides how far an open status of that type is allowed to pull the combined status pill towards red. Escalation never changes the underlying arithmetic verdict itself, only the colour a person sees.
  • Thresholds: how many points behind expected pace counts as “Behind” (15 by default), how many days before an end date counts as “Approaching” (5 by default), how much credit an in-progress step gets by default (50% is the shipped default, the most widely recognised convention, and the direct answer to steps that read as 0% done despite real progress having happened), and whether an instance allows a person to type their own percentage on a step instead of using that default credit.

“Overdue” itself has no threshold to configure, anywhere, on purpose. It fires the instant an end date has passed, an arithmetic fact, not a judgement call, so there’s deliberately no grace period to set to zero or anything else.

Any change here is itself a governed act, recorded in the change register the same way a date move is, because retuning a threshold moves every number on every board, not just the one screen you’re looking at.

The sign-off (acceptance) chain (Admin → Sign-off chain)

Section titled “The sign-off (acceptance) chain (Admin → Sign-off chain)”

Every deliverable has its own acceptance chain, an ordered list of stages, each either required or optional, each signed either by anyone holding a named role (programme-wide, or narrowed to “the person with that role for this specific work”) or by an explicit list of named people. The shipped default:

  1. Definition agreed: customer, required.
  2. Internal QA: the workstream lead, optional.
  3. Customer acceptance: customer, required.

You can add, remove, or reorder stages, change who signs each one, or make an optional stage required. For each stage you can also allow self-sign, letting the person who did the work sign it themselves, though the default, and the recommendation, is to leave that off; segregation of duties is what stops a person marking their own work done.

Reconfiguring this chain only affects deliverables created after the change. A deliverable’s chain is pinned the moment its acceptance record is created, so that a deliverable accepted under an older configuration keeps that exact record, permanently, even after you change the chain for everyone else. The screen shows you a live count of exactly how many existing deliverables are still running on the chain you’re about to replace, before you save.

The change-approval path (Admin → Approval path)

Section titled “The change-approval path (Admin → Approval path)”

This is a different chain from the one above, it governs whether a sitting (a bundle of plan changes) needs one or more approvals before it’s decided, not whether a deliverable is done. Every instance ships with one simple, unconditional stage: anyone holding the Reviewer right (or a reach-scoped approval right, for a sitting entirely within their own reach).

You can add conditional stages, extra approvals that only apply when a sitting actually trips a specific, named condition:

  • it slips a date past a configured number of days,
  • it changes effort past a percentage or a fixed number of days,
  • it changes cost past a percentage or a fixed amount,
  • it changes scope at all,
  • it touches something already in the active baseline,
  • it touches a deliverable that already has sign-off progress or a linked benefit,
  • it deletes something,
  • it spans more than one workstream.

You can also override the default path for one specific workstream, useful for, say, a supplier-run workstream that genuinely needs a stricter or different path than the rest of the programme. A workstream’s own override entirely replaces the default for that workstream; it never stacks on top of it. A change is held to the chain of its own workstream; a sitting that touches more than one workstream is reviewed as a whole as described below.

Who signs what in a sitting that touches several workstreams. Each change needs only the stages that change triggers on its own, signed for it by someone who can see it; a change that triggers none still needs one approval from someone who can see it. A stage that fires because the sitting spans more than one workstream is a single review of the whole sitting: it is signed once, by one of its signers who can see every change in the sitting and did not propose it, and it keeps its place in the stage order. People who see only part of a sitting are never shown that review, its state, or anything about the parts they cannot see.

If nobody who can sign that review can see the whole sitting (say its only signers are two workstream owners who each see their own workstream), the sitting is marked as stuck when it is proposed. Someone with the right to administer approval paths (a delivery lead or admin) who sees the whole sitting is then offered Release on Reviews: they give a reason, it is recorded in the audit log and shown on the sitting, and the sitting goes ahead once every change has its own approvals. A review someone can still sign is never released, and you cannot release a review on a sitting you proposed while “Require a distinct approver” is on. To avoid releases altogether, name at least one signer for that stage who reaches every workstream.

A sitting already in flight keeps the rules it was evaluated against. Reconfiguring this chain never rewrites what an in-flight or already-decided sitting says it needed, the reasoning shown to the person who opened it, and to whoever approved it, was worked out once, at the time, and is never silently recomputed later.

Every one of these three screens’ writes goes through the same audited, change-registered path as any other governed write, and every one also fires the ordinary live-update broadcast, status_rule_version, acceptance_chain_version and change_approval_chain_version are each their own recognised entity type in realtime/permissionFilter.ts, gated behind that screen’s own read permission (never plan.read) rather than a workstream-reach check, because an instance-wide config’s broadcast audience is “everyone entitled to read this configuration,” not one workstream’s own readers. If you retune a threshold or reconfigure a chain while a colleague has the same admin screen open, with nothing of their own unsaved, it refreshes in place; with an unsaved edit, it flags that the saved configuration has moved on rather than silently discarding what they were typing.

A relabel here does not reach a work-package list or page. The status pills on Deliver, Plan and on a work package’s own page use fixed labels, not the labels saved on this screen, so a relabel is confirmed on this screen alone. A retuned threshold is a separate matter: it can change which status a work package classifies as.

The status vocabulary, the sign-off chain and the change-approval path screens update live for a colleague who has the same screen open. With an unsaved edit of their own, they are told that the saved configuration has moved on. packages/server/tests/unit/realtime/permissionFilter.test.ts`) rather than