fix(ci): restore release gates and contract baseline (fixes #4) #6
@@ -2,8 +2,6 @@ name: GeoIntel release gates
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main, develop, "codex/**", "build/**"]
|
|
||||||
pull_request:
|
|
||||||
branches: [main, develop]
|
branches: [main, develop]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
@@ -22,10 +20,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Secret scan
|
- name: Secret scan
|
||||||
uses: trufflesecurity/trufflehog@v3.79.0
|
run: >-
|
||||||
with:
|
docker run --rm
|
||||||
path: ./
|
--volume "$PWD:/repo:ro"
|
||||||
extra_args: --only-verified
|
trufflesecurity/trufflehog:3.79.0@sha256:7104dbb84d1ad2f5f6fa1134e92c6aa6f701f0a4ac2efd5a4c5c96225d899fe3
|
||||||
|
filesystem /repo --only-verified --no-update
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|||||||
@@ -22,10 +22,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Secret scan
|
- name: Secret scan
|
||||||
uses: trufflesecurity/trufflehog@v3.79.0
|
run: >-
|
||||||
with:
|
docker run --rm
|
||||||
path: ./
|
--volume "$PWD:/repo:ro"
|
||||||
extra_args: --only-verified
|
trufflesecurity/trufflehog:3.79.0@sha256:7104dbb84d1ad2f5f6fa1134e92c6aa6f701f0a4ac2efd5a4c5c96225d899fe3
|
||||||
|
filesystem /repo --only-verified --no-update
|
||||||
- uses: actions/setup-python@v5
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ maximaal zichtbaar op het hoofdscherm terwijl analyses, kwaliteitsbewijs en
|
|||||||
resultaten live in een tweede venster staan. Zie
|
resultaten live in een tweede venster staan. Zie
|
||||||
[`docs/DUAL_DISPLAY_WORKFLOW.md`](docs/DUAL_DISPLAY_WORKFLOW.md).
|
[`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">
|
<p align="center">
|
||||||
<img src="frontend/public/geointel-icon-180.png" width="92" alt="GeoIntel logo">
|
<img src="frontend/public/geointel-icon-180.png" width="92" alt="GeoIntel logo">
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -345,7 +345,7 @@ def test_frontend_exposes_map_bbox_selection_contracts() -> None:
|
|||||||
theme_hook = read_feature("map_workspace")
|
theme_hook = read_feature("map_workspace")
|
||||||
|
|
||||||
assert "selectVectorFeatures" in api_client
|
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 "Teken rechthoek" in map_workspace
|
||||||
assert "Objecten in gebied ophalen" in map_workspace
|
assert "Objecten in gebied ophalen" in map_workspace
|
||||||
assert "Gebiedsdownload bewaren" 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:
|
def test_map_selection_qa_shortcut_uses_existing_qa_workflow_contract() -> None:
|
||||||
hook_path = ROOT / "frontend" / "src" / "hooks" / "useMapSelectionQa.ts"
|
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")
|
map_workspace = read_feature("map_workspace")
|
||||||
|
|
||||||
assert hook_path.exists()
|
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:
|
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 "qaApi" not in app
|
||||||
assert "from './services/api'" 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")
|
types = read_text("frontend/src/types.ts")
|
||||||
hook = read_text("frontend/src/hooks/useMapSelectionQa.ts")
|
hook = read_text("frontend/src/hooks/useMapSelectionQa.ts")
|
||||||
workspace = read_feature("map_workspace")
|
workspace = read_feature("map_workspace")
|
||||||
app = read_text("frontend/src/App.tsx")
|
app = read_feature("shell")
|
||||||
|
|
||||||
assert "quality_check_id?: string" in types
|
assert "quality_check_id?: string" in types
|
||||||
assert "latestMapSelectionQualityCheckId" in hook
|
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():
|
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")
|
api_contracts = read_text("docs/API_CONTRACTS.md")
|
||||||
|
|
||||||
assert "/api/v1/qa/detections-vs-reference" in api_contracts
|
assert "/api/v1/qa/detections-vs-reference" in api_contracts
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from tests.frontend_contract import read_feature
|
||||||
|
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[2]
|
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:
|
def test_detection_lab_exposes_persisted_threshold_calibration_comparison() -> None:
|
||||||
detection_lab = ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx"
|
detection_lab = ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx"
|
||||||
app = ROOT / "frontend" / "src" / "App.tsx"
|
|
||||||
todo = ROOT / "docs" / "TODO.md"
|
todo = ROOT / "docs" / "TODO.md"
|
||||||
|
|
||||||
source = detection_lab.read_text(encoding="utf-8")
|
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")
|
todo_source = todo.read_text(encoding="utf-8")
|
||||||
|
|
||||||
assert "qualityChecks: QualityCheckRead[]" in source
|
assert "qualityChecks: QualityCheckRead[]" in source
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from tests.frontend_contract import read_feature
|
||||||
|
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[2]
|
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:
|
def test_detection_lab_has_guided_threshold_calibration_runner() -> None:
|
||||||
hook = ROOT / "frontend" / "src" / "hooks" / "useDetectionWorkflow.ts"
|
hook = ROOT / "frontend" / "src" / "hooks" / "useDetectionWorkflow.ts"
|
||||||
lab = ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx"
|
lab = ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx"
|
||||||
app = ROOT / "frontend" / "src" / "App.tsx"
|
|
||||||
todo = ROOT / "docs" / "TODO.md"
|
todo = ROOT / "docs" / "TODO.md"
|
||||||
|
|
||||||
hook_source = hook.read_text(encoding="utf-8")
|
hook_source = hook.read_text(encoding="utf-8")
|
||||||
lab_source = lab.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")
|
todo_source = todo.read_text(encoding="utf-8")
|
||||||
|
|
||||||
assert "interface DetectionCalibrationRunRow" in hook_source
|
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 "detectionCalibrationRows" in hook_source
|
||||||
assert "detectionCalibrationError" in hook_source
|
assert "detectionCalibrationError" in hook_source
|
||||||
assert "runDetectionCalibration" 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
|
# 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
|
# lowest cut: detections above a higher cut are a subset of it, and
|
||||||
# suppression walks candidates in descending confidence, so the kept set
|
# suppression walks candidates in descending confidence, so the kept set
|
||||||
# above a cut does not depend on the threshold the run used. Asserting the
|
# 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.
|
# 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_thresholds: thresholds" in hook_source
|
||||||
assert "calibration_sweep" 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 "detectionApi.compareWithReference(result.analysis_run_id" in hook_source
|
||||||
assert "reference_dataset_id: detectionReferenceDatasetId" in hook_source
|
assert "reference_dataset_id: referenceDatasetId" in hook_source
|
||||||
assert "Select a reference dataset before calibration" in hook_source
|
assert "Kies eerst een referentiebron om te kalibreren" in hook_source
|
||||||
assert "Provide at least one valid threshold between 0 and 1" in hook_source
|
assert "Geef minstens één geldige drempel tussen 0 en 1 op" in hook_source
|
||||||
assert "Configured YOLO calibration requires a tile manifest" in hook_source
|
assert "Kalibratie met YOLO vereist een beeldtegelmanifest" in hook_source
|
||||||
assert "Select a local model asset before calibration" in hook_source
|
assert "Kies eerst een lokaal modelbestand om te kalibreren" in hook_source
|
||||||
|
|
||||||
assert "Modelkalibratie voor beheerders" in lab_source
|
assert "Modelkalibratie voor beheerders" in lab_source
|
||||||
assert "Voert het lokale model en een kwaliteitscontrole uit" in lab_source
|
assert "Voert het lokale model en een kwaliteitscontrole uit" in lab_source
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from tests.frontend_contract import read_feature
|
||||||
|
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[2]
|
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:
|
def test_guided_calibration_rows_link_to_existing_qa_evidence_map() -> None:
|
||||||
lab = ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx"
|
lab = ROOT / "frontend" / "src" / "components" / "detection" / "DetectionLab.tsx"
|
||||||
app = ROOT / "frontend" / "src" / "App.tsx"
|
|
||||||
todo = ROOT / "docs" / "TODO.md"
|
todo = ROOT / "docs" / "TODO.md"
|
||||||
|
|
||||||
lab_source = lab.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")
|
todo_source = todo.read_text(encoding="utf-8")
|
||||||
|
|
||||||
assert "onOpenCalibrationEvidence" in lab_source
|
assert "onOpenCalibrationEvidence" in lab_source
|
||||||
|
|||||||
@@ -15,22 +15,15 @@ def _bash_path(path: Path) -> str:
|
|||||||
if os.name != "nt":
|
if os.name != "nt":
|
||||||
return raw_path
|
return raw_path
|
||||||
|
|
||||||
try:
|
# ``bash`` is WSL on the supported Windows development path. Converting
|
||||||
result = subprocess.run(
|
# the drive path directly avoids starting a second WSL process merely to
|
||||||
["bash", "-lc", f"wslpath -a {shlex.quote(raw_path)}"],
|
# run ``wslpath``; that process can time out while the full suite is busy
|
||||||
capture_output=True,
|
# and would leave curl with an invalid file://C:\\... URL.
|
||||||
text=True,
|
drive, tail = os.path.splitdrive(raw_path)
|
||||||
timeout=10,
|
if drive:
|
||||||
check=False,
|
normalized_tail = tail.lstrip("\\/").replace("\\", "/")
|
||||||
)
|
return f"/mnt/{drive[0].lower()}/{normalized_tail}"
|
||||||
except (subprocess.TimeoutExpired, OSError):
|
return raw_path.replace("\\", "/")
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
def test_split_background_promotion_workflow_runs_split_then_split_aware_report() -> None:
|
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.
|
# the map fits the selection bounds without an animation.
|
||||||
assert "map.fitBounds(" in geomap
|
assert "map.fitBounds(" in geomap
|
||||||
assert "duration: 0" 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 "const activeCollection = areaData ?? (fitDataOnChange ? data : null)" in geomap
|
||||||
assert "data && fitDataOnChange && !areaData" in geomap
|
assert "data && fitDataOnChange && !areaData" in geomap
|
||||||
assert "resizeObserver.disconnect()" 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 "getDatasetSourceDisplayName(dataset)" in catalog
|
||||||
assert "statusLabel(item.state)" in status
|
assert "statusLabel(item.state)" in status
|
||||||
assert "Technische modelevaluatie" in detection
|
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:
|
def test_full_area_fast_path_requires_matching_area_and_clipped_operator_provenance() -> None:
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ def test_geomap_exposes_v1_layer_controls_and_feature_inspection_contract() -> N
|
|||||||
assert "mapStyleReady" in geomap
|
assert "mapStyleReady" in geomap
|
||||||
assert "map.on('load'" in geomap
|
assert "map.on('load'" in geomap
|
||||||
assert "if (!map || !mapStyleReady)" 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 "setLayoutProperty('dataset-fill', 'visibility'" in geomap
|
||||||
assert "setPaintProperty('dataset-fill', 'fill-opacity', opacity)" 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-domain-grid" in styles
|
||||||
assert "source-opportunity-domain" in styles
|
assert "source-opportunity-domain" in styles
|
||||||
assert "section.source-catalog-panel > .panel-title-row" 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
|
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:
|
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")
|
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 "dataset.id === result.candidate_dataset_id" in demo_hook
|
||||||
assert "await loadDatasetDetails(result.project_id, candidateDataset)" 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:
|
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")
|
workspace = read_feature("map_workspace")
|
||||||
styles = read("frontend/src/styles/app.css")
|
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:
|
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 "useDetectionWorkflow" in app
|
||||||
assert "useSegmentationWorkflow" 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:
|
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 "<DetectionLab" in app
|
||||||
assert "onRunDetection={runDetection}" 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:
|
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 "useExportWorkflow" in app
|
||||||
assert "useQualityWorkflow" 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:
|
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")
|
quality_panel = read_feature("quality")
|
||||||
|
|
||||||
assert "<QualityResultsPanel" in app
|
assert "<QualityResultsPanel" in app
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ ROOT = Path(__file__).resolve().parents[2]
|
|||||||
|
|
||||||
|
|
||||||
def test_app_uses_dataset_workflow_hook() -> None:
|
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 "useDatasetWorkflow" in app
|
||||||
assert "from './hooks/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:
|
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")
|
dataset_panel = read_feature("datasets")
|
||||||
detail_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:
|
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")
|
inspector = read_feature("shell")
|
||||||
|
|
||||||
assert "from './components/datasets/DatasetPanel'" in app
|
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:
|
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/quality/QualityResultsPanel'" in app
|
||||||
assert "from './components/map/MapWorkspace'" 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:
|
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")
|
styles = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
|
||||||
|
|
||||||
assert "activeWorkspace === 'map'" in app
|
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")
|
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")
|
frontend_icon = (ROOT / "frontend" / "public" / "geointel-icon.svg").read_text(encoding="utf-8")
|
||||||
deploy_png = ROOT / "deploy" / "unraid" / "geointel-icon.png"
|
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")
|
index = (ROOT / "frontend" / "index.html").read_text(encoding="utf-8")
|
||||||
|
|
||||||
assert "<svg" in deploy_icon
|
assert "<svg" in deploy_icon
|
||||||
assert "<title id=\"title\">GeoIntel</title>" 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 deploy_icon
|
||||||
assert "Een geometrische G als geografische lens" in frontend_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 deploy_png.stat().st_size > 1000
|
||||||
assert '<link rel="icon" type="image/svg+xml" href="/geointel-icon.svg" />' in index
|
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:
|
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 "useProjectWorkspace" in app
|
||||||
assert "useDemoWorkflow" 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:
|
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_bytes = app_path.read_bytes()
|
||||||
app = app_path.read_text(encoding="utf-8")
|
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:
|
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")
|
hook = read_feature("shell")
|
||||||
|
|
||||||
assert "loadProjects().catch(() => null)" not in app
|
assert "loadProjects().catch(() => null)" not in app
|
||||||
assert "loadDetectionResults().catch(() => null)" not in app
|
assert "loadDetectionResults().catch(() => null)" not in app
|
||||||
assert "loadSegmentationResults().catch(() => null)" not in app
|
assert "loadSegmentationResults().catch(() => null)" not in app
|
||||||
assert "loadProjects().catch(() => null)" in hook
|
assert "loadProjects().catch(meld('werkruimtes'))" in hook
|
||||||
assert "loadCapabilities().catch(() => null)" in hook
|
assert "loadCapabilities().catch(meld('bronkoppelingen'))" in hook
|
||||||
assert "loadDetectionModels().catch(() => null)" in hook
|
assert "loadDetectionModels().catch(meld('detectiemodellen'))" in hook
|
||||||
assert "loadSegmentationModels().catch(() => null)" in hook
|
assert "loadSegmentationModels().catch(meld('segmentatiemodellen'))" in hook
|
||||||
assert "resetProjectData()" in hook
|
assert "resetProjectData()" in hook
|
||||||
assert "resetDatasetForProject()" in hook
|
assert "resetDatasetForProject()" in hook
|
||||||
assert "loadProjectData(selectedProjectId).catch(() => null)" in hook
|
assert "loadProjectData(selectedProjectId).catch(meld('gebieden en bronnen'))" in hook
|
||||||
assert "loadDetectionResults().catch(() => null)" in hook
|
assert "loadDetectionResults().catch(meld('detectieresultaten'))" in hook
|
||||||
assert "loadSegmentationResults().catch(() => null)" in hook
|
assert "loadSegmentationResults().catch(meld('segmentatieresultaten'))" in hook
|
||||||
|
|
||||||
|
|
||||||
def test_project_workspace_hook_owns_project_area_dataset_loading() -> None:
|
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:
|
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(
|
css = (ROOT / "frontend" / "src" / "styles" / "atlas-workbench.css").read_text(
|
||||||
encoding="utf-8"
|
encoding="utf-8"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ def test_ai_labs_use_result_state_blocks() -> None:
|
|||||||
segmentation_lab = read_feature("segmentation")
|
segmentation_lab = read_feature("segmentation")
|
||||||
|
|
||||||
for content in (detection_lab, segmentation_lab):
|
for content in (detection_lab, segmentation_lab):
|
||||||
assert 'className="result-state result-state-loading"' in content
|
assert "result-state-loading" in content
|
||||||
assert 'className="result-state result-state-error"' in content
|
assert "result-state-error" in content
|
||||||
assert 'className="result-state result-state-empty"' in content
|
assert "result-state-empty" in content
|
||||||
assert 'className="result-state result-state-ready"' 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")
|
css = (ROOT / "frontend" / "src" / "styles" / "app.css").read_text(encoding="utf-8")
|
||||||
|
|
||||||
assert ".workbench-status-strip" in css
|
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 ".status-strip-grid" in css
|
||||||
assert "grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));" in css
|
assert "grid-template-columns: repeat(auto-fit, minmax(10.5rem, 1fr));" in css
|
||||||
assert ".status-tile-topline" 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-action-copy" in css
|
||||||
assert ".overview-quick-actions" in css
|
assert ".overview-quick-actions" in css
|
||||||
assert ".quick-action-button" 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 "border-left: 4px solid var(--accent);" in css
|
||||||
assert "align-content: center;" 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-panel-list-block" in css
|
||||||
assert ".data-section-label" in css
|
assert ".data-section-label" in css
|
||||||
assert "grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));" 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
|
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-inspection-surface" in css
|
||||||
assert ".map-workspace-shell .map-toolbar" in css
|
assert ".map-workspace-shell .map-toolbar" in css
|
||||||
assert "grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));" 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
|
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 ".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(8.25rem, 1fr));" in css
|
||||||
assert "grid-template-columns: repeat(auto-fit, minmax(7.5rem, 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 "onLoadResults" in segmentation
|
||||||
assert "onRunQa" in segmentation
|
assert "onRunQa" in segmentation
|
||||||
assert "selectedSegmentationModelConfigured" 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
|
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"' in export_center
|
||||||
assert 'className="export-center-shell"' in export_center
|
assert 'className="export-center-shell"' in export_center
|
||||||
assert 'className="export-summary-surface"' 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 '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 '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-state-stack"' in export_center
|
||||||
assert 'className="export-history-surface export-history-disclosure"' 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:
|
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-grid" in css
|
||||||
assert ".provider-provenance-card" in css
|
assert ".provider-provenance-card" in css
|
||||||
assert "grid-template-columns: repeat(auto-fit, minmax(8rem, 1fr));" 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
|
||||||
|
|||||||
@@ -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
|
endpoints the results table and the map overlay call after every run; they
|
||||||
previously returned all of them.
|
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
|
Returns the reviewable geometry behind one quality check: the objects the model
|
||||||
missed, the ones it found without a reference, and the confirmed matches.
|
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
|
`GEOINTEL_ANALYSIS_WORKER_POLL_SECONDS`), which claims a job before dispatching
|
||||||
it so the same run is never started twice. Poll the project-bound
|
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.
|
`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
|
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
|
session; query parameters never authorize a run, result or export belonging to
|
||||||
another project.
|
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.
|
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
|
Returns the evidence queue plus a `summary`, which now carries
|
||||||
`reviewed_metrics`: the score with the operator's verdicts applied, next to the
|
`reviewed_metrics`: the score with the operator's verdicts applied, next to the
|
||||||
|
|||||||
@@ -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
|
## 2026-08-21 - Gesynchroniseerde tweeschermwerkruimte
|
||||||
|
|
||||||
### Opgeleverd
|
### Opgeleverd
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# GeoIntel TODO
|
# 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)
|
## Tweeschermwerkruimte (2026-08-21)
|
||||||
|
|
||||||
- [x] Voeg een gesynchroniseerde analyseconsole voor een tweede beeldscherm toe.
|
- [x] Voeg een gesynchroniseerde analyseconsole voor een tweede beeldscherm toe.
|
||||||
|
|||||||
Generated
+3
-3
@@ -2530,9 +2530,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.16",
|
"version": "3.3.18",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz",
|
||||||
"integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
|
"integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,6 +15,9 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": "^20.19.0 || >=22.12.0"
|
"node": "^20.19.0 || >=22.12.0"
|
||||||
},
|
},
|
||||||
|
"overrides": {
|
||||||
|
"nanoid": "3.3.18"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/manrope": "^5.3.0",
|
"@fontsource/manrope": "^5.3.0",
|
||||||
"@fontsource/public-sans": "^5.3.0",
|
"@fontsource/public-sans": "^5.3.0",
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ docker run --rm \
|
|||||||
-v "$ROOT:/workspace" \
|
-v "$ROOT:/workspace" \
|
||||||
-w /workspace \
|
-w /workspace \
|
||||||
"$SYFT_IMAGE" \
|
"$SYFT_IMAGE" \
|
||||||
"$TARGET_IMAGE" \
|
"docker:$TARGET_IMAGE" \
|
||||||
-o "spdx-json=$OUTPUT"
|
-o "spdx-json=$OUTPUT"
|
||||||
|
|
||||||
test -s "$ROOT/$OUTPUT"
|
test -s "$ROOT/$OUTPUT"
|
||||||
|
|||||||
@@ -44,8 +44,12 @@ fi
|
|||||||
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
|
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||||
BASE_DIR="${CALIBRATION_EVIDENCE_SMOKE_DIR:-${ROOT}/artifacts/detection-calibration-smoke/${timestamp}}"
|
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
|
||||||
BASE_DIR="$(cygpath -u "$BASE_DIR")"
|
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
|
fi
|
||||||
|
|
||||||
SUMMARY_DIR="${BASE_DIR}/summary"
|
SUMMARY_DIR="${BASE_DIR}/summary"
|
||||||
|
|||||||
Reference in New Issue
Block a user