From ea1fd234812ff6f19de2583bd86aed2af0a8b9dd Mon Sep 17 00:00:00 2001 From: tegwick Date: Thu, 2 Jul 2026 01:47:47 +0200 Subject: [PATCH] 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 --- mcp_server/server.py | 6 +++-- scripts/project_rules/agents-codex.template | 12 ++++----- .../project_rules/credential-routing.template | 2 +- scripts/project_rules/first-session.template | 26 +++++++++++-------- .../project_rules/session-protocol.template | 20 +++++++++----- .../workplan-convention.template | 13 +++++++--- scripts/update_agent_instruction_files.py | 12 ++++++++- 7 files changed, 60 insertions(+), 31 deletions(-) diff --git a/mcp_server/server.py b/mcp_server/server.py index c571fe6..4287d37 100644 --- a/mcp_server/server.py +++ b/mcp_server/server.py @@ -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, diff --git a/scripts/project_rules/agents-codex.template b/scripts/project_rules/agents-codex.template index f48ed78..7e379dc 100644 --- a/scripts/project_rules/agents-codex.template +++ b/scripts/project_rules/agents-codex.template @@ -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": "", + "workplan_id": "", "task_id": "" }' ``` -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/" \ ## 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: "" # written by fix-consistency — do not edit +state_hub_workstream_id: "" # written by fix-consistency — do not edit (legacy name; holds the workplan id) --- ``` diff --git a/scripts/project_rules/credential-routing.template b/scripts/project_rules/credential-routing.template index 68ec02c..b1e18fd 100644 --- a/scripts/project_rules/credential-routing.template +++ b/scripts/project_rules/credential-routing.template @@ -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 diff --git a/scripts/project_rules/first-session.template b/scripts/project_rules/first-session.template index 2f2da88..9aabb6d 100644 --- a/scripts/project_rules/first-session.template +++ b/scripts/project_rules/first-session.template @@ -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 1–3 workstreams — each a coherent strand, weeks to months, anchored to a +**Step 3 — Propose workplans to Bernd** +Propose 1–3 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-.md ← write this first +workplans/{WP_PREFIX}-NNNN-.md ← write this, commit it ``` -Then register in the hub: -``` -create_workstream(topic_id="{TOPIC_ID}", title="...", owner="...", description="...") -create_task(workstream_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} ) ``` diff --git a/scripts/project_rules/session-protocol.template b/scripts/project_rules/session-protocol.template index bad3f75..bc5d40d 100644 --- a/scripts/project_rules/session-protocol.template +++ b/scripts/project_rules/session-protocol.template @@ -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="") +add_progress_event(summary="...", topic_id="{TOPIC_ID}", workplan_id="") ``` 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":"","event_type":"note","summary":"what changed","author":"codex"}' + -d '{"topic_id":"{TOPIC_ID}","workplan_id":"","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: diff --git a/scripts/project_rules/workplan-convention.template b/scripts/project_rules/workplan-convention.template index c0e27df..ff22391 100644 --- a/scripts/project_rules/workplan-convention.template +++ b/scripts/project_rules/workplan-convention.template @@ -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-.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: "" # 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. + diff --git a/scripts/update_agent_instruction_files.py b/scripts/update_agent_instruction_files.py index e2522f2..cf1ec57 100644 --- a/scripts/update_agent_instruction_files.py +++ b/scripts/update_agent_instruction_files.py @@ -27,6 +27,10 @@ def fetch(path: str): EXTENSION_MARKER = "" +# 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}"