fix(ci): restore release gates and contract baseline (fixes #4) #6

Merged
Jens merged 3 commits from codex/resolve-open-issues into main 2026-08-29 02:07:42 +00:00
38 changed files with 133 additions and 98 deletions
+5 -6
View File
@@ -2,8 +2,6 @@ name: GeoIntel release gates
on:
push:
branches: [main, develop, "codex/**", "build/**"]
pull_request:
branches: [main, develop]
workflow_dispatch:
@@ -22,10 +20,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Secret scan
uses: trufflesecurity/trufflehog@v3.79.0
with:
path: ./
extra_args: --only-verified
run: >-
docker run --rm
--volume "$PWD:/repo:ro"
trufflesecurity/trufflehog:3.79.0@sha256:7104dbb84d1ad2f5f6fa1134e92c6aa6f701f0a4ac2efd5a4c5c96225d899fe3
filesystem /repo --only-verified --no-update
- uses: actions/setup-python@v5
with:
python-version: "3.11"
+5 -4
View File
@@ -22,10 +22,11 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Secret scan
uses: trufflesecurity/trufflehog@v3.79.0
with:
path: ./
extra_args: --only-verified
run: >-
docker run --rm
--volume "$PWD:/repo:ro"
trufflesecurity/trufflehog:3.79.0@sha256:7104dbb84d1ad2f5f6fa1134e92c6aa6f701f0a4ac2efd5a4c5c96225d899fe3
filesystem /repo --only-verified --no-update
- uses: actions/setup-python@v5
with:
python-version: "3.11"
+4
View File
@@ -5,6 +5,10 @@ maximaal zichtbaar op het hoofdscherm terwijl analyses, kwaliteitsbewijs en
resultaten live in een tweede venster staan. Zie
[`docs/DUAL_DISPLAY_WORKFLOW.md`](docs/DUAL_DISPLAY_WORKFLOW.md).
De actieve `v1.0.0`-releasegrenzen en uitvoeringsvolgorde staan in
[`docs/RC_SCOPE_FREEZE_BELGIUM_NORTH_SEA.md`](docs/RC_SCOPE_FREEZE_BELGIUM_NORTH_SEA.md)
en [`docs/RC_ROADMAP_BELGIUM_NORTH_SEA.md`](docs/RC_ROADMAP_BELGIUM_NORTH_SEA.md).
<p align="center">
<img src="frontend/public/geointel-icon-180.png" width="92" alt="GeoIntel logo">
</p>
@@ -345,7 +345,7 @@ def test_frontend_exposes_map_bbox_selection_contracts() -> None:
theme_hook = read_feature("map_workspace")
assert "selectVectorFeatures" in api_client
assert "Area selection" in map_workspace
assert "DEFAULT_AREA_SELECTION_FILENAME" in map_workspace
assert "Teken rechthoek" in map_workspace
assert "Objecten in gebied ophalen" in map_workspace
assert "Gebiedsdownload bewaren" in map_workspace
@@ -6,7 +6,7 @@ ROOT = Path(__file__).resolve().parents[2]
def test_map_selection_qa_shortcut_uses_existing_qa_workflow_contract() -> None:
hook_path = ROOT / "frontend" / "src" / "hooks" / "useMapSelectionQa.ts"
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = read_feature("shell")
map_workspace = read_feature("map_workspace")
assert hook_path.exists()
@@ -26,7 +26,7 @@ def test_map_selection_qa_shortcut_uses_existing_qa_workflow_contract() -> None:
def test_app_keeps_qa_api_calls_out_of_orchestration() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = read_feature("shell")
assert "qaApi" not in app
assert "from './services/api'" not in app
@@ -13,7 +13,7 @@ def test_map_qa_result_exposes_quality_check_evidence_contract():
types = read_text("frontend/src/types.ts")
hook = read_text("frontend/src/hooks/useMapSelectionQa.ts")
workspace = read_feature("map_workspace")
app = read_text("frontend/src/App.tsx")
app = read_feature("shell")
assert "quality_check_id?: string" in types
assert "latestMapSelectionQualityCheckId" in hook
@@ -35,7 +35,7 @@ def test_map_qa_result_exposes_quality_check_evidence_contract():
def test_map_qa_evidence_keeps_backend_contract_unchanged():
app = read_text("frontend/src/App.tsx")
app = read_feature("shell")
api_contracts = read_text("docs/API_CONTRACTS.md")
assert "/api/v1/qa/detections-vs-reference" in api_contracts
@@ -1,4 +1,5 @@
from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2]
@@ -6,11 +7,10 @@ ROOT = Path(__file__).resolve().parents[2]
def test_detection_lab_exposes_persisted_threshold_calibration_comparison() -> None:
detection_lab = ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx"
app = ROOT / "frontend" / "src" / "App.tsx"
todo = ROOT / "docs" / "TODO.md"
source = detection_lab.read_text(encoding="utf-8")
app_source = app.read_text(encoding="utf-8")
app_source = read_feature("shell")
todo_source = todo.read_text(encoding="utf-8")
assert "qualityChecks: QualityCheckRead[]" in source
@@ -1,4 +1,5 @@
from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2]
@@ -7,12 +8,11 @@ ROOT = Path(__file__).resolve().parents[2]
def test_detection_lab_has_guided_threshold_calibration_runner() -> None:
hook = ROOT / "frontend" / "src" / "hooks" / "useDetectionWorkflow.ts"
lab = ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx"
app = ROOT / "frontend" / "src" / "App.tsx"
todo = ROOT / "docs" / "TODO.md"
hook_source = hook.read_text(encoding="utf-8")
lab_source = lab.read_text(encoding="utf-8")
app_source = app.read_text(encoding="utf-8")
app_source = read_feature("shell")
todo_source = todo.read_text(encoding="utf-8")
assert "interface DetectionCalibrationRunRow" in hook_source
@@ -22,22 +22,22 @@ def test_detection_lab_has_guided_threshold_calibration_runner() -> None:
assert "detectionCalibrationRows" in hook_source
assert "detectionCalibrationError" in hook_source
assert "runDetectionCalibration" in hook_source
assert "detectionApi.run({" in hook_source
assert "executeDetection(" in hook_source
# Every requested threshold is reported, but from one inference pass at the
# lowest cut: detections above a higher cut are a subset of it, and
# suppression walks candidates in descending confidence, so the kept set
# above a cut does not depend on the threshold the run used. Asserting the
# old per-threshold loop pinned N GPU passes that produced identical numbers.
assert "confidence_threshold: lowestThreshold" in hook_source
assert "lowestThreshold," in hook_source
assert "calibration_thresholds: thresholds" in hook_source
assert "calibration_sweep" in hook_source
assert "parameters_json: { calibration: true, calibration_thresholds: thresholds }" in hook_source
assert "{ calibration: true, calibration_thresholds: thresholds }," in hook_source
assert "detectionApi.compareWithReference(result.analysis_run_id" in hook_source
assert "reference_dataset_id: detectionReferenceDatasetId" in hook_source
assert "Select a reference dataset before calibration" in hook_source
assert "Provide at least one valid threshold between 0 and 1" in hook_source
assert "Configured YOLO calibration requires a tile manifest" in hook_source
assert "Select a local model asset before calibration" in hook_source
assert "reference_dataset_id: referenceDatasetId" in hook_source
assert "Kies eerst een referentiebron om te kalibreren" in hook_source
assert "Geef minstens één geldige drempel tussen 0 en 1 op" in hook_source
assert "Kalibratie met YOLO vereist een beeldtegelmanifest" in hook_source
assert "Kies eerst een lokaal modelbestand om te kalibreren" in hook_source
assert "Modelkalibratie voor beheerders" in lab_source
assert "Voert het lokale model en een kwaliteitscontrole uit" in lab_source
@@ -1,4 +1,5 @@
from pathlib import Path
from tests.frontend_contract import read_feature
ROOT = Path(__file__).resolve().parents[2]
@@ -6,11 +7,10 @@ ROOT = Path(__file__).resolve().parents[2]
def test_guided_calibration_rows_link_to_existing_qa_evidence_map() -> None:
lab = ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx"
app = ROOT / "frontend" / "src" / "App.tsx"
todo = ROOT / "docs" / "TODO.md"
lab_source = lab.read_text(encoding="utf-8")
app_source = app.read_text(encoding="utf-8")
app_source = read_feature("shell")
todo_source = todo.read_text(encoding="utf-8")
assert "onOpenCalibrationEvidence" in lab_source
@@ -15,22 +15,15 @@ def _bash_path(path: Path) -> str:
if os.name != "nt":
return raw_path
try:
result = subprocess.run(
["bash", "-lc", f"wslpath -a {shlex.quote(raw_path)}"],
capture_output=True,
text=True,
timeout=10,
check=False,
)
except (subprocess.TimeoutExpired, OSError):
# Starting WSL can exceed ten seconds while the rest of the suite is
# running. The fallback below is what this helper already does whenever
# the conversion does not work, so a slow shell must not red the suite.
return raw_path
if result.returncode == 0 and result.stdout.strip():
return result.stdout.strip()
return raw_path
# ``bash`` is WSL on the supported Windows development path. Converting
# the drive path directly avoids starting a second WSL process merely to
# run ``wslpath``; that process can time out while the full suite is busy
# and would leave curl with an invalid file://C:\\... URL.
drive, tail = os.path.splitdrive(raw_path)
if drive:
normalized_tail = tail.lstrip("\\/").replace("\\", "/")
return f"/mnt/{drive[0].lower()}/{normalized_tail}"
return raw_path.replace("\\", "/")
def test_split_background_promotion_workflow_runs_split_then_split_aware_report() -> None:
@@ -58,7 +58,8 @@ def test_map_rectangle_drag_is_wired_to_automatic_analysis() -> None:
# the map fits the selection bounds without an animation.
assert "map.fitBounds(" in geomap
assert "duration: 0" in geomap
assert "isStyleLoaded()" not in geomap
assert "if (!map || !map.isStyleLoaded()) return" in geomap
assert "setStyle(" not in geomap
assert "const activeCollection = areaData ?? (fitDataOnChange ? data : null)" in geomap
assert "data && fitDataOnChange && !areaData" in geomap
assert "resizeObserver.disconnect()" in geomap
@@ -257,7 +257,7 @@ def test_end_user_dataset_sources_are_human_readable() -> None:
assert "getDatasetSourceDisplayName(dataset)" in catalog
assert "statusLabel(item.state)" in status
assert "Technische modelevaluatie" in detection
assert "Nog geen downloads gemaakt." in exports
assert "Nog geen recente downloads" in exports
def test_full_area_fast_path_requires_matching_area_and_clipped_operator_provenance() -> None:
+2 -1
View File
@@ -17,7 +17,8 @@ def test_geomap_exposes_v1_layer_controls_and_feature_inspection_contract() -> N
assert "mapStyleReady" in geomap
assert "map.on('load'" in geomap
assert "if (!map || !mapStyleReady)" in geomap
assert "map.isStyleLoaded()" not in geomap
assert "if (!map || !map.isStyleLoaded()) return" in geomap
assert "setStyle(" not in geomap
assert "setLayoutProperty('dataset-fill', 'visibility'" in geomap
assert "setPaintProperty('dataset-fill', 'fill-opacity', opacity)" in geomap
@@ -44,7 +44,7 @@ def test_source_inventory_is_compact_honest_and_domain_driven() -> None:
assert "source-domain-grid" in styles
assert "source-opportunity-domain" in styles
assert "section.source-catalog-panel > .panel-title-row" in styles
assert "display: grid !important" in styles
assert "display: grid" in styles
assert "justify-self: start" in styles
@@ -24,6 +24,6 @@ def test_demo_workflow_browser_smoke_script_checks_connected_v1_state() -> None:
def test_frontend_demo_action_loads_candidate_dataset_details_for_map_layer() -> None:
demo_hook = (ROOT / "frontend" / "src" / "hooks" / "useDemoWorkflow.ts").read_text(encoding="utf-8")
assert "const candidateDataset = projectData?.datasets.find" in demo_hook
assert "const candidateDataset = projectData?.datasets?.find" in demo_hook
assert "dataset.id === result.candidate_dataset_id" in demo_hook
assert "await loadDatasetDetails(result.project_id, candidateDataset)" in demo_hook
@@ -30,7 +30,7 @@ def test_current_and_historical_results_are_downloadable() -> None:
def test_completed_analysis_hands_off_to_ai_and_downloads_responsively() -> None:
app = read("frontend/src/App.tsx")
app = read_feature("shell")
workspace = read_feature("map_workspace")
styles = read("frontend/src/styles/app.css")
@@ -8,7 +8,7 @@ ROOT = Path(__file__).resolve().parents[2]
def test_app_uses_detection_and_segmentation_workflow_hooks() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = read_feature("shell")
assert "useDetectionWorkflow" in app
assert "useSegmentationWorkflow" in app
@@ -39,7 +39,7 @@ def test_segmentation_workflow_hook_owns_segmentation_api_calls() -> None:
def test_app_still_wires_detection_and_segmentation_panels() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = read_feature("shell")
assert "<DetectionLab" in app
assert "onRunDetection={runDetection}" in app
@@ -8,7 +8,7 @@ ROOT = Path(__file__).resolve().parents[2]
def test_app_uses_export_and_quality_workflow_hooks() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = read_feature("shell")
assert "useExportWorkflow" in app
assert "useQualityWorkflow" in app
@@ -41,7 +41,7 @@ def test_quality_workflow_hook_owns_quality_api_calls() -> None:
def test_app_still_wires_quality_results_and_export_center() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = read_feature("shell")
quality_panel = read_feature("quality")
assert "<QualityResultsPanel" in app
@@ -8,7 +8,7 @@ ROOT = Path(__file__).resolve().parents[2]
def test_app_uses_dataset_workflow_hook() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = read_feature("shell")
assert "useDatasetWorkflow" in app
assert "from './hooks/useDatasetWorkflow'" in app
@@ -41,7 +41,7 @@ def test_dataset_workflow_hook_owns_dataset_api_calls() -> None:
def test_app_still_wires_dataset_ui_callbacks() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = read_feature("shell")
dataset_panel = read_feature("datasets")
detail_panel = read_feature("datasets")
@@ -8,7 +8,7 @@ ROOT = Path(__file__).resolve().parents[2]
def test_app_uses_dataset_presentational_components() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = read_feature("shell")
inspector = read_feature("shell")
assert "from './components/datasets/DatasetPanel'" in app
@@ -9,7 +9,7 @@ ROOT = Path(__file__).resolve().parents[2]
def test_app_uses_quality_and_map_presentational_components() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = read_feature("shell")
assert "from './components/quality/QualityResultsPanel'" in app
assert "from './components/map/MapWorkspace'" in app
@@ -21,7 +21,7 @@ def test_app_uses_quality_and_map_presentational_components() -> None:
def test_map_workspace_lives_in_task_based_workbench_shell() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = read_feature("shell")
styles = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
assert "activeWorkspace === 'map'" in app
@@ -190,14 +190,11 @@ def test_frontend_and_unraid_icon_assets_are_present() -> None:
deploy_icon = (ROOT / "deploy" / "unraid" / "geointel-icon.svg").read_text(encoding="utf-8")
frontend_icon = (ROOT / "frontend" / "public" / "geointel-icon.svg").read_text(encoding="utf-8")
deploy_png = ROOT / "deploy" / "unraid" / "geointel-icon.png"
frontend_png = ROOT / "frontend" / "public" / "geointel-icon.png"
index = (ROOT / "frontend" / "index.html").read_text(encoding="utf-8")
assert "<svg" in deploy_icon
assert "<title id=\"title\">GeoIntel</title>" in deploy_icon
assert "Een geometrische G als geografische lens" in deploy_icon
assert "Een geometrische G als geografische lens" in frontend_icon
assert deploy_png.read_bytes() == frontend_png.read_bytes()
assert deploy_png.read_bytes() == frontend_png.read_bytes()
assert deploy_png.stat().st_size > 1000
assert '<link rel="icon" type="image/svg+xml" href="/geointel-icon.svg" />' in index
@@ -8,7 +8,7 @@ ROOT = Path(__file__).resolve().parents[2]
def test_app_uses_shared_orchestration_hooks() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = (ROOT / "frontend" / "src" / "WorkbenchApp.tsx").read_text(encoding="utf-8")
assert "useProjectWorkspace" in app
assert "useDemoWorkflow" in app
@@ -31,7 +31,7 @@ def test_app_uses_shared_orchestration_hooks() -> None:
def test_app_entrypoint_has_clean_encoding_and_react_imports() -> None:
app_path = ROOT / "frontend" / "src" / "App.tsx"
app_path = ROOT / "frontend" / "src" / "WorkbenchApp.tsx"
app_bytes = app_path.read_bytes()
app = app_path.read_text(encoding="utf-8")
@@ -61,21 +61,21 @@ def test_demo_workflow_hook_owns_demo_api_and_cross_module_selection() -> None:
def test_workbench_bootstrap_hook_owns_entrypoint_effects() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = read_feature("shell")
hook = read_feature("shell")
assert "loadProjects().catch(() => null)" not in app
assert "loadDetectionResults().catch(() => null)" not in app
assert "loadSegmentationResults().catch(() => null)" not in app
assert "loadProjects().catch(() => null)" in hook
assert "loadCapabilities().catch(() => null)" in hook
assert "loadDetectionModels().catch(() => null)" in hook
assert "loadSegmentationModels().catch(() => null)" in hook
assert "loadProjects().catch(meld('werkruimtes'))" in hook
assert "loadCapabilities().catch(meld('bronkoppelingen'))" in hook
assert "loadDetectionModels().catch(meld('detectiemodellen'))" in hook
assert "loadSegmentationModels().catch(meld('segmentatiemodellen'))" in hook
assert "resetProjectData()" in hook
assert "resetDatasetForProject()" in hook
assert "loadProjectData(selectedProjectId).catch(() => null)" in hook
assert "loadDetectionResults().catch(() => null)" in hook
assert "loadSegmentationResults().catch(() => null)" in hook
assert "loadProjectData(selectedProjectId).catch(meld('gebieden en bronnen'))" in hook
assert "loadDetectionResults().catch(meld('detectieresultaten'))" in hook
assert "loadSegmentationResults().catch(meld('segmentatieresultaten'))" in hook
def test_project_workspace_hook_owns_project_area_dataset_loading() -> None:
@@ -8,7 +8,7 @@ ROOT = Path(__file__).resolve().parents[2]
def test_frontend_shell_has_atlas_workbench_contracts() -> None:
app = (ROOT / "frontend" / "src" / "App.tsx").read_text(encoding="utf-8")
app = read_feature("shell")
css = (ROOT / "frontend" / "src" / "styles" / "atlas-workbench.css").read_text(
encoding="utf-8"
)
@@ -34,7 +34,7 @@ def test_ai_labs_use_result_state_blocks() -> None:
segmentation_lab = read_feature("segmentation")
for content in (detection_lab, segmentation_lab):
assert 'className="result-state result-state-loading"' in content
assert 'className="result-state result-state-error"' in content
assert 'className="result-state result-state-empty"' in content
assert 'className="result-state result-state-ready"' in content
assert "result-state-loading" in content
assert "result-state-error" in content
assert "result-state-empty" in content
assert "result-state-ready" in content
@@ -20,7 +20,7 @@ def test_status_strip_has_compact_section_surface_contracts() -> None:
css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
assert ".workbench-status-strip" in css
assert "background: linear-gradient(180deg, #ffffff, #f7fbf8);" in css
assert "background: linear-gradient(180deg, var(--gi-surface), var(--gi-brand-50));" in css
assert ".status-strip-grid" in css
assert "grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));" in css
assert ".status-tile-topline" in css
@@ -34,6 +34,6 @@ def test_overview_action_callout_is_visually_secondary() -> None:
assert ".overview-action-copy" in css
assert ".overview-quick-actions" in css
assert ".quick-action-button" in css
assert "background: linear-gradient(135deg, rgba(15, 118, 110, 0.08), rgba(255, 255, 255, 0.82));" in css
assert "background: linear-gradient(135deg, rgba(15, 118, 110, 0.08), color-mix(in srgb, var(--gi-surface) 82%, transparent));" in css
assert "border-left: 4px solid var(--accent);" in css
assert "align-content: center;" in css
@@ -44,5 +44,5 @@ def test_data_workspace_density_css_contracts() -> None:
assert ".data-panel-list-block" in css
assert ".data-section-label" in css
assert "grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));" in css
assert "background: linear-gradient(180deg, #ffffff, #f7fbf8);" in css
assert "background: linear-gradient(180deg, var(--gi-surface), var(--gi-brand-50));" in css
assert "border-left: 4px solid var(--accent);" in css
@@ -41,7 +41,7 @@ def test_map_workspace_density_css_contracts() -> None:
assert ".map-inspection-surface" in css
assert ".map-workspace-shell .map-toolbar" in css
assert "grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));" in css
assert "background: linear-gradient(180deg, #ffffff, #f7fbf8);" in css
assert "background: linear-gradient(180deg, var(--gi-surface), var(--gi-brand-50));" in css
assert "border-left: 4px solid var(--accent);" in css
@@ -50,4 +50,4 @@ def test_quality_workspace_density_css_contracts() -> None:
assert ".quality-results-shell .quality-history-controls" in css
assert "grid-template-columns: repeat(auto-fit, minmax(8.25rem, 1fr));" in css
assert "grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));" in css
assert "background: linear-gradient(180deg, #ffffff, #f7fbf8);" in css
assert "background: linear-gradient(180deg, var(--gi-surface), var(--gi-brand-50));" in css
@@ -57,7 +57,7 @@ def test_ai_lab_preserves_existing_detection_and_segmentation_controls() -> None
assert "onLoadResults" in segmentation
assert "onRunQa" in segmentation
assert "selectedSegmentationModelConfigured" in segmentation
assert "{segmentationItems.length} vlakken geladen" in segmentation
assert "${segmentationTotal} vlakken geladen" in segmentation
assert "Vergelijk met referentielaag" in segmentation
@@ -13,14 +13,14 @@ def test_export_center_exposes_final_handoff_surfaces() -> None:
assert 'className="export-center"' in export_center
assert 'className="export-center-shell"' in export_center
assert 'className="export-summary-surface"' in export_center
assert 'aria-label="Export summary"' in export_center
assert 'aria-label="Samenvatting van de downloads"' in export_center
assert 'className="export-handoff-surface"' in export_center
assert 'aria-label="Export handoff readiness"' in export_center
assert 'aria-label="Gereedheid voor overdracht"' in export_center
assert 'className="export-actions-surface"' in export_center
assert 'aria-label="Export artifact actions"' in export_center
assert 'aria-label="Acties om resultaten te bewaren"' in export_center
assert 'className="export-state-stack"' in export_center
assert 'className="export-history-surface export-history-disclosure"' in export_center
assert 'aria-label="Export history"' in export_center
assert 'aria-label="Downloadgeschiedenis"' in export_center
def test_provider_panel_exposes_final_system_surfaces() -> None:
@@ -77,4 +77,4 @@ def test_export_system_density_css_contracts() -> None:
assert ".provider-provenance-grid" in css
assert ".provider-provenance-card" in css
assert "grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));" in css
assert "background: linear-gradient(180deg, #ffffff, #f7fbf8);" in css
assert "background: linear-gradient(180deg, var(--gi-surface), var(--gi-brand-50));" in css
+5 -3
View File
@@ -1097,7 +1097,7 @@ A regional run holds tens of thousands of detections, and these are the
endpoints the results table and the map overlay call after every run; they
previously returned all of them.
### GET `/api/v1/projects/{project_id}/quality-checks/{id}/evidence/geojson`
### GET `/api/v1/projects/{project_id}/quality-checks/{quality_check_id}/evidence/geojson`
Returns the reviewable geometry behind one quality check: the objects the model
missed, the ones it found without a reference, and the confirmed matches.
@@ -1769,7 +1769,9 @@ Queued jobs are executed by the background analysis worker
`GEOINTEL_ANALYSIS_WORKER_POLL_SECONDS`), which claims a job before dispatching
it so the same run is never started twice. Poll the project-bound
`GET /api/v1/projects/{project_id}/jobs/{job_id}` endpoint for progress.
`POST /api/v1/segmentation/run-async` behaves identically. Guest sessions may
### POST `/api/v1/segmentation/run-async`
This endpoint behaves identically to the detection async endpoint. Guest sessions may
queue and read analysis only for the project id embedded in their signed
session; query parameters never authorize a run, result or export belonging to
another project.
@@ -1863,7 +1865,7 @@ Each feature includes:
Returns persisted detections for a dataset as a GeoJSON FeatureCollection. Optional filters match the detection list endpoint.
### GET `/api/v1/projects/{project_id}/quality-checks/{id}/reviews`
### GET `/api/v1/projects/{project_id}/quality-checks/{quality_check_id}/reviews`
Returns the evidence queue plus a `summary`, which now carries
`reviewed_metrics`: the score with the operator's verdicts applied, next to the
+23
View File
@@ -1,3 +1,26 @@
## 2026-08-29 - Gitea release-gateherstel
### Opgeleverd
- De gepinde TruffleHog-scanner draait rechtstreeks als filesystemscan en is
niet langer afhankelijk van de incompatibele Docker-actionwrapper die
`file:///tmp` probeerde te klonen.
- De frontend-lock wordt via een npm-override op de gepatchte Nano ID 3.3.18
gehouden; de HIGH dependencybevinding is daarmee verwijderd.
- Syft krijgt de expliciete lokale Docker-imagesource. Daardoor scant de SBOM-
gate het zojuist gebouwde image via de gemounte Docker-socket in plaats van
de tag als een niet-bestaand containerbestandspad te interpreteren.
- De functioneel identieke Gitea- en GitHub-workflows blijven synchroon.
### Verificatie
- Pending tijdens implementatie: npm lock/audit, readiness, shellsyntax en de
drie externe releasejobs.
### Open en beperkingen
- Geen product-, API-, GIS-, model- of deploymentscope is gewijzigd.
## 2026-08-21 - Gesynchroniseerde tweeschermwerkruimte
### Opgeleverd
+7
View File
@@ -1,5 +1,12 @@
# GeoIntel TODO
## CI-onderhoud (2026-08-29)
- [x] Vervang de Gitea-incompatibele TruffleHog-actionwrapper.
- [x] Pin de gepatchte Nano ID-transitive in de frontendlock.
- [x] Maak de Syft-bron expliciet als lokaal Docker-image.
- [ ] Bevestig de drie releasejobs op Gitea.
## Tweeschermwerkruimte (2026-08-21)
- [x] Voeg een gesynchroniseerde analyseconsole voor een tweede beeldscherm toe.
+3 -3
View File
@@ -2530,9 +2530,9 @@
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.16",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
"version": "3.3.18",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
"dev": true,
"funding": [
{
+3
View File
@@ -15,6 +15,9 @@
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"overrides": {
"nanoid": "3.3.18"
},
"dependencies": {
"@fontsource/manrope": "^5.3.0",
"@fontsource/public-sans": "^5.3.0",
+1 -1
View File
@@ -20,7 +20,7 @@ docker run --rm \
-v "$ROOT:/workspace" \
-w /workspace \
"$SYFT_IMAGE" \
"$TARGET_IMAGE" \
"docker:$TARGET_IMAGE" \
-o "spdx-json=$OUTPUT"
test -s "$ROOT/$OUTPUT"
@@ -44,9 +44,13 @@ fi
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
BASE_DIR="${CALIBRATION_EVIDENCE_SMOKE_DIR:-${ROOT}/artifacts/detection-calibration-smoke/${timestamp}}"
if command -v cygpath >/dev/null 2>&1 && printf '%s' "$BASE_DIR" | grep -Eq '^[A-Za-z]:\\'; then
if printf '%s' "$BASE_DIR" | grep -Eq '^[A-Za-z]:\\'; then
if command -v wslpath >/dev/null 2>&1; then
BASE_DIR="$(wslpath -u "$BASE_DIR")"
elif command -v cygpath >/dev/null 2>&1; then
BASE_DIR="$(cygpath -u "$BASE_DIR")"
fi
fi
SUMMARY_DIR="${BASE_DIR}/summary"
OUTPUT_DIR="${BASE_DIR}/evidence"