feat(verification): typed verification_spec with roles, modes, and score rollup - #2
feat(verification): typed verification_spec with roles, modes, and score rollup#2geojaz wants to merge 3 commits into
Conversation
…h, rollup, and two new verifiers - VerificationEntry(name, role, spec) with Role = correctness | safety | catastrophic - Task.verification_spec typed as list[VerificationEntry] | None; legacy raw-dict path preserved - BaseVerifier gains mode, weight, hold_window_sec, hold_interval_sec; VerificationResult gains weight - VerificationRunner.run_entry() dispatches converge/assert/hold; unchanged raises NotImplementedError - rollup() produces RollupScores(c, rec_v, cat_v) from paired entries + results - ResourcePropertyVerifier: kubectl get -o json, dotted path eval, eq/ne/gt/gte/lt/lte/exists/absent/contains/matches, all/any/none quantifiers - HttpProbeVerifier: kubectl run --rm curl pod, HTTP status + body matching - optimize-scale task.yaml regression test: parses as list[VerificationEntry] without authoring change
…anch rename EvaluatedEntry replaces the parallel (entries, results) contract in rollup(): run_entry() now returns an EvaluatedEntry that bundles role+name+result, and rollup() accepts a single list[EvaluatedEntry] with no second sequence to misalign. Added a regression test that would have caught the original bug. VerificationEntry now rejects mode='unchanged' with a ValidationError at task-load time rather than raising NotImplementedError mid-eval. The validator walks the raw spec dict recursively so nested occurrences (e.g. inside a parallel) are caught too. The _run_leaf guard is kept as defense-in-depth. Branch renamed from worktree-agent-ad828774e2fb42332 to feat/verification-spec-schema.
There was a problem hiding this comment.
Code Review
This pull request refactors the verification harness by introducing a typed schema with VerificationEntry, execution modes (converge, assert, hold), and a scoring rollup mechanism. It also adds two new verifiers: HttpProbeVerifier and ResourcePropertyVerifier. The review feedback highlights several critical issues: HttpProbeVerifier needs the -i flag in kubectl run to capture stdout; the rollup scoring requires a guard against division by zero; the hold-mode logic in VerifierAgent needs to correctly handle 0.0 values for windows and intervals; and the regex matching in ResourcePropertyVerifier needs to handle None values safely to avoid false positives.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
…llup and hold-mode zero guards, matches None) - Fix 1 (CRITICAL): add k8s.run_pod() with -i flag and route http_probe through it; the previous hand-rolled kubectl run lacked -i so kubectl exited without attaching/capturing stdout on real clusters. run_pod is exported from devops_bench.k8s and follows the same _run_kubectl / kubeconfig-resolution pattern as the other wrappers. - Fix 2 (HIGH): guard both denominators in rollup.py against zero. Safety entries whose leaves all carry weight=0 now yield rec_v=None (consistent with no-safety-entries behavior) instead of ZeroDivisionError. Correctness leaves with total weight=0 raise a clear ValueError naming the problem. - Fix 3+4 (MEDIUM): replace truthy-or fallback with is None checks for hold_window_sec and hold_interval_sec so an explicit 0.0 is honored. Separate the loop-termination decision (time remaining in window) from the sleep call so interval=0.0 keeps sampling until the window elapses instead of breaking after one sample. - Fix 5 (MEDIUM): guard matches operator in resource_property.py against None values; str(None)=="None" previously caused false positives (e.g. pattern "on" matching a null field). None value now always returns False.
feat: verification_spec schema with roles, modes, and rollup
What this renovates and what is preserved unchanged
Renovates:
Task.verification_specmoves fromAnytolist[VerificationEntry] | None.Each entry carries a
name,role(correctness,safety,catastrophic), and a rawspecdict.
BaseVerifiergainsweight,mode, and hold-window fields.VerificationResultgainsweight.run_entry()returns a typedEvaluatedEntrythat bundles role, name, and resultso there is no second parallel sequence to misalign;
rollup()acceptslist[EvaluatedEntry]and produces
(c, rec_v, cat_v)scores. Two new verifiers land:ResourcePropertyVerifierand
HttpProbeVerifier.Preserved unchanged:
BaseVerifier.verify()signature is untouched.pod_healthy.pyandscaling_complete.pyare untouched.optimize-scale/task.yamlparses without any authoring change (regression test included)._build_verification_mappingis still the fallback for testsand any task that predates this schema.
The mode insight
Three of the four modes are scheduling strategies layered over the existing
verify()contract,not changes to it:
converge: pollverify(N)until success or timeout (existing behavior, now named).assert: callverify(0)exactly once;poll_untilevaluates the predicate before checkingthe deadline so
timeout_sec=0means one shot, no sleep.hold: sampleverify(0)repeatedly across a window; any failing sample fails the check.unchangedis designed but intentionally not built. It needs a baseline snapshot and adiff strategy that depends on verifier type. A
ValidationErroris raised at task-loadtime when any spec contains
mode: unchanged(theVerificationEntryvalidator walksthe raw spec dict recursively); the
NotImplementedErrorin_run_leafis kept asdefense-in-depth.
What is real in this PR vs. proposed next
Real (in this PR):
VerificationEntrymodel withRoleliteralTask.verification_spec: list[VerificationEntry] | NoneBaseVerifier.mode,weight,hold_window_sec,hold_interval_secVerificationResult.weightVerificationRunner.run_entry():converge,assert,holdrun_entry()returnsEvaluatedEntry(role + name + result in one object)rollup(list[EvaluatedEntry])withRollupScores; ordering-safe by constructionResourcePropertyVerifier:kubectl get -o json, dotted path eval, quantifiersHttpProbeVerifier:kubectl run --rmcurl pod, status + body matchingProposed next (not in this PR):
rollup()output into the judge/metrics layer.run_entry()called from the harness (today the harness still useswait_for_condition).unchangedmode implementation.Vocabulary roadmap
Probes that would be cheap to add under the same
BaseVerifiercontract:dns_probe: resolve a hostname from inside the clustertcp_probe: TCP connect to host:portenv_probe: check env var value inside a running container (kubectl exec)file_probe: check file contents inside a containerlog_probe: grep recent pod logs for a patterncan_i:kubectl auth can-ifor RBAC verificationcert_probe: TLS cert expiry / SAN checkunchanged_outside: a scopedunchangedmode targeting resources outside a namespacecloud_resource_property: same asresource_propertybut for GCP/AWS/Azure resourcesvia their respective APIs
policy_compliance: OPA/Gatekeeper policy evaluation resultforbidden_action: assert a named action was NOT taken (audit log or webhook check)exec_probe(arbitrary shell inside a container) is a last resort. It works but it couplesverification logic to image contents and makes tasks harder to port.