Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
054986d
feat(reviewer): add exact-claim evidence receipt contract
seonghobae Sep 7, 2026
2571d7f
test(reviewer): reject ambiguous receipt identities
seonghobae Sep 7, 2026
66c15f2
feat(reviewer): produce time-bounded claim receipts
seonghobae Sep 7, 2026
4b669ef
fix(reviewer): bind trusted receipt semantics
seonghobae Sep 7, 2026
46abf62
fix(reviewer): keep claim evidence in canonical package
seonghobae Sep 7, 2026
db28d7e
fix(reviewer): authenticate claim evidence manifests
seonghobae Sep 7, 2026
6fed7a6
test(reviewer): require canonical source evidence producer
seonghobae Sep 7, 2026
dd547c2
fix(reviewer): add canonical source evidence producer
seonghobae Sep 7, 2026
809aa6d
fix(reviewer): export canonical source evidence producer
seonghobae Sep 7, 2026
b6b25ce
test(reviewer): require canonical claim receipt references
seonghobae Sep 7, 2026
ed243aa
feat(reviewer): admit canonical claim receipt references
seonghobae Sep 7, 2026
9b48419
test(reviewer): require public receipt citation adapter
seonghobae Sep 7, 2026
2b123ec
feat(reviewer): export claim receipt citation port
seonghobae Sep 7, 2026
85d8044
test(reviewer): reproduce unbound publication evidence
seonghobae Sep 7, 2026
68d1cee
feat(reviewer): gate model findings on trusted receipts
seonghobae Sep 7, 2026
7a7168a
feat(reviewer): admit receipt evidence before deterministic gates
seonghobae Sep 7, 2026
942da88
feat(reviewer): load authenticated evidence before review
seonghobae Sep 7, 2026
f39fbbd
feat(reviewer): export trusted evidence runtime port
seonghobae Sep 7, 2026
b52d30e
feat(reviewer): attest source receipts through publication
seonghobae Sep 7, 2026
e8a78ee
test(reviewer): pass boolean CLI flags canonically
seonghobae Sep 7, 2026
863bb2d
test(reviewer): cover receipt producer and admission edges
seonghobae Sep 7, 2026
389cd49
test(reviewer): prove single-assignment receipt handoff
seonghobae Sep 7, 2026
ea0ef25
refactor(reviewer): keep receipt runtime minimal
seonghobae Sep 7, 2026
18431d6
docs(reviewer): record live receipt publication boundary
seonghobae Sep 7, 2026
676747e
docs(gap): bind source receipts and retain adapter gap
seonghobae Sep 7, 2026
45ecc9d
fix(reviewer): attest review and claim manifests separately
seonghobae Sep 7, 2026
c62535d
fix(reviewer): bind source receipts to finding coordinates
seonghobae Sep 7, 2026
5121e1e
test(reviewer): reject source receipt coordinate reuse
seonghobae Sep 7, 2026
81eef2a
test(reviewer): reject unreceipted model publication claims
seonghobae Sep 7, 2026
0d5eab3
fix(reviewer): project only authenticated claim authority
seonghobae Sep 7, 2026
5b5f4de
fix(reviewer): gate normalized model findings
seonghobae Sep 7, 2026
3d0275e
test(reviewer): verify authenticated publication projection
seonghobae Sep 7, 2026
52043d8
docs(reviewer): record model prose authority boundary
seonghobae Sep 7, 2026
174d0b0
docs(reviewer): trace receipt-authority projection
seonghobae Sep 7, 2026
f299381
test(reviewer): require trusted sandboxed_verify execution adapter
seonghobae Sep 7, 2026
ea751bc
feat(reviewer): adapt sandboxed_verify result into execution receipt
seonghobae Sep 7, 2026
fecb03d
feat(reviewer): export sandboxed_verify execution adapter
seonghobae Sep 7, 2026
5503700
merge current reviewer base without dropping evidence work
seonghobae Sep 8, 2026
451683f
test(reviewer): reject source-only runtime verdict authority
seonghobae Sep 8, 2026
9d6d52c
fix(reviewer): separate claim publication authority
seonghobae Sep 8, 2026
29cb77b
fix(docs): retain live PR authority invariant
seonghobae Sep 8, 2026
363d62b
Merge current reviewer base without dropping claim evidence work
seonghobae Sep 8, 2026
bd48185
Restack claim-evidence receipts onto protected #535 integration
seonghobae Sep 8, 2026
dbab4cd
test(reviewer): reject unbound source claim receipts
seonghobae Sep 8, 2026
440346e
fix(reviewer): bind source claims to exact line bytes
seonghobae Sep 8, 2026
920eb7b
test(reviewer): cover exact source-line binding edges
seonghobae Sep 8, 2026
1cd8db5
test(reviewer): align source-kind guard with exact line claim
seonghobae Sep 8, 2026
809ccb7
test(reviewer): remove unused runtime module import
seonghobae Sep 8, 2026
6629f07
test(reviewer): reproduce vacuous blocking verdict
seonghobae Sep 8, 2026
4c21537
fix(reviewer): require evidence for model non-approval
seonghobae Sep 8, 2026
860714c
docs(reviewer): record nonapproval evidence invariant
seonghobae Sep 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 66 additions & 5 deletions .github/workflows/central-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -231,9 +231,14 @@ jobs:
run: |
set -euo pipefail
python - <<'PY'
import hashlib
import os
from datetime import datetime, timedelta, timezone
from pathlib import Path

from noema_reviewer.claim_evidence_runtime import (
produce_current_head_source_manifest,
)
from noema_reviewer.github_io import fetch_manifest
from noema_reviewer.sandbox import DockerCodeGraphRunner

Expand All @@ -248,6 +253,26 @@ jobs:
)
output = Path(os.environ["RUNNER_TEMP"]) / "noema-manifest.json"
output.write_text(manifest.model_dump_json(indent=2), encoding="utf-8")
issued_at = datetime.now(timezone.utc)
claim_manifest = produce_current_head_source_manifest(
manifest,
source_root=Path(source_root),
workflow_ref=(
"ContextualWisdomLab/noema/.github/workflows/central-review.yml@"
+ os.environ["GITHUB_SHA"]
),
run_id=int(os.environ["GITHUB_RUN_ID"]),
run_attempt=int(os.environ["GITHUB_RUN_ATTEMPT"]),
issued_at=issued_at,
expires_at=issued_at + timedelta(hours=6),
)
claim_output = Path(os.environ["RUNNER_TEMP"]) / "noema-claim-evidence-manifest.json"
claim_output.write_bytes(claim_manifest)
claim_digest = hashlib.sha256(claim_manifest).hexdigest()
(Path(os.environ["RUNNER_TEMP"]) / "noema-claim-evidence-manifest.sha256").write_text(
f"{claim_digest} noema-claim-evidence-manifest.json\n",
encoding="ascii",
)
print(
f"Collected bounded manifest for {manifest.repo}#{manifest.pr_number} "
f"head={manifest.head_sha} checks={len(manifest.check_conclusions)} "
Expand All @@ -256,6 +281,7 @@ jobs:
PY
cd "$RUNNER_TEMP"
sha256sum noema-manifest.json >noema-manifest.sha256
sha256sum --check noema-claim-evidence-manifest.sha256

- name: Upload short-lived bounded review manifest
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
Expand All @@ -264,6 +290,8 @@ jobs:
path: |
${{ runner.temp }}/noema-manifest.json
${{ runner.temp }}/noema-manifest.sha256
${{ runner.temp }}/noema-claim-evidence-manifest.json
${{ runner.temp }}/noema-claim-evidence-manifest.sha256
if-no-files-found: error
retention-days: 1

Expand Down Expand Up @@ -293,6 +321,7 @@ jobs:
set -euo pipefail
cd "$RUNNER_TEMP/noema-attestation-input"
sha256sum --check noema-manifest.sha256
sha256sum --check noema-claim-evidence-manifest.sha256
jq -e \
--arg repo "$TARGET_REPOSITORY" \
--argjson pr "$PR_NUMBER" \
Expand All @@ -310,20 +339,34 @@ jobs:
create-storage-record: false
show-summary: false

- name: Stage signed manifest attestation
- name: Attest claim evidence manifest provenance
id: attest_claim_evidence
uses: actions/attest@59d89421af93a897026c735860bf21b6eb4f7b26 # v4.1.0
with:
subject-path: ${{ runner.temp }}/noema-attestation-input/noema-claim-evidence-manifest.json
create-storage-record: false
show-summary: false

- name: Stage signed manifest attestations
env:
ATTESTATION_BUNDLE_PATH: ${{ steps.attest.outputs.bundle-path }}
REVIEW_ATTESTATION_BUNDLE_PATH: ${{ steps.attest.outputs.bundle-path }}
CLAIM_ATTESTATION_BUNDLE_PATH: ${{ steps.attest_claim_evidence.outputs.bundle-path }}
run: |
set -euo pipefail
test -f "$ATTESTATION_BUNDLE_PATH"
install -m 0600 "$ATTESTATION_BUNDLE_PATH" \
test -f "$REVIEW_ATTESTATION_BUNDLE_PATH"
test -f "$CLAIM_ATTESTATION_BUNDLE_PATH"
install -m 0600 "$REVIEW_ATTESTATION_BUNDLE_PATH" \
"$RUNNER_TEMP/noema-manifest-attestation.json"
install -m 0600 "$CLAIM_ATTESTATION_BUNDLE_PATH" \
"$RUNNER_TEMP/noema-claim-evidence-manifest-attestation.json"

- name: Upload short-lived signed manifest attestation
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: noema-manifest-attestation-${{ needs.collect_evidence.outputs.head_sha }}
path: ${{ runner.temp }}/noema-manifest-attestation.json
path: |
${{ runner.temp }}/noema-manifest-attestation.json
${{ runner.temp }}/noema-claim-evidence-manifest-attestation.json
if-no-files-found: error
retention-days: 1

Expand Down Expand Up @@ -383,6 +426,9 @@ jobs:
install -m 0600 \
"$RUNNER_TEMP/noema-attestation/noema-manifest-attestation.json" \
noema-manifest-attestation.json
install -m 0600 \
"$RUNNER_TEMP/noema-attestation/noema-claim-evidence-manifest-attestation.json" \
noema-claim-evidence-manifest-attestation.json
gh attestation verify noema-manifest.json \
--bundle noema-manifest-attestation.json \
--repo ContextualWisdomLab/noema \
Expand All @@ -392,7 +438,17 @@ jobs:
--source-ref refs/heads/main \
--cert-oidc-issuer https://token.actions.githubusercontent.com \
--deny-self-hosted-runners
gh attestation verify noema-claim-evidence-manifest.json \
--bundle noema-claim-evidence-manifest-attestation.json \
--repo ContextualWisdomLab/noema \
--signer-workflow ContextualWisdomLab/noema/.github/workflows/central-review.yml \
--signer-digest "$GITHUB_SHA" \
--source-digest "$GITHUB_SHA" \
--source-ref refs/heads/main \
--cert-oidc-issuer https://token.actions.githubusercontent.com \
--deny-self-hosted-runners
sha256sum --check noema-manifest.sha256
sha256sum --check noema-claim-evidence-manifest.sha256
live_json="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"
state="$(jq -r '.state // empty' <<<"$live_json")"
live_head="$(jq -r '.head.sha // empty' <<<"$live_json")"
Expand Down Expand Up @@ -459,6 +515,11 @@ jobs:
set +e
python -m noema_reviewer \
--manifest-file "$RUNNER_TEMP/noema-evidence/noema-manifest.json" \
--claim-evidence-manifest-file "$RUNNER_TEMP/noema-evidence/noema-claim-evidence-manifest.json" \
--claim-evidence-manifest-sha256 "$(cut -d ' ' -f1 "$RUNNER_TEMP/noema-evidence/noema-claim-evidence-manifest.sha256")" \
--claim-evidence-workflow-ref "ContextualWisdomLab/noema/.github/workflows/central-review.yml@${GITHUB_SHA}" \
--claim-evidence-run-id "$GITHUB_RUN_ID" \
--claim-evidence-run-attempt "$GITHUB_RUN_ATTEMPT" \
--strict \
--publish \
--token-source "$NOEMA_REVIEW_TOKEN_SOURCE" \
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Changelog

- Add a Noema-owned exact-claim evidence receipt contract whose execution and research producers serialize one canonical artifact that binds every receipt semantic field, including command/result/isolation/network or source revision/excerpt/retrieval policy. Admission accepts only a receipt ID from untrusted model output. The owner API first verifies the exact authenticated OpenCode-handoff manifest digest, canonical envelope bytes, reviewed producer-to-kind policy, and repository/head/workflow/run/attempt identity before it can construct an immutable typed index; admission then reconstructs each canonical artifact and verifies time/claim/artifact identity. The version-2 manifest now binds a separate producer-authenticated `ClaimEvidenceRequirement` containing the exact claim, independently required evidence kind, and `context` or `finding` publication authority. Raw current-head source lines are context only: they are withheld from finding-reference prompts and cannot publish a finding or `request_changes`; an explicitly producer-authorized source finding remains usable and retains exact path/line checks. Finding-free model `request_changes` and `blocked` verdicts cannot bypass receipt admission to publish a vacuous blocking review. Requirement/receipt kind mismatch, fixed-artifact semantic substitution, caller-supplied receipt dictionaries, model self-classification, stale identities, cross-kind receipts, marker-only sandbox output, noncanonical artifact bytes, and expired receipts fail closed before the GitHub publisher. This remains the owner prerequisite for ContextualWisdomLab/.github#1641 and issue #555. The reviewed `sandboxed_verify` adapter exists in owner source, but its actual central stdout/stderr/marker-to-manifest wiring and the trusted research producer are not yet integrated; exact-head hosted GREEN, immutable release, and the verified central consumer bump remain required.

## Unreleased
- `noema-core` provider-neutral Shared Kernel을 추가하여 이미 해석된 PydanticAI `Model`과 역할별 prompt/schema만 받아 Agent를 구성한다. 문자열 model identifier와 provider discovery·credential·routing·retry·failover는 Shared Kernel 밖에 두고 `Agent(..., retries=0)`으로 repository-local model-attempt authority를 만들지 않는다. Reviewer wheel·sdist·editable 설치는 canonical `packages/noema-core` source를 포함하거나 참조하며 별도 100% coverage·docstring과 clean install smoke로 검증한다. 외부 소비는 immutable versioned publication·exact source identity·SBOM/provenance·licensing/NOTICE·compatibility/rollback evidence 전에는 허용하지 않는다.
- `writeAcquisitionPrivateFile`의 기존 대상 사전-교체 검증 read(`existingDescriptor` open)에 `O_NONBLOCK`을 추가해 fail-closed를 강화한다. 이 open은 이미 필수 filesystem capability로 `O_NONBLOCK`을 검증했지만 실제로는 사용하지 않아, 로컬 권한을 가진 행위자가 사전 `lstatSync` 정규 파일 확인과 이 open 사이에 대상 경로를 FIFO로 교체하면 writer가 나타날 때까지 무한정 블로킹해 writer lease를 계속 점유할 수 있었다. `O_NONBLOCK`은 정규 파일에는 영향이 없고, FIFO에서는 open이 즉시 반환되어 이어지는 descriptor 타입 검증이 그대로 fail-closed로 거부한다. 회귀 테스트(`test/acquisition-private-output-existing-target-nonblocking.test.ts`)와 기존 open-flags 계약 테스트 갱신으로 고정했다.
Expand Down
24 changes: 24 additions & 0 deletions reviewer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,30 @@ carries structured actionability rather than relying on free-form prose:
}
```

### Claim-evidence publication authority

An authenticated claim-evidence manifest entry binds two separate objects: a
producer receipt and a `ClaimEvidenceRequirement`. The requirement owns the
exact claim, independently required evidence kind, and either `context` or
`finding` publication authority. The verifier rejects a requirement/receipt
kind mismatch; the model supplies neither field and can cite only a receipt ID
for a producer-authorized exact finding claim.

The bounded current-head line collector marks raw source lines as `context`.
Those receipts remain authenticated source context, but are withheld from the
model's finding-reference list and cannot authorize a published finding or
`request_changes`. A trusted source producer may explicitly issue `source` +
`finding` authority for an exact source-only defect; exact path and line binding
still applies. Execution or research claims require their independently bound
kind, so an authentic source line cannot be reused as proof of runtime or
external behavior. Admission fails before deterministic gates and the GitHub
publisher instead of rewriting an unsupported claim into a blocking verdict.

This contract does not by itself claim that the central `sandboxed_verify`
result or a trusted research retrieval has populated the production manifest.
Those producer adapters, protected exact-head verification, immutable release,
and the released `.github` consumer remain separate completion conditions.

`check_name` is optional for ordinary source, SARIF, dependency, and review-thread
findings. A finding offered as the RCA for a failed current-head check must bind
to that exact check name. The deterministic gate requires each ordinary failed
Expand Down
56 changes: 56 additions & 0 deletions reviewer/noema_reviewer/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,38 @@

from typing import Any

from .claim_evidence import (
ClaimEvidenceRequirement,
ClaimEvidenceReceipt,
ClaimPublicationAuthority,
EvidenceKind,
ExecutionClaimReceipt,
ProducedClaimEvidence,
ResearchClaimReceipt,
SourceClaimReceipt,
VerifiedClaimEvidenceIndex,
admit_claim_evidence,
index_claim_evidence_receipts,
produce_claim_evidence_manifest,
produce_execution_claim_receipt,
produce_research_claim_receipt,
sha256_text,
verify_claim_evidence_manifest,
)
from .claim_evidence_reference import (
admit_claim_evidence_reference,
parse_claim_evidence_reference,
)
from .claim_evidence_runtime import (
admit_review_verdict_evidence,
produce_current_head_source_manifest,
prompt_claim_evidence_references,
verify_claim_evidence_file,
)
from .sandboxed_verify_claim_evidence import (
produce_sandboxed_verify_execution_claim_receipt,
)
from .source_claim_evidence import produce_source_claim_receipt
from .manifest import ReviewManifest
from .models import Confidence, EvidenceType, Finding, Priority, ReviewVerdict, Severity, Verdict
from .patch_image_validation import (
Expand Down Expand Up @@ -50,6 +82,12 @@ def __getattr__(name: str) -> Any:


__all__ = [
"ClaimEvidenceRequirement",
"ClaimEvidenceReceipt",
"ClaimPublicationAuthority",
"EvidenceKind",
"ExecutionClaimReceipt",
"ProducedClaimEvidence",
"Confidence",
"DockerPatchValidationRunner",
"DockerPatchValidatorImageRunner",
Expand All @@ -64,13 +102,31 @@ def __getattr__(name: str) -> Any:
"PatchValidatorImageResult",
"PatchValidatorImageStatus",
"PydanticAIReviewAgent",
"ResearchClaimReceipt",
"Priority",
"ReviewAgent",
"ReviewManifest",
"ReviewVerdict",
"Severity",
"SourceClaimReceipt",
"VerifiedClaimEvidenceIndex",
"Verdict",
"admit_claim_evidence",
"admit_claim_evidence_reference",
"admit_review_verdict_evidence",
"build_agent",
"inspect_patch_bytes",
"index_claim_evidence_receipts",
"inspect_patch_for_image",
"parse_claim_evidence_reference",
"produce_claim_evidence_manifest",
"produce_current_head_source_manifest",
"produce_execution_claim_receipt",
"produce_research_claim_receipt",
"produce_sandboxed_verify_execution_claim_receipt",
"produce_source_claim_receipt",
"prompt_claim_evidence_references",
"sha256_text",
"verify_claim_evidence_file",
"verify_claim_evidence_manifest",
]
68 changes: 59 additions & 9 deletions reviewer/noema_reviewer/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@

from __future__ import annotations

from collections.abc import Callable
from datetime import datetime, timezone
from typing import Protocol, runtime_checkable

from noema_core import NOEMA_PERSONA
from noema_core import build_agent as build_core_agent
from pydantic_ai import Agent, ModelSettings
from pydantic_ai.models import Model

from .claim_evidence import VerifiedClaimEvidenceIndex
from .claim_evidence_runtime import (
admit_review_verdict_evidence,
prompt_claim_evidence_references,
)
from .config import ReviewerConfig, resolve_config, resolve_model
from .gating import apply_gates
from .manifest import ReviewManifest
Expand Down Expand Up @@ -85,8 +92,11 @@ def _dependency_lines(manifest: ReviewManifest) -> list[str]:
return lines


def build_prompt(manifest: ReviewManifest) -> str:
"""Build the bounded user prompt handed to the model for one review."""
def build_prompt(
manifest: ReviewManifest,
trusted_index: VerifiedClaimEvidenceIndex | None = None,
) -> str:
"""Build the bounded prompt plus producer-authenticated receipt references."""
sections: list[str] = [
f"Repository: {manifest.repo}",
f"PR: #{manifest.pr_number}",
Expand Down Expand Up @@ -120,7 +130,13 @@ def build_prompt(manifest: ReviewManifest) -> str:
files = [f"### {changed.path}\n{changed.content}" for changed in manifest.changed_files]
if files:
sections.append("Changed-file context:\n" + "\n\n".join(files))

claim_references = prompt_claim_evidence_references(trusted_index)
if claim_references:
sections.append(
"Trusted claim evidence references (copy an exact full line into Finding.evidence; "
"do not alter the claim or receipt ID):\n- "
+ "\n- ".join(claim_references)
)
sections.append("Diff:\n" + (manifest.diff or "(no diff provided)"))
return "\n\n".join(sections)

Expand All @@ -140,32 +156,66 @@ def __init__(
model: Model,
*,
model_settings: ModelSettings | None = None,
claim_evidence_index: VerifiedClaimEvidenceIndex | None = None,
admitted_at: Callable[[], datetime] | None = None,
) -> None:
"""Build the agent around an already resolved real or test model."""
if isinstance(model, str):
raise TypeError(
"PydanticAIReviewAgent requires a pre-resolved Model; "
"provider/model routing belongs to contextual-orchestrator"
)
self._claim_evidence_index = claim_evidence_index
self._admitted_at = admitted_at or (lambda: datetime.now(timezone.utc))
self._agent: Agent[None, ReviewVerdict] = build_core_agent(
model,
output_type=ReviewVerdict,
system_prompt=SYSTEM_PROMPT,
)
self._model_settings = model_settings

def bind_claim_evidence(
self,
trusted_index: VerifiedClaimEvidenceIndex,
*,
admitted_at: Callable[[], datetime] | None = None,
) -> "PydanticAIReviewAgent":
"""Bind one verified workflow index before model execution and publication."""
if self._claim_evidence_index is not None:
raise ValueError("claim evidence index is already bound")
self._claim_evidence_index = trusted_index
if admitted_at is not None:
self._admitted_at = admitted_at
return self

def prompt_for(self, manifest: ReviewManifest) -> str:
"""Return the exact prompt including only verified receipt references."""
return build_prompt(manifest, self._claim_evidence_index)

def review(self, manifest: ReviewManifest, *, strict: bool = False) -> ReviewVerdict:
"""Run the model over the manifest and apply the deterministic gates."""
prompt = build_prompt(manifest)
result = self._agent.run_sync(prompt, model_settings=self._model_settings)
return apply_gates(manifest, result.output, strict=strict)
"""Admit model evidence before deterministic gates can add trusted findings."""
result = self._agent.run_sync(
self.prompt_for(manifest),
model_settings=self._model_settings,
)
admitted = admit_review_verdict_evidence(
result.output,
trusted_index=self._claim_evidence_index,
admitted_at=self._admitted_at(),
)
return apply_gates(manifest, admitted, strict=strict)


def build_agent(config: ReviewerConfig | None = None) -> PydanticAIReviewAgent:
"""Build a production review agent from one validated gateway configuration."""
def build_agent(
config: ReviewerConfig | None = None,
*,
claim_evidence_index: VerifiedClaimEvidenceIndex | None = None,
) -> PydanticAIReviewAgent:
"""Build a production reviewer with an optional verified evidence index."""
resolved = config or resolve_config()
model = resolve_model(resolved)
return PydanticAIReviewAgent(
model,
model_settings=model_settings_for_config(resolved),
claim_evidence_index=claim_evidence_index,
)
Loading
Loading