[AI-1899] Parse retain_fact object shape + carry applies_to_* on the judge-fact wire - #546
Conversation
…udge-fact wire
The judge may now return retain_fact as the plain string (unchanged) or an object
{"fact": "...", "applies_to_vendors": [...], "applies_to_session_kinds": [...]} declaring which
vendors / session kinds the fact is specific to. ExtractRetainFact returns a RetainedFact struct
(fact + optional axes) parsing both shapes: a malformed object (missing/empty fact) yields no fact
without throwing, and a malformed axis (non-string element) drops that axis to null while keeping the
fact and the other axis. The verdict JSON schema widens retain_fact to string|object|null, and
JudgeFactPayload carries the two optional arrays (omitted when null so older servers ignore them).
Server ships first; an older server tolerates the extra JSON fields, and an older CLI already
degrades an object-shaped retain_fact to no fact (defensive parse), so the rollout is safe either way.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PR Summary by QodoParse widened retain_fact contract and forward applicability on judge-fact posts
AI Description
Diagram
High-Level Assessment
Files changed (3)
|
Code Review by Qodo
1.
|
|
/agentic_review |
|
Code review by qodo was updated up to the latest commit 527dc56 |
…und applicability arrays - Schema (Q2): retain_fact object now requires "fact" (a factless applicability-only object no longer passes validation) and each applies_to_* array has maxItems:16. - Trim (Q3): ReadStringArrayOrNull trims each value so "codex " matches the server's exact vendor/session-kind filter. - Bound (Q4): the parser caps each axis at 16 items (mirrors the schema), so a hallucinating judge can't bloat the outgoing payload. - Raw ValueKind checks (Q1) left as-is: they match EvalService's established idiom (the surrounding ParseRetrospective/ReadStringOrNull/ReadStringArray all inspect ValueKind directly). Tests: +2 (trims values, caps oversized array); ExtractRetainFact suite now 16.
- Guard JSON root with ValueKind == Object before TryGetProperty (it throws InvalidOperationException on a bare string/array/number/bool root, which the JsonException catch would not swallow — contract is malformed -> null, never throw). Regression test added (4 cases). - Add nested additionalProperties:false to the retain_fact object node so a strict structured-output validator accepts the widened schema. - Move MaxApplicabilityItems const above ReadStringArrayOrNull's doc comment so the summary sits directly on the method. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the raw JsonElement.ValueKind comparisons in ExtractRetainFact and ReadStringArrayOrNull with the project's IsObject/IsString/Str/Arr helpers (the repo's standardized, hardened JSON-inspection surface). Behavior is unchanged — 20 ExtractRetainFact tests still pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
AI-1899 (kcap-cli) — parse the widened retain_fact contract
The daemon eval runner's judge may now return
retain_factas either the plain string (unchanged) or an object{"fact": "...", "applies_to_vendors": [...], "applies_to_session_kinds": [...]}declaring which vendors / session kinds a fact is specific to. This is the kcap-cli half; the kcap-server prompt widening + payload-tolerance ship first.Changes
EvalService.ExtractRetainFactreturns aRetainedFactstruct (fact + optionalAppliesToVendors/AppliesToSessionKinds), parsing both shapes:fact(required; missing/empty ⇒ no fact, never throws) + each axis read as an array of non-empty strings, or null when absent/empty/malformed (a non-string element drops the whole axis, keeping the fact and the other axis — matching the server's whole-axis-discard).retain_factis nowstring | object | null.JudgeFactPayloadcarries the two optional arrays (omitted from the wire when null, so older servers ignore them).Rollout safety
Server-first: an older server tolerates the extra JSON fields, and an older CLI already degrades an object-shaped
retain_factto no fact (its parse returns null for non-string), so no combination breaks.Tests
EvalServiceTestsExtractRetainFact suite: 14 cases (string shape has no applicability; object both-axes / one-axis / empty-array-axis→null / malformed-axis-drops-that-axis / object-without-fact→null; plus the existing null/empty/fence/malformed cases).