Architecture at a glance
Apps arrive in the client's Qlik Cloud tenant via QAMT (exact on-premise app, permissions included). All work happens inside one shared space restricted to developers — apps are never published; "publish" always means sheet visibility within that space. Original sheet IDs are preserved end-to-end so bookmarks stay intact; the kit re-creates each sheet's contents as cloud-born objects (full property export → new object created by the Cloud engine → grid-cell swap), guaranteeing every object is registered as cloud-created and receives future cloud feature updates. Frozen "(Original)" clone sheets in a public "Original" group are the visual reference and part of the rollback safety net.
funnelchart / wordcloud and their sn-* nebula
ids). 72 Qlik-to-Cloud conversions run ungated (56 identity
re-creations); every Qlik object type has a conversion path. The only
exceptions are the correct ones: Share button (replaced by the publish
bookmark capability), Multi KPI (removed from Cloud, rebuild), and NL
Insights (manual migration per Qlik).The lifecycle
- Preflight (once per tenant/space): validates every schema
assumption live — write capability, sheet cloning, container children
refs, layout geometry keys, sheet-group schema, publish rights →
preflight_report.json. First create one sheet group manually in the test app so the group schema can be confirmed.--executerefuses to run without a passing preflight. - Dry-run: prints the complete per-app action plan. The orchestrator is a plan/execute split — execute applies exactly the printed plan; the two cannot drift.
- Execute (per app): canary "object testing" sheet proves edit rights → every sheet (hidden ones included) is cloned as "(Original) <name>", grouped, published → working sheets go private → masters, sheet cells, and container children convert to the standard targets. Every conversion is verified immediately; the first instance of each conversion type must pass before bulk conversion of that type, and a circuit breaker halts the app after repeated findings. Originals are never destroyed at this stage.
- Review
verification_report.csv; runscan_stories.pyfor story/snapshot references to old object ids. - Sign-off with the client on the converted (still private) sheets while consumers keep seeing the frozen Original clones.
- Promote: the explicit go-live — publishes the converted
working sheets. Refuses while unresolved "attention" verifications
exist (
--forceoverrides). - Cleanup: destroys original objects only where the conversion verified.
- Decommission: removes the "(Original)" sheets and group so apps don't carry doubled sheets forever.
Safety model
- Single-space guardrail:
QLIK_SPACE_IDis mandatory; every script refuses any app outside the designated space. - Dry-run by default everywhere; state-changing runs need
--execute. - Approval gating: Qlik first-party objects convert ungated;
third-party (Vizlib etc.) convert only with
approved=Yinapproved_conversions.csv(seeded bymake_approval_worklist.py, merge-safe on rerun). - Deferred destruction: converted originals stay as invisible
orphans until
--cleanup; rollback is a cell swap. - Checkpoint/resume: progress saved per app at DoSave phase
boundaries (
run_state/); interrupted runs resume, never half-redo. - Change freeze: plan fingerprints sheet state; execute aborts if the app changed since planning.
- Audit everything:
restructure_log.csv(all actions),conversion_audit.csv(every object, old→new id, map version),container_rebuilds.jsonl+object_exports.jsonl(full pre-conversion property trees).
Four-tier recovery (rollback_app.py --app <id>)
- Whole-app QVF backup — with
QLIK_CLI_BACKUP=1, qlik-cli exports each app toqvf_backups/before any change; a failed backup aborts the app untouched. - Orphaned original — still exists until cleanup; swap the cell back. Fast and exact.
- JSON export replay — full pre-conversion property trees.
- "(Original)" clone copy — every duplicated sheet holds an exact copy of each original object at the same grid position; located automatically, and always available for manual recovery by simply opening the "(Original) <name>" sheet. (Master-item instances on clones follow the shared converted master — tiers 1-2 cover those.)
What's in the kit
| File | Purpose |
|---|---|
qlik_engine.py | Shared Engine API session: retry with backoff, throttling (QLIK_THROTTLE_MS), JSON-RPC recording (QLIK_RECORD) + offline replay. |
qlik_cloud_inventory.py | Primary crawler: recursive walk of every app in the space — sheets, master visualizations, container children (native + Vizlib), master-instance resolution, orphaned objects. |
qlik_object_inventory.py | On-prem/Desktop crawler variant; hosts the shared crawl logic. |
analyze_inventory.py | Scores every object: vendor, legacy flag, deprecation date, latest version, cloud-native equivalent, compatibility %, version status. Per-app rollup + unmapped-type discovery. |
object_classification_map.csv | The knowledge base: 91 object types with authoritative ids from qlik.dev (both bundle id spellings), verified against Qlik's deprecation timelines (May 2027 removals, retired classic table, legacy Text & image), incl. newer objects like Write table and Navigation menu. The accumulating asset — carry the latest copy. |
make_approval_worklist.py | Seeds/refreshes the approval worklist; human decisions survive reruns. |
restructure_app_sheets.py | The orchestrator: preflight, plan/dry-run, execute (steps 01-09 incl. 13-step container rebuilds and cloud-born re-creation), promote, cleanup, decommission, discover. |
rollback_app.py | Object-level rollback across the four recovery tiers (see above). |
scan_stories.py | Finds story/snapshot references to converted (old) object ids; names the replacement id per hit. |
test_planner.py | Offline planner test against a fake engine — run anywhere, no tenant needed. |
conversion_analysis_app.qvs | Load script for the Conversion Analysis app: scored inventory + map + approvals + audit + verification in one Qlik data model. |
build_analysis_app.py | Builds the Conversion Analysis app in the tenant automatically: five dashboard sheets (Overview, Object Explorer, Legacy & Deadlines, Conversion Progress, Approvals) of cloud-born native objects, load script pre-set. Upload the output CSVs to the space data files and reload. |
AGENTS.md | Canonical cross-AI agent guide (Claude, ChatGPT/Codex, Gemini, Grok, Cursor …): inviolable rules, env vars, lifecycle commands, architectural invariants, Qlik MCP + qlik-cli guidance. |
README.md / CLAUDE.md | Full runbook; CLAUDE.md is a thin pointer to AGENTS.md. |
Client-environment setup
- Python 3.8+, then
pip install websocket-client(the kit's only dependency). - Claude Code: install per claude.com/claude-code and sign in
with the company account. The kit's
AGENTS.mdbriefs any AI assistant automatically (CLAUDE.mdpoints Claude to it). - Download & unzip the kit from this page into a working folder; open the folder in Claude Code.
- Tenant API key: Profile settings → API keys (developer role required).
- Environment variables:
QLIK_TENANT = clienttenant.region.qlikcloud.com QLIK_API_KEY = <api key> QLIK_SPACE_ID = <designated shared-space GUID>
- Sanity check offline:
python test_planner.py - Inventory first:
python qlik_cloud_inventory.py→python analyze_inventory.py→ add anyunmapped_types.csvrows to the map →python make_approval_worklist.py→ review in the Conversion Analysis app. - Then the lifecycle above, piloting one app with
--app <id>before the fleet.
HTTPS_PROXY / https_proxy automatically
(QLIK_PROXY overrides; NO_PROXY and
user:pass@host:port supported). If the proxy intercepts TLS,
set QLIK_CA_BUNDLE=<corporate-root.pem>
(QLIK_INSECURE_TLS=1 is a loud last-resort while obtaining
it). pip may need --proxy and
--trusted-host pypi.org --trusted-host files.pythonhosted.org;
Claude Code honors HTTPS_PROXY too. Ask IT to allow
WebSocket (wss/443) upgrades to *.qlikcloud.com and
HTTPS to api.anthropic.com / claude.ai — some
proxies block WebSocket upgrades outright, which looks like a hang at
"Crawling ...".AI integration (works with any assistant)
- AGENTS.md in the kit is the canonical, cross-AI agent
guide (Claude, ChatGPT/Codex, Gemini, Grok, Cursor …);
CLAUDE.mdjust points to it. Any capable assistant can run the full lifecycle. - Qlik MCP server (official, tenant-hosted): connect any
MCP-capable assistant at
https://<tenant-id>.<region>.qlikcloud.com/api/ai/mcp(CIMD recommended; DCR or manual OAuth otherwise; requires the Qlik MCP: Allowed permission). ~90 tools for discovery, analysis, dashboards, master items, lineage, and automations — ideal for inventory cross-checks and exploring the findings app. No conversion tools exist in MCP: the object surgery remains this kit's Engine API code. - qlik-cli v3.4.0+ (github.com/qlik-oss/qlik-cli/releases;
verify the published SHA-256): runs off the same env vars. Set
QLIK_CLI_BACKUP=1for automatic whole-app QVF backups before each app executes — recovery tier 0; a failed backup aborts the app untouched.
Open decisions & reminders
- Vizlib licensing: if the client holds/renews Vizlib Cloud licenses, many Vizlib objects can upgrade in place instead of converting to native at 50-75% fidelity. Resolve before approving Vizlib conversions.
- Multi KPI is already broken in Cloud (unavailable since Apr 2025; removal May 2027) — apps using it are first-wave rebuild candidates.
- Old table removal date still unannounced (as of the May 2026 release; Qlik promises advance notice) — moot for this design, since every classic table converts to the Straight table anyway.
- Cross-check the inventory: the May 2026 client-managed release ships a deprecated-chart tracking sheet (Content Monitor) — run it on the source system as an independent validation of the kit's findings before each migration wave.
- Change freeze: agree a no-edit / no-scheduled-reload window for each conversion run (single-developer space makes this easy, but the orchestrator checks anyway).
- Phase two: expression-level compatibility scan; custom theme availability in the tenant.