{"record":{"id":"c5b07726cc291e9d","repo":"666ghj/MiroFish","slug":"reconstruction-stars-must-be-strictly-increasing","errorCode":null,"errorMessage":"reconstruction stars must be strictly increasing","messagePattern":"reconstruction stars must be strictly increasing","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":417,"sourceCode":"        if not isinstance(raw_point, dict):\n            raise StarHistoryError(\"reconstruction point must be an object\")\n        _expect_keys(raw_point, {\"date\", \"stars\"}, \"reconstruction point\")\n        raw_date = raw_point[\"date\"]\n        if not isinstance(raw_date, str):\n            raise StarHistoryError(\"reconstruction date must be a string\")\n        try:\n            point_day = date.fromisoformat(raw_date)\n        except ValueError as exc:\n            raise StarHistoryError(\"reconstruction date is invalid\") from exc\n        if point_day.isoformat() != raw_date:\n            raise StarHistoryError(\"reconstruction date is not canonical\")\n        stars = _strict_non_negative_int(raw_point[\"stars\"], \"reconstruction stars\")\n        if index == 0 and stars <= 0:\n            raise StarHistoryError(\"first reconstruction point must have stars\")\n        if previous_day is not None and point_day <= previous_day:\n            raise StarHistoryError(\"reconstruction dates must be strictly increasing\")\n        if index > 0 and stars <= previous_stars:\n            raise StarHistoryError(\"reconstruction stars must be strictly increasing\")\n        if point_day >= generated_at.date():\n            raise StarHistoryError(\"reconstruction must contain only completed UTC dates\")\n        previous_day = point_day\n        previous_stars = stars\n\n    snapshots = state[\"snapshots\"]\n    if not isinstance(snapshots, list):\n        raise StarHistoryError(\"snapshots must be a list\")\n    previous_snapshot: datetime | None = None\n    first_snapshot: datetime | None = None\n    for raw_snapshot in snapshots:\n        if not isinstance(raw_snapshot, dict):\n            raise StarHistoryError(\"snapshot must be an object\")\n        _expect_keys(raw_snapshot, {\"at\", \"stars\"}, \"snapshot\")\n        snapshot_at = _parse_state_timestamp(raw_snapshot[\"at\"], \"snapshot.at\")\n        _strict_non_negative_int(raw_snapshot[\"stars\"], \"snapshot stars\")\n        if previous_snapshot is not None and snapshot_at <= previous_snapshot:\n            raise StarHistoryError(\"snapshot timestamps must be strictly increasing\")","sourceCodeStart":399,"sourceCodeEnd":435,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L399-L435","documentation":"Raised by validate_state() when a daily point after the first has stars <= the previous point's stars. Daily star counts are cumulative totals, so they must strictly increase over time; equal or decreasing values indicate corrupted aggregation or unsorted data.","triggerScenarios":"validate_state(state) where daily[i]['stars'] <= daily[i-1]['stars'] for i > 0 — e.g. two consecutive days both at 100, or a decreasing value from misordered aggregation.","commonSituations":"Writing per-day deltas instead of cumulative totals; merging states and averaging/overwriting totals; GitHub pagination issues that dropped stars between fetches.","solutions":["Ensure each daily point stores the cumulative total star count at end of that UTC day, strictly greater than the prior day","Re-run the reconstruction step to rebuild daily from starred_at data","If deltas were written, convert them to running totals before validation"],"exampleFix":"// before\n\"daily\": [{\"date\": \"2024-01-01\", \"stars\": 100}, {\"date\": \"2024-01-02\", \"stars\": 5}]\n// after\n\"daily\": [{\"date\": \"2024-01-01\", \"stars\": 100}, {\"date\": \"2024-01-02\", \"stars\": 105}]","handlingStrategy":"validation","validationCode":"for i in range(1, len(daily)):\n    if daily[i][\"stars\"] <= daily[i - 1][\"stars\"]:\n        raise ValueError(f\"non-increasing stars at {daily[i]['date']}\")","typeGuard":"def stars_strictly_increasing(daily: list) -> bool:\n    return all(\n        daily[i][\"stars\"] > daily[i - 1][\"stars\"] for i in range(1, len(daily))\n    )","tryCatchPattern":"try:\n    validate_state(state)\nexcept StarHistoryError as exc:\n    if \"reconstruction stars must be strictly increasing\" in str(exc):\n        # rebuild daily as cumulative totals from deltas, then re-validate\n        ...","preventionTips":["Store cumulative totals, not per-day deltas, in daily","Validate monotonicity in the producer before persisting","Re-run reconstruction when GitHub pagination may have dropped data"],"tags":["validation","data-integrity","state-file"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}