mvanhorn/last30days-skill · error · HandoffContractError

No discovery nominations bundle found. Searched:\n{_searched

Error message

No discovery nominations bundle found. Searched:\n{_searched_lines(searched)}\n{_RESWEEP_REMEDY}

What it means

`load_nominations_bundle` raises HandoffContractError (exit 2) when no `discover-nominations.json` exists in any searched location. Search is strict: the save dir when `--save-dir` was supplied, else the config dir — never a cross-store fallback. The message lists the exact paths searched and appends the re-sweep remedy ('Run a fresh `--discover --nominate-only` re-sweep').

Source

Thrown at skills/last30days/scripts/lib/discovery_handoff.py:374

    )


def read_nominations_bundle(
    *,
    save_dir: str | Path | None = None,
    config_dir: Path | None = None,
) -> NominationsBundle:
    """Locate and parse the nominations bundle for legs 2 and 3.

    The bundle lives in the save dir when one was supplied, else the config
    dir - never both (no cross-store fallback). Raises HandoffContractError
    (naming the searched location and the re-sweep remedy) when no bundle
    exists, and for any top-level contract violation in the file found.
    """
    searched = _search_paths(save_dir, config_dir, nominations_bundle_path)
    path = next((candidate for candidate in searched if candidate.exists()), None)
    if path is None:
        raise HandoffContractError(
            "No discovery nominations bundle found. Searched:\n"
            f"{_searched_lines(searched)}\n{_RESWEEP_REMEDY}"
        )
    return _parse_bundle_file(path)


def _parse_handoff_envelope(
    path: Path,
    *,
    label: str,
    kind: str,
    schema_version: str,
    remedy: str,
    missing_id_context: str,
    stale_context: str,
) -> tuple[dict[str, Any], str, Any]:
    """Shared strict top-level validation for the two engine-written handoff
    files (nominations bundle, pending report): readable, valid JSON object,

View on GitHub (pinned to c7460f6114)

Solutions

  1. Run leg 1 first: `--discover --nominate-only`, and use the SAME `--save-dir` (or same config dir) for every subsequent leg.
  2. Verify the file exists at the path printed in 'Searched:' — it should be `discover-nominations.json` inside that dir.
  3. If the bundle is genuinely absent (new machine, cleaned dir), re-run the nominate-only sweep to regenerate it, then re-author judgments against the new bundle_id.
  4. Do not hand-copy the bundle to another store hoping for fallback — resolution is save-dir-else-config-dir by design.

Example fix

# before
python3 last30days.py "topic" --discover --judgments judgments.json   # no leg 1 ever ran
# after
python3 last30days.py "topic" --discover --nominate-only --save-dir /tmp/run
python3 last30days.py "topic" --discover --judgments judgments.json --save-dir /tmp/run
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
from lib.discovery_handoff import nominations_bundle_path, handoff_state_dir

state = handoff_state_dir(save_dir, config_dir)
if state is None or not nominations_bundle_path(state).exists():
    raise SystemExit("run `--discover --nominate-only` first in this store")

Try / catch

from lib import discovery_handoff
try:
    bundle = discovery_handoff.load_nominations_bundle(save_dir=save_dir, config_dir=config_dir)
except discovery_handoff.HandoffContractError as exc:
    sys.stderr.write(exc.message + "\n")
    sys.exit(2)

Prevention

When it happens

Trigger: Invoking leg 2 (`--discover --judgments <file>`) before ever running leg 1; running leg 1 with `--save-dir A` and leg 2 with `--save-dir B` or no save dir; running leg 2 on a machine/container that never saw the bundle.

Common situations: Multi-step protocol split across sessions where leg 1 ran in a different directory or host; agents resuming a discovery run in a fresh sandbox; the user deleting/`clean`ing the save dir between legs.

Related errors


AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15). Data as JSON: /api/errors/6390a6cd4c3a3194. Report an issue: GitHub.