---
name: backlog-report
description: "Generates a complete sprint-planning backlog as copy-pasteable markdown: all aspirations, goals, scores, blockers, and user action items, written to {agent}/BACKLOG.md with a compact terminal summary. Use whenever the user says \"show me the backlog\", \"give me sprint planning material\", \"what's in the queue\", or needs pasteable content for a planning doc or standup. User-invocable AND agent-callable; valid in any mode including reader."
user-invocable: true
triggers:
  - "/backlog-report"
tools_used: [Bash, Read, Write]
conventions: [aspirations, goal-schemas, goal-selection, pipeline]
minimum_mode: reader
revision_id: "skill-bootstrap-backlog-report-d7df00"
previous_revision_id: null
---

# /backlog-report — Sprint Planning Backlog

Generates a complete, copy-pasteable markdown backlog of all aspirations, goals,
scores, blockers, and user action items. Writes `agents/<agent>/BACKLOG.md` and displays a compact terminal summary.

**Hybrid skill**: user-invocable AND agent-callable. Valid from ANY state.
Safe: read-only with respect to agent state (only writes the output file).

## Sub-commands

```
/backlog-report              — Generate full backlog report
```

## Phase 0: Load Conventions

**Step 0: Load Conventions** — `Bash: load-conventions.sh` with each name from the `conventions:` front matter. Read only the paths returned (files not yet in context). If output is empty, all conventions already loaded — proceed to next step.

## Phase 1: Gather Data

Run these in parallel where possible:

```
1. Active aspirations (full detail)
   Bash: aspirations-read.sh --active
   → Parse JSON → store as aspirations[]

2. Scored goal rankings
   Bash: goal-selector.sh select
   → Parse JSON → store as scored_goals[]

3. Blocked goals with diagnostics
   Bash: goal-selector.sh blocked
   → Parse JSON → store as blocked_data

4. Pending questions (user review items)
   Read: agents/<agent>/session/pending-questions.yaml
   → Parse YAML → filter status == "pending" → store as pending_questions[]
   → IF file missing or empty: pending_questions = []

5. Active hypotheses
   Bash: pipeline-read.sh --stage active
   → Parse JSON → store as active_hypotheses[]

6. OHS-delta rollup per aspiration (g-245-03)
   Bash: meta-read.sh improvement-velocity.yaml
   → Parse YAML → for each entry, record {goal_id, ohs_delta_since_previous_ohs_run}
   → Build velocity_map[goal_id] = ohs_delta (float OR 'no_ohs_data')
   → IF field missing from entry: treat as 'no_ohs_data' (pre-schema entries)
```

## Phase 2: Build Indexes

```
1. SCORE MAP — For each goal in scored_goals[]:
     score_map[goal_id] = {score, breakdown, category, recurring, recurring_urgency}

2. BLOCKED MAP — For each goal in blocked_data.blocked_goals[]:
     blocked_map[goal_id] = {reason_group, block_detail}

3. USER GOALS — Scan all goals across aspirations[]:
     # Two passes: (a) all user-routed goals (for the historical-count note),
     # (b) only actionable goals (pending|deferred|blocked) for the dashboard.
     # Without the status filter, the renderer reports completed historical
     # records as "needing you" — observed pollution: 22 of 24 entries
     # were status=completed (g-115-210 / rb-526). Status filter applies
     # ONLY to the "Goals Needing You" surface in section 3b — completed
     # records remain queryable via aspirations-read.sh for audit.
     ACTIONABLE_STATUSES = {"pending", "in-progress", "deferred", "blocked"}
     all_user_goals = [g for g in <all goals> if "user" in g.participants]
     user_goals = [g for g in all_user_goals if g.status in ACTIONABLE_STATUSES]
     # Counts for the render-time note (used in section 3b):
     total_user_routed = len(all_user_goals)
     historical_user_routed = total_user_routed - len(user_goals)
     # Each user_goals entry includes: goal_id, aspiration_id, aspiration_title,
     # title, priority, score (from score_map), category, status

4. RECURRING HEALTH — Scan all goals across aspirations[]:
     IF goal.recurring == true:
       IF lastAchievedAt is null: overdue_by = infinity (never completed — always most overdue)
       ELSE: overdue_by = max(0, hours_since(lastAchievedAt) - interval_hours)
       Add to recurring_list[]: goal_id, title, interval_hours, lastAchievedAt, overdue_by, currentStreak
     Sort recurring_list by overdue_by descending

5. TESTABLE HYPOTHESES — Filter active_hypotheses[]:
     IF resolves_no_earlier_than is null/missing OR resolves_no_earlier_than <= today:
       add to testable_hypotheses[]
     Sort by confidence descending

6. OHS_DELTA_MAP — For each active aspiration, sum OHS deltas of its completed goals:
     ohs_delta_map[asp_id] = {"sum": float_or_na, "scored": int, "total_completed": int}
     FOR EACH asp in aspirations[]:
       numeric_deltas = []
       completed_count = 0
       FOR EACH goal in asp.goals WHERE status == "completed":
         completed_count += 1
         delta = velocity_map.get(goal.id, 'no_ohs_data')
         IF isinstance(delta, (int, float)):
           numeric_deltas.append(delta)
       IF numeric_deltas:
         ohs_delta_map[asp.id] = {"sum": round(sum(numeric_deltas), 2), "scored": len(numeric_deltas), "total_completed": completed_count}
       ELSE:
         ohs_delta_map[asp.id] = {"sum": "n/a", "scored": 0, "total_completed": completed_count}
     # Display: "+0.45 (3/12)" when scored > 0; "n/a" when no OHS-tracked goals yet
```

## Phase 3: Render Markdown

Construct the full markdown document. Use these section templates:

### 3a: Header

```markdown
# Backlog Report

> Generated: {YYYY-MM-DDTHH:MM:SS}
> {output of `bash core/scripts/learning-ratio.sh` — artifact ratio (guardrails+rb)}
> {output of `bash core/scripts/learning-ratio.sh --scope goals` — goal-scope ratio (pending+in-progress)}

---
```

The two ratios intentionally differ in shape: artifacts show accumulated
learning distribution, goals show the in-flight portfolio split. A session
can be rebalancing (goal ratio healthier than artifact ratio) or drifting
(goal ratio worse than artifact ratio) — both signals belong in the header.

### 3b: Your Action Items

Only show this section if pending_questions[] or user_goals[] are non-empty.

```markdown
## Your Action Items

### Pending Decisions

| ID | Date | Question | Agent's Default Action |
|----|------|----------|-----------------------|
| pq-001 | 2026-03-15 | I decided X because Y... | {default_action} |
```

IF pending_questions[] is empty: omit "Pending Decisions" subsection.

```markdown
### Goals Needing You

(showing {len(user_goals)} of {total_user_routed} total user-routed goals;
{historical_user_routed} are completed historical records)

| Goal | Aspiration | Title | Priority | Score | Category |
|------|------------|-------|----------|-------|----------|
| g-001-05 | asp-001 | Review deployment... | HIGH | 6.2 | infrastructure |
```

IF user_goals[] is empty: omit "Goals Needing You" subsection.
IF both are empty: omit entire "Your Action Items" section.
The historical-count note (line 2 above) is omitted when
historical_user_routed == 0 (clean state — every user-routed goal is
still actionable).

### 3c: Recommended Next Actions

Top 10 goals from scored_goals[] (by score descending).

```markdown
## Recommended Next Actions

| # | Goal | Aspiration | Title | Score | Priority | Category | Recurring |
|---|------|------------|-------|-------|----------|----------|-----------|
| 1 | g-001-01 | asp-001 | Reflect and journal | 8.7 | HIGH | maintenance | 4h |
| 2 | g-001-03 | asp-001 | Research API response... | 7.9 | MEDIUM | intelligence | — |
```

Recurring column: show interval (e.g., "4h", "24h", "0.25h") if recurring, "—" if not.
Title: truncate to 50 chars with "..." if longer.

### 3d: Recurring Health

Only show if any recurring goals exist.

```markdown
## Recurring Health

| Goal | Title | Interval | Last Done | Overdue By | Streak |
|------|-------|----------|-----------|------------|--------|
| g-001-04 | Check agent inbox | 0.25h | 14:30 today | 2.1h | 8 |
| g-001-01 | Reflect and journal | 4h | 10:09 today | — | 15 |
```

Overdue By: show hours if overdue, "—" if on time.
Last Done: show relative time (e.g., "14:30 today", "yesterday 18:00", date if older).
Sort by overdue_by descending (most overdue first), then on-time goals by next-due.

### 3e: Aspirations (one section per active aspiration)

```markdown
## asp-001: Maintain Agent Health [ACTIVE] (3/12 goals) | OHS Δ: +0.45 (3/12)

**Priority**: MEDIUM | **Motivation**: Keep the agent's knowledge fresh...

| Goal | Title | Status | Pri | Score | Category | Skill | Recur | Who |
|------|-------|--------|-----|-------|----------|-------|-------|-----|
| g-001-01 | Reflect and journal | pending | HIGH | 8.7 | maintenance | /reflect | 4h | agent |
| g-001-03 | Tree maintenance | pending | MED | 5.2 | maintenance | /tree maintain | 24h | agent |
| g-001-05 | Run test circuits | blocked | HIGH | BLK | testing | /some-domain-skill | — | agent |
```

Rules:
- Title: truncate to 40 chars with "..."
- Pri: abbreviated (HIGH, MED, LOW)
- Score: from score_map; "BLK" if blocked; "—" if terminal (completed/skipped/expired)
- Recur: interval string if recurring, "—" if not
- Who: comma-joined participants list, or "agent" if unset
- Sort order: in-progress first, then pending (by score desc), then blocked, then terminal
- Terminal goals (completed/skipped/expired): show as a summary count below the table,
  not as full rows. E.g., "3 completed, 1 skipped"
- Aspirations ordered by priority (HIGH → MEDIUM → LOW), then by asp-NNN id
- OHS Δ header format: `| OHS Δ: <sum> (<scored>/<total_completed>)` when scored > 0;
  `| OHS Δ: n/a` when no goals have numeric deltas (e.g. aspirations closed before
  ohs-trend.jsonl started populating). Sum format: signed 2-decimal float (e.g.
  `+0.45`, `-0.12`). Render BEFORE the **Priority** line to make product-impact
  visible at a glance alongside goal-progress (g-245-03).

### 3f: Blocked Goals

Only show if blocked_data.summary.total_blocked > 0.

```markdown
## Blocked Goals ({total_blocked} of {total_active_goals} active)

### Bottlenecks

| Root Goal | Aspiration | Title | Cause | Downstream |
|-----------|------------|-------|-------|------------|
| g-001-04 | asp-001 | Test server start... | INFRA: external-service | 3 |
```

Then list by reason group. Omit any group with count 0.

```markdown
### Infrastructure ({count})
- g-001-04: Test server start — external-service unavailable

### Dependencies ({head_count} heads, {downstream_count} downstream)
- g-003-02: Deploy config — waiting on g-001-02

### Deferred ({count})
- g-001-06: Check inbox — until 2026-03-17 (cooldown)

### Hypothesis Gate ({count})
- g-001-06: Verify spatial memory — resolves_no_earlier_than 2026-03-20

### Explicitly Blocked ({count})
- g-001-07: Review cache depth — reason: awaiting session data
```

### 3g: Hypotheses Ready to Test

Only show if testable_hypotheses[] is non-empty.

```markdown
## Hypotheses Ready to Test ({count})

| ID | Title | Confidence | Type | Category | Window |
|----|-------|------------|------|----------|--------|
| 2026-03-10_api-latency | API latency at P99... | 0.70 | calibration | api-performance | Mar 15 – Apr 10 |
```

Title: truncate to 40 chars.
Window: "{resolves_no_earlier_than} – {resolves_by}" formatted as short dates.

### 3h: Summary Footer

```markdown
---

**Summary**: {N} aspirations | {G} active goals | {S} selectable | {B} blocked | {P} pending decisions | {H} testable hypotheses
```

## Phase 4: Write File

```
Write: agents/<agent>/BACKLOG.md ← rendered markdown from Phase 3

IF agents/<agent>/BACKLOG.md already exists: overwrite (regenerated snapshot, not append-only)
```

## Phase 5: Terminal Summary

Display compact summary in the terminal:

```
═══ BACKLOG REPORT ════════════════════════════

Generated: {timestamp}

Your Action Items: {N} pending decisions, {M} goals needing you
  (list goal ids + short titles if ≤ 5 items)

Top 5 Next Actions:
  1. [{score}] g-XXX-XX: {title (60 chars)} ({priority})
  2. [{score}] g-XXX-XX: {title (60 chars)} ({priority})
  3. [{score}] g-XXX-XX: {title (60 chars)} ({priority})
  4. [{score}] g-XXX-XX: {title (60 chars)} ({priority})
  5. [{score}] g-XXX-XX: {title (60 chars)} ({priority})

Recurring: {N} overdue, {M} on-streak
Aspirations:
  asp-001: {title} [ACTIVE] ({completed}/{total}) — {priority}
  asp-001: {title} [ACTIVE] ({completed}/{total}) — {priority}

Blocked: {N} goals, {B} bottlenecks
Hypotheses: {H} ready to test

Read agents/<agent>/session/pending-questions.yaml
IF any entry has type: "priority-review" AND status: "pending":
  Bash: echo "Priority review pending — run /priority-review to reorder"

Full report written to: {absolute_path_to_agents/<agent>/BACKLOG.md}
═══════════════════════════════════════════════
```

## Chaining

- **Called by**: User directly (`/backlog-report`), OR by agent during RUNNING state
- **Calls**: No other skills — only framework scripts (`aspirations-read.sh`, `goal-selector.sh`, `pipeline-read.sh`) and file reads
- **Modifies**: Writes/overwrites `agents/<agent>/BACKLOG.md`

## Return Protocol

See `.claude/rules/return-protocol.md` — last action must be a tool call, not text.
When called mid-loop (agent mode), the terminal action is the Write of BACKLOG.md.
Never end with a text summary after the Write.
