diff --git a/internal/push/testdata/parity/cases.json b/internal/push/testdata/parity/cases.json index a1ec5151..60540f02 100644 --- a/internal/push/testdata/parity/cases.json +++ b/internal/push/testdata/parity/cases.json @@ -584,8 +584,8 @@ "category": "semantic_segmentation", "label_column": "label", "cli_verdict": "reject", - "ingestor_verdict": "accept", - "note": "DELIBERATE divergence AT THIS PIN (7b4ecac, the di#359 merge): the manifest has no mask_id column. The CLI previews the backend#816 contract (CheckMaskIdColumn) \u2014 without mask_id the training client can't resolve masks and fails late. The PINNED ingestor predates di#358's MaskIdColumnValidator, so its preflight accepts. Flip ingestor_verdict to reject when the pin bumps past di#358 (cli#286 pin-bump); the goldens regen will flag exactly this case." + "ingestor_verdict": "reject", + "note": "The manifest has no mask_id column. The CLI previews the backend#816 contract (CheckMaskIdColumn) and rejects; the pinned ingestor now includes di#358's MaskIdColumnValidator (adopted via the cli#286 pin-bump), so its preflight also rejects an undeclared mask_id. Both sides agree \u2014 reject." }, { "extension": ".jpg", diff --git a/internal/push/testdata/parity/goldens.json b/internal/push/testdata/parity/goldens.json index a61871e8..0446d06b 100644 --- a/internal/push/testdata/parity/goldens.json +++ b/internal/push/testdata/parity/goldens.json @@ -223,8 +223,10 @@ "verdict": "reject" }, "semseg-missing-mask-id": { - "errors": [], - "verdict": "accept" + "errors": [ + "MaskIdColumnValidator: Required column 'mask_id' not found in semantic-segmentation manifest CSV header (columns: filename). The training client reads 'mask_id' to locate each mask file (backend#816) \u2014 without it, every mask lookup raises FileNotFoundError at train time. Add a 'mask_id' column mapping each row to its mask filename. See templates/semantic_segmentation/ for the layout." + ], + "verdict": "reject" }, "semseg-ok": { "errors": [], diff --git a/scripts/gen-validator-goldens.py b/scripts/gen-validator-goldens.py index 02ef6893..01a0f01a 100644 --- a/scripts/gen-validator-goldens.py +++ b/scripts/gen-validator-goldens.py @@ -152,6 +152,19 @@ def run_case(case): schema = {} options["schema"] = schema options["full_schema"] = schema + elif case["category"] == "semantic_segmentation": + # semseg declares the masks sidecar's mask_id link column in the + # schema it sends to the ingestor (spec.go: buildSpec sets + # schema={"mask_id": "VARCHAR(255)"}). data-ingestors #358's + # MaskIdColumnValidator REQUIRES that declaration — an undeclared + # mask_id is dropped at ingest, so the stored table lacks it and the + # training client raises FileNotFoundError. The harness must drive + # the validators with the SAME schema the CLI sends, or every semseg + # case is spuriously rejected on the mask_id contract. An explicit + # per-case schema still wins (e.g. a case that deliberately omits + # mask_id to exercise the reject path). + options["schema"] = case.get("schema", {"mask_id": "VARCHAR(255)"}) + options["full_schema"] = options["schema"] errors = [] try: