mvanhorn/last30days-skill · error · HandoffContractError

Judgments file {path} must carry a top-level \"judgments\" l

Error message

Judgments file {path} must carry a top-level \"judgments\" list.

What it means

Raised by the judgments loader in discovery_handoff.py when the file is valid JSON, is a top-level object, and is correctly bundle-bound, but the 'judgments' key is missing or is not a JSON list. This is the third strictness tier: strict on top-level structure, lenient per row (unknown ids are warned, junk fields degrade per-row). It is a HandoffContractError, so the fix is always on the host-file side.

Source

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

    save_dir: str | Path | None = None,
    config_dir: Path | None = None,
) -> dict[str, HostJudgment]:
    """Read the host judgments file for leg 2, keyed by nomination id.

    Strict at the top level (readable, valid JSON object, ``judgments`` list,
    bundle_id bound to ``bundle``), lenient per row: an unknown id is warned
    and ignored, a missing/unusable name or junk field is per-row-absent, and
    worthiness is clamped to 0-100 integers. Nominations with no row at all
    are simply missing from the mapping - use ``judgment_for`` to get the
    ROW_ABSENT marker for them.
    """
    payload = _load_host_file(path, "judgments")
    _require_bundle_binding(
        payload, bundle, label="judgments", save_dir=save_dir, config_dir=config_dir,
    )
    rows = payload.get("judgments")
    if not isinstance(rows, list):
        raise HandoffContractError(
            f"Judgments file {path} must carry a top-level \"judgments\" list."
        )
    known = {entry.nomination_id for entry in bundle.nominations}
    judgments: dict[str, HostJudgment] = {}
    for row_id, row in _known_rows(
        rows, known, row_label="judgments", unknown_label="judgment"
    ):
        # Only a real JSON boolean is a junk verdict: null, "false", 0, or
        # any other non-bool value is per-row-absent (bundle heuristic),
        # never coerced - bool("false") is True.
        raw_junk = row.get("junk")
        judgments[row_id] = HostJudgment(
            name=_sanitized_name(row.get("name")),
            junk=raw_junk if isinstance(raw_junk, bool) else None,
            worthiness=_clamped_worthiness(row.get("worthiness")),
        )
    return judgments

View on GitHub (pinned to c7460f6114)

Solutions

  1. Ensure the top-level object contains "judgments": [...] — a JSON array of row objects, each with nomination_id.
  2. Check spelling and case exactly: the key must be lowercase 'judgments'.
  3. After fixing, re-run the same leg; per-row problems will not raise this error, only the missing/mistyped key does.

Example fix

// before
{
  "bundle_id": "...",
  "rows": [{"nomination_id": "n1", "junk": false}]
}

// after
{
  "bundle_id": "...",
  "judgments": [{"nomination_id": "n1", "junk": false}]
}
Defensive patterns

Strategy: validation

Validate before calling

import json

def valid_judgments_file(path: str) -> bool:
    payload = json.loads(open(path, encoding="utf-8").read())
    return isinstance(payload, dict) and isinstance(payload.get("judgments"), list)

Try / catch

try:
    load_judgments(path, bundle, ...)
except HandoffContractError as e:
    if 'top-level' in str(e) and 'judgments' in str(e):
        # structural fix on host side: add/rename the "judgments" array key
        ...

Prevention

When it happens

Trigger: Passing a judgments file whose top-level object omits the 'judgments' key entirely; naming the key differently ('rows', 'entries', 'items'); setting "judgments": {...} (an object) instead of an array; setting it to null.

Common situations: The host model paraphrases the schema and renames the key; a partially written file from an interrupted edit; copy-pasting an angles-file shape for a judgments file.

Related errors


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