fulcra-agent-review ยท git:20260713.8ab67b8 ยท 2026-07-13 ยท sha256 c98fa147b0c0ae89
fulcra-agent-review git:20260713.8ab67b8A
Immutable. This exact content is served forever at /api/v1/blob/c98fa147b0c0ae89.
---
name: fulcra-agent-review
description: "Add a review handshake to a fulcra-agent-teams space: request review of an artifact (PR, doc, plan), reviewers leave verdicts, and the overall APPROVED/CHANGES/PENDING state is computed deterministically โ including required-reviewer gating."
homepage: "https://github.com/ashfulcra/fulcra-tools"
license: "MIT"
user-invocable: true
metadata: { "openclaw": { "emoji": "๐" } }
---
# Fulcra Agent Review
Enhances the [`fulcra-agent-teams`](https://github.com/fulcradynamics/agent-skills) skill with a
lightweight **review handshake**: an author requests review of an artifact, one or more reviewers leave
verdicts, and the overall state is folded deterministically. The single-file actions (request, verdict)
are prose over `fulcra-api file` + the teams inbox; the **verdict tally** is a `coord-engine` command
(folding multiple reviewers is a derived state โ code, not eyeballing).
## Where to start โ the re-entrancy probes
Before requesting a review or leaving a verdict, probe where the handshake already stands. Enter at the
**first probe that fails** (per the repo's skill-quality pattern, `docs/skill-quality-pattern.md`);
requesting is a single-file write and a verdict is an overwrite (re-uploading your verdict file just
supersedes it), so re-entry never corrupts the tally:
| Probe (run in order) | Command | Passes when | If it fails, enter at |
|---|---|---|---|
| Engine + auth usable? | `coord-engine doctor <team>` | exits 0 and the last line is exactly `doctor: healthy` | fix engine/auth first (see fulcra-agent-reconcile) โ do NOT tally against a broken engine |
| Any reviews owed me? | `coord-engine needs-me <team> --agent <me>` | NO `[REVIEW] pending verdict:` row prints for you โ no `pending_required` entry names you (NON-mutating read) | **Leave a verdict** โ each printed `[REVIEW] pending verdict: <slug> (required: โฆ)` row is an open obligation on you; write your verdict at the echoed path `team/<team>/review/<slug>/verdicts/<required-token>.md` (the filename stem is the `required` token the row names โ a role, or a direct agent name โ not your own), then verify + ack per [Lifecycle](#lifecycle) step 2 |
| Known artifact's handshake state settled? | `coord-engine review status <team> <slug>` | prints a line beginning `review <slug> in team/<team>:` ending in `APPROVED` or `CHANGES` (deterministic fold โ never tally by hand) | if it prints `PENDING`, the review is not settled โ chase the `awaiting required:` reviewers per [Lifecycle](#lifecycle) step 3 |
All probes clean โ nothing is blocked on your verdict and any artifact you name is at its folded state;
proceed to request a new review or advance an existing one below.
## Layout (under `team/<team>/review/<slug>/`)
- **`review/<slug>.md`** โ the review request, written by `review request` (below). OKF `type: Review`.
`<slug>` is a short id for the artifact (e.g. `pr-42`). The `required` list is what the tally gates on
(roles preferred โ resolved to fresh lease holders):
```yaml
---
type: Review
schema: review-request/v1
requested_by: ash
of: https://github.com/org/repo/pull/42
required: [reviewer, security] # all must approve for APPROVED (string "a, b" also accepted)
ts: 2026-07-08T12:00:00Z
---
Review requested: <artifact>
```
- **`review/<slug>/verdicts/<required-token>.md`** โ one verdict per requirement. The **filename stem is
the tally key** and must equal a `required` token (the role, or the direct agent name), not the holder's
own name. OKF `type: Verdict`:
```yaml
---
type: Verdict
reviewer: alice # who signed off (informational โ the FILENAME drives the tally)
verdict: approve # approve | changes
---
Notes / requested changes.
```
## Lifecycle
1. **Request** (author) โ one command, never a hand-written doc and never a bare `tell`:
```bash
coord-engine review request <team> <slug-or-title> \
--of <artifact> --reviewer <role> [--reviewer <role> โฆ] [--from <me>]
```
`<slug-or-title>` slugs exactly the way a `tell` title does (an already-slug-like arg round-trips
unchanged); name **roles**, not identities, so `needs-me` resolves the fresh lease holders
(role-routing doctrine). The command writes `review/<slug>.md` at the exact path the tally reads and
echoes, per required reviewer, the verdict path to fill:
```
review <slug> requested (required: reviewer, security)
reviewer reviewer -> file verdict at team/<team>/review/<slug>/verdicts/reviewer.md
```
Requesting the same slug twice is refused (exit 1, `already exists`) rather than clobbering.
**Why the verb, not a `tell`:** the request doc itself IS the obligation. It lands in every required
reviewer's `needs-me` as a `pending_required` marker and persists there until that reviewer's verdict
file exists at the echoed path โ the tally folds presence-of-file, so the duty survives sessions,
hosts, and compaction with no one having to remember it. A bare `tell` is the failure mode this
replaces: an acked directive leaves **no** durable marker, so a dropped or forgotten review vanishes
silently and the merge gates on nothing. Never request reviews via `tell`.
2. **Verdict** (reviewer): write the verdict file at the **exact path `review request` echoed** for you โ
**slug-exact**, and named after the **requirement, not yourself**. The **filename stem is what the tally
matches against the `required` token**, not the frontmatter `reviewer:` field:
- **role requirement** (`required: reviewer`) โ `review/<review-slug>/verdicts/reviewer.md`, whoever
holds the role. Writing `verdicts/alice.md` records an approval the tally can't credit: `reviewer`
stays in `pending_required` and the review can never reach APPROVED.
- **direct requirement** (`required: alice`) โ `review/<review-slug>/verdicts/alice.md`.
Write it with `verdict: approve|changes` and notes. The **verdict file is what discharges the
obligation** (the tally folds presence-of-file). Then **verify** the fold reflects it (`coord-engine
review status <team> <review-slug>` โ that requirement must no longer be in `pending_required`) and
**only then ack** the accompanying directive as inbox hygiene โ using the **directive** id, NOT the
`<review-slug>`: the review-request directive has its own slug `review-request-<review-slug>-<hash>`, so
ack that (read it from `coord-engine inbox <team> --agent <you> --json` โ the `name` of the `REVIEW
REQUEST: <review-slug>` row), never `--ack <review-slug>` (which the directive would never match,
leaving it re-notifying). Never satisfy a review by acking without a verdict file, or against a
different review's status. To change your mind, re-upload the same file (overwrites; the File Store
keeps the history). **Fail-closed:** a `changes` verdict keeps blocking until that same file is
re-uploaded as `approve` โ pushing a fix does **not** clear it; the requirement must be re-affirmed.
3. **Check state** (anyone) โ deterministic fold, do not tally by hand:
```bash
coord-engine review status <team> <slug> --json
# -> {state: APPROVED|CHANGES|PENDING, approvals, changes, required, pending_required}
```
**CHANGES** if any reviewer requests changes; **APPROVED** if there's an approval, no outstanding
changes, and all `required` reviewers approved; **PENDING** otherwise.
A review that reaches **APPROVED** with every `required` verdict in is *settled*: the fold caches it
at `verdicts/.settled` so the fan-out folds (`briefing`/`needs-me`) skip it. Settled reviews are
immutable โ re-opening under a changed `required` list is a **new slug**, never an edit to the old
one. `review status` never trusts the marker: it recomputes the full tally on every call, so a stale
or wrong marker self-heals on direct query.
`review status` **exits 1** with `... unreadable (missing slug or degraded transport) โ tally unknown,
retry` when the review doc can't be read โ a transport failure or a nonexistent slug, indistinguishable
and both UNKNOWN (without the `required` list a lone approval would tally as a clean APPROVED and
durably hide a pending review). A watcher must read rc 1 as *transport down, retry*, never as a state.
**Nudge only against a live obligation.** Before nudging a reviewer about a pending review, run
`review status <team> <slug> --json` on the *exact* slug and nudge only if `pending_required` still
names that assignee โ a verdict may have landed since you last looked, and a stale nudge is noise that
trains reviewers to ignore the real ones. rc 1 is *transport down, retry* (not a settled state), so
never take an unreadable tally as "no longer pending" and suppress a legitimate nudge on it.
## When to use
- Gating a merge/land on review in a multi-agent team.
- Any "N reviewers must sign off" flow where you need an unambiguous, non-drifting verdict state.
See [`references/review-cli.md`](references/review-cli.md) for exact commands.