--- id: CUST-WP-0074 type: workplan title: "Qualify task wait states: external commitment versus human gate" domain: infotech repo: the-custodian status: ready owner: the-custodian topic_slug: custodian flavor: planning created: "2026-09-28" updated: "2026-09-28" related: - CUST-WP-0072 - STATE-WP-0092 - COORDINATION-WP-0005 - RAIL-FAB-WP-0029 - CUST-WP-0071 origin: analysis origin_ref: the-custodian/history/20260928-blocked-workplan-graph.md state_hub_workstream_id: "a442e062-9c91-5ac6-8f1d-35f2117b7069" --- # Qualify task wait states: external commitment versus human gate ## Why On 2026-09-28 the hub held 105 blocked workplans (retired slugs excluded) with 220 `wait` tasks between them. The blocker was recorded as prose in the task text. Only 7 dependency edges existed in `workplan_dependencies` across all open workplans, and only 5 of the 220 wait tasks set `needs_human`. Nothing could answer "which of these only Bernd can unblock" or "which clear on their own when `FLEX-WP-0020` finishes" without reading every task. Reading them shows two different kinds of wait: - **Case A — external commitment.** The task waits for another repo's workplan or task to reach a terminal state. It clears by itself; the waiting side only needs to observe. Example: five workplans wait on the `flex-auth` → `access-engine` rename (`FLEX-WP-0020`). - **Case B — human gate.** The task waits for a decision, an approval, an attended privileged action, or a credential provisioned by an operator. Nothing in the fleet clears it; a person does. Example: about 25 workplans sit behind one attended OpenBao provisioning session (`CCR-2026-0004` and the per-lane scoped applies). Both are `wait` today. Founder decision 2026-09-28: keep `wait` as the stored status and qualify it, deriving two visible states from the qualifier. This follows the existing convention that `stalled` and `needs_review` are derived labels, not stored statuses (`.claude/rules/workplan-convention.md`). Two new stored statuses were rejected: every consumer (hub enum, fix-consistency rank logic, Fabric `OPEN_TASK`, ralph-workplan, dashboards) would have to change at once across 1465 workplans, whereas a qualifier is additive and a WARN drives migration one workplan at a time. Analysis, gate roots and the extracted graph: `history/20260928-blocked-workplan-graph.md`. ## The shape No new keys. Three keys that task blocks already carry become meaningful together: ```yaml # Case A — 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" # Case B — 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 ``` Rules: 1. A `wait` task carries `depends_on` (Case A), `needs_human: true` (Case B), or both. Neither → consistency WARN. 2. `blocking_reason` stays required for `wait` (the hub API already refuses `wait` without it). 3. Case A is satisfied when every `depends_on` target is `done`, `finished` or `archived` — the coordination-model v0.2 commitment rule. A task still `wait` past satisfaction → consistency WARN. 4. Case B is satisfied by a resolved decision or by the human's recorded action in the task text; when `decision_id` is set, resolution of that decision triggers the same WARN. 5. Workplan `blocked` stays stored. The hub derives `blocked-external` when all its wait tasks are Case A, `blocked-human` when any is Case B, so "everything waiting on a person" is one query. 6. A wait on a repo that has no workplan id yet stays Case A with the repo named in `blocking_reason` and the workplan-level `blocked_on:` field (C-25). Upgrade to a `depends_on` id as soon as the owner returns one. The workplan-level `depends_on` (C-20) is unchanged. Task-level `depends_on` is the new commitment surface; `related:` stays context. ## Write the standard and update the convention ```task id: CUST-WP-0074-T01 status: todo priority: high state_hub_task_id: "82e9522b-bb69-52f4-b182-13ddfa8cd56e" ``` Owner: the-custodian. - `canon/standards/task-wait-qualifiers_v0.1.md`: the shape above, the six rules, the derived-state names, and the satisfaction rule cross- referenced to `coordination-engine/spec/coordination-model-v0.2.md` §5. - `.claude/rules/workplan-convention.md`: extend the task-block template and the status-progression paragraph; state that `wait` without a qualifier is a WARN once T02 ships. - `~/ralph-workplan/workplan-spec.md`: same template extension, so the loop's "all tasks done" check is unaffected and it never picks up a `needs_human` task. - Human-gated: canon goes through proposal-then-review. Commit the proposal, then record the founder's acceptance here. ## Hand off enforcement and indexing to state-hub ```task id: CUST-WP-0074-T02 status: todo priority: high state_hub_task_id: "e5a05f83-1db5-59a7-8cae-095237f50b7d" ``` Owner: state-hub. This task is the handoff and its acceptance; the implementation lives in a `STATE-WP` workplan whose id gets recorded in `depends_on` here once it exists. Requested, by hub message to `state-hub`: - C-20 reads task-block `depends_on` (workplan and task ids) and writes `workplan_dependencies` rows with the waiting task as the from side — the table has `to_task_id`; check whether a `from_task_id` column is needed or whether `from_workplan_id` plus description suffices. - New check (next free C-id): `wait` task with neither task-block `depends_on` nor `needs_human: true` → WARN, not fixable. - New check: `wait` task whose every `depends_on` target is terminal, or whose `decision_id` is resolved → WARN "blocker satisfied", not fixable. - `needs_human`, `blocking_reason` and `decision_id` written from file to hub on sync (columns exist; `decision_id` may need one). - `GET /tasks/` and `GET /workplans/` expose a derived `wait_kind` / `blocked_kind` field per rule 5. Read model only — no new write route. Acceptance: the four checks run green against this repo, and the derived fields answer for `CUST-WP-0074-T05` below. ## Hand off the ontology addendum to coordination-engine ```task id: CUST-WP-0074-T03 status: todo priority: medium state_hub_task_id: "87711ade-f9f8-5e7f-9c15-025e5c235b35" ``` Owner: coordination-engine. - `spec/coordination-model-v0.2.md` §5 gains commitment `kind: decision` for Case B, obligor = the named human or decision id, satisfaction = decision resolved. Case A is the existing `kind: completion`. - `spec/cross-owner-wait-mode-v0.1.md` entry condition 1 changes from "names another repo, owner, or workplan id" in prose to task-block `depends_on`; the prose form remains a fallback until the T05 backfill is complete. - Human-gate cases must never be leased to a worker; make that explicit in the Act section. ## Hand off the view to railiance-fabric ```task id: CUST-WP-0074-T04 status: todo priority: medium state_hub_task_id: "aaf60a09-ba58-578d-ba26-480d874c6b97" ``` Owner: railiance-fabric, under `RAIL-FAB-WP-0029`. - `coordination_graph.py` reads task-block `depends_on` for edges and the derived `wait_kind` for colour: external commitment versus human gate. - Chokepoint sizing already uses in-degree; add a filter for `needs-human` only, so the operator's queue is one view. - Fabric remains a read of State Hub. No authoring. ## Backfill the open wait tasks ```task id: CUST-WP-0074-T05 status: wait priority: high needs_human: false depends_on: [CUST-WP-0074-T01, CUST-WP-0074-T02] blocking_reason: "Standard must be accepted (T01) and the unqualified-wait WARN must exist (T02) before touching foreign workplan files." state_hub_task_id: "03a9c033-470e-56cf-965f-a5b289dd71af" ``` Owner: the-custodian, following the `CUST-WP-0072` backfill pattern: edit files in each repo, commit, run fix-consistency, never patch the hub directly. - Start with the gate roots in the history record (R1–R8): qualifying those roughly 60 tasks turns the largest fan-outs into real edges. - Case B tasks: set `needs_human: true`; attach `decision_id` where a CCR or hub decision exists (`CCR-2026-0004` covers the offsite-backup lane). - Case A tasks: convert the referenced id in the prose to `depends_on`. - Leave `flavor: residual` workplans alone. - Done when the unqualified-wait WARN count for open workplans is zero. ## Correct the mis-blocked workplans ```task id: CUST-WP-0074-T06 status: todo priority: low state_hub_task_id: "8c51fc11-b09b-598a-8866-7cc39f3f1ada" ``` Owner: the-custodian coordinates; each repo owner edits. Six blocked workplans have no `wait` task at all (`fluid-wp-0008`, `fluid-wp-0009`, `ft-wp-0001`, `glas-wp-0015`, `key-wp-0013`, `three-phoenix-ha-cluster`); the `FEP-WP-0002/0003/0004/0006/0008` chain is self-ordering inside one repo; `testdrive-jsui-publication` waits on a submodule init. Message each owner with the finding and the qualifier rule. Do not edit their files or reassign their tasks. Record the outcome per workplan here.