@foggy-projects/deepseek-harness-plugin 0.4.0-beta.14 → 0.4.0-beta.16

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
@@ -14,7 +14,7 @@ modify `PATH`, or register Python globally. Advanced users may explicitly set
14
14
  ## Local beta installation
15
15
 
16
16
  ```powershell
17
- dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.0-beta.14.tgz
17
+ dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.0-beta.16.tgz
18
18
  ```
19
19
 
20
20
  Restart `dsh web`, use the browser it opens (or the complete printed URL,
@@ -30,9 +30,13 @@ dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-p
30
30
  For development, `FOGGY_ASSET_CACHE_DIRS` can contain platform-delimited verified
31
31
  asset-cache directories. It is not required for a normal install.
32
32
 
33
- Database credentials remain outside the ordinary DSH settings document. The
34
- bundled onboarding Skill drives datasource discovery, semantic drafting,
35
- checkpointed publication, and bounded read-only query verification.
33
+ The bundled onboarding Skill is intentionally development-first. Users may
34
+ provide a local datasource password in the conversation, a user-managed
35
+ connection file, an Agent environment variable, or a compatible Runtime
36
+ Console. Direct passwords are submitted to the already-running Runtime without
37
+ being copied into onboarding state or evidence. The Skill then drives schema
38
+ discovery, semantic drafting, local Bundle registration, and bounded query
39
+ verification without restarting Runtime.
36
40
 
37
41
  ## Native Skill and workspace contract
38
42
 
@@ -42,14 +46,12 @@ are available in every DSH workspace without copying or symlinking `.agents`.
42
46
  The current session `cwd` remains the workspace boundary for semantic drafts and
43
47
  evidence.
44
48
 
45
- Opaque CLI profiles default to the private persistent
46
- `<dataRoot>/cli-profiles` directory. Composite onboarding commands are
49
+ Opaque CLI profiles remain available for users who prefer them and default to
50
+ the private persistent `<dataRoot>/cli-profiles` directory, but they are not a
51
+ prerequisite for ordinary development. Composite onboarding commands are
47
52
  idempotent: unchanged completed phases are resumed rather than re-adding a
48
- datasource or re-registering a published bundle. Settings detects legacy
49
- temporary profiles and offers an explicit, validated migration into the
50
- persistent store. A completed profile can also be bound non-destructively to an
51
- additional DSH workspace when its reviewed connection contract and published
52
- semantic digest are unchanged.
53
+ datasource or re-registering a local Bundle. Settings detects legacy temporary
54
+ profiles and offers an explicit, validated migration into the persistent store.
53
55
 
54
56
  Private Python, CLI, Launcher, the analysis Skill, install state, and Runtime
55
57
  state live in the user-level Foggy component directories. The managed CLI is intentionally isolated
@@ -73,9 +75,11 @@ same wildcard binding used by the Java server, so a conflicting application or W
73
75
  port proxy produces an immediate, high-visibility error instead of a readiness timeout.
74
76
  The CLI and Skills resolve the resulting stable Runtime URL from managed state.
75
77
 
76
- This beta remains a local dev/test integration. The bundled Runtime reports
77
- `securityMode=none-dev-test-only` and must not be exposed to a network or used as
78
- a production service.
78
+ This beta remains a local dev/test integration. It does not automatically extend
79
+ local credentials or approvals into a formal environment. Production model
80
+ publication should use a separate manual or dedicated deployment workflow with
81
+ an explicit target, model Git commit/tag, credentials, verification, and
82
+ rollback plan.
79
83
 
80
84
  See [`docs/PUBLIC-BETA-READINESS.md`](./docs/PUBLIC-BETA-READINESS.md) for the
81
85
  tested public Beta scope, release gates, and stable-release blockers.
@@ -1,10 +1,10 @@
1
- # Public beta readiness: 0.4.0-beta.14
1
+ # Public beta readiness: 0.4.0-beta.16
2
2
 
3
- Assessment date: 2026-09-04
3
+ Assessment date: 2026-09-05
4
4
 
5
5
  ## Verdict
6
6
 
7
- `0.4.0-beta.14` is suitable for a scoped public beta on Windows 10/11 x64 and
7
+ `0.4.0-beta.16` is suitable for a scoped public beta on Windows 10/11 x64 and
8
8
  Linux/WSL2 x64. It is not a general-availability release.
9
9
 
10
10
  The plugin package stays small and downloads large components only during
@@ -26,8 +26,24 @@ system PATH, registry, or a user's existing Python installation.
26
26
  - WSL2 reported the existing managed Python, CLI, Launcher, and analysis Skill
27
27
  as ready; an isolated Windows profile correctly reported its fresh component
28
28
  state and rejected the host's Java 12 as below the Java 17 prerequisite.
29
- - The UI reported Python 3.12.13 as `Foggy private`, and CLI 0.1.23, Launcher
30
- 0.1.18, analysis Skill 0.1.17, and onboarding Skill 0.4.0-beta.14 as installed.
29
+ - Onboarding is development-first: a direct password or Agent environment
30
+ variable is submitted to the public datasource API of an already-running
31
+ Runtime. The password is excluded from onboarding state and evidence, and
32
+ datasource setup does not trigger Runtime restart. Opaque profiles remain an
33
+ optional advanced path.
34
+ - A real Launcher 0.1.18 smoke on Java 17 accepted a synthetic inline-password
35
+ datasource through the public Runtime API while retaining the same Java PID.
36
+ The isolated registry confirmed the documented local-development plaintext
37
+ persistence boundary; the test datasource was then removed and the temporary
38
+ Runtime stopped.
39
+ - A full isolated `onboard-datasource-run` then created, tested, bound, and
40
+ inspected a SQLite datasource containing one table. Runtime retained the same
41
+ PID, while the persisted onboarding state and command evidence contained no
42
+ copy of the synthetic password.
43
+ - The package and settings contract report Python 3.12.13 as `Foggy private`,
44
+ CLI 0.1.23, Launcher 0.1.18, analysis Skill 0.1.17, and onboarding Skill
45
+ 0.4.0-beta.16; the same component display was accepted in the preceding
46
+ native Windows beta run.
31
47
  - Runtime startup now reports the current phase, elapsed/timeout seconds, and a
32
48
  bounded progress value. A real Windows launch with Temurin 17 reached readiness
33
49
  and passed capabilities in 18.9 seconds. A controlled post-launch Java exit was
@@ -47,8 +63,14 @@ system PATH, registry, or a user's existing Python installation.
47
63
  downloaded artifacts are not trusted until the complete SHA256 matches.
48
64
  - Windows progress writes tolerate transient sharing violations without losing
49
65
  the previous valid progress document or leaving temporary files behind.
50
- - Automated regression passed on Windows and Linux/WSL2: 20 Node tests and 17
51
- Python tests on each platform, plus shell and PowerShell syntax checks.
66
+ - Current automated regression passes 20 Node tests and 23 Python tests. The
67
+ new datasource tests cover secret-free persisted state, direct Runtime API
68
+ submission, CLI bypass for inline development credentials, stable inline
69
+ credential resumption, bounded transient connection retry, and output redaction.
70
+ - A real Java 17 / Launcher 0.1.18 regression resumed the same completed inline
71
+ datasource composite twice, retained the Runtime PID, and emitted a complete
72
+ `01` through `06` workspace evidence sequence without falling back to granular
73
+ commands.
52
74
 
53
75
  ## Beta boundaries
54
76
 
@@ -63,6 +85,10 @@ system PATH, registry, or a user's existing Python installation.
63
85
  - Runtime production authentication is outside this beta's default local
64
86
  onboarding mode. The UI and doctor continue to report `productionReady=false`
65
87
  for the development-only no-auth mode.
88
+ - Direct development passwords are stored by the current Runtime in its private
89
+ local datasource registry. Production publication is a separate manual or
90
+ future dedicated deployment workflow and should use the target environment's
91
+ credential controls.
66
92
 
67
93
  ## Release gate after publication
68
94
 
@@ -1,7 +1,7 @@
1
1
  # Native Windows Beta acceptance
2
2
 
3
3
  This runbook validates DeepSeek Harness `0.1.2-rc.1` with Foggy plugin
4
- `0.4.0-beta.14` on a clean 64-bit Windows 10 or Windows 11 machine. Use a local
4
+ `0.4.0-beta.16` on a clean 64-bit Windows 10 or Windows 11 machine. Use a local
5
5
  directory that is not synchronized by OneDrive.
6
6
 
7
7
  ## Prerequisites
@@ -54,7 +54,7 @@ Expected component state:
54
54
  - CLI 0.1.23;
55
55
  - Launcher 0.1.18;
56
56
  - analysis Skill 0.1.17;
57
- - onboarding Skill 0.4.0-beta.14;
57
+ - onboarding Skill 0.4.0-beta.16;
58
58
  - Java 17+ available;
59
59
  - native DSH Skill registration available.
60
60
 
@@ -74,21 +74,24 @@ starting again must clear the applicable conflict state.
74
74
 
75
75
  ## Database and semantic-layer acceptance
76
76
 
77
- Keep database credentials out of this public repository. Create a private CLI
78
- profile outside the DSH conversation, pass only its opaque profile ID and
79
- revision to the onboarding workflow, and use a test-only database account with
80
- read-only permissions.
77
+ Use a development or test database. For this local acceptance run, connection
78
+ details and a password may be supplied directly in the prompt or in a local
79
+ connection JSON file. The onboarding wrapper must configure the datasource
80
+ online without stopping or restarting an already-running Runtime. It must not
81
+ copy the password into onboarding state, evidence, TM/QM files, or Git.
81
82
 
82
83
  Use a natural prompt such as:
83
84
 
84
85
  ```text
85
- 请帮我用 Foggy 分析这个测试数据库。我已经按验收手册创建了私有连接
86
- profile。请先确认连接和表结构,再围绕订单数据创建一个简单的语义层,
87
- 包含订单数量、状态和开单时间,最后执行一个不超过 20 行的只读查询。
88
- 不要修改数据库,也不要在对话或验收文件中输出密码。
86
+ 请帮我用 Foggy 分析这个测试数据库:地址、数据库名、账号和密码分别是
87
+ <连接信息>。直接创建开发数据源并绑定一个合适的 namespace,不要重启
88
+ Runtime。读取表结构后,在当前工作目录创建订单语义模型,包含订单数量、
89
+ 状态和开单时间,调试到模型通过,并执行一个不超过 20 行的只读查询。
89
90
  ```
90
91
 
91
- Pass only when Runtime readiness and capabilities succeed, datasource metadata
92
- can be discovered, TM/QM models validate and publish, the bounded read-only
93
- query succeeds, diagnostics contain no credentials, and stopping Runtime frees
94
- the configured port (default `18166`).
92
+ Pass only when Runtime readiness and capabilities succeed, datasource setup
93
+ does not restart Runtime, metadata can be discovered, TM/QM models validate and
94
+ become effective in the local Runtime, the bounded read-only query succeeds,
95
+ diagnostics contain no credentials, and stopping Runtime frees the configured
96
+ port (default `18166`). Recommend Git management for the completed model files;
97
+ do not treat this acceptance run as production publication.
@@ -3,7 +3,7 @@ set -euo pipefail
3
3
 
4
4
  DSH_VERSION="0.1.2-rc.1"
5
5
  PNPM_VERSION="11.7.0"
6
- PLUGIN_VERSION="0.4.0-beta.14"
6
+ PLUGIN_VERSION="0.4.0-beta.16"
7
7
  PLUGIN_REF="v${PLUGIN_VERSION}"
8
8
  PLUGIN_REPOSITORY="https://github.com/foggy-projects/foggy-deepseek-harness-plugin.git"
9
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foggy-projects/deepseek-harness-plugin",
3
- "version": "0.4.0-beta.14",
3
+ "version": "0.4.0-beta.16",
4
4
  "description": "Foggy Java data analysis engine integration for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -1,137 +1,126 @@
1
1
  ---
2
2
  name: foggy-deepseek-onboarding
3
- description: Install and operate a pinned Foggy CLI-first dev/test environment from DeepSeek Harness, including opaque datasource profiles, resumable verification, and schema discovery before semantic authoring. Use for initial Foggy setup or local Runtime onboarding; do not use for production deployment or MCP configuration.
3
+ description: Set up and use Foggy Runtime for data-source exploration and semantic-model development inside DeepSeek Harness. Use for local experience, datasource onboarding, TM/QM authoring, validation, and development publication; route production deployment or updates to a separate release workflow.
4
4
  ---
5
5
 
6
6
  # Foggy DeepSeek onboarding
7
7
 
8
- Set up Foggy through shell and `foggy-runtime` CLI. Do not configure Foggy MCP for this local workflow.
9
-
10
- ## Native registry and installed-state gate
11
-
12
- This Skill is registered by the Foggy plugin through DeepSeek Harness's native Skill registry. Its
13
- resource base is the authoritative location for these scripts and references; do not search for or
14
- copy this Skill into the current workspace.
15
-
16
- - Resolve the global install state from the platform default component directory: on Linux use
17
- `${XDG_DATA_HOME:-$HOME/.local/share}/foggy/deepseek-harness/install-state.json`; on Windows use
18
- `%LOCALAPPDATA%\Foggy\DeepSeekHarness\install-state.json`.
19
- - Confirm the installation with this Skill's `doctor` wrapper and pass the current DSH session
20
- workspace as `--project-root`. The absence of `foggy-runtime` from `PATH` is not evidence that the
21
- managed CLI is missing; the plugin intentionally installs it in an isolated environment and records
22
- its absolute command in the global install state.
23
- - The plugin downloads and verifies a pinned private Python runtime before running this Skill. Do not
24
- search for, install, or repair a system Python. Wrappers resolve the interpreter recorded in the
25
- global install state. If private Python is missing, use the plugin's Python repair action. Only use
26
- `FOGGY_PYTHON` or `FOGGY_ONBOARDING_PYTHON` when the user explicitly supplied an advanced override.
27
- - Do not independently download or reinstall the CLI. If the global install state, managed marker, or
28
- analysis Skill is missing or invalid, ask the user to open the Foggy plugin settings and use
29
- the matching component repair action. Repair restores managed components and invalidates the
30
- native DSH Skill catalog. Reinstall or upgrade the plugin itself to restore this bundled Skill.
31
- - Treat the current DSH session workspace as the authoritative `projectRoot` for the whole onboarding
32
- run. Do not redirect semantic drafts or contracts to a different repository merely because another
33
- Skill or example was found there. Place non-secret contracts below
34
- `<projectRoot>/.foggy/onboarding-contracts/<profile>/`, drafts below
35
- `<projectRoot>/.foggy/onboarding-drafts/<profile>/`, and published files below the approved
36
- project-relative `modelsDir`.
37
-
38
- ## Mandatory orchestration boundary
39
-
40
- For every new-database onboarding session, this Skill is the orchestration authority until
41
- `onboard-status` reports `next.status=completed`:
42
-
43
- - Invoke datasource, schema, semantic, bundle, and query operations only through this Skill's
44
- `scripts/onboard.ps1` or `scripts/onboard.sh`. Do not call `foggy-runtime` directly for those
45
- operations, even if another loaded Skill documents equivalent CLI commands.
46
- - Use `foggy-ai-analysis` only to author TM/QM draft content. Its general direct-CLI workflow does not
47
- supersede this Skill's state machine, approval gates, names, paths, query limit, or evidence rules.
48
- - Use the exact profile, datasource, namespace, bundle, model names, paths, fields, and query limit
49
- supplied or confirmed by the user. Do not replace them with examples or inferred alternatives.
50
- - Do not use raw SQL to sample business rows during onboarding. `schema-discover` is the metadata gate;
51
- any later SQL probe requires separate explicit approval and must be bounded and read-only.
52
- - Never use `--replace`, `--replace-bundle`, `--prune`, `--watch`, or `--execute` unless that exact
53
- mutation was explicitly approved. Approval for adding a new resource is not approval to replace one.
54
- - Save each wrapper result as the single JSON object returned on stdout when the user requests evidence.
55
- Do not claim completion when required evidence is missing or the persisted status is incomplete.
56
- - Never read query-execution evidence back into the conversation. Report only validation state,
57
- execution state, row count, and evidence path; do not report row values or generated SQL containing
58
- business literals.
59
- - Prefer one composite command per approval boundary. Do not inspect this Skill's Python implementation
60
- or the CLI package source during a normal run; use the documented command contract and inspect code
61
- only after a structured wrapper error requires troubleshooting.
62
-
63
- ## Boundaries
64
-
65
- - Treat the bundled Launcher as local dev/test only. Its expected security mode is
66
- `none-dev-test-only`; never expose it to a network.
67
- - Do not print, persist, or request secrets in chat. Use named environment variables or a private env
68
- file outside the Skill and evidence directories.
69
- - Do not modify Foggy engine or CLI source. If setup cannot continue without such a change, stop and
70
- ask the user for explicit authorization.
71
- - Before downloads, installs, replacement, Runtime start/stop, or removal, state the concrete action
72
- and obtain any authorization required by the host.
73
-
74
- ## Workflow
75
-
76
- 1. Run `scripts/doctor.ps1 --project-root <current-session-workspace>` on Windows or
77
- `bash scripts/doctor.sh --project-root <current-session-workspace>` on Linux.
78
- 2. If private Python, the pinned CLI, Launcher, or global managed analysis Skill is missing, use the
79
- Foggy plugin's matching Repair action. First-time Python bootstrap requires the plugin UI unless
80
- the user explicitly supplies `FOGGY_ONBOARDING_PYTHON`. Use a matching install script only after
81
- private Python exists and the plugin UI is unavailable; use `--dry-run` first when paths or
82
- permissions are uncertain.
83
- 3. Run `runtime-start` and require successful `wait-ready` plus `capabilities`. Record engine,
84
- Runtime API version, schema version, security mode, URL, namespace, PID, and evidence path. If the
85
- recorded Runtime is already running, `runtime-start` verifies and reuses it instead of starting a
86
- second process.
87
- 4. Load `foggy-ai-analysis` from the native DSH Skill registry. Do not require a workspace copy.
88
- 5. For a new business database, read [references/onboarding-workflow.md](references/onboarding-workflow.md)
89
- and prefer its two composite `onboard-datasource-run` / `onboard-semantic-run` commands. Require the
90
- trusted operator to create the private CLI profile outside Harness. Unless the operator explicitly
91
- overrides it, use the persistent profile store reported by the wrapper under the Foggy data root
92
- (`<dataRoot>/cli-profiles`), never `/tmp`. Accept only the opaque profile
93
- ID, exact revision, datasource name/type, and namespace; never request JDBC URL, username,
94
- password, or password environment-variable name in Harness.
95
- If plugin settings report a legacy temporary profile, use the explicit migration action before
96
- onboarding. It moves only validated connection metadata and environment-variable references; it
97
- never copies a password value and leaves a recoverable private backup below the Foggy data root.
98
- 6. After schema discovery, use `foggy-ai-analysis` only to author TM/QM files in the standard project-local
99
- draft directory. Register, validate, publish, and verify them through this Skill's wrapper using the deterministic commands in
100
- [references/onboarding-workflow.md](references/onboarding-workflow.md). Do not publish, prune, replace
101
- a bundle, or execute a business-data query without the matching explicit flag and user approval.
102
- 7. Stop only the Runtime PID recorded by this package. Preserve Runtime data unless the user explicitly
103
- requests purge.
104
-
105
- ## Command contract
106
-
107
- All package scripts return one JSON object on stdout. Treat `success=false` or a nonzero exit code as a
108
- failure. Do not infer readiness from a fixed sleep; require CLI `wait-ready` and `capabilities`.
109
-
110
- Use this analysis order after setup:
8
+ Use this Skill for the normal DeepSeek Harness experience: connect a development datasource, inspect
9
+ it, create TM/QM files in the current workspace, and iterate until queries pass. Use the managed
10
+ `foggy-runtime` CLI and this Skill's wrappers; do not configure Foggy MCP for this local workflow.
11
+
12
+ ## Product boundary
13
+
14
+ - Treat the plugin and bundled Launcher as local experience and development tooling.
15
+ - Datasources, namespace bindings, model Bundles, refreshes, and query checks are online Runtime
16
+ operations. They do not require a Runtime restart.
17
+ - Start Runtime only when it is absent. Reuse a healthy running Runtime. Restart only when the user
18
+ asks, a Launcher/JAR upgrade requires it, Runtime is unhealthy, or an actual startup setting such as
19
+ port, JVM options, Runtime authentication, or an opt-in module changes.
20
+ - Do not turn production publication into a continuation of local onboarding. When the user wants to
21
+ deploy or update a formal environment, recommend a manual release or a dedicated production
22
+ deployment workflow with separately supplied target access, credentials, version, verification, and
23
+ rollback information.
24
+ - Do not modify Foggy engine or CLI source. If progress genuinely requires either change, stop and ask
25
+ for explicit authorization.
26
+
27
+ ## Managed installation
28
+
29
+ This Skill is registered through DeepSeek Harness's native Skill registry. Its scripts and references
30
+ are authoritative; do not copy the Skill into the current workspace.
31
+
32
+ - Linux install state: `${XDG_DATA_HOME:-$HOME/.local/share}/foggy/deepseek-harness/install-state.json`.
33
+ - Windows install state: `%LOCALAPPDATA%\Foggy\DeepSeekHarness\install-state.json`.
34
+ - Run this Skill's `doctor` wrapper with the current DSH workspace as `--project-root`. The managed CLI
35
+ is intentionally isolated, so absence from `PATH` does not mean it is missing.
36
+ - Use plugin settings to initialize or repair private Python, CLI, Launcher, or the managed analysis
37
+ Skill. Do not independently reinstall them during a normal analysis session.
38
+ - Treat the current DSH workspace as `projectRoot`. Keep model drafts and final model files there rather
39
+ than redirecting them to an example repository.
40
+
41
+ ## Development workflow
42
+
43
+ 1. Run `doctor`. Start Runtime only if it is not already healthy; require `wait-ready` and
44
+ `capabilities` after a new start.
45
+ 2. Accept datasource connection details from the user's message, a user-supplied local JSON file, an
46
+ environment variable, or Runtime Console. Direct `password` is supported for local development.
47
+ The wrapper submits it to the public Runtime API without copying it into onboarding state or
48
+ evidence. Do not echo it in the response.
49
+ 3. Create or select the namespace, add/test the datasource online, and bind it to the namespace. Do
50
+ not stop or restart Runtime to make a password available. A running Runtime can accept a direct
51
+ development password through its datasource API.
52
+ 4. Inspect tables, columns, keys, and relationships. Small, bounded, read-only SQL samples are allowed
53
+ when they help infer captions, enums, units, or date semantics. Do not run mutations unless the user
54
+ explicitly requests them.
55
+ 5. Load `foggy-ai-analysis` from the native registry only for TM/QM authoring and query tuning; its
56
+ generic installation, datasource-secret, and production-deployment guidance does not override this
57
+ development workflow. Default to a project-local `models/` directory unless the user specifies
58
+ another directory.
59
+ 6. Iterate through model validation, Bundle registration/update, model refresh/describe, query
60
+ validation, and bounded query execution. Development publication means making the local model
61
+ directory effective in this local Runtime; it is not a production release.
62
+ 7. When the model works, recommend committing the model directory to the user's own Git repository.
63
+ Do not initialize a repository, commit, push, or create a remote unless the user requests it.
64
+
65
+ For a normal end-to-end request, take the documented composite-command path first. Do not inspect
66
+ `onboarding.py`, enumerate CLI help, search installation directories, load the sales-drop example, or
67
+ fetch online syntax documentation before trying that path. Use those troubleshooting or reference
68
+ sources only when a concrete error or unsupported modeling requirement makes them relevant.
69
+
70
+ Use the user's business terms to narrow schema discovery with `--pattern` or repeated `--table` when
71
+ there are clear candidates. Otherwise keep the bounded default, then inspect only the few tables and
72
+ columns needed for the requested model. Full results belong in the evidence files; retain concise
73
+ summaries and paths in the conversation instead of repeatedly reading complete JSON artifacts.
74
+
75
+ Read [references/onboarding-workflow.md](references/onboarding-workflow.md) for the wrapper commands and
76
+ credential shapes. Use `foggy-ai-analysis` references for detailed TM/QM modeling and query tuning.
77
+
78
+ ## Credential choices for local development
79
+
80
+ Choose the simplest source the user provides:
81
+
82
+ - `password` in a connection file: simplest for an experience session; the source file remains under
83
+ the user's control and should normally stay outside Git.
84
+ - `passwordEnv`: the wrapper reads the variable from the Agent process and submits the value online;
85
+ Runtime does not need to inherit it at startup.
86
+ - Opaque profile: optional for users who already have one; never require it for ordinary onboarding.
87
+ - Runtime Console: when a Launcher exposing Runtime Console is installed, the user may enter the
88
+ connection there using the management token shown by the host/plugin.
89
+
90
+ Keep only a minimal development safety baseline: do not echo passwords, put them in TM/QM files,
91
+ include them in evidence/diagnostics, or commit them to Git. Do not impose production IAM, audit,
92
+ approval, secret-store, or network-governance requirements on this local flow.
93
+
94
+ ## Command behavior
95
+
96
+ All scripts emit one JSON object. Treat `success=false` or a nonzero exit code as failure. Prefer the
97
+ two resumable composite commands for a complete requested experience:
111
98
 
112
99
  ```text
113
- datasource test -> bind -> diagnostics
114
- tables list -> inspect -> optional bounded read-only SQL
115
- models validate -> bundles add/update -> models refresh -> models describe
116
- query validate -> query execute -> interpretation
100
+ onboard-datasource-run --project-root <workspace> --connection-file <json> \
101
+ --approve-configure --approve-bind --include-indexes
102
+
103
+ onboard-semantic-run --project-root <workspace> --semantic-plan <json> \
104
+ --query-payload <json> --approve-validate --approve-publish --approve-execute
105
+ ```
106
+
107
+ When the user has already asked to connect, build, and test a new local model, those flags implement
108
+ that request and do not require separate question-by-question confirmation. Replacement, pruning, broad
109
+ queries, destructive SQL, Git push, and production deployment still require their own clear scope.
110
+
111
+ The normal analysis order is:
112
+
113
+ ```text
114
+ datasource add/test -> namespace bind -> table/schema inspection
115
+ TM/QM authoring -> models validate -> bundle add/update -> refresh/describe
116
+ query validate -> bounded query execute -> tune -> optional Git handoff
117
117
  ```
118
118
 
119
- In DeepSeek Harness, do not expand the composite onboarding commands back into these individual CLI
120
- operations. This order documents what the wrapper enforces internally and becomes a direct CLI workflow
121
- only after onboarding is complete.
122
-
123
- Composite commands are checkpointed and idempotent: a retry skips completed datasource, validation,
124
- publication, and verification phases when the approved contract and draft digest are unchanged. Fix a
125
- query payload in place and rerun the same semantic composite command; do not remove a successfully
126
- published bundle merely to recover from a later query-validation failure.
127
-
128
- An already-completed profile may be reused from another DSH workspace when the approved connection
129
- contract is byte-for-byte equivalent. Run the datasource composite in the new workspace first; it adds
130
- that workspace as a non-destructive binding and reuses datasource/schema checkpoints. Then pass the
131
- current workspace explicitly as `--project-root` to the semantic composite. A secondary workspace may
132
- reuse an identical published semantic digest and run its own bounded verification query, but it cannot
133
- replace the published semantic layer; changes must be published from the original workspace or a new
134
- profile.
135
-
136
- Keep user business data separate from the sales-drop SQLite demo. Prefer a read-only database account,
137
- opaque CLI profile references, and bounded query limits.
119
+ Stop only the PID recorded by this package and preserve the Runtime work directory unless the user
120
+ explicitly requests removal.
121
+
122
+ ## Result
123
+
124
+ Report the Runtime URL, namespace, datasource name, model directory, Bundle and QueryModel names,
125
+ validation/query status, and useful evidence paths. Never include the password or business row values
126
+ unless the user specifically asks for those values.
@@ -12,6 +12,7 @@
12
12
  "type": {"enum": ["sqlite", "mysql", "postgres", "postgresql"]},
13
13
  "jdbcUrl": {"type": "string", "minLength": 1},
14
14
  "username": {"type": "string"},
15
+ "password": {"type": "string", "description": "Optional local development password. It is submitted once and is not copied into onboarding state or evidence."},
15
16
  "passwordEnv": {"type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$"},
16
17
  "opaqueProfileId": {"type": "string", "pattern": "^fop_[a-f0-9]{32}$"},
17
18
  "opaqueRevision": {"type": "string", "pattern": "^sha256:[a-f0-9]{64}$"},
@@ -27,6 +28,7 @@
27
28
  "not": {"anyOf": [
28
29
  {"required": ["jdbcUrl"]},
29
30
  {"required": ["username"]},
31
+ {"required": ["password"]},
30
32
  {"required": ["passwordEnv"]}
31
33
  ]}
32
34
  },
@@ -38,13 +40,9 @@
38
40
  ]}
39
41
  }
40
42
  ],
41
- "allOf": [
42
- {
43
- "if": {
44
- "required": ["jdbcUrl"],
45
- "properties": {"type": {"not": {"const": "sqlite"}}}
46
- },
47
- "then": {"required": ["passwordEnv"]}
43
+ "allOf": [{
44
+ "not": {
45
+ "required": ["password", "passwordEnv"]
48
46
  }
49
- ]
47
+ }]
50
48
  }
@@ -3,8 +3,9 @@
3
3
  "profile": "business",
4
4
  "name": "business-db",
5
5
  "type": "mysql",
6
- "opaqueProfileId": "fop_0123456789abcdef0123456789abcdef",
7
- "opaqueRevision": "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
6
+ "jdbcUrl": "jdbc:mysql://127.0.0.1:3306/business",
7
+ "username": "business_dev",
8
+ "password": "replace-for-local-development",
8
9
  "namespace": "business",
9
10
  "schemas": ["public"],
10
11
  "modelsDir": "models",
@@ -1,5 +1,5 @@
1
- # Copy only the variables you need into a private, gitignored env file.
2
- # Never put provider keys or database passwords in DSH settings, Skill files, or evidence.
1
+ # Optional development configuration. Direct passwords in a user-supplied connection file are also
2
+ # supported; the onboarding wrapper does not copy them into state or evidence.
3
3
 
4
4
  FOGGY_RUNTIME_API_URL=http://127.0.0.1:18166
5
5
  FOGGY_NAMESPACE=default
@@ -10,5 +10,5 @@ FOGGY_NAMESPACE=default
10
10
  # Optional data-plane authorization value.
11
11
  # FOGGY_RUNTIME_AUTHORIZATION=
12
12
 
13
- # Example datasource password reference. Use a datasource-specific variable name.
13
+ # Optional alternative to a direct development password.
14
14
  # FOGGY_DATASOURCE_PASSWORD=
@@ -34,7 +34,29 @@
34
34
  }
35
35
  },
36
36
  "runtime": {"type": "object"},
37
- "connection": {"$ref": "connection.schema.json"},
37
+ "connection": {
38
+ "type": "object",
39
+ "additionalProperties": false,
40
+ "required": ["schemaVersion", "connectionMode", "name", "type", "namespace"],
41
+ "properties": {
42
+ "schemaVersion": {"const": "foggy-deepseek-connection/v1"},
43
+ "connectionMode": {"enum": ["legacy-inline", "opaque-profile"]},
44
+ "credentialMode": {"enum": ["inline-development", "agent-environment", "opaque-profile", "none"]},
45
+ "name": {"type": "string"},
46
+ "type": {"type": "string"},
47
+ "jdbcUrl": {"type": ["string", "null"]},
48
+ "username": {"type": ["string", "null"]},
49
+ "passwordEnv": {"type": ["string", "null"]},
50
+ "opaqueProfileId": {"type": "string"},
51
+ "opaqueRevision": {"type": "string"},
52
+ "namespace": {"type": "string"},
53
+ "schemas": {"type": "array", "items": {"type": "string"}},
54
+ "modelsDir": {"type": "string"},
55
+ "readOnlyRecommended": {"type": "boolean"},
56
+ "profile": {"type": "string"},
57
+ "evidenceDir": {"type": "string"}
58
+ }
59
+ },
38
60
  "semantic": {"type": "object"},
39
61
  "steps": {"type": "object"},
40
62
  "artifacts": {"type": "object"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": "foggy-deepseek-onboarding-versions/v1",
3
- "packageVersion": "0.4.0-beta.14",
3
+ "packageVersion": "0.4.0-beta.16",
4
4
  "validatedAt": "2026-09-04",
5
5
  "components": {
6
6
  "deepseekHarness": {
@@ -1,156 +1,154 @@
1
- # Resumable database onboarding
2
-
3
- Use the thin `scripts/onboard.ps1` or `scripts/onboard.sh` entrypoint. Every command emits one JSON
4
- object. Preserve the returned `next` instruction and use `onboard-status` or `onboard-resume` after an
5
- interruption.
1
+ # Development datasource and semantic-model workflow
2
+
3
+ Use this workflow after the Foggy plugin is initialized. It is designed for local experience and
4
+ model development in DeepSeek Harness, not for production deployment.
5
+
6
+ ## Connection input
7
+
8
+ The simplest local connection contract may contain a direct password:
9
+
10
+ ```json
11
+ {
12
+ "schemaVersion": "foggy-deepseek-connection/v1",
13
+ "profile": "business",
14
+ "name": "business-db",
15
+ "type": "mysql",
16
+ "jdbcUrl": "jdbc:mysql://127.0.0.1:3306/business",
17
+ "username": "business_dev",
18
+ "password": "local-development-password",
19
+ "namespace": "business",
20
+ "modelsDir": "models",
21
+ "evidenceDir": ".foggy/onboarding-command-evidence/business"
22
+ }
23
+ ```
6
24
 
7
- The wrapper is mandatory for the entire sequence below. Until `onboard-status` returns
8
- `next.status=completed`, do not substitute direct `foggy-runtime datasources`, `tables`, `sql`, `models`,
9
- `bundles`, or `query` calls. A second Skill may help write draft TM/QM files, but must not take over
10
- orchestration. Do not inspect sample rows to author the semantic layer; use schema metadata plus the
11
- business definitions confirmed by the user.
25
+ The wrapper uses the direct password only while submitting the datasource to the public Runtime API.
26
+ It does not copy it into onboarding state, command evidence, TM/QM files, or conversational output.
27
+ The source connection file is user-managed; do not commit it. The local Runtime currently stores a
28
+ direct password in its private datasource registry, so use `passwordRef`-style production credential
29
+ management when moving beyond local development.
30
+
31
+ An environment variable is an optional alternative:
32
+
33
+ ```json
34
+ {
35
+ "schemaVersion": "foggy-deepseek-connection/v1",
36
+ "profile": "business",
37
+ "name": "business-db",
38
+ "type": "mysql",
39
+ "jdbcUrl": "jdbc:mysql://127.0.0.1:3306/business",
40
+ "username": "business_dev",
41
+ "passwordEnv": "FOGGY_BUSINESS_DB_PASSWORD",
42
+ "namespace": "business",
43
+ "modelsDir": "models"
44
+ }
45
+ ```
12
46
 
13
- ## Conversation contract
47
+ Here `passwordEnv` is read by the onboarding wrapper and submitted online to the already-running
48
+ Runtime. Do not restart Runtime merely to make the variable part of the Java process environment.
49
+ Opaque CLI profiles remain supported for users who already prefer them, but are not required for a
50
+ normal DSH experience.
14
51
 
15
- Collect these non-secret values before creating a plan:
52
+ ## Normal sequence
16
53
 
17
- 1. an opaque Foggy profile ID and its exact reviewed revision;
18
- 2. datasource name, database type, and namespace from the CLI's public profile summary;
19
- 3. optional schemas, the project-relative semantic model directory, and a project-relative evidence directory.
54
+ Run the platform-specific wrapper from this Skill directory. `onboard-datasource-run` performs the
55
+ online datasource setup, namespace binding, and schema discovery against the healthy Runtime:
20
56
 
21
- Create the private profile in a trusted operator session outside DeepSeek Harness. Do not ask the user
22
- to paste a password and do not put a JDBC URL, username, password environment-variable name, or password in the JSON plan, command
23
- line, logs, or evidence. For non-SQLite databases, ask the user to set the named environment variable
24
- before Runtime starts so the Java process inherits it. Recommend a read-only database account.
57
+ ```text
58
+ onboard-datasource-run --project-root <current-workspace> \
59
+ --connection-file <connection-json> \
60
+ --approve-configure --approve-bind --include-indexes
61
+ ```
25
62
 
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.
63
+ If the user already requested the complete connection experience, include both approval flags in the
64
+ first call. They refer only to adding the named development datasource and binding the requested
65
+ namespace. Do not add `--replace` unless replacement was explicitly requested.
31
66
 
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.
67
+ The command performs:
35
68
 
36
- If plugin settings detect profiles in the legacy temporary store, use **Move to persistent store**.
37
- Migration validates the opaque profile schema, rejects embedded passwords and conflicts, writes the
38
- destination with private permissions, verifies it, and moves the legacy JSON to a recoverable private
39
- backup below the Foggy data root.
40
- Do not manually copy or edit opaque profile JSON.
69
+ ```text
70
+ datasource add -> datasource test -> namespace bind -> diagnostics
71
+ tables list -> bounded schema inspection
72
+ ```
41
73
 
42
- Treat every user-supplied identifier and bound as immutable input: profile, datasource, namespace,
43
- models directory, bundle name, TM/QM name, query fields, and limit. Do not swap in demo names, add
44
- fields, raise the limit, or introduce replacement flags. If one of these inputs is missing, pause for
45
- that input rather than inventing it.
74
+ It does not stop or restart Runtime. Repeated calls reuse completed checkpoints when the public
75
+ connection contract is unchanged. Inline password values are excluded from the comparison.
76
+ Transient connection-pool readiness timeouts are retried a small, bounded number of times; invalid
77
+ credentials, invalid URLs, and other configuration errors fail immediately.
46
78
 
47
- For composite Harness execution, persist `profile` and `evidenceDir` inside both approved contract
48
- files. The wrapper derives them from the files and rejects conflicting command-line values. Present the
49
- normalized contracts to the user before accepting mutation approvals; do not combine contract drafting
50
- and approval into an unseen one-turn mutation.
79
+ When the user's request clearly identifies a business subject or likely tables, add `--pattern` or
80
+ one or more `--table` arguments so discovery remains focused. Do not enumerate CLI help, inspect the
81
+ wrapper source, load unrelated examples, or fetch online documentation on the successful normal path.
82
+ Keep full command payloads in the numbered evidence files and use only their concise result fields in
83
+ the conversation.
51
84
 
52
- Write the non-secret input using `assets/connection.schema.json`; `assets/datasource.example.json` is a
53
- template. Then execute the sequence below.
85
+ After discovery, use `foggy-ai-analysis` to create TM/QM drafts from actual tables, columns, keys,
86
+ small read-only samples when useful, and the user's business questions. Keep the files in the current
87
+ workspace, normally:
54
88
 
55
- ## Deterministic sequence
89
+ ```text
90
+ models/
91
+ model/
92
+ <Name>.tm
93
+ query/
94
+ <Name>QueryModel.qm
95
+ ```
56
96
 
57
- For Harness-driven onboarding, use the two composite commands below. Each writes the numbered JSON
58
- evidence internally and refuses to cross an unapproved mutation gate:
97
+ Then run the semantic composite:
59
98
 
60
99
  ```text
61
- onboard-datasource-run --project-root <current-session-workspace> \
62
- --connection-file <approved-json> \
63
- --approve-configure --approve-bind --include-indexes
64
-
65
- # After authoring the registered TM/QM draft from schema metadata:
66
- onboard-semantic-run --project-root <current-session-workspace> \
67
- --semantic-plan <approved-json> --query-payload <approved-json> \
100
+ onboard-semantic-run --project-root <current-workspace> \
101
+ --semantic-plan <semantic-json> \
102
+ --query-payload <bounded-query-json> \
68
103
  --approve-validate --approve-publish --approve-execute
69
104
  ```
70
105
 
71
- Include an approval flag only after the user approves that exact action. Without it, the composite
72
- command stops after the corresponding dry-run and returns `phaseStatus=awaiting-...-approval`.
106
+ For an explicitly requested end-to-end local experience, these flags may be used together. The command
107
+ validates the model files, copies them to the approved model directory when needed, registers or
108
+ updates the local Runtime Bundle, refreshes/describes the declared QueryModels, validates the bounded
109
+ query, and executes it. It never means production publication.
73
110
 
74
- Both composite commands are resumable. When the approved contract is unchanged, they skip completed
75
- checkpoints instead of re-adding an existing datasource, revalidating an already published draft, or
76
- registering the same bundle twice. If query validation fails after publication, correct the project-local
77
- payload and rerun `onboard-semantic-run`; it resumes at query verification and leaves the active bundle in
78
- place. A same-name datasource is accepted idempotently only when its public name and database type match
79
- the approved plan; otherwise replacement still requires explicit approval.
111
+ Use `--watch` only when the user wants the local Runtime to follow model-file edits. Use `--prune` or
112
+ bundle replacement only when the user clearly asks for those changes.
80
113
 
81
- When a completed profile already belongs to another workspace, the datasource composite may add the
82
- current workspace as a binding only if the approved connection contract is identical and the datasource,
83
- schema, and semantic publication checkpoints are complete. The semantic composite then accepts the same
84
- published draft digest from the bound workspace and performs a workspace-specific bounded verification
85
- query. It refuses semantic replacement from the secondary workspace.
114
+ ## Iteration and troubleshooting
86
115
 
87
- The granular commands below remain available for manual troubleshooting and resumption. Do not expand
88
- the composite commands into this list during a normal Harness turn.
116
+ The granular commands remain available when a specific stage needs repair:
89
117
 
90
118
  ```text
91
119
  onboard-plan --connection-file <json> --profile <profile>
92
- datasource-configure --profile <profile>
93
- datasource-configure --profile <profile> --apply
94
- datasource-verify --profile <profile>
95
- datasource-verify --profile <profile> --bind
96
- schema-discover --profile <profile> --include-indexes
120
+ datasource-configure --profile <profile> [--apply]
121
+ datasource-verify --profile <profile> [--bind]
122
+ schema-discover --profile <profile> [--include-indexes]
97
123
  semantic-draft --profile <profile> --semantic-plan <json>
98
- semantic-validate --profile <profile>
99
- semantic-validate --profile <profile> --apply
100
- semantic-publish --profile <profile>
101
- semantic-publish --profile <profile> --apply
102
- semantic-verify --profile <profile> --project-root <current-session-workspace> --query-payload <json>
103
- semantic-verify --profile <profile> --project-root <current-session-workspace> --query-payload <json> --execute
124
+ semantic-validate --profile <profile> [--apply]
125
+ semantic-publish --profile <profile> [--apply]
126
+ semantic-verify --profile <profile> --query-payload <json> [--execute]
104
127
  onboard-status --profile <profile>
128
+ onboard-resume --profile <profile>
105
129
  ```
106
130
 
107
- The first `datasource-configure` call is a dry run. Explain the concrete datasource mutation and get
108
- approval before adding `--apply`. `datasource-verify` tests connectivity without binding; explain the
109
- namespace binding mutation and get approval before adding `--bind`. Add `--replace` only when the user
110
- explicitly approves replacing an existing datasource definition.
111
-
112
- Schema discovery is metadata-only and inspects at most 25 tables by default. Use repeated `--schema` or
113
- `--table` arguments to narrow scope, `--list-only` for inventory only, and raise `--max-tables` only when
114
- the user needs broader inspection. The resulting evidence remains under the private Runtime data root.
115
-
116
- ## Semantic authoring and publishing
117
-
118
- After `schemaDiscovered=completed`, review table names, columns, keys, and relationships with the user.
119
- Use `foggy-ai-analysis` for TM/QM authoring in a separate project-local draft directory. Do not invent
120
- business definitions, joins, units, enum meanings, or date semantics. Create a plan using
121
- `assets/semantic-plan.schema.json`; its declared query-model names must exist in the draft `.qm` files.
122
-
123
- `semantic-draft` records TM/QM hashes. Any later file change invalidates the recorded validation and must
124
- be registered and validated again. `semantic-validate` is a dry run until `--apply`; applying it may
125
- replace the Runtime validation catalog, so explain that mutation before approval.
126
-
127
- `semantic-publish` shows added, updated, unchanged, preserved, and optionally removed files. Applying it:
131
+ A standalone `datasource-configure --apply` can resolve `passwordEnv` from its current Agent process.
132
+ For a direct `password`, use the composite datasource command with the original connection file so the
133
+ secret stays ephemeral to that invocation.
128
134
 
129
- 1. backs up every overwritten or pruned project model file;
130
- 2. copies the registered draft;
131
- 3. validates the published directory;
132
- 4. registers the bundle;
133
- 5. refreshes only the declared query models.
135
+ When a failure occurs, repair only the failed input and rerun the same composite command. Do not remove
136
+ a successfully registered Bundle merely because later query validation failed. Keep SQL samples small,
137
+ read-only, and relevant to semantic authoring.
134
138
 
135
- Use `--prune` only when the user approves removing target-only TM/QM files. Use `--replace-bundle` only
136
- when the user approves replacing an existing Runtime bundle, and `--watch` only when file watching is
137
- desired. A failure before refresh restores project files; a refresh failure is reported as a partial
138
- Runtime publication and must be diagnosed before retrying.
139
+ ## Git handoff
139
140
 
140
- For verification, build a project-local query payload only from `models describe` field names. The helper
141
- requires an integer `limit` from 1 through 100. The first `semantic-verify` call lists/describes models and
142
- validates the query; only `--execute` reads business data. Full results stay in evidence and the command
143
- returns only counts and paths.
141
+ When validation and representative questions pass, recommend placing the model directory under the
142
+ user's existing Git repository. Git is the source of truth for model history; Runtime Bundle
143
+ registration only makes a selected local directory active. Do not create commits, remotes, tags, or
144
+ pushes without the user's request.
144
145
 
145
- Before the first semantic mutation, the composite command checks that the query payload is inside
146
- `projectRoot`, has a bounded limit, and targets a query model declared in the approved semantic plan.
147
- After publication, `semantic-verify` always describes the live model before validation. If a field is
148
- rejected, update the payload from those described names and rerun the same composite command.
146
+ ## Production handoff
149
147
 
150
- Do not open, summarize, or quote `query-execute.json` or the Runtime's generated SQL after execution.
151
- The conversational result may contain only `queryValidated`, `queryExecuted`, `rowCount`, the model
152
- name, and the evidence path. A successful direct CLI command is not proof that onboarding completed;
153
- only the wrapper's persisted state with all steps `completed` is authoritative.
148
+ Stop the local onboarding flow when the user asks to publish or update a formal environment. A separate
149
+ manual or dedicated deployment workflow should collect the exact target, Launcher version, model Git
150
+ commit/tag, namespace, production datasource procedure, access credentials, verification plan, and
151
+ rollback point. It should then install/start Runtime if needed, prepare the target datasource, register
152
+ the versioned model directory, refresh, and run a narrow smoke check.
154
153
 
155
- Do not bypass a failed step. If a command fails, report its structured error, fix only the relevant input
156
- or environment, and run `onboard-resume` to continue from the first incomplete step.
154
+ Local onboarding credentials and approvals do not authorize production access or publication.
@@ -26,5 +26,7 @@ if (-not $pythonCommand -or -not (Test-Path -LiteralPath $pythonCommand)) {
26
26
  exit 1
27
27
  }
28
28
 
29
+ $env:PYTHONUTF8 = '1'
30
+ $env:PYTHONIOENCODING = 'utf-8'
29
31
  & $pythonCommand (Join-Path $PSScriptRoot 'onboarding.py') @OnboardingArgs
30
32
  exit $LASTEXITCODE
@@ -18,6 +18,7 @@ import subprocess
18
18
  import sys
19
19
  import tempfile
20
20
  import time
21
+ import urllib.error
21
22
  import urllib.request
22
23
  import venv
23
24
  import zipfile
@@ -918,7 +919,7 @@ def validate_connection(payload: dict) -> dict:
918
919
  if payload.get("schemaVersion") != CONNECTION_SCHEMA:
919
920
  raise OnboardingError(f"connection schemaVersion must be {CONNECTION_SCHEMA}")
920
921
  allowed = {
921
- "schemaVersion", "name", "type", "jdbcUrl", "username", "passwordEnv",
922
+ "schemaVersion", "name", "type", "jdbcUrl", "username", "password", "passwordEnv",
922
923
  "opaqueProfileId", "opaqueRevision",
923
924
  "profile", "namespace", "schemas", "modelsDir", "evidenceDir", "readOnlyRecommended",
924
925
  }
@@ -930,6 +931,7 @@ def validate_connection(payload: dict) -> dict:
930
931
  for name in required:
931
932
  if not isinstance(payload.get(name), str) or not payload[name].strip():
932
933
  raise OnboardingError(f"connection.{name} must be a non-empty string")
934
+ password = payload.get("password")
933
935
  password_env = payload.get("passwordEnv")
934
936
  jdbc_url = None
935
937
  if opaque:
@@ -937,15 +939,19 @@ def validate_connection(payload: dict) -> dict:
937
939
  raise OnboardingError("connection.opaqueProfileId must be an opaque Foggy profile ID")
938
940
  if not isinstance(payload.get("opaqueRevision"), str) or not OPAQUE_REVISION_PATTERN.fullmatch(payload["opaqueRevision"]):
939
941
  raise OnboardingError("connection.opaqueRevision must be a sha256 revision")
940
- exposed = sorted(name for name in ("jdbcUrl", "username", "passwordEnv") if name in payload)
942
+ exposed = sorted(name for name in ("jdbcUrl", "username", "password", "passwordEnv") if name in payload)
941
943
  if exposed:
942
944
  raise OnboardingError(f"Opaque connection plans must not contain: {', '.join(exposed)}")
943
945
  else:
946
+ if password is not None and not isinstance(password, str):
947
+ raise OnboardingError("connection.password must be a string")
944
948
  if password_env is not None and (not isinstance(password_env, str) or not ENV_NAME_PATTERN.fullmatch(password_env)):
945
949
  raise OnboardingError("connection.passwordEnv must be an environment variable name")
950
+ if password is not None and password_env is not None:
951
+ raise OnboardingError("Provide either connection.password or connection.passwordEnv, not both")
946
952
  jdbc_url = payload["jdbcUrl"].strip()
947
953
  if re.search(r"(?i)(?:password|passwd|pwd)\s*=", jdbc_url) or re.search(r"//[^/@:]+:[^/@]+@", jdbc_url):
948
- raise OnboardingError("Do not embed passwords in jdbcUrl; use passwordEnv")
954
+ raise OnboardingError("Do not embed passwords in jdbcUrl; use password or passwordEnv")
949
955
  schemas = payload.get("schemas", [])
950
956
  if not isinstance(schemas, list) or any(not isinstance(item, str) or not item.strip() for item in schemas):
951
957
  raise OnboardingError("connection.schemas must be an array of non-empty strings")
@@ -973,6 +979,7 @@ def validate_connection(payload: dict) -> dict:
973
979
  else:
974
980
  result["jdbcUrl"] = jdbc_url
975
981
  result["username"] = payload.get("username")
982
+ result["password"] = password
976
983
  result["passwordEnv"] = password_env
977
984
  if payload.get("profile") is not None:
978
985
  result["profile"] = safe_profile(payload["profile"])
@@ -980,11 +987,24 @@ def validate_connection(payload: dict) -> dict:
980
987
  result["evidenceDir"] = payload["evidenceDir"].strip()
981
988
  if result["type"] not in {"sqlite", "mysql", "postgres", "postgresql"}:
982
989
  raise OnboardingError("Initial onboarding supports sqlite, mysql, postgres, and postgresql")
983
- if not opaque and result["type"] != "sqlite" and not result["passwordEnv"]:
984
- raise OnboardingError("Non-SQLite connections require passwordEnv")
985
990
  return result
986
991
 
987
992
 
993
+ def persisted_connection(connection: dict) -> dict:
994
+ """Return the resumable connection contract without an inline development password."""
995
+ persisted = {key: value for key, value in connection.items() if key != "password"}
996
+ credential_mode = connection.get("credentialMode")
997
+ if credential_mode not in {"inline-development", "agent-environment", "opaque-profile", "none"}:
998
+ credential_mode = (
999
+ "inline-development" if connection.get("password") is not None
1000
+ else "agent-environment" if connection.get("passwordEnv")
1001
+ else "opaque-profile" if connection.get("connectionMode") == "opaque-profile"
1002
+ else "none"
1003
+ )
1004
+ persisted["credentialMode"] = credential_mode
1005
+ return persisted
1006
+
1007
+
988
1008
  def validate_semantic_plan(payload: dict) -> dict:
989
1009
  if payload.get("schemaVersion") != SEMANTIC_PLAN_SCHEMA:
990
1010
  raise OnboardingError(f"semantic plan schemaVersion must be {SEMANTIC_PLAN_SCHEMA}")
@@ -1780,6 +1800,108 @@ def cli_json(install_state: dict, runtime_state: dict, namespace: str, command:
1780
1800
  return payload
1781
1801
 
1782
1802
 
1803
+ TRANSIENT_DATASOURCE_TEST_MARKERS = (
1804
+ "connection is not available",
1805
+ "connection pool",
1806
+ "request timed out",
1807
+ "timeout waiting for connection",
1808
+ )
1809
+
1810
+
1811
+ def datasource_test_with_retry(
1812
+ install_state: dict,
1813
+ runtime_state: dict,
1814
+ namespace: str,
1815
+ datasource_name: str,
1816
+ *,
1817
+ attempts: int = 3,
1818
+ initial_delay_seconds: float = 1.0,
1819
+ ) -> dict:
1820
+ """Retry only transient pool/readiness failures; authentication and configuration failures fail fast."""
1821
+ for attempt in range(1, attempts + 1):
1822
+ try:
1823
+ result = cli_json(
1824
+ install_state,
1825
+ runtime_state,
1826
+ namespace,
1827
+ ["datasources", "test", datasource_name],
1828
+ "datasources test",
1829
+ )
1830
+ if attempt > 1:
1831
+ result = {
1832
+ **result,
1833
+ "onboardingRetry": {
1834
+ "attempts": attempt,
1835
+ "reason": "transient-datasource-readiness",
1836
+ },
1837
+ }
1838
+ return result
1839
+ except OnboardingError as exc:
1840
+ transient = any(marker in str(exc).lower() for marker in TRANSIENT_DATASOURCE_TEST_MARKERS)
1841
+ if not transient or attempt == attempts:
1842
+ raise
1843
+ time.sleep(initial_delay_seconds * attempt)
1844
+ raise AssertionError("unreachable")
1845
+
1846
+
1847
+ def runtime_api_json(
1848
+ runtime_state: dict,
1849
+ namespace: str,
1850
+ method: str,
1851
+ path: str,
1852
+ body: dict | None,
1853
+ label: str,
1854
+ timeout: int = 60,
1855
+ ) -> dict:
1856
+ """Call the public Runtime API for development inputs not yet exposed by the pinned CLI."""
1857
+ base_url = str(runtime_state["runtimeUrl"]).rstrip("/")
1858
+ request_body = json.dumps(body).encode("utf-8") if body is not None else None
1859
+ headers = {
1860
+ "Accept": "application/json",
1861
+ "Content-Type": "application/json",
1862
+ "X-NS": namespace,
1863
+ }
1864
+ auth_code = os.environ.get("FOGGY_RUNTIME_API_AUTH_CODE")
1865
+ if auth_code:
1866
+ headers["X-Foggy-Runtime-Code"] = auth_code
1867
+ request = urllib.request.Request(
1868
+ f"{base_url}{path}",
1869
+ data=request_body,
1870
+ headers=headers,
1871
+ method=method,
1872
+ )
1873
+ try:
1874
+ with urllib.request.urlopen(request, timeout=timeout) as response:
1875
+ raw = response.read().decode("utf-8", errors="replace")
1876
+ except urllib.error.HTTPError as exc:
1877
+ raw = exc.read().decode("utf-8", errors="replace")
1878
+ try:
1879
+ failed = json.loads(raw)
1880
+ except json.JSONDecodeError:
1881
+ failed = None
1882
+ error = failed.get("error") if isinstance(failed, dict) else None
1883
+ detail = (
1884
+ " | ".join(str(error.get(name)) for name in ("code", "phase", "message") if error.get(name))
1885
+ if isinstance(error, dict)
1886
+ else str(exc.reason)
1887
+ )
1888
+ raise OnboardingError(f"{label} failed with HTTP {exc.code}: {detail or exc.reason}") from exc
1889
+ except urllib.error.URLError as exc:
1890
+ raise OnboardingError(f"{label} could not reach Runtime: {exc.reason}") from exc
1891
+ try:
1892
+ payload = json.loads(raw)
1893
+ except json.JSONDecodeError as exc:
1894
+ raise OnboardingError(f"{label} did not return JSON") from exc
1895
+ if payload.get("success") is not True:
1896
+ error = payload.get("error")
1897
+ if isinstance(error, dict):
1898
+ detail = " | ".join(str(error.get(name)) for name in ("code", "phase", "message") if error.get(name))
1899
+ else:
1900
+ detail = str(error or "unknown Runtime error")
1901
+ raise OnboardingError(f"{label} returned success=false: {detail}")
1902
+ return payload
1903
+
1904
+
1783
1905
  CONNECTION_SECRET_KEYS = {
1784
1906
  "jdbcurl", "url", "username", "password", "passwordenv", "passwordref",
1785
1907
  }
@@ -1837,7 +1959,8 @@ def onboarding_plan_command(args: argparse.Namespace) -> dict:
1837
1959
  if not project_root.is_dir():
1838
1960
  raise OnboardingError(f"Project root not found: {project_root}")
1839
1961
  connection_file = normalized(args.connection_file)
1840
- connection = validate_connection(read_json_object(connection_file, "Connection plan"))
1962
+ requested_connection = validate_connection(read_json_object(connection_file, "Connection plan"))
1963
+ connection = persisted_connection(requested_connection)
1841
1964
  if connection.get("connectionMode") == "opaque-profile":
1842
1965
  require_opaque_profile_cli(install_state)
1843
1966
  existing = read_onboarding_state(data_root, profile, required=False)
@@ -1882,7 +2005,10 @@ def onboarding_plan_command(args: argparse.Namespace) -> dict:
1882
2005
  "schemaVersion": "foggy-deepseek-onboarding-plan-result/v1",
1883
2006
  "profile": profile,
1884
2007
  "statePath": str(path),
1885
- "connection": {**connection, "passwordEnvPresent": bool(password_env and os.environ.get(password_env))},
2008
+ "connection": {
2009
+ **connection,
2010
+ "passwordEnvPresent": bool(password_env and os.environ.get(password_env)),
2011
+ },
1886
2012
  "runtimeAvailable": runtime_state is not None,
1887
2013
  "next": "run datasource-configure --apply after reviewing the plan",
1888
2014
  "productionReady": False,
@@ -1913,10 +2039,11 @@ def datasource_configure_command(args: argparse.Namespace) -> dict:
1913
2039
  if opaque:
1914
2040
  plan.update({"profileId": connection["opaqueProfileId"], "revision": connection["opaqueRevision"]})
1915
2041
  else:
2042
+ inline_password = getattr(args, "runtime_password", None)
1916
2043
  plan.update({
1917
2044
  "jdbcUrl": connection["jdbcUrl"],
1918
2045
  "username": connection.get("username"),
1919
- "passwordEnv": connection.get("passwordEnv"),
2046
+ "credentialMode": connection.get("credentialMode", "none"),
1920
2047
  })
1921
2048
  if not args.apply:
1922
2049
  return {"success": True, "dryRun": True, "profile": state["profile"], "plan": plan, "next": "rerun with --apply after approval"}
@@ -1928,22 +2055,50 @@ def datasource_configure_command(args: argparse.Namespace) -> dict:
1928
2055
  ]
1929
2056
  label = "opaque profile configure"
1930
2057
  else:
2058
+ inline_password = getattr(args, "runtime_password", None)
1931
2059
  password_env = connection.get("passwordEnv")
1932
- if password_env and not os.environ.get(password_env):
2060
+ if connection.get("credentialMode") == "inline-development" and inline_password is None:
2061
+ raise OnboardingError(
2062
+ "Direct development password is not persisted; rerun onboard-datasource-run with the original connection file"
2063
+ )
2064
+ if inline_password is None and password_env and os.environ.get(password_env) is None:
1933
2065
  raise OnboardingError(f"Required password environment variable is not present: {password_env}")
1934
- command = ["datasources", "add", "--name", connection["name"], "--type", connection["type"], "--jdbc-url", connection["jdbcUrl"]]
1935
- if connection.get("username"):
1936
- command.extend(["--username", connection["username"]])
1937
- if password_env:
1938
- command.extend(["--password-env", password_env])
1939
2066
  label = "datasources add"
1940
- if args.replace:
1941
- command.append("--replace")
2067
+ resolved_password = inline_password if inline_password is not None else (
2068
+ os.environ.get(password_env) if password_env else None
2069
+ )
2070
+ command = None
1942
2071
  already_present = False
1943
2072
  try:
1944
- result = redact_connection_material(
1945
- cli_json(install_state, runtime_state, connection["namespace"], command, label)
1946
- )
2073
+ if not opaque and resolved_password is not None:
2074
+ body = {
2075
+ "name": connection["name"],
2076
+ "type": connection["type"],
2077
+ "jdbcUrl": connection["jdbcUrl"],
2078
+ "replace": bool(args.replace),
2079
+ "enabled": True,
2080
+ "password": resolved_password,
2081
+ }
2082
+ if connection.get("username"):
2083
+ body["username"] = connection["username"]
2084
+ result = redact_connection_material(runtime_api_json(
2085
+ runtime_state,
2086
+ connection["namespace"],
2087
+ "POST",
2088
+ "/api/v1/datasources",
2089
+ body,
2090
+ label,
2091
+ ))
2092
+ else:
2093
+ if not opaque:
2094
+ command = ["datasources", "add", "--name", connection["name"], "--type", connection["type"], "--jdbc-url", connection["jdbcUrl"]]
2095
+ if connection.get("username"):
2096
+ command.extend(["--username", connection["username"]])
2097
+ if args.replace:
2098
+ command.append("--replace")
2099
+ result = redact_connection_material(
2100
+ cli_json(install_state, runtime_state, connection["namespace"], command, label)
2101
+ )
1947
2102
  except OnboardingError as exc:
1948
2103
  if "DATASOURCE_ALREADY_EXISTS" not in str(exc):
1949
2104
  raise
@@ -1984,9 +2139,8 @@ def datasource_verify_command(args: argparse.Namespace) -> dict:
1984
2139
  raise OnboardingError("Datasource is not configured; run datasource-configure --apply first")
1985
2140
  connection = state["connection"]
1986
2141
  namespace = connection["namespace"]
1987
- tested = redact_connection_material(cli_json(
1988
- install_state, runtime_state, namespace,
1989
- ["datasources", "test", connection["name"]], "datasources test",
2142
+ tested = redact_connection_material(datasource_test_with_retry(
2143
+ install_state, runtime_state, namespace, connection["name"],
1990
2144
  ))
1991
2145
  if not args.bind:
1992
2146
  mark_step(state, "datasourceVerified", "waiting-for-binding", connectionTested=True)
@@ -2641,6 +2795,7 @@ def datasource_run_command(args: argparse.Namespace) -> dict:
2641
2795
  _install_root, install_state, data_root, _runtime_state = onboarding_context(args, require_runtime=True)
2642
2796
  project_root = normalized(args.project_root or Path.cwd())
2643
2797
  requested_connection = validate_connection(read_json_object(normalized(args.connection_file), "Connection plan"))
2798
+ resumable_connection = persisted_connection(requested_connection)
2644
2799
  if not requested_connection.get("profile"):
2645
2800
  raise OnboardingError("Composite datasource onboarding requires connection.profile in the approved contract")
2646
2801
  profile = requested_connection["profile"]
@@ -2654,7 +2809,7 @@ def datasource_run_command(args: argparse.Namespace) -> dict:
2654
2809
  files: list[str] = []
2655
2810
  existing = read_onboarding_state(data_root, profile, required=False)
2656
2811
  if existing:
2657
- if existing.get("connection") != requested_connection:
2812
+ if persisted_connection(existing.get("connection", {})) != resumable_connection:
2658
2813
  raise OnboardingError("Existing onboarding profile does not match the requested connection plan")
2659
2814
  adopted = bind_completed_workspace(existing, data_root, project_root)
2660
2815
  plan_result = {
@@ -2687,6 +2842,7 @@ def datasource_run_command(args: argparse.Namespace) -> dict:
2687
2842
  else:
2688
2843
  configure_dry = datasource_configure_command(argparse.Namespace(
2689
2844
  install_root=args.install_root, data_root=args.data_root, profile=profile, apply=False, replace=False,
2845
+ runtime_password=requested_connection.get("password"),
2690
2846
  ))
2691
2847
  save_composite_result(evidence_dir, "02-datasource-dry.json", configure_dry, files)
2692
2848
  if not args.approve_configure:
@@ -2702,6 +2858,7 @@ def datasource_run_command(args: argparse.Namespace) -> dict:
2702
2858
  }
2703
2859
  configured = datasource_configure_command(argparse.Namespace(
2704
2860
  install_root=args.install_root, data_root=args.data_root, profile=profile, apply=True, replace=False,
2861
+ runtime_password=requested_connection.get("password"),
2705
2862
  ))
2706
2863
  save_composite_result(evidence_dir, "03-datasource-apply.json", configured, files)
2707
2864
  state = read_onboarding_state(data_root, profile)
@@ -3314,6 +3471,10 @@ def build_parser() -> argparse.ArgumentParser:
3314
3471
 
3315
3472
 
3316
3473
  def main() -> None:
3474
+ for stream in (sys.stdout, sys.stderr):
3475
+ reconfigure = getattr(stream, "reconfigure", None)
3476
+ if callable(reconfigure):
3477
+ reconfigure(encoding="utf-8", errors="replace")
3317
3478
  args = build_parser().parse_args()
3318
3479
  try:
3319
3480
  result = args.handler(args)