@foggy-projects/deepseek-harness-plugin 0.4.0-beta.5 → 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 CHANGED
@@ -8,7 +8,7 @@ only when the user selects **Initialize Foggy**.
8
8
  ## Local beta installation
9
9
 
10
10
  ```powershell
11
- dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.0-beta.5.tgz
11
+ dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.0-beta.6.tgz
12
12
  ```
13
13
 
14
14
  Restart `dsh web`, open Settings → Plugins → Foggy Data Analysis, and initialize
@@ -23,8 +23,9 @@ dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-p
23
23
  For development, `FOGGY_ASSET_CACHE_DIRS` can contain platform-delimited verified
24
24
  asset-cache directories. It is not required for a normal install.
25
25
 
26
- Database credentials are deliberately outside the ordinary DSH settings
27
- document. The database and semantic-layer wizard is the next Bundle milestone.
26
+ Database credentials remain outside the ordinary DSH settings document. The
27
+ bundled onboarding Skill drives datasource discovery, semantic drafting,
28
+ checkpointed publication, and bounded read-only query verification.
28
29
 
29
30
  ## Native Skill and workspace contract
30
31
 
@@ -34,6 +35,11 @@ are available in every DSH workspace without copying or symlinking `.agents`.
34
35
  The current session `cwd` remains the workspace boundary for semantic drafts and
35
36
  evidence.
36
37
 
38
+ Opaque CLI profiles default to the private persistent
39
+ `<dataRoot>/cli-profiles` directory. Composite onboarding commands are
40
+ idempotent: unchanged completed phases are resumed rather than re-adding a
41
+ datasource or re-registering a published bundle.
42
+
37
43
  CLI, Launcher, the analysis Skill, install state, and Runtime state live in the
38
44
  user-level Foggy component directories. The managed CLI is intentionally isolated
39
45
  and does not need to be on `PATH`. **Re-download / Repair** verifies the global
package/lib/client.js CHANGED
@@ -68,6 +68,7 @@ window.__ModuleLoader__.load({
68
68
  roots: '本地目录',
69
69
  installRoot: '组件目录',
70
70
  dataRoot: '数据目录',
71
+ profileStore: 'CLI Profile',
71
72
  runtimeUrl: 'Runtime 地址',
72
73
  nextTitle: '后续配置',
73
74
  nextCopy: 'Skills 已通过 DeepSeek Harness 原生注册表提供给所有工作区;每个会话直接使用自己的工作目录。Runtime 启动成功后,将继续进入数据库连接与语义层向导。',
@@ -116,6 +117,7 @@ window.__ModuleLoader__.load({
116
117
  roots: 'Local directories',
117
118
  installRoot: 'Components',
118
119
  dataRoot: 'Data',
120
+ profileStore: 'CLI profiles',
119
121
  runtimeUrl: 'Runtime URL',
120
122
  nextTitle: 'Next configuration',
121
123
  nextCopy: 'Skills are provided to every workspace through the native DeepSeek Harness registry, and each session uses its own working directory. After Runtime starts, the database connection and semantic-layer wizard comes next.',
@@ -288,6 +290,7 @@ window.__ModuleLoader__.load({
288
290
  jsxs('dl', { children: [
289
291
  jsx('dt', { children: t('installRoot') }), jsx('dd', { children: status.roots.installRoot }),
290
292
  jsx('dt', { children: t('dataRoot') }), jsx('dd', { children: status.roots.dataRoot }),
293
+ jsx('dt', { children: t('profileStore') }), jsx('dd', { children: status.roots.profileStore }),
291
294
  status.runtimeUrl ? jsx('dt', { children: t('runtimeUrl') }) : null,
292
295
  status.runtimeUrl ? jsx('dd', { children: status.runtimeUrl }) : null,
293
296
  ] }),
package/lib/index.js CHANGED
@@ -20,17 +20,20 @@ function defaultRoots() {
20
20
  return {
21
21
  installRoot: join(base, 'Foggy', 'DeepSeekHarness'),
22
22
  dataRoot: join(base, 'Foggy', 'DeepSeekHarnessData'),
23
+ profileStore: process.env.FOGGY_RUNTIME_PROFILE_STORE || join(base, 'Foggy', 'DeepSeekHarnessData', 'cli-profiles'),
23
24
  }
24
25
  }
25
26
  const home = process.env.HOME
26
27
  if (!home) throw new Error('HOME is not set')
28
+ const dataRoot = process.env.XDG_STATE_HOME
29
+ ? join(process.env.XDG_STATE_HOME, 'foggy', 'deepseek-harness')
30
+ : join(home, '.local', 'state', 'foggy', 'deepseek-harness')
27
31
  return {
28
32
  installRoot: process.env.XDG_DATA_HOME
29
33
  ? join(process.env.XDG_DATA_HOME, 'foggy', 'deepseek-harness')
30
34
  : join(home, '.local', 'share', 'foggy', 'deepseek-harness'),
31
- dataRoot: process.env.XDG_STATE_HOME
32
- ? join(process.env.XDG_STATE_HOME, 'foggy', 'deepseek-harness')
33
- : join(home, '.local', 'state', 'foggy', 'deepseek-harness'),
35
+ dataRoot,
36
+ profileStore: process.env.FOGGY_RUNTIME_PROFILE_STORE || join(dataRoot, 'cli-profiles'),
34
37
  }
35
38
  }
36
39
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foggy-projects/deepseek-harness-plugin",
3
- "version": "0.4.0-beta.5",
3
+ "version": "0.4.0-beta.6",
4
4
  "description": "Foggy Java data analysis engine integration for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -55,6 +55,6 @@
55
55
  },
56
56
  "scripts": {
57
57
  "check": "node --check lib/index.js && node --check lib/skill-provider.js && node --check lib/client.js && node --check lib/typert.js && node --check lib/remote.js",
58
- "test": "node --test test/package.test.js"
58
+ "test": "node --test test/package.test.js && python test/onboarding_unit.py"
59
59
  }
60
60
  }
@@ -24,6 +24,12 @@ copy this Skill into the current workspace.
24
24
  analysis Skill is missing or invalid, ask the user to open the Foggy plugin settings and use
25
25
  **Re-download / Repair**. Repair restores the global managed analysis Skill and invalidates the
26
26
  native DSH Skill catalog. Reinstall or upgrade the plugin itself to restore this bundled Skill.
27
+ - Treat the current DSH session workspace as the authoritative `projectRoot` for the whole onboarding
28
+ run. Do not redirect semantic drafts or contracts to a different repository merely because another
29
+ Skill or example was found there. Place non-secret contracts below
30
+ `<projectRoot>/.foggy/onboarding-contracts/<profile>/`, drafts below
31
+ `<projectRoot>/.foggy/onboarding-drafts/<profile>/`, and published files below the approved
32
+ project-relative `modelsDir`.
27
33
 
28
34
  ## Mandatory orchestration boundary
29
35
 
@@ -46,6 +52,9 @@ For every new-database onboarding session, this Skill is the orchestration autho
46
52
  - Never read query-execution evidence back into the conversation. Report only validation state,
47
53
  execution state, row count, and evidence path; do not report row values or generated SQL containing
48
54
  business literals.
55
+ - Prefer one composite command per approval boundary. Do not inspect this Skill's Python implementation
56
+ or the CLI package source during a normal run; use the documented command contract and inspect code
57
+ only after a structured wrapper error requires troubleshooting.
49
58
 
50
59
  ## Boundaries
51
60
 
@@ -70,10 +79,12 @@ For every new-database onboarding session, this Skill is the orchestration autho
70
79
  4. Load `foggy-ai-analysis` from the native DSH Skill registry. Do not require a workspace copy.
71
80
  5. For a new business database, read [references/onboarding-workflow.md](references/onboarding-workflow.md)
72
81
  and prefer its two composite `onboard-datasource-run` / `onboard-semantic-run` commands. Require the
73
- trusted operator to create the private CLI profile outside Harness. Accept only the opaque profile
82
+ trusted operator to create the private CLI profile outside Harness. Unless the operator explicitly
83
+ overrides it, use the persistent profile store reported by the wrapper under the Foggy data root
84
+ (`<dataRoot>/cli-profiles`), never `/tmp`. Accept only the opaque profile
74
85
  ID, exact revision, datasource name/type, and namespace; never request JDBC URL, username,
75
86
  password, or password environment-variable name in Harness.
76
- 6. After schema discovery, use `foggy-ai-analysis` only to author TM/QM files in a separate project-local
87
+ 6. After schema discovery, use `foggy-ai-analysis` only to author TM/QM files in the standard project-local
77
88
  draft directory. Register, validate, publish, and verify them through this Skill's wrapper using the deterministic commands in
78
89
  [references/onboarding-workflow.md](references/onboarding-workflow.md). Do not publish, prune, replace
79
90
  a bundle, or execute a business-data query without the matching explicit flag and user approval.
@@ -98,5 +109,10 @@ In DeepSeek Harness, do not expand the composite onboarding commands back into t
98
109
  operations. This order documents what the wrapper enforces internally and becomes a direct CLI workflow
99
110
  only after onboarding is complete.
100
111
 
112
+ Composite commands are checkpointed and idempotent: a retry skips completed datasource, validation,
113
+ publication, and verification phases when the approved contract and draft digest are unchanged. Fix a
114
+ query payload in place and rerun the same semantic composite command; do not remove a successfully
115
+ published bundle merely to recover from a later query-validation failure.
116
+
101
117
  Keep user business data separate from the sales-drop SQLite demo. Prefer a read-only database account,
102
118
  opaque CLI profile references, and bounded query limits.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": "foggy-deepseek-onboarding-versions/v1",
3
- "packageVersion": "0.4.0-beta.5",
3
+ "packageVersion": "0.4.0-beta.6",
4
4
  "validatedAt": "2026-09-01",
5
5
  "components": {
6
6
  "deepseekHarness": {
@@ -23,6 +23,16 @@ to paste a password and do not put a JDBC URL, username, password environment-va
23
23
  line, logs, or evidence. For non-SQLite databases, ask the user to set the named environment variable
24
24
  before Runtime starts so the Java process inherits it. Recommend a read-only database account.
25
25
 
26
+ Use the current DSH session workspace as `projectRoot`. Store approved non-secret contracts at
27
+ `.foggy/onboarding-contracts/<profile>/`, semantic drafts at `.foggy/onboarding-drafts/<profile>/`, and
28
+ command evidence at `.foggy/onboarding-command-evidence/<profile>/`. Do not split these files across the
29
+ session workspace and a separate example repository. The wrapper rejects a query payload outside the
30
+ recorded project root before any semantic validation or publication mutation occurs.
31
+
32
+ The wrapper defaults `FOGGY_RUNTIME_PROFILE_STORE` to the private persistent directory
33
+ `<dataRoot>/cli-profiles`. An explicit operator-provided value still wins. Do not use `/tmp` for a profile
34
+ that must survive a WSL or Harness restart.
35
+
26
36
  Treat every user-supplied identifier and bound as immutable input: profile, datasource, namespace,
27
37
  models directory, bundle name, TM/QM name, query fields, and limit. Do not swap in demo names, add
28
38
  fields, raise the limit, or introduce replacement flags. If one of these inputs is missing, pause for
@@ -53,6 +63,13 @@ onboard-semantic-run --semantic-plan <approved-json> --query-payload <approved-j
53
63
  Include an approval flag only after the user approves that exact action. Without it, the composite
54
64
  command stops after the corresponding dry-run and returns `phaseStatus=awaiting-...-approval`.
55
65
 
66
+ Both composite commands are resumable. When the approved contract is unchanged, they skip completed
67
+ checkpoints instead of re-adding an existing datasource, revalidating an already published draft, or
68
+ registering the same bundle twice. If query validation fails after publication, correct the project-local
69
+ payload and rerun `onboard-semantic-run`; it resumes at query verification and leaves the active bundle in
70
+ place. A same-name datasource is accepted idempotently only when its public name and database type match
71
+ the approved plan; otherwise replacement still requires explicit approval.
72
+
56
73
  The granular commands below remain available for manual troubleshooting and resumption. Do not expand
57
74
  the composite commands into this list during a normal Harness turn.
58
75
 
@@ -111,6 +128,11 @@ requires an integer `limit` from 1 through 100. The first `semantic-verify` call
111
128
  validates the query; only `--execute` reads business data. Full results stay in evidence and the command
112
129
  returns only counts and paths.
113
130
 
131
+ Before the first semantic mutation, the composite command checks that the query payload is inside
132
+ `projectRoot`, has a bounded limit, and targets a query model declared in the approved semantic plan.
133
+ After publication, `semantic-verify` always describes the live model before validation. If a field is
134
+ rejected, update the payload from those described names and rerun the same composite command.
135
+
114
136
  Do not open, summarize, or quote `query-execute.json` or the Runtime's generated SQL after execution.
115
137
  The conversational result may contain only `queryValidated`, `queryExecuted`, `rowCount`, the model
116
138
  name, and the evidence path. A successful direct CLI command is not proof that onboarding completed;
@@ -448,6 +448,33 @@ def onboarding_state_path(data_root: Path, profile: str) -> Path:
448
448
  return data_root / "onboarding" / "profiles" / f"{safe_profile(profile)}.json"
449
449
 
450
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
+
451
478
  def read_onboarding_state(data_root: Path, profile: str, required: bool = True) -> dict | None:
452
479
  path = onboarding_state_path(data_root, profile)
453
480
  if not path.is_file():
@@ -802,10 +829,12 @@ def install_command(args: argparse.Namespace) -> dict:
802
829
  assert_managed_root(data_root, "Data root")
803
830
  if install_root == data_root:
804
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")
805
833
  plan = {
806
834
  "schemaVersion": "foggy-deepseek-onboarding-plan/v1",
807
835
  "installRoot": str(install_root),
808
836
  "dataRoot": str(data_root),
837
+ "profileStore": str(profile_store),
809
838
  "workspaceMode": "dsh-session-cwd",
810
839
  "versions": {name: value.get("version") for name, value in components.items()},
811
840
  "operations": ["install isolated CLI", "verify Launcher assets", "install global analysis Skill", "write install state"],
@@ -813,6 +842,7 @@ def install_command(args: argparse.Namespace) -> dict:
813
842
  }
814
843
  if args.dry_run:
815
844
  return {"success": True, "dryRun": True, "plan": plan}
845
+ profile_store = configure_profile_store(data_root)
816
846
  progress = ProgressReporter(
817
847
  getattr(args, "progress_file", None),
818
848
  getattr(args, "operation_id", None),
@@ -945,6 +975,7 @@ def install_command(args: argparse.Namespace) -> dict:
945
975
  "packageVersion": versions["packageVersion"],
946
976
  "installRoot": str(install_root),
947
977
  "dataRoot": str(data_root),
978
+ "profileStore": str(profile_store),
948
979
  "workspaceMode": "dsh-session-cwd",
949
980
  "cli": {"version": cli_component["version"], "command": str(cli_command), "mode": cli_mode},
950
981
  "launcher": {"version": components["launcher"]["version"], "path": str(launcher_dir)},
@@ -1163,6 +1194,7 @@ def onboarding_context(args: argparse.Namespace, require_runtime: bool = False)
1163
1194
  install_state = read_install_state(install_root)
1164
1195
  data_root = normalized(args.data_root or install_state["dataRoot"])
1165
1196
  assert_managed_root(data_root, "Data root")
1197
+ configure_profile_store(data_root, create=False)
1166
1198
  runtime_state_path = data_root / "runtime-state.json"
1167
1199
  runtime_state = read_json_object(runtime_state_path, "Runtime state") if runtime_state_path.is_file() else None
1168
1200
  if runtime_state and runtime_state.get("schemaVersion") != RUNTIME_STATE_SCHEMA:
@@ -1222,6 +1254,28 @@ def require_opaque_profile_cli(install_state: dict) -> None:
1222
1254
  )
1223
1255
 
1224
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
+
1225
1279
  def onboarding_plan_command(args: argparse.Namespace) -> dict:
1226
1280
  install_root, install_state, data_root, runtime_state = onboarding_context(args, require_runtime=False)
1227
1281
  profile = safe_profile(args.profile)
@@ -1283,7 +1337,9 @@ def onboarding_plan_command(args: argparse.Namespace) -> dict:
1283
1337
 
1284
1338
  def require_profile(args: argparse.Namespace, require_runtime: bool = False) -> tuple[dict, dict, Path, dict | None]:
1285
1339
  _install_root, install_state, data_root, runtime_state = onboarding_context(args, require_runtime=require_runtime)
1286
- state = read_onboarding_state(data_root, safe_profile(args.profile))
1340
+ profile = resolve_profile_name(data_root, getattr(args, "profile", None))
1341
+ args.profile = profile
1342
+ state = read_onboarding_state(data_root, profile)
1287
1343
  if normalized(state["installRoot"]) != normalized(install_state["installRoot"]):
1288
1344
  raise OnboardingError("Onboarding profile belongs to a different install root")
1289
1345
  return state, install_state, data_root, runtime_state
@@ -1329,10 +1385,31 @@ def datasource_configure_command(args: argparse.Namespace) -> dict:
1329
1385
  label = "datasources add"
1330
1386
  if args.replace:
1331
1387
  command.append("--replace")
1332
- result = redact_connection_material(
1333
- cli_json(install_state, runtime_state, connection["namespace"], command, label)
1334
- )
1335
- mark_step(state, "datasourceConfigured", "completed", replace=args.replace)
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)
1336
1413
  path = write_onboarding_state(data_root, state)
1337
1414
  return {
1338
1415
  "success": True,
@@ -1340,6 +1417,7 @@ def datasource_configure_command(args: argparse.Namespace) -> dict:
1340
1417
  "profile": state["profile"],
1341
1418
  "statePath": str(path),
1342
1419
  "dataSource": connection["name"],
1420
+ "alreadyPresent": already_present,
1343
1421
  "runtime": result,
1344
1422
  "next": "run datasource-verify; add --bind to approve namespace binding",
1345
1423
  "productionReady": False,
@@ -1798,7 +1876,16 @@ def semantic_verify_command(args: argparse.Namespace) -> dict:
1798
1876
  )
1799
1877
  atomic_json(evidence_dir / "query-execute.json", executed)
1800
1878
  row_count = response_row_count(executed)
1801
- mark_step(state, "semanticVerified", "completed", queryModel=query_model, queryValidated=True, queryExecuted=True)
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
+ )
1802
1889
  state.setdefault("artifacts", {})["semanticVerifyEvidence"] = str(evidence_dir)
1803
1890
  path = write_onboarding_state(data_root, state)
1804
1891
  return {
@@ -1818,14 +1905,15 @@ def semantic_verify_command(args: argparse.Namespace) -> dict:
1818
1905
 
1819
1906
 
1820
1907
  def next_onboarding_action(state: dict) -> dict:
1908
+ profile_flag = f" --profile {state['profile']}"
1821
1909
  ordered = [
1822
- ("datasourceConfigured", "datasource-configure --apply"),
1823
- ("datasourceVerified", "datasource-verify --bind"),
1824
- ("schemaDiscovered", "schema-discover"),
1825
- ("semanticDrafted", "semantic-draft --semantic-plan <json>"),
1826
- ("semanticValidated", "semantic-validate --apply"),
1827
- ("semanticPublished", "semantic-publish --apply"),
1828
- ("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"),
1829
1917
  ]
1830
1918
  for name, command in ordered:
1831
1919
  current = state.get("steps", {}).get(name, {})
@@ -1838,7 +1926,7 @@ def next_onboarding_action(state: dict) -> dict:
1838
1926
  "instruction": "Inspect semantic publish evidence and repair refresh before any republish attempt.",
1839
1927
  }
1840
1928
  if name == "semanticValidated" and current.get("status") == "failed":
1841
- command = "repair TM/QM, then semantic-draft --semantic-plan <json>"
1929
+ command = f"repair TM/QM, then semantic-draft{profile_flag} --semantic-plan <json>"
1842
1930
  return {"step": name, "command": command, "status": current.get("status", "pending")}
1843
1931
  return {"step": None, "command": None, "status": "completed"}
1844
1932
 
@@ -1883,6 +1971,21 @@ def save_composite_result(evidence_dir: Path, name: str, payload: dict, files: l
1883
1971
  files.append(str(path))
1884
1972
 
1885
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
+
1886
1989
  def datasource_run_command(args: argparse.Namespace) -> dict:
1887
1990
  _install_root, install_state, data_root, _runtime_state = onboarding_context(args, require_runtime=True)
1888
1991
  project_root = normalized(args.project_root or Path.cwd())
@@ -1902,6 +2005,11 @@ def datasource_run_command(args: argparse.Namespace) -> dict:
1902
2005
  if existing:
1903
2006
  if existing.get("connection") != requested_connection:
1904
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
+ )
1905
2013
  plan_result = {
1906
2014
  "success": True,
1907
2015
  "schemaVersion": "foggy-deepseek-onboarding-plan-result/v1",
@@ -1921,59 +2029,81 @@ def datasource_run_command(args: argparse.Namespace) -> dict:
1921
2029
  replace_plan=False,
1922
2030
  ))
1923
2031
  save_composite_result(evidence_dir, "01-plan.json", plan_result, files)
2032
+ state = read_onboarding_state(data_root, profile)
1924
2033
 
1925
- configure_dry = datasource_configure_command(argparse.Namespace(
1926
- install_root=args.install_root, data_root=args.data_root, profile=profile, apply=False, replace=False,
1927
- ))
1928
- save_composite_result(evidence_dir, "02-datasource-dry.json", configure_dry, files)
1929
- if not args.approve_configure:
1930
- return {
1931
- "success": True,
1932
- "schemaVersion": "foggy-deepseek-datasource-run/v1",
1933
- "profile": profile,
1934
- "phaseStatus": "awaiting-configure-approval",
1935
- "evidenceDir": str(evidence_dir),
1936
- "evidenceFiles": files,
1937
- "next": "rerun with --approve-configure after the datasource mutation is approved",
1938
- "productionReady": False,
1939
- }
1940
-
1941
- configured = datasource_configure_command(argparse.Namespace(
1942
- install_root=args.install_root, data_root=args.data_root, profile=profile, apply=True, replace=False,
1943
- ))
1944
- save_composite_result(evidence_dir, "03-datasource-apply.json", configured, files)
1945
- tested = datasource_verify_command(argparse.Namespace(
1946
- install_root=args.install_root, data_root=args.data_root, profile=profile, bind=False,
1947
- ))
1948
- save_composite_result(evidence_dir, "04-datasource-test.json", tested, files)
1949
- if not args.approve_bind:
1950
- return {
1951
- "success": True,
1952
- "schemaVersion": "foggy-deepseek-datasource-run/v1",
1953
- "profile": profile,
1954
- "phaseStatus": "awaiting-bind-approval",
1955
- "evidenceDir": str(evidence_dir),
1956
- "evidenceFiles": files,
1957
- "next": "rerun with --approve-configure --approve-bind after namespace binding is approved",
1958
- "productionReady": False,
1959
- }
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)
1960
2059
 
1961
- bound = datasource_verify_command(argparse.Namespace(
1962
- install_root=args.install_root, data_root=args.data_root, profile=profile, bind=True,
1963
- ))
1964
- save_composite_result(evidence_dir, "05-datasource-bind.json", bound, files)
1965
- discovered = schema_discover_command(argparse.Namespace(
1966
- install_root=args.install_root,
1967
- data_root=args.data_root,
1968
- profile=profile,
1969
- schema=args.schema,
1970
- pattern=args.pattern,
1971
- table=args.table,
1972
- max_tables=args.max_tables,
1973
- list_only=False,
1974
- no_views=args.no_views,
1975
- include_indexes=args.include_indexes,
1976
- ))
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
+ ))
1977
2107
  save_composite_result(evidence_dir, "06-schema.json", discovered, files)
1978
2108
  return {
1979
2109
  "success": True,
@@ -1989,6 +2119,29 @@ def datasource_run_command(args: argparse.Namespace) -> dict:
1989
2119
  }
1990
2120
 
1991
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
+
1992
2145
  def semantic_run_command(args: argparse.Namespace) -> dict:
1993
2146
  approved_plan = validate_semantic_plan(read_json_object(normalized(args.semantic_plan), "Semantic plan"))
1994
2147
  if not approved_plan.get("profile"):
@@ -2000,6 +2153,17 @@ def semantic_run_command(args: argparse.Namespace) -> dict:
2000
2153
  profile_args.profile = profile
2001
2154
  state, _install_state, _data_root, _runtime_state = require_profile(profile_args, require_runtime=True)
2002
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")
2003
2167
  contract_evidence = approved_plan.get("evidenceDir")
2004
2168
  if contract_evidence and args.evidence_dir:
2005
2169
  if normalized(project_root / contract_evidence) != normalized(args.evidence_dir):
@@ -2007,78 +2171,141 @@ def semantic_run_command(args: argparse.Namespace) -> dict:
2007
2171
  evidence_dir = composite_evidence_dir(project_root, profile, str(project_root / contract_evidence) if contract_evidence else args.evidence_dir)
2008
2172
  files: list[str] = []
2009
2173
 
2010
- drafted = semantic_draft_command(argparse.Namespace(
2011
- install_root=args.install_root,
2012
- data_root=args.data_root,
2013
- profile=profile,
2014
- semantic_plan=args.semantic_plan,
2015
- ))
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"]
2016
2187
  save_composite_result(evidence_dir, "07-semantic-draft.json", drafted, files)
2017
- validate_dry = semantic_validate_command(argparse.Namespace(
2018
- install_root=args.install_root,
2019
- data_root=args.data_root,
2020
- profile=profile,
2021
- apply=False,
2022
- include_stack_trace=False,
2023
- ))
2024
- save_composite_result(evidence_dir, "08-semantic-validate-dry.json", validate_dry, files)
2025
- if not args.approve_validate:
2026
- return {
2027
- "success": True,
2028
- "schemaVersion": "foggy-deepseek-semantic-run/v1",
2029
- "profile": profile,
2030
- "phaseStatus": "awaiting-validate-approval",
2031
- "evidenceDir": str(evidence_dir),
2032
- "evidenceFiles": files,
2033
- "next": "rerun with --approve-validate after the validation catalog mutation is approved",
2034
- "productionReady": False,
2035
- }
2036
-
2037
- validated = semantic_validate_command(argparse.Namespace(
2038
- install_root=args.install_root,
2039
- data_root=args.data_root,
2040
- profile=profile,
2041
- apply=True,
2042
- include_stack_trace=False,
2043
- ))
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
+ ))
2044
2225
  save_composite_result(evidence_dir, "09-semantic-validate-apply.json", validated, files)
2045
- publish_dry = semantic_publish_command(argparse.Namespace(
2046
- install_root=args.install_root,
2047
- data_root=args.data_root,
2048
- profile=profile,
2049
- apply=False,
2050
- replace_bundle=False,
2051
- watch=False,
2052
- prune=False,
2053
- ))
2054
- save_composite_result(evidence_dir, "10-semantic-publish-dry.json", publish_dry, files)
2055
- if not args.approve_publish:
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)
2056
2290
  return {
2057
2291
  "success": True,
2058
2292
  "schemaVersion": "foggy-deepseek-semantic-run/v1",
2059
2293
  "profile": profile,
2060
- "phaseStatus": "awaiting-publish-approval",
2294
+ "phaseStatus": "completed",
2295
+ "resumed": True,
2296
+ "queryModel": declared_query_model,
2297
+ "queryValidated": True,
2298
+ "queryExecuted": True,
2299
+ "rowCount": verified_step.get("rowCount"),
2061
2300
  "evidenceDir": str(evidence_dir),
2062
2301
  "evidenceFiles": files,
2063
- "next": "rerun with --approve-validate --approve-publish after publication is approved",
2064
2302
  "productionReady": False,
2065
2303
  }
2066
-
2067
- published = semantic_publish_command(argparse.Namespace(
2068
- install_root=args.install_root,
2069
- data_root=args.data_root,
2070
- profile=profile,
2071
- apply=True,
2072
- replace_bundle=False,
2073
- watch=False,
2074
- prune=False,
2075
- ))
2076
- save_composite_result(evidence_dir, "11-semantic-publish-apply.json", published, files)
2077
2304
  query_validated = semantic_verify_command(argparse.Namespace(
2078
2305
  install_root=args.install_root,
2079
2306
  data_root=args.data_root,
2080
2307
  profile=profile,
2081
- query_model=args.query_model,
2308
+ query_model=declared_query_model,
2082
2309
  query_payload=args.query_payload,
2083
2310
  execute=False,
2084
2311
  ))
@@ -2101,7 +2328,7 @@ def semantic_run_command(args: argparse.Namespace) -> dict:
2101
2328
  install_root=args.install_root,
2102
2329
  data_root=args.data_root,
2103
2330
  profile=profile,
2104
- query_model=args.query_model,
2331
+ query_model=declared_query_model,
2105
2332
  query_payload=args.query_payload,
2106
2333
  execute=True,
2107
2334
  ))
@@ -2280,13 +2507,13 @@ def build_parser() -> argparse.ArgumentParser:
2280
2507
  status = sub.add_parser("onboard-status")
2281
2508
  status.add_argument("--install-root")
2282
2509
  status.add_argument("--data-root")
2283
- status.add_argument("--profile", default="default")
2510
+ status.add_argument("--profile")
2284
2511
  status.set_defaults(handler=onboarding_status_command)
2285
2512
 
2286
2513
  resume = sub.add_parser("onboard-resume")
2287
2514
  resume.add_argument("--install-root")
2288
2515
  resume.add_argument("--data-root")
2289
- resume.add_argument("--profile", default="default")
2516
+ resume.add_argument("--profile")
2290
2517
  resume.set_defaults(handler=onboarding_resume_command)
2291
2518
 
2292
2519
  datasource_run = sub.add_parser("onboard-datasource-run")
@@ -2322,7 +2549,7 @@ def build_parser() -> argparse.ArgumentParser:
2322
2549
  datasource_configure = sub.add_parser("datasource-configure")
2323
2550
  datasource_configure.add_argument("--install-root")
2324
2551
  datasource_configure.add_argument("--data-root")
2325
- datasource_configure.add_argument("--profile", default="default")
2552
+ datasource_configure.add_argument("--profile")
2326
2553
  datasource_configure.add_argument("--apply", action="store_true")
2327
2554
  datasource_configure.add_argument("--replace", action="store_true")
2328
2555
  datasource_configure.set_defaults(handler=datasource_configure_command)
@@ -2330,14 +2557,14 @@ def build_parser() -> argparse.ArgumentParser:
2330
2557
  datasource_verify = sub.add_parser("datasource-verify")
2331
2558
  datasource_verify.add_argument("--install-root")
2332
2559
  datasource_verify.add_argument("--data-root")
2333
- datasource_verify.add_argument("--profile", default="default")
2560
+ datasource_verify.add_argument("--profile")
2334
2561
  datasource_verify.add_argument("--bind", action="store_true")
2335
2562
  datasource_verify.set_defaults(handler=datasource_verify_command)
2336
2563
 
2337
2564
  schema_discover = sub.add_parser("schema-discover")
2338
2565
  schema_discover.add_argument("--install-root")
2339
2566
  schema_discover.add_argument("--data-root")
2340
- schema_discover.add_argument("--profile", default="default")
2567
+ schema_discover.add_argument("--profile")
2341
2568
  schema_discover.add_argument("--schema", action="append")
2342
2569
  schema_discover.add_argument("--pattern")
2343
2570
  schema_discover.add_argument("--table", action="append")
@@ -2350,14 +2577,14 @@ def build_parser() -> argparse.ArgumentParser:
2350
2577
  semantic_draft = sub.add_parser("semantic-draft")
2351
2578
  semantic_draft.add_argument("--install-root")
2352
2579
  semantic_draft.add_argument("--data-root")
2353
- semantic_draft.add_argument("--profile", default="default")
2580
+ semantic_draft.add_argument("--profile")
2354
2581
  semantic_draft.add_argument("--semantic-plan", required=True)
2355
2582
  semantic_draft.set_defaults(handler=semantic_draft_command)
2356
2583
 
2357
2584
  semantic_validate = sub.add_parser("semantic-validate")
2358
2585
  semantic_validate.add_argument("--install-root")
2359
2586
  semantic_validate.add_argument("--data-root")
2360
- semantic_validate.add_argument("--profile", default="default")
2587
+ semantic_validate.add_argument("--profile")
2361
2588
  semantic_validate.add_argument("--apply", action="store_true")
2362
2589
  semantic_validate.add_argument("--include-stack-trace", action="store_true")
2363
2590
  semantic_validate.set_defaults(handler=semantic_validate_command)
@@ -2365,7 +2592,7 @@ def build_parser() -> argparse.ArgumentParser:
2365
2592
  semantic_publish = sub.add_parser("semantic-publish")
2366
2593
  semantic_publish.add_argument("--install-root")
2367
2594
  semantic_publish.add_argument("--data-root")
2368
- semantic_publish.add_argument("--profile", default="default")
2595
+ semantic_publish.add_argument("--profile")
2369
2596
  semantic_publish.add_argument("--apply", action="store_true")
2370
2597
  semantic_publish.add_argument("--replace-bundle", action="store_true")
2371
2598
  semantic_publish.add_argument("--watch", action="store_true")
@@ -2375,7 +2602,7 @@ def build_parser() -> argparse.ArgumentParser:
2375
2602
  semantic_verify = sub.add_parser("semantic-verify")
2376
2603
  semantic_verify.add_argument("--install-root")
2377
2604
  semantic_verify.add_argument("--data-root")
2378
- semantic_verify.add_argument("--profile", default="default")
2605
+ semantic_verify.add_argument("--profile")
2379
2606
  semantic_verify.add_argument("--query-model")
2380
2607
  semantic_verify.add_argument("--query-payload", required=True)
2381
2608
  semantic_verify.add_argument("--execute", action="store_true")