Dev Doc Update¶
Review current-session code changes, cross-check against the knowledge store, update units that have gone stale or need creating, then validate store integrity. Enforces scan → propose → confirm → apply → validate → report so doc drift cannot be silently skipped and broken cross-refs cannot silently ship.
Workflow name: dev-document
Execution: main
Slash command: /wb-dev-document
Override not allowed
Steps¶
| # | ID | Name | Type | Depends on |
|---|---|---|---|---|
| 1 | scan |
Scan current changes and candidate knowledge units | code |
|
| 2 | propose |
Propose knowledge-store edits (updates + new units) | reasoning |
scan |
| 3 | confirm |
Confirm proposals with the user | reasoning |
propose |
| 4 | apply |
Apply accepted edits via docs_ / workflow_ capabilities | code |
confirm |
| 5 | validate |
Validate store integrity after edits | code |
apply |
| 6 | report |
Report what changed and flag follow-ups | reasoning |
validate |
Step instructions¶
scan¶
Auto-run. The conductor calls work_buddy.dev.document.scan_changes(base_ref="HEAD") and wires the result into the next step. You don't invoke this — it returns:
- changed_files: repo-relative paths of uncommitted + untracked files
- classified: files grouped by bucket (module / knowledge / slash / tests / config / other)
- subsystem_slugs: module keys derived from the changed paths (e.g. obsidian/tasks, namespace_suggest)
- candidate_units: knowledge units whose text references a changed file or subsystem, ranked by match strength. First-pass net only — you must still do your own semantic searches in the next step.
- warnings: non-fatal issues (empty diff, direct JSON edits).
propose¶
Reasoning step. You're looking at the scan output and producing a list of concrete knowledge-store edits. You know the code you just wrote; the scan shows you which existing units textually reference it. Cross-check both, then fill the gap the scan cannot fill: semantic drift (the unit's prose still looks fine in isolation but describes a behavior you just changed).
Required actions¶
-
Read each candidate_unit at
depth="full"viaagent_docs(path=..., depth="full")that you haven't already loaded. Skim, decide: is this unit's content still accurate? Does it need new content added? -
Do 2-4 semantic searches against
agent_docs(query=...)for concepts your change touches that the scan may have missed. Examples: if you added a workflow, query"workflow authoring","how to add workflow", and the specific capability names. The scan is good at surfacing units that mention the changed file or its docstring vocabulary; ad-hoc semantic searches catch units that describe the same concept from a different angle (e.g. a directions unit that names the workflow rather than the implementation file). -
Check CLAUDE.md (the top-level instruction surface) for stale references: grep for keywords from your change. Stale entries in CLAUDE.md mislead every future agent.
-
Consider new units: if you added a new subsystem, capability cluster, or workflow, there may be nothing in the store that describes it yet. Propose
action: "create"with an appropriate path, kind, and content.
Field placement — content_full vs dev_notes¶
Every proposal must consciously route content between two body fields:
content_full— read by every agent (operational + dev) onagent_docs(depth="full"). Surfaces, semantic contracts, user-visible behavior.dev_notes— surfaced only when dev mode is on (dev_mode_toggle, auto-enabled by/wb-dev). Implementation patterns, snapshot/cache invariants, refactor footguns, decision rationale.
The default failure mode is dumping everything into content_full. Resist it. Operational agents reading this unit shouldn't have their context window filled with implementation detail they cannot act on. The decision test: "if an operational agent was calling this subsystem from a capability, would they want this in their context window?" Yes → content_full. No, only useful while editing the code → dev_notes.
When updating an existing unit, route new facts by their nature, not by which field you happen to be editing. Adding both a public surface and an internal pattern to the same unit is two fields entries on the same proposal: one for content_full, one for dev_notes.
See dev/dev-document-directions for the full criteria, anti-patterns, and worked examples.
Required output: units_loaded¶
Declare which candidate units you actually read at depth="full" during this step:
"units_loaded": ["path/to/unit1", "path/to/unit2", ...]
This isn't ceremony — it's the same orientation discipline /wb-dev enforces via its units_read field. The result_schema requires at least 3 entries, but the real intent is honesty: declare what you actually loaded so the next agent (or you, in the report step) can tell whether the proposal list is grounded in reading or guessed at from the candidate metadata. Empty or trivial lists are how skim drift gets in.
Advance with¶
{
"proposals": [
{
"action": "update" | "create" | "delete" | "no_op",
"path": "<unit path>",
"kind": "directions" | "system" | "workflow" (required for create),
"rationale": "<one-sentence why>",
"fields": {
/* for update: only the fields changing */
/* for create: all fields needed by docs_create or workflow_create */
"content_full": "...",
"dev_notes": "...",
"description": "...",
/* for workflow kind, include steps (as a list) and step_instructions (as a dict) */
}
}
]
}
An empty proposals: [] is a valid honest answer — if nothing is stale, don't invent work. But "I can't find anything" and "I didn't look" are different; you must have loaded at least the top candidate_units and done your semantic searches first.
Advance via wb_advance(workflow_run_id=..., step_result={...}). The parameter is step_result (not result) — FastMCP silently drops unknown kwargs.
confirm¶
Reasoning step. Present the proposed edits to the user in a compact, readable form. For each proposal show: action, path, one-sentence rationale, and for content updates a brief diff hint (what's being added / changed / removed — not the full prose).
Ask the user to accept, reject, or modify each. Batch the ask when the proposals are obviously independent; itemize when any single one might be contentious.
Advance with¶
Common case — user accepted all proposals as-is, OR declined entirely:
{"confirmed": true}
or, if declined:
{"confirmed": false}
Modified case — you (or the user) trimmed or edited the proposal list. Pass the modified list under final_proposals:
{"confirmed": true, "final_proposals": [/* the modified subset */]}
Do NOT pass final_proposals when it's identical to propose.proposals — that just round-trips a potentially large list through the response. The apply step reads from step_results.propose.proposals by default; only override when you actually changed the list.
Advance via wb_advance(workflow_run_id=..., step_result={...}). The parameter is step_result (not result) — FastMCP silently drops unknown kwargs.
apply¶
Code step. The proposals to apply come from one of two places:
- If
step_results.confirm.final_proposalsis present (agent modified the list), use it. - Otherwise, read
step_results.propose.proposals(the unmodified list from the propose step).
If step_results.confirm.confirmed is false, skip everything and return {applied: [], failed: [], skipped: [<all paths from propose.proposals>], message: "User declined; no edits applied."}.
Otherwise, iterate the proposals. For each:
action: "update"+kindin {directions, system} →docs_update(path=..., **fields)action: "create"+kindin {directions, system} →docs_create(path=..., kind=..., name=..., description=..., **fields)action: "update"+kind == "workflow"→workflow_update(path=..., **fields)(serializestepsandstep_instructionsas JSON strings)action: "create"+kind == "workflow"→workflow_create(path=..., name=..., description=..., workflow_name=..., steps=..., **fields)action: "delete"→docs_delete(path=...)action: "no_op"→ skip.
Fault tolerance: one failure does not abort the loop. Collect results as {applied: [path, ...], failed: [{path, error}, ...], skipped: [path, ...]} and return them.
Advance via wb_advance(workflow_run_id=..., step_result={...}). The parameter is step_result (not result) — FastMCP silently drops unknown kwargs.
validate¶
Auto-run. After edits land, the conductor calls work_buddy.knowledge.validate.docs_validate() with no args (all checks: dag_integrity, command_mapping, thinned_commands, store_path_validity, required_fields, directions_fields, kind_specific_fields, parent_child_symmetry). Returns:
- passed (bool): true iff every check found zero errors.
- failed (int): total error count across all checks.
- summary ({check_name: count}): errors per check type.
- errors (list[{check, path, message}]): the individual problems, if any.
- total_units, checks_run: run metadata.
You don't invoke this — it runs automatically. The next (report) step is where you surface any failures to the user.
Why this gate exists: doc edits are easy to get structurally wrong (orphaned parents, typo'd path references, directions unit missing a trigger, slash command pointing at a non-existent unit). Catching these right after apply — not on the next unrelated commit — keeps drift localized.
report¶
Reasoning step. Short summary of what changed:
- Count of applied / failed / skipped edits (from apply).
- Validation outcome (from validate): if passed: false, enumerate the errors by check type and path. These are structural problems you introduced and need to resolve before committing — they are not stylistic warnings.
- For any CLAUDE.md or similar non-store files the user still needs to edit manually, flag them explicitly (the store capabilities don't touch CLAUDE.md).
If validation failed, recommend concrete next actions to the user: typically a follow-up docs_update or workflow_update to fix the referenced path/field. Do NOT treat validation failures as cosmetic; they indicate the store is in a broken state.
Do NOT re-summarize the code change itself — the user already knows what they did. Focus on doc hygiene + validation outcomes.
Advance via wb_advance(workflow_run_id=..., step_result={...}). The parameter is step_result (not result) — FastMCP silently drops unknown kwargs.
Context¶
Reviews the agent's current-session code changes and updates the knowledge store to match. The workflow gives the agent a deterministic starting set (changed files, classified subsystems, and knowledge units that textually reference them) via the auto_run scan step, then asks the agent to supplement that with semantic searches and user-knowledge about what was changed. After the agent applies edits, an auto_run validate step checks the store's structural integrity so broken refs cannot silently ship.
Philosophy¶
Stale documentation is worse than missing documentation — it actively misleads. Agents in dev mode have a recurring failure mode: making behavioral changes and forgetting to update the units that describe that behavior. This workflow makes doc hygiene a step with a DAG gate, not a prose item in a checklist that gets skipped under time pressure.
Broken docs are a parallel failure mode: the agent updates a unit's parents or workflow field and introduces a dangling reference, or mints a new slash command without a matching directions unit. These are easy to create and hard to notice until someone else hits them. The validate step runs docs_validate right after apply so the blast radius stays confined to the same edit pass.
What the agent must bring¶
The workflow's intelligence is in the agent, not the scan. Specifically:
- Which of the candidate units are actually semantically stale (the scan ranks by lexical+semantic match, but it can't tell whether a unit describes a behavior you just changed).
- What new units (if any) should be created for subsystems the scan cannot know are new.
- Which body field each new fact belongs in — content_full (every agent) vs dev_notes (dev-mode-only). Conscious routing between the two is the structural mechanism for the operational/developmental separation; the propose step's instruction enforces it inline, and dev/dev-document-directions carries the full criteria.
- Whether CLAUDE.md or similar top-level instruction surfaces need manual updates — those live outside the knowledge store.
- How to resolve any validation failures surfaced by the validate step — typically with a follow-up docs_update or workflow_update to fix the referenced path.
What this workflow is NOT¶
- Not a commit gate by itself —
/wb-dev-prwraps it for that. - Not a substitute for the agent's own judgment about whether an edit is worth making; empty
proposals: []is fine when nothing is stale. - Not a lint pass —
validatecatches structural breakage (missing required fields, DAG violations, dangling refs), not prose quality.