Repository navigation
feat(views): define the benchmark scorecards ViewModel contract - #124
Conversation
The remediation and remediation_finding tables (postgres + sqlite): mutable current-state domain table + many-to-many junction, following the finding_cwe and run_order precedents. 💘 Generated with Crush Assisted-by: Crush:rits/zai-org/glm-5-3
traust-core#377 (Qodo): a scheduled remediate_finding order carried only its asset and repository, so the runner took the repository's oldest pending work item, not the one the order was created for. - traust_ops.run_order.work_item_id: the remediation assessment (work item) an order runs, FK to assessment, ON DELETE RESTRICT, nullable for every asset that runs no named item; last column, so an upgraded database has the same order; partial index for the runner's lookup - ops_sql.run_order_work_item_upgrade(dialect): the explicit additive upgrade for a database whose run_order predates it (no inference) - tests: only a real assessment, RESTRICT, upgrade keeps existing rows Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…_order - remediation_finding.repository_id must own its finding (insert and update), so the per-repository view never attributes a finding elsewhere - run_order.work_item_id must be a work item on the order's repository; the additive upgrade installs the guard too - the empty-effort deletion test now deletes an empty effort and checks it is gone (it deleted a finding id from remediation, a no-op) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- a finding under remediation keeps its repository (its links name it) - a repository_ref holding a work item an order runs keeps its repository (assessments are immutable, so the ref is the only way the pairing could break); the run_order upgrade installs this guard too Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The forward guards take FOR SHARE on the finding / repository_ref they compare against, so a concurrent move waits for the link or order (then its reverse guard, on a fresh snapshot, sees it) and a link or order waits for a move in flight (then reads where it went). Two-connection regressions cover both interleavings for both pairings. The documented manual rollback now drops the guards left on finding and repository_ref; a test runs it and moves an unlinked finding and ref. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings #119 (critical misses view) so traust-core#377 can pin one commit holding both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR Summary by QodoDefine the benchmark scorecards ViewModel contract
AI Description
Diagram
High-Level Assessment
Files changed (3)
|
Code Review by Qodo
1. Existing databases lack the new tables
|
views_schema/v1/benchmark-scorecards.schema.json: a scorecards array, one per auditor version over the recorded benchmark audits (oldest first, empty when none), each with overall and per-CWE/severity/provenance/language/split recall, near misses, the injection summary, mean stability and target counts. Recall is null when nothing was expected, never 0; rates stay in [0, 1]. Held-out targets never reach this view. Consumer: traust-core's benchmark-scorecards Insight view over DetectionQualityService.scorecards. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
0d2566f to
4b1a274
Compare
|
Code review by qodo was updated up to the latest commit 4b1a274 |
|
Code review by qodo was updated up to the latest commit 68c78de |
|
Addressing the Qodo review findings (both the round-1 comment and the current one, reviewed up to Round 2, finding 1 — "Existing databases lack the new tables" (High): obsolete. The cited focus areas ( Round 2, finding 2 — "Unrelated audits can become work items": ⊘ Outdated (same reason — the run-order DDL is gone from this PR). Round 2, finding 3 — "A scorecard can have no auditor version": ✓ fixed in Round 2, finding 4 — "Held-out or misspelled buckets still pass the view contract": fixed in Round 2, finding 5 / round 1, finding 3 — "Empty buckets can show false misses" (recall/expected disconnect): fixed in Round 1, finding 1 — "Impossible injection totals pass validation": kept as documented boundary. This matches the sibling compliance-precision contract's explicit "JSON Schema checks structure and ranges, not count arithmetic" rule, which the README section for this view already follows. Round 1, finding 2 — "Reviewers cannot identify near misses": clarified, deliberately summary-only. This view is one scorecard per auditor version over every recorded audit — a fleet-level summary. The per-match identities for adjudication are the run's Expected-finding matches table (core's
|
|
Code review by qodo was updated up to the latest commit 7c239f0 |
| "total": {"type": "integer", "minimum": 0}, | ||
| "passed": {"type": "integer", "minimum": 0}, | ||
| "reported": {"type": "integer", "minimum": 0}, | ||
| "pass_rate": {"type": ["number", "null"], "minimum": 0, "maximum": 1} |
There was a problem hiding this comment.
pass_rate is nullable with no stated null condition, unlike recall, which pins null exactly when nothing was expected. Add the same if/then pair on total, or a description saying when it is null.
| } | ||
| }, | ||
| "by_severity": { | ||
| "description": "Recall per answer-key severity, severest first.", |
There was a problem hiding this comment.
"Severest first" is unenforceable — object keys have no order. If severity ordering matters for rendering, make by_severity an ordered array; otherwise drop the phrase.
| "description": "Recall per split. Held-out targets are release scoring and never in this view, so the held_out bucket is refused.", | ||
| "allOf": [{"$ref": "#/$defs/breakdown"}], | ||
| "propertyNames": {"not": {"const": "held_out"}} | ||
| }, |
There was a problem hiding this comment.
Any key except held_out validates (typos included), unlike by_provenance/by_severity, which pin recorded vocabularies. Pin the producer's split vocabulary, or state in the description that keys are producer-defined free-form.
| "targets_skipped": {"type": "integer", "minimum": 0}, | ||
| "auditor_versions": { | ||
| "type": "array", | ||
| "minItems": 1, |
There was a problem hiding this comment.
Duplicate versions validate today — add "uniqueItems": true beside minItems.
| benchmark-scorecards view: a `scorecards` array, oldest auditor version first and | ||
| empty when no benchmark audit is recorded. Each scorecard carries overall recall, | ||
| recall by CWE, severity, target provenance, language and split, near misses (review-tier | ||
| matches, listed for adjudication and never counted as detected), the injection |
There was a problem hiding this comment.
"Listed for adjudication" reads as a list, but the schema carries only a count. Reword to "counted; identities live in the run's Expected-finding matches table" to match the near_misses description.
|
First-pass notes, all five addressed in
|
|
Code review by qodo was updated up to the latest commit 9a5157f |
|
Code review by qodo was updated up to the latest commit 2853e3e |
|
Follow-up in |
|
/lgtm |
|
@chubirka: you cannot LGTM your own PR. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: chubirka The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
The contract's own description says one scorecard per auditor version over the recorded benchmark audits; an empty auditor_versions array names no producer and cannot occur from DetectionQualityService.scorecards, which groups by assessor_version. minItems: 1 refuses the unattributable shape.
The shared breakdown def accepted any key, so a held-out split bucket or a provenance/severity outside the recorded vocabularies still validated. By provenance and by severity now $ref the benchmark-target vocabulary (the compliance-precision pattern: validators provide a local registry), and by_split refuses the held_out bucket the view contract excludes. The recall def bounded the rate independently of expected, admitting a 0 rate for nothing expected and a null rate for findings expected. An if/then per side now requires null exactly when expected is 0 and a number otherwise; a measured zero with findings expected still validates. near_misses is documented as a summary count: the per-match identities for adjudication are the run's Expected-finding matches table, not this view.
Five notes from smith-xyz's first pass: - pass_rate pins null exactly when there were no canaries, mirroring recall's if/then coupling to expected; - 'severest first' moves from an unenforceable object-key claim to what the producer does (SEVERITY_ORDER); the description now says keys carry no order of their own; - by_split pins the recorded vocabulary (development only) instead of accepting any key but held_out; - auditor_versions adds uniqueItems beside minItems; - the README counts near misses for adjudication instead of listing them; identities live in the run's Expected-finding matches table.
Review follow-up on 097d789: the phrase survives as a producer description, which is neither of the two options the note offered. Keys are unordered; that is all the description now says.
2853e3e to
245d8bd
Compare
Benchmark-scorecards (PR #124) landed on development; the README grew its view paragraph in the same spot this branch adds the inventory-export paragraph. Keep both: scorecards first, then inventory-export.
|
Code review by qodo was updated up to the latest commit 245d8bd |
Plan "traust runs on core development" (
11-consumer-adoption/done/15-michele-traust-runs-on-core-development.md), decision 10: the benchmark/scorecards slice. It follows the critical-misses chain (contracts#119 → core#379 → traust#376). This PR is the contract; core's view and traust's publish follow.views_schema/v1/benchmark-scorecards.schema.jsonis the ViewModel for traust-core'sbenchmark-scorecardsview overDetectionQualityService.scorecards:scorecardsarray with one scorecard per auditor version over the recorded benchmark audits, oldest first, empty when none is recorded.detectedoverexpected, andnullwhen nothing was expected (never 0). Rates stay in [0, 1] and counts stay non-negative.--runs-dir) remains a core door, not a view.I validated a real core
Scorecard.model_dump(mode="json")against the schema with no errors.Tests
New
tests/test_benchmark_scorecards_view.py: a valid document, an empty list, null recall and unmeasured values, range and non-negative bounds, every field required, and unknown fields refused. Contracts suite: 1,392 passed (the skipped cases are Postgres-only; this PR has no DDL).🤖 Generated with Claude Code