Files
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.8 KiB

name, kind
name kind
race composite

Race

Multiple delegates work on the same task in parallel. First acceptable result wins. Others are cancelled.

Metadata

  • version: 0.1.0
  • role: coordinator

Runtime

  • state.reads: &controlState
  • state.writes: &controlState

Slots

  • candidates

Shape

  • self: dispatch same brief to all candidates, accept first good result, cancel rest
  • delegates:
    • candidate_1..candidate_N: attempt the task
  • prohibited: waiting for all candidates when one has already succeeded

Requires

  • &controlState exists at __controlState with: candidates: string[] -- component names for each candidate task_brief: string -- the task (same brief goes to all candidates) acceptance_criteria: string -- (optional) declarative description of what makes a result acceptable. Default: any non-error result is accepted.

Ensures

  • All candidates receive the same brief and start in parallel
  • First result that meets acceptance criteria is returned immediately
  • Remaining candidates are cancelled (best-effort)
  • No candidate knows other candidates exist
  • If no candidate produces an acceptable result: return null with all attempts in failure_history
  • &controlState.result contains the winning result (or null)
  • &controlState.winner contains the winning candidate's name (or null)
  • &controlState.attempts contains the number of candidates that completed before a winner

Delegation

const { candidates, task_brief, acceptance_criteria } = __controlState;

// All candidates race in parallel
const results = await Promise.all(
  candidates.map(async (candidate) => {
    try {
      const result = await rlm(task_brief, null, { use: candidate });
      return { candidate, result, error: null };
    } catch (e) {
      return { candidate, result: null, error: e.message };
    }
  })
);

// Evaluate results in order of candidate preference (first in list = highest preference)
for (const entry of results) {
  if (entry.error) continue;

  if (acceptance_criteria) {
    const evalBrief = `Does this result meet the acceptance criteria?\n\nCriteria: "${acceptance_criteria}"\n\nResult:\n${String(entry.result).slice(0, 2000)}\n\nRespond with JSON: { "acceptable": true/false }`;
    const evalResult = await rlm(evalBrief, null, {});
    try {
      const jsonMatch = String(evalResult).match(/\{[\s\S]*"acceptable"[\s\S]*\}/);
      const evaluation = JSON.parse(jsonMatch[0]);
      if (!evaluation.acceptable) continue;
    } catch {
      // Unparseable evaluation — treat as acceptable (fail open)
    }
  }

  __controlState.result = entry.result;
  __controlState.winner = entry.candidate;
  __controlState.attempts = results.indexOf(entry) + 1;
  return(entry.result);
}

// No winner
__controlState.result = null;
__controlState.winner = null;
__controlState.attempts = candidates.length;
return(null);

Notes

No candidate knows other candidates exist. Each receives the same brief and works independently.

True cancellation depends on the runtime. In environments without preemption, all candidates run to completion and the first acceptable result (in preference order) wins. The candidates array order expresses preference — if two candidates both succeed, the earlier-listed one wins.

Different from fan-out: fan-out waits for ALL results and returns the full collection. Race returns the FIRST acceptable result. Use race for speculative execution, redundancy, or when multiple approaches might work but you only need one.

Different from fallback-chain: fallback-chain tries candidates sequentially (A, then B if A fails, then C). Race tries all candidates simultaneously. Use fallback-chain when later candidates are cheaper or less preferred and should only run if earlier ones fail. Use race when all candidates are worth trying in parallel.