Artifact Input Contract
River Review is a review agent that consumes artifacts produced by upstream workflows such as PlanGate as external inputs and performs review, QA, and double-check operations. This document defines the input contract that River Review can read stably.
Related issues: #516 (Task) / #508 (Capability) / #507 (Epic)
Policy
- River Review operates artifact-driven and does not depend on PlanGate-internal commands or on a specific directory layout.
- Inputs are consumed on a file path basis; only the content format (Markdown / JSON / XML / plain) is contracted.
- Behavior when a file is missing (skip / degrade / error) is defined per artifact.
- When adding a new artifact, update this document and preserve backward compatibility.
Artifact Catalog
The input artifacts recognized by River Review are listed below. See "Legend" at the end for column semantics.
| ID | Example filename | Format | Required | Schema / reference | Role |
|---|---|---|---|---|---|
pbi-input | pbi-input.md | Markdown | Optional (rec.) | Free-form | Input spec / background of the Product Backlog Item |
plan | plan.md | Markdown | Optional (rec.) | Free-form | Implementation plan and design rationale |
design | — (no default) | Markdown | Optional (rec.) | Free-form | Design document (architecture and technical premises) |
todo | todo.md | Markdown | Optional | Free-form (checklist) | Implementation tasks and progress |
test-cases | test-cases.md | Markdown | Optional | Free-form (bullets or tables) | Test case design |
review-self | review-self.md | Markdown | Optional | Free-form | Self-review by the author |
review-external | review-external.md | Markdown | Optional | Free-form | External review (AI or human) |
diff | diff.patch | unified diff | Required (alt.) | git diff compatible | Review target diff; falls back to git diff when absent |
junit | junit.xml | XML | Optional | JUnit XML | Unit/integration test results |
coverage | coverage.xml etc. | XML / JSON | Optional | One of Cobertura / LCOV / Istanbul JSON | Coverage report |
lint | lint.json etc. | JSON / plain | Optional | ESLint JSON, stylelint JSON, or tool-specific plain | Lint result |
typecheck | typecheck.txt etc. | plain / JSON | Optional | tsc --pretty=false or tool-specific plain | Type checker result |
findings-pool | findings-pool.json | JSON | Optional | findings-pool section in this document | Aggregated findings[] history from multiple Review Artifacts |
tdd-ledger | tdd-ledger.json | JSON | Optional | tdd-ledger section in this document | RED/GREEN/REFACTOR VERIFY phase execution evidence for TDD |
Legend
- Required
Required: Without this input River Review aborts.Required (alt.): If absent, an alternative (e.g.git diff) is used automatically.Optional: Missing files are tolerated; related skills are skipped or degraded.Optional (rec.): Missing is allowed, but review quality drops meaningfully.
- Format: Encoding and syntax. Multiple accepted formats are comma-separated.
- Example filename: The well-known filename probed by current-directory detection, the third channel described in "Input channels" below. An artifact marked
— (no default)is excluded from that probe and resolves only when supplied explicitly via a CLI argument or the config file.
Per-artifact Contract
pbi-input / plan / todo / test-cases
- Format: UTF-8 Markdown. Heading structure and bullets are unconstrained.
- Size guideline: 100 KB or less per file recommended. Beyond that, River Review may apply diff optimization (summarization / trimming).
- When absent: Skills referencing the artifact skip their observation and record it in
skippedSkills. reviewSignals(optional): Structured signals for automatic reviewer selection, supplied as a field on the review plan object rather than in the Markdown body. See the next section.
reviewSignals (optional signals carried on the review plan)
reviewSignals is an optional input that gives extra hints to reviewer role auto-selection under --reviewers auto. It is supplied as a field on the review plan object, not in the Markdown body of plan.md (the implementation reads it at context.plan.reviewSignals). River Review owns the vocabulary; it is not tied to the format of any particular upstream workflow.
- Format: JSON object with a
stagestring and a set of boolean signal keys. - Supply channel: Programmatic embedding — the review plan passed to
runLocalReview({ context }). It is not exposed as a CLI flag or a dedicated file. - Producer: No producer exists in this repository; supplying the value is the host's responsibility. PlanGate is only one possible supplier, and River Review acts purely as a consumer.
- When absent: Absence is the default.
--reviewers autoselects roles from file types and risk assessment alone, behaving exactly as it did beforereviewSignalsexisted.
stage vocabulary
stage | Roles added |
|---|---|
requirements | (none) |
plan | security-scanner, test-gap |
design | frontend-reviewer |
exec | security-scanner |
verify | test-gap |
release | security-scanner |
An unknown stage value is ignored and adds no role.
Signal keys
| Signal key | Role added |
|---|---|
touchesAuth | security-scanner |
changesPermissions | security-scanner |
handlesSensitiveData | security-scanner |
databaseMigration | security-scanner |
breakingChange | security-scanner |
changesUi | frontend-reviewer |
changesUserFlow | frontend-reviewer |
deploymentChange | ci-cd-reviewer |
- Only truthy keys are evaluated; unknown keys are ignored.
changesPublicApi/changesCliInterface/changesInstallationcorrespond to the devex Lens and deliberately map to no role, because no dedicated role exists for it.- Signals only add roles. They never remove anything from the existing selection, including the always-on
bug-hunter. - For the Lens correspondence, see Reviewer Lens Taxonomy.
Example:
{
"reviewSignals": {
"stage": "exec",
"touchesAuth": true,
"changesUi": true
}
}
design
An artifact that supplies the design document. A Flow's declared design input resolves to this same-named artifact ID.
- How to supply it: Always supply it explicitly, with
--artifact design=<path>orartifacts.designin the config file. It has no well-known filename for current-directory detection. - Why it has no default filename:
designis a required input ofdesign-review/technical-review, and a default binding on a required input would let a file that merely sits in the working tree declare the input satisfied (see Runner CLI Reference). - Format: UTF-8 Markdown. Describes architecture, design decisions, and technical premises.
- Size guideline: 100 KB or less per file recommended.
- Flows that require it:
design-review/technical-review - Flows that treat it as optional:
plan-review/requirements-review/research-review - When absent (Flows that require it): The input is reported as unbound.
- When absent (Flows that treat it as optional): The related observation is skipped.
- Distinct from the
stagevocabulary: Thedesignlisted in thestagevocabulary above is a value ofreviewSignals.stageand does not share a vocabulary space with artifact IDs. The names match, but the two are unrelated.
review-self / review-external
- Format: UTF-8 Markdown. Existing AI reviewer (including River Review itself) or human review output may be stored verbatim.
- When absent: Double-check (W-check) skills are skipped.
- Compatibility: Content may follow the
issuedefinition inschemas/output.schema.json, but this is not required.
See also: pages/guides/w-check.md — W-check Practical Guide
findings-pool
-
Format: UTF-8 JSON. An aggregation of
findings[]collected from multiple Review Artifacts (execution history ofriver review exec/river review verify). -
Size guideline: 5 MB or less recommended (typically hundreds of findings). When exceeded, apply rotation or time-window filtering on the CLI side.
-
Schema (provisional):
{"version": "1","entries": [{"timestamp": "2026-04-17T00:00:00Z","phase": "exec","skillId": "plangate-plan-integrity","severity": "major","file": "path/to/file.ts","line": 42,"message": "description","source": "path/to/review-artifact.json"}]}version: Fixed string"1"(bumped on incompatible changes).entries[]: One entry per finding.entries[].phase:execorverify.entries[].skillId: ID of the skill that produced the finding.entries[].severity: External vocabulary (critical/major/minor/info).entries[].file/entries[].line: Target location. Omittable for findings that reference outside the diff.entries[].message: Human-readable description of the finding.entries[].source(optional): Path of the originating Review Artifact. Recommended to preserve provenance.
-
Construction: CLI consumers are expected to build this artifact by reading multiple
review-artifact.jsonfiles and concatenating theirfindings[]intoentries[](implementation tracked in follow-up issue). -
When absent: Skills that require this artifact, such as
plangate-rule-promotion, returnNO_REVIEWat the Pre-execution Gate and skip the promotion-judgement process.
tdd-ledger
-
Format: UTF-8 JSON. A ledger recording each TDD (test-driven development) phase execution. Expected to be produced during exec by upstream workflows such as PlanGate.
-
Role: Records the command and result (exitCode) of the RED / GREEN / REFACTOR VERIFY phases, providing evidence that TDD was performed in the declared, correct order.
-
Schema (provisional):
{"version": "1","task": "TASK-1234","phases": [{"phase": "tdd_red","command": "npm test -- discount.test.ts","exitCode": 1,"conclusion": "Fails as expected because applyDiscount is unimplemented","testCaseRefs": ["TC2"]},{"phase": "tdd_green","command": "npm test -- discount.test.ts","exitCode": 0,"conclusion": "Minimal implementation makes TC2 pass","testCaseRefs": ["TC2"]}]}version: Fixed string"1"(bumped on future incompatible changes).task(optional): Identifier of the corresponding task.phases[].phase: One oftdd_red/tdd_green/refactor_verify/verification.phases[].command: The test/verification command executed.phases[].exitCode: Exit code of the command (tdd_redexpects!= 0;tdd_green/refactor_verify/verificationexpect0).phases[].conclusion(optional): Explanation of the phase outcome or failure reason.phases[].testCaseRefs(optional): Array of correspondingtest-casesIDs.
-
When absent: Skills that require this artifact, such as
plangate-tdd-evidence, returnNO_REVIEWat the Pre-execution Gate and skip the TDD evidence review.
diff
- Format: unified diff (
git diffcompatible). Binary diffs are ignored when the diff is supplied as an artifact. (Whenreview plan|exec --base <ref>obtains the diff from git, the changed-file set comes fromgit diff --name-only, so binary changes and 100% renames are included.) - Requirement: A diff must be supplied by some channel. When no artifact is specified, River Review internally runs
git diff <mergeBase>..HEADand uses the result as the diff. - Precedence against
--base(#2046): an explicitly specified artifact (tier 1 CLI argument / tier 2 config file) wins overreview plan|exec --base <ref>— provided the file exists at that path; when it does not, the--baserange is used and a warning says so.--basewins over tier 3 directory auto-detection (diff.patch). Either way, the discarded input is announced as a warning on stderr. - When the resulting diff is empty: If the supplied diff (explicit or fallback) is empty,
statusis set tono-changesand review skills are not executed.
Diff supply routes (full enumeration against the implementation)
"Input Channels" below is the resolution order for artifact files, not a list of every route by which a diff reaches River Review. The implementation has 8 entry points through which a diff enters the system (as of 2026-09-17; the fixture-evaluation-only paths review-fixtures-eval.mjs / repo-wide-fixtures-eval.mjs are excluded as non-production). This section specifies, per route, who produces the diff, the context width, and whether a combined diff (diff --cc / @@@) can enter.
| # | Route (implementation site) | Surface | Diff producer | Context width | Combined diff |
|---|---|---|---|---|---|
| 1 | src/cli/commands/review.mjs:56 → collectRepoDiff | river review plan / river review exec | River Review (git) | 3 | cannot enter |
| 2 | src/cli/commands/skills.mjs:133 → collectRepoDiff | river skills | River Review (git) | 3 | cannot enter |
| 3 | src/lib/local-runner.mjs:269 planLocalReview | river run | River Review (git) | 3 (10 with --debug) | cannot enter |
| 4 | src/lib/local-runner.mjs:767 doctorLocalReview | river doctor | River Review (git) | 0 (10 with --debug) | cannot enter |
| 5 | artifact file read at src/lib/review-plan.mjs:484 / :841 | the diff artifact via tier 1 / 2 / 3 | Host (may be real git output) | unspecified | supported |
| 6 | src/lib/review-plan.mjs:481 / :838 diffOverride.diffText | --base <ref> on review plan / exec | River Review (git) | 3 | cannot enter |
| 7 | src/lib/local-runner.mjs:533 runLocalReview({ context }) | programmatic embedding | Host | unspecified | supported |
| 8 | the diffText option at runners/node-api/src/index.ts:342 / :389 | Node API (buildExecutionPlan() / review()) | Host | unspecified | supported |
- git-produced routes (1 / 2 / 3 / 4 / 6): all go through
git diff --unified=<N> --no-color <baseRef>atsrc/lib/git.mjs:520. Because an explicit base ref makes this a two-tree diff, no combined diff is emitted. Even in a working tree with an unresolved merge conflict the output stays two-tree, with conflict markers appearing as ordinary added lines (measured in a throwaway repository on 2026-09-17). - Context width: the default is 3. Only
river doctor(route 4) uses 0, and routes 3 / 4 use 10 when--debugis set. The width is an implementation choice inside River Review, not a value this contract promises externally. For diffs the host supplies through routes 5 / 7 / 8 the context width is unspecified. - Combined diff:
diff --cc/@@@output is supported (#2294).git show --ccandgit log -p --ccare real git output and arrive legitimately through routes 5 / 7 / 8. The parse layer accepts a hunk header whose marker widens by one@per parent (@@@for two parents,@@@@for a three-parent octopus merge) and counts each body line by its per-parent prefix COLUMNS. A line is absent from the merge result, and consumes no line number, when any column is-; it is recorded inaddedLineswhen any column is+. A conflict-resolution line (++for two parents) belongs to neither parent and therefore reaches review, which is the point. - Binary files inside a combined section: a combined section for a binary file emits only
Binary files differand carries no---/+++triple, so that file is not registered infiles[]and is not reviewed. This matches the behaviour for ordinary (non-combined) binary diffs and is identical before and after #2294 (measured in a throwaway repository). \ No newline at end of file: the marker is metadata, not a line of the merge result, and it consumes no line number on either the combined or the ordinary path (#2309). Real git always emits it for a file with no trailing newline, so host-supplied diffs on routes 5 / 7 / 8 are treated the same way.- Migration (#2309): before the fix the ordinary path counted the marker as a context line, so every addition below it was recorded in
addedLinesone line further down the file than it really is. A line number that is now smaller by one for the same diff is that correction, not a regression. - Measured blast radius (#2309): replaying the 400 most recent commits of this repository through the parser before and after the fix changed the output of 96 of them (24%), and every one of those 96 contains this marker. No commit without the marker changed. Whether you are affected therefore comes down to whether your diff contains a file with no trailing newline.
- Effect on v2 suppressions (#2309): the v2 suppressions in
src/lib/suppression-apply.mjsare line-anchored, so one placed below the marker can stop matching once the line numbers are corrected. A suppression that stops matching shows up as the finding reappearing, so re-anchor it if you still want it suppressed. v1 fingerprints carry no line number and are unaffected. - Migration: before #2294 a combined-diff input returned zero findings. The same input now returns findings. When comparing against a result recorded while those files read as empty, the difference is not a regression — it is previously invisible change becoming visible.
Ownership of diff supply
- Routes 1 / 2 / 3 / 4 / 6: producing the diff is River Review's responsibility. Format, context width, and base resolution are all decided by the implementation.
- Routes 5 / 7 / 8: supplying the diff is the host's responsibility. As with
reviewSignals, no producer exists in this repository. River Review acts purely as a consumer and does not verify that what it receives came from real git, nor that it is a valid unified diff. Host-supplied does not mean non-git-produced: route 5 includes the legitimate use case of passing real git output — such asgit show --cc— as a file. Hosts should pass a two-tree unified diff equivalent togit diff --unified=3. Behavior for non-git-produced diffs and hand-written patches is unspecified. - Beware fail-silent behavior: "unspecified" does not mean "you will notice when it breaks". An invalid diff does not raise; it can come back as a normal result with zero findings. Confirm on the host side — by cross-checking output such as
changedFiles— that the diff you supplied was interpreted as intended.
junit
- Format: JUnit XML compatible. Nested
<testsuite>is permitted. - When absent: Test-pass/fail skills are skipped.
coverage
- Format: One of Cobertura XML, LCOV, or Istanbul JSON.
- When absent: Coverage skills are skipped.
- Note: Threshold evaluation is the skill's responsibility; this contract only fixes schema passthrough.
lint / typecheck
- Format: Prefer JSON (ESLint / stylelint / tsc JSON), fall back to plain text. Skills perform a tool-specific light parse on plain input.
- When absent: Static-analysis skills are skipped.
Input Channels
River Review resolves artifacts in this order:
- CLI / GitHub Action arguments (defined in
river review plan/river review execCLI spec). Example:--artifact pbi-input=./path/to/pbi-input.md - Configuration file (defined in
river review plan/river review execCLI spec).artifactssection inriver.config.*. - Current directory auto-detection (fallback). Searches the workspace root for the default filenames above.
Artifacts not resolved by any channel are treated as "absent" and follow the per-artifact absence behavior above.
These three tiers apply only to the river review plan and river review exec surfaces. river run, river skills, and river doctor perform no artifact resolution and always obtain the diff from git themselves (routes 3 / 2 / 4 in "Diff supply routes" above).
Downstream Integration
CLI
river runrecords the resolved artifact set in thecontext/debugsections of the Review Artifact.- Failure to resolve required artifacts exits with code
1. See Stable Interfaces.
Skills
- Individual skills declare the artifact IDs they require (implemented as part of the skill-pack design).
- Skills requiring an unresolved artifact are auto-skipped and recorded in
plan.skippedSkills.
CI
⚠️ GitHub Action limitation (not yet implemented)
The
--artifactand--ensembleflags are not yet available as GitHub Action inputs. As a workaround, invoke thedist/index.mjsCLI directly. See the W-check Practical Guide for a concrete example. A dedicatedartifactinput is planned (seerunners/github-action/action.yml).
- CI should decide failure from the Review Artifact
statusand the severity mix offindings.
PlanGate Independence
This contract treats PlanGate as one of several possible producers and deliberately avoids:
- Hard-coding PlanGate-specific directory layouts (e.g.
plangate/<phase>/) as default paths. - Adopting artifact names tied to PlanGate-internal commands or execution models.
- Assuming PlanGate versions and River Review skill versions are co-released.
- Adopting a PlanGate-specific signal format as the
reviewSignalscontract. Thestagevocabulary and signal keys are defined by River Review; PlanGate is treated as one possible supplier.
This keeps River Review usable for workflows other than PlanGate or for artifacts generated manually.
Versioning
- This contract is managed as document version
1(when later formalized as JSON Schema, aversionfield will be added). - Adding artifacts or extending formats is a minor bump (backward compatible); removal is a major bump.
See Also
- Review Artifact — Output schema for review runs
- Stable Interfaces — CLI / GitHub Actions stable contract
- Runner CLI Reference — Runner CLI usage
- Review Policy — AI review policy