{"record":{"id":"bc2c67780931575b","repo":"HKUDS/Vibe-Trading","slug":"unknown-criterion-id-criterion-id","errorCode":null,"errorMessage":"unknown criterion_id: {criterion_id}","messagePattern":"unknown criterion_id: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"agent/src/goal/store.py","lineNumber":884,"sourceCode":"    def _artifact_hash_matches(path: Path, expected_hash: str | None) -> bool:\n        if not expected_hash:\n            return False\n        try:\n            digest = hashlib.sha256(path.read_bytes()).hexdigest()\n        except OSError:\n            return False\n        return digest == expected_hash.lower().removeprefix(\"sha256:\")\n\n    def _require_criterion(self, goal_id: str, criterion_id: str) -> GoalCriterion:\n        row = self._conn.execute(\n            \"\"\"\n            SELECT * FROM goal_criteria\n            WHERE goal_id = ? AND criterion_id = ?\n            \"\"\",\n            (goal_id, criterion_id),\n        ).fetchone()\n        if row is None:\n            raise ValueError(f\"unknown criterion_id: {criterion_id}\")\n        return self._criterion_from_row(row)\n\n    def _require_claim(self, goal_id: str, claim_id: str) -> GoalClaim:\n        row = self._conn.execute(\n            \"\"\"\n            SELECT * FROM goal_claims\n            WHERE goal_id = ? AND claim_id = ?\n            \"\"\",\n            (goal_id, claim_id),\n        ).fetchone()\n        if row is None:\n            raise ValueError(f\"unknown claim_id: {claim_id}\")\n        return self._claim_from_row(row)\n\n    def _validate_completion_audit(\n        self,\n        goal: GoalRecord,\n        audit: list[AuditRow],","sourceCodeStart":866,"sourceCodeEnd":902,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/goal/store.py#L866-L902","documentation":"append_evidence referenced a criterion_id that has no row in goal_criteria for that goal. Criteria are declared when the goal is created; evidence must attach to one of them.","triggerScenarios":"Calling append_evidence with a criterion_id that is misspelled, belongs to another goal, or was removed/never added to this goal's criteria.","commonSituations":"Hardcoded criterion ids in tests; criteria template changed between goal creation and evidence append; id copy-paste errors.","solutions":["List the goal's criteria and use an existing criterion_id","If the criterion is legitimately new, recreate the goal with the updated criteria set","Add a pre-call validation step mapping evidence to declared criteria"],"exampleFix":"// before\nstore.append_evidence(..., criterion_id=\"wrong-id\")\n// after\ncriteria = {c.criterion_id for c in store.list_criteria(goal_id)}\nassert criterion_id in criteria, f\"unknown {criterion_id}; have {criteria}\"\nstore.append_evidence(..., criterion_id=criterion_id)","handlingStrategy":"validation","validationCode":"known = {c.criterion_id for c in store.list_criteria(goal_id)}\nif criterion_id not in known:\n    raise KeyError(f\"criterion {criterion_id} not in {sorted(known)}\")","typeGuard":"def criterion_exists(store, goal_id: str, criterion_id: str) -> bool:\n    return any(c.criterion_id == criterion_id for c in store.list_criteria(goal_id))","tryCatchPattern":"except ValueError as e: if str(e).startswith('unknown criterion_id'): list criteria and re-map or fail with context","preventionTips":["Derive criterion ids from the created goal's criteria, never hardcode","Regenerate goal fixtures when criteria templates change","Validate ids at the boundary between LLM output and store calls"],"tags":["goal-store","criterion","foreign-key","python"],"backgroundTag":"unknown-foreign-key-reference","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}