Skip to main content

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.

Informations de source

Dépôt
restarter/lets-workflow
Dernière activité de la source
3 novembre 2026 à 20:43
Langue détectée de SKILL.md
anglais
Étoiles
17
Forks
3

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
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|check|review-round> 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. A name never starts with `report`, `summary`, `findings` or `analysis` (any case): Claude Code's Write tool refuses a subagent's `.md` whose basename starts with one of them, and the report is lost (lets-jyjxc). A name never ends in `-r<digits>`: that suffix is Step 3's retry. `reportnames_test.go` pins both for every template in the plugin. 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. Run as written: ```bash 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 printf 'TAKEN %s %s\n' "$n" "$f" else printf 'REPORT_FILE %s=%s\n' "$n" "$f"; fi done ``` A `REPORT_FILE <name>=<path>` line is that agent's path, exactly as `op=open` step 3 - the dispatcher copies each line into its agent's prompt. A `TAKEN <name> <path>` line hands out NO path: that file belongs to an earlier dispatch, and pointing a new agent at it would let `op=collect` read the earlier report as this one's. Dispatch nothing under that name - pick a fresh one (the caller's own suffix rule) and run `op=add` for it. For Step 3's `{name}-r2`, `TAKEN` is a second guard against a repeat retry (a `-r2` file already on disk, e.g. after a resume); the first guard is the run's own dispatch record (Step 3). ## op=peek - inspect without concluding Args: `dir=<directory> names=<name>,<name>`. Runs `op=collect` Step 1 (classify) and, for each `REPORT` line in state `OK`, 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`). It prints ONE line per expected name, `REPORT <name> <state> <path|none>`, judging the name's own file and its `-r2` retry file together: an `OK` file wins (the retry's, else the original - a late original counts); otherwise the richest file with content (an `UNTERMINATED` file - the larger non-blank line count, a tie to the retry); none with content -> `none`. Every consumer keys by `<name>`; none strips a retry suffix. ```bash REPORT_DIR="{dir}" classify() { # sets s (state) and c (non-blank lines) for the file in f c=0 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; c=0 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 } for n in $(printf '%s' "{names}" | tr ',' ' '); do # names: comma-separated, [a-z0-9.-] only f="$REPORT_DIR/$n.md"; classify; s1=$s; c1=$c; p1=$f f="$REPORT_DIR/$n-r2.md"; s2=""; c2=0; p2="" if [ -e "$f" ]; then classify; s2=$s; c2=$c; p2=$f; fi if [ "$s2" = OK ]; then printf 'REPORT %s OK %s\n' "$n" "$p2" elif [ "$s1" = OK ]; then printf 'REPORT %s OK %s\n' "$n" "$p1" elif [ "$s2" = UNTERMINATED ] && { [ "$s1" != UNTERMINATED ] || [ "$c2" -ge "$c1" ]; }; then printf 'REPORT %s UNTERMINATED %s\n' "$n" "$p2" elif [ "$s1" = UNTERMINATED ]; then printf 'REPORT %s UNTERMINATED %s\n' "$n" "$p1" else printf 'REPORT %s %s none\n' "$n" "${s2:-$s1}"; fi done ``` ### Step 2: READ EVERY REPORT IN FULL For each `REPORT <name> OK <path>` line, Read `<path>` 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 `REPORT` line not in state `OK` (no `REPORT` line for a name, or a Read of an `OK` path that errored, counts as `UNREADABLE` - never skipped) -> re-dispatch THAT agent ONCE per run with a FRESH report file: `op=add dir={dir} names={name}-r2`. A name this run already re-dispatched (your own record of this run - the retry message you sent) is never re-dispatched again, even when its `-r2` file is missing: it goes straight to resolution and, still not `OK`, to its gap. `TAKEN` on `{name}-r2` is the second guard - a `-r2` file already on disk (a collect run again after a resume): no second retry either. Otherwise the re-dispatch is the original prompt with its `REPORT_FILE:` line replaced by the `-r2` path and, right after it, the one line `Your previous attempt left its report file {state}. Write the WHOLE report to REPORT_FILE in ONE Write call, last line REPORT-END.` - never the failed content or its path (the failed attempt is the only evidence of what went wrong, and the Write tool refuses to overwrite a file the new agent has not read). Every retry of the phase goes in one message; then Step 1 again for the retried names (it judges each name's original AND `-r2` file - a late original counts, never a false gap) and Step 2 for its `OK` lines. - Coverage counts names, not files: a name is `OK` when its `REPORT` line is. - `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 <name>: UNREADABLE`. - Still not `OK` -> `GAP <name>: <state>`. A `REPORT` line with a path (an `UNTERMINATED` file - content without `REPORT-END`) -> Read that file IN FULL and carry it into the aggregation marked `UNVERIFIED - report file without REPORT-END, may be incomplete`. A path of `none` -> when the agent's final text held report content, carry it marked `UNVERIFIED - from the final text, may be truncated` (the retry's final text, else the first attempt's); otherwise the lens contributes nothing. A file with content beats the channel this skill exists to replace. ### Step 4: Report coverage - always Print `Coverage: <ok>/<expected> reports` and every `GAP` line. The caller MUST: - show them in its user-facing output AND in its saved artifact when it saves one; - never aggregate a gap as "no findings", "no objection", a vote, or a verified finding; - finish its own remaining steps - a gap lowers coverage, it never ends the run. ## Rules - The agent's final message is a pointer (`REPORT_WRITTEN <path>`), never the transport. - One directory per run, one file per dispatch, paths only from this skill. - Report files stay after the run (`.lets/reports/` is gitignored and shared by every worktree); nothing here deletes them. - `--workflow` paths do not use this skill - their agents return through StructuredOutput schemas. - Every consumer carries the phrase READ EVERY REPORT IN FULL at its collect call (pinned by `agentreport_test.go`).
Voir sur GitHub