fffiloni commited on
Commit
fc6204d
·
verified ·
1 Parent(s): 9126eca

Upload 4 files

Browse files
Files changed (2) hide show
  1. README.md +7 -6
  2. app.py +232 -27
README.md CHANGED
@@ -133,15 +133,16 @@ See `CHANGELOG_V194.md` for the Model Pre-scan Decision Card UI pass.
133
  See `CHANGELOG_V195.md` for the Runtime CSS Cleanup with Legacy Safety Net pass.
134
 
135
 
136
- Current release: Agentic Space Factory v198.26.119.
137
 
138
- ## v198.26.119Run Stats Badge Reconciliation
139
 
140
- - Aligns the compact Run Stats badge with the detailed Build Status card.
141
- - Shows `running` in the compact badge only for active Build Runs, not stale/non-terminal linked validation summaries.
142
- - Keeps linked Space Test counts visible while leaving linked-test lifecycle detail in the dedicated stats band.
 
143
 
144
- See `CHANGELOG_V198_26_119.md`.
145
 
146
  ## v198.26.118 — Runtime Risk Hints Advisory Layer
147
 
 
133
  See `CHANGELOG_V195.md` for the Runtime CSS Cleanup with Legacy Safety Net pass.
134
 
135
 
136
+ Current release: Agentic Space Factory v198.26.121.
137
 
138
+ ## v198.26.121Target Space Identity Override
139
 
140
+ - Adds an auditable Target Space override for Build Runs whose generated Space was renamed manually in Hugging Face settings.
141
+ - Preserves the original generated target Space while using the effective override for future Space Tests and quick links.
142
+ - Keeps validation linked to the selected Build Run: arbitrary target edits are still rejected unless the override was explicitly saved first.
143
+ - Excludes `TARGET_SPACE_OVERRIDE.json` from target Space release payloads while preserving it in the run bucket.
144
 
145
+ See `CHANGELOG_V198_26_121.md`.
146
 
147
  ## v198.26.118 — Runtime Risk Hints Advisory Layer
148
 
app.py CHANGED
@@ -11,7 +11,13 @@ from fastapi import FastAPI, HTTPException, Request
11
  from fastapi.responses import HTMLResponse, JSONResponse, FileResponse, RedirectResponse
12
  from huggingface_hub import HfApi, attach_huggingface_oauth
13
 
14
- from src.bucket import RunPaths, check_user_bucket, create_user_bucket, delete_run_folder, read_run_bundle, list_recent_runs, read_json, upsert_run_index_entry, write_json, write_launch_metadata
 
 
 
 
 
 
15
  from src.config import settings, user_bucket_source
16
  from src.eval_config import activate_eval_archive_config, disable_eval_archive_config, flush_eval_archive_records, public_eval_config
17
  from src.eval_archive import maybe_publish_eval_record
@@ -328,10 +334,10 @@ def _target_space_from_events(events: list[dict[str, Any]] | None, *, require_su
328
  def _resolve_target_space_identity(run_id: str | None, bundle: dict[str, Any], *, bucket_source: str | None, job_url: str | None = None) -> dict[str, Any]:
329
  """Return canonical Space links from all stable run sources.
330
 
331
- v198.26.4: buttons stayed disabled when summary.target_space_url was empty
332
- even though create_space/upload_files had succeeded. Resolve links from
333
- summary/state/launch/runtime_upload_epoch/hardware events and expose a
334
- single contract to the UI.
335
  """
336
  summary = bundle.get("summary") or bundle.get("summary_file") or {}
337
  state = bundle.get("state") or {}
@@ -340,7 +346,7 @@ def _resolve_target_space_identity(run_id: str | None, bundle: dict[str, Any], *
340
  explicit_url = _first_non_empty(
341
  summary.get("target_space_url"), state.get("target_space_url"), launch.get("target_space_url")
342
  )
343
- target = _normalize_target_space_id(
344
  _first_non_empty(
345
  summary.get("target_space"), summary.get("target_space_id"),
346
  state.get("target_space"), state.get("target_space_id"),
@@ -349,21 +355,20 @@ def _resolve_target_space_identity(run_id: str | None, bundle: dict[str, Any], *
349
  _target_space_from_events(bundle.get("events") or []),
350
  )
351
  )
352
- if explicit_url and not target:
353
- target = _normalize_target_space_id(explicit_url)
354
- target_space_url = explicit_url or (f"https://huggingface.co/spaces/{target}" if target else "")
 
 
355
  target_space_settings_url = f"{target_space_url}/settings" if target_space_url else ""
356
  events = bundle.get("events") or []
357
  space_runtime = bundle.get("space_runtime") or {}
358
  runtime_history = runtime_upload_epoch.get("history") if isinstance(runtime_upload_epoch.get("history"), list) else []
359
- create_event_ok = bool(target and any(str((e or {}).get("step") or "") in {"create_space", "create_space_hardware"} and str((e or {}).get("status") or "").lower() == "success" for e in events if isinstance(e, dict)))
360
  upload_event_ok = bool(any(str((e or {}).get("step") or "") in {"upload_files", "runtime_upload_epoch"} and str((e or {}).get("status") or "").lower() == "success" for e in events if isinstance(e, dict)))
361
  runtime_uploaded = bool(runtime_upload_epoch.get("last_upload_completed_at") or runtime_upload_epoch.get("upload_sequence") or runtime_history or upload_event_ok)
362
  space_runtime_known = bool(isinstance(space_runtime, dict) and (space_runtime.get("stage") or space_runtime.get("status") or space_runtime.get("runtime_status") or space_runtime.get("updated_at") or space_runtime.get("url")))
363
- # v198.26.9: Space links are durable run facts, not a projection of the
364
- # latest 100 events. A runtime upload epoch or a persisted runtime probe
365
- # proves that the Space existed even if the final outcome later failed.
366
- space_created = bool(target and (create_event_ok or runtime_uploaded or space_runtime_known))
367
  sources = []
368
  if create_event_ok:
369
  sources.append("create_space_event")
@@ -375,14 +380,21 @@ def _resolve_target_space_identity(run_id: str | None, bundle: dict[str, Any], *
375
  sources.append("space_runtime")
376
  if explicit_url:
377
  sources.append("explicit_target_space_url")
378
- links_ready = bool(target_space_url and (space_created or runtime_uploaded or space_runtime_known))
 
 
379
  return {
380
- "schema_version": "space_identity.v198_26_9",
381
  "target_space": target,
382
  "target_space_id": target,
383
  "target_space_known": bool(target),
384
  "target_space_url": target_space_url,
385
  "target_space_settings_url": target_space_settings_url,
 
 
 
 
 
386
  "space_created": bool(space_created),
387
  "runtime_uploaded": bool(runtime_uploaded),
388
  "space_uploaded": bool(runtime_uploaded),
@@ -406,6 +418,131 @@ def _api_links(*, run_id: str | None, bucket_source: str | None, target_space: s
406
  }
407
 
408
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
  def _job_id_from_run_bundle(summary: dict[str, Any], launch: dict[str, Any], state: dict[str, Any]) -> str:
410
  return str(summary.get("job_id") or launch.get("job_id") or state.get("job_id") or "").strip()
411
 
@@ -600,13 +737,17 @@ def _build_live_run_snapshot(
600
  view_bundle["eval_publish_status"] = eval_publish
601
  space_identity = _resolve_target_space_identity(run_id, view_bundle, bucket_source=bucket_source, job_url=effective_state.get("job_url") or effective_summary.get("job_url"))
602
  if space_identity.get("target_space"):
603
- effective_state.setdefault("target_space", space_identity["target_space"])
604
- effective_state.setdefault("target_space_id", space_identity["target_space"])
605
- effective_summary.setdefault("target_space", space_identity["target_space"])
606
- effective_summary.setdefault("target_space_id", space_identity["target_space"])
 
 
 
 
607
  if space_identity.get("target_space_url"):
608
- effective_state.setdefault("target_space_url", space_identity["target_space_url"])
609
- effective_summary.setdefault("target_space_url", space_identity["target_space_url"])
610
  view_bundle["state"] = effective_state
611
  view_bundle["summary"] = effective_summary
612
  view_bundle["space_identity"] = space_identity
@@ -1036,17 +1177,26 @@ def register_custom_routes(fastapi_app: FastAPI) -> None:
1036
  parent_bundle = read_run_bundle(parent_build_run_id, bucket_source=bucket_status.get("bucket_source") or user_bucket_source(username=ctx["username"], bucket_name=bucket_name), token=ctx["token"], include_heavy=True)
1037
  if _run_is_validation_like({"run_id": parent_build_run_id, **parent_bundle}):
1038
  raise ValueError("Space Test must be linked to a Build Run, not to another validation run. Select the parent Build Run and retry validation.")
1039
- parent_summary = parent_bundle.get("summary") or {}
1040
- parent_target = parent_summary.get("target_space") or (parent_bundle.get("state") or {}).get("target_space") or (parent_bundle.get("launch") or {}).get("target_space") or ""
1041
- requested_target = str(payload.get("target_space_id") or "").strip()
 
 
 
 
 
1042
  if not parent_target:
1043
  raise ValueError("Linked Space Test requires a parent Build Run with a generated target Space.")
1044
  if requested_target != parent_target:
1045
- raise ValueError("Space Test must remain linked to the selected Build Run target Space. Standalone validation is not supported in this app.")
1046
  parent_view = build_run_view_model(parent_build_run_id, parent_bundle, bucket_source=bucket_status.get("bucket_source") or user_bucket_source(username=ctx["username"], bucket_name=bucket_name))
1047
  space_test_policy = parent_view.get("space_test_policy") or parent_view.get("space_test", {}).get("policy") or {}
1048
  if not space_test_policy.get("enabled"):
1049
  raise ValueError(str(space_test_policy.get("message") or "This Build Run is not eligible for linked Space Test yet."))
 
 
 
 
1050
  payload_source_raw = str(payload.get("payload_source") or "").strip()
1051
  manual_endpoint_override = bool(payload.get("endpoint_override")) or payload_source_raw == "endpoint_registry_manual_override"
1052
  api_name_for_validation = str(payload.get("api_name") or "").strip()
@@ -1070,6 +1220,9 @@ def register_custom_routes(fastapi_app: FastAPI) -> None:
1070
  api_name_for_validation = ""
1071
  requested_validation_mode = space_test_policy.get("mode") or payload.get("validation_mode") or "complete"
1072
  validation_mode_for_launch = "complete" if manual_endpoint_override and str(requested_validation_mode or "").strip().lower() == "replay" else requested_validation_mode
 
 
 
1073
  # Compatibility anchor: "ui_payload_source": payload.get("payload_source") or ""
1074
  validation_launch_payload = {
1075
  "schema_version": "1.0",
@@ -1081,6 +1234,10 @@ def register_custom_routes(fastapi_app: FastAPI) -> None:
1081
  "test_args": test_args,
1082
  "test_kwargs": test_kwargs,
1083
  "validation_mode": validation_mode_for_launch,
 
 
 
 
1084
  "payload_source": (payload.get("payload_source") if manual_endpoint_override else "parent_automatic_smoke") if replay_source else (payload.get("payload_source") or "ui_payload"),
1085
  "endpoint_override": bool(manual_endpoint_override),
1086
  "endpoint_registry_source_run_id": payload.get("endpoint_registry_source_run_id") or "",
@@ -1089,6 +1246,7 @@ def register_custom_routes(fastapi_app: FastAPI) -> None:
1089
  "space_test_policy": {
1090
  "mode": space_test_policy.get("mode"),
1091
  "launch_mode": validation_mode_for_launch,
 
1092
  "requires_endpoint_discovery": bool(space_test_policy.get("requires_endpoint_discovery")),
1093
  "effective_status_on_success": space_test_policy.get("effective_status_on_success"),
1094
  },
@@ -1104,7 +1262,9 @@ def register_custom_routes(fastapi_app: FastAPI) -> None:
1104
  expected_output_type=validation_launch_payload["expected_output_type"],
1105
  live_timeout_seconds=payload.get("live_timeout_seconds") or 1800,
1106
  validation_mode=validation_launch_payload["validation_mode"], # effectively: validation_mode=space_test_policy.get("mode")
1107
- effective_status_on_success=space_test_policy.get("effective_status_on_success") or "validated_after_manual_space_test",
 
 
1108
  parent_replay_source_json=json.dumps(replay_source, ensure_ascii=False) if replay_source and not manual_endpoint_override else None,
1109
  validation_launch_payload_json=json.dumps(validation_launch_payload, ensure_ascii=False),
1110
  run_id=payload.get("run_id"),
@@ -1133,6 +1293,51 @@ def register_custom_routes(fastapi_app: FastAPI) -> None:
1133
  pass
1134
  return JSONResponse(result)
1135
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1136
  @fastapi_app.post("/api/progress/from-events")
1137
  async def api_progress_from_events(payload: dict[str, Any]): # type: ignore[no-untyped-def]
1138
  events = payload.get("events") or []
 
11
  from fastapi.responses import HTMLResponse, JSONResponse, FileResponse, RedirectResponse
12
  from huggingface_hub import HfApi, attach_huggingface_oauth
13
 
14
+ from src.bucket import RunPaths, append_run_event, check_user_bucket, create_user_bucket, delete_run_folder, read_run_bundle, list_recent_runs, read_json, upsert_run_index_entry, write_json, write_launch_metadata
15
+ from src.target_space_identity import (
16
+ TARGET_SPACE_OVERRIDE_FILENAME,
17
+ normalize_target_space_id as normalize_effective_target_space_id,
18
+ target_space_identity_overlay,
19
+ target_space_url as effective_target_space_url,
20
+ )
21
  from src.config import settings, user_bucket_source
22
  from src.eval_config import activate_eval_archive_config, disable_eval_archive_config, flush_eval_archive_records, public_eval_config
23
  from src.eval_archive import maybe_publish_eval_record
 
334
  def _resolve_target_space_identity(run_id: str | None, bundle: dict[str, Any], *, bucket_source: str | None, job_url: str | None = None) -> dict[str, Any]:
335
  """Return canonical Space links from all stable run sources.
336
 
337
+ v198.26.121: apply an auditable Target Space identity override before
338
+ building UI links or validation prefill. The raw/generated target remains
339
+ available as target_space_original for audit; target_space is the effective
340
+ repo id to use for future validations and buttons.
341
  """
342
  summary = bundle.get("summary") or bundle.get("summary_file") or {}
343
  state = bundle.get("state") or {}
 
346
  explicit_url = _first_non_empty(
347
  summary.get("target_space_url"), state.get("target_space_url"), launch.get("target_space_url")
348
  )
349
+ raw_target = _normalize_target_space_id(
350
  _first_non_empty(
351
  summary.get("target_space"), summary.get("target_space_id"),
352
  state.get("target_space"), state.get("target_space_id"),
 
355
  _target_space_from_events(bundle.get("events") or []),
356
  )
357
  )
358
+ if explicit_url and not raw_target:
359
+ raw_target = _normalize_target_space_id(explicit_url)
360
+ overlay = target_space_identity_overlay({**bundle, "summary": summary, "state": state, "launch": launch})
361
+ target = overlay.get("target_space") or raw_target
362
+ target_space_url = overlay.get("target_space_url") or (f"https://huggingface.co/spaces/{target}" if target else "")
363
  target_space_settings_url = f"{target_space_url}/settings" if target_space_url else ""
364
  events = bundle.get("events") or []
365
  space_runtime = bundle.get("space_runtime") or {}
366
  runtime_history = runtime_upload_epoch.get("history") if isinstance(runtime_upload_epoch.get("history"), list) else []
367
+ create_event_ok = bool(raw_target and any(str((e or {}).get("step") or "") in {"create_space", "create_space_hardware"} and str((e or {}).get("status") or "").lower() == "success" for e in events if isinstance(e, dict)))
368
  upload_event_ok = bool(any(str((e or {}).get("step") or "") in {"upload_files", "runtime_upload_epoch"} and str((e or {}).get("status") or "").lower() == "success" for e in events if isinstance(e, dict)))
369
  runtime_uploaded = bool(runtime_upload_epoch.get("last_upload_completed_at") or runtime_upload_epoch.get("upload_sequence") or runtime_history or upload_event_ok)
370
  space_runtime_known = bool(isinstance(space_runtime, dict) and (space_runtime.get("stage") or space_runtime.get("status") or space_runtime.get("runtime_status") or space_runtime.get("updated_at") or space_runtime.get("url")))
371
+ space_created = bool(target and (create_event_ok or runtime_uploaded or space_runtime_known or overlay.get("target_space_override_active")))
 
 
 
372
  sources = []
373
  if create_event_ok:
374
  sources.append("create_space_event")
 
380
  sources.append("space_runtime")
381
  if explicit_url:
382
  sources.append("explicit_target_space_url")
383
+ if overlay.get("target_space_override_active"):
384
+ sources.insert(0, "target_space_override")
385
+ links_ready = bool(target_space_url and (space_created or runtime_uploaded or space_runtime_known or overlay.get("target_space_override_active")))
386
  return {
387
+ "schema_version": "space_identity.v198_26_121",
388
  "target_space": target,
389
  "target_space_id": target,
390
  "target_space_known": bool(target),
391
  "target_space_url": target_space_url,
392
  "target_space_settings_url": target_space_settings_url,
393
+ "target_space_original": overlay.get("target_space_original") or raw_target,
394
+ "target_space_original_url": overlay.get("target_space_original_url") or (f"https://huggingface.co/spaces/{raw_target}" if raw_target else ""),
395
+ "target_space_override_active": bool(overlay.get("target_space_override_active")),
396
+ "target_space_override": overlay.get("target_space_override") or {},
397
+ "target_space_source": overlay.get("target_space_source") or "generated_run",
398
  "space_created": bool(space_created),
399
  "runtime_uploaded": bool(runtime_uploaded),
400
  "space_uploaded": bool(runtime_uploaded),
 
418
  }
419
 
420
 
421
+
422
+
423
+ def _run_target_override_path(run_id: str, bucket_source: str) -> str:
424
+ return f"{RunPaths(run_id, bucket_source=bucket_source).root}/{TARGET_SPACE_OVERRIDE_FILENAME}"
425
+
426
+
427
+ def _target_space_override_response(run_id: str, bucket_source: str, bundle: dict[str, Any]) -> dict[str, Any]:
428
+ identity = _resolve_target_space_identity(run_id, bundle, bucket_source=bucket_source, job_url=(bundle.get("state") or {}).get("job_url") or (bundle.get("summary") or {}).get("job_url") or "")
429
+ return {
430
+ "run_id": run_id,
431
+ "bucket_source": bucket_source,
432
+ "target_space": identity.get("target_space") or "",
433
+ "target_space_id": identity.get("target_space") or "",
434
+ "target_space_url": identity.get("target_space_url") or "",
435
+ "target_space_settings_url": identity.get("target_space_settings_url") or "",
436
+ "target_space_original": identity.get("target_space_original") or "",
437
+ "target_space_original_url": identity.get("target_space_original_url") or "",
438
+ "target_space_override_active": bool(identity.get("target_space_override_active")),
439
+ "target_space_override": identity.get("target_space_override") or {},
440
+ "target_space_source": identity.get("target_space_source") or "generated_run",
441
+ "space_identity": identity,
442
+ "links": {**identity, "run_id": run_id, "bucket_source": bucket_source},
443
+ }
444
+
445
+
446
+ def _assert_override_repo_allowed(*, target_space: str, username: str, token: str | None) -> None:
447
+ owner = target_space.split("/", 1)[0] if "/" in target_space else ""
448
+ if username and owner and owner != username:
449
+ raise ValueError(f"Target Space override must stay in your namespace for now ({username}/...).")
450
+ try:
451
+ HfApi(token=token).repo_info(repo_id=target_space, repo_type="space", token=token)
452
+ except Exception as exc: # noqa: BLE001
453
+ raise ValueError(f"Could not verify target Space `{target_space}`. Make sure it exists and your OAuth token can access it. {redact(str(exc))}") from exc
454
+
455
+
456
+ def _write_target_space_override(
457
+ *,
458
+ run_id: str,
459
+ bucket_source: str,
460
+ username: str,
461
+ token: str | None,
462
+ target_space: str,
463
+ reason: str,
464
+ ) -> dict[str, Any]:
465
+ bundle = read_run_bundle(run_id, bucket_source=bucket_source, token=token, include_heavy=False)
466
+ if _run_is_validation_like({"run_id": run_id, **bundle}):
467
+ raise ValueError("Target Space override can only be set on a Build Run, not on a validation run.")
468
+ original_identity = target_space_identity_overlay(bundle)
469
+ original = original_identity.get("target_space_original") or original_identity.get("target_space") or ""
470
+ payload = {
471
+ "schema_version": "target_space_override.v1",
472
+ "status": "active",
473
+ "parent_build_run_id": run_id,
474
+ "original_target_space_id": original,
475
+ "target_space_id": target_space,
476
+ "target_space": target_space,
477
+ "target_space_url": effective_target_space_url(target_space),
478
+ "target_space_settings_url": f"{effective_target_space_url(target_space)}/settings",
479
+ "reason": reason,
480
+ "set_by": username,
481
+ "verified_repo_exists": True,
482
+ "updated_at": utc_now_iso(),
483
+ }
484
+ write_json(_run_target_override_path(run_id, bucket_source), payload, token=token)
485
+ try:
486
+ append_run_event(
487
+ run_id,
488
+ bucket_source=bucket_source,
489
+ step="target_space_override",
490
+ status="success",
491
+ message="Target Space identity override saved",
492
+ details={"target_space": target_space, "original_target_space": original, "reason": reason},
493
+ token=token,
494
+ )
495
+ except Exception:
496
+ pass
497
+ refreshed = read_run_bundle(run_id, bucket_source=bucket_source, token=token, include_heavy=False)
498
+ response = _target_space_override_response(run_id, bucket_source, refreshed)
499
+ try:
500
+ summary = refreshed.get("summary") or {}
501
+ upsert_run_index_entry(run_id, bucket_source=bucket_source, summary={**summary, **response, "updated_at": utc_now_iso()}, token=token)
502
+ except Exception:
503
+ pass
504
+ return response
505
+
506
+
507
+ def _clear_target_space_override(*, run_id: str, bucket_source: str, username: str, token: str | None, reason: str = "") -> dict[str, Any]:
508
+ bundle = read_run_bundle(run_id, bucket_source=bucket_source, token=token, include_heavy=False)
509
+ if _run_is_validation_like({"run_id": run_id, **bundle}):
510
+ raise ValueError("Target Space override can only be cleared on a Build Run, not on a validation run.")
511
+ current = bundle.get("target_space_override") or {}
512
+ original_identity = target_space_identity_overlay(bundle)
513
+ payload = {
514
+ "schema_version": "target_space_override.v1",
515
+ "status": "cleared",
516
+ "parent_build_run_id": run_id,
517
+ "previous_target_space_id": current.get("target_space_id") or current.get("target_space") or "",
518
+ "original_target_space_id": original_identity.get("target_space_original") or "",
519
+ "reason": reason or "Target Space override cleared by user.",
520
+ "cleared_by": username,
521
+ "updated_at": utc_now_iso(),
522
+ }
523
+ write_json(_run_target_override_path(run_id, bucket_source), payload, token=token)
524
+ try:
525
+ append_run_event(
526
+ run_id,
527
+ bucket_source=bucket_source,
528
+ step="target_space_override",
529
+ status="cleared",
530
+ message="Target Space identity override cleared",
531
+ details={"previous_target_space": payload["previous_target_space_id"], "reason": payload["reason"]},
532
+ token=token,
533
+ )
534
+ except Exception:
535
+ pass
536
+ refreshed = read_run_bundle(run_id, bucket_source=bucket_source, token=token, include_heavy=False)
537
+ response = _target_space_override_response(run_id, bucket_source, refreshed)
538
+ try:
539
+ summary = refreshed.get("summary") or {}
540
+ upsert_run_index_entry(run_id, bucket_source=bucket_source, summary={**summary, **response, "updated_at": utc_now_iso()}, token=token)
541
+ except Exception:
542
+ pass
543
+ return response
544
+
545
+
546
  def _job_id_from_run_bundle(summary: dict[str, Any], launch: dict[str, Any], state: dict[str, Any]) -> str:
547
  return str(summary.get("job_id") or launch.get("job_id") or state.get("job_id") or "").strip()
548
 
 
737
  view_bundle["eval_publish_status"] = eval_publish
738
  space_identity = _resolve_target_space_identity(run_id, view_bundle, bucket_source=bucket_source, job_url=effective_state.get("job_url") or effective_summary.get("job_url"))
739
  if space_identity.get("target_space"):
740
+ effective_state["target_space"] = space_identity["target_space"]
741
+ effective_state["target_space_id"] = space_identity["target_space"]
742
+ effective_state["target_space_original"] = space_identity.get("target_space_original") or effective_state.get("target_space_original") or ""
743
+ effective_state["target_space_override_active"] = bool(space_identity.get("target_space_override_active"))
744
+ effective_summary["target_space"] = space_identity["target_space"]
745
+ effective_summary["target_space_id"] = space_identity["target_space"]
746
+ effective_summary["target_space_original"] = space_identity.get("target_space_original") or effective_summary.get("target_space_original") or ""
747
+ effective_summary["target_space_override_active"] = bool(space_identity.get("target_space_override_active"))
748
  if space_identity.get("target_space_url"):
749
+ effective_state["target_space_url"] = space_identity["target_space_url"]
750
+ effective_summary["target_space_url"] = space_identity["target_space_url"]
751
  view_bundle["state"] = effective_state
752
  view_bundle["summary"] = effective_summary
753
  view_bundle["space_identity"] = space_identity
 
1177
  parent_bundle = read_run_bundle(parent_build_run_id, bucket_source=bucket_status.get("bucket_source") or user_bucket_source(username=ctx["username"], bucket_name=bucket_name), token=ctx["token"], include_heavy=True)
1178
  if _run_is_validation_like({"run_id": parent_build_run_id, **parent_bundle}):
1179
  raise ValueError("Space Test must be linked to a Build Run, not to another validation run. Select the parent Build Run and retry validation.")
1180
+ parent_identity = _resolve_target_space_identity(
1181
+ parent_build_run_id,
1182
+ parent_bundle,
1183
+ bucket_source=bucket_status.get("bucket_source") or user_bucket_source(username=ctx["username"], bucket_name=bucket_name),
1184
+ job_url=(parent_bundle.get("state") or {}).get("job_url") or (parent_bundle.get("summary") or {}).get("job_url"),
1185
+ )
1186
+ parent_target = parent_identity.get("target_space") or ""
1187
+ requested_target = normalize_effective_target_space_id(payload.get("target_space_id") or "")
1188
  if not parent_target:
1189
  raise ValueError("Linked Space Test requires a parent Build Run with a generated target Space.")
1190
  if requested_target != parent_target:
1191
+ raise ValueError("Space Test must remain linked to the selected Build Run effective target Space. Save a Target Space override first if the Space was renamed.")
1192
  parent_view = build_run_view_model(parent_build_run_id, parent_bundle, bucket_source=bucket_status.get("bucket_source") or user_bucket_source(username=ctx["username"], bucket_name=bucket_name))
1193
  space_test_policy = parent_view.get("space_test_policy") or parent_view.get("space_test", {}).get("policy") or {}
1194
  if not space_test_policy.get("enabled"):
1195
  raise ValueError(str(space_test_policy.get("message") or "This Build Run is not eligible for linked Space Test yet."))
1196
+ expert_intervention_required = bool(payload.get("expert_intervention_required"))
1197
+ expert_intervention_note = str(payload.get("expert_intervention_note") or "").strip()
1198
+ if expert_intervention_required and not expert_intervention_note:
1199
+ raise ValueError("Expert intervention acceptance requires a short note describing the manual repair before validation.")
1200
  payload_source_raw = str(payload.get("payload_source") or "").strip()
1201
  manual_endpoint_override = bool(payload.get("endpoint_override")) or payload_source_raw == "endpoint_registry_manual_override"
1202
  api_name_for_validation = str(payload.get("api_name") or "").strip()
 
1220
  api_name_for_validation = ""
1221
  requested_validation_mode = space_test_policy.get("mode") or payload.get("validation_mode") or "complete"
1222
  validation_mode_for_launch = "complete" if manual_endpoint_override and str(requested_validation_mode or "").strip().lower() == "replay" else requested_validation_mode
1223
+ base_validation_mode_for_launch = validation_mode_for_launch
1224
+ if expert_intervention_required:
1225
+ validation_mode_for_launch = "expert_acceptance"
1226
  # Compatibility anchor: "ui_payload_source": payload.get("payload_source") or ""
1227
  validation_launch_payload = {
1228
  "schema_version": "1.0",
 
1234
  "test_args": test_args,
1235
  "test_kwargs": test_kwargs,
1236
  "validation_mode": validation_mode_for_launch,
1237
+ "base_validation_mode": base_validation_mode_for_launch,
1238
+ "expert_intervention_required": expert_intervention_required,
1239
+ "expert_intervention_note": expert_intervention_note,
1240
+ "expert_acceptance_requires_validation": bool(expert_intervention_required),
1241
  "payload_source": (payload.get("payload_source") if manual_endpoint_override else "parent_automatic_smoke") if replay_source else (payload.get("payload_source") or "ui_payload"),
1242
  "endpoint_override": bool(manual_endpoint_override),
1243
  "endpoint_registry_source_run_id": payload.get("endpoint_registry_source_run_id") or "",
 
1246
  "space_test_policy": {
1247
  "mode": space_test_policy.get("mode"),
1248
  "launch_mode": validation_mode_for_launch,
1249
+ "base_launch_mode": base_validation_mode_for_launch,
1250
  "requires_endpoint_discovery": bool(space_test_policy.get("requires_endpoint_discovery")),
1251
  "effective_status_on_success": space_test_policy.get("effective_status_on_success"),
1252
  },
 
1262
  expected_output_type=validation_launch_payload["expected_output_type"],
1263
  live_timeout_seconds=payload.get("live_timeout_seconds") or 1800,
1264
  validation_mode=validation_launch_payload["validation_mode"], # effectively: validation_mode=space_test_policy.get("mode")
1265
+ effective_status_on_success="success_with_expert_intervention" if expert_intervention_required else (space_test_policy.get("effective_status_on_success") or "validated_after_manual_space_test"),
1266
+ expert_intervention_required=expert_intervention_required,
1267
+ expert_intervention_note=expert_intervention_note,
1268
  parent_replay_source_json=json.dumps(replay_source, ensure_ascii=False) if replay_source and not manual_endpoint_override else None,
1269
  validation_launch_payload_json=json.dumps(validation_launch_payload, ensure_ascii=False),
1270
  run_id=payload.get("run_id"),
 
1293
  pass
1294
  return JSONResponse(result)
1295
 
1296
+ @fastapi_app.post("/api/runs/{run_id}/target-space-override")
1297
+ async def api_set_target_space_override(request: Request, run_id: str, payload: dict[str, Any]): # type: ignore[no-untyped-def]
1298
+ ctx = _oauth_context_from_request(request)
1299
+ bucket_name = payload.get("bucket_name") or settings.bucket_name
1300
+ bucket_source = user_bucket_source(username=ctx["username"], bucket_name=bucket_name)
1301
+ run_id = validate_run_id(run_id)
1302
+ target = normalize_effective_target_space_id(payload.get("target_space_id") or payload.get("target_space") or "")
1303
+ if not target:
1304
+ raise HTTPException(status_code=400, detail="Enter a valid Hugging Face Space repo ID like owner/space-name.")
1305
+ reason = str(payload.get("reason") or "").strip()
1306
+ if not reason:
1307
+ raise HTTPException(status_code=400, detail="Target Space override requires a short reason, for example: renamed manually in HF Space settings.")
1308
+ try:
1309
+ _assert_override_repo_allowed(target_space=target, username=ctx["username"], token=ctx["token"])
1310
+ result = _write_target_space_override(
1311
+ run_id=run_id,
1312
+ bucket_source=bucket_source,
1313
+ username=ctx["username"],
1314
+ token=ctx["token"],
1315
+ target_space=target,
1316
+ reason=reason,
1317
+ )
1318
+ return JSONResponse(result)
1319
+ except Exception as exc: # noqa: BLE001
1320
+ raise _json_error(exc) from exc
1321
+
1322
+ @fastapi_app.post("/api/runs/{run_id}/target-space-override/clear")
1323
+ async def api_clear_target_space_override(request: Request, run_id: str, payload: dict[str, Any] | None = None): # type: ignore[no-untyped-def]
1324
+ ctx = _oauth_context_from_request(request)
1325
+ payload = payload or {}
1326
+ bucket_name = payload.get("bucket_name") or settings.bucket_name
1327
+ bucket_source = user_bucket_source(username=ctx["username"], bucket_name=bucket_name)
1328
+ run_id = validate_run_id(run_id)
1329
+ try:
1330
+ result = _clear_target_space_override(
1331
+ run_id=run_id,
1332
+ bucket_source=bucket_source,
1333
+ username=ctx["username"],
1334
+ token=ctx["token"],
1335
+ reason=str(payload.get("reason") or "").strip(),
1336
+ )
1337
+ return JSONResponse(result)
1338
+ except Exception as exc: # noqa: BLE001
1339
+ raise _json_error(exc) from exc
1340
+
1341
  @fastapi_app.post("/api/progress/from-events")
1342
  async def api_progress_from_events(payload: dict[str, Any]): # type: ignore[no-untyped-def]
1343
  events = payload.get("events") or []