Initial GeoIntel V1 foundation
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled

This commit is contained in:
Codex
2026-06-16 23:36:32 +02:00
commit 6ea3586a3e
605 changed files with 45284 additions and 0 deletions
View File
+46
View File
@@ -0,0 +1,46 @@
# Scripts
Setup-, import-, demo- en maintenance-scripts voor GeoIntel.
## Runtime verification
Verify the browser-facing Docker/LAN runtime:
```bash
bash scripts/verify_browser_runtime.sh http://192.168.10.150:1202
bash scripts/verify_gis_runtime.sh http://192.168.10.150:1202
```
Verify the explicit demo workflow plus export artifact path:
```bash
bash scripts/verify_demo_export_workflow.sh http://192.168.10.150:1202
```
The demo/export smoke seeds the offline fixture demo, verifies persisted QA/QC
results, creates metadata/report/vector GeoJSON exports, lists exports and
downloads the JSON/GeoJSON/HTML artifacts through the frontend proxy.
## Tower deployment
Push the local branch to Gitea, then rebuild the Unraid/Tower Docker runtime:
```bash
bash scripts/deploy_tower.sh
```
For the first deployment into an existing non-Git appdata folder, bootstrap the
checkout explicitly:
```bash
DEPLOY_BOOTSTRAP=1 bash scripts/deploy_tower.sh
```
Useful overrides:
```bash
REMOTE_HOST=root@192.168.10.150
REMOTE_PATH=/mnt/user/appdata/geointel
REMOTE_REPO=gitea-widefrog:NuklearRabbit/geointel.git
FRONTEND_URL=http://192.168.10.150:1202
```
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/../backend"
if command -v python3 >/dev/null 2>&1 && python3 -m uvicorn --version >/dev/null 2>&1; then
PYTHON_BIN="python3"
elif command -v python >/dev/null 2>&1 && python -m uvicorn --version >/dev/null 2>&1; then
PYTHON_BIN="python"
elif command -v python.exe >/dev/null 2>&1 && python.exe -m uvicorn --version >/dev/null 2>&1; then
PYTHON_BIN="python.exe"
else
echo "No python interpreter with uvicorn available" >&2
exit 1
fi
"${PYTHON_BIN}" -m uvicorn app.main:app --reload
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/../backend"
if command -v python3 >/dev/null 2>&1 && python3 -m pip --version >/dev/null 2>&1; then
PYTHON_BIN="python3"
elif command -v python >/dev/null 2>&1 && python -m pip --version >/dev/null 2>&1; then
PYTHON_BIN="python"
elif command -v python.exe >/dev/null 2>&1 && python.exe -m pip --version >/dev/null 2>&1; then
PYTHON_BIN="python.exe"
else
echo "No python interpreter with pip available" >&2
exit 1
fi
"${PYTHON_BIN}" -m pip install -e .[dev]
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/../backend"
if [ ! -d .venv ]; then
if command -v python3 >/dev/null 2>&1 && python3 -m pytest --version >/dev/null 2>&1; then
PYTHON_BIN="python3"
elif command -v python >/dev/null 2>&1 && python -m pytest --version >/dev/null 2>&1; then
PYTHON_BIN="python"
elif command -v python.exe >/dev/null 2>&1 && python.exe -m pytest --version >/dev/null 2>&1; then
PYTHON_BIN="python.exe"
else
echo "No python interpreter with pytest available" >&2
exit 1
fi
"${PYTHON_BIN}" -m pytest --version >/dev/null 2>&1 || "${PYTHON_BIN}" -m pip install -e .[dev]
fi
if command -v python3 >/dev/null 2>&1 && python3 -m pytest --version >/dev/null 2>&1; then
PYTHON_BIN="python3"
elif command -v python >/dev/null 2>&1 && python -m pytest --version >/dev/null 2>&1; then
PYTHON_BIN="python"
elif command -v python.exe >/dev/null 2>&1 && python.exe -m pytest --version >/dev/null 2>&1; then
PYTHON_BIN="python.exe"
else
echo "No python interpreter with pytest available" >&2
exit 1
fi
"${PYTHON_BIN}" -m pytest
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
required=("README.md" "AGENTS.md" "docs/M5_OPERATIONAL_READINESS.md" "docs/BUILD_GOVERNANCE.md" "docs/V1_SCOPE_FREEZE.md" "backend" "frontend" "contracts/api" "contracts/database" "fixtures/geojson" "storage/originals" "storage/derived" "storage/exports")
for path in "${required[@]}"; do
if [ ! -e "$path" ]; then echo "Missing required path: $path" >&2; exit 1; fi
done
echo "Repo structure OK"
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail
if [ -n "${PYTHON_BIN:-}" ]; then
PYTHON_BIN="${PYTHON_BIN}"
elif command -v python3 >/dev/null 2>&1 && python3 -c "import sys" >/dev/null 2>&1; then
PYTHON_BIN="python3"
elif command -v python.exe >/dev/null 2>&1 && python.exe -c "import sys" >/dev/null 2>&1; then
PYTHON_BIN="python.exe"
else
PYTHON_BIN="python"
fi
echo "== GeoIntel pass end check =="
"$PYTHON_BIN" scripts/smoke_docs.py
"$PYTHON_BIN" scripts/validate_fixtures.py
"$PYTHON_BIN" scripts/smoke_contracts.py
if grep -R \
--exclude-dir=node_modules \
--exclude-dir=dist \
--exclude-dir=__pycache__ \
"TODO: implement later\|placeholder only\|fake completed" \
-n backend frontend docs 2>/dev/null; then
echo "Suspicious placeholder completion text found" >&2
exit 1
fi
echo "Pass end check OK"
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env bash
set -euo pipefail
echo "== GeoIntel Codex preflight =="
required=(
"AGENTS.md"
"docs/CODEX_MASTER_PROMPT.md"
"docs/V1_SCOPE_FREEZE.md"
"docs/M6_AUTONOMY_BOUNDARIES.md"
"docs/M6_QUALITY_GATES.md"
"prompts/codex/PASS_00_REPO_AUDIT.md"
".env.example"
"docker-compose.yml"
)
for path in "${required[@]}"; do
if [[ ! -e "$path" ]]; then
echo "Missing required file: $path" >&2
exit 1
fi
echo "OK: $path"
done
if grep -R "<<<<<<<\|>>>>>>>\|=======" -n . --exclude-dir=.git --exclude='*.zip'; then
echo "Merge conflict markers found" >&2
exit 1
fi
echo "Preflight OK"
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
echo "== Lightweight contract drift grep =="
if grep -R "TODO: decide\|TBD\|placeholder only\|fake real" -n docs contracts backend frontend prompts 2>/dev/null; then
echo "Potential unresolved decision markers found. Review output above."
exit 1
fi
echo "No obvious unresolved decision markers found."
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -euo pipefail
REMOTE_HOST="${REMOTE_HOST:-root@192.168.10.150}"
REMOTE_PATH="${REMOTE_PATH:-/mnt/user/appdata/geointel}"
REMOTE_BRANCH="${REMOTE_BRANCH:-main}"
REMOTE_REPO="${REMOTE_REPO:-gitea-widefrog:NuklearRabbit/geointel.git}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/widefrog_unraid_deploy}"
FRONTEND_URL="${FRONTEND_URL:-http://192.168.10.150:1202}"
BOOTSTRAP="${DEPLOY_BOOTSTRAP:-0}"
ssh_opts=(-o BatchMode=yes -o StrictHostKeyChecking=accept-new)
if [[ -f "$SSH_KEY" ]]; then
ssh_opts+=(-i "$SSH_KEY")
fi
ssh "${ssh_opts[@]}" "$REMOTE_HOST" \
"REMOTE_PATH='$REMOTE_PATH' REMOTE_BRANCH='$REMOTE_BRANCH' REMOTE_REPO='$REMOTE_REPO' FRONTEND_URL='$FRONTEND_URL' DEPLOY_BOOTSTRAP='$BOOTSTRAP' bash -s" <<'REMOTE_SCRIPT'
set -euo pipefail
cd "$REMOTE_PATH"
if [[ ! -d .git ]]; then
if [[ "$DEPLOY_BOOTSTRAP" != "1" ]]; then
echo "No git checkout found in $REMOTE_PATH."
echo "Re-run with DEPLOY_BOOTSTRAP=1 for the first deployment bootstrap."
exit 2
fi
git init
git remote add origin "$REMOTE_REPO"
fi
git fetch origin "$REMOTE_BRANCH"
git checkout -B "$REMOTE_BRANCH" "origin/$REMOTE_BRANCH"
docker compose config >/dev/null
docker compose build backend frontend
docker compose up -d
docker compose ps
if [[ -x scripts/live_migration_smoke.sh ]]; then
bash scripts/live_migration_smoke.sh
fi
if [[ -x scripts/verify_browser_runtime.sh ]]; then
bash scripts/verify_browser_runtime.sh "$FRONTEND_URL"
fi
REMOTE_SCRIPT
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
if ! command -v npm >/dev/null 2>&1; then
echo "Node/npm is required but was not found in PATH." >&2
echo "Please install Node.js 18+ and ensure npm is available." >&2
exit 1
fi
cd "$(dirname "$0")/../frontend"
npm run build
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
if ! command -v npm >/dev/null 2>&1; then
echo "Node/npm is required but was not found in PATH." >&2
echo "Please install Node.js 18+ and ensure npm is available." >&2
exit 1
fi
cd "$(dirname "$0")/../frontend"
npm run start -- --host 0.0.0.0 --port 5173
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
if ! command -v npm >/dev/null 2>&1; then
echo "Node/npm is required but was not found in PATH." >&2
echo "Please install Node.js 18+ and ensure npm is available." >&2
exit 1
fi
cd "$(dirname "$0")/../frontend"
npm install
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
if ! command -v npm >/dev/null 2>&1; then
echo "Node/npm is required but was not found in PATH." >&2
echo "Please install Node.js 18+ and ensure npm is available." >&2
exit 1
fi
cd "$(dirname "$0")/../frontend"
npm run typecheck
+15
View File
@@ -0,0 +1,15 @@
from __future__ import annotations
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
BACKEND_SCRIPTS = ROOT / "backend" / "scripts"
sys.path.insert(0, str(BACKEND_SCRIPTS))
from gis_import_smoke import main # noqa: E402
if __name__ == "__main__":
raise SystemExit(main())
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
PYTHON_BIN="${PYTHON_BIN:-}"
if [ -z "$PYTHON_BIN" ]; then
for candidate in python3 python.exe python; do
if command -v "$candidate" >/dev/null 2>&1 && "$candidate" -c "import sys" >/dev/null 2>&1; then
PYTHON_BIN="$candidate"
break
fi
done
fi
if [ -z "$PYTHON_BIN" ]; then
echo "No usable Python interpreter found. Set PYTHON_BIN=/path/to/python and retry." >&2
exit 1
fi
cd "$ROOT/backend"
echo "== GeoIntel live migration smoke =="
echo "Using Python: $PYTHON_BIN"
echo "Database URL is read from backend settings / DATABASE_URL."
"$PYTHON_BIN" - <<'PY'
from sqlalchemy import text
from app.db.session import get_engine
with get_engine().connect() as connection:
connection.execute(text("SELECT 1"))
print("Database connection: ok")
PY
"$PYTHON_BIN" -m alembic upgrade head
"$PYTHON_BIN" - <<'PY'
from sqlalchemy import text
from app.db.session import get_engine
required_objects = [
"public.projects",
"public.areas",
"public.datasets",
"public.jobs",
"public.vector_features",
"public.quality_checks",
"public.metrics",
"public.analysis_runs",
"public.detections",
"public.segmentations",
"public.ix_vector_features_geometry",
"public.ix_detections_geometry",
"public.ix_segmentations_geometry",
]
with get_engine().connect() as connection:
postgis_version = connection.execute(text("SELECT PostGIS_Version()")).scalar()
print(f"PostGIS version: {postgis_version}")
missing = [
object_name
for object_name in required_objects
if connection.execute(text("SELECT to_regclass(:object_name)"), {"object_name": object_name}).scalar() is None
]
if missing:
raise SystemExit(f"Missing expected migrated schema objects: {', '.join(missing)}")
print("Required runtime schema objects: ok")
PY
HEAD_COUNT="$("$PYTHON_BIN" -m alembic heads | grep -c 'head')"
if [ "$HEAD_COUNT" -ne 1 ]; then
echo "Expected exactly one Alembic head, found $HEAD_COUNT" >&2
"$PYTHON_BIN" -m alembic heads >&2
exit 1
fi
"$PYTHON_BIN" -m alembic heads
echo "== Live migration smoke passed =="
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
set -euo pipefail
echo "== GeoIntel M7 self-review =="
missing=0
for file in \
docs/12-build-control/M7_IMPLEMENTATION_CONTROL_LAYER.md \
docs/11-quality/SELF_REVIEW_CHECKLIST.md \
docs/11-quality/REGRESSION_TRAPS.md \
docs/13-implementation-traps/GEOSPATIAL_CALCULATION_RULES.md \
docs/13-implementation-traps/API_RESPONSE_RULES.md \
docs/12-build-control/CODEX_DECISION_BOUNDARIES.md \
docs/12-build-control/BUILD_SEQUENCE_LOCK.md; do
if [[ ! -f "$file" ]]; then
echo "Missing required M7 file: $file"
missing=1
else
echo "OK: $file"
fi
done
if [[ $missing -ne 0 ]]; then
exit 1
fi
echo "M7 control documents present."
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env python3
from pathlib import Path
import sys
ROOT = Path(__file__).resolve().parents[1]
required = [
"CODEX_START.md",
"README.md",
"docs/00-start/START_HERE.md",
"docs/20-run-readiness/RUN_READINESS_FINAL.md",
"docs/20-run-readiness/PASS_SEQUENCE_FINAL.md",
"docs/20-run-readiness/CODEX_TOMORROW_RUNBOOK.md",
"docs/governance/GEOINTEL_CONSTITUTION.md",
"docs/governance/ARCHITECTURE_INVARIANTS.md",
"docs/governance/FORBIDDEN_DECISIONS.md",
"docs/specs/CANONICAL_DOMAIN_MODELS.md",
"docs/specs/GIS_STANDARDS.md",
"docs/specs/STATE_MACHINES.md",
"docs/workflows/GOLDEN_PATHS.md",
"prompts/codex/final/DAY_1_MASTER_PROMPT.md",
"demo/geel/area_geel_center.geojson",
"demo/geel/reference_buildings.geojson",
"demo/geel/demo_detections.geojson",
]
missing = [p for p in required if not (ROOT / p).exists()]
if missing:
print("Preimplementation audit failed. Missing required files:")
for p in missing:
print(f"- {p}")
sys.exit(1)
print("Preimplementation audit passed.")
print(f"Checked {len(required)} required files.")
+174
View File
@@ -0,0 +1,174 @@
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
from uuid import uuid4
ROOT = Path(__file__).resolve().parents[1]
BACKEND_ROOT = ROOT / "backend"
if str(BACKEND_ROOT) not in sys.path:
sys.path.insert(0, str(BACKEND_ROOT))
from app.models import Area, Dataset, Metric, QualityCheck # noqa: E402
from app.services.qa_service import QaService # noqa: E402
from app.services.quality_service import QualityService # noqa: E402
class BenchmarkSession:
def __init__(self, datasets: list[Dataset]) -> None:
self.datasets = {dataset.id: dataset for dataset in datasets}
self.added: list[object] = []
self.commits = 0
self.refreshes: list[object] = []
def get(self, model, item_id):
if model.__name__ == "Dataset":
return self.datasets.get(item_id)
if model.__name__ == "Area":
return None
return None
def add(self, item) -> None:
self.added.append(item)
def commit(self) -> None:
self.commits += 1
def refresh(self, item) -> None:
self.refreshes.append(item)
def _load_expected() -> dict:
return json.loads((ROOT / "fixtures" / "golden" / "expected_qa_metrics.json").read_text(encoding="utf-8"))
def _dataset(dataset_id, project_id, name: str, path: Path, *, role: str) -> Dataset:
return Dataset(
id=dataset_id,
project_id=project_id,
name=name,
dataset_type="vector",
source="golden_fixture",
dataset_role=role,
source_name="fixture",
reference_layer_name="buildings" if role == "reference" else None,
storage_path=str(path),
crs="EPSG:4326",
metadata_json={"crs_assumed": False},
status="ready",
)
def _assert_close(label: str, actual: float | int | None, expected: float | int, tolerance: float) -> None:
if actual is None:
raise AssertionError(f"{label} is None, expected {expected}")
if abs(float(actual) - float(expected)) > tolerance:
raise AssertionError(f"{label} drifted: actual={actual}, expected={expected}, tolerance={tolerance}")
def run_benchmark() -> dict:
expected = _load_expected()
project_id = uuid4()
candidate_dataset_id = uuid4()
reference_dataset_id = uuid4()
candidate_path = ROOT / expected["candidate_fixture"]
reference_path = ROOT / expected["reference_fixture"]
tolerance = float(expected["tolerance"])
session = BenchmarkSession(
[
_dataset(candidate_dataset_id, project_id, "golden_predicted_buildings.geojson", candidate_path, role="source"),
_dataset(reference_dataset_id, project_id, "golden_reference_buildings.geojson", reference_path, role="reference"),
]
)
result = QaService.compare_candidate_with_reference(
db=session,
project_id=project_id,
candidate_dataset_id=candidate_dataset_id,
reference_dataset_id=reference_dataset_id,
iou_threshold=float(expected["iou_threshold"]),
)
metrics = {
"precision": result.precision,
"recall": result.recall,
"f1": result.f1_score,
"mean_iou": result.mean_iou,
"false_positive_count": result.false_positives,
"false_negative_count": result.false_negatives,
}
_assert_close("candidate_feature_count", result.candidate_feature_count, expected["candidate_feature_count"], 0)
_assert_close("reference_feature_count", result.reference_feature_count, expected["reference_feature_count"], 0)
_assert_close("matches", result.matches, expected["matches"], 0)
_assert_close("false_positive_count", result.false_positives, expected["false_positive_count"], 0)
_assert_close("false_negative_count", result.false_negatives, expected["false_negative_count"], 0)
_assert_close("precision", result.precision, expected["precision"], tolerance)
_assert_close("recall", result.recall, expected["recall"], tolerance)
_assert_close("f1", result.f1_score, expected["f1"], tolerance)
_assert_close("mean_iou", result.mean_iou, expected["mean_iou"], tolerance)
quality_check = QualityService.persist_quality_check(
db=session,
project_id=project_id,
reference_dataset_id=reference_dataset_id,
check_type="golden_candidate_vs_reference",
status=result.status,
score=result.f1_score,
parameters={"iou_threshold": result.iou_threshold, "benchmark_id": expected["benchmark_id"]},
findings={
"matches": result.matches,
"false_positives": result.false_positives,
"false_negatives": result.false_negatives,
},
candidate_dataset_id=candidate_dataset_id,
metrics=metrics,
)
persisted_metrics = [item for item in session.added if isinstance(item, Metric)]
return {
"status": "passed",
"benchmark_id": expected["benchmark_id"],
"metrics": metrics,
"quality_check_id": str(quality_check.id),
"persistence": {
"quality_check_count": len([item for item in session.added if isinstance(item, QualityCheck)]),
"metric_count": len(persisted_metrics),
"metric_keys": [metric.metric_key for metric in persisted_metrics],
"commit_count": session.commits,
},
"fixtures": {
"candidate": expected["candidate_fixture"],
"reference": expected["reference_fixture"],
},
}
def main() -> int:
parser = argparse.ArgumentParser(description="Run GeoIntel golden QA/QC benchmark.")
parser.add_argument("--json", action="store_true", help="Print machine-readable JSON only.")
args = parser.parse_args()
try:
payload = run_benchmark()
except Exception as exc:
if args.json:
print(json.dumps({"status": "failed", "error": str(exc)}, indent=2))
else:
print(f"Golden QA/QC benchmark failed: {exc}", file=sys.stderr)
return 1
if args.json:
print(json.dumps(payload, indent=2, sort_keys=True))
else:
print("GeoIntel golden QA/QC benchmark passed")
print(json.dumps(payload, indent=2, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
if [ -n "${PYTHON_BIN:-}" ]; then
PYTHON_BIN="${PYTHON_BIN}"
else
PYTHON_BIN=""
for candidate in python3 python.exe python; do
if command -v "${candidate}" >/dev/null 2>&1 && "${candidate}" -c "import sys, pytest" >/dev/null 2>&1; then
PYTHON_BIN="${candidate}"
break
fi
done
if [ -z "${PYTHON_BIN}" ]; then
for candidate in python3 python.exe python; do
if command -v "${candidate}" >/dev/null 2>&1 && "${candidate}" -c "import sys" >/dev/null 2>&1; then
PYTHON_BIN="${candidate}"
break
fi
done
fi
fi
if [ -z "${PYTHON_BIN}" ]; then
echo "No usable python interpreter found" >&2
exit 1
fi
echo "== GeoIntel run readiness check =="
echo "Using Python: ${PYTHON_BIN}"
bash scripts/check_repo_structure.sh
${PYTHON_BIN} scripts/smoke_docs.py
${PYTHON_BIN} scripts/validate_fixtures.py
${PYTHON_BIN} scripts/smoke_contracts.py
${PYTHON_BIN} scripts/preimplementation_audit.py
${PYTHON_BIN} scripts/validate_m13_codex_assets.py
${PYTHON_BIN} scripts/validate_m14_launch_assets.py
${PYTHON_BIN} -m py_compile scripts/gis_import_smoke.py
${PYTHON_BIN} -m py_compile scripts/seed_demo_workflow.py
${PYTHON_BIN} -m compileall backend/app
(cd backend && ${PYTHON_BIN} -m pytest -W error::DeprecationWarning)
(cd backend && ${PYTHON_BIN} -m alembic heads)
(cd frontend && npm run typecheck)
(cd frontend && npm run build)
bash -n scripts/live_migration_smoke.sh
bash -n scripts/verify_browser_runtime.sh
bash -n scripts/verify_demo_export_workflow.sh
bash -n scripts/verify_gis_runtime.sh
echo "== Run readiness check passed =="
+38
View File
@@ -0,0 +1,38 @@
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "backend"))
from app.db.session import SessionLocal
from app.services.demo_workflow_service import DemoWorkflowService
def main() -> int:
parser = argparse.ArgumentParser(description="Seed the explicit GeoIntel offline demo workflow.")
parser.add_argument("--json", action="store_true", help="Print machine-readable JSON.")
args = parser.parse_args()
with SessionLocal() as db:
result = DemoWorkflowService.seed(db)
payload = result.model_dump(mode="json")
if args.json:
print(json.dumps(payload, sort_keys=True))
else:
print(f"Demo workflow status: {payload['status']}")
print(f"Project: {payload['project_id']}")
print(f"Area: {payload['area_id']}")
print(f"Reference dataset: {payload['reference_dataset_id']}")
print(f"Candidate dataset: {payload['candidate_dataset_id']}")
print(f"Quality check: {payload['quality_check_id']}")
print(payload["message"])
return 0
if __name__ == "__main__":
raise SystemExit(main())
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail
if [ ! -d "backend" ]; then echo "backend directory missing" >&2; exit 1; fi
if [ -n "${PYTHON_BIN:-}" ]; then
PYTHON_BIN="${PYTHON_BIN}"
elif command -v python3 >/dev/null 2>&1 && python3 -c "import importlib; import app.main" >/dev/null 2>&1; then
PYTHON_BIN="python3"
elif command -v python.exe >/dev/null 2>&1 && python.exe -c "import importlib; import app.main" >/dev/null 2>&1; then
PYTHON_BIN="python.exe"
elif command -v python >/dev/null 2>&1 && python -c "import importlib; import app.main" >/dev/null 2>&1; then
PYTHON_BIN="python"
else
echo "No python interpreter available for backend import smoke" >&2
exit 1
fi
if [ -f "backend/app/main.py" ]; then
(cd backend && "$PYTHON_BIN" -c "import importlib; import app.main; importlib.import_module('app.main'); print('Backend import OK')")
else
echo "backend/app/main.py not implemented yet; smoke skipped for documentation-only milestone"
fi
+5
View File
@@ -0,0 +1,5 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
missing=[d for d in ["contracts/api","contracts/database","contracts/events"] if not (ROOT/d).exists()]
if missing: raise SystemExit("Missing contract directories: "+", ".join(missing))
print("Contracts smoke OK")
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail
echo "GeoIntel Day 1 smoke script"
echo "This script is intentionally conservative. It should be updated by Codex once package managers and commands exist."
if [ -d backend ]; then
echo "[backend] directory exists"
else
echo "[backend] missing" >&2
exit 1
fi
if [ -d frontend ]; then
echo "[frontend] directory exists"
else
echo "[frontend] missing" >&2
exit 1
fi
if [ -f README.md ]; then
echo "[docs] README present"
else
echo "[docs] README missing" >&2
exit 1
fi
echo "Smoke scaffold complete. Codex must extend this with real test/build commands during implementation."
+6
View File
@@ -0,0 +1,6 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
required_docs = ["docs/M5_OPERATIONAL_READINESS.md","docs/BUILD_GOVERNANCE.md","docs/CI_CD_SPECIFICATION.md","docs/HEALTHCHECK_CONTRACTS.md","docs/TROUBLESHOOTING_RUNBOOK.md","docs/RELEASE_PROCESS.md","docs/ROLLBACK_AND_RECOVERY.md"]
missing=[p for p in required_docs if not (ROOT/p).exists()]
if missing: raise SystemExit("Missing docs:\n"+"\n".join(missing))
print("Documentation smoke OK")
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
echo "GeoIntel M10 smoke placeholder"
echo "Run backend tests, frontend build, and docs checks after implementation."
if [ -d backend ]; then
echo "backend directory present"
fi
if [ -d frontend ]; then
echo "frontend directory present"
fi
if [ -f docs/18-ultra-prep/CODEX_START_HERE.md ]; then
echo "M10 docs present"
fi
+9
View File
@@ -0,0 +1,9 @@
from pathlib import Path
import json
ROOT=Path(__file__).resolve().parents[1]
fixture_dir=ROOT/"fixtures"/"geojson"
if not fixture_dir.exists(): raise SystemExit("fixtures/geojson missing")
for path in fixture_dir.glob("*.geojson"):
data=json.loads(path.read_text(encoding="utf-8"))
if data.get("type") != "FeatureCollection": raise SystemExit(f"{path} is not a FeatureCollection")
print("Fixture validation OK")
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env python3
from pathlib import Path
import sys
ROOT = Path(__file__).resolve().parents[1]
required = [
"docs/30-codex-optimization/CODEX_OPTIMIZATION_OVERVIEW.md",
"docs/30-codex-optimization/CODEX_RUN_CHECKLIST.md",
"docs/30-codex-optimization/PROMPT_DISCIPLINE.md",
"docs/30-codex-optimization/TOKEN_BUDGET_POLICY.md",
"docs/30-codex-optimization/SECRETS_AND_ENV_POLICY.md",
"docs/30-codex-optimization/PARALLEL_AGENT_STRATEGY.md",
"docs/30-codex-optimization/CODEX_SKILLS_INDEX.md",
"prompts/codex/m13/DAY_1_OPTIMIZED_MASTER_PROMPT.md",
"prompts/codex/m13/PASS_COMPLETION_REPORT_PROMPT.md",
"prompts/codex/m13/PARALLEL_AGENT_COORDINATION_PROMPT.md",
]
skills = [
"geoai-backend-build",
"postgis-migration",
"raster-pipeline",
"vector-processing",
"frontend-maplibre-workbench",
"qaqc-review",
"codex-pass-review",
]
missing = []
for item in required:
p = ROOT / item
if not p.exists() or p.stat().st_size == 0:
missing.append(item)
for skill in skills:
p = ROOT / "skills" / skill / "SKILL.md"
if not p.exists() or p.stat().st_size == 0:
missing.append(str(p.relative_to(ROOT)))
if missing:
print("M13 validation failed. Missing/empty assets:")
for item in missing:
print(f"- {item}")
sys.exit(1)
print("M13 Codex optimization assets OK")
+45
View File
@@ -0,0 +1,45 @@
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
required = [
'docs/40-build-launch/BUILD_SUCCESS_DEFINITION.md',
'docs/40-build-launch/SPRINT_1_SCOPE_FREEZE.md',
'docs/40-build-launch/DATA_ACQUISITION_PLAYBOOK.md',
'docs/40-build-launch/GOLDEN_DATASET_PACKAGE.md',
'docs/40-build-launch/BUILD_ORDER_GRAPH.md',
'docs/40-build-launch/MODULE_ACCEPTANCE_CRITERIA.md',
'docs/40-build-launch/CODEX_STOP_RULES.md',
'docs/40-build-launch/RELEASE_STRATEGY.md',
'docs/40-build-launch/RISK_REGISTER.md',
'docs/40-build-launch/BACKLOG_PRIORITIES_MOSCOW.md',
'docs/40-build-launch/FOLDER_OWNERSHIP.md',
'prompts/codex/m14/CODEX_FIRST_DAY_MASTER_PROMPT.md',
'checklists/SPRINT_1_OPERATOR_CHECKLIST.md',
'release/v0.1-foundation-target.md',
]
missing = [p for p in required if not (ROOT / p).exists()]
if missing:
raise SystemExit('Missing M14 launch assets:\n' + '\n'.join(missing))
prompt = (ROOT / 'prompts/codex/m14/CODEX_FIRST_DAY_MASTER_PROMPT.md').read_text(encoding='utf-8')
required_terms = [
'Sprint 1 only',
'Do not build yet',
'YOLO live inference',
'CODEX_STOP_RULES',
'BUILD_SUCCESS_DEFINITION',
]
missing_terms = [term for term in required_terms if term not in prompt]
if missing_terms:
raise SystemExit('M14 prompt missing expected control terms: ' + ', '.join(missing_terms))
start = (ROOT / 'CODEX_START.md').read_text(encoding='utf-8')
if 'prompts/codex/m14/CODEX_FIRST_DAY_MASTER_PROMPT.md' not in start:
raise SystemExit('CODEX_START.md does not point to M14 first-day prompt')
readme = (ROOT / 'README.md').read_text(encoding='utf-8')
if 'M14 — Build Launch Package' not in readme:
raise SystemExit('README.md does not identify M14 as current milestone')
print('M14 launch assets OK')
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -euo pipefail
FRONTEND_URL="${1:-http://localhost:1202}"
BACKEND_HEALTH_URL="${2:-}"
API_URL="${FRONTEND_URL%/}/api/v1/projects"
if ! command -v curl >/dev/null 2>&1; then
echo "curl is required for browser runtime verification" >&2
exit 1
fi
echo "== GeoIntel browser runtime verification =="
echo "Frontend: ${FRONTEND_URL}"
echo "API through frontend proxy: ${API_URL}"
frontend_status="$(curl -fsS -o /dev/null -w "%{http_code}" "${FRONTEND_URL}")"
if [ "${frontend_status}" != "200" ]; then
echo "Frontend returned HTTP ${frontend_status}, expected 200" >&2
exit 1
fi
api_response="$(curl -fsS "${API_URL}")"
case "${api_response}" in
*"<!doctype html"*|*"<html"*)
echo "Frontend API proxy returned HTML instead of the backend JSON envelope." >&2
echo "Rebuild/restart the frontend container so Vite loads the /api proxy config." >&2
exit 1
;;
esac
if ! printf '%s' "${api_response}" | grep -q '"data"'; then
echo "API response does not look like the canonical GeoIntel envelope:" >&2
printf '%s\n' "${api_response}" >&2
exit 1
fi
if [ -n "${BACKEND_HEALTH_URL}" ]; then
echo "Backend health: ${BACKEND_HEALTH_URL}"
backend_health="$(curl -fsS "${BACKEND_HEALTH_URL}")"
if ! printf '%s' "${backend_health}" | grep -q '"status":"ok"'; then
echo "Backend health response is not healthy:" >&2
printf '%s\n' "${backend_health}" >&2
exit 1
fi
fi
echo "Browser runtime verification passed"
+131
View File
@@ -0,0 +1,131 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${1:-http://localhost:1202}"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "${TMP_DIR}"' EXIT
if ! command -v curl >/dev/null 2>&1; then
echo "curl is required for demo/export workflow verification" >&2
exit 1
fi
if [ -n "${PYTHON_BIN:-}" ]; then
PYTHON_BIN="${PYTHON_BIN}"
else
PYTHON_BIN=""
for candidate in python3 python.exe python; do
if command -v "${candidate}" >/dev/null 2>&1 && "${candidate}" -c "import json, sys" >/dev/null 2>&1; then
PYTHON_BIN="${candidate}"
break
fi
done
fi
if [ -z "${PYTHON_BIN}" ]; then
echo "A Python interpreter is required for JSON parsing" >&2
exit 1
fi
json_field() {
local file_path="$1"
local expression="$2"
"${PYTHON_BIN}" - "$file_path" "$expression" <<'PY'
import json
import sys
path, expression = sys.argv[1], sys.argv[2]
with open(path, "r", encoding="utf-8") as handle:
payload = json.load(handle)
value = payload
for part in expression.split("."):
if part:
value = value[part]
print(value)
PY
}
require_json_data() {
local file_path="$1"
"${PYTHON_BIN}" - "$file_path" <<'PY'
import json
import sys
with open(sys.argv[1], "r", encoding="utf-8") as handle:
payload = json.load(handle)
if "data" not in payload:
raise SystemExit("Response is not a canonical GeoIntel data envelope")
PY
}
echo "== GeoIntel demo/export workflow verification =="
echo "Base URL: ${BASE_URL}"
curl -fsS "${BASE_URL%/}/health" > "${TMP_DIR}/health.json"
if ! grep -q '"status":"ok"' "${TMP_DIR}/health.json"; then
echo "Health endpoint did not return ok:" >&2
cat "${TMP_DIR}/health.json" >&2
exit 1
fi
curl -fsS -X POST "${BASE_URL%/}/api/v1/demo/workflow" > "${TMP_DIR}/demo.json"
require_json_data "${TMP_DIR}/demo.json"
project_id="$(json_field "${TMP_DIR}/demo.json" "data.project_id")"
candidate_dataset_id="$(json_field "${TMP_DIR}/demo.json" "data.candidate_dataset_id")"
curl -fsS "${BASE_URL%/}/api/v1/projects/${project_id}/quality-checks" > "${TMP_DIR}/quality_checks.json"
require_json_data "${TMP_DIR}/quality_checks.json"
quality_count="$(json_field "${TMP_DIR}/quality_checks.json" "data.total")"
if [ "${quality_count}" -lt 1 ]; then
echo "Expected at least one persisted QA/QC result after demo workflow" >&2
exit 1
fi
curl -fsS -X POST "${BASE_URL%/}/api/v1/exports/metadata" \
-H "Content-Type: application/json" \
-d "{\"project_id\":\"${project_id}\"}" > "${TMP_DIR}/metadata_export.json"
require_json_data "${TMP_DIR}/metadata_export.json"
metadata_export_id="$(json_field "${TMP_DIR}/metadata_export.json" "data.export_id")"
curl -fsS -X POST "${BASE_URL%/}/api/v1/exports/report" \
-H "Content-Type: application/json" \
-d "{\"project_id\":\"${project_id}\"}" > "${TMP_DIR}/report_export.json"
require_json_data "${TMP_DIR}/report_export.json"
report_export_id="$(json_field "${TMP_DIR}/report_export.json" "data.export_id")"
curl -fsS -X POST "${BASE_URL%/}/api/v1/exports/geojson" \
-H "Content-Type: application/json" \
-d "{\"export_kind\":\"dataset\",\"dataset_id\":\"${candidate_dataset_id}\"}" > "${TMP_DIR}/dataset_export.json"
require_json_data "${TMP_DIR}/dataset_export.json"
dataset_export_id="$(json_field "${TMP_DIR}/dataset_export.json" "data.export_id")"
curl -fsS "${BASE_URL%/}/api/v1/exports/projects/${project_id}/exports" > "${TMP_DIR}/exports.json"
require_json_data "${TMP_DIR}/exports.json"
export_count="$(json_field "${TMP_DIR}/exports.json" "data.total")"
if [ "${export_count}" -lt 3 ]; then
echo "Expected at least three exports after workflow smoke, found ${export_count}" >&2
exit 1
fi
curl -fsS "${BASE_URL%/}/api/v1/exports/${metadata_export_id}/content" > "${TMP_DIR}/metadata_content.json"
require_json_data "${TMP_DIR}/metadata_content.json"
if ! grep -q '"quality_checks"' "${TMP_DIR}/metadata_content.json"; then
echo "Metadata export content does not include QA/QC summary" >&2
exit 1
fi
curl -fsS "${BASE_URL%/}/api/v1/exports/${dataset_export_id}/download" > "${TMP_DIR}/dataset_download.geojson"
if ! grep -q '"FeatureCollection"' "${TMP_DIR}/dataset_download.geojson"; then
echo "Dataset GeoJSON download is not a FeatureCollection" >&2
exit 1
fi
curl -fsS "${BASE_URL%/}/api/v1/exports/${report_export_id}/download" > "${TMP_DIR}/report_download.html"
if ! grep -qi '<!doctype html>' "${TMP_DIR}/report_download.html"; then
echo "Report download is not an HTML artifact" >&2
exit 1
fi
echo "Demo/export workflow verification passed"
echo "Project: ${project_id}"
echo "Exports created: metadata=${metadata_export_id}, report=${report_export_id}, dataset=${dataset_export_id}"
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
set -euo pipefail
BASE_URL="${1:-${GE_INTEL_BASE_URL:-http://localhost:1202}}"
CAPABILITIES_URL="${BASE_URL%/}/api/v1/system/capabilities"
echo "== GeoIntel GIS runtime verification =="
echo "Checking: ${CAPABILITIES_URL}"
if ! command -v curl >/dev/null 2>&1; then
echo "curl is required for GIS runtime verification" >&2
exit 1
fi
response="$(curl -fsS "${CAPABILITIES_URL}")"
if printf '%s' "${response}" | grep -qi '<!doctype html'; then
echo "Capabilities endpoint returned HTML; frontend proxy/API routing is not correct" >&2
exit 1
fi
if ! printf '%s' "${response}" | grep -q '"postgis":true'; then
echo "PostGIS capability is not reported as available" >&2
printf '%s\n' "${response}" >&2
exit 1
fi
if ! printf '%s' "${response}" | grep -q '"rasterio":true'; then
echo "Rasterio capability is not reported as available in the running backend image" >&2
printf '%s\n' "${response}" >&2
exit 1
fi
if ! printf '%s' "${response}" | grep -q '"geopandas":true'; then
echo "GeoPandas capability is not reported as available in the running backend image" >&2
printf '%s\n' "${response}" >&2
exit 1
fi
echo "GIS runtime capabilities are available."
+52
View File
@@ -0,0 +1,52 @@
from __future__ import annotations
import argparse
import json
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
BACKEND_ROOT = ROOT / "backend"
if str(BACKEND_ROOT) not in sys.path:
sys.path.insert(0, str(BACKEND_ROOT))
from app.core.config import Settings # noqa: E402
from app.services.yolo_preflight_service import YoloPreflightService # noqa: E402
def main() -> int:
parser = argparse.ArgumentParser(description="Run local YOLO configuration preflight without loading a model.")
parser.add_argument("--model-path", help="Existing local YOLO model path.")
parser.add_argument("--tile-manifest-path", help="Existing raster tile manifest path.")
parser.add_argument("--enabled", action="store_true", help="Treat YOLO as enabled for this preflight.")
parser.add_argument("--max-tiles", type=int, default=100, help="Maximum tile count allowed by preflight.")
parser.add_argument(
"--assume-dependencies",
action="store_true",
help="Skip checking installed ultralytics/torch packages; useful for validating local paths on non-AI machines.",
)
parser.add_argument("--json", action="store_true", help="Print JSON output only.")
args = parser.parse_args()
settings = Settings(
yolo_enabled=args.enabled or bool(args.model_path),
yolo_model_path=args.model_path,
yolo_max_tiles=args.max_tiles,
)
payload = YoloPreflightService.run(
settings=settings,
tile_manifest_path=args.tile_manifest_path,
assume_dependencies=args.assume_dependencies,
)
if args.json:
print(json.dumps(payload, indent=2, sort_keys=True))
else:
print("GeoIntel YOLO preflight")
print(json.dumps(payload, indent=2, sort_keys=True))
return 0 if payload["status"] in {"ready", "not_configured", "dependency_unavailable"} else 1
if __name__ == "__main__":
raise SystemExit(main())