{"record":{"id":"fad9861c3d958d80","repo":"666ghj/MiroFish","slug":"relative-is-not-synchronized-with-history-json","errorCode":null,"errorMessage":"{relative} is not synchronized with history.json","messagePattern":"(.+?) is not synchronized with history\\.json","errorType":"exception","errorClass":"StarHistoryError","httpStatus":null,"severity":"error","filePath":"scripts/star_history.py","lineNumber":1357,"sourceCode":"        for temporary in temporary_paths.values():\n            try:\n                temporary.unlink(missing_ok=True)\n            except OSError:\n                pass\n\n    check_workspace(workspace)\n    return True\n\n\ndef check_workspace(workspace: Path) -> None:\n    state = load_state(workspace, require_canonical=True)\n    expected = _output_payloads(state)\n    for relative in OUTPUT_RELATIVES[1:]:\n        target = _safe_target(workspace, relative, create_parent=False)\n        actual = _read_limited(target, MAX_STATE_BYTES, str(relative))\n        _validate_svg(actual)\n        if actual != expected[relative]:\n            raise StarHistoryError(f\"{relative} is not synchronized with history.json\")\n\n\ndef execute(\n    command: str,\n    *,\n    github: GitHubGateway | None,\n    clock: Clock,\n    workspace: Path,\n    force: bool = False,\n    star_count: int | None = None,\n) -> Result:\n    root = _safe_workspace(workspace)\n    now = _normalize_now(clock.now())\n\n    if command == \"backfill\":\n        state_target = _safe_target(root, STATE_RELATIVE, create_parent=False)\n        if state_target.exists():\n            raise StarHistoryError(\"history state already exists; refusing to overwrite backfill\")","sourceCodeStart":1339,"sourceCodeEnd":1375,"githubUrl":"https://github.com/666ghj/MiroFish/blob/b5b53acc57189a4a42e44a23e149dc655c98fe82/scripts/star_history.py#L1339-L1375","documentation":"Raised by check_workspace (called at the end of _write_outputs and usable standalone): it reloads history.json canonically, regenerates the expected light/dark SVGs via _output_payloads, and byte-compares them against the files on disk. Any byte difference means the committed SVGs are not exactly what the current state would render — a drift guard that keeps history.json and the two SVGs in lockstep as a single atomic unit.","triggerScenarios":"Hand-editing static/image/star-history-light.svg or -dark.svg (tweaking colors, removing the watermark) without touching history.json; committing an SVG from an older version of the generator after the template changed; a partial/interrupted older write that left new SVGs with old state (or vice versa); reformatting line endings or trailing newlines.","commonSituations":"A designer 'fixes' the SVG directly in an editor; a merge conflict resolves history.json from one branch and the SVGs from another; CI regenerates with a different Python/dependency version producing different float formatting; lint/formatter tools normalizing SVG whitespace.","solutions":["Never edit the SVGs by hand — regenerate both via the script so all three files update together (e.g. re-run the update/refresh command that calls _write_outputs).","If state is correct and the SVGs are stale, re-run the generator; it rewrites the SVGs to match history.json byte-for-byte.","If a template change altered rendering, commit the regenerated SVGs in the same commit as the template/validator change.","Check for tooling that rewrites the files (prettier, editors stripping trailing newlines, CRLF conversion on Windows checkouts) and exclude these generated files from it (.gitattributes: *.svg -text or export-ignore formatters)."],"exampleFix":"# before: manual SVG edit drifted from state\n$ nano static/image/star-history-light.svg   # tweak color\n$ git commit -am 'tweak'\n# -> '{relative} is not synchronized with history.json' on next check\n\n# after: change the template, then regenerate all outputs atomically\n$ edit scripts/star_history.py   # template + allowlist\n$ python scripts/star_history.py <update-command>   # rewrites all three files","handlingStrategy":"validation","validationCode":"def outputs_in_sync(workspace: Path) -> bool:\n    try:\n        check_workspace(workspace)\n        return True\n    except StarHistoryError:\n        return False\n\n# cheap preflight in CI before any commit that touches these files\nassert outputs_in_sync(root), \"SVGs drifted from history.json; regenerate before committing\"","typeGuard":null,"tryCatchPattern":"try:\n    check_workspace(workspace)\nexcept StarHistoryError as exc:\n    if \"not synchronized\" in str(exc):\n        # regenerate all three outputs via the script (never hand-patch one file)\n        # then commit them together\n        ...","preventionTips":["Never hand-edit the generated SVGs; change the template and regenerate.","Commit history.json and both SVGs in the same commit (they are one atomic unit).","Exclude generated SVGs from formatters and disable line-ending conversion for them (.gitattributes).","Run check_workspace as a CI gate on every PR touching scripts/star_history.py or the outputs."],"tags":["consistency","generated-files","synchronization","ci"],"backgroundTag":null,"analyzedSha":"b5b53acc57189a4a42e44a23e149dc655c98fe82","analyzedAt":"2026-08-14T22:29:33.146Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}