Validation Review Web Reviewer Workflow
Objective
Define the production flow for validation decisions, bot onboarding, and run-level sharing in the web lane, with optional API/CLI execution when needed.
Guardrails
- Contract-first: only use routes defined in
/docs/architecture/specs/platform-api.openapi.yaml. - Platform API is the only client entrypoint for web, SDK, and optional CLI flows.
- Identity is auth-derived; reviewer tooling must not treat raw tenant/user headers as trusted identity input.
- JSON is canonical (
validation_runartifact). HTML/PDF are optional derived outputs. - Shared access is run-level only and must route through the dedicated Shared Validation UX flow.
- No client-side direct provider calls for this workflow surface.
Web Flow (/validation)
- Open
/validationand authenticate through the dashboard session. - Create a run in the web form (owner user or owner bot context reflected through run actor metadata).
- Confirm the proxy creates the run through
POST /api/validation/runs->POST /v2/validation-runs. - Load run status and canonical artifact:
GET /api/validation/runs/{runId}GET /api/validation/runs/{runId}/artifact
- Submit reviewer decision from the review form:
POST /api/validation/runs/{runId}/reviewPOST /v2/validation-runs/{runId}/review- required fields:
reviewerType,decision
- Request render output only when required for external distribution:
POST /api/validation/runs/{runId}/render- body
{"format":"html"}or{"format":"pdf"}
Bot Onboarding In Shared Validation Program
- Invite-code registration path (
trial, rate-limited):POST /v2/validation-bots/registrations/invite-code- request fields:
inviteCode,botName, optionalmetadata
- Partner bootstrap registration path:
POST /v2/validation-bots/registrations/partner-bootstrap- request fields:
partnerKey,partnerSecret,ownerEmail,botName, optionalmetadata
- Both paths return:
bot(user-owned bot record)registrationaudit recordissuedKeywith one-timerawKey
Key Lifecycle (Create, Show-Once, Rotate, Revoke)
- Create:
- initial bot registration response includes
issuedKey.rawKey.
- initial bot registration response includes
- Show once:
- raw key is returned only on create/rotate (
BotIssuedApiKey.rawKey), never through metadata endpoints.
- raw key is returned only on create/rotate (
- Rotate:
POST /v2/validation-bots/{botId}/keys/rotate- returns new
issuedKey.rawKeyand metadata.
- Revoke:
POST /v2/validation-bots/{botId}/keys/{keyId}/revoke- returns key metadata with
status=revoked.
Actor Linkage Model (User vs Bot)
- Validation run actor identity is explicit:
ValidationRun.actor.actorType:user | botValidationRun.actor.actorId+ optionaluserIdandbotId
- Invite creator actor identity is explicit:
ValidationInvite.invitedByActorType:user | bot
- Bot ownership remains user-bound (
ownerUserId) with no brand entity in v1.
Run-Level Invite Model And Permissions
- Create invite by email:
POST /v2/validation-sharing/runs/{runId}/invites- required field:
email
- List invites for one run:
GET /v2/validation-sharing/runs/{runId}/invites
- Revoke invite:
POST /v2/validation-sharing/invites/{inviteId}/revoke
- Accept invite during login/session flow:
POST /v2/validation-sharing/invites/{inviteId}/accept- request includes
acceptedEmail
- Permission boundary:
- canonical shared permission enum is
view | review. viewgrants shared read access only (GET /v2/validation-sharing/runs/{runId}and/artifact).reviewgrants shared read access plus shared write viaPOST /v2/validation-sharing/runs/{runId}/review.- owner-scoped invite management and run-share mutation stay owner-only.
- canonical shared permission enum is
- Backward-compatibility note:
- legacy alias permissions (
comment,decide) normalize toreview; newly authored docs and clients should only emitview | review.
- legacy alias permissions (
Dedicated Shared Validation UX Surface
- Shared users enter through the Shared Validation reviewer flow (current web lane route
/validation). - Shared run access is established only through invite acceptance path; no ambient cross-run access.
- Shared users review canonical JSON first and request HTML/PDF only as derived outputs.
- Shared review submissions must target
POST /v2/validation-sharing/runs/{runId}/review(not owner-scoped/v2/validation-runs/{runId}/review).
Deep-Link Flow (#279)
CLI and external tooling may open review-web directly with runId in query params:
/validation?runId=<runId>
Workflow expectations:
- Reviewer verifies the loaded run matches the CLI-provided
runId. - Deep-link load uses existing read routes only:
GET /v2/validation-runs/{runId}GET /v2/validation-runs/{runId}/artifact
- If
runIdis absent,/validationfalls back to manual run lookup without behavior change.
Operator notes:
- No new Platform API route is introduced for deep-link support.
- Deep-link failures do not bypass auth-derived identity controls.
- Release evidence should include one successful deep-link open sample (
runId,requestId, decision state).
Optional API/CLI Lane
CLI output for #279 can include a direct reviewer URL (/validation?runId=<runId>).
When running outside the CLI flow, use SDK or direct Platform API requests.
export API_BASE="https://api-nexus.lona.agency"
export TOKEN="<bearer-token>"
export REQUEST_ID="req-validation-review-$(date +%s)"
export IDEM_KEY="idem-validation-review-$(uuidgen | tr '[:upper:]' '[:lower:]')"
curl -sS "$API_BASE/v2/validation-runs" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "X-Request-Id: $REQUEST_ID" \
-H "Idempotency-Key: $IDEM_KEY" \
-d '{
"strategyId":"strat-001",
"requestedIndicators":["zigzag","ema"],
"datasetIds":["dataset-btc-1h-2025"],
"backtestReportRef":"blob://validation/candidate/backtest-report.json",
"policy":{
"profile":"STANDARD",
"blockMergeOnFail":true,
"blockReleaseOnFail":true,
"blockMergeOnAgentFail":true,
"blockReleaseOnAgentFail":false,
"requireTraderReview":true,
"hardFailOnMissingIndicators":true,
"failClosedOnEvidenceUnavailable":true
}
}'
export RUN_ID="<shared-run-id>"
export SHARED_REVIEW_IDEM_KEY="idem-shared-review-$(uuidgen | tr '[:upper:]' '[:lower:]')"
curl -sS "$API_BASE/v2/validation-sharing/runs/$RUN_ID/review" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "X-Request-Id: req-shared-review-$(date +%s)" \
-H "Idempotency-Key: $SHARED_REVIEW_IDEM_KEY" \
-d '{
"reviewerType":"trader",
"decision":"pass",
"summary":"Shared review completed with no blocking findings."
}'
Reviewer Evidence Checklist
- Record
runId,requestId, and decision timestamp. - Save canonical artifact output from
GET /v2/validation-runs/{runId}/artifact. - Save review submission request/response payloads.
- Save render request/response payloads when HTML/PDF is requested.
- Link governance checks from the PR:
contracts-governancedocs-governancellm-package-governance
PR and Program Status Updates
When opening and merging the PR for #313, post status updates in both the child issue and #310.
Validation Review Web status:
- Parent: #310
- Child: #313
- PR: <url>
- Status: OPEN | IN_REVIEW | MERGED
- Checks: contracts-governance=<status>, docs-governance=<status>, llm-package-governance=<status>
- Cursor/Greptile findings: resolved | disposition linked
- Evidence: <CI run links + artifact refs>
Traceability
- Child issue: #313
- Parent issue: #310
- Prior validation-review parent: #288
- Web lane page:
/frontend/src/app/(dashboard)/validation/page.tsx - Web API proxy:
/frontend/src/app/api/validation/runs/route.ts - Shared web API proxy:
/frontend/src/app/api/shared-validation/runs/ - Contract source:
/docs/architecture/specs/platform-api.openapi.yaml - Governance:
/.github/workflows/contracts-governance.yml,/.github/workflows/docs-governance.yml,/.github/workflows/llm-package-governance.yml