---
title: "agent.finding@1 · OpenVibe.Services"
canonical: "https://openvibe.services/docs/contracts/agent.finding"
updated: null
---

# agent.finding@1 · OpenVibe.Services

HTML: [agent.finding@1 · OpenVibe.Services](https://openvibe.services/docs/contracts/agent.finding)

# AgentFinding `agent.finding@1`

Generated at 2026-11-13 06:25:48 UTC from [`openvibe-contracts v0.419.0`](https://github.com/OpenVibers/OpenVibe.Contracts/tree/v0.419.0) and [`openvibe-sdk v0.161.0`](https://github.com/OpenVibers/OpenVibe.SDK/tree/v0.161.0).

- Version: 1.0.0
- Owner: actor
- Visibility: public
- Status: active
- Compatibility: backward
- Schema: [`https://openvibe.network/contracts/agent/finding.v1.json`](/docs/contracts/agent.finding.json)

One finding of a review, a hunt or a security pass. The verdict is the evidence bar, not the severity: \`confirmed\` means the finding was reproduced or re-read and carries \`severity\` and \`confidence\`; \`needs\_validation\` is an unprovable claim turned into a lead with an exact \`blockers\` entry and a \`validation\_plan\`, and it has no severity by design (never noise, never a silent pass); \`rejected\` says why it does not stand. \`fingerprint\` is the stable identity — repository-relative file plus whitespace-normalised, lowercased text — so a re-wrapped finding keeps its id while a real rewording becomes a new one. \`trace\` and \`evidence\` carry repository-relative locations and the commands or answers that show the fact. A validator must also refuse \`severity.overall\_severity\` above the demonstrated \`severity.impact\`; the schema cannot rank two enums, so \`description\` states the rule.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `verdict` | enum | yes | confirmed: reproduced or re-read by a second agent. needs\_validation: a lead that could not be proven locally; it carries blockers and a plan, never a severity. rejected: the claim was refuted; it carries a reason. | - `one of "confirmed", "needs_validation", "rejected"` |
| `fingerprint` | string | yes | The stable id: repository-relative file + whitespace-normalised, lowercased finding text (for example \`server/x.js:the cache never expires\`). | - `minLength 1`<br>- `maxLength 200` |
| `title` | string | yes |  | - `minLength 1`<br>- `maxLength 200` |
| `description` | string | yes | What is wrong, in plain words; no severity claim beyond the evidence. | - `minLength 1`<br>- `maxLength 4000` |
| `trace` | `https://openvibe.network/contracts/agent/finding.v1.json#/$defs/location` | yes | Where the issue lives, repository-relative, most specific first. | - `minItems 1`<br>- `maxItems 50` |
| `evidence` | `https://openvibe.network/contracts/agent/finding.v1.json#/$defs/evidence` | yes | What shows the fact: the source read, the command and its output, the response, the screenshot or the test. | - `minItems 1`<br>- `maxItems 50` |
| `severity` | `https://openvibe.network/contracts/agent/finding.v1.json#/$defs/severity` |  |  |  |
| `confidence` | `https://openvibe.network/contracts/agent/finding.v1.json#/$defs/confidence` |  |  |  |
| `remediation` | `https://openvibe.network/contracts/agent/finding.v1.json#/$defs/remediation` |  |  |  |
| `claimed_root_cause` | string |  | The suspected cause of a needs\_validation lead, stated as a claim to test. | - `minLength 1`<br>- `maxLength 2000` |
| `blockers` | array of string |  | The exact reason the claim could not be proven here (a credential, an environment, data, a downstream service). | - `minItems 1`<br>- `maxItems 20`<br>- `items: minLength 1`<br>- `items: maxLength 500` |
| `validation_plan` | `https://openvibe.network/contracts/agent/finding.v1.json#/$defs/validation_plan` |  |  |  |
| `reason` | string |  | Why a rejected claim does not stand. | - `minLength 1`<br>- `maxLength 2000` |

## Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

 Valid: confirmed

~~~~
{
  "verdict": "confirmed",
  "fingerprint": "server/session.js:the cookie is set without httponly",
  "title": "Session cookie has no HttpOnly flag",
  "description": "The sign-in response sets ov_session without HttpOnly, so a script on the page can read it.",
  "trace": [
    {
      "file": "server/session.js",
      "line": 42,
      "symbol": "setSessionCookie"
    }
  ],
  "evidence": [
    {
      "kind": "source",
      "summary": "The cookie options omit httpOnly.",
      "location": {
        "file": "server/session.js",
        "line": 42
      }
    },
    {
      "kind": "command",
      "summary": "The sign-in response shows the cookie without HttpOnly.",
      "command": "curl -si -X POST localhost:4900/signin -d '{}' | grep set-cookie",
      "output": "set-cookie: ov_session=…; Path=/; SameSite=Lax"
    }
  ],
  "severity": {
    "likelihood": "medium",
    "impact": "high",
    "overall_severity": "high"
  },
  "confidence": {
    "level": "high",
    "reason": "Reproduced against the local server and the source shows the same."
  },
  "remediation": {
    "strategy": "Add HttpOnly to the cookie options.",
    "code_changes": [
      {
        "file": "server/session.js",
        "change": "Add httpOnly: true to the cookie options."
      }
    ]
  }
}
~~~~

Valid: needs-validation

~~~~
{
  "verdict": "needs_validation",
  "fingerprint": "server/billing.js:the retry may debit twice",
  "title": "A retried debit may charge twice",
  "description": "The retry path calls the debit again without reading the receipt; on a real card this could charge twice.",
  "trace": [
    {
      "file": "server/billing.js",
      "line": 118,
      "symbol": "debitRetry"
    }
  ],
  "evidence": [
    {
      "kind": "source",
      "summary": "The retry calls debit() before the receipt is read.",
      "location": {
        "file": "server/billing.js",
        "line": 118
      }
    }
  ],
  "claimed_root_cause": "The retry does not look up the first attempt's idempotency key.",
  "blockers": [
    "A test charge needs the live payment provider's sandbox credential, which is not on this machine."
  ],
  "validation_plan": {
    "local": "Point the provider client at the sandbox and post one retried payment; the provider's ledger shows one or two charges.",
    "deployment": "Replay a failed billing retry with a test account and read the provider's ledger."
  },
  "remediation": {
    "strategy": "Reuse the first attempt's idempotency key on the retry."
  }
}
~~~~

Valid: rejected

~~~~
{
  "verdict": "rejected",
  "fingerprint": "server/cache.js:the cache never expires",
  "title": "The cache never expires",
  "description": "The review said the cache has no expiry; the entry it named has a TTL two lines down.",
  "trace": [
    {
      "file": "server/cache.js",
      "line": 31,
      "symbol": "put"
    }
  ],
  "evidence": [
    {
      "kind": "source",
      "summary": "The named call passes ttlMs.",
      "location": {
        "file": "server/cache.js",
        "line": 31
      }
    }
  ],
  "reason": "The TTL is set by the options object on line 34, which the review did not read."
}
~~~~

Rejected: confirmed-without-severity

~~~~
{
  "verdict": "confirmed",
  "fingerprint": "server/x.js:a confirmed finding without severity",
  "title": "A confirmed finding without a severity",
  "description": "A confirmed finding must say how likely and how bad it is.",
  "trace": [
    {
      "file": "server/x.js",
      "line": 1
    }
  ],
  "evidence": [
    {
      "kind": "test",
      "summary": "The failing test."
    }
  ],
  "confidence": {
    "level": "high",
    "reason": "The test fails."
  }
}
~~~~

Rejected: lead-with-severity

~~~~
{
  "verdict": "needs_validation",
  "fingerprint": "server/x.js:a lead with a severity",
  "title": "A lead that still carries a severity",
  "description": "A claim that could not be proven must stay a lead and carry no severity.",
  "trace": [
    {
      "file": "server/x.js",
      "line": 1
    }
  ],
  "evidence": [
    {
      "kind": "source",
      "summary": "The source."
    }
  ],
  "claimed_root_cause": "A guess.",
  "blockers": [
    "No credential."
  ],
  "validation_plan": {
    "local": "Try it."
  },
  "severity": {
    "likelihood": "low",
    "impact": "low",
    "overall_severity": "low"
  }
}
~~~~

Rejected: lead-without-blocker

~~~~
{
  "verdict": "needs_validation",
  "fingerprint": "server/x.js:a lead without its blocker",
  "title": "A lead without a blocker",
  "description": "An unprovable claim must say exactly what blocked it.",
  "trace": [
    {
      "file": "server/x.js",
      "line": 1
    }
  ],
  "evidence": [
    {
      "kind": "source",
      "summary": "The source."
    }
  ],
  "claimed_root_cause": "A guess.",
  "validation_plan": {
    "local": "Try it."
  }
}
~~~~

Rejected: rejected-without-reason

~~~~
{
  "verdict": "rejected",
  "fingerprint": "server/x.js:a rejected claim without a reason",
  "title": "A rejected claim without a reason",
  "description": "A rejected finding must say why it does not stand.",
  "trace": [
    {
      "file": "server/x.js",
      "line": 1
    }
  ],
  "evidence": [
    {
      "kind": "source",
      "summary": "The source."
    }
  ]
}
~~~~

## Validate

~~~~
const contracts = require('openvibe-contracts');
contracts.validate('agent.finding@1', value);   // { valid, errors: [{ path, message }] }
~~~~
