{"record":{"id":"c66650540e0590eb","repo":"mvanhorn/last30days-skill","slug":"the-label-file-is-bound-to-bundle-id-file-bundl","errorCode":null,"errorMessage":"The {label} file is bound to bundle_id {file_bundle_id!r} but the {noun} is {bundle.bundle_id!r}. {location_label}:\\n{_searched_lines(searched)}\\nCorrect the bundle_id field in your {label} file to {bundle.bundle_id!r} and re-run this same leg.","messagePattern":"The (.+?) file is bound to bundle_id (.+?) but the (.+?) is (.+?)\\. (.+?):\\\\n(.+?)\\\\nCorrect the bundle_id field in your (.+?) file to (.+?) and re-run this same leg\\.","errorType":"exception","errorClass":"HandoffContractError","httpStatus":null,"severity":"error","filePath":"skills/last30days/scripts/lib/discovery_handoff.py","lineNumber":667,"sourceCode":"    pending report on the finalize leg - so a host's retry is not misdirected\n    at the nominations bundle. A mismatch means the host echoed the wrong id\n    into an otherwise-current file, so the remedy is the cheap one - correct\n    the bundle_id field and re-run this same leg - never the expensive\n    re-sweep/resume remedies (those belong to missing/stale state).\"\"\"\n    file_bundle_id = str(payload.get(\"bundle_id\") or \"\")\n    if file_bundle_id == bundle.bundle_id:\n        return\n    if isinstance(bundle, PendingReport):\n        searched = _search_paths(save_dir, config_dir, pending_report_path)\n        noun = \"current pending discovery report\"\n        location_label = \"Pending-report locations searched\"\n    else:\n        searched = _search_paths(save_dir, config_dir, nominations_bundle_path)\n        noun = \"current nominations bundle\"\n        location_label = \"Bundle locations searched\"\n    if not searched and bundle.path is not None:\n        searched = [bundle.path]\n    raise HandoffContractError(\n        f\"The {label} file is bound to bundle_id {file_bundle_id!r} but the \"\n        f\"{noun} is {bundle.bundle_id!r}. {location_label}:\\n\"\n        f\"{_searched_lines(searched)}\\n\"\n        f\"Correct the bundle_id field in your {label} file to \"\n        f\"{bundle.bundle_id!r} and re-run this same leg.\"\n    )\n\n\ndef _truncate_at_word(text: str, max_chars: int) -> str:\n    \"\"\"Cap ``text`` at ``max_chars``, cutting back to a word boundary and\n    stripping trailing punctuation. Text within the cap passes through\n    untouched.\"\"\"\n    if len(text) <= max_chars:\n        return text\n    return text[:max_chars].rsplit(\" \", 1)[0].rstrip(_TRUNCATE_STRIP_CHARS)\n\n\ndef _sanitized_name(raw: object) -> str | None:","sourceCodeStart":649,"sourceCodeEnd":685,"githubUrl":"https://github.com/mvanhorn/last30days-skill/blob/c7460f6114449ddfe6ea3fc2f23c3d910c0e740c/skills/last30days/scripts/lib/discovery_handoff.py#L649-L685","documentation":"Raised by _require_bundle_binding when a host file (judgments/angles) carries a bundle_id that does not match the bundle_id of the nominations bundle or pending report the engine is currently operating on. The handoff protocol binds every host file to one bundle so stale judgments from a previous run are never silently applied to a new nomination set. The message includes every bundle/pending-report path searched so the operator can find the current ID.","triggerScenarios":"Re-running a discovery leg after the engine regenerated the nominations bundle (new bundle_id) while reusing a judgments/angles file written for the old run; hand-copying an example judgments file with a placeholder bundle_id; two concurrent runs sharing a save_dir with different bundles.","commonSituations":"Iterating on judgments across legs: the user edits the file, re-runs, and the engine has already produced a fresh pending report with a different ID; using a file from a different machine or save_dir; editing bundle_id by hand and introducing a typo or keeping quotes from the error message (the repr includes quotes — paste the inner value only).","solutions":["Read the current bundle_id from the bundle/pending-report path listed in the error's searched-locations lines and set the bundle_id field in your host file to exactly that value, then re-run the same leg.","If the stale file is from an abandoned run, regenerate the judgments/angles file against the current bundle instead of editing the old one.","If you expected the old bundle to still be current, check that you are running with the same --save-dir / config dir as the run that produced it."],"exampleFix":"// before (judgments.json)\n{\n  \"bundle_id\": \"3f0c9a...-old-run\",\n  \"judgments\": []\n}\n\n// after\n{\n  \"bundle_id\": \"<bundle_id printed by the error / found in the searched bundle path>\",\n  \"judgments\": []\n}","handlingStrategy":"validation","validationCode":"import json\n\ndef bundle_id_matches(path: str, bundle) -> bool:\n    payload = json.loads(open(path, encoding=\"utf-8\").read())\n    return payload.get(\"bundle_id\") == bundle.bundle_id","typeGuard":null,"tryCatchPattern":"try:\n    load_judgments(path, bundle, ...)\nexcept HandoffContractError as e:\n    # the message lists every searched bundle path; read the current id from there\n    current = extract_bundle_id_from_error(e)  # then rewrite the host file and re-run","preventionTips":["Never hand-copy bundle_id between runs; read it from the bundle/pending-report file the engine just wrote.","Treat a fresh nominations bundle as invalidating prior judgments/angles files.","Automate: after each leg, script the bundle_id from the bundle file into the host file template."],"tags":["contract","discovery-handoff","stale-state","validation"],"backgroundTag":null,"analyzedSha":"c7460f6114449ddfe6ea3fc2f23c3d910c0e740c","analyzedAt":"2026-08-15T03:34:49.540Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}