the-custodian/workplans/CUST-WP-0074-qualified-wait-states.md
codex a2fc2e20ea
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 3s
CUST-WP-0074-T02 done: migration live on the primary, 126 task edges written, first blocked_kind numbers
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-29 00:38:08 +02:00

351 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
id: CUST-WP-0074
type: workplan
title: "Qualify task wait states: external commitment versus human gate"
domain: infotech
repo: the-custodian
status: active
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. *Revised during T05 (2026-09-28): the task block carries
`blocked_on: message-from:<agent>` instead; C-39 accepts it, the hub
read model does not yet (reports `unqualified`) — follow-up in T02.*
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: done
priority: high
state_hub_task_id: "82e9522b-bb69-52f4-b182-13ddfa8cd56e"
```
Owner: the-custodian.
2026-09-28: proposal committed — standard `11e6202` (status `proposed`),
convention edit in the same commit, ralph-workplan spec `abea4cc`.
Founder accepted 2026-09-28; standard set to `accepted`, revision `accepted-1`.
- `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: done
priority: high
state_hub_task_id: "e5a05f83-1db5-59a7-8cae-095237f50b7d"
```
2026-09-28 — implemented in state-hub, three commits on main, not pushed,
907 tests pass: `b9997d6` (read model: `wait_kind` on `/tasks/`,
`blocked_kind` on `/workplans/`, `decision_id` on tasks, `from_task_id` on
dependency rows, migration `e8f9a0b1c2d3`), `00c6e7a` (checks C-39
unqualified wait, C-40 blocker satisfied, C-41 qualifier drift file→hub;
C-20 indexes task-block `depends_on` with the task as from side), and a
follow-up that skips closed plans/tasks in task-scope C-20. Check-only run
on this repo: C-39 and C-40 found real drift in CUST-WP-0071/0073, fixed in
the files. Remaining for `done`: deploy the migration to the primary, then
one `--fix` run so C-20/C-41 write through (held until the primary has the
columns — a `--fix` before that would PATCH fields the API rejects).
2026-09-29: image `main-85cc6d7` built after the founder had fluid-core run
#21 stopped (it held the single-slot runner 1h31m; post-mortem requested
from fluid-core `7902287e`, runner observations to railiance-forge
`338888ac`). Headroom preflight `ok`. The `helm upgrade` itself is a
founder-run step (agent deploy blocked by harness policy):
`helm -n state-hub upgrade state-hub deploy/railiance/apps/charts/state-hub
--reuse-values --set image.tag=main-85cc6d7 --atomic --timeout 5m`, then
commit `appVersion`, then `make fix-consistency` across the touched repos
writes the ~130 parked task edges.
**Done 2026-09-29 00:21 CEST** — founder ran the promote: release rev 66,
`main-85cc6d7` on both pods, `schema.applied = e8f9a0b1c2d3`, `appVersion`
recorded (`65cda5d`). Sync across the 21 repos wrote 126 task-scoped
dependency rows. First read-model answer: blocked workplans by
`blocked_kind` = human 29, external 22, none 59; wait tasks by `wait_kind`
= external 63, human 28, both 11, unqualified 147 (the hub counts
`blocked_on: message-from:` waits as unqualified — the rule-6 follow-up —
plus the long tail). Fabric's coordination export consumes the fields
(63/28/11) but reports `task_depends_on_edges: 0` because it reads
`/state/deps` stubs, which do not carry `from_task_id` yet — T04 follow-up
in RAIL-FAB-WP-0029, not a blocker.
Owner: state-hub. Founder 2026-09-28: implement directly from this plan,
no delegated `STATE-WP` workplan — keep the layer count down while getting
a grip on exactly this. Receipt (commits, checks, tests) is recorded here.
Requested:
- 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: done
priority: medium
state_hub_task_id: "87711ade-f9f8-5e7f-9c15-025e5c235b35"
```
Owner: coordination-engine.
**Done 2026-09-28** (founder: implement directly, no delegated workplan).
Spec edits committed in coordination-engine `354e800`, not pushed:
`coordination-model-v0.2.md` §5 gains `kind: decision` and task-scope
`depends_on`; `cross-owner-wait-mode-v0.1.md` entry 1 now keys on
task-block `depends_on`, prose is a fallback until T05, and the Act
section states that a human gate is never leased or woken.
- `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: done
priority: medium
state_hub_task_id: "aaf60a09-ba58-578d-ba26-480d874c6b97"
```
Owner: railiance-fabric, under `RAIL-FAB-WP-0029`.
**Done 2026-09-28** (implemented directly; railiance-fabric `9dbb8f0` on
main, not pushed; 87 tests pass, `node --check` on the inline script OK).
Task nodes carry `wait_kind`, workplan nodes `blocked_kind` (hub value
wins, derived otherwise); task-level `depends_on` becomes
`edge_kind: commitment` edges counted in chokepoint in-degree; human
gates are a `humanGate` node marker (rose `#be123c`; commitment cyan
`#0891b2`). Filter: `needs_human=true` on the UI checkbox, server route
and `export --format coordination --needs-human`. Caveat: the view reads
`/state/deps` stubs, not per-workplan dependency rows, so task-scope
edges appear once T02's indexing is deployed on the primary.
- `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: progress
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.
**Slice R1 done 2026-09-28** — `FLEX-WP-0020` dependents: NK-WP-0039-T03/T04
(`70bdc4a`), USER-WP-0034-T02 (`b08b2d7`), RCLK-WP-0002-T01–T04 (`82c6784`),
each synced through fix-consistency. RAIL-FAB-WP-0031 and REUSE-WP-0023 are
`flavor: residual` and were left alone. Rule 6 was revised on contact:
owner-return waits use task-block `blocked_on: message-from:<agent>`.
Queue for the next slices is C-39 output per repo; seen so far: net-kingdom
8, user-engine 8, railiance-clock 8 (all R2/R3/R8 roots).
**Slice R2 done 2026-09-28** — credential-provisioning root, 41 tasks in
secrets-engine `ccd057a`, ops-mason `0d2fe45`, sand-boxer `6fca6de`,
glas-harness `9f04c42`, intelligence-radar `9bcc20e`, railiance-platform
`360cc5c`, ops-warden `dda65ef`; all synced. Left unqualified on purpose:
WARDEN-WP-0040-T01 (no external gate stated in the file — owner decides),
WARDEN-WP-0039 (residual). C-40 now flags RPF-WP-0035-T02/T06/T08: every
external target terminal, human part remains — the intended signal.
Hub migration still queued behind a fluid-core CI build; task edges parked.
**Slices R3, R5, R8 and the R1 root done 2026-09-28** — R3 audit-core
returns: flex-auth `912dfcb`, net-kingdom `a705961`, info-tech-canon
`8a5b9b2`. R5 ArgoCD lane: railiance-platform `98e0c3a` (RPF-WP-0036-T03
left for its owner: no gate stated). R8 identity custody: hub-core `77b664c`
(HUB-WP-0012's prose `blocked_on` moved to `blocking_reason`), user-engine
`0d554dc`, net-kingdom `2a004a7`, plus HUB-WP-0006-T06 and NK-WP-0022/0027.
R1 root: flex-auth `23c93bb` qualifies FLEX-WP-0020-T04..T11 and
FLEX-WP-0027-T03. **Finding:** FLEX-WP-0020-T04 waits on the downstream
owners' handoffs, which are the very plans (NK-WP-0039, USER-WP-0034,
RAIL-FAB-WP-0031, REUSE-WP-0023) that wait on FLEX-WP-0020 — a cycle that
was invisible as prose; T04 looks satisfiable. Route to flex-auth in T06.
Residuals left untouched: HUB-WP-0011, APPROVAL-WP-0002, TEN-WP-0012,
WARDEN-WP-0039, RAIL-FAB-WP-0031, REUSE-WP-0023.
**Slices R4, R6, R7 done 2026-09-29** — R4 was already qualified by an
earlier session (RAPPS-WP-0014, VERGABE-WP-0018/0019); added
VERGABE-WP-0019-T06 `f4943cc`, FIN-WP-0004 `cc8a5f4`. R6: state-hub
`09942bc`, rapp-core-hub `566482f`, core-hub `fc9cb69`, railiance-fabric
`094f226`. R7: railiance-clock `f207393`. All eight gate roots are now
qualified and synced; 21 repos touched in total.
Left unqualified on purpose (gate not stated in the file — owner decides,
routed in T06): STATE-WP-0079-T04, ORGREF-WP-0001-T01/T02,
RCLK-WP-0004-T03, RPF-WP-0036-T03, WARDEN-WP-0040-T01, FIN-WP-0005-T01–T04.
**Standard gap for v0.2:** STATE-WP-0079-T05 waits on a one-week
legacy-meter evidence window — neither an external commitment nor a human
gate. A `wait_until: <date>` qualifier (time/evidence kind) is the missing
third form; do not force it into `needs_human`.
Long tail beyond the gate roots (about 30 workplans) is left to the C-39
sweep: `make check-consistency --all` gives the queue.
## Correct the mis-blocked workplans
```task
id: CUST-WP-0074-T06
status: progress
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.
**Messages sent 2026-09-29** (hub inbox, from the-custodian): fluid-core
`341e7b03`, fluid-telegram `f88aec3d`, key-cape `b68505f8`,
railiance-cluster `a1368d99`, frontend-patterns `252fc89f`, markitect-main
`ab79418f`, flex-auth `a7e08fdb` (FLEX-WP-0020-T04 cycle), state-hub
`ab51efff` (T04 gate, T05 time-window), prj-forgejo-org-refactor
`cf4be649`, railiance-clock `072c5b48`, railiance-platform `a2868f24`,
ops-warden `daca8a09`, fin-hub `722a471e`. Done when each owner has
replied or re-statused; record outcomes below.