🛡️ Preview Before Import
An import is a deliberate data operation, not a release side effect. Keep the source archive intact, choose an explicit server path, preview conversion, then separately approve the write and verify the actual DSH session.
Selected importer is not import-ready
The exact selected
dsh-chat-import@0.11.0source has release blockers against prepared Deek rc.2: its generatedassistant/messageseed omits the requireddata.streamsettlement array; the primaryagents.createpath rejects that seed, and its fallback uses the removedsessionPersistence.appendAPI. Separately, its raw/api-importroutes lack the required native Connection trust/authentication guard. Source review is not runtime acceptance. Do not install, expose, or use those routes on the live service, and do not silently fork, weaken the gate, or downgrade the artifact. The workflow below is the acceptance/runbook for a compatible reviewed release, not a claim that the blocked package already works.
In that historical import-preparation round, the then-live alpha.4 service was left unchanged. The decision was no installation or promotion in that round, not promotion without the importer. No private exports had been supplied and no private migration occurred. This is not a current owner-release assertion or new import acceptance.
Source-proven failure, not a runtime reproduction
- The plugin calls
agents.create({sessionId, meta, seed: events}). The selected host forwards the seed tosessions.prepare, which builds a version-3 header itself. Thus the plugin’s version-0 metadata is not the primary-path rejection. - The plugin synthesizes assistant messages with
turn,step, andmessage, but nostream. The core validates each seed event, and its settlement assertion requiresdata.streamto be an array. The exact source error isseed assistant/message at index N has invalid settlement fields. - The fallback calls the removed service-level
append. The selected persistence API instead returns a SessionHandle fromcreate; its storage contract also rejects the old version in that fallback path. - The plugin’s raw routes lack native request rejection; the webserver dispatch does not supply it automatically, unlike the Connection RPC guard. The public edge still enforces Authelia. The exposure is therefore an Authelia-authenticated caller without the native DSH cookie, or a local loopback process, not an asserted anonymous-internet bypass.
These are source-proven at the immutable lines, not runtime-reproduced by decision. No DSH composition or installed plugin was run to reproduce that full import path. The separately authorized pure-converter fixture run below is narrower and does not produce a DSH import acceptance receipt. A maintained compatible artifact is required; neither a guessed compatibility flag nor a custom local converter is the release fix.
Transfer files, not browser-local filenames
The selected plugin’s panel routes accept server-side source paths. They are not a multipart/byte-upload endpoint. A browser-local /Users/... or C:\... filename does not put data on the Debian server. The authentication defect above is an additional blocker, not an invitation to call those routes directly.
If no reviewed authenticated upload route is proven for the deployed artifact:
- The human chooses the exports and uses their own approved upload/file-transfer method to land only that selected batch under
/home/loca/deek-imports/<source>/<batch-id>/. This guide does not automate a home scan or access SSH credentials. - The deployment owner verifies path canonicalization, file types, sizes, source selection, and archive entries before copying anything. Reject symlinks, special files, absolute archive paths, parent traversal, and unexpected executable/configuration content. Do not extract or execute an arbitrary uploaded archive as root.
- A bounded root-admin operation copies regular selected files only to
/var/lib/deek/workspace/imports/<source>/<batch-id>/, and sets private0700directories and0600files with numeric65534:65534 on-disk ownership, the verified nobody/nogroup backing ownership for this idmapped Deek state. Refresh that physical-state gate separately from the runtime UID; never chown to the per-start DynamicUser UID. The systemd257 branch otherwise treats the tree as legacy recursive-chown state. State archives/restores usetar --numeric-ownerand preserve required xattrs. - The service sees only its import tree. It does not gain access to the landing account’s home;
ProtectHomeis not loosened. Preserve the original source and landing archive. - Record the exact final service-visible source path privately and leave it stable after import: source paths participate in importer deduplication. Renaming or re-copying the same file to a different batch can create a distinct source identity.
For Grok Build, keep the selected summary.json and chat_history.jsonl together. For ChatGPT, select the verified conversations.json, not the entire account ZIP or HTML viewer. For Hermes, do not copy an entire agent home or an inconsistent live SQLite file. See the format matrix.
Three operations that are easy to confuse
| Operation | Native contract | What it proves |
|---|---|---|
scan_discover / panel session listing | Discovers source metadata/status from the explicit server path | Selection context, not complete conversion fidelity |
Import operation with preview: true | Reads/converts but does not create imported sessions | Predicted conversion and warnings; must be checked against persistence baseline |
| Real import with preview omitted | Creates/appends supported session history and updates the native registry | A data mutation that still needs independent persistence/index/fidelity checks |
The calls below are native host-tool API examples, not shell commands, JavaScript to paste in the browser console, or a request to send a model prompt. The credential-free verifier drives the existing host tools in isolated staging. For a human-facing flow, use only the deployed plugin’s verified native UI/command capability; if it cannot expose explicit conversion preview, that capability is pending rather than replaced with a guessed flag.
Select and inspect one synthetic batch
scan_discover({
format: "chatgpt",
path: "/var/lib/deek/workspace/imports/chatgpt/sample-batch/conversations.json"
})Do not omit path and scan every default source home. Discovery may populate its own caches; the release assertion is that preview creates no sessions, not that every read path makes zero filesystem writes.
Convert without creating sessions
import_chat({
format: "chatgpt",
path: "/var/lib/deek/workspace/imports/chatgpt/sample-batch/conversations.json",
branch: "all",
preview: true
})The generic import_chat operation forwards branch: "all" for ChatGPT; without it the default main branch can omit alternatives. Save the returned count, format, branch policy, warnings/loss counters, and intended workspace privately before making a separate decision to import. Do not use force: true to hide a duplicate or source-shrink error.
Import the same selection deliberately
After the actual compatibility/authentication gates and the user’s selection approval, the real native operation is the same payload without preview: true. Preserve branch: "all" for the all-branch ChatGPT case.
The source also documents a direct no-model slash command:
/import claude /var/lib/deek/workspace/imports/claude/sample-batch/session.jsonl
/doctor/import <source> <path> writes immediately. Its command parser has no --preview or --branch parsing. Do not append invented flags; do not use that command to claim an all-branch preview. The standalone dsh-chat-import CLI exposes export/doctor/help, not a substitute native import engine. The usage reference separates these surfaces.
Verify the persistent result
Refresh Deek’s session list after import. A generated JSON object, nonempty file, or success status is not enough: the selected release must parse and index the actual persisted history.
verify_session({ sessionId: "<ID returned by the import>" })
doctor()These are the existing host tools. With injectTools: minimal, only the main import entry tool is kept resident in model context; the other operations are not assumed to be visible as chat tools. The verifier must invoke the real native mechanisms, and the user can use /doctor where the commands service is mounted. No extra model request is needed to test conversion or indexing.
| Check | Expected proof | Stop condition |
|---|---|---|
| Explicit dry run | Session persistence/index counts and IDs unchanged | Preview creates a session |
| Correct content | Known user/assistant roles, turn order, exact Unicode and text | Missing/reordered/normalized content not accepted as exact |
| Branches | Both known ChatGPT root-to-leaf alternatives persisted when branch: "all" | Only one branch or missing tool history |
| Tool pairs | Each intended call/result pair survives and native structural audit passes | Orphan/duplicate IDs or folding failure |
| DSH visibility | Native persistence readable and expected sessions visible after refresh | JSON exists but DSH cannot parse/index it |
| Repeated unchanged import | Existing non-archived target is already-imported; no duplicate session | New session for an unchanged source without an explicit copy operation |
| Grown source | Only new turns appended if the exact API supports it | Rewritten old history or unsupported append path |
| Truncated source | sourceShrunk or exact loss/failure surfaced | Silent “successful” replacement |
| Malformed/secret-shaped/unsupported input | Blocked or precise anomaly/loss retained for review | Blind success or secret content copied into public evidence |
| Workspace | Real registered service-visible cwd only | Historical out-of-bound cwd becomes a writable workspace |
| Native health | verify_session and doctor results with issues retained | A warning erased or replaced by “passed” |
Known silent loss to inspect: the selected Claude/Hermes converters can turn string-form tool_result.content into an empty result without incrementing droppedToolResults. A paired call/result or zero reported loss is therefore insufficient: compare the expected tool output text. Text-block-array positives and string-form negatives must stay separate; do not rewrite the negative fixture to hide this behavior. The authorized remote isolated repeat passed seven corrected positive converter fixtures and observed eight negative cases with network access denied and no package install, authentication, or model request. The string-form loss persists; all generated assistant settlements still lack data.stream, and an out-of-bound cwd passes through conversion. This is not an accepted DSH import, eight safe input rejections, or a workspace isolation proof; its private receipt is not public guide content.
A synthetic Pi pass never becomes an OMP compatibility pass. A synthetic Hermes JSONL pass never certifies every live Hermes SQLite version. The matrix names these separate rows.
Safe configuration, in the actual owning layer
injectToolsis an importer preference in thechat-importsettings namespace, not an invented Cordisconfigfield. Keep itminimal.- Keep
chat-import.importSystemPrompt: falsefor the isolated import profile so historical system/developer prompts are not adopted as instructions. Review the actual settings API before applying a seed. - Keep
DSH_IMPORT_CONTEXT_BRIDGE=0andDSH_IMPORT_SESSION_HINT=0in the reviewed service-owned environment. The optional Claude bridge is controlled by its native environment switch, not a guessedcontextBridgealias. - The native sync document has
inbound.enabled,outbound.enabled, andwatch.enabled; all remainfalse. Do not turn on bidirectional sync or source write-back. - Do not call
import_agents,import_mcp, orimport_settingsduring conversation import. No skills, hooks, approval rules, MCP commands, provider settings, or OAuth grants travel with transcripts.
Immutable preference/sync/bridge anchors: import preferences, sync config, context bridge, entry/toolkit.
Receipts and recovery
Store only the selected native import/verification results under that batch’s private receipts/: source hash and stable service path, artifact version/integrity, format/branch choice, returned session IDs/statuses, exact loss counters, verification outcomes, and any deliberately chosen real workspace. This is a record of the existing flow, not a second approval ledger. Never put private titles, paths, transcript text, or secret-shaped content in this public wiki.
The plugin’s native import registry is the import-history authority. Its documented recovery has distinct effects:
retract_importremoves the registry record and returns deletion guidance; it does not erase the DSH session or restore the original state.- The panel’s History deletion path affects plugin-created sessions after confirmation and still needs exact runtime proof. DSH’s in-memory index may require a restart for a removed artifact to disappear.
force: truecreates a fresh full copy under a new ID. It is not rollback or deduplication.- Archived targets can intentionally re-import as fresh copies; do not mistake that documented exception for unchanged-input idempotence.
/attach-workspacesis a global reattachment operation. Do not run it against every import to fix one batch;per-projectgrouping does not recreate missing files.- Preserve source archives and the stopped-state release backup. If a release migrated state incompatibly, use private page (sign-in required), not the old binary against changed state.
If the actual selected export is unsupported, leave it intact and report the exact format gap. A private migration or custom converter is not silently added to this release.