From d2802fc55db5d6ec1e5351a2b488d17b80f889e9 Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 14 Sep 2026 16:46:45 +0200 Subject: [PATCH] Specify prj- drivers and cross-owner wait mode. COORDINATION-WP-0005: map project-repository workplans onto CoordinationCase, refuse residual as a driver, and document the wait mode without enabling live worker injection. Assistant: grok Assistant-Session: 01a09dc1-b21e-77e1-919e-fcad2f82b267 --- AGENTS.md | 14 +- README.md | 5 + spec/coordination-model-v0.1.md | 2 + spec/coordination-model-v0.2.md | 123 +++++++++++ spec/cross-owner-wait-mode-v0.1.md | 109 ++++++++++ spec/worker-coordination-service-v0.1.md | 5 + src/coordination_engine/project_driver.py | 205 ++++++++++++++++++ tests/test_project_driver.py | 108 +++++++++ ...N-WP-0005-prj-cross-repo-and-agent-mode.md | 17 +- 9 files changed, 582 insertions(+), 6 deletions(-) create mode 100644 spec/coordination-model-v0.2.md create mode 100644 spec/cross-owner-wait-mode-v0.1.md create mode 100644 src/coordination_engine/project_driver.py create mode 100644 tests/test_project_driver.py diff --git a/AGENTS.md b/AGENTS.md index e83eefd..a17ce70 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,7 +2,7 @@ ## Repo Identity -**Purpose:** Coordination Engine - (fill in purpose) +**Purpose:** Goal-driven digital coordination; project-repository workplans as cross-repo drivers; worker leases/receipts (not live unless the operator started the service). **Domain:** communication **Repo slug:** coordination-engine @@ -27,6 +27,18 @@ Queueable writes return an explicit queued receipt if the central hub is unreachable. Treat that as pending local evidence, then ask the operator to run statehub outbox status/replay after connectivity returns. +### Cross-repo waits (COORDINATION-WP-0005) + +Do not mint a stranded `wait` when another repo/owner is the blocker. + +1. Look for a `prj-` workplan that lists both repos (`spec/coordination-model-v0.2.md`). That plan is the **driver**; product repos **implement**. +2. Observe the foreign record on State Hub. Do not copy or PATCH the other owner's tasks. +3. `flavor: residual` is not a driver and not implementation demand unless promoted. +4. Lease/wakeup only via the v0.1 worker service for selected gita repos. This session does **not** enable live worker injection. +5. Do not message `ops-warden` for secrets (SSH certs only). + +Mode: `spec/cross-owner-wait-mode-v0.1.md`. + ### Orient at session start ```bash diff --git a/README.md b/README.md index 7642a83..0c17adf 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,11 @@ Installation does not enable a service or start workers. See the [runbook](docs/worker-runtime.md) for configuration, worker acknowledgements, checkpoint continuation, backups and local verification. +Project-repository workplans (`prj-`) are coordination **drivers**; product +repos implement. See [v0.2](spec/coordination-model-v0.2.md) and +[cross-owner wait mode](spec/cross-owner-wait-mode-v0.1.md). Residual flavor +is not a driver. + The [v0.1 contract](spec/worker-coordination-service-v0.1.md) and [adapter decision](docs/adr/002-worker-runtime-boundary.md) describe the ownership boundary. The authoritative transport contract lives in diff --git a/spec/coordination-model-v0.1.md b/spec/coordination-model-v0.1.md index 72e65cd..450b096 100644 --- a/spec/coordination-model-v0.1.md +++ b/spec/coordination-model-v0.1.md @@ -14,6 +14,8 @@ * `ProductRequirementsDocument.md` * `RuntimeArchitectureAndAdapterSubsystem.md` * `AdapterInterfaceSpecification.md` +* `coordination-model-v0.2.md` — project-repository drivers (COORDINATION-WP-0005) +* `cross-owner-wait-mode-v0.1.md` ## 2. Purpose diff --git a/spec/coordination-model-v0.2.md b/spec/coordination-model-v0.2.md new file mode 100644 index 0000000..c801b23 --- /dev/null +++ b/spec/coordination-model-v0.2.md @@ -0,0 +1,123 @@ +# coordination-model-v0.2.md + +## 1. Document Status + +**Document:** coordination-model-v0.2.md +**Project:** coordination-engine +**Version:** 0.2 +**Status:** Addendum to v0.1 (COORDINATION-WP-0005) +**Scope:** Project-repository workplans as coordination drivers +**Extends:** `spec/coordination-model-v0.1.md` +**Related:** `spec/cross-owner-wait-mode-v0.1.md`, +`spec/worker-coordination-service-v0.1.md`, +`the-custodian/canon/architecture/adr-005-cross-repo-workplans-project-repos.md`, +`the-custodian/canon/standards/project-repository-flavor_v0.1.md` + +v0.1 remains the ontology. This addendum maps **estate work records** onto +that ontology. It does not replace actors, goals, commitments, or observation +loops. + +## 2. Purpose + +Cross-repository work in this estate is supposed to live in a **project +repository** (`prj-`, `repo_flavor: project`, ADR-005). Agents were +instead minting stranded `wait` notes on product workplans. The engine must +treat a `prj-` workplan as the **driver** of a `CoordinationCase`, and +product-repo workplans as **implementers**. + +## 3. Out of scope + +- The engine **does not author workplans**. Files remain source of truth + (ADR-001). State Hub remains the work-record read model. +- The engine **does not replace State Hub**, Fabric, or product + implementation in participating repos. +- The engine **does not vend credentials**. `ops-warden` issues SSH + certificates only. +- This addendum **does not enable live worker injection**. Wake/lease + remains the v0.1 worker-coordination service, off until an operator + starts it. + +## 4. Driver and implementer + +| Role | Binding | Authority | +| --- | --- | --- | +| **Driver** | Workplan whose repo is `prj-*` / `category: project` / `repo_flavor: project` | Sequences the effort, names participating repos, holds `depends_on` and related child ids. Does not implement production code. | +| **Implementer** | Workplan in a participating product/tooling repo | Owns code, tests, deployables, and local tasks. | + +Participant roles on the case (v0.1 `Participant.roles[]`): + +- `driver` — the project-repository actor (system: State Hub record + + project repo) +- `implementer` — each participating product repo (system) +- `observer` — State Hub / Fabric (read-only) +- `agent` — a worker only when the v0.1 service has selected that gita + repo as a wake target + +A workplan with `flavor: residual` is **not** a driver and is **not** +actionable demand unless promoted (STATE-WP-0092). Unspecified flavor is +relevant. + +## 5. Mapping onto v0.1 + +```text +prj- workplan → CoordinationCase + goal ← GOAL.md outcome + workplan title + driver ← the prj- workplan (commitment: sequence the effort) + implementers ← child workplans in other repos (related / depends_on) + commitments ← depends_on edges (kind: response | completion) + observations ← State Hub task/workplan status, receipts +``` + +`depends_on` is a **commitment**, not prose. The obligor is the +implementer (or upstream driver) named by the target id. The beneficiary +is the waiting plan. Satisfaction is the target reaching a terminal +satisfactory status (`done` / `finished` / `archived`), matching the +v0.1 worker-coordination dependency rule. + +`related:` without `depends_on` is context, not a commitment. + +## 6. Case identity + +```yaml +CoordinationCase: + id: "case:prj:{repo_slug}:{workplan_id}" # derived, not authored + scenario_type: project_driver + driver_workplan_id: string # hub UUID or canonical id + driver_repo: prj-* + implementers: + - repo: string + workplan_id: string? + role: implementer + commitments: # from depends_on + - kind: completion + obligor_workplan_id: string + beneficiary_workplan_id: string + flavor: planning | implementation | ... # driver's flavor +``` + +The engine derives this object from hub JSON. It does not POST a +workplan. + +## 7. Worked example — `prj-helixforge-factory` / `HFACT-WP-0001` + +Repo `prj-helixforge-factory` is `repo_flavor: project`. Workplan +`HFACT-WP-0001` (“Establish the internal HelixForge software factory on +Railiance”) is the driver. It states that it owns integration acceptance +and handoff and **neither replaces nor copies child implementation +plans**. + +Implementers named in `related:` include `GLAS-WP-0012`, `SAND-WP-0015`, +`REINAH-WP-0003`, `ACTIVITY-WP-0032`, `KEY-WP-0013`, `INFD-WP-0001`, +`APPROVAL-WP-0002`, `SECRETS-WP-0009`, `AUDIT-WP-0009`, `RPF-WP-0038`. +Those remain implementer workplans in their owning repos. + +A wait on `KEY-WP-0013` from the factory driver is a **commitment** on +the key-cape implementer, not a stranded `wait` on the driver with no +observer. Cross-owner handling is `spec/cross-owner-wait-mode-v0.1.md`. + +## 8. Compatibility + +v0.1 cases that are not `scenario_type: project_driver` are unchanged. +The worker-coordination service continues to poll ready/active +implementer tasks. This addendum tells it (and agents) which workplan is +the driver when one exists. diff --git a/spec/cross-owner-wait-mode-v0.1.md b/spec/cross-owner-wait-mode-v0.1.md new file mode 100644 index 0000000..fddf9b4 --- /dev/null +++ b/spec/cross-owner-wait-mode-v0.1.md @@ -0,0 +1,109 @@ +# Cross-owner wait handling mode v0.1 + +Status: specified (COORDINATION-WP-0005-T02). Not a live wake enablement. +Owner: coordination-engine. +Reconciles with: `spec/worker-coordination-service-v0.1.md` (do not fork +leases, safety, TAMQ, or receipts). +Ontology: `spec/coordination-model-v0.2.md`. + +## Problem + +Repo A sets a task `wait` because repo B’s owner has not returned. Nobody +observes B. The wait becomes a stranded inbox. That pattern is the top +theme holding the open-workplan count up. + +## Mode (replace “set wait and hope”) + +### Entry + +Any of: + +1. A task is `wait` / `needs_human` and names another repo, owner, or + workplan id. +2. A workplan is `blocked` on a `depends_on` target whose `repo` is not + this repo. +3. A `prj-` driver lists both repos (preferred). If no driver exists, + still open a **case object** from the waiting plan — do not author a + new workplan. + +Do **not** enter this mode for `flavor: residual` unless the flavor has +been promoted. + +### Observe + +Read the foreign owner’s record from State Hub (workplan, tasks, +`depends_on`, flavor, repo classification). Do not copy their task list +into this repo. Do not PATCH their tasks. + +### Attach + +Open or attach a `CoordinationCase` (`scenario_type: project_driver` +when a `prj-` workplan exists, otherwise `scenario_type: cross_owner_wait`). +The waiting plan is the beneficiary. The foreign plan is the obligor +commitment. + +### Act (only if the v0.1 service is already running) + +Lease/wakeup **only** selected gita-registered worker targets for the +implementer that is actually unblocked. Safety, sensitive-text stops, +human flags, and “no silent reroute” are exactly the v0.1 worker service. +This mode does not add a second wakeup path. + +If the service is not running, the case still exists as a derived object +and a hub-projectable receipt of `observed` / `waiting`. Agents follow +the instruction pack instead of injecting workers. + +### Checkpoint + +Reuse v0.1 checkpoint shape (summary, files_changed, next_action, +blocked_reason, 8 KiB, local). `blocked_reason` stops automatic +continuation. A checkpoint is not completion. + +### Fail closed + +Missing authorization, unregistered target, residual-as-driver, or +attempt to reassign another owner’s task → `stopped` with reason. Never +silently re-assign. + +### Exit + +| Exit | Condition | +| --- | --- | +| `satisfied` | Obligor workplan/task reaches `done`/`finished`/`archived` | +| `stopped` | Safety, human, missing auth, residual driver | +| `waiting` | Foreign record still open; case remains attached | + +## Receipt shape (hub-projectable) + +Extends the v0.1 sanitized receipt (state, reason, lease/trigger ids). +Additional fields (no payloads, no secrets): + +```yaml +scenario_type: project_driver | cross_owner_wait +case_id: string +driver_workplan_id: string? +driver_repo: string? +obligor_workplan_id: string +beneficiary_workplan_id: string +flavor: string? +``` + +Projection remains `POST /progress/` with +`event_type: coordination_receipt` as in the v0.1 Hub adapter. Queued +edge-relay receipts are not proof of central publication. + +## Common estate pattern + +Repo A (e.g. `net-kingdom`) blocked on repo B (e.g. `key-cape`, +`flex-auth`, OpenBao owner). If `HFACT-WP-0001` (or another `prj-` +driver) already lists both, attach there. Otherwise derive +`cross_owner_wait` from A’s waiting workplan. Observe B. Do not implement +B’s tasks from A’s session. Do not message `ops-warden` for a secret. + +## Anti-patterns + +- Stranded `wait` with no case and no observer +- Copying B’s tasks into A’s workplan +- Treating `flavor: residual` as a driver +- Inventing `warden secret` / asking ops-warden for API keys +- Enabling live worker injection as a side effect of documenting this mode diff --git a/spec/worker-coordination-service-v0.1.md b/spec/worker-coordination-service-v0.1.md index 6bb3bef..2009365 100644 --- a/spec/worker-coordination-service-v0.1.md +++ b/spec/worker-coordination-service-v0.1.md @@ -12,6 +12,11 @@ that repository. This package installs `coordination-engine`, never `tamq`. ## Observation and actionability +Cross-owner waits that used to become stranded `wait` notes are specified in +`spec/cross-owner-wait-mode-v0.1.md`. That mode **reuses** this service's +observation, leases, safety gates and receipts; it does not add a second +wakeup path or enable live injection. + The runtime polls State Hub's repos, workplans, tasks, dependency edges and unread messages. It dispatches only after a complete successful snapshot, only for explicitly configured gita repositories. It refreshes gita before addressing a diff --git a/src/coordination_engine/project_driver.py b/src/coordination_engine/project_driver.py new file mode 100644 index 0000000..7e4eff8 --- /dev/null +++ b/src/coordination_engine/project_driver.py @@ -0,0 +1,205 @@ +"""Derive a project-driver CoordinationCase from hub-shaped JSON. + +Does not author workplans, wake workers, or treat residual flavor as demand. +""" + +from __future__ import annotations + +from typing import Any + +RESIDUAL = "residual" +PROJECT_CATEGORIES = frozenset({"project"}) +OPEN_PLANS = frozenset({"proposed", "ready", "active", "blocked", "backlog"}) +SATISFIED = frozenset({"done", "finished", "archived", "cancel"}) + + +def repo_is_project(repo: dict[str, Any] | None) -> bool: + if not repo: + return False + slug = str(repo.get("slug") or "") + category = str( + repo.get("category") + or (repo.get("repo_classification") or {}).get("category") + or "" + ) + flavor = str(repo.get("repo_flavor") or "") + return slug.startswith("prj-") or category in PROJECT_CATEGORIES or flavor == "project" + + +def workplan_is_residual(plan: dict[str, Any] | None) -> bool: + if not plan: + return False + return str(plan.get("flavor") or "").strip().lower() == RESIDUAL + + +def _index_repos(snapshot: dict[str, Any]) -> dict[str, dict[str, Any]]: + repos = snapshot.get("repos") or {} + if isinstance(repos, dict): + # Hub adapter snapshot uses {repo_id: slug}. Tests may pass full rows. + out: dict[str, dict[str, Any]] = {} + for key, value in repos.items(): + if isinstance(value, dict): + out[str(value.get("id") or key)] = value + else: + out[str(key)] = {"id": key, "slug": value} + return out + return {str(row.get("id")): row for row in repos if isinstance(row, dict) and row.get("id")} + + +def _plans(snapshot: dict[str, Any]) -> dict[str, dict[str, Any]]: + rows = snapshot.get("plans") or snapshot.get("workplans") or [] + return {str(row["id"]): row for row in rows if isinstance(row, dict) and row.get("id")} + + +def _depends_targets(plan: dict[str, Any]) -> list[str]: + raw = plan.get("depends_on") + if raw is None: + raw = plan.get("depends_on_workplans") + if raw is None: + return [] + if not isinstance(raw, list): + raw = [raw] + out: list[str] = [] + for item in raw: + if isinstance(item, dict): + target = item.get("workplan_id") or item.get("workstream_id") or item.get("id") + else: + target = item + if target: + out.append(str(target)) + return out + + +def _related_ids(plan: dict[str, Any]) -> list[str]: + raw = plan.get("related") or plan.get("related_workplans") or [] + if isinstance(raw, str): + return [raw] + return [str(item) for item in raw] + + +def _canonical(plan: dict[str, Any]) -> str: + return str(plan.get("record_id") or plan.get("slug") or plan.get("id") or "") + + +def _match_plan(plans: dict[str, dict[str, Any]], token: str) -> dict[str, Any] | None: + if token in plans: + return plans[token] + needle = token.lower().replace("_", "-") + for plan in plans.values(): + slug = str(plan.get("slug") or "").lower().replace("_", "-") + record = str(plan.get("record_id") or "").lower() + if slug == needle or record == needle or slug.upper() == token.upper(): + return plan + if str(plan.get("id")) == token: + return plan + return None + + +class ResidualDriverError(ValueError): + """Raised when a residual workplan is asked to drive a case.""" + + +class NotProjectDriverError(ValueError): + """Raised when the selected workplan is not in a project repository.""" + + +def build_driver_case( + snapshot: dict[str, Any], + workplan_id: str | None = None, +) -> dict[str, Any]: + """Build a derived CoordinationCase from hub JSON. + + Residual flavor is refused as a driver. Non-project workplans are refused + as drivers (use cross_owner_wait instead). + """ + repos = _index_repos(snapshot) + plans = _plans(snapshot) + if workplan_id: + plan = _match_plan(plans, workplan_id) + if plan is None: + raise KeyError(f"workplan not in snapshot: {workplan_id}") + else: + plan = next( + ( + row + for row in plans.values() + if str(row.get("status") or "") in OPEN_PLANS + and repo_is_project(repos.get(str(row.get("repo_id")))) + and not workplan_is_residual(row) + ), + None, + ) + if plan is None: + raise NotProjectDriverError("no open project-repository workplan in snapshot") + + if workplan_is_residual(plan): + raise ResidualDriverError("residual flavor is not a coordination driver") + + repo = repos.get(str(plan.get("repo_id"))) + if not repo_is_project(repo): + raise NotProjectDriverError("workplan repo is not a project repository") + + repo_slug = str((repo or {}).get("slug") or "") + driver_id = str(plan["id"]) + implementers = [] + seen_repos: set[str] = set() + commitments = [] + for token in _depends_targets(plan): + target = _match_plan(plans, token) + commitments.append( + { + "kind": "completion", + "obligor_workplan_id": str(target["id"]) if target else token, + "beneficiary_workplan_id": driver_id, + "state": ( + "satisfied" + if target and str(target.get("status") or "") in SATISFIED + else "active" + ), + } + ) + if target: + t_repo = repos.get(str(target.get("repo_id"))) + slug = str((t_repo or {}).get("slug") or "") + if slug and slug not in seen_repos and slug != repo_slug: + seen_repos.add(slug) + implementers.append( + { + "repo": slug, + "workplan_id": str(target["id"]), + "role": "implementer", + } + ) + for token in _related_ids(plan): + target = _match_plan(plans, token) + if not target: + continue + t_repo = repos.get(str(target.get("repo_id"))) + slug = str((t_repo or {}).get("slug") or "") + if slug and slug not in seen_repos and slug != repo_slug: + seen_repos.add(slug) + implementers.append( + { + "repo": slug, + "workplan_id": str(target["id"]), + "role": "implementer", + } + ) + + flavor = str(plan.get("flavor") or "").strip().lower() or None + return { + "id": f"case:prj:{repo_slug}:{driver_id}", + "scenario_type": "project_driver", + "driver_workplan_id": driver_id, + "driver_repo": repo_slug, + "flavor": flavor, + "goal": plan.get("title"), + "implementers": implementers, + "commitments": commitments, + "actionable": True, + } + + +def actionable_demand(plan: dict[str, Any]) -> bool: + """False for residual flavor. Unspecified flavor is relevant.""" + return not workplan_is_residual(plan) diff --git a/tests/test_project_driver.py b/tests/test_project_driver.py new file mode 100644 index 0000000..8a4b6ee --- /dev/null +++ b/tests/test_project_driver.py @@ -0,0 +1,108 @@ +from coordination_engine.project_driver import ( + NotProjectDriverError, + ResidualDriverError, + actionable_demand, + build_driver_case, + repo_is_project, + workplan_is_residual, +) + +HFACT = { + "repos": { + "prj": { + "id": "prj", + "slug": "prj-helixforge-factory", + "category": "project", + "repo_flavor": "project", + }, + "key": {"id": "key", "slug": "key-cape", "category": "product"}, + "glas": {"id": "glas", "slug": "glas-harness", "category": "tooling"}, + "infd": {"id": "infd", "slug": "informed-decision", "category": "product"}, + }, + "plans": [ + { + "id": "ed4fe524", + "slug": "hfact-wp-0001", + "title": "Establish the internal HelixForge software factory on Railiance", + "status": "active", + "flavor": "implementation", + "repo_id": "prj", + "depends_on": [{"workplan_id": "key-wp"}], + "related": ["GLAS-WP-0012", "INFD-WP-0001", "KEY-WP-0013"], + }, + { + "id": "key-wp", + "slug": "key-wp-0013", + "title": "Approval-engine resource audience", + "status": "blocked", + "flavor": "implementation", + "repo_id": "key", + }, + { + "id": "glas-wp", + "slug": "glas-wp-0012", + "title": "Prove the first local rein profile", + "status": "blocked", + "repo_id": "glas", + }, + { + "id": "infd-wp", + "slug": "infd-wp-0001", + "title": "Founding specs", + "status": "active", + "flavor": "planning", + "repo_id": "infd", + }, + { + "id": "leftover", + "slug": "hfact-wp-0099", + "title": "Factory leftover", + "status": "proposed", + "flavor": "residual", + "repo_id": "prj", + }, + ], +} + + +def test_hfact_builds_driver_case_with_implementers_and_depends_on_commitments(): + case = build_driver_case(HFACT, "ed4fe524") + assert case["scenario_type"] == "project_driver" + assert case["driver_repo"] == "prj-helixforge-factory" + assert case["driver_workplan_id"] == "ed4fe524" + assert case["actionable"] is True + repos = {row["repo"] for row in case["implementers"]} + assert "key-cape" in repos + assert "glas-harness" in repos + assert "informed-decision" in repos + assert "prj-helixforge-factory" not in repos + assert case["commitments"] + assert case["commitments"][0]["kind"] == "completion" + assert case["commitments"][0]["obligor_workplan_id"] == "key-wp" + assert case["commitments"][0]["beneficiary_workplan_id"] == "ed4fe524" + assert case["commitments"][0]["state"] == "active" + + +def test_residual_is_refused_as_driver_and_is_not_actionable_demand(): + leftover = next(p for p in HFACT["plans"] if p["id"] == "leftover") + assert workplan_is_residual(leftover) + assert actionable_demand(leftover) is False + try: + build_driver_case(HFACT, "leftover") + raise AssertionError("residual must not drive a case") + except ResidualDriverError: + pass + + +def test_product_workplan_is_not_a_project_driver(): + assert repo_is_project(HFACT["repos"]["key"]) is False + try: + build_driver_case(HFACT, "key-wp") + raise AssertionError("product plan must not be a driver") + except NotProjectDriverError: + pass + + +def test_auto_selects_open_project_plan_and_skips_residual(): + case = build_driver_case(HFACT) + assert case["driver_workplan_id"] == "ed4fe524" diff --git a/workplans/COORDINATION-WP-0005-prj-cross-repo-and-agent-mode.md b/workplans/COORDINATION-WP-0005-prj-cross-repo-and-agent-mode.md index 4874347..e6dd45a 100644 --- a/workplans/COORDINATION-WP-0005-prj-cross-repo-and-agent-mode.md +++ b/workplans/COORDINATION-WP-0005-prj-cross-repo-and-agent-mode.md @@ -4,7 +4,7 @@ type: workplan title: "prj- awareness, cross-owner waits, and agent coordination mode" domain: communication repo: coordination-engine -status: proposed +status: finished owner: grok topic_slug: communication flavor: extension @@ -42,7 +42,7 @@ Do not invent credential vending. ```task id: COORDINATION-WP-0005-T01 -status: todo +status: done priority: high state_hub_task_id: "de21e1da-e66d-51d4-8689-6017346199bd" ``` @@ -63,7 +63,7 @@ existing project repo. ```task id: COORDINATION-WP-0005-T02 -status: todo +status: done priority: high depends_on: [COORDINATION-WP-0005-T01] state_hub_task_id: "9e99e12b-b4a2-569a-bce2-dbdc645d75a1" @@ -85,7 +85,7 @@ service rather than forked. ```task id: COORDINATION-WP-0005-T03 -status: wait +status: done priority: medium depends_on: [COORDINATION-WP-0005-T01, STATE-WP-0092] state_hub_task_id: "2c5d91a5-2e77-5778-ac52-f95ea5c2ed63" @@ -104,7 +104,7 @@ workplan and refuse to treat a residual as actionable demand. ```task id: COORDINATION-WP-0005-T04 -status: wait +status: done priority: high depends_on: [COORDINATION-WP-0005-T02] state_hub_task_id: "967e709e-1fe8-5ee0-a544-41e7cfe13f4b" @@ -124,3 +124,10 @@ residuals; do not message `ops-warden` for secrets. Keep the pack short. Done when the named files describe the mode with a pointer to the spec, and a new session can choose “coordinate via engine” without inventing a parallel process. + +2026-09-14: T01–T04 landed. Specs: +`spec/coordination-model-v0.2.md`, `spec/cross-owner-wait-mode-v0.1.md`. +Adapter: `coordination_engine.project_driver.build_driver_case` (HFACT +fixture; residual refused). Agent pack in this `AGENTS.md`, +`the-custodian` session protocol, and repo-manager scaffold/rapp +templates. Live worker injection was not enabled.