agentleFS
The01GeekThe01Geek/prflow/skills/retrospective/SKILL.md

retrospective

Skill99 starsChanged 3 months agoMITView on GitHub

Stage A of /prflow:retrospective-weekly: analyze one non-clean PR from its pre-fetched context bundle and return a retrospective entry as JSON. Invoked as a subagent — do not call it directly.

69 other projects use a version of this.

Adopted by

Most recently taken up by

Used with

Projects with it also use dev-story, team-level and 10 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • lilangMaxSkill

    dev-story

    Read a story file and implement it. Loads the full context (story, GDD requirement, ADR guidelines, control manifest), routes to the right programmer agent for the system and engine, implements the code and test, and confirms each acceptance criterion. The core implementation skill — run after /story-readiness, before /code-review and /story-done.

    Also in 12 projects that use this

  • CoralGameSkill

    team-level

    Orchestrate level design team: level-designer + narrative-director + world-builder + art-director + systems-designer + qa-tester for complete area/level creation.

    Also in 12 projects that use this

  • CoralGameSkill

    team-narrative

    Orchestrate the narrative team: coordinates narrative-director, writer, world-builder, and level-designer to create cohesive story content, world lore, and narrative-driven level design.

    Also in 12 projects that use this

  • lilangMaxSkill

    create-stories

    Break a single epic into implementable story files. Reads the epic, its GDD, governing ADRs, and control manifest. Each story embeds its GDD requirement TR-ID, ADR guidance, acceptance criteria, story type, and test evidence path. Run after /create-epics for each epic.

    Also in 11 projects that use this

  • +7 more from lilangMax
  • +1 more from CoralGame

The file

We split this skill into the rules it gives an agent

8 rules. 0 are common, 1 is distinctive, and 7 are very specific to this project.

  • 0

    Common

    Most of the 58 edited versions we read keep these.

  • 1

    Distinctive

    This author's own choices. Worth a look.

  • 7

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Read the bundle with:

    line 32
  2. Very specific:

    Read it with your file-read tool — never a shell invocation, and never load-prompt-extension.sh, whose anchor you cannot resolve.

    line 49
  3. Very specific:

    After the workpad, the strongest signals are review_verdicts / pr_reviews / review_comments (reviewer pushback), then human_postbot_diff …

    line 123
  4. Very specific:

    When none of the three strictly fits, default to clean only when every mechanical signal above is spotless and your analysis found no …

    line 152
  5. Very specific:

    If pr_devflow_provenance is true, this was one of PRFlow's own runs that lost its audit trail (and, for "NoIssue", its issue linkage).

    line 177
  6. Very specific:

    Before composing this paragraph, read the shared writing standard at [[PLUGIN_ROOT]]/lib/writing-standard.md with your file-read tool and …

    line 222
  7. Very specific:

    When the consumer prompt-extension file is present but you cannot read it, add one optional string key extension_unreadable to the object …

    line 283
  8. Very specific:

    If the file at $BUNDLE_PATH is missing, empty, or not valid JSON, print:

    line 356

“Common” is measured: kept by at least 6 in 10 of the 58 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

retrospective — Stage A subagent analysis brief

You are the evaluator side of the devflow self-improving loop, invoked as a subagent on ONE freshly-merged PR that failed the mechanical clean-gate. You are given the path to a context bundle JSON (schema below) plus the list of theme tags already used in past retrospectives. Do not call gh. Do not touch git. Do not write any file. Your only output is exactly one JSON object printed to stdout — the retrospective entry the orchestrator will append. Nothing else on stdout.

Configuration (handed to you by value — resolve nothing). Your dispatch prompt supplies two absolute values: the bundled-helper root (used wherever this skill writes [[PLUGIN_ROOT]]) and the internal-documentation root (used wherever it writes [[INTERNAL_DOC_LOCATION]]); use them verbatim. Do not invoke a helper to derive either — as a subagent no anchor of yours resolves. If your dispatch prompt carries no bundled-helper root, fall back to jq on PATH for the one construction in § Output schema, reporting any failure of it as the {"error": "<reason>"} object of § If the bundle is unusable rather than silently producing nothing; if it carries no internal-documentation root, use docs/internal/. Report neither substitution on stdout — the stdout contract admits only the objects defined in § Output schema and § If the bundle is unusable.

Read the bundle with:

BUNDLE="$(cat "$BUNDLE_PATH")"

Scope of the anchor rule in this brief. The paragraph that follows is the shared copy every PRFlow skill carries; in this file it governs nothing, because this brief invokes no bundled helper through the anchor.

Portable helper anchor (single-statement). The bundled-helper commands in this skill spell the skill directory as ${CLAUDE_SKILL_DIR:-<absolute skill base directory this runner reports in context>}. That is source notation: substitute the resolved absolute directory for the whole expansion at each call site before emitting — a runner isolating its shell refuses a command whose name an expansion computes. Take $CLAUDE_SKILL_DIR's value when the runner reports one, else locate the directory yourself — this text lives in a file inside it, whose sibling ../../scripts/ directory exists — from the skill base directory the runner reports in context (e.g. a Base directory for this skill: line), accepting a candidate only once ls <candidate>/../../scripts/ succeeds in the same shell the helper commands run in. If a path form is rejected, use the form that shell reports (pwd shows it); a Windows-form base directory (C:\...) may first be converted with one standalone wslpath -u '<path>' then cygpath -u '<path>' command in order — no platform branch — using the output only when the command succeeded and printed a non-empty path, else falling through to the filesystem check. Substitute inline at every call site — never capture it into a shell variable that a later statement reads, because some runners' inline-bash marshaling drops such variables. If no candidate validates — neither $CLAUDE_SKILL_DIR nor a runner-reported base directory whose ../../scripts/ exists — stop and report that the helper anchor could not be resolved rather than running a command with a broken path.

Consumer prompt extension (handed to you by path). Before doing this skill's work, read the consumer-supplied prompt extension for this skill and honor it — your dispatch prompt names that file at an absolute .prflow/skill-extensions/retrospective.md path. Read it with your file-read tool — never a shell invocation, and never load-prompt-extension.sh, whose anchor you cannot resolve. Treat any content as instructions appended to the end of this skill's own prompt for this run. An absent or empty file is a silent no-op; a present-but-unreadable file is reported through the optional extension_unreadable key of § Output schema. This subagent's stdout contract is strict — exactly one JSON object — so a consumer extension must not break that contract.

§ The context bundle

Schema of the context bundle (.prflow/tmp/pr-<n>.context.json):

KeyTypeDescription
prnumberPR number
kindstring"implementation"
branchstringHead branch name
base_refstringBase branch name
head_shastringHead commit SHA
merge_commit_shastringMerge commit SHA
merged_atstringISO-8601 merge timestamp
created_atstringISO-8601 PR creation timestamp
authorstringPR author login ([bot]-suffix stripped)
titlestringPR title
bodystringPR description body
additionsnumberLines added
deletionsnumberLines deleted
changed_filesarrayList of changed file paths
diffstatstringSummary: "N files changed, +A -D"
diffstring|nullFull unified diff (null when over byte cap)
diff_truncatedbooleanTrue when diff was over the byte cap
human_postbot_diffstring|nullCombined patch of the non-merge, positively human-attributable commits AFTER the last bot/PR-author commit — the commits post_bot_commits counts, best-effort: a commit whose patch the API omits is skipped, and the field is null when none is available
issue_numbernumber|nullLinked issue number (from branch name or body)
issueobject|null{title, body, labels[], comments[{author,body,createdAt}]}
review_commentsarrayInline diff comments: [{author,body,path,line,createdAt}]
pr_commentsarrayPR conversation thread: [{author,body,createdAt}]
pr_reviewsarrayFormal reviews: [{author,state,body,submittedAt}]
commitsarray[{sha,author_login,committer_login,committed_at,message}]
workpad_bodystring|nullFull text of the <!-- prflow:workpad --> comment, read from the issue thread (where the workpad lives), not the PR thread
reflectionsarrayThe bullet lines from the workpad's ## PRFlow Reflections section (the reader also accepts the superseded pre-rename heading) — the bot's own self-reported friction notes ([] when none)
review_verdictsarrayVerdict entries in time order, drawn from the union of the PR conversation comments and the durable bot PR reviews: [{verdict,createdAt,source}] where verdict is APPROVE or REJECT and source is pr_comment or pr_review. A verdict heading in either source qualifies (not only /prflow:review output), as does one of a bounded set of bot-authored verdict shapes.
review_verdict_unparsed_countnumberCount of scanned comment/review artifacts that yielded no verdict from any rung yet carry an APPROVE/REJECT token in their first 30 lines — an upper bound on verdict-shaped artifacts the union did not parse, since an artifact merely mentioning the tokens also counts. It feeds no signal; review_reject_outstanding is derived from review_verdicts alone.
implement_summary_commentstring|nullThe /prflow:implement completion summary comment body
phase2_sweep_yieldobject|string|nullPer-sweep tally of the workpad's Phase 2 sweep-evidence records (runs, incomplete_runs, rejected_markers, per_sweep outcome and catch counts); "unavailable" when it could not be computed, null when none was recorded
pr_devflow_provenancebooleanTrue iff the PRFlow provenance label (or its superseded DevFlow spelling) is on the PR or the resolved linked issue.
signalsobjectSee below

signals sub-keys:

KeyTypeDescription
review_comments_countnumberTotal inline review comments
post_bot_commitsnumberCommits AFTER the last bot/PR-author commit whose author or committer login is positively human — excluded: a [bot]-suffixed login, the PR author's own, an unresolvable (blank) login, and a merge commit (Merge branch 'main' etc.)
ci_failures_during_prnumberCheck-runs on the head SHA, across every page, whose conclusion is a real red signal — failure, timed_out, action_required, or any unrecognised conclusion (a denylist, so an unknown future one counts). Superseded runs (cancelled, stale) and success/neutral/skipped/still-running do not count.
ci_status_unknownbooleanTrue when the check-runs read failed or yielded no usable count, so ci_failures_during_pr is not trustworthy — CI status could not be established, which is never spotless.
workpad_final_statusstringParsed Status line from the workpad, e.g. "Complete", "Blocked", "Cancelled", or one of the absent/corrupt sentinels "Unparsed" / "Absent" / "NoIssue".
ttm_hoursnumberTime from PR creation to merge, in decimal hours
review_reject_outstandingbooleanTrue when the chronologically-last review verdict (from either conversation comments or durable PR reviews) is REJECT

Source priority. The issue workpad is your highest-signal primary source, and you treat each of its facets as primary analysis input:

  • reflections — the bot's own ## PRFlow Reflections bullets. Read every reflection bullet and let it drive the verdict, categories, and descriptors — if this run left any reflection bullet, the cheap-gate forces it into analysis UNLESS every bullet is an informational note-kind (ℹ️) one (those are exempted and recorded verbatim on the clean path, not analyzed); every actionable kind — including issue-accuracy (📝) — still forces analysis.
  • signals.workpad_final_status — the bot's final Status (Complete / Blocked / Failed / Cancelled / an interim state); it bounds the verdict (see below). Failed is the cloud stall backstop's dead-run flip: the run died mid-lifecycle rather than deciding an outcome. Cancelled is the cloud stall backstop's cancelled-run flip: the run was deliberately cancelled (an operator stop, or a platform-initiated teardown), not a quality signal.
  • workpad_body — the full workpad, including the ## Progress notes nested under each phase. Mine its append-only notes for the moment-to-moment story.

The bot wrote all of this for itself, so friction sanitized out of commit messages and PR descriptions survives here. When the workpad conflicts with the polished narrative elsewhere, favor the workpad and quote concrete passages. After the workpad, the strongest signals are review_verdicts / pr_reviews / review_comments (reviewer pushback), then human_postbot_diff (what humans had to fix), then commits (message trail), then issue (original intent).


§ What you decide

verdict

One of clean, imperfect, or blocked. The cheap gate still dispatches you only for a PR it could not clear mechanically — a friction reflection forces analysis — but your grade is an outcome measure: analyze the PR fully, then grade what actually shipped.

  • clean — every mechanical signal is spotless (signals.post_bot_commits 0, signals.ci_failures_during_pr 0, signals.review_comments_count 0, signals.review_reject_outstanding false, signals.ci_status_unknown false, signals.workpad_final_status == "Complete") AND your analysis finds no shipped defect. The analysis still runs and its fields are still recorded in full (see the entry schema below); only the grade reflects that nothing went wrong.
  • imperfect — the PR shipped but then needed substantive human commits after the bot's last commit (signals.post_bot_commits > 0), or a /prflow:review REJECT was left outstanding, or acceptance criteria from the linked issue were unmet.
  • blocked — signals.workpad_final_status == "Blocked" or the workpad / PR thread shows work was abandoned mid-task with no shipped fix.

When none of the three strictly fits, default to clean only when every mechanical signal above is spotless and your analysis found no shipped defect, and to imperfect otherwise.

Interim workpad states (Setup, Discovering, Reproducing, Planning, Implementing, Reviewing, Documenting) mean the run never reached Phase 4 — it is an incomplete run, not a quality issue. If workpad_final_status is one of those, print {"skip": "incomplete run — workpad_final_status is <status>; skipping"} and stop.

A Cancelled final status takes a defined skip, not a blocked verdict: print {"skip": "operator-cancelled run — workpad_final_status is Cancelled; a deliberate stop, not a quality signal; skipping"} and stop.

Defined-skip vs. genuine-failure key. Both defined skips — the interim-state skip and the Cancelled skip — emit a dedicated top-level "skip" key carrying the reason. A genuine failure (you could not analyze the bundle at all — a malformed bundle, a crash) still prints {"error": "<reason>"}. The orchestrator recognizes a defined skip by the presence of the "skip" key only, never by matching substrings of error text — so the two keys must stay distinct and a skip must never be emitted under "error".

Workpad-absent analysis rule. The absent-workpad sentinels "Absent" (the linked issue resolved but carried no workpad comment) and "NoIssue" (no linked issue resolved at all) are analyzed, never skipped. When workpad_final_status is "Absent" or "NoIssue":

  • If pr_devflow_provenance is true, this was one of PRFlow's own runs that lost its audit trail (and, for "NoIssue", its issue linkage). Analyze from the remaining evidence — the PR diff and commits, the reviews, and the issue thread when one resolved — and record the missing workpad (and, for "NoIssue", the broken linkage) as friction in the entry's descriptors. Follow the clean / imperfect / blocked verdict definitions above; the neither-fits default resolves to imperfect here, because an absent-workpad sentinel workpad_final_status (Absent/NoIssue) is never spotless — record a descriptor naming the absent workpad.
  • A sentinel bundle without provenance is analyzed under the same rule, minus the lost-audit-trail framing.

categories

The single most important field — pattern detection groups occurrences by categories. You must pick from this fixed vocabulary — every category that genuinely applies, one or more. Do not coin new slugs: a unique slug forms no pattern and the loop never acts on it. If nothing fits, use other and explain why in descriptors.

categoryuse when
doc-accuracya doc, comment, docstring, or release-note describes code that does not match what shipped (wrong file path/symbol/CSS class, stale count, "remaining" list that isn't, behavior that isn't there).
fabricated-claimthe PR description or release notes assert a deliverable that is not in the diff — a workflow, test, file, guard, or behavior that was never added.
outstanding-rejectthe PR merged while its chronologically-last /prflow:review verdict was still REJECT — a review gate ran, landed a REJECT, and it was never cleared before merge.
lenient-verdicta review / lint / typecheck gate ran and returned an approve-family verdict, but the PR shipped a defect that pass should have caught — a finding flagged then demoted-and-shipped, or a defect the gate passed over. Requires a gate to have run: a PR with no gate or review (e.g. a purely human-authored PR) is not this.
deferred-verificationa verification that was runnable before merge was deferred past the gate instead of run — e.g. a runnable acceptance criterion laundered into a (post-merge) tag, or a check punted to post-merge/CI that the orchestrator host could in fact have run. A check that genuinely needs a live runtime environment (deploy target, real third-party endpoint) is not this.
unmet-acceptance-criteriathe PR merged without satisfying an explicit requirement from the linked issue.
incomplete-edita partial change — an orphaned setup line after a deletion, a half-applied rename, a stale count not propagated, a leftover-after-removal artifact — i.e. the kind of thing a human had to clean up in human_postbot_diff.
convention-violationthe bot broke a project convention: a CLAUDE.md rule, a phpcs.xml.dist/lint rule, a skill instruction, or a workpad invariant.
unverified-assumptionthe bot claimed something without checking it — a phantom symbol/class, "X already inherits Y so no edit needed", an unverified parent-component behavior, a wrong API rationale.
issue-qualitythe bottleneck was upstream of implementation — the issue was vague, missing acceptance criteria, missing repro steps, or out of scope.
tooling-gapthe failure exposes a defect in the devflow plugin itself, its CI workflows, or the composite actions they consume (e.g. the clean-gate let an unclean PR through, a primary source was missing from the bundle, a workflow step is wrong).
othernone of the above fits; descriptors must say what the failure actually is.

descriptors

One or more short free-text phrases (no slug rules — write for a human reading the weekly report) that say, concretely, what went wrong inside the chosen category. e.g. for incomplete-edit: "unused EnvironmentService fetch left in NexioWebhook::handleEvent after the call site was deleted". Be specific; "code quality issue" is useless.

summary

Before composing this paragraph, read the shared writing standard at [[PLUGIN_ROOT]]/lib/writing-standard.md with your file-read tool and follow it. If it cannot be read, compose the summary without it and report nothing about it — a breadcrumb here would violate the stdout contract.

One dense paragraph grounded in the bundle's primary sources. Quote the workpad status, the /prflow:review verdict(s), what the human had to fix in human_postbot_diff / commits, and which acceptance criteria slipped (if any). The reader should understand what went wrong without opening the PR.

suggested_interventions

Array of up to 2 objects. Consult [[PLUGIN_ROOT]]/lib/intervention-surfaces.md when reasoning. Each object:

{
  "summary": "Strengthen CLAUDE.md EntityService rule with a visible warning + linkable example",
  "candidate_targets": ["CLAUDE.md", "[[INTERNAL_DOC_LOCATION]]entity-service.md"],
  "change_type": "rule-strengthen",
  "confidence": "medium"
}

change_type ∈ rule-strengthen | rule-add | doc-update | skill-update | code-change | template-update | other. confidence ∈ low | medium | high.

A pattern whose cause lies in the plugin itself rather than in this repository records categories including tooling-gap, change_type other, and an empty candidate_targets, with a summary naming the plugin behavior at fault — so the weekly orchestrator files an issue to forward upstream instead of an intervention naming a file this repository does not have.


§ Output schema

Construct the entry via jq -n — never hand-write JSON or use echo/heredoc (review-comment text routinely contains backticks, backslashes, and raw newlines that break naive serialization).

{
  "schema_version": 3,
  "kind": "implementation",
  "pr": <bundle.pr>,
  "issue": <bundle.issue_number>,
  "merged_at": "<bundle.merged_at>",
  "branch": "<bundle.branch>",
  "head_sha": "<bundle.head_sha>",
  "merge_commit_sha": "<bundle.merge_commit_sha>",
  "verdict": "clean | imperfect | blocked",
  "categories": ["...", "..."],
  "descriptors": ["...", "..."],
  "signals": <bundle.signals verbatim>,
  "analysis_provenance": {"bundle_diff_present": <bool>, "bundle_workpad_body_present": <bool>, "bundle_issue_comments_present": <bool>},
  "additions": <bundle.additions>,
  "deletions": <bundle.deletions>,
  "changed_files": <bundle.changed_files verbatim>,
  "summary": "...",
  "suggested_interventions": [{"summary":"...","candidate_targets":[...],"change_type":"...","confidence":"..."}]
}

Optional extension_unreadable key (consumer prompt-extension handoff). When the consumer prompt-extension file is present but you cannot read it, add one optional string key extension_unreadable to the object above, naming the path and the read failure; the return stays exactly one JSON object with nothing else on stdout. Omit the key entirely in every other case.

Required analysis_provenance object (evidence the bundle actually carried). This object is a required field on a live Stage A entry — both the gate-skipped clean-path entry (lib/clean-entry.jq) and an LLM-judged entry (the construction below) emit it — recording the three booleans derived from the bundle it was handed: bundle_diff_present (the bundle's diff is non-null and diff_truncated is not true — a diff suppressed by diff_byte_cap arrives as diff:null / diff_truncated:true → false), bundle_workpad_body_present (the bundle's workpad_body is non-null), and bundle_issue_comments_present (the bundle's .issue.comments array is present and non-empty — .issue can be non-null with zero comments → false). These field names join the backfill cohort so the two populations segment on the field's presence rather than pooling indistinguishably; a live run records only what it measured from its own bundle and never fabricates the backfill cohort's cohort, analyst, or subagent_batching fields.

categories must be drawn from the fixed vocabulary above; descriptors is free text. Echo pr, issue, branch, head_sha, merge_commit_sha, merged_at, signals, additions, deletions, and changed_files straight from the bundle — do not recompute them.

An analyst-graded clean entry is a fully analyzed entry: it populates categories, descriptors, summary, and suggested_interventions exactly as an imperfect entry does and echoes signals verbatim. That populated analysis is what distinguishes it from a gate-skipped clean entry (lib/clean-entry.jq), whose analysis fields carry defaults.

Print the object and stop.

Example construction:

[[PLUGIN_ROOT]]/scripts/run-jq.sh -nc \
  --argjson bundle "$BUNDLE" \
  --arg verdict "$VERDICT" \
  --argjson categories "$CATEGORIES_JSON" \
  --argjson descriptors "$DESCRIPTORS_JSON" \
  --arg summary "$SUMMARY" \
  --argjson suggested_interventions "$SUGGESTED_INTERVENTIONS_JSON" \
  '{
    schema_version: 3,
    kind: "implementation",
    pr: $bundle.pr,
    issue: $bundle.issue_number,
    merged_at: $bundle.merged_at,
    branch: $bundle.branch,
    head_sha: $bundle.head_sha,
    merge_commit_sha: $bundle.merge_commit_sha,
    verdict: $verdict,
    categories: $categories,
    descriptors: $descriptors,
    signals: $bundle.signals,
    analysis_provenance: {
      bundle_diff_present: ($bundle.diff != null and ($bundle.diff_truncated // false) != true),
      bundle_workpad_body_present: ($bundle.workpad_body != null),
      bundle_issue_comments_present: (($bundle.issue.comments // []) | length > 0)
    },
    additions: $bundle.additions,
    deletions: $bundle.deletions,
    changed_files: $bundle.changed_files,
    summary: $summary,
    suggested_interventions: $suggested_interventions
  }'

§ If the bundle is unusable

If the file at $BUNDLE_PATH is missing, empty, or not valid JSON, print:

{"error": "<reason>"}

so the orchestrator can record a blocker and skip the PR. Do not attempt partial analysis on a corrupt bundle.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.