Rifqi Hafizuddin Claude Opus 5.5 commited on
Commit
863bf8e
·
1 Parent(s): 5595f0e

[NOTICKET] fix(planner): a failed plan is not a data gap (P0-9)

Browse files

TaskList._exhausted (private attr, not in the model's schema) marks a plan
that failed validation on every attempt; the coordinator shows its own text
without the data-gap lead. New EN/ID texts (register "Anda"): the plan failed
its checks, which does not mean the data is missing; ask more specifically.

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

src/agents/planner/schemas.py CHANGED
@@ -13,7 +13,7 @@ from __future__ import annotations
13
  import re
14
  from typing import Any, Literal
15
 
16
- from pydantic import BaseModel, Field
17
 
18
  from ..scope.models import ScopeChange
19
 
@@ -55,6 +55,11 @@ class Task(BaseModel):
55
 
56
 
57
  class TaskList(BaseModel):
 
 
 
 
 
58
  plan_id: str
59
  goal_restated: str
60
  assumptions: list[str] = Field(default_factory=list)
 
13
  import re
14
  from typing import Any, Literal
15
 
16
+ from pydantic import BaseModel, Field, PrivateAttr
17
 
18
  from ..scope.models import ScopeChange
19
 
 
55
 
56
 
57
  class TaskList(BaseModel):
58
+ # Set by PlannerService when every attempt failed validation (audit P0-9): the
59
+ # answer then says the PLAN failed, not that the data lacks the measure. Private,
60
+ # so it is not in the structured-output schema the model fills.
61
+ _exhausted: bool = PrivateAttr(default=False)
62
+
63
  plan_id: str
64
  goal_restated: str
65
  assumptions: list[str] = Field(default_factory=list)
src/agents/planner/service.py CHANGED
@@ -263,12 +263,14 @@ class PlannerService:
263
  attempts=self._max_retries,
264
  errors=previous_errors,
265
  )
266
- return TaskList(
267
  plan_id=context.project_id,
268
  goal_restated=query,
269
  tasks=[],
270
  infeasible_reason=_exhausted_reason(reply_language),
271
  )
 
 
272
 
273
 
274
  async def plan_analysis(
@@ -306,17 +308,21 @@ def _schema_error(exc: Exception) -> str:
306
  #: What the user sees when the planner could not build a valid plan. Deliberately
307
  #: says what happened in THEIR terms and does not quote the validator: they cannot
308
  #: act on "not a declared foreign key", and it is not their problem.
 
 
 
 
309
  _EXHAUSTED_EN = (
310
- "I could not work out a way to answer that from the data connected to this "
311
- "analysis. It may need a measure that is not in the catalog, or a link between "
312
- "tables that this source does not define. Try asking about one table at a time, "
313
- "or name the exact columns you mean."
314
  )
315
  _EXHAUSTED_ID = (
316
- "Saya belum menemukan cara menjawab pertanyaan itu dari data yang terhubung ke "
317
- "analisis ini. Kemungkinan ukurannya tidak ada di katalog, atau butuh kaitan "
318
- "antar tabel yang tidak didefinisikan di sumber ini. Coba tanyakan satu tabel "
319
- "saja, atau sebutkan kolom yang kamu maksud."
320
  )
321
 
322
 
 
263
  attempts=self._max_retries,
264
  errors=previous_errors,
265
  )
266
+ exhausted = TaskList(
267
  plan_id=context.project_id,
268
  goal_restated=query,
269
  tasks=[],
270
  infeasible_reason=_exhausted_reason(reply_language),
271
  )
272
+ exhausted._exhausted = True
273
+ return exhausted
274
 
275
 
276
  async def plan_analysis(
 
308
  #: What the user sees when the planner could not build a valid plan. Deliberately
309
  #: says what happened in THEIR terms and does not quote the validator: they cannot
310
  #: act on "not a declared foreign key", and it is not their problem.
311
+ # Audit P0-9 (2026-11-03): these used to blame the data ("the measure may not be in
312
+ # the catalog") and were shown under the data-gap lead, so a chart request right after
313
+ # the user SAW the data read as "the data cannot answer" twice. Exhaustion means the
314
+ # plan failed its own checks — say that. Indonesian register: "Anda" (decision D2).
315
  _EXHAUSTED_EN = (
316
+ "I couldn't put together a working analysis for that question — my plan kept "
317
+ "failing its own checks. That does not mean the data is missing. Please ask again "
318
+ "more specifically (name the measure, the period and the unit or site), or split "
319
+ "it into two questions."
320
  )
321
  _EXHAUSTED_ID = (
322
+ "Saya belum berhasil menyusun analisis untuk pertanyaan itu — setelah beberapa kali "
323
+ "mencoba, rencana analisisnya masih gagal pemeriksaan internal. Ini tidak berarti "
324
+ "datanya tidak ada. Silakan ajukan lagi dengan lebih spesifik (sebutkan ukuran, "
325
+ "periode, dan unit atau site-nya), atau pecah menjadi dua pertanyaan."
326
  )
327
 
328