feat(terminology): prose sweep tool and custodian workplan cleanup (CUST-WP-0055 T04)
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 6s

Add sweep_workstream_prose.py for agent-guidance files, sweep active workplan
prose in-repo, tighten scan allowlist exclusions, and update ADR-001 closure
protocol to workplan-first terminology.
This commit is contained in:
codex 2026-07-08 16:35:37 +02:00
parent 3bdefb3c4a
commit 2e0deee2ef
42 changed files with 312 additions and 186 deletions

View file

@ -46,7 +46,7 @@ fix-consistency. Do not edit or rename it.
**Workplan** is the fleet term — see
`the-custodian/canon/standards/workplan-terminology-fleet_v0.1.md`.
**Workstream** is legacy only: some API routes (`/workstreams/`), params
**Workplan** is legacy only: some API routes (`/workstreams/`), params
(`workstream_id`), MCP aliases (`create_workstream`), and the frontmatter field
above remain until `STATE-WP-0069` retires them via legacy-meter. Treat those
identifiers as workplan IDs. Prefer `GET /workplans/` and `workplan_id` in new

View file

@ -83,7 +83,7 @@ custodian-key-deploy:
## USER= SSH user → SANDBOXER_SSH_USER
## KEY= SSH key → SANDBOXER_SSH_KEY (default: custodian key if present)
## KEEP=1 keep sandbox after run
## WORKSTREAM_ID= State Hub workstream for progress event
## WORKSTREAM_ID= State Hub workplan UUID for progress event (legacy env name)
## NO_REPORT=1 skip State Hub reporting
REPO_PATH := $(HOME)/$(REPO)

View file

@ -124,7 +124,7 @@ their own repositories and are referenced here only as integration pointers.
## Terminology
- Preferred terms: canon, workstream, workplan, topic, progress event, domain
- Preferred terms: canon, workplan, workplan, topic, progress event, domain
- Also known as: "the hub" (loosely) — but the *service* is the State Hub repo;
this repo is the governance substrate
- Potentially confusing terms:

View file

@ -14,7 +14,7 @@ This agent is now primarily a routing shim:
- inspect the target repository enough to decide whether a new SCOPE.md is
needed or an existing one should be refreshed;
- when tied to a State Hub workstream or task, prefer `get_flow_state()` for
- when tied to a State Hub workplan or task, prefer `get_flow_state()` for
lifecycle orientation and `advance_workstation()` for flow-aware movement
after requisite assertions are satisfied;
- prefer `scope.generate` when no SCOPE.md exists or the file is only a stub;
@ -36,7 +36,7 @@ request_capability(
capability_type="api",
requesting_agent="scope-analyst",
requesting_domain="custodian",
requesting_workstream_id="<workstream-uuid>",
requesting_workstream_id="<workplan-uuid>",
priority="medium",
)
```

View file

@ -174,17 +174,17 @@ and others as "file-authoritative."
Rejected: introduces ambiguity about which records matter; violates the
"single source of truth" principle.
## Workstream Closure Protocol
## Workplan Closure Protocol
When a workstream is about to be marked `completed`, the responsible agent
When a workplan is about to be marked `finished`, the responsible agent
MUST perform a closure review before writing the status change. This prevents
the stale-task accumulation that this ADR was designed to make detectable.
### Steps
1. **Query all non-done tasks** in the workstream via
`GET /tasks/?workstream_id=<uuid>` (filter for `todo`, `in_progress`,
`blocked`).
1. **Query all non-done tasks** in the workplan via
`GET /tasks/?workplan_id=<uuid>` (legacy alias: `workstream_id`; filter for
`todo`, `in_progress`, `blocked`).
2. **Classify each task** into one of three outcomes:
@ -215,13 +215,13 @@ the stale-task accumulation that this ADR was designed to make detectable.
| TASK-ID — title | CUST-WP-XXXX |
```
4. **If any tasks are carried forward**: do not mark the workstream
`completed` yet. Create the new workplan file (or amend an existing active
one), then close the current workstream.
4. **If any tasks are carried forward**: do not mark the workplan
`finished` yet. Create the new workplan file (or amend an existing active
one), then close the current workplan.
5. **Update the workplan frontmatter** `status: completed` and `updated:` date.
5. **Update the workplan frontmatter** `status: finished` and `updated:` date.
6. **Mark the workstream `completed`** in the state hub via MCP or API.
6. **Mark the workplan `finished`** in the state hub via MCP or API (`update_workplan_status`).
### Daily Stale-Task Cleanup

View file

@ -129,7 +129,7 @@ Bootstrap operations inherit `custodian_constitution_v0.1.md`:
- Agent actions must be auditable and reversible within 5 minutes where they
affect production infrastructure.
- Runway below 3 months triggers mandatory Financial Allocator review and
dev-hub workstream deprioritization signal (fin→dev coupling, T25).
dev-hub workplan deprioritization signal (fin→dev coupling, T25).
## 6. Success Criteria for Bootstrap Exit

View file

@ -408,7 +408,7 @@ The Canon Hub MUST remain sparse, stable, and deliberately slower-moving than op
### Purpose
The Dev Hub coordinates software design and implementation work across repositories, workstreams, and coding agents.
The Dev Hub coordinates software design and implementation work across repositories, workplans, and coding agents.
It answers:
@ -429,7 +429,7 @@ It answers:
### Typical Derived Views
* workstream summaries
* workplan summaries
* blocker maps
* dependency graphs
* capability catalogs
@ -778,7 +778,7 @@ Beyond the shared contract, each hub SHOULD expose domain-specific functions.
Examples:
* Dev Hub: workstreams, capabilities, decisions
* Dev Hub: workplans, capabilities, decisions
* Ops Hub: services, incidents, runbooks, access paths
* Sec Hub: controls, exposures, exceptions
* Fin Hub: budgets, commitments, runway

View file

@ -97,9 +97,9 @@ in the relevant row and continue from its task state.
## Registration State
State Hub active workstreams queried on 2026-06-27:
State Hub active workplans queried on 2026-06-27:
| Workstream | Current pickup meaning |
| Workplan | Current pickup meaning |
| --- | --- |
| `artifact-store-wp-0007` | Start D7.1/D7.2 assessment and compatibility harness; D7.3 STS vending may route to NetKingdom. |
| `ihub-wp-0022` | Ops-hub evidence intake contract is aligned to live vocabulary; runtime key custody, protected widget lookup, and smoke remain. |
@ -119,7 +119,7 @@ State Hub active workstreams queried on 2026-06-27:
Hygiene status:
- `CUST-WP-0045-cutover-runbook` is no longer active; it is a finished runbook
record, not an empty active workstream.
record, not an empty active workplan.
- `CUST-WP-0014` is reopened as `backlog`; it is no longer a done workplan with
todo task blocks.
- Completed or cancelled tasks no longer carry the stale human-needed flags
@ -127,7 +127,7 @@ Hygiene status:
- `make fix-consistency REPO=the-custodian` still reports pre-existing C-12
orphan-row warnings, but the relevant workplan lifecycle and task states sync.
- `RAIL-BS-WP-0006-staged-promotion-lifecycle` is finished: all seven tasks
are done, the workstream is finished in State Hub, and the file frontmatter
are done, the workplan is finished in State Hub, and the file frontmatter
is `status: finished`.
## Blocker Board

View file

@ -40,9 +40,9 @@ def main() -> None:
help="Keep sandbox after run",
)
parser.add_argument(
"--workstream-id",
"--workplan-id",
default=None,
help="State Hub workstream ID for progress event",
help="State Hub workplan ID for progress event",
)
parser.add_argument(
"--no-report",

View file

@ -51,12 +51,12 @@ available fallback and say so in the report.
3. `GET http://127.0.0.1:8000/messages/?to_agent=hub&unread_only=true`
4. `GET http://127.0.0.1:8000/state/next_steps`
5. `GET http://127.0.0.1:8000/workstreams/workplan-index`
6. Local workplan files for candidate workstreams that need closer inspection
6. Local workplan files for candidate workplans that need closer inspection
7. Read-only `git status --short` for repos named in top recommendations
Optional enrichment:
- `GET /tasks/?workstream_id=<id>` for a top-ranked workstream
- `GET /tasks/?workstream_id=<id>` for a top-ranked workplan
- `GET /progress/?workstream_id=<id>&limit=5` for staleness confidence
- State Hub domain summaries through MCP when available
@ -64,11 +64,11 @@ Optional enrichment:
Build the candidate list from:
- all open workstreams in `state_summary.open_workstreams`
- all open workplans in `state_summary.open_workstreams`
- all derived `state_summary.next_steps`
- blocked tasks and blocking decisions from the summary
- high-priority file-backed workplans surfaced by `workplan-index`
- workstreams with suspicious structure, such as zero parsed tasks or stale
- workplans with suspicious structure, such as zero parsed tasks or stale
active plans
Keep the scored table compact. Score at most 15 candidates internally and
@ -83,9 +83,9 @@ Flag a candidate when one or more signals apply:
| Active with no recent progress | no progress in 14 days, or 7 days for high-priority items | `revisit` |
| Large unstarted plan | `tasks_total >= 6`, `tasks_done == 0`, `tasks_in_progress == 0` | `split` or `revisit` |
| Near complete | `tasks_done / tasks_total >= 0.75` and `tasks_todo <= 3` | `close-out` or `work-next` |
| Zero parsed tasks | active or blocked workstream with `tasks_total == 0` | `needs-consistency-sync` |
| Blocked but dependency appears closed | blocked workstream with cleared dependency or empty blocked reason | `revisit` |
| Open decision blocks execution | blocking decision connected to workstream/task | `needs-human` |
| Zero parsed tasks | active or blocked workplan with `tasks_total == 0` | `needs-consistency-sync` |
| Blocked but dependency appears closed | blocked workplan with cleared dependency or empty blocked reason | `revisit` |
| Open decision blocks execution | blocking decision connected to workplan/task | `needs-human` |
| File-backed mismatch | missing index entry, stale `updated`, `needs_review`, or status disagreement | `needs-consistency-sync` |
| Too broad for one session | many unrelated task clusters in one workplan | `split` |
| Safety-sensitive | security, identity, secrets, legal, external reputation, money | `needs-human` |
@ -110,7 +110,7 @@ Score higher when the work aligns with:
- dependency-chain foundations: Railiance, identity, secrets, backup, HA
- explicit `planning_priority: high` or low `planning_order`
- active repo/domain goals
- closing a workstream that is already consuming coordination attention
- closing a workplan that is already consuming coordination attention
### Time Criticality
@ -134,7 +134,7 @@ Score higher when the work reduces:
Score higher when the work unlocks:
- multiple downstream workstreams
- multiple downstream workplans
- agent autonomy or fewer repeated setup steps
- reliable cross-repo operation
- future daily triage quality
@ -191,7 +191,7 @@ source: daily-state-hub-wsjf-triage
## Snapshot
- Generated at: <timestamp>
- Workstreams: <active> active, <blocked> blocked, <finished> finished
- Workplans: <active> active, <blocked> blocked, <finished> finished
- Tasks: <todo> todo, <in_progress> in progress, <blocked> blocked
- Decisions: <open> open, <escalated> escalated
- Inbox: <count> unread hub messages
@ -201,7 +201,7 @@ source: daily-state-hub-wsjf-triage
| Rank | Action | Candidate | WSJF | Confidence | Why now |
|------|--------|-----------|------|------------|---------|
| 1 | work-next | <workstream/task> | 0.0 | high | <short reason> |
| 1 | work-next | <workplan/task> | 0.0 | high | <short reason> |
## Loose Ends

View file

@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Cancel duplicate CUST-WP-0054 hub tasks created during 2026-07-07 workstream recreation.
# Cancel duplicate CUST-WP-0054 hub tasks created during 2026-07-07 workplan recreation.
set -euo pipefail
API_BASE="${API_BASE:-http://127.0.0.1:8000}"
@ -21,7 +21,7 @@ for id in "${DUPLICATES[@]}"; do
http=$(curl -sS -o /tmp/task-patch.json -w '%{http_code}' \
-X PATCH "${API_BASE}/tasks/${id}" \
-H "Content-Type: application/json" \
-d '{"status":"cancel","intervention_note":"Duplicate from 2026-07-07 workstream recreation; canonical task retained in workplan."}')
-d '{"status":"cancel","intervention_note":"Duplicate from 2026-07-07 workplan recreation; canonical task retained in workplan."}')
if [[ "$http" == "200" ]]; then
echo "CANCEL ${id}"
elif [[ "$http" == "404" ]]; then

View file

@ -7,6 +7,8 @@ global:
path_prefixes:
- workplans/archived/
- memory/
- agents_backup_
- agent-tools/
per_repo:
the-custodian:
@ -16,6 +18,7 @@ per_repo:
- tools/scan_workstream_allowlist.yaml
- docs/evidence/workstream-terminology-baseline-20260708.json
- workplans/CUST-WP-0055-workplan-terminology-fleet-refactor.md
- .custodian-brief.md
- workplans/CUST-WP-0010-workstream-lifecycle-docs.md
- wiki/
- roadmap/

View file

@ -0,0 +1,123 @@
#!/usr/bin/env python3
"""Mechanical workstream→workplan prose sweep for agent guidance (CUST-WP-0055 T04).
Usage:
python tools/sweep_workstream_prose.py --repo ops-warden
python tools/sweep_workstream_prose.py --repo ops-warden --repo hub-core --dry-run
"""
from __future__ import annotations
import argparse
import re
from pathlib import Path
HOME = Path("/home/worsch")
TARGET_NAMES = {"AGENTS.md", "SCOPE.md", "INTENT.md", "README.md", "CLAUDE.md"}
TARGET_RULES = ".claude/rules"
PRESERVE = (
"state_hub_workstream_id",
"workstream_id",
"workstream_slug",
"workstream_title",
"workstream_status",
"open_workstreams",
"create_workstream",
"update_workstream",
"list_workstreams",
"update_workstream_status",
"/workstreams",
"workstreams/",
"workstream-dod",
"workstream-health",
"workstream-lifecycle",
"workstream-kpi",
"related_workstream",
"by_workstream",
)
REPLACEMENTS = (
(re.compile(r"\bWorkstreams\b"), "Workplans"),
(re.compile(r"\bworkstreams\b"), "workplans"),
(re.compile(r"\bWorkstream\b"), "Workplan"),
(re.compile(r"\bworkstream\b"), "workplan"),
)
def _protect(text: str) -> tuple[str, dict[str, str]]:
tokens: dict[str, str] = {}
def repl(match: re.Match[str]) -> str:
key = f"__KEEP_{len(tokens)}__"
tokens[key] = match.group(0)
return key
for term in sorted(PRESERVE, key=len, reverse=True):
text = re.sub(re.escape(term), repl, text)
return text, tokens
def _restore(text: str, tokens: dict[str, str]) -> str:
for key, value in tokens.items():
text = text.replace(key, value)
return text
def sweep_text(text: str) -> str:
protected, tokens = _protect(text)
updated = protected
for pattern, replacement in REPLACEMENTS:
updated = pattern.sub(replacement, updated)
return _restore(updated, tokens)
def target_files(repo_root: Path) -> list[Path]:
paths: list[Path] = []
for name in TARGET_NAMES:
path = repo_root / name
if path.is_file():
paths.append(path)
rules_dir = repo_root / TARGET_RULES
if rules_dir.is_dir():
paths.extend(sorted(rules_dir.glob("*.md")))
return paths
def sweep_repo(repo_root: Path, *, dry_run: bool) -> list[str]:
changed: list[str] = []
for path in target_files(repo_root):
original = path.read_text(encoding="utf-8")
updated = sweep_text(original)
if updated != original:
changed.append(str(path.relative_to(repo_root)))
if not dry_run:
path.write_text(updated, encoding="utf-8")
return changed
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo", action="append", required=True, help="Repo slug under ~/")
parser.add_argument("--dry-run", action="store_true")
args = parser.parse_args()
exit_code = 0
for slug in args.repo:
root = HOME / slug
if not root.is_dir():
print(f"SKIP missing checkout: {slug}")
exit_code = 1
continue
changed = sweep_repo(root, dry_run=args.dry_run)
if changed:
mode = "would update" if args.dry_run else "updated"
print(f"{slug}: {mode} {len(changed)} file(s)")
for rel in changed:
print(f" {rel}")
else:
print(f"{slug}: no changes")
return exit_code
if __name__ == "__main__":
raise SystemExit(main())

View file

@ -17,7 +17,7 @@ state_hub_workstream_id: "3f54ce9c-1f95-42de-894f-0f81a52ba2e8"
Small same-day improvements that are useful to track, but do not justify a
dedicated requirement/workplan cycle.
## Overview workstreams by repository
## Overview workplans by repository
```task
id: ADHOC-2026-05-02-T01
@ -26,6 +26,6 @@ priority: medium
state_hub_task_id: "e9a302b7-81ab-4643-a256-4565a8c753e0"
```
Changed the State Hub Overview workstream chart from domain-first grouping to
Changed the State Hub Overview workplan chart from domain-first grouping to
domain/repository grouping, added workplan filename detail to chart hover data,
and made workstream bars open the workstream drilldown with attached tasks.
and made workplan bars open the workplan drilldown with attached tasks.

View file

@ -12,7 +12,7 @@ updated: "2026-02-24"
completed: "2026-02-24"
state_hub_workstream_id: "2b0efa54-0209-4ca9-8ab3-30dfbdb991b0"
note: >
Pre-ADR-001 record. This workstream was created DB-first during the first
Pre-ADR-001 record. This workplan was created DB-first during the first
Custodian session (2026-02-24) before the workplan-as-repository-artefact
convention was established. This file is a retroactive record written on
2026-03-11 to satisfy the ADR-001 consistency checker (C-08).---
@ -24,10 +24,10 @@ note: >
The first live implementation layer of the Custodian system, delivered in the
initial session on 2026-02-24:
- PostgreSQL schema (topics, workstreams, tasks, decisions, progress_events)
- PostgreSQL schema (topics, workplans, tasks, decisions, progress_events)
- FastAPI app with routers for all entities + `/state/summary`
- FastMCP stdio server (11 tools, 5 resources/templates)
- Observable Framework dashboard (4 pages: index, workstreams, decisions, progress)
- Observable Framework dashboard (4 pages: index, workplans, decisions, progress)
- Docker Compose for local PostgreSQL
- Alembic migration `0001_initial_schema`
- Seed script inserting 6 canonical topics

View file

@ -12,7 +12,7 @@ updated: "2026-02-25"
completed: "2026-02-25"
state_hub_workstream_id: "6585ee66-aa4e-436e-bbec-d83293c33e8f"
note: >
Pre-ADR-001 record. This workstream was created DB-first before the
Pre-ADR-001 record. This workplan was created DB-first before the
workplan-as-repository-artefact convention was established. Retroactive
file written on 2026-03-11 to satisfy the ADR-001 consistency checker (C-08).---
@ -24,11 +24,11 @@ Delivered 2026-02-25, evolving the hub from a state tracker to an active
coordination layer:
- `WorkstreamDependency` model + migration `0b547c153153` — directed
dependency graph between workstreams
dependency graph between workplans
- API: `POST/GET /workstreams/{id}/dependencies/`,
`DELETE /workstreams/{id}/dependencies/{dep_id}`
- API: `GET /state/next_steps` — derived next-action suggestions (never persisted)
- `StateSummary` extended with `next_steps` and `depends_on`/`blocks` on workstreams
- `StateSummary` extended with `next_steps` and `depends_on`/`blocks` on workplans
- Design boundary formalised: hub is a read model with exactly two write use
cases — resolving decisions and suggesting next steps
- MCP: `get_next_steps()` tool added

View file

@ -36,7 +36,7 @@ Both dependencies resolved and removed from the dependency graph (2026-03-11):
Railiance01 operational with k3s + HA failover tested + backup verified
(RAIL-BS-WP-0002, RAIL-BS-WP-0003, RAIL-BS-WP-0004 all completed).
**This workstream is unblocked. T2 is ready to execute.**
**This workplan is unblocked. T2 is ready to execute.**
## Tasks

View file

@ -93,7 +93,7 @@ status: done
priority: low
```
The staged-promotion-lifecycle workstream defines EP-RAIL-NNN IDs and
The staged-promotion-lifecycle workplan defines EP-RAIL-NNN IDs and
inline doc markers. Review and update so the Railiance convention is a
proper instance of the custodian master spec, not a parallel one.

View file

@ -142,7 +142,7 @@ priority: high
```
Add `Pydantic Field(le=1000)` to `limit` parameters on all list
endpoints (progress, tasks, workstreams, decisions, extension_points,
endpoints (progress, tasks, workplans, decisions, extension_points,
technical_debt) to prevent memory exhaustion.
### P3.3 — Extract shared dashboard CSS to common stylesheet

View file

@ -104,7 +104,7 @@ New file: `state-hub/api/routers/domains.py`
Endpoints:
- `GET /domains/` — list all (filter: ?status=active|archived|all)
- `GET /domains/{slug}/` — get by slug with counts (topics, workstreams, EPs, TDs, repos)
- `GET /domains/{slug}/` — get by slug with counts (topics, workplans, EPs, TDs, repos)
- `POST /domains/` — create new (slug: unique, lowercase, underscored)
- `PATCH /domains/{slug}/rename` — update slug + name; cascade EP/TD string columns
- `PATCH /domains/{slug}/archive` — soft-delete; 409 if active topics exist
@ -152,7 +152,7 @@ topic_id UUID FK topics(id) NULLABLE, created_at, updated_at.
Design: `domain_id` is the primary association (not `topic_id`) — a domain
can have multiple repos without a topic for each. `topic_id` is optional,
for workstream-level linkage.
for workplan-level linkage.
Coordinate with v0.3 P2.2: if v0.3 runs first, extend rather than replace.
Add to `models/__init__.py` and register router.
@ -264,7 +264,7 @@ New file: `state-hub/dashboard/src/domains.md`
Layout:
- KPI row: total domains, total repos, newest domain.
- Domain cards: name, slug, status badge, repo count, workstream count,
- Domain cards: name, slug, status badge, repo count, workplan count,
EP count, TD count.
- Repos sub-list per card: name, local_path, remote_url (linked), date.
- Click → entity modal (extend `entity-modal.js` for domain type).
@ -275,7 +275,7 @@ Data loaders:
Add to `observablehq.config.js` nav.
### P4.2 — Dashboard: domain filter on workstreams, EP, TD pages
### P4.2 — Dashboard: domain filter on workplans, EP, TD pages
```task
id: CUST-WP-0005-T11
@ -285,7 +285,7 @@ priority: low
```
Add `Inputs.select` domain filter to:
- `workstreams.md` — filter by domain via topic.domain_slug
- `workplans.md` — filter by domain via topic.domain_slug
- `extensions.md` — wire existing API domain filter to UI
- `techdept.md` — wire existing API domain filter to UI
@ -325,7 +325,7 @@ Check if `WorkstreamRead` already exposes `domain_slug`; add if missing.
- CUST-WP-0005-T08 — MCP tools: domain lifecycle + repo registration
- CUST-WP-0005-T09 — Live domain validation for EP/TD + domain stats in state summary
- CUST-WP-0005-T10 — Dashboard: domains.md page
- CUST-WP-0005-T11 — Dashboard: domain filter on workstreams, EP, TD pages
- CUST-WP-0005-T11 — Dashboard: domain filter on workplans, EP, TD pages
### Cancelled (DB records only — legacy stale rows, not real cancellations)

View file

@ -52,14 +52,14 @@ See companion document. Summary:
| `Domain` | Complex | Ecosystem (implicit root) | 6 canonical domains |
| `Topic` | Complex | Domain | Focus area / active project |
| `ManagedRepo` | Complex | Domain | Managed git repo |
| `Workstream` | Complex | **Repository** (currently Topic) | Work package — ADR-001 mismatch |
| `Workplan` | Complex | **Repository** (currently Topic) | Work package — ADR-001 mismatch |
| `SBOMSnapshot` | Complex | Repository | Does not yet exist as an entity |
| `Task` | Atom | Workstream | ✓ correct |
| `Decision` | Atom | Repository (currently Topic or Workstream) | Dual-attach ambiguity |
| `Task` | Atom | Workplan | ✓ correct |
| `Decision` | Atom | Repository (currently Topic or Workplan) | Dual-attach ambiguity |
| `TechnicalDebt` | Atom | Repository (currently domain: str) | String FK inconsistency |
| `ExtensionPoint` | Atom | Repository (currently domain: str) | String FK inconsistency |
| `Contribution` | Atom | Repository (no domain FK) | No domain affiliation |
| `ProgressEvent` | Atom | Workstream (or Topic) | Multi-attach ambiguity |
| `ProgressEvent` | Atom | Workplan (or Topic) | Multi-attach ambiguity |
| `SBOMEntry` | Atom | SBOMSnapshot (currently ManagedRepo) | No container |
| `WorkstreamDependency` | Relation | Domain | Flat junction table |
@ -83,12 +83,12 @@ to `domains.id`. The rename_domain API patches these manually via string updates
there is no referential integrity. Dashboard filtering silently returns empty results
when slugs drift.
**I-2 — Workstream primary container is Topic, not Repository (critical severity)**
GEMS §7 places `Workstream.primary = Repository`. ADR-001 states that workplans
(the file backing a workstream) must originate in a repository. However, the current
schema has `Workstream.topic_id NOT NULL` — Topic is the enforced primary container.
**I-2 — Workplan primary container is Topic, not Repository (critical severity)**
GEMS §7 places `Workplan.primary = Repository`. ADR-001 states that workplans
(the file backing a workplan) must originate in a repository. However, the current
schema has `Workplan.topic_id NOT NULL` — Topic is the enforced primary container.
This is an ADR-001 violation embedded in the schema itself. There is currently no
`repo_id` on Workstream.
`repo_id` on Workplan.
**I-3 — Decision dual attachment without clear hierarchy (medium severity)**
`Decision` has both `topic_id` and `workstream_id` FKs, with a CHECK constraint
@ -137,7 +137,7 @@ state_hub_task_id: "da639706-3b14-42b8-92de-b9de84dbb2be"
Six decisions were escalated (see state-hub records):
- DEC-GEMS-001: GEMS implementation architecture (typed tables vs. generic entity model)
- DEC-GEMS-002: Workstream primary container — Topic vs. Repository
- DEC-GEMS-002: Workplan primary container — Topic vs. Repository
- DEC-GEMS-003: Domain string → FK migration for ExtensionPoint and TechnicalDebt
- DEC-GEMS-004: SBOMSnapshot container entity
- DEC-GEMS-005: Ecosystem root entity
@ -164,7 +164,7 @@ See companion document for the full analysis. Summary verdict:
The migration is **worth pursuing incrementally** (Pattern C from GEMS §9). The most
impactful and least risky first move is:
1. Fix I-1: migrate domain string → FK on EP/TD (low risk, high consistency gain)
2. Fix I-2: add `repo_id` to Workstream (medium risk, fixes ADR-001 alignment)
2. Fix I-2: add `repo_id` to Workplan (medium risk, fixes ADR-001 alignment)
3. Add SBOMSnapshot container (medium risk, enables snapshot diffing)
Full generic entity table architecture (Option A) is deferred until after the typed-table

View file

@ -116,9 +116,9 @@ use `domain_id` FK for filtering. Remove reliance on string comparison.
---
## Pass 2 — Align Workstream with ADR-001
## Pass 2 — Align Workplan with ADR-001
**Scope:** Resolve I-2. Breaking change to workstream schema. `topic_id`
**Scope:** Resolve I-2. Breaking change to workplan schema. `topic_id`
becomes a nullable secondary annotation; `repo_id` becomes the primary FK.
Workplan frontmatter format gains `repo_slug` field.
@ -135,13 +135,13 @@ state_hub_task_id: "b34b6bb0-3968-464f-b340-389c4758821e"
```
Operations:
1. Add `repo_id` UUID FK (nullable) to `workstreams`
2. Backfill `repo_id` using heuristic: workstream → topic → domain → first
repo for that domain (adequate for current data; all custodian workstreams
1. Add `repo_id` UUID FK (nullable) to `workplans`
2. Backfill `repo_id` using heuristic: workplan → topic → domain → first
repo for that domain (adequate for current data; all custodian workplans
map to the-custodian repo)
3. For topics without a repo: leave nullable (MCP tooling handles this)
### Task T08: Update Workstream model
### Task T08: Update Workplan model
```task
id: T08
@ -154,7 +154,7 @@ state_hub_task_id: "6f1fcf2c-824b-4e3e-884f-5e48b5dea51d"
Add `repo_id: Mapped[uuid.UUID | None]` nullable FK to `managed_repos`.
Keep `topic_id` as nullable secondary. Add `repo` relationship.
### Task T09: Update workstream router and MCP tools
### Task T09: Update workplan router and MCP tools
```task
id: T09
@ -165,7 +165,7 @@ state_hub_task_id: "58a23afa-601a-40a5-b658-2603dc006d13"
```
- `create_workstream` MCP tool: add optional `repo_id` / `repo_slug` param
- Workstream read schema: expose `repo_id` and `repo_slug`
- Workplan read schema: expose `repo_id` and `repo_slug`
- Dependency resolution in `state/summary` uses `repo.domain` when available
### Task T10: Update workplan frontmatter format

View file

@ -19,7 +19,7 @@ in registered repos and the state-hub DB. The file is always authoritative;
the DB is the cache/index layer.
Checks C-01 through C-12 cover: missing workplans/, parse failures, stale
references, status/title drift, unlinked workplans, orphan DB workstreams,
references, status/title drift, unlinked workplans, orphan DB workplans,
repo mismatches, task drift, and orphan DB tasks.
The `--fix` flag applies auto-fixable issues (C-04, C-05, C-06, C-09, C-10, C-11).

View file

@ -1,7 +1,7 @@
---
id: CUST-WP-0010
type: workplan
title: Workstream Lifecycle Documentation
title: Workplan Lifecycle Documentation
domain: infotech
status: completed
owner: custodian
@ -11,11 +11,11 @@ created: 2026-03-03
updated: 2026-03-03
---
# CUST-WP-0010 — Workstream Lifecycle Documentation
# CUST-WP-0010 — Workplan Lifecycle Documentation
## Purpose
The dashboard "Workstreams by Domain" chart exposes seven computed states
The dashboard "Workplans by Domain" chart exposes seven computed states
(active, accepted, finished, blocked, stalled, oldies + time-based modes).
These need to be documented so that Bernd and the custodian share a common
vocabulary, and so the "accepted" quality-gate pattern is clearly understood
@ -46,7 +46,7 @@ priority: high
state_hub_task_id: "95e5810d-947a-4039-b017-9bee85cf4f48"
```
### Step 2: Update workstreams reference
### Step 2: Update workplans reference
```task
id: CUST-WP-0010-T2
title: Update docs/workstreams.md status table
@ -68,7 +68,7 @@ state_hub_task_id: "4cef0841-5324-4fbc-a44b-ad8520f77c9f"
- "accepted" maps to DB `status = "completed"` — the name change reflects intent
- The lifecycle is linear: active → finished (task-derived) → accepted (human+custodian gate)
- Attention signals (blocked, stalled, oldies) are orthogonal — a workstream can be
- Attention signals (blocked, stalled, oldies) are orthogonal — a workplan can be
active AND stalled at the same time
- The custodian uses the "finished but not accepted" gap as the trigger to run
quality checks and create follow-up tasks before signing off

View file

@ -19,7 +19,7 @@ updated: 2026-07-08
Capture strategic intent at two levels — domain and repository — as first-class
entities in the State Hub. Domain goals express the high-level direction for a
domain; repository goals refine that into actionable targets for a specific repo.
Workstreams can be linked to a repo goal so the goal hierarchy is visible when
Workplans can be linked to a repo goal so the goal hierarchy is visible when
planning and reviewing progress.
## Schema
@ -34,8 +34,8 @@ planning and reviewing progress.
- Optional FK to `domain_goal_id` — traces which domain goal drove this repo goal
- `status`: active | paused | completed | archived
### `workstreams.repo_goal_id`
- Nullable FK added so workstreams can be tagged to a specific repo goal
### `workplans.repo_goal_id`
- Nullable FK added so workplans can be tagged to a specific repo goal
## MCP Tools Added

View file

@ -112,7 +112,7 @@ Add `priority: str | None` and `due_date_before: date | None` query params to
---
### T05 — Add owner and slug filters to workstream list endpoint
### T05 — Add owner and slug filters to workplan list endpoint
```task
id: CUST-WP-0018-T05

View file

@ -51,7 +51,7 @@ state_hub_task_id: "35a52abb-15b1-4c12-b1c6-5e321377ddfa"
---
### T02 — Core router tests: topics, workstreams, tasks, decisions
### T02 — Core router tests: topics, workplans, tasks, decisions
```task
id: CUST-WP-0020-T02

View file

@ -17,7 +17,7 @@ state_hub_workstream_id: "6f459d9f-b4d4-46d7-a5d7-d5f10721b29e"
## Goal
Record AI token consumption at task granularity and aggregate it up to
workstream, repo, commit, and release level. Makes agent work visible as a
workplan, repo, commit, and release level. Makes agent work visible as a
cost/effort metric — reviewable alongside tasks, workplans, and releases.
## Background
@ -37,14 +37,14 @@ per iteration. Commit/release tagging is optional and manual.
token_events
id UUID PK
task_id UUID FK tasks (nullable)
workstream_id UUID FK workstreams (nullable)
workstream_id UUID FK workplans (nullable)
repo_id UUID FK managed_repos (nullable)
session_id TEXT -- agent session identifier
model TEXT -- e.g. "claude-sonnet-4-6"
tokens_in INT NOT NULL
tokens_out INT NOT NULL
agent TEXT -- "custodian", "ralph", etc.
ref_type TEXT -- 'task'|'workstream'|'commit'|'release'|'session'
ref_type TEXT -- 'task'|'workplan'|'commit'|'release'|'session'
ref_id TEXT -- commit SHA, release tag, etc.
note TEXT
created_at TIMESTAMPTZ server_default=now()
@ -56,7 +56,7 @@ Aggregation endpoint rolls up by any FK axis.
## Exit Criteria
- Token events can be recorded via MCP tool
- Aggregation queries work for task / workstream / repo / commit / release
- Aggregation queries work for task / workplan / repo / commit / release
- Dashboard page shows token spend by repo, workplan, model
- `ralph-workplan` logs a token event per completed task iteration
- All tests passing; consistency check clean
@ -94,7 +94,7 @@ state_hub_task_id: "57d71132-001a-4c85-bc39-2d20155c4971"
```
Add `api/models/token_event.py` (SQLAlchemy ORM, relationships to Task,
Workstream, ManagedRepo). Add `api/schemas/token_event.py`:
Workplan, ManagedRepo). Add `api/schemas/token_event.py`:
- `TokenEventCreate` — input (task_id, workstream_id, repo_id all nullable;
tokens_in, tokens_out required; model, agent, ref_type, ref_id, note optional)
@ -125,7 +125,7 @@ Add `api/routers/token_events.py`:
- `GET /token-events/` — list with filters: `task_id`, `workstream_id`,
`repo_id`, `ref_type`, `ref_id`, `model`, `agent`; default limit 100
- `GET /token-events/summary/` — aggregation; required query param `scope`
(`task`|`workstream`|`repo`|`commit`|`release`) + `id` (the FK value or
(`task`|`workplan`|`repo`|`commit`|`release`) + `id` (the FK value or
ref_id). Returns `TokenSummary`.
Register router in `api/main.py`.
@ -149,7 +149,7 @@ Add two tools to `mcp_server/server.py`:
**`record_token_event(tokens_in, tokens_out, task_id?, workstream_id?,
repo_id?, model?, agent?, ref_type?, ref_id?, note?, session_id?)`**
- POSTs to `/token-events/`
- Returns the created event id and running total for the task/workstream
- Returns the created event id and running total for the task/workplan
**`get_token_summary(scope, id)`**
- GETs `/token-events/summary/?scope=X&id=Y`
@ -195,13 +195,13 @@ state_hub_task_id: "02cc5d8e-a9da-4fb3-9c39-fdc05812d8d0"
Add `dashboard/src/token-cost.md` Observable page:
- **By repo bar chart** — total tokens per repo (stacked in/out)
- **By workplan table** — workstream slug, title, tokens_total, event_count,
- **By workplan table** — workplan slug, title, tokens_total, event_count,
dominant model
- **By model breakdown** — pie or bar; shows model mix across all events
- **Top 10 tasks by tokens** — useful for identifying expensive tasks
Data loader: `dashboard/src/data/token-summary.json.py` — calls
`GET /token-events/summary/` for each repo and workstream.
`GET /token-events/summary/` for each repo and workplan.
Add page to `observablehq.config.js` nav under "Analytics".
@ -247,7 +247,7 @@ state_hub_task_id: "a3627144-9d98-4a3b-aa64-3079fd087448"
Add tests to `state-hub/tests/`:
- `test_token_events.py`: create event, list with filter, summary aggregation
(single task, cross-workstream rollup, by-model breakdown)
(single task, cross-workplan rollup, by-model breakdown)
- `test_token_passthrough.py`: `update_task_status` with tokens creates event
Run `make test`. Run `make fix-consistency REPO=the-custodian`.

View file

@ -152,14 +152,14 @@ description: >
Import refCell from ./components/ref-cell.js.
For each of the three entity tables (By Repo, By Workplan, Top Tasks):
- Prepend a "REF" column using refCell(i+1, recordType, row.id).
Record types: "repos" for By Repo (using repo_id), "workstreams" for
Record types: "repos" for By Repo (using repo_id), "workplans" for
By Workplan (using scope_id), "token-events" for Top Tasks (using
task_id — note: links to the task landing page, not a token event page,
until T04 is done; use recordType "tasks").
- Add a Name column as the second data column:
- By Repo: repo_slug (no truncation needed, slugs are short)
- By Workplan: scope_id displayed as first 8 chars + "…" (unchanged)
→ replace with workstream title if available; for now show scope_id
→ replace with workplan title if available; for now show scope_id
truncated to 36 chars with full UUID tooltip.
- Top Tasks: task_id truncated to 80 chars with full-id tooltip.
Keep all existing columns unchanged.
@ -200,7 +200,7 @@ description: >
Implement as a client-side sort applied after filtering and before slicing for
max-results. For the first four options sort descending by the named field.
For "Most Recent", sort each table's rows by the most recent created_at among
the individual token events belonging to that row's group (repo/workstream/task).
the individual token events belonging to that row's group (repo/workplan/task).
Derive a lastEventAt lookup map from the already-fetched /token-events/ data;
rows with no events sort last. The sort applies uniformly to all three tables.
state_hub_task_id: "84183245-5016-4d87-ad6a-9cd5f6873245"
@ -257,7 +257,7 @@ defining `eventId`, cascading into a `RuntimeError: wsId is not defined`
on the next cell. Fixed by replacing all `FileAttachment` calls in landing
pages with direct `fetch(${API}/...)` calls.
### A03 — amendment — Workstream and repo landing pages missing
### A03 — amendment — Workplan and repo landing pages missing
T04 only created a token-event landing page. The By Workplan and By Repo
tables also had REF links (to `/workstreams/<id>` and `/repos/<slug>`),
@ -283,7 +283,7 @@ landing page returned "Token event not found". Fixed by:
### I02 — improvement — Entity FK fields on detail pages link to their targets
On every detail page (token-event, task, workstream, repo), fields that hold
On every detail page (token-event, task, workplan, repo), fields that hold
a foreign-key UUID (`task_id`, `workstream_id`, `repo_id`) now render as
clickable links with an async-loaded bubble-help showing the entity title.
@ -322,11 +322,11 @@ come from the already-fetched `/repos/` list plus "All repos" at the top.
Selecting a repo filters all three tables client-side to show only rows
attributable to that repo. No API change needed.
### I01 — improvement — Workstream and task Name columns show titles
### I01 — improvement — Workplan and task Name columns show titles
T05 originally showed truncated UUIDs in the Workstream and Task name
T05 originally showed truncated UUIDs in the Workplan and Task name
columns (the summary data carries only IDs, not titles). Improved by
fetching `/workstreams/` and `/tasks/` in parallel with the token-event
poll and building lookup maps (`wsMap`, `taskMap`). The Name column now
displays `workstream.title` and `task.title` (truncated to 80 chars) with
displays `workplan.title` and `task.title` (truncated to 80 chars) with
the full UUID as tooltip.

View file

@ -107,7 +107,7 @@ status: done
priority: high
description: >
Extend the `get_domain_summary` MCP tool response to include a
`capabilities` list alongside `repos` and `workstreams`. Each entry
`capabilities` list alongside `repos` and `workplans`. Each entry
should be compact: {type, title, repo_slug} only (no description or
keywords, to keep token cost low).
Implementation: in mcp_server/server.py, after building the summary dict,

View file

@ -22,7 +22,7 @@ remaining sustained load on the API worker comes from the dashboard polling patt
- **`index.md` alone** runs 4 independent polling loops firing 11 API calls per cycle:
`/state/summary`, `/sbom/snapshots/`, `/progress/`, `/workstreams/`, `/tasks/?limit=2000`,
`/topics/`, `/repos/`, `/workstreams/workplan-index`
- **`workstreams.md` and `dependencies.md`** each call `/state/summary` (the most
- **`workplans.md` and `dependencies.md`** each call `/state/summary` (the most
expensive endpoint — queries 10+ tables) every 60 s just to extract dependency
edges from `open_workstreams[].depends_on`
- **Reference data** (`/topics/`, `/repos/`) is fetched independently by 10+ pages
@ -100,7 +100,7 @@ priority: high
state_hub_task_id: "cb7608d3-5dad-4b51-9b91-080539f7aa65"
```
`workstreams.md` and `dependencies.md` call `/state/summary` (a ~10-table query)
`workplans.md` and `dependencies.md` call `/state/summary` (a ~10-table query)
only to extract `open_workstreams[].{id, depends_on, blocks}`. Add a dedicated
endpoint that returns just this:
@ -109,7 +109,7 @@ GET /state/deps
→ [{"id": "...", "title": "...", "status": "...", "depends_on": [...], "blocks": [...]}]
```
Query: `SELECT id, title, status FROM workstreams WHERE status IN ('active','blocked')`
Query: `SELECT id, title, status FROM workplans WHERE status IN ('active','blocked')`
plus the dependency join — roughly 1/10th the work of the full summary.
**Implementation:** New route in `api/routers/state.py` (or a new `deps.py`).
@ -118,7 +118,7 @@ Schema: `WorkstreamDepStub` already exists in `api/schemas/workstream_dependency
---
### T4 — Replace `/state/summary` in workstreams.md and dependencies.md
### T4 — Replace `/state/summary` in workplans.md and dependencies.md
```task
id: CUST-WP-0039-T4
@ -128,7 +128,7 @@ depends_on: [CUST-WP-0039-T3]
state_hub_task_id: "b80dce9c-b1ef-4606-9460-5100d6f58bce"
```
Switch `workstreams.md` and `dependencies.md` to use the new `/state/deps` endpoint
Switch `workplans.md` and `dependencies.md` to use the new `/state/deps` endpoint
instead of the full `/state/summary`. Both pages construct a dep-edge map from
`open_workstreams[].depends_on`; `/state/deps` provides exactly that.

View file

@ -52,8 +52,8 @@ rather than scattered cron code in the-custodian.
- **activity-core** (WP-0003 in progress): event loop that receives NATS events
and evaluates rules/instructions to create tasks in issue-core.
- **State hub**: tracks domain state, workstreams, decisions, and progress events.
It knows when things happen (repo registered, workstream completed, stale task).
- **State hub**: tracks domain state, workplans, decisions, and progress events.
It knows when things happen (repo registered, workplan completed, stale task).
- **The gap**: the state hub knows about org events but has no way to publish them
to NATS so that activity-core can react. This workplan closes that gap.
@ -91,7 +91,7 @@ Add a NATS JetStream publisher client to the state hub. It should:
Document and agree on the NATS subject naming convention for state hub events.
Proposed pattern: `org.statehub.{noun}.{verb}` — e.g.:
- `org.statehub.repo.registered`
- `org.statehub.workstream.completed`
- `org.statehub.workplan.completed`
- `org.statehub.task.stale`
- `org.statehub.domain.goal.activated`
@ -102,7 +102,7 @@ Cross-reference with activity-core's event-types registry.
Wire the NATS publisher into state hub write operations. Candidate events:
- `org.statehub.repo.registered` — when a repo is registered in the hub
- `org.statehub.workstream.completed` — when a workstream status → completed
- `org.statehub.workplan.completed` — when a workplan status → completed
- `org.statehub.task.stale` — periodic check, tasks not updated in 14+ days
Each event must conform to the EventEnvelope schema defined in activity-core's

View file

@ -22,8 +22,8 @@ under 100 ms:
| Endpoint | Observed latency | Root cause |
|----------|-----------------|------------|
| `/workstreams/workplan-index` | 4171 ms | Synchronous filesystem scan (all repos, all `.md` files, YAML parse) on every request — no cache |
| `/topics/` | 2382 ms | `lazy="selectin"` on `Topic` triggers full loads of `workstreams`, `decisions`, and `progress_events` per topic |
| `/domains/` | 2252 ms | `lazy="selectin"` on `Domain` cascades into topics → workstreams chain |
| `/topics/` | 2382 ms | `lazy="selectin"` on `Topic` triggers full loads of `workplans`, `decisions`, and `progress_events` per topic |
| `/domains/` | 2252 ms | `lazy="selectin"` on `Domain` cascades into topics → workplans chain |
| `/repos/` | 2222 ms | `lazy="selectin"` on `ManagedRepo` loads `domain` + `goals` |
| `/tasks/?limit=500` | 2174 ms vs 850 ms for `limit=2000` | Query planner picks a bad plan without a `status` index |
| `/sbom/snapshots/` | 2704 ms | Missing composite index on `(repo_id, snapshot_at)` used by the latest-snapshot subquery |
@ -65,8 +65,8 @@ Add a new Alembic migration with the following indexes:
CREATE INDEX ix_tasks_status ON tasks(status);
CREATE INDEX ix_tasks_workstream_status ON tasks(workstream_id, status);
-- /workstreams/?status= and state/summary active-workstream filter
CREATE INDEX ix_workstreams_status ON workstreams(status);
-- /workstreams/?status= and state/summary active-workplan filter
CREATE INDEX ix_workstreams_status ON workplans(status);
-- /sbom/snapshots/ latest-snapshot subquery: MAX(snapshot_at) GROUP BY repo_id
CREATE INDEX ix_sbom_snapshots_repo_at ON sbom_snapshots(repo_id, snapshot_at DESC);
@ -106,7 +106,7 @@ async def workplan_index(
if not refresh and _INDEX_CACHE is not None and (time.monotonic() - _INDEX_CACHE_AT) < _INDEX_TTL:
return _INDEX_CACHE
# ... existing scan logic ...
_INDEX_CACHE = {"workstreams": index}
_INDEX_CACHE = {"workplans": index}
_INDEX_CACHE_AT = time.monotonic()
return _INDEX_CACHE
```

View file

@ -107,7 +107,7 @@ should become authoritative in `/home/worsch/state-hub`:
| `infra/` | Move; local Postgres compose and infra docs. |
| `mcp_server/` | Move; MCP server and tool reference. |
| `migrations/` | Move; Alembic migration environment and versions. |
| `policies/` | Move; repo DoI and workstream DoD policies. |
| `policies/` | Move; repo DoI and workplan DoD policies. |
| `prompts/` | Move; State Hub prompt assets. |
| `pyproject.toml` | Move; Python package/dependency definition. |
| `scripts/` | Move; consistency, registration, SBOM, token, image, and repo-sync tooling. |
@ -271,7 +271,7 @@ Connect `/home/worsch/state-hub` to State Hub as a managed repo in the
Use the file-first rule for repo-backed workplans. If new workplan files are
created in the standalone repo, write them before running consistency sync or
registration tooling so no ghost workstreams are created.
registration tooling so no ghost workplans are created.
Done when State Hub repo metadata resolves the local host path for
`/home/worsch/state-hub`, and `fix-consistency --here` or the equivalent repo
@ -291,13 +291,13 @@ registered. The first priority is the recently generated
`CUST-WP-0042 - Workplan State Model Cleanup`.
The migration should preserve existing State Hub IDs where appropriate and avoid
creating duplicate workstreams. If the consistency engine cannot safely move an
existing file-backed workstream across repos, record the limitation and use the
creating duplicate workplans. If the consistency engine cannot safely move an
existing file-backed workplan across repos, record the limitation and use the
least confusing transition path:
- keep the old file as an archived pointer, or
- create a new State Hub-local continuation plan that references the old
workstream explicitly
workplan explicitly
Done when future agents can work on the State Hub state-model cleanup from the
standalone repo without editing `the-custodian/state-hub/`.
@ -360,7 +360,7 @@ After extraction is verified, resume `CUST-WP-0042` from the standalone
- confirm the workplan paths point at the new repo layout
- confirm `state_hub_workstream_id` and task IDs still refer to the intended
workstream/tasks
workplan/tasks
- update any acceptance criteria that assumed the embedded path
- record a progress event linking this extraction workplan to the resumed
state-model cleanup work
@ -385,7 +385,7 @@ complete, because its changes should land in the standalone `state-hub` repo.
- `the-custodian` no longer owns a live copy of State Hub implementation code.
- State Hub repo metadata knows the standalone repo path on this host.
- New State Hub workplans can be synced from the standalone repo without ghost
workstreams.
workplans.
- Existing active State Hub work, especially `CUST-WP-0042`, has a clear and
non-duplicating handoff path into the standalone repo.
- Local API, MCP, migration, dashboard, test, and consistency workflows are

View file

@ -31,7 +31,7 @@ promoting work into execution without human or agent review.
As of 2026-05-17, the State Hub snapshot shows:
- 24 active workstreams and 2 blocked workstreams
- 24 active workplans and 2 blocked workplans
- 184 todo tasks, 5 in-progress tasks, and 5 blocked tasks
- 5 open decisions
- several large active workplans with low completion ratios
@ -49,7 +49,7 @@ In scope:
MCP tools when available.
- WSJF assessment for open workplans and representative next tasks.
- Detection of loose-end patterns: stale active plans, large unstarted plans,
blocked work, open decisions, dependency-cleared work, zero-task workstreams,
blocked work, open decisions, dependency-cleared work, zero-task workplans,
and inconsistent file-backed state.
- A daily recommendation artifact that separates:
- work to do next
@ -75,7 +75,7 @@ Each daily run should collect:
- State Hub summary via `get_state_summary()` or `GET /state/summary`
- hub inbox via `get_messages(to_agent="hub", unread_only=True)` or
`GET /messages/?to_agent=hub&unread_only=true`
- open workstreams, open tasks, blocked tasks, blocking decisions, next-step
- open workplans, open tasks, blocked tasks, blocking decisions, next-step
hints, workplan index, and repo goal guidance
- relevant git status and consistency state for repos whose workplans are
likely to be touched
@ -98,7 +98,7 @@ Use a 1-5 scale for each numerator factor and denominator factor.
| Strategic value | Alignment with current Custodian, State Hub, Railiance, or dependency-chain goals. |
| Time criticality | Whether waiting increases operational risk, blocks other work, or lets context decay. |
| Risk reduction | How much the work reduces security, reliability, data-loss, coordination, or governance risk. |
| Opportunity enablement | How much the work unlocks other active workstreams or reduces future agent friction. |
| Opportunity enablement | How much the work unlocks other active workplans or reduces future agent friction. |
| Job size | Estimated effort and uncertainty; 1 is small/clear, 5 is large/uncertain. |
Formula:
@ -208,9 +208,9 @@ Signals include:
- active workplans with no recent progress
- large active workplans with many todo tasks and no in-progress task
- blocked workplans whose dependencies are now resolved or misclassified
- workstreams with zero parsed tasks
- workplans with zero parsed tasks
- high-priority workplans with stale `updated` metadata
- tasks still open after their parent workstream appears effectively complete
- tasks still open after their parent workplan appears effectively complete
- workplans whose file-backed state differs from State Hub state
Done when the daily report names concrete revisit candidates and the reason

View file

@ -370,7 +370,7 @@ no State Hub digest data left the workstation:
- State Hub progress event: `e42c0ada-8111-4d88-9791-821252cd04a2`
The real Claude-backed llm-connect trigger was not run in that pass. The
execution wrapper blocked it because private State Hub workstream/task digest
execution wrapper blocked it because private State Hub workplan/task digest
data would be sent to an external LLM provider. The operator then clarified that
`llm-connect` is the intended backend boundary for LLM providers and depth
tuning. Follow-up implementation keeps that boundary explicit: activity-core
@ -546,7 +546,7 @@ The runbook also documents two operational gotchas the 2026-05-21 attempts hit:
The current `daily_triage_digest` (built with the live State Hub and the
ActivityDefinition's params) was inspected: 10,175 bytes, totals across 13
active topics / 13 active workstreams / 149 todo tasks, 12 open workstreams
active topics / 13 active workplans / 149 todo tasks, 12 open workplans
in the digest with `hf-wp-0001`, `cust-wp-0044`, `cust-wp-0045`, `cust-wp-0046`
at the priority head. This is substantive context — a working real-LLM canary
should produce non-trivial recommendations, not the `"summary":"ok"` stub from
@ -658,7 +658,7 @@ T07 is done by reusing the State Hub WSJF Triage review page from
Evidence:
- `STATE-WP-0053` is `finished`; its dashboard page is reachable from
Workstreams -> WSJF Triage and implemented in the State Hub dashboard at
Workplans -> WSJF Triage and implemented in the State Hub dashboard at
`dashboard/src/wsjf-triage.md`.
- The page reads `/progress/?event_type=daily_triage&limit=14` and
`/workstreams/workplan-index`, shows live/last-updated state, recent reports,

View file

@ -89,7 +89,7 @@ Preferred path:
1. State Hub exposes a deterministic batch endpoint for RecentlyOnScope hourly
runs. It owns active-domain detection because it already owns domains,
topics, workstreams, tasks, decisions, progress events, and report storage.
topics, workplans, tasks, decisions, progress events, and report storage.
2. activity-core schedules an hourly ActivityDefinition that calls the State Hub
batch endpoint. activity-core should not duplicate domain-selection SQL.
3. The hourly run is idempotent by `(window, domain_slug, range)`, reusing the
@ -144,7 +144,7 @@ Required behavior:
- accept a range such as `1h`, defaulting to `1h`
- compute the window in UTC
- select active domains with at least one qualifying source in the window:
progress events, decisions, updated workstreams, or updated tasks
progress events, decisions, updated workplans, or updated tasks
- optionally include domains with open human-intervention items when configured
- generate one RecentlyOnScope report per active domain using the existing
`generate_report()` service
@ -300,7 +300,7 @@ Implemented pieces:
- State Hub batch generation reuses the existing RecentlyOnScope collector,
renderer, report id, and report directory.
- The batch endpoint selects domains by qualifying activity in the requested
window: progress events, decisions, updated workstreams, or updated tasks.
window: progress events, decisions, updated workplans, or updated tasks.
- Domains with only registered repositories are skipped; domains with open
human-intervention items can be included by setting `include_attention: true`.
- The batch endpoint records one `recently_on_scope_hourly` progress event with

View file

@ -129,7 +129,7 @@ Run the State Hub consistency sync for `the-custodian` so this workplan and its
task statuses are registered in the hub database.
Done when `make fix-consistency REPO=the-custodian` has completed and the
workstream appears in State Hub.
workplan appears in State Hub.
## Task: Activate Ops-Hub Widgets In Inter-Hub

View file

@ -28,7 +28,7 @@ This is a child workplan of `CUST-WP-0025` Phase 2. The umbrella task
`hub-core` should own reusable hub primitives and factory-based router/MCP
surfaces. `state-hub` should keep development coordination concepts such as
topics, workstreams, tasks, decisions, dependencies, consistency checks, SBOM,
topics, workplans, tasks, decisions, dependencies, consistency checks, SBOM,
dashboard loaders, and workflow side effects.
The migration should proceed by narrow seams:
@ -66,7 +66,7 @@ The migration should proceed by narrow seams:
hub-core pytest passed with 18 tests; focused State Hub tests passed with 17
and 54 tests.
- 2026-06-07: Added host-model/schema injection to the hub-core progress router
and replaced State Hub's progress router while preserving topic/workstream/task
and replaced State Hub's progress router while preserving topic/workplan/task
filters and shared risk/alert views. Verification: hub-core pytest passed with
19 tests; State Hub focused tests passed with 71 tests.
- 2026-06-07: Added host-model/schema injection, optional update-route
@ -175,7 +175,7 @@ state_hub_task_id: "669dea12-9ef1-4ab4-83ae-5ee4cf5fd159"
Review capability request creation, acceptance, status changes, patching,
dispute, and reroute routes. Decide which write paths can move to hub-core via
callbacks and which require a dev-hub extension table or context mapping for
workstream/task references.
workplan/task references.
Completed 2026-06-22: all write routes mount from
`create_capability_request_write_router`. Dev-hub keeps `requesting_workplan_id`,
@ -198,7 +198,7 @@ composition or inheritance while keeping dev-hub-only tools local.
Completed 2026-06-22: State Hub calls `HubCoreMCPServer.attach_to(mcp,
exclude=_HUB_CORE_MCP_EXCLUDE)` for 18 generic tools. Local overrides remain
for dev-hub orientation, extended repo/capability/TPSC contracts, and all
workstream/task/decision tooling.
workplan/task/decision tooling.
## T07 - Run Regression And Coupling Report

View file

@ -75,10 +75,10 @@ the Hub's current 14 coordination domains. Per the steering decision on
2026-06-22, the new market-domain vocabulary **replaces** the Hub's domain model
(rather than augmenting it or running a parallel two-axis model).
The current spine is `Domain → Topic → Workstream`, where `topics.domain_id` and
`workstreams.topic_id` are both **NOT NULL** and the 14 domains are seeded **1:1
The current spine is `Domain → Topic → Workplan`, where `topics.domain_id` and
`workplans.topic_id` are both **NOT NULL** and the 14 domains are seeded **1:1
with 14 topics** (a data convention — the schema actually allows many topics per
domain, but that has never been used). `workstreams.repo_id` and
domain, but that has never been used). `workplans.repo_id` and
`repo_goals.repo_id` already exist, but the *required* anchor is the soft,
hub-only `topic`, while the stable git-managed `repo` link is optional.
@ -87,7 +87,7 @@ Per the 2026-06-22 steering decision, this redesign **flips the polarity**: the
**derived** from the repo's `.repo-classification.yaml`, not stored as a separate
`topic`/`domain` parent. Concretely:
- `workstreams.repo_id` becomes the **required** anchor; `topic_id` is demoted to
- `workplans.repo_id` becomes the **required** anchor; `topic_id` is demoted to
optional (or `topic` is retired) — see T10.
- Market-domain is computed from `repo → classification.domain`; the standalone
`topics.domain_id` / `managed_repos.domain_id` spine is removed.
@ -124,7 +124,7 @@ In scope (added 2026-06-22):
- Re-anchor workplans to repos (`repo_id` required, `topic` optional/retired) and
derive market-domain from classification (T04/T10).
- Rename `workstream → workplan` across schema, API, and MCP so the Hub
- Rename `workplan → workplan` across schema, API, and MCP so the Hub
vocabulary matches the repo files and current usage (T10).
Out of scope:
@ -231,7 +231,7 @@ custodian fixtures (T02) plus T01 are sufficient to build and prove the redesign
Per this plan's repo boundary and the 2026-06-22 decision, the implementation of
the State Hub redesign now lives in a **state-hub-local workplan**:
`state-hub/workplans/STATE-WP-0065-repo-anchored-classification-spine.md`
(workstream `8dc7d106-11e2-41df-b512-89ed69d2a65f`). CUST-WP-0050 remains the
(workplan `8dc7d106-11e2-41df-b512-89ed69d2a65f`). CUST-WP-0050 remains the
**coordination driver** (canon standard, decisions D1/D1a, ADR-005). The original
implementation tasks below are **cancelled here** (re-homed, not abandoned); the
efficient regrouping merges the three spine-rewriting tasks into one migration:
@ -298,7 +298,7 @@ status: cancel
priority: high
state_hub_task_id: "bee16416-a67f-4155-93d7-09f278daa04f"
```
Re-homed → STATE-WP-0065 P1 (re-anchor `repo_id` required + `workstream → workplan` rename, merged into the spine migration).
Re-homed → STATE-WP-0065 P1 (re-anchor `repo_id` required + `workplan → workplan` rename, merged into the spine migration).
### Phase 4 (custodian) — Post-cutover inventory

View file

@ -34,9 +34,9 @@ for removing cross-workplan blocks and creating a reliable handoff point.
Reviewed on 2026-06-27 from State Hub and the repo workplan files.
Active registered workstreams with open work:
Active registered workplans with open work:
| Workstream | Open state | Main stabilization meaning |
| Workplan | Open state | Main stabilization meaning |
| --- | --- | --- |
| artifact-store-wp-0007 | 5 todo | Object-store compatibility and STS credential vending lane. |
| ihub-wp-0022 | 3 wait, 5 done | Ops-hub evidence intake waits on widget seed/runtime key/smoke. |
@ -46,7 +46,7 @@ Active registered workstreams with open work:
| three-phoenix-ha-cluster | 7 todo | Target HA substrate is planned but not executed. |
| staged-promotion-lifecycle | finished, 7 done | Promotion discipline ready for broad production cutovers. |
| rail-ho-wp-0005 | 11 todo, 1 progress | Forgejo production migration needs human design and cutover decisions. |
| cust-wp-0045-cutover-runbook | 0 tasks | Registered runbook is appearing as an active no-task workstream. |
| cust-wp-0045-cutover-runbook | 0 tasks | Registered runbook is appearing as an active no-task workplan. |
| net-wp-0020 | 2 wait, 1 todo, 2 done | OpenBao unseal custody models still need operator profile decisions. |
| issue-wp-0003 | 2 progress, 5 done | issue-core deploy is close; finish live wiring and runbook evidence. |
| activity-wp-0006 | 1 wait, 1 todo, 6 done | Three-run calibration waits on the daily-triage live gate. |
@ -124,21 +124,21 @@ Clean up the planning substrate before execution work resumes.
Minimum scope:
- Decide whether `CUST-WP-0045-cutover-runbook` should stay registered as an
active workstream or be represented only as a runbook under `CUST-WP-0045`.
active workplan or be represented only as a runbook under `CUST-WP-0045`.
- Resolve `CUST-WP-0014`: archive as superseded, or reopen and re-scope the six
remaining State Hub sync-health tasks.
- Clear or annotate stale `needs_human` flags on done/cancel tasks after source
workplans confirm they are no longer live gates.
- Run State Hub consistency after file changes.
Done when the active workstream list no longer contains no-task runbooks or
Done when the active workplan list no longer contains no-task runbooks or
contradictory done-with-todo files, and the human-needed view shows only live
human gates.
Progress 2026-06-27:
- `CUST-WP-0045-cutover-runbook` now has `status: finished`; State Hub no
longer lists it as an active workstream.
longer lists it as an active workplan.
- `CUST-WP-0014` is reopened as `backlog` with its task detail preserved, so it
is no longer a contradictory done-with-todo file or an active queue item.
- `make fix-consistency REPO=the-custodian` passed with pre-existing C-12
@ -357,7 +357,7 @@ priority: medium
state_hub_task_id: "2083f0e4-e037-48bf-8069-f31e8db2fd95"
```
Move near-complete service workstreams to done before starting larger migrations.
Move near-complete service workplans to done before starting larger migrations.
Priority order:
@ -488,7 +488,7 @@ Progress 2026-06-27 staged promotion T07 and finish:
deploy/observe plans, Stage 3 promote/rollback plans, and blocked apply paths
for missing approval/Helm/revision evidence.
- Marked `RAIL-BS-WP-0006` `status: finished`; `make fix-consistency
REPO=railiance-cluster` synced the finished workstream with only pre-existing
REPO=railiance-cluster` synced the finished workplan with only pre-existing
C-12 orphan-row warnings.
Progress 2026-06-30 policy-gate support closeout:
@ -714,7 +714,7 @@ Close this metaplan by creating an operator-friendly checkpoint.
Minimum contents:
- active workstream list with zero stale runbooks and zero contradictory task
- active workplan list with zero stale runbooks and zero contradictory task
states;
- blocker board showing no unowned credential, access, or approval gates;
- daily automation evidence from the latest successful scheduled run;
@ -728,7 +728,7 @@ workplan without reconstructing this review.
Completed 2026-06-27: added
`docs/infrastructure-stabilization-pickup-checkpoint.md` with the live active
workstream list, named blocker board, latest daily-triage evidence, production
workplan list, named blocker board, latest daily-triage evidence, production
service status summary, and next-pick sequence. This closes the handoff surface
for future agents while the child workplans remain the execution source of
truth.

View file

@ -1,7 +1,7 @@
---
id: CUST-WP-0055
type: workplan
title: "Fleet-wide workplan terminology refactor (workstream → workplan)"
title: "Fleet-wide workplan terminology refactor (workplan → workplan)"
domain: infotech
repo: the-custodian
status: active
@ -20,14 +20,14 @@ state_hub_workstream_id: "d96b72d5-24f2-492b-8bb4-50c39058848a"
Make **workplan** the consistent product and documentation term across all
Coulomb-registered repositories, while preserving compatibility bridges where
clients, events, or frontmatter still depend on legacy `workstream` identifiers.
clients, events, or frontmatter still depend on legacy `workplan` identifiers.
## Context
State Hub already completed the spine rename (`STATE-WP-0065`) and the
compatibility-first terminology transition (`STATE-WP-0054`,
`docs/workplan-terminology-transition.md`). Preferred REST/MCP surfaces expose
`workplan`; legacy `workstream` paths remain metered via `legacy-meter`.
`workplan`; legacy `workplan` paths remain metered via `legacy-meter`.
A fleet scan on **2026-07-08** (see inventory below) shows the term is still
widespread outside State Hub internals:
@ -35,7 +35,7 @@ widespread outside State Hub internals:
| Metric | Value |
| --- | --- |
| Registered repos scanned | 76 |
| Repos with `workstream` hits | 73 |
| Repos with `workplan` hits | 73 |
| Total occurrences | 22,237 |
| Files touched | 7,823 |
| Missing local checkouts | `markitect-project`, `vergabe_teilnahme` — exclude from exit counts until paths exist or repos marked dormant |
@ -55,9 +55,9 @@ widespread outside State Hub internals:
| Pattern | Count | Refactor stance |
| --- | ---: | --- |
| `workstreams` (generic) | 13,108 | Prose/docs → `workplans`; code paths case-by-case |
| `workplans` (generic) | 13,108 | Prose/docs → `workplans`; code paths case-by-case |
| `workstream_id` | 3,830 | Keep API alias until legacy-meter retires |
| `workstream ` (prose) | 2,420 | **Replace** in user-facing text |
| `workplan ` (prose) | 2,420 | **Replace** in user-facing text |
| `state_hub_workstream_id` | 1,065 | **Keep** frontmatter bridge until dedicated migration |
| `/workstreams/` routes | 490 | Keep compat routes; docs point to `/workplans/` |
| `create_workstream` MCP | 346 | Keep alias; guidance prefers `create_workplan` |
@ -80,8 +80,8 @@ python tools/scan_workstream_terminology.py --repo the-custodian --json
| Workplan frontmatter link field | `state_hub_workstream_id` | holds workplan UUID | Document; rename field in later WP |
| REST/MCP params | `workplan_id` preferred | `workstream_id` alias | Guidance only; retire per legacy-meter |
| REST routes | `/workplans/` | `/workstreams/` | Docs + dashboard; retire per meter |
| NATS / State Hub events | `org.statehub.workplan.completed` | `org.statehub.workstream.completed` | State Hub dual-publishes today; retire legacy per meter |
| activity-core event catalog | `org.statehub.workplan.completed` | `org.workstream.completed` | Align catalog to State Hub subjects; retire custodian-era type |
| NATS / State Hub events | `org.statehub.workplan.completed` | `org.statehub.workplan.completed` | State Hub dual-publishes today; retire legacy per meter |
| activity-core event catalog | `org.statehub.workplan.completed` | `org.workplan.completed` | Align catalog to State Hub subjects; retire custodian-era type |
| Python/TS identifiers | `workplan_*` | `workstream_*` | Refactor when behaviour unchanged |
| DB tables / ORM models | `workplan` | — | Done in STATE-WP-0065 |
@ -90,10 +90,10 @@ columns in this plan — that is a separate bridge-field migration.
**Event namespace note:** three subjects exist in the fleet today. State Hub
emits **`org.statehub.workplan.completed`** (preferred) and
**`org.statehub.workstream.completed`** (legacy, metered) on the same
**`org.statehub.workplan.completed`** (legacy, metered) on the same
transition — see `state-hub/api/routers/workstreams.py`. activity-core still
documents the older custodian catalog type **`org.workstream.completed`**
(`activity-core/event-types/org.workstream.completed.md`). This plan aligns
documents the older custodian catalog type **`org.workplan.completed`**
(`activity-core/event-types/org.workplan.completed.md`). This plan aligns
activity-core to the State Hub subjects; it does **not** introduce a bare
`org.workplan.completed` subject.
@ -124,12 +124,12 @@ Update `state-hub/scripts/project_rules/*.template` so regenerated
partially note the legacy mapping — close remaining gaps).
Done when `update_agent_instruction_files` output uses workplan in prose and
only mentions `workstream` in an explicit compatibility footnote.
only mentions `workplan` in an explicit compatibility footnote.
Progress 2026-07-08: canon addendum drafted at
`canon/standards/workplan-terminology-fleet_v0.1.md` (fleet term, legacy
bridges, event subjects, agent rules, retirement rule). Baseline JSON at
`docs/evidence/workstream-terminology-baseline-20260708.json`. Template regeneration (`state-hub/scripts/project_rules/*.template`) verified
`docs/evidence/workplan-terminology-baseline-20260708.json`. Template regeneration (`state-hub/scripts/project_rules/*.template`) verified
workplan-first with explicit legacy footnotes only.
## Task: State Hub and hub-core legacy surface retirement plan
@ -145,8 +145,8 @@ state_hub_task_id: "2bb01721-a86b-43a0-ab4c-e5966743d295"
`state-hub/workplans/STATE-WP-0069-workplan-terminology-legacy-retirement.md`
before any interface retirement executes.
**T02-T02:** inventory remaining `workstream` strings in dashboard, tests, flows
(`flows/workstream.yaml`), and compat routers; tie each to a `legacy-meter`
**T02-T02:** inventory remaining `workplan` strings in dashboard, tests, flows
(`flows/workplan.yaml`), and compat routers; tie each to a `legacy-meter`
key; set retirement order after weekly review shows zero callers.
Deliverables: `STATE-WP-0069` file registered via `fix-consistency`, ranked
@ -169,14 +169,14 @@ Align activity-core to State Hub's existing dual-publish contract:
| Subject | Role | Action |
| --- | --- | --- |
| `org.statehub.workplan.completed` | preferred | Add/rename activity-core event type; new automations subscribe here |
| `org.statehub.workstream.completed` | legacy (State Hub) | Document as legacy; register in legacy-meter if not already |
| `org.workstream.completed` | legacy (custodian catalog) | Deprecate; map subscribers to `org.statehub.workplan.completed` |
| `org.statehub.workplan.completed` | legacy (State Hub) | Document as legacy; register in legacy-meter if not already |
| `org.workplan.completed` | legacy (custodian catalog) | Deprecate; map subscribers to `org.statehub.workplan.completed` |
State Hub already emits both `org.statehub.*` subjects on workplan completion;
activity-core must **not** invent `org.workplan.completed`. Update
`activity-core/event-types/`, `activity_core/context_resolvers/state_hub.py`
log messages, k8s manifests, and workplan prose. Publish a sunset date for
`org.workstream.completed` in the activity-core catalog.
`org.workplan.completed` in the activity-core catalog.
Done when activity-core event definitions and subscribers use
`org.statehub.workplan.completed`, legacy subjects are documented with
@ -187,18 +187,18 @@ subscriptions.
```task
id: CUST-WP-0055-T04
status: todo
status: progress
priority: medium
state_hub_task_id: "2ff6cef9-7ec2-4d44-bce0-b232b1f889dc"
```
Mechanical pass on the ~60 domain repos with the standard bootstrap shape
(typically 2580 hits each): `AGENTS.md`, `SCOPE.md`, `INTENT.md`, `README.md`,
and active root workplans. Replace user-facing `workstream` with `workplan`;
and active root workplans. Replace user-facing `workplan` with `workplan`;
leave `state_hub_workstream_id` and API examples that demonstrate legacy aliases.
Use `scan_workstream_terminology.py --json` before/after per repo; target zero
`prose:workstream ` hits in agent-guidance buckets (per T08 exclusions).
`prose:workplan ` hits in agent-guidance buckets (per T08 exclusions).
Batch ~10 repos per PR to limit merge churn.
## Task: Code and integration sweep (activity-core, issue-core, railiance-*)
@ -249,10 +249,10 @@ state_hub_task_id: "362790c8-cf27-4042-81e4-533a6b48fb26"
```
Update **active** workplan prose only; for `workplans/archived/`, add a
single header note that historical text may say workstream. Optionally normalize
single header note that historical text may say workplan. Optionally normalize
titles in archived files when the edit is mechanical (no ID renames).
Grandfathered filenames containing `workstream` (e.g.
Grandfathered filenames containing `workplan` (e.g.
`CUST-WP-0010-workstream-lifecycle-docs.md`) keep their paths per ADR-001
non-rename policy.
@ -282,17 +282,17 @@ script with `--apply-allowlist`.
**Exit criteria:**
1. `scan_workstream_terminology.py` — zero `prose:workstream ` across domain
1. `scan_workstream_terminology.py` — zero `prose:workplan ` across domain
repos (with exclusions); `state-hub` under agreed grep budget for non-compat
prose only.
2. State Hub `legacy-meter` weekly review — no new prose-only legacy keys.
3. `fix-consistency` / interface-change registry — no new `workstream`-named
3. `fix-consistency` / interface-change registry — no new `workplan`-named
public tools without a workplan alias.
4. CI or activity-core scheduled check fails when prose hits regress outside
the allowlist.
Done when all four checks are automated and the 2026-07-08 baseline is stored
as `docs/evidence/workstream-terminology-baseline-20260708.json` (or equivalent)
as `docs/evidence/workplan-terminology-baseline-20260708.json` (or equivalent)
at T01 completion.
## Sequencing