Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 30 additions & 41 deletions .claude/hooks/issues-surface.sh
Original file line number Diff line number Diff line change
@@ -1,91 +1,80 @@
#!/usr/bin/env bash
# SessionStart hook — surface the outstanding-work memory into context.
# SessionStart hook — surface the universal task ledger into context.
#
# Reads docs/outstanding-issues.md (the /issues ledger) and prints a compact,
# glanceable summary of the OPEN items so every session starts already aware of
# what is outstanding. When the trigger is a context reset (compact / resume /
# clear) it also emits a reminder to run `/issues capture` — that is the moment
# a session's in-flight follow-ups are most likely to be lost.
# Reads docs/outstanding-issues.md and prints a compact summary of the active Prioritised queue.
# On compact/resume/clear it also reminds the agent to capture new follow-ups.
#
# Contract: READ-ONLY. Never writes, never commits, never fails a session — it
# always exits 0, and every step is guarded so a parse error just yields less
# output. SessionStart hook stdout is injected into the model's context.
# Contract: READ-ONLY. Never writes, never commits, never fails a session.
set -uo pipefail

# --- locate the repo + ledger -------------------------------------------------
root="${CLAUDE_PROJECT_DIR:-}"
[ -z "$root" ] && root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
[ -z "$root" ] && root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." 2>/dev/null && pwd || true)"
ledger="$root/docs/outstanding-issues.md"
[ -f "$ledger" ] || exit 0

# --- read the hook payload (stdin JSON) to learn the trigger source -----------
payload="$(cat 2>/dev/null || true)"
source_val="$(printf '%s' "$payload" \
| grep -o '"source"[[:space:]]*:[[:space:]]*"[^"]*"' \
| head -n1 | sed -E 's/.*"([^"]*)"$/\1/')"

# --- parse the "Open items" table only ---------------------------------------
# Emit "PRI<TAB>ID<TAB>TYPE<TAB>SUMMARY" per open row. Scoped between the
# "## Open items" heading and the next "## " heading so the Resolved/archive
# table (different columns) is never counted.
# Emit ACUITY<TAB>ORDER<TAB>SOURCE<TAB>OUTCOME for active rows only.
rows="$(awk '
/^## Open items/ { inopen=1; next }
/^## / { if (inopen) inopen=0 }
inopen && /^\| #[0-9]/ {
/^## Prioritised queue/ { inqueue=1; next }
/^## / { if (inqueue) inqueue=0 }
inqueue && /^\|[[:space:]]*[0-9]+[[:space:]]*\|/ {
n=split($0, c, "|")
id=c[2]; pri=c[3]; typ=c[4]; sum=c[5]
gsub(/^[ \t]+|[ \t]+$/, "", id)
gsub(/^[ \t]+|[ \t]+$/, "", pri)
gsub(/^[ \t]+|[ \t]+$/, "", typ)
gsub(/^[ \t]+|[ \t]+$/, "", sum)
printf "%s\t%s\t%s\t%s\n", pri, id, typ, sum
ord=c[2]; src=c[3]; outcome=c[4]; acuity=c[5]
gsub(/^[ \t]+|[ \t]+$/, "", ord)
gsub(/^[ \t]+|[ \t]+$/, "", src)
gsub(/^[ \t]+|[ \t]+$/, "", outcome)
gsub(/^[ \t]+|[ \t]+$/, "", acuity)
printf "%s\t%s\t%s\t%s\n", acuity, ord, src, outcome
}
' "$ledger" 2>/dev/null || true)"

total="$(printf '%s' "$rows" | grep -c . || true)"
if [ "${total:-0}" -eq 0 ]; then
echo "[issues] Outstanding-work memory (docs/outstanding-issues.md): no open items. Record one with /issues add …"
echo "[issues] Universal task ledger (docs/outstanding-issues.md): no active recommended work. Record one with /issues add …"
exit 0
fi

group() { printf '%s\n' "$rows" | awk -F'\t' -v p="$1" '$1==p'; }
count() { printf '%s' "$1" | grep -c . || true; }
p1="$(group P1)"; p2="$(group P2)"; p3="$(group P3)"
c1="$(count "$p1")"; c2="$(count "$p2")"; c3="$(count "$p3")"
a1="$(group A1)"; a2="$(group A2)"; a3="$(group A3)"; optional="$(group Optional)"
c1="$(count "$a1")"; c2="$(count "$a2")"; c3="$(count "$a3")"; co="$(count "$optional")"

echo "[issues] Outstanding-work memory — ${total} open (${c1}×P1, ${c2}×P2, ${c3}×P3). Source of truth: docs/outstanding-issues.md · read the full list back with /issues."
echo "[issues] Universal task ledger — ${total} active (${c1}×A1, ${c2}×A2, ${c3}×A3, ${co} optional). Source of truth: docs/outstanding-issues.md · read the full queue with /issues."

print_group() { # $1=rows $2=max-to-list
local data="$1" limit="$2" shown=0 more=0 pri id typ sum
print_group() {
local data="$1" limit="$2" shown=0 more=0 acuity ord src outcome
[ -z "$data" ] && return 0
while IFS=$'\t' read -r pri id typ sum; do
[ -z "$pri" ] && continue
while IFS=$'\t' read -r acuity ord src outcome; do
[ -z "$acuity" ] && continue
if [ "$shown" -lt "$limit" ]; then
echo " ${pri} ${id} ${typ} — ${sum}"
echo " ${ord}. ${acuity} ${src} — ${outcome}"
shown=$((shown + 1))
else
more=$((more + 1))
fi
done <<EOF
$data
EOF
[ "$more" -gt 0 ] && echo " … +${more} more at this priority (see /issues)"
[ "$more" -gt 0 ] && echo " … +${more} more at this acuity (see /issues)"
return 0
}

# P1 = do-next, list all. P2 = should-do, list up to 8. P3 = collapse to a count.
[ "$c1" -gt 0 ] && print_group "$p1" 999
[ "$c2" -gt 0 ] && print_group "$p2" 8
[ "$c3" -gt 0 ] && echo " ${c3} × P3 (nice-to-have / revisit-when) — see /issues"
[ "$c1" -gt 0 ] && print_group "$a1" 999
[ "$c2" -gt 0 ] && print_group "$a2" 8
[ "$c3" -gt 0 ] && echo " ${c3} × A3 (planned/triggered) — see /issues"
[ "$co" -gt 0 ] && echo " ${co} × Optional — see /issues"

# --- capture reminder ---------------------------------------------------------
case "$source_val" in
compact | resume | clear)
echo "[issues] Context was just reset (${source_val}). Before this session wraps up, run /issues capture to record any new follow-ups, deferrals, or risks that surfaced so they aren't lost from memory."
echo "[issues] Context was just reset (${source_val}). Before this session wraps up, run /issues capture so new follow-ups are not lost."
;;
*)
echo "[issues] When the work in this session wraps up, offer to run /issues capture for any new follow-ups before the context is lost."
echo "[issues] When this session wraps up, offer /issues capture for any new follow-ups."
;;
esac
exit 0
43 changes: 24 additions & 19 deletions .claude/skills/issues/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ name: issues
description: Track and recall all outstanding tasks, recommendations, and issues for this repo as durable cross-session memory. Use when the user types "/issues" (state the open items back), or asks to add/close/update/capture an outstanding task, recommendation, or issue. The memory lives in docs/outstanding-issues.md; a plain "/issues" is read-only.
---

# issues — the outstanding-work memory
# issues — the universal task ledger

`docs/outstanding-issues.md` is the durable, cross-session memory of everything still outstanding:
open **tasks**, **recommendations** not yet acted on, and **issues** not yet resolved. Chat context
resets; that file does not. This skill reads it back and keeps it current.
`docs/outstanding-issues.md` is the single durable, cross-session ledger. Its **Prioritised queue**
is the authoritative list of work still recommended; the evidence register and resolved archive in
the same file preserve supporting detail and history. Chat context resets; that file does not.

**The ledger is the source of truth, not chat memory.** Never answer `/issues` from conversation
recall — always read the file first, so the answer is correct even in a fresh session.
Expand All @@ -20,25 +20,27 @@ recall — always read the file first, so the answer is correct even in a fresh
## Default: `/issues` (read-only)

1. Read `docs/outstanding-issues.md`.
2. State the **open items** back, grouped by priority (P1 → P3), each as
`#ID · type · summary — next action (source)`.
3. End with a one-line count, e.g. `5 open: 0×P1, 3×P2, 2×P3 · 0 resolved this session`.
2. State the **Prioritised queue** back in execution order. Include source/ID, outcome, acuity,
timing, dependency or approval, and stop rule concisely.
3. End with counts by acuity (`A1`, `A2`, `A3`, `Optional`) and the total active rows.
4. Do **not** mutate the file or commit on a plain read.

If a filter is given, narrow step 2: `/issues P1` (by priority), `/issues issues` / `/issues recs`
/ `/issues tasks` (by type), `/issues <keyword>` (summary/detail substring match).
If a filter is given, narrow step 2: `/issues A2`, `/issues Optional`, `/issues <ID/source>`, or
`/issues <keyword>` (queue outcome/evidence substring match).

## Mutating subcommands

Parse the intent from natural language too — the exact syntax is a convenience, not a requirement.

- **`/issues add <text>`** — append a row to **Open items**. Infer `Pri`/`Type` from the text
(ask only if genuinely ambiguous; default `P2`/`task`). Allocate the ID from the
`<!-- issues:next-id=NNN -->` marker, then bump that marker. Fill `Source` with
`session <today>` unless the user names one; `Added` is today's date.
- **`/issues done <id> [outcome]`** — move that row from **Open items** to **Resolved / archive**
with today's date and a one-line outcome. Archive, never delete.
- **`/issues update <id> <text>`** — edit an open row's summary or next action in place.
- **`/issues add <text>`** — add one row to the **Prioritised queue** with order, source/ID,
outcome, acuity, capability, timing, effort, dependencies/approvals, and success/stop rule. Add a
supporting evidence-register row when the task needs a durable issue ID. Allocate that ID from
`<!-- issues:next-id=NNN -->`, bump the marker, and use `session <today>` as the source when none
is supplied.
- **`/issues done <id-or-source> [outcome]`** — remove the active queue row and move/update its
evidence row under **Resolved / archive** with today's date and a one-line outcome. Archive,
never erase evidence.
- **`/issues update <id-or-source> <text>`** — update the queue row and its evidence row together.
- **`/issues capture`** — scan the current session for recommendations, follow-ups, deferrals, and
unfixed problems that surfaced but were not recorded. Propose them as a numbered list and add the
confirmed ones (dedupe against existing rows first — do not re-add something already tracked).
Expand All @@ -52,16 +54,19 @@ paragraph; put the smallest next action in **Detail / next action**.

## Writing rules

- Keep the table format and column order exactly as in `docs/outstanding-issues.md`. One row per item.
- Keep the queue and evidence table formats exactly as in `docs/outstanding-issues.md`. One queue
row per active work order.
- Renumber the active queue when sequencing changes; evidence IDs remain monotonic and never change.
- An evidence-register row is not active work unless it also appears in the Prioritised queue.
- IDs are monotonic and never reused — always allocate from the `issues:next-id` marker and bump it.
- Escape `|` inside cell text (write `\|`) so the markdown table stays intact.
- Respect the repo's RAG/clinical/privacy flagging rules if an item _itself_ touches a protected
surface — recording it here is fine, but acting on it later still needs the usual gate.

## Persist the memory (commit)

After any mutation, stage and commit **only** `docs/outstanding-issues.md` so the memory survives the
ephemeral container and other worktrees:
After a ledger-only mutation, stage and commit **only** `docs/outstanding-issues.md` so the memory
survives the ephemeral container and other worktrees:

```
git add docs/outstanding-issues.md
Expand Down
14 changes: 12 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -454,9 +454,19 @@ Run the matching planner command in `docs/productivity-workflows.md` without sid
**recommendation**, and **issue** for this repo. Chat context resets between sessions; that file does
not, so anything worth remembering after a session ends belongs there.

`docs/outstanding-issues.md` is the single universal task ledger. Its active queue owns dependency
order, acuity, required capability, timing, effort, approvals, success criteria, verification, and
stop rules; the same file preserves evidence, decisions, and resolution history. Detailed provider
runbooks may remain in `docs/operator-backlog.md`, but every provider action still recommended must
also appear in the active queue. Update the affected sections together when work is completed,
dropped, superseded, or materially re-scoped. Never copy completed, stale, duplicate, speculative,
or rejected work back into the active queue.

- When the user types `/issues`, invoke the `issues` skill (`.claude/skills/issues/SKILL.md`): read
`docs/outstanding-issues.md` and state the open items back, grouped by priority. A plain `/issues`
is read-only — it mutates and commits nothing.
`docs/outstanding-issues.md` and state the **Prioritised queue** back in execution order, including
source/ID, acuity, timing, dependencies/approvals, and stop rule. Evidence-register rows are not
active work unless they also appear in that queue. A plain `/issues` is read-only — it mutates
and commits nothing.
- `/issues add|done|update|capture …` mutate the ledger; each mutation commits **only**
`docs/outstanding-issues.md` (no push unless the user asks or you are already handing off).
- Proactively offer to `capture` unresolved follow-ups, deferrals, and known risks into the ledger
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ npm run docs:check-links

## Plans and workstreams (living)

- [outstanding-issues.md](outstanding-issues.md) — single universal task ledger, prioritised execution queue, evidence and resolution memory
- [maturity-backlog-workorders.md](maturity-backlog-workorders.md) — actionable work orders tracking the repository-maturity audit backlog
- [framework-dependency-modernization-checklist.md](framework-dependency-modernization-checklist.md) — ordered Next.js 16, runtime, dependency, Turbopack, and verification migration program
- [search-rag-master-plan.md](search-rag-master-plan.md) / [search-rag-master-context.md](search-rag-master-context.md) — search/RAG roadmap and shared context
Expand Down
Loading
Loading