Workplan terminology: templates, updater guard, add_progress_event alias

- project_rules templates: rename workstream->workplan in prose; registration
  guidance is now file-first + fix-consistency C-06 (manual create_workplan/
  create_workstream calls create duplicates); progress examples use
  workplan_id; legacy field names (state_hub_workstream_id) annotated
- update_agent_instruction_files: never overwrite filled-in
  stack-and-commands/repo-boundary/architecture rules (TODO-marker guard)
- mcp_server: add_progress_event accepts workplan_id (preferred) with
  workstream_id kept as legacy alias, mirroring create_task

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 01:47:47 +02:00
parent a361ce8731
commit ea1fd23481
7 changed files with 60 additions and 31 deletions

View File

@@ -989,6 +989,7 @@ def add_progress_event(
summary: str,
event_type: str = "note",
topic_id: str | None = None,
workplan_id: str | None = None,
workstream_id: str | None = None,
task_id: str | None = None,
detail: dict | str | None = None,
@@ -999,7 +1000,8 @@ def add_progress_event(
summary: human-readable summary of what happened
event_type: free-form label, e.g. note | milestone | blocker | insight
topic_id: optional topic UUID
workstream_id: optional workstream UUID
workplan_id: optional workplan UUID (preferred)
workstream_id: legacy alias for workplan_id
task_id: optional task UUID
detail: optional structured data (JSONB); accepts a dict or a JSON string
"""
@@ -1010,7 +1012,7 @@ def add_progress_event(
detail = {"raw": detail}
event = _post("/progress", {
"topic_id": topic_id,
"workstream_id": workstream_id,
"workplan_id": workplan_id or workstream_id,
"task_id": task_id,
"event_type": event_type,
"summary": summary,

View File

@@ -33,8 +33,8 @@ statehub outbox status/replay after connectivity returns.
# Offline brief — works without hub connection
cat .custodian-brief.md
# Active workstreams for this domain
curl -s "http://127.0.0.1:8000/workstreams/?topic_id={TOPIC_ID}&status=active" \
# Active workplans for this domain
curl -s "http://127.0.0.1:8000/workplans/?topic_id={TOPIC_ID}&status=active" \
| python3 -m json.tool
# Check inbox
@@ -57,12 +57,12 @@ curl -s -X POST http://127.0.0.1:8000/progress/ \
"summary": "what was done",
"event_type": "note",
"author": "codex",
"workstream_id": "<uuid>",
"workplan_id": "<uuid>",
"task_id": "<uuid>"
}'
```
Omit `workstream_id` / `task_id` when not applicable.
Omit `workplan_id` / `task_id` when not applicable.
### Update task status
@@ -86,7 +86,7 @@ curl -s -X PATCH "http://127.0.0.1:8000/tasks/<task_id>" \
## Session Protocol
**Start:**
1. `cat .custodian-brief.md` — domain goal and open workstreams (offline-safe)
1. `cat .custodian-brief.md` — domain goal and open workplans (offline-safe)
2. Check inbox: `GET /messages/?to_agent={REPO_SLUG}&unread_only=true`; mark read
3. Scan workplans: `ls workplans/` — note `status: ready`, `active`, or `blocked` files and open tasks
4. Check human-needed tasks: `GET /tasks/?needs_human=true`
@@ -145,7 +145,7 @@ owner: codex
topic_slug: ...
created: "YYYY-MM-DD"
updated: "YYYY-MM-DD"
state_hub_workstream_id: "<uuid>" # written by fix-consistency — do not edit
state_hub_workstream_id: "<uuid>" # written by fix-consistency — do not edit (legacy name; holds the workplan id)
---
```

View File

@@ -20,7 +20,7 @@ Requires the `warden` CLI from `~/ops-warden` (`uv tool install .` or `uv run wa
| Agent runtime | How to orient |
| --- | --- |
| **Codex / Grok** (shell, HTTP State Hub) | `warden route` commands above; inbox `to_agent={REPO_SLUG}` is for coordination, not secret vending |
| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workstreams; **still** use `warden route` for credential ownership |
| **Claude Code** (MCP when available) | `get_domain_summary("custodian")` for workplans; **still** use `warden route` for credential ownership |
| **llm-connect** (inference service) | Never put secret retrieval in prompts; route custody to OpenBao/operator paths surfaced by `warden route` |
### Quick routing table

View File

@@ -1,6 +1,6 @@
## First Session Protocol
Triggered when `get_domain_summary("{DOMAIN}")` shows **no workstreams**.
Triggered when `get_domain_summary("{DOMAIN}")` shows **no workplans**.
The project is registered but work has not yet been structured.
**Step 1 — Read, don't write**
@@ -11,27 +11,31 @@ The project is registered but work has not yet been structured.
**Step 2 — Survey in-progress work**
Look for TODOs, open branches, half-finished files. Note done vs. started but incomplete.
**Step 3 — Propose workstreams to Bernd**
Propose 13 workstreams — each a coherent strand, weeks to months, anchored to a
**Step 3 — Propose workplans to Bernd**
Propose 13 workplans — each a coherent strand, weeks to months, anchored to a
roadmap phase. **Wait for approval before creating.**
**Step 4 — Create workplan file first, then DB record (ADR-001)**
**Step 4 — Write the workplan file; fix-consistency registers it (ADR-001)**
```
workplans/{WP_PREFIX}-NNNN-<slug>.md ← write this first
workplans/{WP_PREFIX}-NNNN-<slug>.md ← write this, commit it
```
Then register in the hub:
```
create_workstream(topic_id="{TOPIC_ID}", title="...", owner="...", description="...")
create_task(workstream_id="<id>", title="...", priority="high|medium|low")
Then register by running the consistency check — do **not** call
`create_workplan`/`create_task` (or legacy `create_workstream`) yourself;
manual registration duplicates what C-06 creates from the file:
```bash
statehub fix-consistency --repo {REPO_SLUG}
```
C-06 creates the hub workplan + tasks and writes `state_hub_workstream_id` /
`state_hub_task_id` back into the file (legacy field names, kept for
compatibility — they hold workplan/task IDs).
**Step 5 — Record the setup**
```
add_progress_event(
summary="First session: structured {DOMAIN} into N workstreams, M tasks",
summary="First session: structured {DOMAIN} into N workplans, M tasks",
event_type="milestone",
topic_id="{TOPIC_ID}",
detail={"workstreams": [...], "tasks_created": M}
detail={"workplans": [...], "tasks_created": M}
)
```

View File

@@ -44,7 +44,7 @@ For each file with `status: ready`, `active`, or `blocked`, note pending
**Step 4 — Present brief**
1. **Active workstreams** for `{DOMAIN}` — title, task counts, blocking decisions
1. **Active workplans** for `{DOMAIN}` — title, task counts, blocking decisions
2. **Pending tasks** from `workplans/` + any `[repo:{REPO_SLUG}]` hub tasks
3. **Goal guidance** — if `goal_guidance` in summary:
- `needs_workplan`: surface as top action — *"Repo goal '{title}' has no workplan yet"*
@@ -52,23 +52,31 @@ For each file with `status: ready`, `active`, or `blocked`, note pending
4. **Suggested next action** — highest-priority open item
5. **SBOM status** — flag if `last_sbom_at` is unset for this repo
If no workstreams: follow First Session Protocol (`first-session.md`).
If no workplans: follow First Session Protocol (`first-session.md`).
**During work:** `record_decision()` · `add_progress_event()` · `resolve_decision()`
> State Hub is a *read model*. Bootstrap tools (`create_workstream`, `create_task`)
> are First Session Protocol only. Work structure belongs in repo files (ADR-001).
> State Hub is a *read model*. **Never register workplans or tasks by hand**
> (`create_workplan`, `create_task`, or the legacy `create_workstream`) — write
> the workplan file in `workplans/` and run `fix-consistency`; its C-06 check
> registers the workplan and its tasks in the hub and writes the IDs back into
> the file. Manual registration creates duplicates the moment fix-consistency
> runs. Work structure belongs in repo files (ADR-001).
>
> Terminology: "workstream" is the legacy name for workplan. Some API/frontmatter
> field names keep it for compatibility (`state_hub_workstream_id`,
> `workstream_id` params) — treat them as workplan IDs.
**Session close:**
With MCP tools:
```
add_progress_event(summary="...", topic_id="{TOPIC_ID}", workstream_id="<uuid>")
add_progress_event(summary="...", topic_id="{TOPIC_ID}", workplan_id="<uuid>")
```
Without MCP tools:
```bash
curl -s -X POST http://127.0.0.1:8000/progress/ \
-H "Content-Type: application/json" \
-d '{"topic_id":"{TOPIC_ID}","workstream_id":"<uuid>","event_type":"note","summary":"what changed","author":"codex"}'
-d '{"topic_id":"{TOPIC_ID}","workplan_id":"<uuid>","event_type":"note","summary":"what changed","author":"codex"}'
```
If workplan files were modified, ensure the local copy is up to date first,
then sync from the repo checkout:

View File

@@ -5,7 +5,7 @@ ID prefix: `{WP_PREFIX}-`
Work items originate as files in this repo **before** being registered in the hub.
Canonical workplan/workstream frontmatter statuses are:
Canonical workplan frontmatter statuses are:
`proposed`, `ready`, `active`, `blocked`, `backlog`, `finished`, `archived`.
Use `proposed` for a newly drafted plan, `ready` after review against current
repo state, and `finished` when implementation is complete. `stalled` and
@@ -16,14 +16,15 @@ prefix: `YYMMDD-{WP_PREFIX}-NNNN-<slug>.md`. The frontmatter id remains
unchanged; the prefix is only for quick visual reference.
Small opportunistic tasks discovered during another session use **Ad Hoc Tasks**:
`workplans/ADHOC-YYYY-MM-DD.md`, workstream slug `adhoc-YYYY-MM-DD`, and task ids
`workplans/ADHOC-YYYY-MM-DD.md`, workplan slug `adhoc-YYYY-MM-DD`, and task ids
`ADHOC-YYYY-MM-DD-T01`, `T02`, etc. Use adhocs only for low-risk work completed
directly. Promote anything requiring analysis, design, approval, dependencies, or
multiple planned phases into a normal workplan.
Ecosystem todos from other agents arrive as `[repo:{REPO_SLUG}]` hub tasks —
visible at session start. Pick one up by creating the workplan file, then registering
the workstream.
visible at session start. Pick one up by creating the workplan file, committing,
and running `statehub fix-consistency` — C-06 registers the workplan in the hub.
Never register by hand with `create_workplan`/`create_workstream`.
Task blocks use this shape:
@@ -37,4 +38,8 @@ state_hub_task_id: "<uuid>" # written by fix-consistency — do not edit
Status progression is `todo` → `progress` → `done`; use `wait` for waiting or
blocked work and `cancel` for stopped work.
Workplan frontmatter carries `state_hub_workstream_id` — a legacy field name
kept for compatibility ("workstream" is the old term for workplan); it holds
the hub workplan id and is written by fix-consistency. Do not edit or rename it.
<!-- Ralph Loop rules and HEUREKA sequence: ~/.claude/CLAUDE.md — do not duplicate here -->

View File

@@ -27,6 +27,10 @@ def fetch(path: str):
EXTENSION_MARKER = "<!-- REPO-AGENTS-EXTENSIONS -->"
# Rule files that repos fill in with local content; only (re)write them while
# they still contain the template's TODO markers.
PRESERVE_IF_CUSTOMIZED = {"stack-and-commands", "repo-boundary", "architecture"}
def render(template: str, values: dict[str, str]) -> str:
for key, value in values.items():
@@ -188,7 +192,13 @@ def update_repo(
rules_dir = path / ".claude" / "rules"
rules_dir.mkdir(parents=True, exist_ok=True)
for name, template in rule_templates.items():
(rules_dir / f"{name}.md").write_text(render(template, values), encoding="utf-8")
target = rules_dir / f"{name}.md"
if name in PRESERVE_IF_CUSTOMIZED and target.exists():
# These files start as TODO templates and get filled per repo;
# never overwrite a filled-in version with the blank template.
if "TODO" not in target.read_text(encoding="utf-8"):
continue
target.write_text(render(template, values), encoding="utf-8")
return f"{repo_slug}\t{path}\t{prefix}"