From 08d406ce7dfc23840b3d8d4b44fa5f6d14b3a298 Mon Sep 17 00:00:00 2001 From: Jens Date: Wed, 29 Jul 2026 17:14:17 +0200 Subject: [PATCH] Expand failed regional building contexts --- .../tests/test_belgium_training_portfolio.py | 16 ++++++++++ docs/BELGIUM_BUILDING_TRAINING_LOOP.md | 9 ++++++ docs/CODEX_EXECUTION_LOG.md | 5 +++ ...ion_belgium_building_training_portfolio.py | 32 +++++++++++++++++++ 4 files changed, 62 insertions(+) diff --git a/backend/tests/test_belgium_training_portfolio.py b/backend/tests/test_belgium_training_portfolio.py index 124a05b8..21b530e3 100644 --- a/backend/tests/test_belgium_training_portfolio.py +++ b/backend/tests/test_belgium_training_portfolio.py @@ -32,3 +32,19 @@ def test_portfolio_bbox_is_metric_sized() -> None: bounds = to_metric.transform_bounds(bbox["min_x"], bbox["min_y"], bbox["max_x"], bbox["max_y"]) assert 255 <= bounds[2] - bounds[0] <= 258 assert 255 <= bounds[3] - bounds[1] <= 258 + + +def test_failure_driven_expansion_is_train_only_and_context_complete() -> None: + additions = [aoi for aoi in module.AOIS if aoi.slug.endswith("-v31") or "-v31-bg" in aoi.slug] + assert len(additions) == 18 + assert {aoi.split for aoi in additions} == {"train"} + assert {aoi.region for aoi in additions} == {"flanders", "wallonia"} + contexts = {(aoi.region, aoi.context) for aoi in additions} + assert { + ("flanders", "industrial"), + ("flanders", "ribbon-development"), + ("flanders", "coastal-urban"), + ("wallonia", "dense-urban"), + ("wallonia", "rural-town"), + ("wallonia", "regional-architecture"), + } <= contexts diff --git a/docs/BELGIUM_BUILDING_TRAINING_LOOP.md b/docs/BELGIUM_BUILDING_TRAINING_LOOP.md index 4ce203a5..b83891b9 100644 --- a/docs/BELGIUM_BUILDING_TRAINING_LOOP.md +++ b/docs/BELGIUM_BUILDING_TRAINING_LOOP.md @@ -67,6 +67,15 @@ contexts. Its remaining known gaps are: The active production model remains unchanged while any gate fails. +The v31 expansion adds eighteen independent train-only AOIs after the v30 +rotated-holdout calibration isolated the remaining domain gaps. Nine Flemish +AOIs cover industrial roofs, ribbon development and coastal urban fabric. Nine +Walloon AOIs add dense urban, rural-town, regional-architecture and difficult +industrial/rail/quarry negatives. Existing validation, calibration, test and +background-test AOIs remain frozen. The provisioner can load a short-lived +operator session from a mode-0600 token file, allowing governed acquisition on +an authenticated runtime without exposing credentials in process arguments. + ## Reproducible evidence - corpus assembler: `scripts/assemble_belgium_building_corpus.py`; diff --git a/docs/CODEX_EXECUTION_LOG.md b/docs/CODEX_EXECUTION_LOG.md index eff91821..d2bb824f 100644 --- a/docs/CODEX_EXECUTION_LOG.md +++ b/docs/CODEX_EXECUTION_LOG.md @@ -6,6 +6,11 @@ per-AOI calibration evidence. Protected calibration, test, background-test and validation tiles remain excluded; manifests now record targeted contexts and their stronger repeat factors for reproducible follow-up iterations. +- Prepared the next immutable corpus expansion with eighteen spatially + independent train-only AOIs: nine Flemish industrial/ribbon/coastal samples + and nine Walloon dense/rural/architecture or difficult-negative samples. + Added mode-0600 session-token-file support to the official provisioner so + authenticated Tower acquisition does not expose session material in argv. ## 2026-07-27 - Guest demo and product professionalization diff --git a/scripts/provision_belgium_building_training_portfolio.py b/scripts/provision_belgium_building_training_portfolio.py index 6aa88fdc..f2b35955 100644 --- a/scripts/provision_belgium_building_training_portfolio.py +++ b/scripts/provision_belgium_building_training_portfolio.py @@ -81,6 +81,17 @@ AOIS = ( Aoi("brasschaat-heath-train-v12-bg", "flanders", "heath-hard-negative", "train", 4.525, 51.350, "background_candidate"), Aoi("limburg-pine-train-v12-bg", "flanders", "forest-hard-negative", "train", 5.680, 51.020, "background_candidate"), Aoi("westhoek-field-train-v12-bg", "flanders", "farmland-hard-negative", "train", 2.720, 50.990, "background_candidate"), + # v31 failure-driven corpus expansion: independent examples matching the + # protected Genk/Lokeren/Ostend calibration failure contexts. + Aoi("tessenderlo-industry-train-v31", "flanders", "industrial", "train", 5.087, 51.065), + Aoi("maasmechelen-industry-train-v31", "flanders", "industrial", "train", 5.691, 50.963), + Aoi("izegem-industry-train-v31", "flanders", "industrial", "train", 3.209, 50.920), + Aoi("zele-ribbon-train-v31", "flanders", "ribbon-development", "train", 4.040, 51.066), + Aoi("putte-ribbon-train-v31", "flanders", "ribbon-development", "train", 4.632, 51.053), + Aoi("overpelt-ribbon-train-v31", "flanders", "ribbon-development", "train", 5.424, 51.211), + Aoi("nieuwpoort-coastal-train-v31", "flanders", "coastal-urban", "train", 2.749, 51.130), + Aoi("knokke-coastal-train-v31", "flanders", "coastal-urban", "train", 3.289, 51.340), + Aoi("bredene-coastal-train-v31", "flanders", "coastal-urban", "train", 2.976, 51.235), Aoi("bruges-val", "flanders", "historic-urban", "val", 3.224, 51.209), Aoi("turnhout-val", "flanders", "suburban", "val", 4.944, 51.322), Aoi("hasselt-cal", "flanders", "suburban", "calibration", 5.340, 50.930), @@ -119,6 +130,17 @@ AOIS = ( Aoi("peruwelz-train-v12", "wallonia", "mixed-urban", "train", 3.591, 50.509), Aoi("ardenne-clearing-train-v12-bg", "wallonia", "forest-hard-negative", "train", 5.460, 50.080, "background_candidate"), Aoi("condroz-open-train-v12-bg", "wallonia", "farmland-hard-negative", "train", 5.020, 50.390, "background_candidate"), + # v31 precision correction: independent positives plus difficult negatives + # for the weak Walloon dense/rural/regional-architecture contexts. + Aoi("herstal-dense-train-v31", "wallonia", "dense-urban", "train", 5.630, 50.665), + Aoi("dison-dense-train-v31", "wallonia", "dense-urban", "train", 5.854, 50.612), + Aoi("gembloux-rural-train-v31", "wallonia", "rural-town", "train", 4.692, 50.561), + Aoi("soignies-rural-train-v31", "wallonia", "rural-town", "train", 4.071, 50.579), + Aoi("stavelot-architecture-train-v31", "wallonia", "regional-architecture", "train", 5.931, 50.395), + Aoi("aubel-architecture-train-v31", "wallonia", "regional-architecture", "train", 5.859, 50.704), + Aoi("liege-brownfield-train-v31-bg", "wallonia", "industrial-hard-negative", "train", 5.607, 50.624, "background_candidate"), + Aoi("charleroi-rail-train-v31-bg", "wallonia", "rail-hard-negative", "train", 4.467, 50.421, "background_candidate"), + Aoi("condroz-quarry-train-v31-bg", "wallonia", "quarry-hard-negative", "train", 5.190, 50.365, "background_candidate"), Aoi("tournai-val", "wallonia", "historic-urban", "val", 3.389, 50.606), Aoi("arlon-val", "wallonia", "small-city", "val", 5.817, 49.683), Aoi("verviers-cal", "wallonia", "suburban", "calibration", 5.860, 50.590), @@ -212,8 +234,18 @@ def main() -> int: parser.add_argument("--side-m", type=float, default=256.0) parser.add_argument("--resolution-m", type=float, default=0.25) parser.add_argument("--force-refresh", action="store_true") + parser.add_argument( + "--session-token-file", + type=Path, + help="Read an operator session token from a local mode-0600 file instead of exposing it on the command line.", + ) args = parser.parse_args() session = requests.Session() + if args.session_token_file: + token = args.session_token_file.read_text(encoding="utf-8").strip() + if not token: + raise SystemExit("--session-token-file is empty") + session.cookies.set("geointel_session", token) samples: list[dict[str, Any]] = [] if args.output_spec.is_file(): existing = json.loads(args.output_spec.read_text(encoding="utf-8-sig"))