Retrospective logo

Retrospective

Community
The01Geek
retrospective

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.

Overview

PublisherThe01Geek
Repositoryprflow
Skill nameretrospective
Stars
100
Forks
24
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by The01Geek on GitHub. Read the source before you install it.

Installation

Install the Retrospective AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/The01Geek/prflow/tree/main/skills/retrospective
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/The01Geek/prflow.git /tmp/prflow
mkdir -p .claude/skills
cp -r /tmp/prflow/skills/retrospective .claude/skills/retrospective
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Retrospective in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Retrospective on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Retrospective is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

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:

bash
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:

json
{
  "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).

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": "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:

bash
[[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:

json
{"error": "<reason>"}

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

Frequently asked questions

What does the Retrospective AI skill do?

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.

Why use Retrospective on TypingMind?

Because you install it once and use it with any model. Retrospective is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Retrospective in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/The01Geek/prflow/tree/main/skills/retrospective. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Retrospective?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Retrospective?

As many as you like. As long as a model supports skills, you can use Retrospective with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Retrospective AI skill free?

Yes. It is published on GitHub by The01Geek under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇