Skip to content

fix(ops): fail closed on unmeasured CAPPO validation - #31

Merged
reprewindai-dev merged 2 commits into
mainfrom
fix/ops-validate-measured-latency
Aug 8, 2026
Merged

fix(ops): fail closed on unmeasured CAPPO validation#31
reprewindai-dev merged 2 commits into
mainfrom
fix/ops-validate-measured-latency

Conversation

@reprewindai-dev

@reprewindai-devreprewindai-dev commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Source-of-truth correction

Current main removed synthetic PGL fallback hashes, but /api/ops/validate still swallows a failed CAPPO health fetch and then reports the elapsed wall-clock time as latency to cappo-backend. That can manufacture a plausible measurement even when no CAPPO HTTP response was observed.

Change

  • fail closed with 502 when the CAPPO health probe cannot connect
  • fail closed when CAPPO returns a non-2xx health response
  • only record/report CAPPO latency after an actual HTTP response
  • do not call PGL when CAPPO health is not established
  • stop returning raw internal exception details to clients
  • add focused Vitest coverage for network failure, unhealthy CAPPO, and successful CAPPO -> PGL sequencing

Truth boundary

This establishes source behavior only. It does not verify CAPPO 8002, cAPI 3003, Gnomledger 8001, container listeners, deployed SHA, or Traefik routing.

Required merge gates

  • npm test -- src/app/api/ops/validate/route.test.ts
  • full npm test
  • lint/type/build
  • security/CodeQL checks on the exact head

Keep draft until those checks execute successfully.

Summary by CodeRabbit

  • Bug Fixes

    • Validation now requires a successful CAPPO health check before proceeding.
    • Connection failures and unhealthy responses return a clear HTTP 502 status without recording misleading latency data.
    • Unexpected validation errors now return a generic HTTP 500 response without exposing sensitive error details.
    • Successful checks continue to evidence anchoring with accurate probe latency and returned anchor hashes.
  • Tests

    • Added coverage for CAPPO failures, unhealthy responses, successful checks, and evidence anchoring.

@vercel

vercelBot commented Aug 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
c-apiErrorErrorAug 8, 2026 8:18am
veklom-id-59uwErrorErrorAug 8, 2026 8:18am

@coderabbitai

coderabbitaiBot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The validation route now requires a successful CAPPO health probe before PGL anchoring. It returns 502 for probe or anchoring failures and a generic 500 for unexpected errors. Tests cover failed, unhealthy, and successful probe flows.

Changes

Validation route

Layer / File(s)Summary
CAPPO probe gate
src/app/api/ops/validate/route.ts
The route returns 502 for CAPPO connection failures and non-OK responses. It records latency only after receiving a response.
PGL anchoring and validation coverage
src/app/api/ops/validate/route.ts, src/app/api/ops/validate/route.test.ts
The route sends measured latency to PGL, returns the evidence anchor hash, and hides unexpected error messages. Tests cover failed, unhealthy, and successful probe flows.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
participant Client
participant ValidateRoute
participant CAPPO
participant PGL
Client->>ValidateRoute: POST /api/ops/validate
ValidateRoute->>CAPPO: health probe
CAPPO-->>ValidateRoute: OK response
ValidateRoute->>PGL: evidence request with latency
PGL-->>ValidateRoute: evidence anchor hash
ValidateRoute-->>Client: success response with hash
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly and concisely describes the main change: making CAPPO validation fail closed when latency is not measured.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ops-validate-measured-latency

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

src/app/api/ops/validate/route.test.ts

Oops! Something went wrong! :(

ESLint: 10.8.1

TypeError [ERR_IMPORT_ATTRIBUTE_MISSING]: Module "file:///.eslintrc.json?mtime=1786195158862" needs an import attribute of "type: json"
at validateAttributes (node:internal/modules/esm/assert:88:15)
at defaultLoadSync (node:internal/modules/esm/load:164:3)
at #loadAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:776:12)
at #loadSync (node:internal/modules/esm/loader:796:49)
at ModuleLoader.load (node:internal/modules/esm/loader:762:26)
at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:504:31)
at #getOrCreateModuleJobAfterResolve (node:internal/modules/esm/loader:555:36)
at afterResolve (node:internal/modules/esm/loader:603:52)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:609:12)
at node:internal/modules/esm/loader:628:32

src/app/api/ops/validate/route.ts

Oops! Something went wrong! :(

ESLint: 10.8.1

TypeError [ERR_IMPORT_ATTRIBUTE_MISSING]: Module "file:///.eslintrc.json?mtime=1786195158862" needs an import attribute of "type: json"
at validateAttributes (node:internal/modules/esm/assert:88:15)
at defaultLoadSync (node:internal/modules/esm/load:164:3)
at #loadAndMaybeBlockOnLoaderThread (node:internal/modules/esm/loader:776:12)
at #loadSync (node:internal/modules/esm/loader:796:49)
at ModuleLoader.load (node:internal/modules/esm/loader:762:26)
at ModuleLoader.loadAndTranslate (node:internal/modules/esm/loader:504:31)
at #getOrCreateModuleJobAfterResolve (node:internal/modules/esm/loader:555:36)
at afterResolve (node:internal/modules/esm/loader:603:52)
at ModuleLoader.getOrCreateModuleJob (node:internal/modules/esm/loader:609:12)
at node:internal/modules/esm/loader:628:32


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@reprewindai-devChatGPT Codex Connector

Copy link
Copy Markdown
OwnerAuthor

@coderabbitai review

Please review the current exact head with emphasis on source-of-truth behavior: CAPPO connection/non-2xx failures must not produce fabricated latency, failed CAPPO validation must prevent PGL anchoring, client errors must not leak raw upstream exception details, and successful responses must distinguish observed CAPPO timing from durable Gnomledger/PGL evidence. Keep runtime/deployment claims out of scope unless backed by measured evidence.

@coderabbitai

coderabbitaiBot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bashset -euo pipefail
echo'--- PR metadata and changed files ---'
gh pr view 31 --repo reprewindai-dev/cAPI --json headRefOid,baseRefOid,files,title,url
echoecho'--- Diff against PR base ---'
gh pr diff 31 --repo reprewindai-dev/cAPI --color=never
echoecho'--- Candidate files ---'
fd -t f 'route(\.test)?\.(ts|tsx|js|jsx)$|validate'.| sed -n '1,160p'echoecho'--- Route/test outlines ---'forfin$(fd -t f .| rg '(^|/)(route|route\.test)\.(ts|tsx|js|jsx)$'| rg 'ops|validate');doecho"### $f"
ast-grep outline "$f" --items all ||truedone

Length of output: 10199


🏁 Script executed:

#!/bin/bashset -euo pipefail
echo'--- Route implementation ---'
cat -n src/app/api/ops/validate/route.ts
echoecho'--- Focused test ---'
cat -n src/app/api/ops/validate/route.test.ts
echoecho'--- Endpoint consumers and response-field conventions ---'
rg -n -C 4 --glob '!src/app/api/ops/validate/route.ts' --glob '!src/app/api/ops/validate/route.test.ts' \
'(api/ops/validate|anchorHash|evidence_hash|latency_ms|cappo_status|pgl_status)' src README.md package.json 2>/dev/null ||trueechoecho'--- Project test/type/build scripts and TypeScript settings ---'if [ -f package.json ];then
node -e "const p=require('./package.json'); console.log(JSON.stringify({scripts:p.scripts,devDependencies:p.devDependencies,dependencies:p.dependencies},null,2))"fi
fd -t f '^(tsconfig.*\.json|vitest.*\.(ts|js|mts|mjs)|next\.config\..*)$'. -x sh -c 'echo "### $1"; cat -n "$1"' sh {}

Length of output: 25675


🏁 Script executed:

#!/bin/bashset -euo pipefail
repo='reprewindai-dev/cAPI'
head='b57012fd6069de77fa4af183ccf0ef6229a2ccf7'echo'--- PR head verification ---'
gh pr view 31 --repo "$repo" --json headRefOid,isDraft,mergeStateStatus,url
echoecho'--- Checks for exact head ---'
gh pr checks 31 --repo "$repo" --json name,state,workflow,link,bucket ||trueechoecho'--- Commit check-runs and statuses for exact head ---'
gh api "repos/$repo/commits/$head/check-runs?per_page=100" \
--jq '.check_runs[] | {name, status, conclusion, details_url, app: .app.slug}'
gh api "repos/$repo/commits/$head/status" \
--jq '{state, statuses: [.statuses[] | {context, state, target_url}]}'

Length of output: 2472


@reprewindai-dev Reviewed PR #31 at exact head b57012fd6069de77fa4af183ccf0ef6229a2ccf7.

No source-level findings.

  • A rejected CAPPO health fetch returns 502 before the PGL request. It records no CAPPO latency.
  • A non-2xx CAPPO health response returns 502 before the PGL request. It reports latency only after an HTTP response exists.
  • The success path records the observed CAPPO timing separately from the PGL evidence hash. It returns success only after PGL returns an evidence_hash.
  • The outer error response no longer exposes error.message.
  • The focused Vitest coverage checks CAPPO connection failure, unhealthy CAPPO, and CAPPO-to-PGL sequencing.

The required merge gates are not complete on this exact head. The GitHub Actions verify check failed. Two Vercel checks also failed. I did not infer any runtime or deployment condition from those checks. Keep the PR as a draft until the required checks pass.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/app/api/ops/validate/route.ts`:
- Around line 53-64: Wrap the PGL fetch and pglRes.json() operations in the
route handler’s local try/catch so transport, timeout, DNS, and
response-decoding failures return the existing controlled 502 response instead
of reaching the outer 500 handler. Preserve the current non-OK status handling
and add regression coverage for rejected PGL requests and invalid PGL JSON.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1f62ffb7-0457-4bfd-b0d9-29d4bba1eb7d

📥 Commits

Reviewing files that changed from the base of the PR and between 3f2525f and b57012f.

📒 Files selected for processing (2)
  • src/app/api/ops/validate/route.test.ts
  • src/app/api/ops/validate/route.ts

Comment on lines +53 to 64
signal: AbortSignal.timeout(3000),
});

if (!pglRes.ok) {
logs.push(`[PGL] Error: returned status ${pglRes.status}. Aborting — no fallback hash.`);
return NextResponse.json(
{ error: 'PGL commitment failed', pgl_status: pglRes.status, logs },
{ status: 502 }
{ status: 502 },
);
}

const pglData = await pglRes.json();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Return 502 for PGL transport and response-decoding failures.

If the PGL fetch call rejects, such as on timeout or DNS failure, the outer handler returns 500. If pglRes.json() rejects, the outer handler also returns 500. Both cases are PGL anchoring failures.

Wrap the PGL request and JSON decoding in a local try/catch. Return the existing controlled 502 response from that handler. Add regression tests for a rejected PGL request and invalid PGL JSON.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/app/api/ops/validate/route.ts` around lines 53 - 64, Wrap the PGL fetch
and pglRes.json() operations in the route handler’s local try/catch so
transport, timeout, DNS, and response-decoding failures return the existing
controlled 502 response instead of reaching the outer 500 handler. Preserve the
current non-OK status handling and add regression coverage for rejected PGL
requests and invalid PGL JSON.

@reprewindai-dev
reprewindai-dev merged commit b2d1f8c into mainAug 8, 2026
2 of 6 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@reprewindai-dev