sofhiaazzhr Claude Opus 4.8 commited on
Commit
6f46e81
Β·
1 Parent(s): cbdbefb

[NOTICKET] fix(eval): update readiness eval to post-KM-652 floor (drop problem_validated)

Browse files

The readiness eval crashed at import (`_MISSING_PROBLEM` gone) and would then have hit a
TypeError (`stub_analysis_state(problem_validated=...)` β€” arg dropped in #4). Same #4
rework regression as test_help. Aligned it to the current floor (>=1 substantive record;
delta-since-report), which no longer gates on a validated goal:

- run_eval: drop the `_MISSING_PROBLEM` import + the `problem` -> code mapping; call
stub_analysis_state() with no arg.
- dataset: remove the `problem_validated` field and the `problem` missing-code from every
case; drop the two cases that only existed to isolate the retired problem gate (now
duplicates); renumber the floor group. Runs 15/15 floor-correct, alignment gap = 2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

eval/readiness/readiness_dataset.json CHANGED
@@ -1,40 +1,37 @@
1
  {
2
  "_about": "Golden dataset for the report-readiness signal (`src/agents/report/readiness.is_report_ready`). Deterministic (no LLM): each case declares an analysis state + a set of persisted AnalysisRecords/reports, and the runner feeds them through is_report_ready via injectable fake stores, scoring the boolean `ready` AND the `missing` gaps. Floor cases should score ~100% (regression value). The `alignment` group probes the deferred LLM-judge β€” see _alignment.",
3
- "_floor": "is_report_ready's deterministic floor: (1) problem_validated, (2) >=1 SUBSTANTIVE record, (3) delta-since-report. SUBSTANTIVE (KM-652 fix T1) = a record whose ANALYSIS task succeeded: tasks_run contains a task with status=success AND an analyze_* tool. A failed analysis still persists a record WITH findings (narrating the failure) and its data-access tasks (check_/retrieve_) succeed β€” so neither 'has findings' nor 'any task succeeded' counts. Only a successful analyze_* does.",
4
  "_records": "records[].analysis = 'success' (analyze_* succeeded β†’ substantive) | 'failure' (analyze_* failed, data-access still succeeded β€” the real e2e case, NOT substantive) | 'none' (only check_/retrieve_ succeeded, no analyze task β€” NOT substantive; guards the 'any task succeeded' trap). records[].findings = count (a failure run still has findings; floor ignores them now). records[].age_min / reports[].age_min = minutes ago (smaller = newer).",
5
- "_alignment": "ALIGNMENT cases: a successful analysis (floor says ready=true) but `aligned=false` means it doesn't address the problem statement β€” a human would say NOT ready. Scored floor-correct, counted separately as the 'alignment gap' = evidence for/against the LLM-judge. Alignment label owner: Rifqi (report semantics) + Sofhia.",
6
  "schema": {
7
  "id": "stable per-case handle, <group>_<NN>",
8
  "group": "floor | delta | edge | alignment",
9
- "problem_validated": "bool",
10
  "report_id": "null = never generated; a string = a report exists",
11
  "records": "[{ analysis: success|failure|none, findings: int, age_min: int }]",
12
  "reports": "[{ age_min: int }] (only meaningful when report_id set)",
13
- "aligned": "bool β€” do the analyses address the problem statement? (floor ignores this)",
14
  "expected_ready": "what the deterministic floor SHOULD return",
15
- "expected_missing": "subset of [problem, analysis, delta]",
16
  "note": "human-readable description"
17
  },
18
  "cases": [
19
- { "id": "floor_01", "group": "floor", "problem_validated": false, "report_id": null, "records": [], "reports": [], "aligned": false, "expected_ready": false, "expected_missing": ["problem", "analysis"], "note": "new analysis: no validated goal and no records" },
20
- { "id": "floor_02", "group": "floor", "problem_validated": false, "report_id": null, "records": [{ "analysis": "success", "findings": 2, "age_min": 30 }], "reports": [], "aligned": true, "expected_ready": false, "expected_missing": ["problem"], "note": "has a successful analysis but goal not validated (isolates the problem gap)" },
21
- { "id": "floor_03", "group": "floor", "problem_validated": true, "report_id": null, "records": [], "reports": [], "aligned": false, "expected_ready": false, "expected_missing": ["analysis"], "note": "validated goal but no analysis run yet" },
22
- { "id": "floor_04", "group": "floor", "problem_validated": true, "report_id": null, "records": [{ "analysis": "failure", "findings": 3, "age_min": 20 }], "reports": [], "aligned": false, "expected_ready": false, "expected_missing": ["analysis"], "note": "T1 REGRESSION: analyze_* FAILED but the record still has 3 findings (narrating failure) + check/retrieve succeeded. Must NOT be ready β€” this is the live e2e case (analyze_aggregate failed, report still got generated under the old 'has findings' rule)." },
23
- { "id": "floor_05", "group": "floor", "problem_validated": true, "report_id": null, "records": [{ "analysis": "none", "findings": 0, "age_min": 15 }], "reports": [], "aligned": false, "expected_ready": false, "expected_missing": ["analysis"], "note": "T1 nuance: only data-access tasks (check/retrieve) succeeded, no analyze task. 'any task succeeded' would wrongly pass β€” must NOT be ready." },
24
- { "id": "floor_06", "group": "floor", "problem_validated": true, "report_id": null, "records": [{ "analysis": "success", "findings": 2, "age_min": 15 }], "reports": [], "aligned": true, "expected_ready": true, "expected_missing": [], "note": "validated + one successful analysis, no prior report β†’ ready" },
25
- { "id": "floor_07", "group": "floor", "problem_validated": true, "report_id": null, "records": [{ "analysis": "success", "findings": 3, "age_min": 40 }, { "analysis": "success", "findings": 1, "age_min": 10 }], "reports": [], "aligned": true, "expected_ready": true, "expected_missing": [], "note": "multiple successful analyses β†’ ready" },
26
- { "id": "floor_08", "group": "floor", "problem_validated": true, "report_id": null, "records": [{ "analysis": "failure", "findings": 3, "age_min": 30 }, { "analysis": "success", "findings": 2, "age_min": 10 }], "reports": [], "aligned": true, "expected_ready": true, "expected_missing": [], "note": "one failed + one successful analysis β†’ the successful one is enough β†’ ready" },
27
 
28
- { "id": "delta_01", "group": "delta", "problem_validated": true, "report_id": "rep-1", "records": [{ "analysis": "success", "findings": 2, "age_min": 120 }], "reports": [{ "age_min": 5 }], "aligned": true, "expected_ready": false, "expected_missing": ["delta"], "note": "report exists, all analysis older than it β†’ nothing new to report" },
29
- { "id": "delta_02", "group": "delta", "problem_validated": true, "report_id": "rep-1", "records": [{ "analysis": "success", "findings": 2, "age_min": 5 }], "reports": [{ "age_min": 120 }], "aligned": true, "expected_ready": true, "expected_missing": [], "note": "newer successful analysis after the report β†’ ready to regenerate" },
30
- { "id": "delta_03", "group": "delta", "problem_validated": true, "report_id": "rep-1", "records": [{ "analysis": "success", "findings": 1, "age_min": 90 }, { "analysis": "success", "findings": 2, "age_min": 10 }], "reports": [{ "age_min": 60 }], "aligned": true, "expected_ready": true, "expected_missing": [], "note": "one old + one newer-than-report success β†’ ready" },
31
- { "id": "delta_04", "group": "delta", "problem_validated": true, "report_id": "rep-2", "records": [{ "analysis": "success", "findings": 2, "age_min": 90 }], "reports": [{ "age_min": 200 }, { "age_min": 30 }], "aligned": true, "expected_ready": false, "expected_missing": ["delta"], "note": "multiple reports β€” newest wins; analysis older than newest report β†’ not ready" },
32
- { "id": "delta_05", "group": "delta", "problem_validated": true, "report_id": "rep-1", "records": [{ "analysis": "success", "findings": 2, "age_min": 120 }, { "analysis": "failure", "findings": 3, "age_min": 5 }], "reports": [{ "age_min": 60 }], "aligned": true, "expected_ready": false, "expected_missing": ["delta"], "note": "T1+delta: the only NEW analysis (age 5) is a FAILURE β†’ no NEW substantive since the report β†’ not ready. A failed retry must not unlock a duplicate report." },
33
 
34
- { "id": "edge_01", "group": "edge", "problem_validated": true, "report_id": null, "records": [], "reports": [], "aligned": false, "expected_ready": false, "expected_missing": ["analysis"], "note": "doc-only analysis (RAG, no structured run) produces no AnalysisRecord β†’ never report-able under the floor. PRODUCT QUESTION: should doc-only be report-able?" },
35
 
36
- { "id": "align_01", "group": "alignment", "problem_validated": true, "report_id": null, "records": [{ "analysis": "success", "findings": 2, "age_min": 15 }], "reports": [], "aligned": false, "expected_ready": true, "expected_missing": [], "note": "GAP: successful analysis but it doesn't address the problem statement. Floor says ready; a human would say not-ready." },
37
- { "id": "align_02", "group": "alignment", "problem_validated": true, "report_id": null, "records": [{ "analysis": "success", "findings": 3, "age_min": 25 }, { "analysis": "success", "findings": 1, "age_min": 5 }], "reports": [], "aligned": false, "expected_ready": true, "expected_missing": [], "note": "GAP: lots of successful analysis, none aligned to the goal" },
38
- { "id": "align_03", "group": "alignment", "problem_validated": true, "report_id": null, "records": [{ "analysis": "success", "findings": 2, "age_min": 15 }], "reports": [], "aligned": true, "expected_ready": true, "expected_missing": [], "note": "control: successful AND aligned β†’ genuinely ready, no gap" }
39
  ]
40
  }
 
1
  {
2
  "_about": "Golden dataset for the report-readiness signal (`src/agents/report/readiness.is_report_ready`). Deterministic (no LLM): each case declares an analysis state + a set of persisted AnalysisRecords/reports, and the runner feeds them through is_report_ready via injectable fake stores, scoring the boolean `ready` AND the `missing` gaps. Floor cases should score ~100% (regression value). The `alignment` group probes the deferred LLM-judge β€” see _alignment.",
3
+ "_floor": "is_report_ready's deterministic floor (KM-652, after the problem_validated gate was removed 2026-06-24): (1) >=1 SUBSTANTIVE record, (2) delta-since-report. SUBSTANTIVE = a record whose ANALYSIS task succeeded: tasks_run contains a task with status=success AND an analyze_* tool. A failed analysis still persists a record WITH findings (narrating the failure) and its data-access tasks (check_/retrieve_) succeed β€” so neither 'has findings' nor 'any task succeeded' counts. Only a successful analyze_* does.",
4
  "_records": "records[].analysis = 'success' (analyze_* succeeded β†’ substantive) | 'failure' (analyze_* failed, data-access still succeeded β€” the real e2e case, NOT substantive) | 'none' (only check_/retrieve_ succeeded, no analyze task β€” NOT substantive; guards the 'any task succeeded' trap). records[].findings = count (a failure run still has findings; floor ignores them now). records[].age_min / reports[].age_min = minutes ago (smaller = newer).",
5
+ "_alignment": "ALIGNMENT cases: a successful analysis (floor says ready=true) but `aligned=false` means it doesn't address the analysis objective β€” a human would say NOT ready. Scored floor-correct, counted separately as the 'alignment gap' = evidence for/against the LLM-judge. Alignment label owner: Rifqi (report semantics) + Sofhia.",
6
  "schema": {
7
  "id": "stable per-case handle, <group>_<NN>",
8
  "group": "floor | delta | edge | alignment",
 
9
  "report_id": "null = never generated; a string = a report exists",
10
  "records": "[{ analysis: success|failure|none, findings: int, age_min: int }]",
11
  "reports": "[{ age_min: int }] (only meaningful when report_id set)",
12
+ "aligned": "bool β€” do the analyses address the objective? (floor ignores this)",
13
  "expected_ready": "what the deterministic floor SHOULD return",
14
+ "expected_missing": "subset of [analysis, delta]",
15
  "note": "human-readable description"
16
  },
17
  "cases": [
18
+ { "id": "floor_01", "group": "floor", "report_id": null, "records": [], "reports": [], "aligned": false, "expected_ready": false, "expected_missing": ["analysis"], "note": "new analysis: no analysis run yet β†’ not ready" },
19
+ { "id": "floor_02", "group": "floor", "report_id": null, "records": [{ "analysis": "failure", "findings": 3, "age_min": 20 }], "reports": [], "aligned": false, "expected_ready": false, "expected_missing": ["analysis"], "note": "T1 REGRESSION: analyze_* FAILED but the record still has 3 findings (narrating failure) + check/retrieve succeeded. Must NOT be ready β€” this is the live e2e case (analyze_aggregate failed, report still got generated under the old 'has findings' rule)." },
20
+ { "id": "floor_03", "group": "floor", "report_id": null, "records": [{ "analysis": "none", "findings": 0, "age_min": 15 }], "reports": [], "aligned": false, "expected_ready": false, "expected_missing": ["analysis"], "note": "T1 nuance: only data-access tasks (check/retrieve) succeeded, no analyze task. 'any task succeeded' would wrongly pass β€” must NOT be ready." },
21
+ { "id": "floor_04", "group": "floor", "report_id": null, "records": [{ "analysis": "success", "findings": 2, "age_min": 15 }], "reports": [], "aligned": true, "expected_ready": true, "expected_missing": [], "note": "one successful analysis, no prior report β†’ ready" },
22
+ { "id": "floor_05", "group": "floor", "report_id": null, "records": [{ "analysis": "success", "findings": 3, "age_min": 40 }, { "analysis": "success", "findings": 1, "age_min": 10 }], "reports": [], "aligned": true, "expected_ready": true, "expected_missing": [], "note": "multiple successful analyses β†’ ready" },
23
+ { "id": "floor_06", "group": "floor", "report_id": null, "records": [{ "analysis": "failure", "findings": 3, "age_min": 30 }, { "analysis": "success", "findings": 2, "age_min": 10 }], "reports": [], "aligned": true, "expected_ready": true, "expected_missing": [], "note": "one failed + one successful analysis β†’ the successful one is enough β†’ ready" },
 
 
24
 
25
+ { "id": "delta_01", "group": "delta", "report_id": "rep-1", "records": [{ "analysis": "success", "findings": 2, "age_min": 120 }], "reports": [{ "age_min": 5 }], "aligned": true, "expected_ready": false, "expected_missing": ["delta"], "note": "report exists, all analysis older than it β†’ nothing new to report" },
26
+ { "id": "delta_02", "group": "delta", "report_id": "rep-1", "records": [{ "analysis": "success", "findings": 2, "age_min": 5 }], "reports": [{ "age_min": 120 }], "aligned": true, "expected_ready": true, "expected_missing": [], "note": "newer successful analysis after the report β†’ ready to regenerate" },
27
+ { "id": "delta_03", "group": "delta", "report_id": "rep-1", "records": [{ "analysis": "success", "findings": 1, "age_min": 90 }, { "analysis": "success", "findings": 2, "age_min": 10 }], "reports": [{ "age_min": 60 }], "aligned": true, "expected_ready": true, "expected_missing": [], "note": "one old + one newer-than-report success β†’ ready" },
28
+ { "id": "delta_04", "group": "delta", "report_id": "rep-2", "records": [{ "analysis": "success", "findings": 2, "age_min": 90 }], "reports": [{ "age_min": 200 }, { "age_min": 30 }], "aligned": true, "expected_ready": false, "expected_missing": ["delta"], "note": "multiple reports β€” newest wins; analysis older than newest report β†’ not ready" },
29
+ { "id": "delta_05", "group": "delta", "report_id": "rep-1", "records": [{ "analysis": "success", "findings": 2, "age_min": 120 }, { "analysis": "failure", "findings": 3, "age_min": 5 }], "reports": [{ "age_min": 60 }], "aligned": true, "expected_ready": false, "expected_missing": ["delta"], "note": "T1+delta: the only NEW analysis (age 5) is a FAILURE β†’ no NEW substantive since the report β†’ not ready. A failed retry must not unlock a duplicate report." },
30
 
31
+ { "id": "edge_01", "group": "edge", "report_id": null, "records": [], "reports": [], "aligned": false, "expected_ready": false, "expected_missing": ["analysis"], "note": "doc-only analysis (RAG, no structured run) produces no AnalysisRecord β†’ never report-able under the floor. PRODUCT QUESTION: should doc-only be report-able?" },
32
 
33
+ { "id": "align_01", "group": "alignment", "report_id": null, "records": [{ "analysis": "success", "findings": 2, "age_min": 15 }], "reports": [], "aligned": false, "expected_ready": true, "expected_missing": [], "note": "GAP: successful analysis but it doesn't address the objective. Floor says ready; a human would say not-ready." },
34
+ { "id": "align_02", "group": "alignment", "report_id": null, "records": [{ "analysis": "success", "findings": 3, "age_min": 25 }, { "analysis": "success", "findings": 1, "age_min": 5 }], "reports": [], "aligned": false, "expected_ready": true, "expected_missing": [], "note": "GAP: lots of successful analysis, none aligned to the objective" },
35
+ { "id": "align_03", "group": "alignment", "report_id": null, "records": [{ "analysis": "success", "findings": 2, "age_min": 15 }], "reports": [], "aligned": true, "expected_ready": true, "expected_missing": [], "note": "control: successful AND aligned β†’ genuinely ready, no gap" }
36
  ]
37
  }
eval/readiness/run_eval.py CHANGED
@@ -35,7 +35,6 @@ from src.agents.gate import stub_analysis_state
35
  from src.agents.report.readiness import (
36
  _MISSING_ANALYSIS,
37
  _MISSING_DELTA,
38
- _MISSING_PROBLEM,
39
  is_report_ready,
40
  )
41
 
@@ -45,9 +44,9 @@ RESULTS_DIR = _HERE / "results"
45
  GROUPS = ["floor", "delta", "edge", "alignment"]
46
 
47
  # Dataset short codes -> the exact `missing` strings is_report_ready emits. Imported
48
- # from the module so the dataset stays readable and survives wording changes.
 
49
  _CODE_TO_MISSING = {
50
- "problem": _MISSING_PROBLEM,
51
  "analysis": _MISSING_ANALYSIS,
52
  "delta": _MISSING_DELTA,
53
  }
@@ -139,7 +138,9 @@ def _build_reports(specs: list[dict[str, Any]], now: datetime) -> list[_FakeRepo
139
 
140
  async def run_case(case: dict[str, Any]) -> CaseResult:
141
  now = datetime.now(UTC)
142
- state = stub_analysis_state(problem_validated=bool(case["problem_validated"]))
 
 
143
  if case.get("report_id"):
144
  state = state.model_copy(update={"report_id": case["report_id"]})
145
 
 
35
  from src.agents.report.readiness import (
36
  _MISSING_ANALYSIS,
37
  _MISSING_DELTA,
 
38
  is_report_ready,
39
  )
40
 
 
44
  GROUPS = ["floor", "delta", "edge", "alignment"]
45
 
46
  # Dataset short codes -> the exact `missing` strings is_report_ready emits. Imported
47
+ # from the module so the dataset stays readable and survives wording changes. The
48
+ # `problem` code was retired with the problem_validated gate (KM-652, 2026-06-24).
49
  _CODE_TO_MISSING = {
 
50
  "analysis": _MISSING_ANALYSIS,
51
  "delta": _MISSING_DELTA,
52
  }
 
138
 
139
  async def run_case(case: dict[str, Any]) -> CaseResult:
140
  now = datetime.now(UTC)
141
+ # The problem_validated gate was removed (KM-652); readiness no longer reads the goal,
142
+ # so a bare stub state + report_id is all is_report_ready needs.
143
+ state = stub_analysis_state()
144
  if case.get("report_id"):
145
  state = state.model_copy(update={"report_id": case["report_id"]})
146