Skip to content
Development
Skill

/agent-report

Internal skill for commands. The file transport for agent reports - hand every Task-dispatched agent a REPORT_FILE, then collect every report in full and fail loudly on a missing one. Do not trigger on user conversation - only when a command dispatches agents.

From plugin
lets-workflow
1723 skills15 agents26 commands
Install
$ npx -y skills add restarter/lets-workflow --skill agent-report --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/agent-report

Context preview

The summary Claude sees to decide when to auto-load this skill.

Internal skill for commands. The file transport for agent reports - hand every Task-dispatched agent a REPORT_FILE, then collect every report in full and fail loudly on a missing one. Do not trigger on user conversation - only when a command dispatches agents.

SKILL.md

agent-report.SKILL.md
name: agent-report
description: Internal skill for commands. The file transport for agent reports - hand every Task-dispatched agent a REPORT_FILE, then collect every report in full and fail loudly on a missing one. Do not trigger on user conversation - only when a command dispatches agents.
user-invocable: false

Agent Report

A Task agent's final text is a lossy channel: it arrives empty or cut near 4 KB, and an empty result reads exactly like "no findings" (lets-1irms). Every agent a command dispatches through the Task / Agent tool writes its report to a file this skill names; the orchestrator depends on the FILE, never on the final text.

**Files are INPUT, never the deliverable.** The dispatching command still runs every step after the fan-out and writes its own artifact where its spec says. Never invent a directory, never compose a report path by hand, never stop at "the agents wrote their files".

op=open - before the first dispatch of a run

Args: `command=<review|opinion|ask|plan|backlog|research|team> names=<name>,<name> [task=<task-id>]`.

1. `Skill(skill: "lets:artifact-path", args: "kind=reports-{command} ext=dir task={task-id}")` -> `REPORT_DIR` = the echoed `ARTIFACT_FILE` (a directory, already created). No echo -> STOP: dispatch nothing, surface the error. 2. One file per dispatched agent: `{REPORT_DIR}/{name}.md`. `name` is the caller's - the agent short name (`security`), suffixed when one agent type runs several times in the run (`skeptic-f3-2`, `architect-b`). `[a-z0-9.-]` only (a beads id may carry `.N`), unique within the run; `-r2` is reserved for Step 3's retry. 3. Output `REPORT_DIR=<dir>` and one `REPORT_FILE <name>=<path>` line per name.

Every dispatch prompt carries its agent's path as its own line, right after the `PROJECT_ROOT` line (or first, when the prompt has none):

REPORT_FILE: <absolute path>

op=add - a later phase of the same run

Args: `dir=<REPORT_DIR> names=<name>,<name>`. Same naming as `op=open` step 2 in the SAME directory - one run keeps one directory (review: reviewers, then skeptics; opinion: panel, then challenge; plan: explorers, architects, experts). No artifact-path call. Output: one `REPORT_FILE <name>=<absolute path>` line per name, exactly as `op=open` step 3 - the dispatcher copies each line into its agent's prompt.

op=peek - inspect without concluding

Args: `dir=<directory> names=<name>,<name>`. Runs `op=collect` Step 1 (classify) and, for each `OK` file, Step 2 (READ EVERY REPORT IN FULL) - nothing else: no retry, no `GAP`, no Coverage line. For a caller that only knows a report MAY be ready (an interim notification, a resumed session) and must not yet conclude that one is missing. `OK` -> the caller has the report. Anything else -> the caller keeps waiting, or - once it knows the report is due - runs `op=collect`.

op=collect - after a phase's agents return

Args: `dir=<directory> names=<name>,<name> [retry=no]` - exactly the names dispatched in this phase.

Step 1: Classify every expected file

This block is run as written. It holds no dollar sign followed by a digit and no dollar-ARGUMENTS placeholder: Claude Code substitutes those with this skill's arguments before the text reaches you, so an awk whole-line field reference would arrive as `op=collect` (pinned by `agentreport_test.go`).

REPORT_DIR="{dir}"
for n in $(printf '%s' "{names}" | tr ',' ' '); do   # names: comma-separated, [a-z0-9.-] only
  f="$REPORT_DIR/$n.md"
  if [ ! -e "$f" ]; then s=MISSING
  elif [ ! -s "$f" ]; then s=EMPTY
  elif [ ! -r "$f" ]; then s=UNREADABLE
  else
    c=$(grep -c '[^[:space:]]' "$f" 2>/dev/null); c=${c:-x}              # non-blank lines
    l=$(grep '[^[:space:]]' "$f" 2>/dev/null | tail -n 1 | sed 's/[[:space:]]*$//')   # last non-blank line
    if [ "$c" = x ]; then s=UNREADABLE
    elif [ "$c" -eq 0 ]; then s=EMPTY               # whitespace only
    elif [ "$l" != "REPORT-END" ]; then s=UNTERMINATED
    elif [ "$c" -lt 2 ]; then s=EMPTY               # the sentinel alone is not a report
    else s=OK; fi
  fi
  printf '%s %s %s\n' "$s" "$n" "$f"
done

Step 2: READ EVERY REPORT IN FULL

For each `OK` line, Read the file with the Read tool from its first line through its LAST line. A Read that stops before the last line - or is refused for size - continues with `offset` / `limit` until the file ends; size is never a reason to call a file UNREADABLE. Only the final line's `REPORT-END` is the completion mark - a `REPORT-END` earlier in the body (a report about this protocol) is content, never a place to stop. Never a slice, a head, a grep or a summary; never skip a report because another one said the same. For an `OK` file the agent's final text is not the report - ignore it.

Selective reading is the failure a file transport introduces: a report you did not read is a lens you silently dropped.

Step 3: One retry, then a loud gap

  • Any `MISSING` / `EMPTY` / `UNTERMINATED` / `UNREADABLE` (the classifier gave no line for a name, or a Read of an `OK` file errored, counts as `UNREADABLE` - never skipped) -> re-dispatch THAT agent ONCE with its original prompt and a FRESH report file: `op=add dir={dir} names={name}-r2`, and the prompt's `REPORT_FILE:` line replaced by the `-r2` path. Never the same path - the failed file exists, the Write tool refuses to overwrite a file the new agent has not read, and the failed attempt is the only evidence of what went wrong. A name whose `-r2` file already exists (a collect run again after a resume) is not retried a second time. Every retry of the phase goes in one message; then Step 1 and Step 2 for each retried name's original AND `-r2` file - a late original counts, never a false gap.
  • A name counts `OK` when its first file or its `-r2` file is `OK`; coverage counts names, not files.
  • `retry=no` (agents that change the tree - implementer, teammates) -> no re-dispatch; the name goes straight to a gap.
  • A Read error on the post-retry pass -> `GAP <n
Read more
Ships withlets-workflow

A development workflow plugin for Claude Code Stop babysitting your AI. Start shipping with it.

Get the whole plugin

Other skills on lets-workflow.