@foggy-projects/deepseek-harness-plugin 0.4.0-beta.4 → 0.4.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -18
- package/lib/client.js +10 -13
- package/lib/index.js +27 -32
- package/lib/skill-provider.js +111 -0
- package/package.json +4 -3
- package/skills/foggy-deepseek-onboarding/SKILL.md +39 -22
- package/skills/foggy-deepseek-onboarding/assets/versions.json +2 -2
- package/skills/foggy-deepseek-onboarding/references/onboarding-workflow.md +22 -0
- package/skills/foggy-deepseek-onboarding/scripts/onboarding.py +393 -268
|
@@ -25,7 +25,6 @@ import zipfile
|
|
|
25
25
|
STATE_SCHEMA = "foggy-deepseek-onboarding-install/v1"
|
|
26
26
|
RUNTIME_STATE_SCHEMA = "foggy-deepseek-onboarding-runtime/v1"
|
|
27
27
|
ONBOARDING_STATE_SCHEMA = "foggy-deepseek-onboarding-state/v1"
|
|
28
|
-
CONTEXT_SCHEMA = "foggy-deepseek-harness-context/v1"
|
|
29
28
|
MANAGED_SKILL_SCHEMA = "foggy-managed-skill/v1"
|
|
30
29
|
MANAGED_SKILL_MARKER = ".foggy-managed-skill.json"
|
|
31
30
|
LEGACY_SKILL_MARKER = ".foggy-onboarding-install.json"
|
|
@@ -420,60 +419,13 @@ def write_skill_marker(
|
|
|
420
419
|
return marker
|
|
421
420
|
|
|
422
421
|
|
|
423
|
-
def backup_skill(destination: Path,
|
|
424
|
-
backup_root = project_root / ".foggy" / "onboarding-backups"
|
|
422
|
+
def backup_skill(destination: Path, backup_root: Path) -> Path:
|
|
425
423
|
backup_root.mkdir(parents=True, exist_ok=True)
|
|
426
424
|
backup = backup_root / f"{destination.name}-{dt.datetime.now().strftime('%Y%m%d-%H%M%S-%f')}"
|
|
427
425
|
shutil.move(str(destination), str(backup))
|
|
428
426
|
return backup
|
|
429
427
|
|
|
430
428
|
|
|
431
|
-
def project_context_path(project_root: Path) -> Path:
|
|
432
|
-
return project_root / ".foggy" / "deepseek-harness" / "context.json"
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
def project_relative(path: Path, project_root: Path) -> str:
|
|
436
|
-
try:
|
|
437
|
-
return path.resolve(strict=False).relative_to(project_root.resolve(strict=False)).as_posix()
|
|
438
|
-
except ValueError:
|
|
439
|
-
return str(path)
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
def write_project_context(state: dict) -> Path:
|
|
443
|
-
project_root = normalized(state["projectRoot"])
|
|
444
|
-
install_root = normalized(state["installRoot"])
|
|
445
|
-
data_root = normalized(state["dataRoot"])
|
|
446
|
-
context_path = project_context_path(project_root)
|
|
447
|
-
skills = {}
|
|
448
|
-
for kind in ("onboarding", "analysis"):
|
|
449
|
-
item = state["skills"][kind]
|
|
450
|
-
skill_path = normalized(item["path"])
|
|
451
|
-
skills[kind] = {
|
|
452
|
-
"version": item.get("version"),
|
|
453
|
-
"path": project_relative(skill_path, project_root),
|
|
454
|
-
"absolutePath": str(skill_path),
|
|
455
|
-
"markerPath": project_relative(skill_path / MANAGED_SKILL_MARKER, project_root),
|
|
456
|
-
"managed": bool(item.get("managed")),
|
|
457
|
-
}
|
|
458
|
-
payload = {
|
|
459
|
-
"schemaVersion": CONTEXT_SCHEMA,
|
|
460
|
-
"managedBy": "@foggy-projects/deepseek-harness-plugin",
|
|
461
|
-
"packageVersion": state["packageVersion"],
|
|
462
|
-
"generatedAt": now_utc(),
|
|
463
|
-
"projectRoot": str(project_root),
|
|
464
|
-
"installStatePath": str(install_root / "install-state.json"),
|
|
465
|
-
"runtimeStatePath": str(data_root / "runtime-state.json"),
|
|
466
|
-
"operationProgressPath": str(data_root / "operation-progress.json"),
|
|
467
|
-
"cli": state["cli"],
|
|
468
|
-
"launcher": state["launcher"],
|
|
469
|
-
"skills": skills,
|
|
470
|
-
"securityMode": state["securityMode"],
|
|
471
|
-
"productionReady": False,
|
|
472
|
-
}
|
|
473
|
-
atomic_json(context_path, payload)
|
|
474
|
-
return context_path
|
|
475
|
-
|
|
476
|
-
|
|
477
429
|
def read_json_object(path: Path, label: str) -> dict:
|
|
478
430
|
if not path.is_file():
|
|
479
431
|
raise OnboardingError(f"{label} not found: {path}")
|
|
@@ -496,6 +448,33 @@ def onboarding_state_path(data_root: Path, profile: str) -> Path:
|
|
|
496
448
|
return data_root / "onboarding" / "profiles" / f"{safe_profile(profile)}.json"
|
|
497
449
|
|
|
498
450
|
|
|
451
|
+
def configure_profile_store(data_root: Path, *, create: bool = True) -> Path:
|
|
452
|
+
"""Keep opaque CLI profiles in the persistent, private Foggy data root by default."""
|
|
453
|
+
configured = os.environ.get("FOGGY_RUNTIME_PROFILE_STORE")
|
|
454
|
+
destination = normalized(configured) if configured else data_root / "cli-profiles"
|
|
455
|
+
if not configured:
|
|
456
|
+
os.environ["FOGGY_RUNTIME_PROFILE_STORE"] = str(destination)
|
|
457
|
+
if create:
|
|
458
|
+
destination.mkdir(parents=True, exist_ok=True)
|
|
459
|
+
if os.name != "nt":
|
|
460
|
+
destination.chmod(0o700)
|
|
461
|
+
return destination
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
def resolve_profile_name(data_root: Path, requested: str | None) -> str:
|
|
465
|
+
if requested:
|
|
466
|
+
return safe_profile(requested)
|
|
467
|
+
profiles_dir = data_root / "onboarding" / "profiles"
|
|
468
|
+
candidates = sorted(path.stem for path in profiles_dir.glob("*.json") if PROFILE_PATTERN.fullmatch(path.stem))
|
|
469
|
+
if len(candidates) == 1:
|
|
470
|
+
return candidates[0]
|
|
471
|
+
if not candidates:
|
|
472
|
+
return "default"
|
|
473
|
+
raise OnboardingError(
|
|
474
|
+
"Multiple onboarding profiles exist; pass --profile explicitly: " + ", ".join(candidates)
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
|
|
499
478
|
def read_onboarding_state(data_root: Path, profile: str, required: bool = True) -> dict | None:
|
|
500
479
|
path = onboarding_state_path(data_root, profile)
|
|
501
480
|
if not path.is_file():
|
|
@@ -766,13 +745,13 @@ def safe_extract(zip_path: Path, destination: Path) -> None:
|
|
|
766
745
|
|
|
767
746
|
def install_analysis_skill(
|
|
768
747
|
zip_path: Path,
|
|
769
|
-
|
|
748
|
+
install_root: Path,
|
|
770
749
|
version: str,
|
|
771
750
|
expected_hash: str,
|
|
772
751
|
package_version: str,
|
|
773
752
|
replace: bool,
|
|
774
753
|
) -> dict:
|
|
775
|
-
skills_root =
|
|
754
|
+
skills_root = install_root / "skills"
|
|
776
755
|
destination = skills_root / "foggy-ai-analysis"
|
|
777
756
|
with tempfile.TemporaryDirectory(prefix="foggy-skill-") as temporary:
|
|
778
757
|
extract_root = Path(temporary)
|
|
@@ -804,7 +783,7 @@ def install_analysis_skill(
|
|
|
804
783
|
return {"path": str(destination), "version": version, "digest": marker["installedDigest"], "managed": True, "action": "kept-matching"}
|
|
805
784
|
if not replace:
|
|
806
785
|
raise OnboardingError(f"Analysis Skill is missing, modified, or outdated at {destination}; use the plugin Repair action to back it up and restore it")
|
|
807
|
-
backup_skill(destination,
|
|
786
|
+
backup_skill(destination, install_root / "skill-backups")
|
|
808
787
|
skills_root.mkdir(parents=True, exist_ok=True)
|
|
809
788
|
shutil.copytree(source, destination)
|
|
810
789
|
marker = write_skill_marker(
|
|
@@ -818,46 +797,6 @@ def install_analysis_skill(
|
|
|
818
797
|
return {"path": str(destination), "version": version, "digest": marker["installedDigest"], "managed": True, "action": "installed"}
|
|
819
798
|
|
|
820
799
|
|
|
821
|
-
def install_onboarding_skill(project_root: Path, package_version: str, replace: bool) -> dict:
|
|
822
|
-
destination = project_root / ".agents" / "skills" / "foggy-deepseek-onboarding"
|
|
823
|
-
source = skill_root()
|
|
824
|
-
source_digest = skill_tree_digest(source)
|
|
825
|
-
if source.resolve() == destination.resolve(strict=False):
|
|
826
|
-
return {"path": str(destination), "version": package_version, "digest": source_digest, "managed": True, "action": "already-running-from-target"}
|
|
827
|
-
if destination.exists():
|
|
828
|
-
marker = read_skill_marker(destination)
|
|
829
|
-
actual_digest = skill_tree_digest(destination)
|
|
830
|
-
if (
|
|
831
|
-
marker
|
|
832
|
-
and marker.get("schemaVersion") == MANAGED_SKILL_SCHEMA
|
|
833
|
-
and marker.get("kind") == "onboarding"
|
|
834
|
-
and actual_digest == source_digest
|
|
835
|
-
and marker.get("sourceDigest") in (None, source_digest)
|
|
836
|
-
and marker.get("installedDigest") in (None, source_digest)
|
|
837
|
-
):
|
|
838
|
-
written = write_skill_marker(
|
|
839
|
-
destination,
|
|
840
|
-
kind="onboarding",
|
|
841
|
-
package_version=package_version,
|
|
842
|
-
component_version=package_version,
|
|
843
|
-
source_digest=source_digest,
|
|
844
|
-
)
|
|
845
|
-
return {"path": str(destination), "version": package_version, "digest": written["installedDigest"], "managed": True, "action": "kept-matching"}
|
|
846
|
-
if not replace:
|
|
847
|
-
raise OnboardingError(f"Onboarding Skill is missing, modified, or outdated at {destination}; use the plugin Repair action to back it up and restore it")
|
|
848
|
-
backup_skill(destination, project_root)
|
|
849
|
-
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
850
|
-
shutil.copytree(source, destination)
|
|
851
|
-
marker = write_skill_marker(
|
|
852
|
-
destination,
|
|
853
|
-
kind="onboarding",
|
|
854
|
-
package_version=package_version,
|
|
855
|
-
component_version=package_version,
|
|
856
|
-
source_digest=source_digest,
|
|
857
|
-
)
|
|
858
|
-
return {"path": str(destination), "version": package_version, "digest": marker["installedDigest"], "managed": True, "action": "installed"}
|
|
859
|
-
|
|
860
|
-
|
|
861
800
|
def read_install_state(install_root: Path, required: bool = True) -> dict | None:
|
|
862
801
|
path = install_root / "install-state.json"
|
|
863
802
|
if not path.is_file():
|
|
@@ -884,24 +823,26 @@ def install_command(args: argparse.Namespace) -> dict:
|
|
|
884
823
|
versions = load_versions()
|
|
885
824
|
install_root = normalized(args.install_root or default_install_root())
|
|
886
825
|
data_root = normalized(args.data_root or default_data_root())
|
|
887
|
-
project_root = normalized(args.project_root or Path.cwd())
|
|
888
826
|
cache_dirs = [normalized(item) for item in args.asset_cache_dir]
|
|
889
827
|
components = versions["components"]
|
|
890
828
|
assert_managed_root(install_root, "Install root")
|
|
891
829
|
assert_managed_root(data_root, "Data root")
|
|
892
830
|
if install_root == data_root:
|
|
893
831
|
raise OnboardingError("Install root and data root must be different")
|
|
832
|
+
profile_store = normalized(os.environ.get("FOGGY_RUNTIME_PROFILE_STORE") or data_root / "cli-profiles")
|
|
894
833
|
plan = {
|
|
895
834
|
"schemaVersion": "foggy-deepseek-onboarding-plan/v1",
|
|
896
835
|
"installRoot": str(install_root),
|
|
897
836
|
"dataRoot": str(data_root),
|
|
898
|
-
"
|
|
837
|
+
"profileStore": str(profile_store),
|
|
838
|
+
"workspaceMode": "dsh-session-cwd",
|
|
899
839
|
"versions": {name: value.get("version") for name, value in components.items()},
|
|
900
|
-
"operations": ["install isolated CLI", "verify Launcher assets", "install
|
|
840
|
+
"operations": ["install isolated CLI", "verify Launcher assets", "install global analysis Skill", "write install state"],
|
|
901
841
|
"productionReady": False,
|
|
902
842
|
}
|
|
903
843
|
if args.dry_run:
|
|
904
844
|
return {"success": True, "dryRun": True, "plan": plan}
|
|
845
|
+
profile_store = configure_profile_store(data_root)
|
|
905
846
|
progress = ProgressReporter(
|
|
906
847
|
getattr(args, "progress_file", None),
|
|
907
848
|
getattr(args, "operation_id", None),
|
|
@@ -911,8 +852,6 @@ def install_command(args: argparse.Namespace) -> dict:
|
|
|
911
852
|
progress.update("preflight", 0, "Checking prerequisites")
|
|
912
853
|
if sys.version_info < (3, 11):
|
|
913
854
|
raise OnboardingError(f"Python 3.11+ required, got {sys.version.split()[0]}")
|
|
914
|
-
if not project_root.is_dir():
|
|
915
|
-
raise OnboardingError(f"Project root not found: {project_root}")
|
|
916
855
|
install_root.mkdir(parents=True, exist_ok=True)
|
|
917
856
|
data_root.mkdir(parents=True, exist_ok=True)
|
|
918
857
|
downloads = install_root / "downloads"
|
|
@@ -1017,21 +956,27 @@ def install_command(args: argparse.Namespace) -> dict:
|
|
|
1017
956
|
zip_asset = next(item for item in analysis_assets if item["role"] == "zip")
|
|
1018
957
|
progress.update("analysis-skill", 3, "Installing analysis Skill", fraction=0.9, current_file=zip_asset["file"])
|
|
1019
958
|
analysis_skill = install_analysis_skill(
|
|
1020
|
-
downloads / "skill" / zip_asset["file"],
|
|
959
|
+
downloads / "skill" / zip_asset["file"], install_root, components["analysisSkill"]["version"],
|
|
1021
960
|
zip_asset["sha256"], versions["packageVersion"], args.replace_skill,
|
|
1022
961
|
)
|
|
1023
962
|
progress.update("analysis-skill", 3, "Analysis Skill ready", fraction=1.0)
|
|
1024
|
-
progress.update("workspace-skills", 4, "
|
|
1025
|
-
onboarding_skill =
|
|
1026
|
-
|
|
963
|
+
progress.update("workspace-skills", 4, "Registering native DSH Skills", fraction=0.1)
|
|
964
|
+
onboarding_skill = {
|
|
965
|
+
"path": str(skill_root()),
|
|
966
|
+
"version": versions["packageVersion"],
|
|
967
|
+
"managed": False,
|
|
968
|
+
"action": "provided-by-plugin",
|
|
969
|
+
"provider": "foggy-managed-skills",
|
|
970
|
+
}
|
|
971
|
+
progress.update("workspace-skills", 4, "Native DSH Skills ready", fraction=1.0)
|
|
1027
972
|
state = {
|
|
1028
973
|
"schemaVersion": STATE_SCHEMA,
|
|
1029
974
|
"installedAt": now_utc(),
|
|
1030
975
|
"packageVersion": versions["packageVersion"],
|
|
1031
976
|
"installRoot": str(install_root),
|
|
1032
977
|
"dataRoot": str(data_root),
|
|
1033
|
-
"
|
|
1034
|
-
"
|
|
978
|
+
"profileStore": str(profile_store),
|
|
979
|
+
"workspaceMode": "dsh-session-cwd",
|
|
1035
980
|
"cli": {"version": cli_component["version"], "command": str(cli_command), "mode": cli_mode},
|
|
1036
981
|
"launcher": {"version": components["launcher"]["version"], "path": str(launcher_dir)},
|
|
1037
982
|
"skills": {"onboarding": onboarding_skill, "analysis": analysis_skill},
|
|
@@ -1041,8 +986,6 @@ def install_command(args: argparse.Namespace) -> dict:
|
|
|
1041
986
|
}
|
|
1042
987
|
progress.update("state", 5, "Writing install state", fraction=0.2, current_file="install-state.json")
|
|
1043
988
|
atomic_json(install_root / "install-state.json", state)
|
|
1044
|
-
progress.update("state", 5, "Writing project context", fraction=0.7, current_file=".foggy/deepseek-harness/context.json")
|
|
1045
|
-
context_path = write_project_context(state)
|
|
1046
989
|
progress.finish()
|
|
1047
990
|
ACTIVE_PROGRESS = None
|
|
1048
991
|
return {
|
|
@@ -1051,8 +994,7 @@ def install_command(args: argparse.Namespace) -> dict:
|
|
|
1051
994
|
"statePath": str(install_root / "install-state.json"),
|
|
1052
995
|
"installRoot": str(install_root),
|
|
1053
996
|
"dataRoot": str(data_root),
|
|
1054
|
-
"
|
|
1055
|
-
"contextPath": str(context_path),
|
|
997
|
+
"workspaceMode": "dsh-session-cwd",
|
|
1056
998
|
"cliVersion": cli_component["version"],
|
|
1057
999
|
"launcherVersion": components["launcher"]["version"],
|
|
1058
1000
|
"analysisSkill": analysis_skill,
|
|
@@ -1252,6 +1194,7 @@ def onboarding_context(args: argparse.Namespace, require_runtime: bool = False)
|
|
|
1252
1194
|
install_state = read_install_state(install_root)
|
|
1253
1195
|
data_root = normalized(args.data_root or install_state["dataRoot"])
|
|
1254
1196
|
assert_managed_root(data_root, "Data root")
|
|
1197
|
+
configure_profile_store(data_root, create=False)
|
|
1255
1198
|
runtime_state_path = data_root / "runtime-state.json"
|
|
1256
1199
|
runtime_state = read_json_object(runtime_state_path, "Runtime state") if runtime_state_path.is_file() else None
|
|
1257
1200
|
if runtime_state and runtime_state.get("schemaVersion") != RUNTIME_STATE_SCHEMA:
|
|
@@ -1311,10 +1254,32 @@ def require_opaque_profile_cli(install_state: dict) -> None:
|
|
|
1311
1254
|
)
|
|
1312
1255
|
|
|
1313
1256
|
|
|
1257
|
+
def datasource_entries(payload: dict) -> list[dict]:
|
|
1258
|
+
for item in nested_data_objects(payload):
|
|
1259
|
+
entries = item.get("datasources")
|
|
1260
|
+
if isinstance(entries, list):
|
|
1261
|
+
return [entry for entry in entries if isinstance(entry, dict)]
|
|
1262
|
+
return []
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
def matching_datasource(payload: dict, connection: dict) -> dict | None:
|
|
1266
|
+
expected_name = connection["name"]
|
|
1267
|
+
expected_type = connection["type"].lower()
|
|
1268
|
+
if expected_type == "postgresql":
|
|
1269
|
+
expected_type = "postgres"
|
|
1270
|
+
for entry in datasource_entries(payload):
|
|
1271
|
+
entry_type = str(entry.get("type", "")).lower()
|
|
1272
|
+
if entry_type == "postgresql":
|
|
1273
|
+
entry_type = "postgres"
|
|
1274
|
+
if entry.get("name") == expected_name and entry_type == expected_type:
|
|
1275
|
+
return entry
|
|
1276
|
+
return None
|
|
1277
|
+
|
|
1278
|
+
|
|
1314
1279
|
def onboarding_plan_command(args: argparse.Namespace) -> dict:
|
|
1315
1280
|
install_root, install_state, data_root, runtime_state = onboarding_context(args, require_runtime=False)
|
|
1316
1281
|
profile = safe_profile(args.profile)
|
|
1317
|
-
project_root = normalized(args.project_root or
|
|
1282
|
+
project_root = normalized(args.project_root or Path.cwd())
|
|
1318
1283
|
if not project_root.is_dir():
|
|
1319
1284
|
raise OnboardingError(f"Project root not found: {project_root}")
|
|
1320
1285
|
connection_file = normalized(args.connection_file)
|
|
@@ -1372,7 +1337,9 @@ def onboarding_plan_command(args: argparse.Namespace) -> dict:
|
|
|
1372
1337
|
|
|
1373
1338
|
def require_profile(args: argparse.Namespace, require_runtime: bool = False) -> tuple[dict, dict, Path, dict | None]:
|
|
1374
1339
|
_install_root, install_state, data_root, runtime_state = onboarding_context(args, require_runtime=require_runtime)
|
|
1375
|
-
|
|
1340
|
+
profile = resolve_profile_name(data_root, getattr(args, "profile", None))
|
|
1341
|
+
args.profile = profile
|
|
1342
|
+
state = read_onboarding_state(data_root, profile)
|
|
1376
1343
|
if normalized(state["installRoot"]) != normalized(install_state["installRoot"]):
|
|
1377
1344
|
raise OnboardingError("Onboarding profile belongs to a different install root")
|
|
1378
1345
|
return state, install_state, data_root, runtime_state
|
|
@@ -1418,10 +1385,31 @@ def datasource_configure_command(args: argparse.Namespace) -> dict:
|
|
|
1418
1385
|
label = "datasources add"
|
|
1419
1386
|
if args.replace:
|
|
1420
1387
|
command.append("--replace")
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1388
|
+
already_present = False
|
|
1389
|
+
try:
|
|
1390
|
+
result = redact_connection_material(
|
|
1391
|
+
cli_json(install_state, runtime_state, connection["namespace"], command, label)
|
|
1392
|
+
)
|
|
1393
|
+
except OnboardingError as exc:
|
|
1394
|
+
if "DATASOURCE_ALREADY_EXISTS" not in str(exc):
|
|
1395
|
+
raise
|
|
1396
|
+
listed = cli_json(
|
|
1397
|
+
install_state, runtime_state, connection["namespace"], ["datasources", "list"], "datasources list"
|
|
1398
|
+
)
|
|
1399
|
+
existing = matching_datasource(listed, connection)
|
|
1400
|
+
if existing is None:
|
|
1401
|
+
raise OnboardingError(
|
|
1402
|
+
f"Datasource {connection['name']} already exists but its public type does not match the approved plan; "
|
|
1403
|
+
"do not replace it without explicit approval"
|
|
1404
|
+
) from exc
|
|
1405
|
+
already_present = True
|
|
1406
|
+
result = {
|
|
1407
|
+
"success": True,
|
|
1408
|
+
"idempotent": True,
|
|
1409
|
+
"status": "already-present",
|
|
1410
|
+
"dataSource": redact_connection_material(existing),
|
|
1411
|
+
}
|
|
1412
|
+
mark_step(state, "datasourceConfigured", "completed", replace=args.replace, alreadyPresent=already_present)
|
|
1425
1413
|
path = write_onboarding_state(data_root, state)
|
|
1426
1414
|
return {
|
|
1427
1415
|
"success": True,
|
|
@@ -1429,6 +1417,7 @@ def datasource_configure_command(args: argparse.Namespace) -> dict:
|
|
|
1429
1417
|
"profile": state["profile"],
|
|
1430
1418
|
"statePath": str(path),
|
|
1431
1419
|
"dataSource": connection["name"],
|
|
1420
|
+
"alreadyPresent": already_present,
|
|
1432
1421
|
"runtime": result,
|
|
1433
1422
|
"next": "run datasource-verify; add --bind to approve namespace binding",
|
|
1434
1423
|
"productionReady": False,
|
|
@@ -1887,7 +1876,16 @@ def semantic_verify_command(args: argparse.Namespace) -> dict:
|
|
|
1887
1876
|
)
|
|
1888
1877
|
atomic_json(evidence_dir / "query-execute.json", executed)
|
|
1889
1878
|
row_count = response_row_count(executed)
|
|
1890
|
-
mark_step(
|
|
1879
|
+
mark_step(
|
|
1880
|
+
state,
|
|
1881
|
+
"semanticVerified",
|
|
1882
|
+
"completed",
|
|
1883
|
+
queryModel=query_model,
|
|
1884
|
+
queryValidated=True,
|
|
1885
|
+
queryExecuted=True,
|
|
1886
|
+
rowCount=row_count,
|
|
1887
|
+
queryPayloadDigest=sha256(payload_path),
|
|
1888
|
+
)
|
|
1891
1889
|
state.setdefault("artifacts", {})["semanticVerifyEvidence"] = str(evidence_dir)
|
|
1892
1890
|
path = write_onboarding_state(data_root, state)
|
|
1893
1891
|
return {
|
|
@@ -1907,14 +1905,15 @@ def semantic_verify_command(args: argparse.Namespace) -> dict:
|
|
|
1907
1905
|
|
|
1908
1906
|
|
|
1909
1907
|
def next_onboarding_action(state: dict) -> dict:
|
|
1908
|
+
profile_flag = f" --profile {state['profile']}"
|
|
1910
1909
|
ordered = [
|
|
1911
|
-
("datasourceConfigured", "datasource-configure --apply"),
|
|
1912
|
-
("datasourceVerified", "datasource-verify --bind"),
|
|
1913
|
-
("schemaDiscovered", "schema-discover"),
|
|
1914
|
-
("semanticDrafted", "semantic-draft --semantic-plan <json>"),
|
|
1915
|
-
("semanticValidated", "semantic-validate --apply"),
|
|
1916
|
-
("semanticPublished", "semantic-publish --apply"),
|
|
1917
|
-
("semanticVerified", "semantic-verify --query-payload <json> --execute"),
|
|
1910
|
+
("datasourceConfigured", f"datasource-configure{profile_flag} --apply"),
|
|
1911
|
+
("datasourceVerified", f"datasource-verify{profile_flag} --bind"),
|
|
1912
|
+
("schemaDiscovered", f"schema-discover{profile_flag}"),
|
|
1913
|
+
("semanticDrafted", f"semantic-draft{profile_flag} --semantic-plan <json>"),
|
|
1914
|
+
("semanticValidated", f"semantic-validate{profile_flag} --apply"),
|
|
1915
|
+
("semanticPublished", f"semantic-publish{profile_flag} --apply"),
|
|
1916
|
+
("semanticVerified", f"semantic-verify{profile_flag} --query-payload <json> --execute"),
|
|
1918
1917
|
]
|
|
1919
1918
|
for name, command in ordered:
|
|
1920
1919
|
current = state.get("steps", {}).get(name, {})
|
|
@@ -1927,7 +1926,7 @@ def next_onboarding_action(state: dict) -> dict:
|
|
|
1927
1926
|
"instruction": "Inspect semantic publish evidence and repair refresh before any republish attempt.",
|
|
1928
1927
|
}
|
|
1929
1928
|
if name == "semanticValidated" and current.get("status") == "failed":
|
|
1930
|
-
command = "repair TM/QM, then semantic-draft --semantic-plan <json>"
|
|
1929
|
+
command = f"repair TM/QM, then semantic-draft{profile_flag} --semantic-plan <json>"
|
|
1931
1930
|
return {"step": name, "command": command, "status": current.get("status", "pending")}
|
|
1932
1931
|
return {"step": None, "command": None, "status": "completed"}
|
|
1933
1932
|
|
|
@@ -1972,9 +1971,24 @@ def save_composite_result(evidence_dir: Path, name: str, payload: dict, files: l
|
|
|
1972
1971
|
files.append(str(path))
|
|
1973
1972
|
|
|
1974
1973
|
|
|
1974
|
+
def resumed_phase(profile: str, phase: str, **values: object) -> dict:
|
|
1975
|
+
return {
|
|
1976
|
+
"success": True,
|
|
1977
|
+
"schemaVersion": "foggy-deepseek-onboarding-resumed/v1",
|
|
1978
|
+
"profile": profile,
|
|
1979
|
+
"phase": phase,
|
|
1980
|
+
"resumed": True,
|
|
1981
|
+
**values,
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
|
|
1985
|
+
def step_completed(state: dict, name: str) -> bool:
|
|
1986
|
+
return state.get("steps", {}).get(name, {}).get("status") == "completed"
|
|
1987
|
+
|
|
1988
|
+
|
|
1975
1989
|
def datasource_run_command(args: argparse.Namespace) -> dict:
|
|
1976
1990
|
_install_root, install_state, data_root, _runtime_state = onboarding_context(args, require_runtime=True)
|
|
1977
|
-
project_root = normalized(args.project_root or
|
|
1991
|
+
project_root = normalized(args.project_root or Path.cwd())
|
|
1978
1992
|
requested_connection = validate_connection(read_json_object(normalized(args.connection_file), "Connection plan"))
|
|
1979
1993
|
if not requested_connection.get("profile"):
|
|
1980
1994
|
raise OnboardingError("Composite datasource onboarding requires connection.profile in the approved contract")
|
|
@@ -1991,6 +2005,11 @@ def datasource_run_command(args: argparse.Namespace) -> dict:
|
|
|
1991
2005
|
if existing:
|
|
1992
2006
|
if existing.get("connection") != requested_connection:
|
|
1993
2007
|
raise OnboardingError("Existing onboarding profile does not match the requested connection plan")
|
|
2008
|
+
if normalized(existing.get("projectRoot", "")) != project_root:
|
|
2009
|
+
raise OnboardingError(
|
|
2010
|
+
"Existing onboarding profile belongs to a different projectRoot; resume from that DSH workspace "
|
|
2011
|
+
"or choose a new profile name"
|
|
2012
|
+
)
|
|
1994
2013
|
plan_result = {
|
|
1995
2014
|
"success": True,
|
|
1996
2015
|
"schemaVersion": "foggy-deepseek-onboarding-plan-result/v1",
|
|
@@ -2010,59 +2029,81 @@ def datasource_run_command(args: argparse.Namespace) -> dict:
|
|
|
2010
2029
|
replace_plan=False,
|
|
2011
2030
|
))
|
|
2012
2031
|
save_composite_result(evidence_dir, "01-plan.json", plan_result, files)
|
|
2032
|
+
state = read_onboarding_state(data_root, profile)
|
|
2013
2033
|
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
return {
|
|
2040
|
-
"success": True,
|
|
2041
|
-
"schemaVersion": "foggy-deepseek-datasource-run/v1",
|
|
2042
|
-
"profile": profile,
|
|
2043
|
-
"phaseStatus": "awaiting-bind-approval",
|
|
2044
|
-
"evidenceDir": str(evidence_dir),
|
|
2045
|
-
"evidenceFiles": files,
|
|
2046
|
-
"next": "rerun with --approve-configure --approve-bind after namespace binding is approved",
|
|
2047
|
-
"productionReady": False,
|
|
2048
|
-
}
|
|
2034
|
+
if step_completed(state, "datasourceConfigured"):
|
|
2035
|
+
configured = resumed_phase(profile, "datasourceConfigured")
|
|
2036
|
+
save_composite_result(evidence_dir, "02-datasource-dry.json", configured, files)
|
|
2037
|
+
save_composite_result(evidence_dir, "03-datasource-apply.json", configured, files)
|
|
2038
|
+
else:
|
|
2039
|
+
configure_dry = datasource_configure_command(argparse.Namespace(
|
|
2040
|
+
install_root=args.install_root, data_root=args.data_root, profile=profile, apply=False, replace=False,
|
|
2041
|
+
))
|
|
2042
|
+
save_composite_result(evidence_dir, "02-datasource-dry.json", configure_dry, files)
|
|
2043
|
+
if not args.approve_configure:
|
|
2044
|
+
return {
|
|
2045
|
+
"success": True,
|
|
2046
|
+
"schemaVersion": "foggy-deepseek-datasource-run/v1",
|
|
2047
|
+
"profile": profile,
|
|
2048
|
+
"phaseStatus": "awaiting-configure-approval",
|
|
2049
|
+
"evidenceDir": str(evidence_dir),
|
|
2050
|
+
"evidenceFiles": files,
|
|
2051
|
+
"next": "rerun with --approve-configure after the datasource mutation is approved",
|
|
2052
|
+
"productionReady": False,
|
|
2053
|
+
}
|
|
2054
|
+
configured = datasource_configure_command(argparse.Namespace(
|
|
2055
|
+
install_root=args.install_root, data_root=args.data_root, profile=profile, apply=True, replace=False,
|
|
2056
|
+
))
|
|
2057
|
+
save_composite_result(evidence_dir, "03-datasource-apply.json", configured, files)
|
|
2058
|
+
state = read_onboarding_state(data_root, profile)
|
|
2049
2059
|
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2060
|
+
if step_completed(state, "datasourceVerified"):
|
|
2061
|
+
bound = resumed_phase(profile, "datasourceVerified")
|
|
2062
|
+
save_composite_result(evidence_dir, "04-datasource-test.json", bound, files)
|
|
2063
|
+
save_composite_result(evidence_dir, "05-datasource-bind.json", bound, files)
|
|
2064
|
+
else:
|
|
2065
|
+
tested = datasource_verify_command(argparse.Namespace(
|
|
2066
|
+
install_root=args.install_root, data_root=args.data_root, profile=profile, bind=False,
|
|
2067
|
+
))
|
|
2068
|
+
save_composite_result(evidence_dir, "04-datasource-test.json", tested, files)
|
|
2069
|
+
if not args.approve_bind:
|
|
2070
|
+
return {
|
|
2071
|
+
"success": True,
|
|
2072
|
+
"schemaVersion": "foggy-deepseek-datasource-run/v1",
|
|
2073
|
+
"profile": profile,
|
|
2074
|
+
"phaseStatus": "awaiting-bind-approval",
|
|
2075
|
+
"evidenceDir": str(evidence_dir),
|
|
2076
|
+
"evidenceFiles": files,
|
|
2077
|
+
"next": "rerun with --approve-configure --approve-bind after namespace binding is approved",
|
|
2078
|
+
"productionReady": False,
|
|
2079
|
+
}
|
|
2080
|
+
bound = datasource_verify_command(argparse.Namespace(
|
|
2081
|
+
install_root=args.install_root, data_root=args.data_root, profile=profile, bind=True,
|
|
2082
|
+
))
|
|
2083
|
+
save_composite_result(evidence_dir, "05-datasource-bind.json", bound, files)
|
|
2084
|
+
state = read_onboarding_state(data_root, profile)
|
|
2085
|
+
|
|
2086
|
+
if step_completed(state, "schemaDiscovered"):
|
|
2087
|
+
schema_step = state["steps"]["schemaDiscovered"]
|
|
2088
|
+
discovered = resumed_phase(
|
|
2089
|
+
profile,
|
|
2090
|
+
"schemaDiscovered",
|
|
2091
|
+
selectedCount=schema_step.get("selectedCount", 0),
|
|
2092
|
+
artifactPath=state.get("artifacts", {}).get("schemaDiscovery"),
|
|
2093
|
+
)
|
|
2094
|
+
else:
|
|
2095
|
+
discovered = schema_discover_command(argparse.Namespace(
|
|
2096
|
+
install_root=args.install_root,
|
|
2097
|
+
data_root=args.data_root,
|
|
2098
|
+
profile=profile,
|
|
2099
|
+
schema=args.schema,
|
|
2100
|
+
pattern=args.pattern,
|
|
2101
|
+
table=args.table,
|
|
2102
|
+
max_tables=args.max_tables,
|
|
2103
|
+
list_only=False,
|
|
2104
|
+
no_views=args.no_views,
|
|
2105
|
+
include_indexes=args.include_indexes,
|
|
2106
|
+
))
|
|
2066
2107
|
save_composite_result(evidence_dir, "06-schema.json", discovered, files)
|
|
2067
2108
|
return {
|
|
2068
2109
|
"success": True,
|
|
@@ -2078,6 +2119,29 @@ def datasource_run_command(args: argparse.Namespace) -> dict:
|
|
|
2078
2119
|
}
|
|
2079
2120
|
|
|
2080
2121
|
|
|
2122
|
+
def semantic_plan_snapshot(state: dict, plan: dict) -> tuple[Path, dict, bool]:
|
|
2123
|
+
project_root = normalized(state["projectRoot"])
|
|
2124
|
+
draft_dir = normalized(project_root / plan["draftDir"])
|
|
2125
|
+
if not is_child(draft_dir, project_root) or draft_dir == project_root:
|
|
2126
|
+
raise OnboardingError("draftDir must stay inside projectRoot and cannot equal it")
|
|
2127
|
+
manifest = semantic_manifest(draft_dir)
|
|
2128
|
+
semantic = state.get("semantic", {})
|
|
2129
|
+
registered = semantic.get("draftManifest") or {}
|
|
2130
|
+
matches = bool(
|
|
2131
|
+
step_completed(state, "semanticDrafted")
|
|
2132
|
+
and normalized(semantic.get("draftDir", draft_dir)) == draft_dir
|
|
2133
|
+
and semantic.get("bundleName") == plan["bundleName"]
|
|
2134
|
+
and semantic.get("queryModels") == plan["queryModels"]
|
|
2135
|
+
and registered.get("digest") == manifest["digest"]
|
|
2136
|
+
)
|
|
2137
|
+
return draft_dir, manifest, matches
|
|
2138
|
+
|
|
2139
|
+
|
|
2140
|
+
def published_digest_matches(state: dict, digest: str) -> bool:
|
|
2141
|
+
published = state.get("steps", {}).get("semanticPublished", {})
|
|
2142
|
+
return published.get("status") == "completed" and published.get("digest") == digest
|
|
2143
|
+
|
|
2144
|
+
|
|
2081
2145
|
def semantic_run_command(args: argparse.Namespace) -> dict:
|
|
2082
2146
|
approved_plan = validate_semantic_plan(read_json_object(normalized(args.semantic_plan), "Semantic plan"))
|
|
2083
2147
|
if not approved_plan.get("profile"):
|
|
@@ -2089,6 +2153,17 @@ def semantic_run_command(args: argparse.Namespace) -> dict:
|
|
|
2089
2153
|
profile_args.profile = profile
|
|
2090
2154
|
state, _install_state, _data_root, _runtime_state = require_profile(profile_args, require_runtime=True)
|
|
2091
2155
|
project_root = normalized(state["projectRoot"])
|
|
2156
|
+
payload_path = normalized(args.query_payload)
|
|
2157
|
+
if not is_child(payload_path, project_root):
|
|
2158
|
+
raise OnboardingError(
|
|
2159
|
+
"Query payload must stay inside projectRoot; place contracts under .foggy/onboarding-contracts before approval"
|
|
2160
|
+
)
|
|
2161
|
+
bounded_query_payload(payload_path)
|
|
2162
|
+
declared_query_model = args.query_model or (approved_plan["queryModels"][0] if len(approved_plan["queryModels"]) == 1 else None)
|
|
2163
|
+
if not declared_query_model:
|
|
2164
|
+
raise OnboardingError("--query-model is required when the semantic plan declares multiple query models")
|
|
2165
|
+
if declared_query_model not in approved_plan["queryModels"]:
|
|
2166
|
+
raise OnboardingError("--query-model must be declared in semanticPlan.queryModels")
|
|
2092
2167
|
contract_evidence = approved_plan.get("evidenceDir")
|
|
2093
2168
|
if contract_evidence and args.evidence_dir:
|
|
2094
2169
|
if normalized(project_root / contract_evidence) != normalized(args.evidence_dir):
|
|
@@ -2096,78 +2171,141 @@ def semantic_run_command(args: argparse.Namespace) -> dict:
|
|
|
2096
2171
|
evidence_dir = composite_evidence_dir(project_root, profile, str(project_root / contract_evidence) if contract_evidence else args.evidence_dir)
|
|
2097
2172
|
files: list[str] = []
|
|
2098
2173
|
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2174
|
+
_draft_dir, manifest, draft_matches = semantic_plan_snapshot(state, approved_plan)
|
|
2175
|
+
published_matches = published_digest_matches(state, manifest["digest"])
|
|
2176
|
+
if draft_matches or published_matches:
|
|
2177
|
+
drafted = resumed_phase(profile, "semanticDrafted", digest=manifest["digest"])
|
|
2178
|
+
else:
|
|
2179
|
+
drafted = semantic_draft_command(argparse.Namespace(
|
|
2180
|
+
install_root=args.install_root,
|
|
2181
|
+
data_root=args.data_root,
|
|
2182
|
+
profile=profile,
|
|
2183
|
+
semantic_plan=args.semantic_plan,
|
|
2184
|
+
))
|
|
2185
|
+
state = read_onboarding_state(normalized(state["dataRoot"]), profile)
|
|
2186
|
+
manifest = state["semantic"]["draftManifest"]
|
|
2105
2187
|
save_composite_result(evidence_dir, "07-semantic-draft.json", drafted, files)
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2188
|
+
state = read_onboarding_state(normalized(state["dataRoot"]), profile)
|
|
2189
|
+
validation_step = state.get("steps", {}).get("semanticValidated", {})
|
|
2190
|
+
published_matches = published_digest_matches(state, manifest["digest"])
|
|
2191
|
+
validation_matches = validation_step.get("status") == "completed" and validation_step.get("digest") == manifest["digest"]
|
|
2192
|
+
if validation_matches or published_matches:
|
|
2193
|
+
if published_matches and not validation_matches:
|
|
2194
|
+
mark_step(state, "semanticValidated", "completed", digest=manifest["digest"], resumedFromPublished=True)
|
|
2195
|
+
write_onboarding_state(normalized(state["dataRoot"]), state)
|
|
2196
|
+
validated = resumed_phase(profile, "semanticValidated", digest=manifest["digest"])
|
|
2197
|
+
save_composite_result(evidence_dir, "08-semantic-validate-dry.json", validated, files)
|
|
2198
|
+
else:
|
|
2199
|
+
validate_dry = semantic_validate_command(argparse.Namespace(
|
|
2200
|
+
install_root=args.install_root,
|
|
2201
|
+
data_root=args.data_root,
|
|
2202
|
+
profile=profile,
|
|
2203
|
+
apply=False,
|
|
2204
|
+
include_stack_trace=False,
|
|
2205
|
+
))
|
|
2206
|
+
save_composite_result(evidence_dir, "08-semantic-validate-dry.json", validate_dry, files)
|
|
2207
|
+
if not args.approve_validate:
|
|
2208
|
+
return {
|
|
2209
|
+
"success": True,
|
|
2210
|
+
"schemaVersion": "foggy-deepseek-semantic-run/v1",
|
|
2211
|
+
"profile": profile,
|
|
2212
|
+
"phaseStatus": "awaiting-validate-approval",
|
|
2213
|
+
"evidenceDir": str(evidence_dir),
|
|
2214
|
+
"evidenceFiles": files,
|
|
2215
|
+
"next": "rerun with --approve-validate after the validation catalog mutation is approved",
|
|
2216
|
+
"productionReady": False,
|
|
2217
|
+
}
|
|
2218
|
+
validated = semantic_validate_command(argparse.Namespace(
|
|
2219
|
+
install_root=args.install_root,
|
|
2220
|
+
data_root=args.data_root,
|
|
2221
|
+
profile=profile,
|
|
2222
|
+
apply=True,
|
|
2223
|
+
include_stack_trace=False,
|
|
2224
|
+
))
|
|
2133
2225
|
save_composite_result(evidence_dir, "09-semantic-validate-apply.json", validated, files)
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2226
|
+
state = read_onboarding_state(normalized(state["dataRoot"]), profile)
|
|
2227
|
+
if published_digest_matches(state, manifest["digest"]):
|
|
2228
|
+
published = resumed_phase(
|
|
2229
|
+
profile,
|
|
2230
|
+
"semanticPublished",
|
|
2231
|
+
digest=manifest["digest"],
|
|
2232
|
+
bundleName=state["semantic"]["bundleName"],
|
|
2233
|
+
)
|
|
2234
|
+
save_composite_result(evidence_dir, "10-semantic-publish-dry.json", published, files)
|
|
2235
|
+
else:
|
|
2236
|
+
publish_dry = semantic_publish_command(argparse.Namespace(
|
|
2237
|
+
install_root=args.install_root,
|
|
2238
|
+
data_root=args.data_root,
|
|
2239
|
+
profile=profile,
|
|
2240
|
+
apply=False,
|
|
2241
|
+
replace_bundle=False,
|
|
2242
|
+
watch=False,
|
|
2243
|
+
prune=False,
|
|
2244
|
+
))
|
|
2245
|
+
save_composite_result(evidence_dir, "10-semantic-publish-dry.json", publish_dry, files)
|
|
2246
|
+
if not args.approve_publish:
|
|
2247
|
+
return {
|
|
2248
|
+
"success": True,
|
|
2249
|
+
"schemaVersion": "foggy-deepseek-semantic-run/v1",
|
|
2250
|
+
"profile": profile,
|
|
2251
|
+
"phaseStatus": "awaiting-publish-approval",
|
|
2252
|
+
"evidenceDir": str(evidence_dir),
|
|
2253
|
+
"evidenceFiles": files,
|
|
2254
|
+
"next": "rerun with --approve-validate --approve-publish after publication is approved",
|
|
2255
|
+
"productionReady": False,
|
|
2256
|
+
}
|
|
2257
|
+
published = semantic_publish_command(argparse.Namespace(
|
|
2258
|
+
install_root=args.install_root,
|
|
2259
|
+
data_root=args.data_root,
|
|
2260
|
+
profile=profile,
|
|
2261
|
+
apply=True,
|
|
2262
|
+
replace_bundle=False,
|
|
2263
|
+
watch=False,
|
|
2264
|
+
prune=False,
|
|
2265
|
+
))
|
|
2266
|
+
save_composite_result(evidence_dir, "11-semantic-publish-apply.json", published, files)
|
|
2267
|
+
state = read_onboarding_state(normalized(state["dataRoot"]), profile)
|
|
2268
|
+
verified_step = state.get("steps", {}).get("semanticVerified", {})
|
|
2269
|
+
payload_digest = sha256(payload_path)
|
|
2270
|
+
if (
|
|
2271
|
+
verified_step.get("status") == "completed"
|
|
2272
|
+
and verified_step.get("queryModel") == declared_query_model
|
|
2273
|
+
and verified_step.get("queryPayloadDigest") == payload_digest
|
|
2274
|
+
):
|
|
2275
|
+
resumed = resumed_phase(
|
|
2276
|
+
profile,
|
|
2277
|
+
"semanticVerified",
|
|
2278
|
+
queryModel=declared_query_model,
|
|
2279
|
+
queryValidated=True,
|
|
2280
|
+
queryExecuted=True,
|
|
2281
|
+
rowCount=verified_step.get("rowCount"),
|
|
2282
|
+
queryPayloadDigest=payload_digest,
|
|
2283
|
+
)
|
|
2284
|
+
save_composite_result(evidence_dir, "12-query-validate.json", resumed, files)
|
|
2285
|
+
save_composite_result(evidence_dir, "13-query-execute.json", resumed, files)
|
|
2286
|
+
status = onboarding_status_command(argparse.Namespace(
|
|
2287
|
+
install_root=args.install_root, data_root=args.data_root, profile=profile,
|
|
2288
|
+
))
|
|
2289
|
+
save_composite_result(evidence_dir, "14-status.json", status, files)
|
|
2145
2290
|
return {
|
|
2146
2291
|
"success": True,
|
|
2147
2292
|
"schemaVersion": "foggy-deepseek-semantic-run/v1",
|
|
2148
2293
|
"profile": profile,
|
|
2149
|
-
"phaseStatus": "
|
|
2294
|
+
"phaseStatus": "completed",
|
|
2295
|
+
"resumed": True,
|
|
2296
|
+
"queryModel": declared_query_model,
|
|
2297
|
+
"queryValidated": True,
|
|
2298
|
+
"queryExecuted": True,
|
|
2299
|
+
"rowCount": verified_step.get("rowCount"),
|
|
2150
2300
|
"evidenceDir": str(evidence_dir),
|
|
2151
2301
|
"evidenceFiles": files,
|
|
2152
|
-
"next": "rerun with --approve-validate --approve-publish after publication is approved",
|
|
2153
2302
|
"productionReady": False,
|
|
2154
2303
|
}
|
|
2155
|
-
|
|
2156
|
-
published = semantic_publish_command(argparse.Namespace(
|
|
2157
|
-
install_root=args.install_root,
|
|
2158
|
-
data_root=args.data_root,
|
|
2159
|
-
profile=profile,
|
|
2160
|
-
apply=True,
|
|
2161
|
-
replace_bundle=False,
|
|
2162
|
-
watch=False,
|
|
2163
|
-
prune=False,
|
|
2164
|
-
))
|
|
2165
|
-
save_composite_result(evidence_dir, "11-semantic-publish-apply.json", published, files)
|
|
2166
2304
|
query_validated = semantic_verify_command(argparse.Namespace(
|
|
2167
2305
|
install_root=args.install_root,
|
|
2168
2306
|
data_root=args.data_root,
|
|
2169
2307
|
profile=profile,
|
|
2170
|
-
query_model=
|
|
2308
|
+
query_model=declared_query_model,
|
|
2171
2309
|
query_payload=args.query_payload,
|
|
2172
2310
|
execute=False,
|
|
2173
2311
|
))
|
|
@@ -2190,7 +2328,7 @@ def semantic_run_command(args: argparse.Namespace) -> dict:
|
|
|
2190
2328
|
install_root=args.install_root,
|
|
2191
2329
|
data_root=args.data_root,
|
|
2192
2330
|
profile=profile,
|
|
2193
|
-
query_model=
|
|
2331
|
+
query_model=declared_query_model,
|
|
2194
2332
|
query_payload=args.query_payload,
|
|
2195
2333
|
execute=True,
|
|
2196
2334
|
))
|
|
@@ -2234,24 +2372,17 @@ def doctor_command(args: argparse.Namespace) -> dict:
|
|
|
2234
2372
|
path = launcher_dir / asset["file"]
|
|
2235
2373
|
launcher_checks.append({"file": asset["file"], "present": path.is_file(), "sha256Valid": path.is_file() and sha256(path) == asset["sha256"]})
|
|
2236
2374
|
launcher_ok = bool(launcher_checks) and all(item["present"] and item["sha256Valid"] for item in launcher_checks)
|
|
2237
|
-
analysis_skill_root =
|
|
2238
|
-
onboarding_skill_root =
|
|
2375
|
+
analysis_skill_root = normalized(state.get("skills", {}).get("analysis", {}).get("path") or install_root / "skills" / "foggy-ai-analysis") if state else install_root / "skills" / "foggy-ai-analysis"
|
|
2376
|
+
onboarding_skill_root = skill_root()
|
|
2239
2377
|
analysis_skill = managed_skill_status(analysis_skill_root, "analysis", versions["components"]["analysisSkill"]["version"])
|
|
2240
|
-
onboarding_skill =
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
context_ok = bool(
|
|
2249
|
-
context
|
|
2250
|
-
and context.get("schemaVersion") == CONTEXT_SCHEMA
|
|
2251
|
-
and context.get("packageVersion") == versions["packageVersion"]
|
|
2252
|
-
and normalized(context.get("projectRoot", "")) == project_root
|
|
2253
|
-
and normalized(context.get("installStatePath", "")) == install_root / "install-state.json"
|
|
2254
|
-
)
|
|
2378
|
+
onboarding_skill = {
|
|
2379
|
+
"path": str(onboarding_skill_root),
|
|
2380
|
+
"present": (onboarding_skill_root / "SKILL.md").is_file(),
|
|
2381
|
+
"managed": False,
|
|
2382
|
+
"version": versions["packageVersion"],
|
|
2383
|
+
"provider": "foggy-managed-skills",
|
|
2384
|
+
"valid": (onboarding_skill_root / "SKILL.md").is_file(),
|
|
2385
|
+
}
|
|
2255
2386
|
runtime = {"status": "stopped"}
|
|
2256
2387
|
if state:
|
|
2257
2388
|
data_root = normalized(state["dataRoot"])
|
|
@@ -2267,7 +2398,6 @@ def doctor_command(args: argparse.Namespace) -> dict:
|
|
|
2267
2398
|
"launcher": launcher_ok,
|
|
2268
2399
|
"analysisSkill": analysis_skill["valid"],
|
|
2269
2400
|
"onboardingSkill": onboarding_skill["valid"],
|
|
2270
|
-
"projectContext": context_ok,
|
|
2271
2401
|
}
|
|
2272
2402
|
if args.strict_runtime:
|
|
2273
2403
|
required["runtime"] = runtime["status"] == "running"
|
|
@@ -2282,7 +2412,7 @@ def doctor_command(args: argparse.Namespace) -> dict:
|
|
|
2282
2412
|
"cli": cli,
|
|
2283
2413
|
"launcherAssets": launcher_checks,
|
|
2284
2414
|
"skills": {"analysis": analysis_skill, "onboarding": onboarding_skill},
|
|
2285
|
-
"
|
|
2415
|
+
"workspace": {"path": str(project_root), "mode": "dsh-session-cwd"},
|
|
2286
2416
|
"runtime": runtime,
|
|
2287
2417
|
"environmentPresence": {name: bool(os.environ.get(name)) for name in ("DEEPSEEK_API_KEY", "ALIYUN_TOKEN_PLAN_API_KEY", "FOGGY_RUNTIME_API_AUTH_CODE", "FOGGY_RUNTIME_AUTHORIZATION")},
|
|
2288
2418
|
"productionReady": False,
|
|
@@ -2298,13 +2428,8 @@ def uninstall_command(args: argparse.Namespace) -> dict:
|
|
|
2298
2428
|
assert_managed_root(data_root, "Data root")
|
|
2299
2429
|
if install_root == data_root:
|
|
2300
2430
|
raise OnboardingError("Install root and data root must be different")
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
skill_targets = [skills_root / name for name in ("foggy-deepseek-onboarding", "foggy-ai-analysis")]
|
|
2304
|
-
if args.remove_skills:
|
|
2305
|
-
for target in skill_targets:
|
|
2306
|
-
if target.exists() and (target.is_symlink() or not is_child(target, skills_root)):
|
|
2307
|
-
raise OnboardingError(f"Refusing to remove unexpected Skill path: {target}")
|
|
2431
|
+
skills_root = install_root / "skills"
|
|
2432
|
+
skill_targets = [skills_root / "foggy-ai-analysis"]
|
|
2308
2433
|
plan = {"installRoot": str(install_root), "dataRoot": str(data_root), "removeSkills": args.remove_skills, "purgeData": args.purge_data}
|
|
2309
2434
|
if args.dry_run:
|
|
2310
2435
|
return {"success": True, "dryRun": True, "plan": plan}
|
|
@@ -2382,13 +2507,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2382
2507
|
status = sub.add_parser("onboard-status")
|
|
2383
2508
|
status.add_argument("--install-root")
|
|
2384
2509
|
status.add_argument("--data-root")
|
|
2385
|
-
status.add_argument("--profile"
|
|
2510
|
+
status.add_argument("--profile")
|
|
2386
2511
|
status.set_defaults(handler=onboarding_status_command)
|
|
2387
2512
|
|
|
2388
2513
|
resume = sub.add_parser("onboard-resume")
|
|
2389
2514
|
resume.add_argument("--install-root")
|
|
2390
2515
|
resume.add_argument("--data-root")
|
|
2391
|
-
resume.add_argument("--profile"
|
|
2516
|
+
resume.add_argument("--profile")
|
|
2392
2517
|
resume.set_defaults(handler=onboarding_resume_command)
|
|
2393
2518
|
|
|
2394
2519
|
datasource_run = sub.add_parser("onboard-datasource-run")
|
|
@@ -2424,7 +2549,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2424
2549
|
datasource_configure = sub.add_parser("datasource-configure")
|
|
2425
2550
|
datasource_configure.add_argument("--install-root")
|
|
2426
2551
|
datasource_configure.add_argument("--data-root")
|
|
2427
|
-
datasource_configure.add_argument("--profile"
|
|
2552
|
+
datasource_configure.add_argument("--profile")
|
|
2428
2553
|
datasource_configure.add_argument("--apply", action="store_true")
|
|
2429
2554
|
datasource_configure.add_argument("--replace", action="store_true")
|
|
2430
2555
|
datasource_configure.set_defaults(handler=datasource_configure_command)
|
|
@@ -2432,14 +2557,14 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2432
2557
|
datasource_verify = sub.add_parser("datasource-verify")
|
|
2433
2558
|
datasource_verify.add_argument("--install-root")
|
|
2434
2559
|
datasource_verify.add_argument("--data-root")
|
|
2435
|
-
datasource_verify.add_argument("--profile"
|
|
2560
|
+
datasource_verify.add_argument("--profile")
|
|
2436
2561
|
datasource_verify.add_argument("--bind", action="store_true")
|
|
2437
2562
|
datasource_verify.set_defaults(handler=datasource_verify_command)
|
|
2438
2563
|
|
|
2439
2564
|
schema_discover = sub.add_parser("schema-discover")
|
|
2440
2565
|
schema_discover.add_argument("--install-root")
|
|
2441
2566
|
schema_discover.add_argument("--data-root")
|
|
2442
|
-
schema_discover.add_argument("--profile"
|
|
2567
|
+
schema_discover.add_argument("--profile")
|
|
2443
2568
|
schema_discover.add_argument("--schema", action="append")
|
|
2444
2569
|
schema_discover.add_argument("--pattern")
|
|
2445
2570
|
schema_discover.add_argument("--table", action="append")
|
|
@@ -2452,14 +2577,14 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2452
2577
|
semantic_draft = sub.add_parser("semantic-draft")
|
|
2453
2578
|
semantic_draft.add_argument("--install-root")
|
|
2454
2579
|
semantic_draft.add_argument("--data-root")
|
|
2455
|
-
semantic_draft.add_argument("--profile"
|
|
2580
|
+
semantic_draft.add_argument("--profile")
|
|
2456
2581
|
semantic_draft.add_argument("--semantic-plan", required=True)
|
|
2457
2582
|
semantic_draft.set_defaults(handler=semantic_draft_command)
|
|
2458
2583
|
|
|
2459
2584
|
semantic_validate = sub.add_parser("semantic-validate")
|
|
2460
2585
|
semantic_validate.add_argument("--install-root")
|
|
2461
2586
|
semantic_validate.add_argument("--data-root")
|
|
2462
|
-
semantic_validate.add_argument("--profile"
|
|
2587
|
+
semantic_validate.add_argument("--profile")
|
|
2463
2588
|
semantic_validate.add_argument("--apply", action="store_true")
|
|
2464
2589
|
semantic_validate.add_argument("--include-stack-trace", action="store_true")
|
|
2465
2590
|
semantic_validate.set_defaults(handler=semantic_validate_command)
|
|
@@ -2467,7 +2592,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2467
2592
|
semantic_publish = sub.add_parser("semantic-publish")
|
|
2468
2593
|
semantic_publish.add_argument("--install-root")
|
|
2469
2594
|
semantic_publish.add_argument("--data-root")
|
|
2470
|
-
semantic_publish.add_argument("--profile"
|
|
2595
|
+
semantic_publish.add_argument("--profile")
|
|
2471
2596
|
semantic_publish.add_argument("--apply", action="store_true")
|
|
2472
2597
|
semantic_publish.add_argument("--replace-bundle", action="store_true")
|
|
2473
2598
|
semantic_publish.add_argument("--watch", action="store_true")
|
|
@@ -2477,7 +2602,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2477
2602
|
semantic_verify = sub.add_parser("semantic-verify")
|
|
2478
2603
|
semantic_verify.add_argument("--install-root")
|
|
2479
2604
|
semantic_verify.add_argument("--data-root")
|
|
2480
|
-
semantic_verify.add_argument("--profile"
|
|
2605
|
+
semantic_verify.add_argument("--profile")
|
|
2481
2606
|
semantic_verify.add_argument("--query-model")
|
|
2482
2607
|
semantic_verify.add_argument("--query-payload", required=True)
|
|
2483
2608
|
semantic_verify.add_argument("--execute", action="store_true")
|