Review Proposals logo

Review Proposals

Community
jpicklyk
review-proposals

Triages pending improvement-proposal MCP items — presents each with its scope and evidence, collects an accept/reject/defer decision per proposal, and carries out the disposition: project-scoped acceptances get their exact YAML applied to .taskorchestrator/config.yaml and pushed per-root; global acceptances get a tracked GitHub issue (or dwell in review for the maintainer); rejections are recorded and cancelled; deferrals are recorded and left in queue. Use when a user says: review proposals, triage proposals, pending proposals, what proposals are open, adopt proposals, process improvement proposals, accept or reject proposals.

Overview

Publisherjpicklyk
Repositorytask-orchestrator
Skill namereview-proposals
Stars
204
Forks
22
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 jpicklyk on GitHub. Read the source before you install it.

Installation

Install the Review Proposals 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.
  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/jpicklyk/task-orchestrator.git /tmp/task-orchestrator
mkdir -p .claude/skills
cp -r /tmp/task-orchestrator/claude-plugins/task-orchestrator/skills/review-proposals .claude/skills/review-proposals
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Review Proposals 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 Review Proposals 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 Review Proposals 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.

Review Proposals

Triages improvement-proposal MCP items created by /session-retrospective when a cross-session trend graduates. Presents pending proposals, collects a per-proposal decision, and carries out the disposition — including applying project-scoped config changes and filing/linking GitHub issues for global changes.

Shared GitHub conventions (issue template, scrub rule, gh guard, dedup procedure) are not duplicated here — see <skill-base-dir>/../session-retrospective/references/github-feedback.md for the full C2/C4/C5 contract and dedup steps referenced throughout Step 5.


Step 0 — Parse Arguments and Read Config

Parse $ARGUMENTS:

  • A UUID → single-proposal mode: skip Step 1 discovery entirely: go straight to Step 2 for that one item.
  • --scope global or --scope project → filter Step 1 discovery to only that scope's query. Absent → run both discovery queries.
  • Neither → full discovery, both scopes.

Read the workspace .taskorchestrator/config.yaml directly (file read, not MCP) for:

  • project.rootId / project.name — enables the project-scoped discovery query and per-root push in Step 5. If absent, project-scoped discovery and project-scoped acceptance are unavailable — proceed with global-only discovery.
  • retrospective.github_feedback.enabled — default false if the block or key is absent.
  • retrospective.github_feedback.repo — default jpicklyk/task-orchestrator if absent.

Step 1 — Discovery

Skip this step entirely in single-proposal mode (UUID argument supplied).

Run both queries in parallel (unless --scope narrows to one):

Global:

query_items(operation="search", query="Improvement Proposals", limit=5)

Find the "Improvement Proposals" container from the result, then:

query_items(operation="overview", anchorId="<container-uuid>", includeChildren=true)

Project-scoped (only if project.rootId is known):

query_items(operation="search", tags="improvement-proposal", ancestorId="<rootId>", limit=50)

Partition results

For each candidate item, classify by role and notes:

  • Pending — role queue, no adoption-decision note yet. The main triage set.
  • Deferred — role queue, has an adoption-decision note whose body starts with "deferred". List in a separate section; do not re-offer for a decision unless the user explicitly asks to revisit deferred proposals, or the stated revisit condition in the note plausibly holds now.
  • Stalled — role work (item was accepted and advanced to work via start but never reached review/terminal — an interrupted or stuck adoption). Flag as stalled adoption; offer to resume (fill remaining notes and complete) or cancel.
  • Terminal — skip; already resolved (accepted-and-verified, rejected, or fully handed off).

If nothing is pending (no queue-role items without an adoption-decision, and no stalled items), report:

No pending improvement proposals.

and stop — do not proceed to later steps.


Step 2 — Load Proposal Detail

For each candidate (pending + stalled; cap at 15 — if more, take the 15 oldest by creation and note the overflow count), fetch notes:

query_notes(operation="list", itemId="<uuid>", includeBody=true)

From the returned notes, extract:

  • Proposal body — the proposal note (or the item's summary if the note is absent).
  • Scope clause — read the scope classification the note states (global vs project-specific). If it cannot be parsed unambiguously, treat as global — this is conservative: never auto-edit a project's config.yaml on an ambiguous scope reading.
  • Existing github-issue: line — the last line of the proposal note body, if present, in the form github-issue: <url>. Carry this forward so Step 5's global-accept path can reuse it instead of filing a duplicate.

Step 3 — Present Triage Table

Render one compact table covering all pending + stalled candidates (deferred proposals get their own short list underneath, not full rows):

| # | ID | Proposal | Scope | Evidence | Age | Issue |
|---|-----|----------|-------|----------|-----|-------|
| 1 | `a1b2c3d4` | Add `session-tracking` maxLength guard | project | 3 sessions, sparse-note trend | 4d | — |
| 2 | `e5f6a7b8` | Nudge cooldown too short for solo dev | global | 2 sessions, friction theme | 1d | #142 |
  • Evidence — a one-line summary of the trend/session count that graduated this proposal (from the proposal body or summary).
  • Age — days since creation.
  • Issue — existing github-issue: link if present, else .

Below the table, list deferred proposals as a short reminder line each: `<short-id>` — deferred: <condition> (deferred <date>) and stalled proposals as `<short-id>` — stalled in work: <hint>.


Step 4 — Collect Decisions

For each pending/stalled proposal, show the proposed change (the exact YAML the proposal names, or the file + section it targets) and ask via AskUserQuestion:

◆ "<proposal title>"  [scope: project]
  Proposed change:
  <exact YAML or file+section from the proposal note>

  What would you like to do?
  1. Accept — apply this change
  2. Reject — do not adopt, record why
  3. Defer — revisit later, leave in queue
  4. Skip — no decision this round

If the user picks Reject or Defer, either take a one-line reason from their follow-up or offer an "Other" free-text option so the rationale can be recorded verbatim in the adoption-decision note. Skip leaves the item untouched — no note upsert, no advance.


Step 5 — Carry Out Dispositions

Accept — project-scoped

  1. Extract the exact YAML from the proposal note. If the proposal doesn't include ready-to-apply YAML, draft the edit yourself, show it to the user as a diff against the current .taskorchestrator/config.yaml, and confirm via AskUserQuestion before applying anything.
  2. If the proposal targets a different rootId than this workspace's project.rootId: do not edit this workspace's config. Instead, upsert adoption-decision recording that the change must be applied from the owning workspace, and treat the item as deferred (leave it in queue) — do not advance it.
  3. Otherwise, Edit the workspace .taskorchestrator/config.yaml to apply the change.
  4. Validate before pushing — same bar as manage-schemas' config-format rules (see <skill-base-dir>/../manage-schemas/references/config-format.md):
    • File still parses as valid YAML.
    • Any note role values are limited to queue | work | review.
    • Existing sections are untouched — the edit is additive/targeted, not a rewrite.
  5. Push: manage_project_config(operation="push", rootId="<rootId>", configYaml="<full file content>").
    • On CONFLICT_ERROR: never force blindly. Call manage_project_config(operation="get", rootId="<rootId>"), diff the server's stored config against the local file, and surface the divergence to the user. Only re-push with force: true after explicit user confirmation.
    • A response listing ignoredSections containing retrospective and/or project is expected, not an error — those sections are client-side-only and never resolved server-side.
  6. Upsert the closure note:
    manage_notes(operation="upsert", notes=[{
      itemId: "<uuid>",
      key: "adoption-decision",
      role: "work",
      body: "accepted — <rationale>. Applied to .taskorchestrator/config.yaml (<section>), pushed per-root <rootId> on <YYYY-MM-DD>."
    }])
  7. Advance with start twice — queue→work, then work→review:
    advance_item(transitions=[{itemId: "<uuid>", trigger: "start"}])
    advance_item(transitions=[{itemId: "<uuid>", trigger: "start"}])
    Do not use complete here — complete jumps straight to terminal and gate-checks required notes across all phases, so it would be blocked by the intentionally-unfilled outcome-verification note. start checks only the current phase's notes (adoption-decision, just filled) and lands in review.
  8. Stop in review — do not advance further. State this explicitly to the user: the item dwells in review with outcome-verification intentionally unfilled — a future /session-retrospective run verifies whether the applied change actually helped, and fills that note then. This is by design, not an oversight.
  9. Apply the Source-trend write-back below — the config change is applied, so the source trend retires now.

Accept — global

  1. Ensure a GitHub issue exists for the change:
    • If Step 2 found an existing github-issue: line, reuse that URL — no new issue.
    • Else, if retrospective.github_feedback.enabled is true and the gh guard (C5 in the shared reference) passes, file an issue per the C4 template and append the record-back line (C2) to the proposal note.
    • Else, proceed without an issue — say so plainly in the adoption-decision note.
  2. Distinguish install type via:
    gh repo view <repo> --json viewerPermission -q .viewerPermission
    • ADMIN / MAINTAIN / WRITE ⇒ maintainer path. Upsert adoption-decision: "accepted — will implement; tracked in <url>." Advance start twice (queue→work, work→review) — the same dwell-in-review pattern as project-scoped acceptance, with the same complete-would-gate-block caveat; outcome-verification is filled by a later retrospective.
    • Anything else, or gh unavailable ⇒ community handoff (default). Upsert both notes:
      manage_notes(operation="upsert", notes=[
        {
          itemId: "<uuid>",
          key: "adoption-decision",
          role: "work",
          body: "accepted — delegated upstream; tracked in <issue-url>."
        },
        {
          itemId: "<uuid>",
          key: "outcome-verification",
          role: "review",
          body: "delegated upstream — verification happens in <repo> issue #<N>, not this workspace. Verdict: n/a (handed off)."
        }
      ])
      Then advance to terminal in two calls:
      advance_item(transitions=[{itemId: "<uuid>", trigger: "start"}])
      advance_item(transitions=[{itemId: "<uuid>", trigger: "complete"}])
      start enters work; complete then goes straight to terminal — its all-phases note gate passes because proposal, adoption-decision, AND outcome-verification are all filled. The item is now fully terminal — the GitHub issue is the living tracker, not this workspace. Either way, finish with the Source-trend write-back below: community handoff (terminal) retires the source trend now; the maintainer path leaves it active until a later retrospective verifies the landed change.

Reject

manage_notes(operation="upsert", notes=[{
  itemId: "<uuid>",
  key: "adoption-decision",
  role: "work",
  body: "rejected — <reason>. Recorded so future retrospectives do not re-propose."
}])
advance_item(transitions=[{itemId: "<uuid>", trigger: "cancel"}])

cancel is a single gate-free call — it goes straight to terminal from any non-terminal role, no note gate involved. Do not fill outcome-verification for a rejection; there is no change to verify. Then apply the Source-trend write-back below.

Defer

manage_notes(operation="upsert", notes=[{
  itemId: "<uuid>",
  key: "adoption-decision",
  role: "work",
  body: "deferred — revisit when <condition>. Deferred on <YYYY-MM-DD>."
}])

No advance_item call — the item stays in queue. A later Accept decision overwrites this same note via upsert (last-writer-wins), so re-running this skill and accepting a previously-deferred proposal works without any special-casing.

Source-trend write-back

Most proposals graduated from a trend item (tags retrospective-trend, one item per cross-session pattern) whose summary carries GRADUATED -> proposal <short-id>. After carrying out a disposition, close the loop at the source. Everything here uses only gate-free operations (manage_items update, advance_item with cancel) — never start or complete on a trend item — so it is safe under any schema config. Locate the source trend:

query_items(operation="search", query="<proposal-short-id>", scope={tags: ["retrospective-trend"]})

Skip this write-back silently when no trend matches — proposals can also be created ad hoc without a graduating trend.

  • After Reject: append one line to the trend item's summary via manage_items(operation="update"): proposal <short-id> rejected <YYYY-MM-DD> — do not re-graduate; see its adoption-decision note. The trend stays active and keeps accumulating evidence, but every future retrospective now sees the rejection inline in the Step 4.2 listing instead of having to discover the cancelled proposal.
  • After Accept, once the change is actually applied or landed (project-scoped config pushed, or a community-handoff issue filed and the item taken terminal): retire the trend — advance_item(itemId="<trend-uuid>", trigger="cancel", summary="archived: addressed via proposal <short-id>"). When adoption is only tracked so far (maintainer path dwelling in review awaiting implementation), leave the trend active — the later retrospective that fills outcome-verification retires it once the change demonstrably held.
  • After Defer: no trend write-back.

Step 6 — Report

Render a summary table of the run:

## Proposal Review — <YYYY-MM-DD>

| ID | Proposal | Disposition | Action |
|----|----------|-------------|--------|
| `a1b2c3d4` | Add maxLength guard | Accepted | pushed .taskorchestrator/config.yaml (work_item_schemas), dwelling in review |
| `e5f6a7b8` | Nudge cooldown | Accepted (global) | filed https://github.com/jpicklyk/task-orchestrator/issues/143, terminal |
| `c9d0e1f2` | Rename note key | Rejected | cancelled — duplicate of existing key |
| `f3a4b5c6` | Widen skill pointer | Deferred | revisit when trait usage grows |

Close with a reminder: accepted items dwelling in review are not stuck — their outcome-verification note is filled by a future /session-retrospective run once the applied change has had a chance to show effect.


Troubleshooting

gh unavailable or unauthenticated

Cause: gh auth status failed (not installed, not logged in, or network issue) — see the C5 guard in <skill-base-dir>/../session-retrospective/references/github-feedback.md.

Solution: For global acceptances, proceed without filing an issue and say so in the adoption-decision note ("accepted — no GitHub issue filed (gh unavailable)."). This never blocks the disposition — filing is best-effort. The user can file the issue manually later and record the URL back onto the proposal note themselves.


CONFLICT_ERROR on manage_project_config push

Cause: The stored per-root config's fingerprint has moved since this workspace last read it — someone else (another session, manage-schemas, or quick-start) pushed a newer version.

Solution: Never force blindly. Call manage_project_config(operation="get", rootId="<rootId>"), diff the returned configYaml against the local file, and show the user exactly what differs. Only retry with force: true after the user explicitly confirms which version should win — a blind force can silently revert someone else's concurrent change.


Proposal has no exact YAML to apply

Cause: The proposal body describes the change in prose only (common for proposals that predate a YAML-inclusion convention, or for changes that aren't schema edits — e.g., a skill wording tweak).

Solution: Draft the edit yourself from the proposal's description, show it to the user as a diff before touching any file, and get explicit confirmation via AskUserQuestion before applying. Do not guess silently and push.


Proposal targets a different workspace's rootId

Cause: The proposal's stated scope names a project root UUID that doesn't match this workspace's project.rootId — the proposal was likely created while working in a different project sharing the same MCP database.

Solution: Do not edit this workspace's .taskorchestrator/config.yaml. Record in the adoption-decision note that the change must be applied from the owning workspace (name the rootId if known), and treat the proposal as deferred — leave it in queue rather than advancing it, since no action was actually taken here.

Frequently asked questions

What does the Review Proposals AI skill do?

Triages pending improvement-proposal MCP items — presents each with its scope and evidence, collects an accept/reject/defer decision per proposal, and carries out the disposition: project-scoped acceptances get their exact YAML applied to .taskorchestrator/config.yaml and pushed per-root; global acceptances get a tracked GitHub issue (or dwell in review for the maintainer); rejections are recorded and cancelled; deferrals are recorded and left in queue. Use when a user says: review proposals, triage proposals, pending proposals, what proposals are open, adopt proposals, process improvement...

Why use Review Proposals on TypingMind?

Because you install it once and use it with any model. Review Proposals 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 Review Proposals in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/jpicklyk/task-orchestrator/tree/main/claude-plugins/task-orchestrator/skills/review-proposals. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Review Proposals?

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 Review Proposals?

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

Is the Review Proposals AI skill free?

Yes. It is published on GitHub by jpicklyk 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 👇