Files
openprose__prose/packages/std/composites/worker-critic.md
irl-dan 63c8ce4b23 Migrate std and co into packages/ monorepo layout
Move the standard library (previously `github.com/openprose/std`) and
the company-as-prose library (previously local dir
`/Users/sl/code/openprose/company-as-prose`) into this repository
under `packages/std/` and `packages/co/` respectively.

Motivation: concentrate attention on one repository so stars,
discussions, and issues aggregate. `openprose/std` was a genuinely
useful asset nobody could find because ~50 stars on its own repo
told a much worse story than 1,106 stars on `openprose/prose`
would have. Same logic applies to co.

Layout choice: `packages/{std,co}/` rather than `std/` + `co/` at
the repo root. Root siblings accumulate over time, and a flat
top-level made future libraries (hub, kit, dev, etc.) compete with
spec infrastructure. `packages/` is bounded — new libraries land
there without cluttering root. Matches pnpm/Deno/monorepo tooling
conventions.

Changes:

- New: `packages/std/` (copied from openprose/std, `.git` excluded)
- New: `packages/co/` (copied from company-as-prose, README updated
  for new name and location)
- `packages/std/README.md`: updated shorthand expansion reference
- `packages/co/README.md`: renamed "company-as-prose" to "co" in
  self-references, added std-vs-co split heuristic, updated
  `prose run` examples to use `co/` shorthand
- `skills/open-prose/deps.md`:
  - `std/` shorthand now expands to
    `github.com/openprose/prose/packages/std/`
  - `co/` shorthand added, expands to
    `github.com/openprose/prose/packages/co/`
  - Both shorthands resolve into the same clone of
    `openprose/prose` under `.deps/github.com/openprose/prose/`;
    `packages/std/` and `packages/co/` are sibling subdirectories
  - `prose.lock` format row updated to `host/owner/repo sha`
    (matches the explicit-host resolution that landed in #35)
  - `.deps/` directory structure example redrawn to show the
    `{host}/{owner}/{repo}/packages/{std,co}/...` shape
  - Error message and Interaction tables refreshed accordingly
  - Resolution Algorithm step 1 now documents both shorthands
- `skills/open-prose/forme.md`: Step 2 resolution list updated to
  include co/ shorthand and the host-keyed `.deps/` layout
- `skills/open-prose/help.md`: Q&A "How do dependencies work?"
  rewritten to name both shorthands and the new explicit-host model
- `skills/open-prose/state/filesystem.md`: `.deps/` directory
  tree re-drawn to include the `{host}/` layer with std/co as
  sibling packages inside the clone
- `skills/open-prose/guidance/patterns.md`,
  `skills/open-prose/guidance/antipatterns.md`: examples now use
  the `std/` shorthand or explicit `github.com/...` rather than
  the deprecated bare `openprose/std/` form
- `skills/open-prose/examples/registry-import/index.md`: sample
  service reference switched to `std/evals/inspector`
- `README.md`: Stdlib badge replaced with internal `packages/std/`
  link plus new `packages/co/` link; "Standard Library" section
  rewritten as "Libraries" with both packages described

Out of scope for this PR (deliberately):
- Tooling changes to `prose install` and Forme's `.deps/` writer
  to adopt the `{host}/{owner}/{repo}/` layout in practice — this
  PR is spec-only. Existing tooling continues to work against
  whatever layout it currently implements; the spec now describes
  the target.
- Retiring the `openprose/std` GitHub repo. Delete that repo
  separately after this merges.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 14:14:31 -07:00

3.7 KiB

name, kind
name kind
worker-critic composite

Worker-Critic

One agent works, another evaluates. Retry until the critic accepts or budget exhausts.

Description

Iteratively refines a worker's output by looping through critic evaluation until acceptance or budget exhaustion.

Metadata

  • version: 0.1.0

Slots

  • worker (primary)
    • requires: task_brief, optional critique from previous attempt
    • ensures: result text
  • critic
    • requires: worker result, original task_brief, criteria
    • ensures: structured verdict with accept/reject, reasoning, issues, suggestions

Config

  • task_brief (string, default: none): The task to pass to the worker
  • criteria (string, default: none): Acceptance criteria the critic evaluates against
  • max_rounds (integer, default: 3): Maximum number of worker-critic cycles before returning best attempt

Invariants

  • Worker never sees the raw criteria — only the critique derived from them
  • Critic always receives the original task brief alongside the result
  • On accept the loop exits immediately and returns the worker's result
  • After max_rounds exhausted the final attempt is returned with its critique

Shape

  • self: manage retry loop, pass critique to worker, return accepted result
  • delegates:
    • worker: produce result from brief
    • critic: evaluate result against criteria
  • prohibited: none

Requires

  • &compositeState exists at __compositeState with: worker: string -- component name to use as worker critic: string -- component name to use as critic task_brief: string -- the task to pass to the worker criteria: string -- acceptance criteria for the critic max_rounds: number -- (optional, default 3)

Ensures

  • Worker receives only the task brief (first attempt) or task brief + critique (retries)
  • Critic receives the worker's result AND the original task brief AND the criteria
  • Critic returns { verdict: "accept" | "reject", reasoning, issues, suggestions }
  • On reject: worker receives the critique as learning signal — not the raw task repeated
  • On accept: return the worker's result immediately
  • After max_rounds exhausted: return the best attempt with the final critique
  • &compositeState.result contains the final output
  • &compositeState.attempts contains the count

Delegation Loop

const { worker, critic, task_brief, criteria, max_rounds = 3 } = __compositeState;
let lastResult = null;
let lastCritique = null;

for (let attempt = 0; attempt < max_rounds; attempt++) {
  // Build worker brief
  let workerBrief = task_brief;
  if (lastCritique) {
    workerBrief += `\n\nPrevious attempt was rejected.\nCritique: ${lastCritique.reasoning}`;
    if (lastCritique.suggestions?.length) {
      workerBrief += `\nSuggestions: ${lastCritique.suggestions.join("; ")}`;
    }
  }

  lastResult = await rlm(workerBrief, null, { use: worker });

  // Build critic brief
  const criticBrief = `Evaluate this result against the criteria.\n\nOriginal task: ${task_brief}\nCriteria: ${criteria}\n\nResult to evaluate:\n${lastResult}`;
  const verdict = await rlm(criticBrief, null, { use: critic });

  // Parse verdict — critic should return structured assessment
  if (verdict?.verdict === "accept" || /accept/i.test(String(verdict))) {
    __compositeState.result = lastResult;
    __compositeState.attempts = attempt + 1;
    return(lastResult);
  }

  lastCritique = verdict;
}

__compositeState.result = lastResult;
__compositeState.attempts = max_rounds;
__compositeState.final_critique = lastCritique;
return(lastResult);

Notes

This is a seed pattern in the standard library. The worker does not know it will be critiqued. The critic does not know its verdict triggers a retry. Multi-polarity is managed here, not in the children.