@adlc/prosecute
v1.4.1
Published
P5 review-evidence recorder for ticket- and revision-bound findings and dry-pass completion.
Maintainers
Readme
@adlc/prosecute
Design decision: this is a recorder, not a reviewer.
@adlc/prosecutemakes zero model calls and runs no adversarial review of its own. It is a P5 evidence ledger: it validates, hashes, and appends normalized reviewer-produced pass records to.adlc/manifest.jsonl. The actual model-judged review is a separate tool -- runnpx adversarial-review(oradlc review, which passes args through to it) -- and feed its output intoprosecuteas the--inputevidence. If you runadlc prosecuteexpecting it to find bugs, it will not; it only proves that a real review already did.
P5 review-evidence recorder. It consumes normalized reviewer-produced pass evidence,
records ticket- and revision-scoped P5 evidence to .adlc/manifest.jsonl, and passes
only after two consecutive dry passes with at least three distinct dry lenses.
Usage
adlc-prosecute --input p5-passes.json --ticket T1 --dir .adlc --jsonInput shape
{
"target": "feature branch",
"provenance": {
"reviewer": "local-reviewer",
"session": "codex-session-123",
"command": "npx adversarial-review --scope working-tree --include-files",
"transcript": ".omo/evidence/p5-review.txt"
},
"review_packet": {
"prompt": ".omo/evidence/p5-prompt.txt",
"prompt_hash": "sha256-of-prompt-file",
"inputs": ".omo/evidence/p5-inputs.txt",
"inputs_hash": "sha256-of-reviewed-input-packet",
"clean_worktree": "git-worktree:..."
},
"passes": [
{
"lens": "security",
"findings": [
{
"id": "F1",
"severity": "high",
"category": "security",
"file": "src/auth.js",
"line_start": 10,
"line_end": 12,
"evidence": "quoted changed code",
"claim": "token bypass",
"recommendation": "validate issuer",
"confidence": 0.8,
"verified_status": "verified"
}
]
},
{
"lens": "security",
"findings": [],
"dry_evidence": "review transcript reports no verified security findings"
},
{
"lens": "correctness",
"findings": [],
"dry_evidence": "review transcript reports no verified correctness findings"
}
]
}verified_status must be verified, killed, or needs-human. Killed findings must
include verification.reason, verification.method, and verification.evidence.
Inputs must use the built-in lens names: security, correctness, tests, behavior,
integration, or docs. Clean reviews with zero finding candidates are accepted when the
dry passes include evidence. If no finding is marked verified, the input must include
no_findings_attestation with
reason, method, and evidence. Only passes with zero findings count as dry; killed
findings are recorded but do not advance dry-pass convergence.
The transcript is not treated as a generic attachment. It must be readable, at least 64
bytes, and it must reference both the --ticket value and the resolved reviewed revision
string (git-worktree:<hash> unless --revision is supplied). This binds the recorded
P5 evidence to the ticket and revision that P6 later checks, but it still does not prove
that an external reviewer ran. Use the named provenance.command and transcript from the
actual skeptical review run, not a hand-written placeholder.
The review packet binds the reviewer prompt and reviewed input packet to P5 evidence.
prompt and inputs are file paths, their hashes must match the supplied SHA-256 values,
and clean_worktree must equal the exact reviewed revision.
Evidence file location is enforced, not just convention. If provenance.transcript,
review_packet.prompt, or review_packet.inputs resolves to a path inside the worktree,
it must live under .adlc/ or .omo/evidence/ -- lib/run.mjs's isEvidencePath() rejects
any other in-worktree location. This is a trust-boundary control: it stops review "evidence"
from pointing at an arbitrary file elsewhere in the repo that could be edited to fake a
clean review. .omo/evidence/ is otherwise-gitignored scratch space, but the three files
backing the bundled example (docs/examples/p5-passes.json) are deliberately carved out
of that ignore rule and tracked -- see the comment in .gitignore before treating anything
under .omo/ as safe to delete.
Trust-root tier — required cross-model review (T39)
For the trust-root tier, a clean same-model P5 is not sufficient. The CLI computes the
changed-file set from the WORKING TREE vs <base> (default --base main) — the two-dot
git diff --name-only <base> (tracked changes incl. uncommitted) unioned with untracked,
non-ignored files (git ls-files --others --exclude-standard) — and classifies it with
lib/tier.mjs. Working-tree-inclusive is load-bearing: prosecution binds to the working-tree
revision, so a three-dot <base>...HEAD diff (committed only) would miss an uncommitted
edit to a trust-root file and let a converged P5 exit 0 with no attestation. A change is
trust-root tier iff it touches an enforcement package
(packages/rails-guard|prosecute|gate-manifest|build-gate/), a gated-artifact producer
(packages/ticket-prune|ticket-sync/), a declared rails deny-path of any ticket, or a
trust-root file (scripts/rails-guard-ci.mjs, docs/ci/rails-guard.yml,
scripts/test/rails-guard-workflow-hashes.json, .adlc/tickets.json). The ticket table for
rails-deny-path tiering is read from the same --dir the prosecution uses (falling back
to .adlc/tickets.json), so rails declared under a custom --dir are not invisible to the
tier. For such a change, a passing P5 additionally requires a cross-model-review
approve in the manifest whose provider is distinct from the author and whose
revision equals the reviewed revision. Missing → exit 2.
Author identity is anchored to the prosecution run. A tiered run MUST declare the author
via --author-provider <p> (or ADLC_AUTHOR_PROVIDER); distinctness is measured against
that prosecution-declared author, not the entry's self-reported authorProvider (an
attestation defines both sides, so comparing only its own fields is forgeable). The gate also
requires the record to have been made for that author context. A tiered run with no
author-provider fails closed (exit 1) — distinctness cannot be proven without knowing the
author.
Fail-closed on an unresolvable base. Tiering needs the base ref to compute the diff. If
<base> cannot be resolved (e.g. a shallow CI checkout with no main), the CLI refuses
the run with exit 1 rather than silently skipping the cross-model requirement — a silent
ungated pass is the fail-open class this gate exists to prevent. CI must provide the
base (fetch it, e.g. git fetch --no-tags origin main, or pass an explicit --base
<ref>). Hermetic unit runs that assert convergence only (not tiering) pass --base HEAD
so, from a clean committed worktree, the working-tree diff is empty and the tier gate
stays off (run such checks post-commit; uncommitted trust-root edits deliberately tier).
Record the attestation (after an actual cross-model review approves) with:
adlc prosecute record-cross-model --ticket <id> \
--provider <p> --author-provider <a> --verdict approve [--input <passes.json>] [--revision <r>]It resolves the revision the same way the gate does (resolveProsecutionRevision), so pass
the same --input/--revision you use for the gate run. --provider must differ from
--author-provider — a same-model attestation is refused at record time and rejected by the
gate (lib/cross-model.mjs, fail-closed). Like rails-guard this cannot prove a model ran; the
author identity now comes from the prosecution invocation (not a self-report), and the record
is an auditable, revision-bound, append-only, distinct-provider, author-anchored entry. See
ADR-0007.
Exit codes
0: two consecutive dry passes were recorded (or a finding/attestation was recorded)1: operational error2: verified/needs-human findings remain, the convergence budget ended before two dry passes, or a trust-root-tier change lacks a matching cross-model attestation
Core gaps
This package records cross-model attestations through @adlc/gate-manifest's chained
record() (see lib/cross-model.mjs) and reads them back via @adlc/core's readEntries.
The trust-root-tier classifier (lib/tier.mjs) lives here rather than in @adlc/core
(frozen) because it is prosecute-specific policy; if a second package ever needs the same
binary trust-root decision, the enumerated surfaces (enforcement packages, producers,
trust-root files) would ideally graduate into @adlc/core alongside the existing
railpath/risk-tier helpers so the list has a single source of truth.
ADLC phase
P5 Prosecute. This tool makes the review-evidence and dry-pass record executable, but
the reviewer command named in provenance.command remains the source of the review.
