From 900f955e32e826c9376737c470b5898d719998cb Mon Sep 17 00:00:00 2001 From: Tom Kaczocha Date: Fri, 7 Aug 2026 13:14:07 +1000 Subject: [PATCH] fix(ux): scannable status, accurate help next steps, copy-paste go Operator command surface friction: status dumped full archive and stale in-progress headers; bare /do-work suggested capture on drained projects; capture/go fallbacks used non-actionable next-step copy; help opened on a 25-row command wall without a primary loop. --- SKILL.md | 13 +++++++ agents/capture.md | 2 +- agents/go.md | 2 +- agents/help.md | 28 ++++++++++++--- agents/status.md | 2 ++ docs/commands.md | 2 +- docs/getting-started.md | 2 +- lib/synth-status.sh | 57 ++++++++++++++++++++++++------ lib/tests/synth-status.test.sh | 64 ++++++++++++++++++++++++++++++++++ 9 files changed, 154 insertions(+), 18 deletions(-) diff --git a/SKILL.md b/SKILL.md index 3b6528b..b5b70e6 100644 --- a/SKILL.md +++ b/SKILL.md @@ -18,6 +18,19 @@ Start → Go. (Or granular: Intake → Capture → Verify → Run.) Work-item storage is pluggable (`tracker.backend`: **markdown** default, or **linear**). Runtime/git (worktrees, merges, state locks, `config.yml`) always stay local. +## Primary loop + +Most days you only need these: + +| Command | What it does | +|---------|-------------| +| `/do-work start [brief]` | Record a brief and build the REQ backlog (ideate on by default; auto-installs). | +| `/do-work go [UR-NNN]` | Verify coverage, then audit + run when confidence ≥ threshold (default 90%). | +| `/do-work status [UR-NNN]` | Live situation room: in-flight, backlog, recent done, coverage. | +| `/do-work` | Help + suggested next steps for this project. | + +Flags for start/go (`--no-ideate`, `--force`, `--auto-fix`, …) are in the full table below. + ## Quick Reference | Command | What it does | diff --git a/agents/capture.md b/agents/capture.md index 28a60e1..e85385c 100644 --- a/agents/capture.md +++ b/agents/capture.md @@ -849,7 +849,7 @@ If `config.next_steps.enabled` is `true` **and** this agent is running standalon 2. **"Run Go"** — Skip to verify + run in one shot 3. **"Skip"** — End the interaction -If `config.next_steps.enabled` is `false`, missing, or this agent is running as a delegate inside start: output "Next step: run verify to check coverage, or run the loop to start executing." and stop. +If `config.next_steps.enabled` is `false`, missing, or this agent is running as a delegate inside start: output `Next step: /do-work go UR-NNN to verify coverage and run.` (substitute the real UR number) and stop. --- diff --git a/agents/go.md b/agents/go.md index e164fa8..ec42e0d 100644 --- a/agents/go.md +++ b/agents/go.md @@ -161,7 +161,7 @@ If `config.next_steps.enabled` is `true`: The go agent is a top-level orchestrator — it is never a delegate, so no suppression logic is needed. Sub-agents (verify, run, log) must suppress their own AskUserQuestion prompts when running inside go. -If `config.next_steps.enabled` is `false` or missing: skip the AskUserQuestion and stop. +If `config.next_steps.enabled` is `false` or missing: output `Next step: /do-work status` (or `/do-work start "…" for new work) and stop. --- diff --git a/agents/help.md b/agents/help.md index 65c4a46..b4d5cd2 100644 --- a/agents/help.md +++ b/agents/help.md @@ -88,14 +88,34 @@ path-unit backlog detected: top-level path REQs define reachable flows; child RE **If URs exist but backlog is empty:** +Do **not** treat every empty-backlog project the same. Distinguish URs that still need decomposition from a drained project whose REQs already live in `archive/` (or Linear done): + +1. Find the **most recent** `UR-NNN` under `user-requests/` (highest N). +2. Check whether **any** REQ for that UR exists in backlog, `working/`, or `archive/` (markdown: `**UR:** UR-NNN` on REQ files; Linear: `list_reqs_for_ur`). +3. Also scan older open URs for any with **zero** REQs anywhere — those still need capture. + +**A — Latest UR has no REQs yet (or any open UR has zero REQs):** + +``` +Suggested next steps: + /do-work capture UR-NNN — Decompose the request into tasks + /do-work go UR-NNN — Verify and run after capture + /do-work start "describe your feature or task" — Record a new brief instead +``` + +Prefer the **oldest** zero-REQ open UR for the capture line when more than one exists; otherwise use the latest UR. Replace `UR-NNN` with that real number. + +**B — Backlog empty and all open URs already have REQs (drained / archive-only):** + ``` Suggested next steps: - /do-work capture UR-NNN — Decompose the latest request into tasks - /do-work go UR-NNN — Verify and run for a specific request /do-work start "describe your feature or task" — Record a new brief + /do-work status — Review the situation room ``` -Replace `UR-NNN` with the most recent UR number. +Do **not** suggest `capture` for a UR that already has REQs in archive — that re-decomposes finished work and confuses operators. + +Then (when the 4-suggestion cap allows) still add retro / close from the heuristics below. **If `runs/` has entries but `.do-work/state/calibration.md` does not exist:** @@ -107,7 +127,7 @@ Suggest retro alongside other applicable suggestions (do not replace them — ad **If archived path-unit REQs exist for a UR but that UR has no `closure.md`:** -Suggest close alongside other applicable suggestions (add it when this condition is true and the 4-suggestion cap allows). Use the most recently completed UR: +Suggest close alongside other applicable suggestions (add it when this condition is true and the 4-suggestion cap allows). Use the most recently completed UR that still needs closure: ``` /do-work close UR-NNN — Walk path-unit entry points end-to-end and write the UR closure report diff --git a/agents/status.md b/agents/status.md index 305a788..4339626 100644 --- a/agents/status.md +++ b/agents/status.md @@ -54,6 +54,8 @@ bash {skill-root}/lib/synth-status.sh [UR-NNN] # passes the optional scope Print stdout verbatim to the user. +Unscoped output prioritizes live work: backlog + working list fully; archive is capped to recent completed rows with a note when more exist. Scope with `UR-NNN` to list every matching archived REQ. Archive rows always show Status `done` even if a file header is stale. + If `$SKILL_ROOT/lib/synth-status.sh` is missing, report `"$SKILL_ROOT/lib/synth-status.sh not found — cannot render status."` and stop. Then render a proof-backed status view. Glob REQ files in backlog, `working/`, and `archive/` (respecting `UR-NNN` scope when provided), and run: diff --git a/docs/commands.md b/docs/commands.md index 95ccb53..e2c83ad 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -163,7 +163,7 @@ Does **not** run the verify confidence gate (unlike `go`). ### `/do-work status [UR-NNN]` -Read-only situation room: REQs, claimers (`hostname.pid`), heartbeats, deadlock warnings, coverage rollup. Optional UR scope. +Read-only situation room: live REQs first (working + backlog), claimers (`hostname.pid`), heartbeats, deadlock warnings, coverage rollup. Unscoped archive is capped to recent completed rows; pass `UR-NNN` to list every matching archived REQ. Use whenever something looks stuck or you are running parallel workers. diff --git a/docs/getting-started.md b/docs/getting-started.md index c927919..0946a20 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -163,7 +163,7 @@ Flags: /do-work status UR-001 ``` -Read-only situation room: backlog vs working vs archive, claimers, heartbeats, deadlock warnings, coverage rollup. +Read-only situation room: backlog vs working first, recent completed (archive capped when unscoped), claimers, heartbeats, deadlock warnings, coverage rollup. ## How you know it worked diff --git a/lib/synth-status.sh b/lib/synth-status.sh index c11bd24..126b111 100755 --- a/lib/synth-status.sh +++ b/lib/synth-status.sh @@ -13,9 +13,9 @@ # Sources: # - `.do-work/REQ-*.md` (Status: backlog) # - `.do-work/working/REQ-*.md` (Status: in-progress / stopped) -# - `.do-work/archive/REQ-*.md` (Status: done; only shown when -# present so the snapshot reflects -# completed work in this run) +# - `.do-work/archive/REQ-*.md` (Status: always rendered as done; +# unscoped runs cap archive rows so +# the situation room stays scannable) # 3. Empty case: a single "no REQs" message in place of the table when there # are zero REQs across all three buckets. # 4. Footer: deadlock warnings from `lib/deadlock-check.sh` if executable; @@ -51,6 +51,9 @@ ARCHIVE_DIR="$DOWORK/archive" CONFIG="$DOWORK/config.yml" DEFAULT_THRESHOLD=300 FOOTPRINT_MAX=60 +# Unscoped archive rows shown in the table (totals still count all archived). +# Scoped runs (`synth-status.sh UR-NNN`) list every matching archive row. +ARCHIVE_CAP=15 # Resolve sibling lib scripts via $0's directory so the script works from any # cwd inside the project. @@ -238,7 +241,8 @@ md_cell() { # Bucket: backlog | working | archive # Status column is derived from the file's own `**Status:**` field so that # `stopped` is rendered correctly when a worker has marked a working slot -# stopped. +# stopped — except archive rows, which always display as `done` (location is +# authoritative; stale in-progress headers in archive used to confuse operators). render_row() { local path="$1" local bucket="$2" @@ -259,8 +263,12 @@ render_row() { return 0 fi - status="$F_STATUS" - [ -z "$status" ] && status="—" + if [ "$bucket" = "archive" ]; then + status="done" + else + status="$F_STATUS" + [ -z "$status" ] && status="—" + fi layer="$F_LAYER" [ -z "$layer" ] && layer="—" @@ -383,8 +391,13 @@ fi printf '**Totals:** backlog=%d, working=%d, archived=%d\n\n' \ "$BACKLOG_N" "$WORKING_N" "$ARCHIVE_N" +# Idle project with history (unscoped only): no live work. Lead with a scan cue. +if [ -z "$UR_FILTER" ] && [ "$BACKLOG_N" -eq 0 ] && [ "$WORKING_N" -eq 0 ] && [ "$ARCHIVE_N" -gt 0 ]; then + printf '_No live work (backlog empty, nothing in-flight). Showing recent completed REQs only._\n\n' +fi + if [ "$TOTAL_N" -eq 0 ]; then - printf '_no REQs found in backlog, working, or archive._\n' + printf '_no REQs found in backlog, working, or archive. Next: `/do-work start "your brief"`._\n' else # Table header. printf '| REQ | UR | Status | Layer | Claimer | Heartbeat-age | Deps-status | Footprint |\n' @@ -403,9 +416,33 @@ else done fi if [ "$ARCHIVE_N" -gt 0 ]; then - for f in "${ARCHIVE_FILES[@]}"; do - render_row "$f" "archive" - done + # Unscoped: cap archive rows (newest REQ ids last in glob → take tail). + # Scoped (UR-NNN): list every matching archive row — the filter already + # narrows the set and operators asked for that UR deliberately. + if [ -n "$UR_FILTER" ]; then + for f in "${ARCHIVE_FILES[@]}"; do + render_row "$f" "archive" + done + else + archive_shown=0 + archive_start=0 + if [ "$ARCHIVE_N" -gt "$ARCHIVE_CAP" ]; then + archive_start=$(( ARCHIVE_N - ARCHIVE_CAP )) + fi + i=0 + for f in "${ARCHIVE_FILES[@]}"; do + if [ "$i" -ge "$archive_start" ]; then + render_row "$f" "archive" + archive_shown=$(( archive_shown + 1 )) + fi + i=$(( i + 1 )) + done + if [ "$ARCHIVE_N" -gt "$ARCHIVE_CAP" ]; then + hidden=$(( ARCHIVE_N - archive_shown )) + printf '\n_… and %d more archived. Scope with `/do-work status UR-NNN`, or inspect `.do-work/archive/`._\n' \ + "$hidden" + fi + fi fi fi diff --git a/lib/tests/synth-status.test.sh b/lib/tests/synth-status.test.sh index 2a033e6..283444f 100755 --- a/lib/tests/synth-status.test.sh +++ b/lib/tests/synth-status.test.sh @@ -282,6 +282,70 @@ if [ "$ELAPSED" -gt 10 ]; then fi teardown_fixture +# ---------------------------------------------------------------------- +# Case 8: unscoped archive is capped; totals still count all archived. +# ---------------------------------------------------------------------- +CURRENT_CASE="archive-cap" +CASES=$((CASES + 1)) +setup_fixture +i=1 +while [ "$i" -le 20 ]; do + # Zero-pad so glob order matches numeric order (REQ-01 … REQ-20). + id="$(printf 'REQ-%03d' "$i")" + write_archive_req "$TMP/.do-work/archive/${id}-a.md" "$id" "UR-020" + i=$((i + 1)) +done +run_synth +assert_eq "0" "$RC" "$CURRENT_CASE rc=0" +assert_contains "archived=20" "$STDOUT" "$CURRENT_CASE totals count all 20" +assert_contains "No live work" "$STDOUT" "$CURRENT_CASE idle cue" +assert_contains "and 5 more archived" "$STDOUT" "$CURRENT_CASE cap note" +assert_contains "REQ-020" "$STDOUT" "$CURRENT_CASE newest archive row shown" +assert_not_contains "REQ-001" "$STDOUT" "$CURRENT_CASE oldest archive row hidden" +teardown_fixture + +# ---------------------------------------------------------------------- +# Case 9: archive bucket always shows status done (stale header ignored). +# ---------------------------------------------------------------------- +CURRENT_CASE="archive-status-done" +CASES=$((CASES + 1)) +setup_fixture +cat > "$TMP/.do-work/archive/REQ-900-stale-header.md" <