-
Notifications
You must be signed in to change notification settings - Fork 1
review: hand off out-of-lane observations instead of dropping them #245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2d746ac
6727eb6
5c00cf3
52073ef
93770e6
30e7527
19f06cb
d1b5288
254dfc4
917cc11
0ee295a
58cd4ed
b19b08d
bced291
da1115c
a6883be
0d02672
93d8dec
e3b34eb
491a983
7b5318c
5dd182b
92bffa2
2be8ede
3a8fc5c
2812679
2ce35a0
c0fece2
e02ac40
d225bd4
cbc838d
63097f1
9012508
25133b4
0a3d212
a547972
391151b
d2c4c70
996766f
fb81be8
a659be8
b7c3786
541e413
fd42efd
6d3459a
1438a67
193ae69
7a2065c
00ce9d4
4bd445c
da1e1db
1bc9e08
80da666
82bc5ed
7f2fc3a
9cc1a89
da93344
aa5a246
248cdf8
51691de
7de0635
6a1723c
cbb2999
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "review": minor | ||
| --- | ||
|
|
||
| Out-of-lane observations are handed off, never dropped. Observed on the review-v1.4.0 re-run lifecycle: the skill-auditor found a real eventual-consistency bug (dedup reads via a Query immediately after PutMulti) and discarded it, verbatim "that's a correctness concern, not a quotable skill-rule violation, so I'll leave it"; correct under quote-the-rule, but the observation died and the defect shipped unflagged. The skill-auditor and every specialist lens may now return `out_of_lane_observations[]` (path, optional line, the concern stated concretely, a required concrete `failure_scenario`, optional `suggested_lane`) for real concerns their own mandate does not let them report. The orchestrator converts each one into a candidate comment with the code-assigned label `question (non-blocking)` (a handoff is not a vetted finding and can never block on its own; the claim-validator never upgrades severity) and routes it through the identical provenance gate, scope filter, claims.json, and validation path as every other candidate. The shape is validated by the new `validateOutOfLaneObservation` in `lib/finding-schema.ts` (a sibling type, so `FINDING_SCHEMA_VERSION` stays 2: no serialized finding is invalidated). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -170,6 +170,46 @@ export type Finding = { | |
| model_authored_prose: string; | ||
| }; | ||
|
|
||
| /** | ||
| * An out-of-lane observation: a real concern a reviewer surfaced that its own | ||
| * mandate does not let it report as a finding — e.g. the skill-auditor notices | ||
| * a correctness problem while checking a rule, but quote-the-rule rightly | ||
| * forbids reporting it as a skill violation. Production motivation: on the | ||
| * review-v1.4.0 re-run lifecycle the skill-auditor found a real | ||
| * eventual-consistency bug (dedup reads via a Query immediately after | ||
| * PutMulti) and dropped it — "that's a correctness concern, not a quotable | ||
| * skill-rule violation, so I'll leave it". Correct per quote-the-rule, but the | ||
| * observation died. | ||
| * | ||
| * The skill-auditor and every specialist lens may return these alongside | ||
| * `findings[]`; the orchestrator routes each one into claim validation as a | ||
| * non-blocking candidate (label code-assigned, never model-chosen), so a | ||
| * declined-as-out-of-lane observation is validated and surfaced rather than | ||
| * discarded. An observation is a handoff, not a vetted finding: it can never | ||
| * block on its own. | ||
| */ | ||
| export type OutOfLaneObservation = { | ||
| /** Repo-relative path the observation anchors on. */ | ||
| path: string; | ||
| /** RIGHT-side diff line, when the concern is line-specific. */ | ||
| line?: number; | ||
| /** One sentence: the concern, stated concretely. Model-authored. */ | ||
| observation: string; | ||
| /** | ||
| * One sentence: the concrete inputs/state and the wrong outcome they | ||
| * produce — the claim the claim-validator attacks, exactly as on a | ||
| * finding. An observation whose scenario cannot be stated concretely is | ||
| * not worth handing off. | ||
| */ | ||
| failure_scenario: string; | ||
| /** The lane the producer thinks owns this, e.g. `correctness`. Optional. */ | ||
| suggested_lane?: string; | ||
| }; | ||
|
|
||
| export type ObservationValidationResult = | ||
| | {ok: true; observation: OutOfLaneObservation} | ||
| | {ok: false; errors: string[]}; | ||
|
|
||
| export type ValidationResult = | ||
| | {ok: true; finding: Finding} | ||
| | {ok: false; errors: string[]}; | ||
|
|
@@ -328,6 +368,60 @@ export const validateFinding = (input: unknown): ValidationResult => { | |
| return {ok: true, finding: input as Finding}; | ||
| }; | ||
|
|
||
| /** | ||
| * Validate an untrusted value against the out-of-lane observation shape. | ||
| * Same error-collecting contract as {@link validateFinding}: every problem is | ||
| * returned, so a producer's malformed handoff is diagnosable from the run | ||
| * artifact rather than silently dropped. | ||
| */ | ||
| export const validateOutOfLaneObservation = ( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question (non-blocking): |
||
| input: unknown, | ||
| ): ObservationValidationResult => { | ||
| const errors: string[] = []; | ||
|
|
||
| if (!isRecord(input)) { | ||
| return {ok: false, errors: ["observation: must be an object"]}; | ||
| } | ||
|
|
||
| if (!isNonEmptyString(input["path"])) { | ||
| errors.push("path: required non-empty string"); | ||
| } | ||
|
|
||
| const line = input["line"]; | ||
| if ( | ||
| line !== undefined && | ||
| (!Number.isInteger(line) || (line as number) < 1) | ||
| ) { | ||
| errors.push("line: must be a positive integer when present"); | ||
| } | ||
|
|
||
| if (!isNonEmptyString(input["observation"])) { | ||
| errors.push("observation: required non-empty string"); | ||
| } | ||
|
|
||
| if (!isNonEmptyString(input["failure_scenario"])) { | ||
| errors.push("failure_scenario: required non-empty string"); | ||
| } | ||
|
|
||
| if ( | ||
| input["suggested_lane"] !== undefined && | ||
| !isNonEmptyString(input["suggested_lane"]) | ||
| ) { | ||
| errors.push("suggested_lane: must be a non-empty string when present"); | ||
| } | ||
|
|
||
| if (errors.length > 0) { | ||
| return {ok: false, errors}; | ||
| } | ||
|
|
||
| return {ok: true, observation: input as OutOfLaneObservation}; | ||
| }; | ||
|
|
||
| /** Narrowing boolean wrapper around {@link validateOutOfLaneObservation}. */ | ||
| export const isValidOutOfLaneObservation = ( | ||
| input: unknown, | ||
| ): input is OutOfLaneObservation => validateOutOfLaneObservation(input).ok; | ||
|
|
||
| /** Narrowing boolean wrapper around {@link validateFinding}. */ | ||
| export const isValidFinding = (input: unknown): input is Finding => | ||
| validateFinding(input).ok; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thought (non-blocking): The production orchestrator now routes out-of-lane observations, but the live-eval producer (
eval/live-producer.tsparseAgentFindings) reads onlyfindings[]. So in a review-trial a handed-off observation is dropped and the arm scores as if it never happened — the same drop this PR closes on the production path. Worth teaching the eval harness to readout_of_lane_observations[]in a follow-up so the trial can measure this feature.