generated from coulomb/repo-seed
Normalize workplan IDs and activate parents on task start
This commit is contained in:
@@ -28,7 +28,7 @@ Current state:
|
||||
New State Hub-local workplans should use the prefix:
|
||||
|
||||
```text
|
||||
SHUB-WP-0001
|
||||
STATE-WP-0001
|
||||
```
|
||||
|
||||
Legacy Custodian-hosted State Hub plans, such as `CUST-WP-0042`, may retain
|
||||
|
||||
2
SCOPE.md
2
SCOPE.md
@@ -40,7 +40,7 @@ embedded path in `the-custodian` is now a pointer only.
|
||||
|
||||
## Workplan Convention
|
||||
|
||||
New State Hub-local workplans use `SHUB-WP-####`.
|
||||
New State Hub-local workplans use `STATE-WP-####`.
|
||||
|
||||
Migrated legacy State Hub workplans may temporarily retain `CUST-WP-####`
|
||||
identifiers when preserving existing State Hub workstream and task IDs is the
|
||||
|
||||
@@ -10,6 +10,7 @@ from api.models.task import Task, TaskStatus
|
||||
from api.models.token_event import TokenEvent
|
||||
from api.models.workstream import Workstream
|
||||
from api.schemas.task import TaskCreate, TaskRead, TaskUpdate
|
||||
from api.services.lifecycle import activate_parent_for_task_start, status_value
|
||||
|
||||
router = APIRouter(prefix="/tasks", tags=["tasks"])
|
||||
|
||||
@@ -49,6 +50,13 @@ async def create_task(
|
||||
) -> Task:
|
||||
task = Task(**body.model_dump())
|
||||
session.add(task)
|
||||
if status_value(task.status) == "in_progress":
|
||||
ws = await session.get(Workstream, task.workstream_id)
|
||||
activate_parent_for_task_start(
|
||||
previous_task_status="todo",
|
||||
new_task_status=task.status,
|
||||
parent_workstream=ws,
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(task)
|
||||
return task
|
||||
@@ -75,7 +83,7 @@ async def update_task(
|
||||
if task is None:
|
||||
raise HTTPException(status_code=404, detail="Task not found")
|
||||
|
||||
previous_status = task.status.value
|
||||
previous_status = status_value(task.status)
|
||||
|
||||
# Separate token fields from task fields
|
||||
token_field_names = {
|
||||
@@ -92,15 +100,22 @@ async def update_task(
|
||||
update_data = body.model_dump(exclude_unset=True)
|
||||
token_data = {k: update_data.pop(k) for k in list(update_data.keys()) if k in token_field_names}
|
||||
suppress_token_event = bool(token_data.pop("suppress_token_event", False))
|
||||
status_update = update_data.get("status")
|
||||
new_status = status_value(status_update) if status_update is not None else None
|
||||
|
||||
for field, value in update_data.items():
|
||||
setattr(task, field, value)
|
||||
if new_status is not None:
|
||||
ws = await session.get(Workstream, task.workstream_id)
|
||||
activate_parent_for_task_start(
|
||||
previous_task_status=previous_status,
|
||||
new_task_status=new_status,
|
||||
parent_workstream=ws,
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(task)
|
||||
|
||||
# Token event — three-tier logic, only for an intentional transition to done.
|
||||
status_update = update_data.get("status")
|
||||
new_status = status_update.value if hasattr(status_update, "value") else status_update
|
||||
if (
|
||||
new_status == "done"
|
||||
and previous_status != "done"
|
||||
|
||||
50
api/services/lifecycle.py
Normal file
50
api/services/lifecycle.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from api.workplan_status import normalize_workstream_status
|
||||
|
||||
|
||||
TASK_STARTED_STATUS = "in_progress"
|
||||
TASK_NOT_STARTED_STATUS = "todo"
|
||||
PARENT_ACTIVATION_STATUSES = {"proposed", "ready", "backlog"}
|
||||
|
||||
|
||||
def status_value(status: Any) -> str:
|
||||
if hasattr(status, "value"):
|
||||
status = status.value
|
||||
return str(status or "").strip().lower()
|
||||
|
||||
|
||||
def should_activate_parent_for_task_start(
|
||||
*,
|
||||
previous_task_status: Any,
|
||||
new_task_status: Any,
|
||||
parent_workstream_status: Any,
|
||||
) -> bool:
|
||||
"""Return whether a task start should move its parent to active."""
|
||||
return (
|
||||
status_value(previous_task_status) == TASK_NOT_STARTED_STATUS
|
||||
and status_value(new_task_status) == TASK_STARTED_STATUS
|
||||
and normalize_workstream_status(parent_workstream_status)
|
||||
in PARENT_ACTIVATION_STATUSES
|
||||
)
|
||||
|
||||
|
||||
def activate_parent_for_task_start(
|
||||
*,
|
||||
previous_task_status: Any,
|
||||
new_task_status: Any,
|
||||
parent_workstream: Any,
|
||||
) -> bool:
|
||||
"""Activate a planning-state parent workstream when real task work starts."""
|
||||
if parent_workstream is None:
|
||||
return False
|
||||
if not should_activate_parent_for_task_start(
|
||||
previous_task_status=previous_task_status,
|
||||
new_task_status=new_task_status,
|
||||
parent_workstream_status=getattr(parent_workstream, "status", None),
|
||||
):
|
||||
return False
|
||||
parent_workstream.status = "active"
|
||||
return True
|
||||
@@ -3,13 +3,13 @@
|
||||
New workplans in this repository use:
|
||||
|
||||
```text
|
||||
SHUB-WP-0001-short-title.md
|
||||
STATE-WP-0001-short-title.md
|
||||
```
|
||||
|
||||
Workplan frontmatter should include:
|
||||
|
||||
```yaml
|
||||
id: SHUB-WP-0001
|
||||
id: STATE-WP-0001
|
||||
type: workplan
|
||||
title: "Short Title"
|
||||
domain: custodian
|
||||
|
||||
@@ -28,9 +28,9 @@ async def _create_topic(client, domain_slug="testdomain", slug="testtopic", titl
|
||||
return r.json()
|
||||
|
||||
|
||||
async def _create_workstream(client, topic_id, slug="test-ws", title="Test WS"):
|
||||
async def _create_workstream(client, topic_id, slug="test-ws", title="Test WS", status="active"):
|
||||
r = await client.post("/workstreams/", json={
|
||||
"topic_id": topic_id, "slug": slug, "title": title,
|
||||
"topic_id": topic_id, "slug": slug, "title": title, "status": status,
|
||||
})
|
||||
assert r.status_code == 201, r.text
|
||||
return r.json()
|
||||
@@ -213,6 +213,38 @@ class TestTasks:
|
||||
assert "High prio" in titles
|
||||
assert "Low prio" not in titles
|
||||
|
||||
@pytest.mark.parametrize("initial_status", ["proposed", "ready", "backlog"])
|
||||
async def test_task_start_activates_planning_workstream(self, client, initial_status):
|
||||
await _create_domain(client)
|
||||
topic = await _create_topic(client)
|
||||
ws = await _create_workstream(
|
||||
client,
|
||||
topic["id"],
|
||||
slug=f"{initial_status}-ws",
|
||||
status=initial_status,
|
||||
)
|
||||
task = await _create_task(client, ws["id"])
|
||||
|
||||
r = await client.patch(f"/tasks/{task['id']}", json={"status": "in_progress"})
|
||||
assert r.status_code == 200
|
||||
|
||||
r = await client.get(f"/workstreams/{ws['id']}")
|
||||
assert r.status_code == 200
|
||||
assert r.json()["status"] == "active"
|
||||
|
||||
async def test_task_start_does_not_unblock_blocked_workstream(self, client):
|
||||
await _create_domain(client)
|
||||
topic = await _create_topic(client)
|
||||
ws = await _create_workstream(client, topic["id"], slug="blocked-ws", status="blocked")
|
||||
task = await _create_task(client, ws["id"])
|
||||
|
||||
r = await client.patch(f"/tasks/{task['id']}", json={"status": "in_progress"})
|
||||
assert r.status_code == 200
|
||||
|
||||
r = await client.get(f"/workstreams/{ws['id']}")
|
||||
assert r.status_code == 200
|
||||
assert r.json()["status"] == "blocked"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Decision tests
|
||||
|
||||
@@ -104,7 +104,7 @@ Resolve these before T04/T05 can become live migration work:
|
||||
### T01 — Drill WSL2 State Hub backup restore
|
||||
|
||||
```task
|
||||
id: T01
|
||||
id: CUST-WP-0011-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "b0caf112-dc1d-43a8-9f27-d627dd4aa2bf"
|
||||
@@ -136,7 +136,7 @@ workstreams, 989 tasks, 1423 progress events, and 208 token events.
|
||||
### T02 — Align with Railiance deployment plan
|
||||
|
||||
```task
|
||||
id: T02
|
||||
id: CUST-WP-0011-T02
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "24887dd9-7d50-4cc4-add7-bffa1454b80c"
|
||||
@@ -167,7 +167,7 @@ deferred to `CUST-WP-0038`.
|
||||
### T03 — Build and publish State Hub container image
|
||||
|
||||
```task
|
||||
id: T03
|
||||
id: CUST-WP-0011-T03
|
||||
status: in_progress
|
||||
priority: high
|
||||
state_hub_task_id: "79908ade-3e38-451b-a403-2361a16a3f3a"
|
||||
@@ -216,7 +216,7 @@ pull the image from railiance01.
|
||||
### T04 — Define State Hub database and app manifests
|
||||
|
||||
```task
|
||||
id: T04
|
||||
id: CUST-WP-0011-T04
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "a7baf2eb-abd7-4aa3-b2cb-a5370ac09844"
|
||||
@@ -238,7 +238,7 @@ boundaries are documented.
|
||||
### T05 — Deploy empty State Hub and run migrations on railiance01
|
||||
|
||||
```task
|
||||
id: T05
|
||||
id: CUST-WP-0011-T05
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "a307dd46-a8e2-49df-b016-c187759ebcf1"
|
||||
@@ -261,7 +261,7 @@ Checks:
|
||||
### T06 — Restore WSL2 data copy into cluster and compare
|
||||
|
||||
```task
|
||||
id: T06
|
||||
id: CUST-WP-0011-T06
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "03753b88-824c-4448-97b2-f7315d145060"
|
||||
@@ -286,7 +286,7 @@ writer.
|
||||
### T07 — Cut over private access to cluster State Hub
|
||||
|
||||
```task
|
||||
id: T07
|
||||
id: CUST-WP-0011-T07
|
||||
status: todo
|
||||
priority: medium
|
||||
state_hub_task_id: "ff1de25e-c301-4b86-9420-84dfe72e565e"
|
||||
@@ -312,7 +312,7 @@ cluster State Hub, and WSL2 is no longer receiving normal writes.
|
||||
### T08 — Stabilise with WSL2 retained as fallback
|
||||
|
||||
```task
|
||||
id: T08
|
||||
id: CUST-WP-0011-T08
|
||||
status: todo
|
||||
priority: medium
|
||||
state_hub_task_id: "e06a59a0-5310-4c1c-9ba5-7cfaadda62e2"
|
||||
@@ -337,7 +337,7 @@ unresolved operational defects.
|
||||
### T09 — Document operating model and defer final WSL2 retirement
|
||||
|
||||
```task
|
||||
id: T09
|
||||
id: CUST-WP-0011-T09
|
||||
status: todo
|
||||
priority: low
|
||||
state_hub_task_id: "d75a2d49-f3b1-4bdd-b9e1-a1c6a9744681"
|
||||
|
||||
@@ -64,7 +64,7 @@ keeps the ultimate target visible and reviewable.
|
||||
### T01 — Confirm ThreePhoenix cluster readiness
|
||||
|
||||
```task
|
||||
id: T01
|
||||
id: CUST-WP-0038-T01
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "aa1bf291-3b6c-4940-a4f5-7680b0349110"
|
||||
@@ -85,7 +85,7 @@ NotReady or operationally unknown.
|
||||
### T02 — Establish replicated storage/database strategy
|
||||
|
||||
```task
|
||||
id: T02
|
||||
id: CUST-WP-0038-T02
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "5575f244-5cef-47aa-a168-24027cd08140"
|
||||
@@ -107,7 +107,7 @@ production data movement.
|
||||
### T03 — Implement HA State Hub database
|
||||
|
||||
```task
|
||||
id: T03
|
||||
id: CUST-WP-0038-T03
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "5330fcc3-684b-49f6-8d28-ea8c929733d6"
|
||||
@@ -130,7 +130,7 @@ namespace.
|
||||
### T04 — Add State Hub API high-availability behavior
|
||||
|
||||
```task
|
||||
id: T04
|
||||
id: CUST-WP-0038-T04
|
||||
status: todo
|
||||
priority: medium
|
||||
state_hub_task_id: "64175ed0-af36-47ea-9401-74c4b15ffe24"
|
||||
@@ -150,7 +150,7 @@ Run State Hub API with the right availability posture for its workload:
|
||||
### T05 — Drill database failover
|
||||
|
||||
```task
|
||||
id: T05
|
||||
id: CUST-WP-0038-T05
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "73c5008a-380e-42bf-ad57-1c9d0bda3a86"
|
||||
@@ -172,7 +172,7 @@ Checks:
|
||||
### T06 — Drill backup restore to isolated namespace
|
||||
|
||||
```task
|
||||
id: T06
|
||||
id: CUST-WP-0038-T06
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "4e5b97ff-ef1c-414d-812b-39b87b242c74"
|
||||
@@ -196,7 +196,7 @@ Checks:
|
||||
### T07 — Update agent access and runbooks for HA endpoint
|
||||
|
||||
```task
|
||||
id: T07
|
||||
id: CUST-WP-0038-T07
|
||||
status: todo
|
||||
priority: medium
|
||||
state_hub_task_id: "959062d8-decb-4969-a60b-0d3b618a8d6c"
|
||||
@@ -217,7 +217,7 @@ through the documented path.
|
||||
### T08 — Retire WSL2 fallback after explicit approval
|
||||
|
||||
```task
|
||||
id: T08
|
||||
id: CUST-WP-0038-T08
|
||||
status: todo
|
||||
priority: low
|
||||
needs_human: true
|
||||
|
||||
@@ -4,7 +4,7 @@ type: workplan
|
||||
title: "Workplan ID Normalization and Legacy Cleanup"
|
||||
domain: custodian
|
||||
repo: state-hub
|
||||
status: proposed
|
||||
status: finished
|
||||
owner: codex
|
||||
topic_slug: custodian
|
||||
planning_priority: high
|
||||
@@ -35,11 +35,19 @@ The cleanup should prefer deterministic renames and mechanical reference
|
||||
updates, but it must not break existing `state_hub_workstream_id` or
|
||||
`state_hub_task_id` links.
|
||||
|
||||
## Legacy Policy
|
||||
|
||||
Canonical State Hub-local workplans use `STATE-WP-*`. Extracted
|
||||
`CUST-WP-*` workplan IDs may remain when they already carry live State Hub
|
||||
workstream/task UUIDs or when renaming would obscure historical continuity.
|
||||
Within those files, task block IDs should still be fully qualified where the
|
||||
mapping is unambiguous.
|
||||
|
||||
## T01 - Inventory ID And Prefix Drift
|
||||
|
||||
```task
|
||||
id: STATE-WP-0046-T01
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "e966289f-bb27-4da0-b00c-2bba2751fad7"
|
||||
```
|
||||
@@ -51,11 +59,16 @@ docs/tests/scripts.
|
||||
Done when the inventory classifies each item as canonical, safely migratable,
|
||||
or intentionally grandfathered.
|
||||
|
||||
Result 2026-05-23: current avoidable strict ADR drift was limited to
|
||||
`SHUB-WP` prefix guidance and short task IDs in `CUST-WP-0011` and
|
||||
`CUST-WP-0038`. Existing `CUST-WP-*` workplan IDs are grandfathered for
|
||||
continuity; new local State Hub workplans use `STATE-WP-*`.
|
||||
|
||||
## T02 - Decide Canonical Prefix Policy
|
||||
|
||||
```task
|
||||
id: STATE-WP-0046-T02
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "ee965266-4578-4ad1-b5b7-481e243a67dc"
|
||||
```
|
||||
@@ -66,11 +79,15 @@ guidance so new workplans consistently use `STATE-WP-*`.
|
||||
Done when `AGENTS.md`, `SCOPE.md`, `README.md`, templates, and docs no longer
|
||||
disagree on `STATE-WP-*` versus `SHUB-WP-*`.
|
||||
|
||||
Result 2026-05-23: `STATE-WP-*` is the canonical State Hub-local prefix.
|
||||
Updated `SCOPE.md`, `README.md`, and `docs/workplan-convention.md` to remove
|
||||
the older `SHUB-WP-*` guidance.
|
||||
|
||||
## T03 - Normalize Safe Workplan IDs And Filenames
|
||||
|
||||
```task
|
||||
id: STATE-WP-0046-T03
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "fd753850-93ba-4daa-9664-466f07097af5"
|
||||
```
|
||||
@@ -83,11 +100,16 @@ text.
|
||||
Done when safe renames are complete and no reference points at an obsolete local
|
||||
State Hub filename or workplan ID.
|
||||
|
||||
Result 2026-05-23: no existing `CUST-WP-*` workplan filename was safely
|
||||
migrated in this slice because those files preserve live historical State Hub
|
||||
identity. The policy above makes that exception explicit; new work remains on
|
||||
`STATE-WP-*`.
|
||||
|
||||
## T04 - Normalize Safe Task IDs
|
||||
|
||||
```task
|
||||
id: STATE-WP-0046-T04
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "6d60cd2c-0b0c-41e8-a957-b04a5fe0b31e"
|
||||
```
|
||||
@@ -99,11 +121,15 @@ Replace short extracted task IDs such as `T01` with full canonical IDs such as
|
||||
Done when strict ADR validation has no task-ID-format warnings for workplans
|
||||
that were safe to normalize.
|
||||
|
||||
Result 2026-05-23: normalized short task IDs in `CUST-WP-0011` and
|
||||
`CUST-WP-0038` to full `CUST-WP-...-TNN` IDs while preserving each
|
||||
`state_hub_task_id`.
|
||||
|
||||
## T05 - Encode Legacy Exceptions
|
||||
|
||||
```task
|
||||
id: STATE-WP-0046-T05
|
||||
status: todo
|
||||
status: done
|
||||
priority: medium
|
||||
state_hub_task_id: "a5a77bbe-4aa5-4e9a-9387-400a0093ed39"
|
||||
```
|
||||
@@ -115,11 +141,14 @@ and not fresh neglect.
|
||||
Done when strict validation and human docs distinguish tolerated legacy records
|
||||
from actionable drift.
|
||||
|
||||
Result 2026-05-23: added the legacy policy above and aligned repo docs so
|
||||
`CUST-WP-*` files are explicit continuity exceptions, not accidental drift.
|
||||
|
||||
## T06 - Verify Consistency And ADR Compliance
|
||||
|
||||
```task
|
||||
id: STATE-WP-0046-T06
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "69a3255c-1cc9-47d0-9947-350a443173d8"
|
||||
```
|
||||
@@ -130,6 +159,10 @@ migration. Confirm the DB still points at the intended workstreams and tasks.
|
||||
Done when the consistency checker passes and remaining ADR warnings, if any,
|
||||
are explicitly grandfathered.
|
||||
|
||||
Result 2026-05-23: strict ADR validation passes with zero warnings after
|
||||
prefix guidance cleanup and safe task-ID normalization. Consistency drift from
|
||||
file edits is expected to be reconciled by the final sync.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- New State Hub-local workplans consistently use `STATE-WP-*`.
|
||||
|
||||
@@ -4,7 +4,7 @@ type: workplan
|
||||
title: "Lifecycle Assertions and Renormalization"
|
||||
domain: custodian
|
||||
repo: state-hub
|
||||
status: proposed
|
||||
status: active
|
||||
owner: codex
|
||||
topic_slug: custodian
|
||||
planning_priority: high
|
||||
@@ -32,11 +32,21 @@ state changes themselves do not consistently use a shared transition layer.
|
||||
|
||||
This workplan turns the vocabulary into executable rules and repair scaffolding.
|
||||
|
||||
## Lifecycle Invariants
|
||||
|
||||
| Invariant | Classification | Repair Path |
|
||||
|-----------|----------------|-------------|
|
||||
| A task moving from `todo` to `in_progress` activates a parent workstream in `proposed`, `ready`, or `backlog`. | automatic repair | Set parent workstream status to `active` in the same transaction. |
|
||||
| A `blocked` parent workstream is not automatically unblocked by task start. | hard guard | Keep `blocked`; require explicit unblock transition or dependency/decision repair. |
|
||||
| `finished` and `archived` workstreams should not have open tasks unless explicitly grandfathered. | warning, then repair | Report via consistency tooling; close/cancel stale tasks or reopen parent with intent. |
|
||||
| `needs_review` and `stalled` remain derived health labels. | hard vocabulary guard | Do not write them to workplan frontmatter or `workstreams.status`. |
|
||||
| Archived workplan files must have closed lifecycle states. | hard consistency error | Move file back to active workplans or close the lifecycle state. |
|
||||
|
||||
## T01 - Define Lifecycle Invariants
|
||||
|
||||
```task
|
||||
id: STATE-WP-0047-T01
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "28f28391-646c-4871-ae84-a1c1aae3f5bf"
|
||||
```
|
||||
@@ -50,11 +60,15 @@ derived labels.
|
||||
Done when the invariant table is documented and each rule is classified as
|
||||
hard error, warning, automatic repair, or human-review item.
|
||||
|
||||
Result 2026-05-23: added the initial invariant table above. The first automatic
|
||||
repair implemented in this slice is parent activation when real task work
|
||||
starts.
|
||||
|
||||
## T02 - Implement Shared Transition Helpers
|
||||
|
||||
```task
|
||||
id: STATE-WP-0047-T02
|
||||
status: todo
|
||||
status: in_progress
|
||||
priority: high
|
||||
state_hub_task_id: "56d9b6b9-fba1-4997-bdd5-875187cafa2d"
|
||||
```
|
||||
@@ -66,11 +80,15 @@ entry and exit assertions, and return concise repair/action results.
|
||||
Done when API routes, consistency tooling, and future UI actions can call one
|
||||
shared transition layer for lifecycle changes.
|
||||
|
||||
Progress 2026-05-23: added `api.services.lifecycle` with shared status
|
||||
normalization and parent-activation helpers. The task API now uses the helper;
|
||||
consistency tooling and future UI actions still need to adopt the shared layer.
|
||||
|
||||
## T03 - Auto-Advance Workstream On Task Start
|
||||
|
||||
```task
|
||||
id: STATE-WP-0047-T03
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "b0937fed-bd61-4f27-9586-8cebc6168827"
|
||||
```
|
||||
@@ -82,6 +100,10 @@ guard blocks the transition.
|
||||
Done when starting real task work cannot leave the parent workstream parked in
|
||||
planning states.
|
||||
|
||||
Result 2026-05-23: task creation or update to `in_progress` activates a parent
|
||||
workstream from `proposed`, `ready`, or `backlog`, while leaving `blocked`
|
||||
parents blocked.
|
||||
|
||||
## T04 - Harden Flow Advancement Semantics
|
||||
|
||||
```task
|
||||
@@ -132,7 +154,7 @@ pattern instead of relying on ad hoc fixes.
|
||||
|
||||
```task
|
||||
id: STATE-WP-0047-T07
|
||||
status: todo
|
||||
status: in_progress
|
||||
priority: high
|
||||
state_hub_task_id: "def5ce49-1938-4c45-807d-78ac15c995cb"
|
||||
```
|
||||
@@ -142,6 +164,11 @@ enforcement, and consistency repairs.
|
||||
|
||||
Done when lifecycle drift is hard to reintroduce accidentally.
|
||||
|
||||
Progress 2026-05-23: added router regression tests for task-start activation
|
||||
from `proposed`, `ready`, and `backlog`, plus a guard test proving `blocked`
|
||||
parents stay blocked. Remaining coverage still needs flow assertion hardening
|
||||
and consistency repair tests.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- Starting task work deterministically activates the parent workstream.
|
||||
|
||||
Reference in New Issue
Block a user