2026-09-28 22:14:38 +02:00
|
|
|
---
|
|
|
|
|
id: canon-task-wait-qualifiers
|
|
|
|
|
type: standard
|
|
|
|
|
title: "Task Wait Qualifiers v0.1"
|
|
|
|
|
domain: custodian
|
2026-09-28 23:10:11 +02:00
|
|
|
status: accepted
|
2026-09-28 22:14:38 +02:00
|
|
|
version: "0.1"
|
|
|
|
|
owner: the-custodian
|
2026-09-28 23:10:11 +02:00
|
|
|
revision: "accepted-1"
|
2026-09-28 22:14:38 +02:00
|
|
|
last_reviewed: "2026-09-28"
|
|
|
|
|
review_interval: 6m
|
|
|
|
|
created: "2026-09-28"
|
|
|
|
|
updated: "2026-09-28"
|
|
|
|
|
scope: fleet
|
|
|
|
|
related_workplans:
|
|
|
|
|
- CUST-WP-0074
|
|
|
|
|
- CUST-WP-0072
|
|
|
|
|
- STATE-WP-0092
|
|
|
|
|
- COORDINATION-WP-0005
|
|
|
|
|
- RAIL-FAB-WP-0029
|
|
|
|
|
supersedes: none
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# Task Wait Qualifiers v0.1
|
|
|
|
|
|
|
|
|
|
## Purpose
|
|
|
|
|
|
|
|
|
|
A task in `status: wait` must say what it waits for in a form the fleet can
|
|
|
|
|
read without a person. Two kinds of wait exist and need different handling:
|
|
|
|
|
|
|
|
|
|
- **External commitment** — the task waits for another workplan or task to
|
|
|
|
|
reach a terminal state. It clears on its own; the waiting side observes.
|
|
|
|
|
- **Human gate** — the task waits for a decision, approval, attended
|
|
|
|
|
privileged action or operator-provisioned credential. Nothing in the fleet
|
|
|
|
|
clears it; a person does.
|
|
|
|
|
|
|
|
|
|
`wait` stays the only stored status for both. The kind is a **derived state**
|
|
|
|
|
computed from qualifier keys, in the same way `stalled` and `needs_review`
|
|
|
|
|
are derived labels rather than stored statuses (ADR-001 workplan convention).
|
|
|
|
|
|
|
|
|
|
Founder decision 2026-09-28 (`CUST-WP-0074`): derived states, not two new
|
|
|
|
|
stored statuses. Reason: a qualifier is additive — existing files stay valid
|
|
|
|
|
and a consistency warning migrates them one workplan at a time — whereas new
|
|
|
|
|
statuses force every consumer (hub enum, fix-consistency rank logic, Fabric
|
|
|
|
|
open-task set, ralph-workplan, dashboards) to change at once.
|
|
|
|
|
|
|
|
|
|
## Shape
|
|
|
|
|
|
|
|
|
|
No new task-block keys. Three keys that task blocks already carry become
|
|
|
|
|
meaningful together:
|
|
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
|
# External commitment → derived state: waiting-external
|
|
|
|
|
status: wait
|
|
|
|
|
depends_on: [FLEX-WP-0020] # workplan id, or task id (KEY-WP-0035-T02)
|
|
|
|
|
blocking_reason: "access-engine rename must land first"
|
|
|
|
|
|
|
|
|
|
# Human gate → derived state: needs-human
|
|
|
|
|
status: wait
|
|
|
|
|
needs_human: true
|
|
|
|
|
blocking_reason: "operator must provision NC_WEBDAV/AGE creds to OpenBao"
|
|
|
|
|
decision_id: CCR-2026-0004 # optional, when a tracked decision exists
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
A task may carry both (derived state: both). `decision_id` is the id of a
|
|
|
|
|
hub decision or a change request; it is optional and never a substitute for
|
|
|
|
|
`needs_human: true`.
|
|
|
|
|
|
|
|
|
|
## Rules
|
|
|
|
|
|
|
|
|
|
1. A `wait` task carries a `depends_on` list, `needs_human: true`, or both.
|
|
|
|
|
Neither → consistency WARN (unqualified wait), not auto-fixable.
|
|
|
|
|
2. `blocking_reason` stays required for `wait`. The hub API already refuses
|
|
|
|
|
`wait` without it; the file must carry it so the hub can be rebuilt from
|
|
|
|
|
files.
|
|
|
|
|
3. An external commitment is satisfied when every `depends_on` target is
|
|
|
|
|
terminal: task targets `done` or `cancel`, workplan targets `finished` or
|
|
|
|
|
`archived` (coordination-model v0.2 §5 commitment rule). A task still
|
|
|
|
|
`wait` after satisfaction → consistency WARN (blocker satisfied). Targets
|
|
|
|
|
the hub cannot resolve produce no warning.
|
|
|
|
|
4. A human gate is satisfied by the person's recorded action in the task text,
|
|
|
|
|
or — when `decision_id` is set — by that decision resolving, which raises
|
|
|
|
|
the same WARN.
|
|
|
|
|
5. Workplan `blocked` stays stored. The hub derives `blocked-human` when any
|
|
|
|
|
wait task is a human gate, else `blocked-external` when any is an external
|
|
|
|
|
commitment, else `none`. "Everything waiting on a person" is therefore one
|
|
|
|
|
query, not a reading exercise.
|
2026-09-28 23:16:48 +02:00
|
|
|
6. A wait on another owner's reply, with no workplan id yet, is an external
|
|
|
|
|
commitment qualified by `blocked_on: message-from:<agent>` on the task
|
|
|
|
|
block — the vocabulary C-25 already watches for unread replies. The
|
|
|
|
|
consistency check accepts it in place of `depends_on`; the hub read model
|
|
|
|
|
reports such a task as `unqualified` until an id exists, so upgrade to a
|
|
|
|
|
`depends_on` id as soon as the owner returns one.
|
2026-09-28 22:14:38 +02:00
|
|
|
7. A human-gate task is never leased to a worker. Coordination tooling treats
|
|
|
|
|
`needs_human: true` as a stop, not a wake condition.
|
|
|
|
|
|
|
|
|
|
## Scope of `depends_on`
|
|
|
|
|
|
|
|
|
|
- Workplan-frontmatter `depends_on` (C-20) is unchanged: it declares
|
|
|
|
|
workplan-level blockers.
|
|
|
|
|
- Task-block `depends_on` is the commitment surface this standard adds. It is
|
|
|
|
|
indexed into the same hub dependency table with the waiting task as the
|
|
|
|
|
from side.
|
|
|
|
|
- `related:` is context, never a commitment.
|
|
|
|
|
|
|
|
|
|
## Derived state names
|
|
|
|
|
|
|
|
|
|
| Stored | Qualifier | Derived task state | Hub field |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| `wait` | `depends_on` only | `waiting-external` | `wait_kind: external` |
|
|
|
|
|
| `wait` | `needs_human: true` only | `needs-human` | `wait_kind: human` |
|
|
|
|
|
| `wait` | both | both | `wait_kind: both` |
|
|
|
|
|
| `wait` | neither | unqualified (WARN) | `wait_kind: unqualified` |
|
|
|
|
|
|
|
|
|
|
Workplan: `blocked_kind` ∈ `human` \| `external` \| `none`, null unless
|
|
|
|
|
`status: blocked`.
|
|
|
|
|
|
|
|
|
|
## Consumers
|
|
|
|
|
|
|
|
|
|
- **state-hub** — fix-consistency indexes task-block `depends_on`, emits the
|
|
|
|
|
two warnings, syncs `needs_human` / `blocking_reason` / `decision_id` from
|
|
|
|
|
file to hub, exposes `wait_kind` and `blocked_kind` on the read model.
|
|
|
|
|
- **coordination-engine** — `cross-owner-wait-mode` enters from task-block
|
|
|
|
|
`depends_on`; the ontology gains commitment `kind: decision` for human
|
|
|
|
|
gates.
|
|
|
|
|
- **railiance-fabric** — the coordination graph colours commitment edges and
|
|
|
|
|
human-gate nodes distinctly and offers a needs-human-only view.
|
|
|
|
|
- **ralph-workplan** — the loop never picks up a `needs_human` task and its
|
|
|
|
|
"all tasks done" retirement rule is unaffected.
|
|
|
|
|
|
|
|
|
|
## Migration
|
|
|
|
|
|
|
|
|
|
Existing `wait` tasks without a qualifier remain valid files. The unqualified
|
|
|
|
|
WARN drives the backfill (`CUST-WP-0074-T05`), gate roots first. Do not
|
|
|
|
|
touch `flavor: residual` workplans.
|
|
|
|
|
|
|
|
|
|
## Review
|
|
|
|
|
|
|
|
|
|
Proposal-then-review. Acceptance is recorded in `CUST-WP-0074-T01`; on
|
|
|
|
|
acceptance set `status: accepted`, `revision: accepted-1`.
|