| name | genie-code-environment |
| description | Session-start behavioral manifest for running this workshop inside Databricks Genie Code. Read FIRST when the client is Genie Code (the coding agent embedded in the Databricks workspace) so you begin knowing how it behaves — surface/page tool-scoping, the three execution paths (runDatabricksCli → SDK → native tools) and the "blocked ≠ impossible, try the next path" discipline, the runDatabricksCli allow-list tiers, bundle-deploy reality (--target dev mandatory, CWD pinned to the page's bundle root, FUSE create-then-validate gap), AppKit/Node reality (apps init --output-dir, no local npm but server-side build, SDK app deploy), agent-skills git-clone install, the Genie-Space deploy tiers, and how to verify a deployed app (3-hop OAuth session). Detection of WHICH client is active lives in vibecoding-state; this skill explains HOW the Genie Code client behaves. Not needed for the IDE+CLI client. |
| metadata | {"author":"prashanth subrahmanyam","version":"1.0","domain":"common","role":"shared","used_by_stages":["all"],"last_verified":"2026-06-03","volatility":"high","clients":["genie_code"],"evidence":["retrospectives/genie-code-field-guide.md (narrative)","probe Ledger P1–P18 (00-overview.md + genie-code-refactor-handoff.md)","probe Ledger P19–P23 (uv/pip toolchain; uv-FastAPI SNAPSHOT server-side build; agent-app /invocations OAuth; Knowledge-Assistant REST via api_client.do on SDK 0.67.0; mlflow.genai eval stack incl GEPA) — agents+mlflow track session 2026-06-03","probe Ledger P24–P33 (AppKit hardening: import-specifier root-cause bisect; pristine scaffold defaults; build logs unreadable from compute; /logz human-readable; deploy ladder + ~50s cost; package-lock.json hard requirement; no-npm/local-typecheck-impossible + non-deterministic CLI allow-list; green-deploy != working app; don't-fabricate-state; P33 round-2: residual static-but-uncaught classes — unused import TS6133, empty Radix value, escaped-quote, \\uXXXX artifact, harmful value='' skill advice, image egress) — AppKit hardening sessions 2026-06-03","coding_assistant='genie-code' fork lessons (03-prompt-section-chain.md §2a, Buckets A-rationale + C)"]} |
Genie Code Environment — session-start manifest
Confidence tags. [DOC] = Databricks documentation; [TESTED] = observed directly in a probe
(probe IDs P1–P18); [CONTESTED] = probes once conflicted — now resolved, see §"Resolved vs.
open"; [INFERENCE] = reasoned, unverified. Every behavioral claim below carries a tag + a citation.
Why this skill exists
The single highest-leverage fix for working in Genie Code is to begin each session knowing how it
behaves, instead of re-discovering it live in front of the user (field guide §6.8 — the meta-fix for
every other gap). This skill is that durable manifest. skills/vibecoding-state detects which client
is active and writes the capability block; this skill explains how the Genie Code client behaves —
detection vs. explanation, no duplication. If client_context == ide_cli, you do not need this skill.
Record the load (G3 manifest-load gate). The moment you have read this manifest in the current
thread, set environment_capabilities.genie_code_manifest_loaded: true in the live state file. This skill
is the owner of the genie_code_manifest_loaded preflight check (skills/vibecoding-state
§ Preflight Check Registry): on Genie Code, the first deploy / client-divergent prompt's enter
halts until this flag is true, so the deploy machinery (allow-list tiers, CWD pin, FUSE gap, App
scaffold/deploy, OAuth-session verify) is in context before you act. The check is inert on ide_cli.
The one operating rule (read this first)
Match the surface to the task. If a path is blocked, try the next of the three execution paths. Never
conclude "impossible" from one path or one page. Every operation that was hard-blocked on one path in
the probes had a working alternative on another. [TESTED, recurring P1–P18]
Don't fabricate state — report unverified facts as unknown. Never claim a page, surface, deploy
state, or capability you did not actually observe (e.g. "I'm on the Apps page" when you never navigated
there, or "the deploy succeeded" without reading deployment.state). If you haven't verified it this
turn, say so and probe it. The runDatabricksCli allow-list is non-deterministic (a verb blocked on
one attempt can be allowed on the next), so "blocked once" is not "impossible" — and "worked once" is not
"always works." [TESTED P32]
1. What Genie Code is, and why the surface matters
Genie Code is Databricks' context-aware AI assistant embedded throughout the workspace — notebooks, SQL
editor, jobs, AI/BI dashboards, the file editor, and bundle folders. It runs on serverless compute and
is pre-authenticated to the workspace (no auth login, no token export). [DOC; TESTED P-runtime]
The defining fact: Genie Code adapts its available tools to the surface (page/asset) you are
currently on. [DOC] A dashboard page exposes dashboard tools; a notebook page exposes code execution; a
bundle folder exposes bundle operations. This surface-scoping is the single most important thing to
understand — the same request can succeed on one page and be "not in the allow-list" on another. The first
move when a capability seems missing is to navigate to the right surface, not to conclude it's
impossible. [TESTED P10 — apps deploy blocked on a file-editor page]
2. The three execution paths
Genie Code can act on Databricks three independent ways, in order of preference:
runDatabricksCli — a pre-authenticated, API-routed CLI path with a per-command allow-list and
safety guardrails. The primary path. [TESTED]
- Python SDK —
from databricks.sdk import WorkspaceClient via executeCode; auto-authenticated;
full REST surface. This is the most capable path: it bypasses the CLI allow-list and is the
reliable way to w.apps.deploy(...), obtain a runtime bearer via w.config.authenticate() (note:
w.config.token is None on serverless — see §7), and poll deployment/run state.
Caveat: the SDK has no bundle-deploy equivalent — bundle deploy is a composite client-side
operation (read databricks.yml, resolve templates, sync files, Terraform state), so it stays on
runDatabricksCli. [TESTED]
- Native workspace tools —
createAsset, editAsset, openAsset, readTable, tableSearch,
findReferencesTool, checkPermissions, renderChart, askDataroom, … operating on governed APIs.
[TESTED]
A fourth, raw shell (executeCode language sh calling the databricks binary), is blocked by a
trampoline unless ENABLE_DATABRICKS_CLI=true — an escape hatch, not an intended path. [TESTED]
Discipline: try path 1 → if blocked, path 2 → if still blocked, path 3.
Full per-command allow-list tiers and the deploy/CWD/FUSE detail live in
references/allow-list-and-commands.md — load on demand.
3. Bundle deploy reality (the spine, on Genie Code)
The deploy contract is identical to the IDE — bundle deploy --target dev, run through
runDatabricksCli (see databricks-asset-bundles for the canonical contract). The Genie-specific facts:
--target dev is mandatory — a targetless bundle deploy is rejected by a content safety
guardrail ("could affect staging/production"); it is not a page block. --help / validate /
summary are pre-approved from any bundle-context page. [TESTED P4/P5/P6]
- CWD is pinned to the current page's bundle root — be on the page of the bundle you are deploying.
There is no
cd, no --bundle-root flag; you can only validate/deploy the bundle tied to the current
page. [TESTED P2]
- How to GET on the bundle page: open the bundle editor. As soon as a folder contains a
databricks.yml,
the Databricks workspace file browser shows an "Open in bundle editor" affordance for that folder (and an
"Open in editor" button at the top of the folder view). Click it to enter the Bundle UI, whose page CWD
IS that bundle root — this is the reliable way to satisfy the CWD pin above, and Genie Code operates more
predictably (deploy/run pre-approved) from inside the bundle editor than from a generic file page. So the
canonical sequence is: write databricks.yml under dp_bundle_root → open that folder's bundle editor →
run bundle validate/deploy/run there. A databricks.yml not found error means you are NOT on the
bundle page — open the bundle editor for the dp_bundle_root folder; never fall back to direct SQL. [TESTED — user-observed]
- Bundle recognition requires a git working tree — this is why
artifact_root is a git clone. A 3x2
workspace probe settled the rule: a folder's databricks.yml shows the "Open in bundle editor" affordance
(and a working Deploy surface) only when it sits inside a git working tree. A web git clone (a
plain workspace "Folder" containing .git) is sufficient — recognized at the root AND in nested
subfolders (so <artifact_root>/<use_case_slug>_dab/databricks.yml is recognized when artifact_root is the
clone); a managed Databricks Repos object is NOT required (it is the documented fallback). A plain
mkdir directory (no .git), even with a valid databricks.yml, is NOT recognized. This is the whole
reason the kickstart git clones the workshop repo INTO artifact_root (and merely copies the tree to the
.assistant/skills discovery path). [TESTED — 3x2 workspace probe]
- Surface a clickable bundle-editor link — don't make the operator hunt for the icon. With the
pre-authenticated
WorkspaceClient (w): host = w.config.host; o = w.get_workspace_id();
file_id = w.workspace.get_status("<dp_bundle_root>/databricks.yml").object_id;
folder_id = w.workspace.get_status("<dp_bundle_root>").object_id. Then the bundle-editor URL is
{host}/editor/files/{file_id}?o={o}&contextId=folder%3A{folder_id} (the plain folder is
{host}/browse/folders/{folder_id}?o={o}). Print the bundle-editor link and tell the operator to open it
before deploy. [TESTED — user-observed]
- 🛑 Blocked
bundle command ⇒ navigate, don't improvise. If still blocked, STOP. bundle deploy/run
are page-context-gated: BLOCKED on a generic file/notebook page, but they work normally from the bundle
editor — CONFIRMED in the field, the same bundle deploy that returned "blocked by safety guardrails" from
a file page returned "Deployment complete!" and bundle run … SUCCESS once the operator opened the bundle
editor. So a "blocked" / databricks.yml not found message is a wrong-page signal, not a dead end: open
the bundle-editor link and retry. Only if it still fails from the bundle editor do you STOP and report the
blocker. Do NOT fall back to the Jobs/Pipelines REST API (jobs/create, /api/2.0/pipelines), the SDK,
or direct SQL to "get the tables created" — that silently defeats version control and bundle destroy
cleanup and is the exact regression this spine prevents. The REST/SDK route is an escape hatch available
only on explicit operator authorization. [TESTED — user-observed]
- Edit the existing on-page
databricks.yml. Files newly written via createAsset/the workspace API
do not reach the CLI's FUSE mount in the same session, so "create a new bundle then validate it"
fails — edit the bundle already on the page. [TESTED P3]
- Use
bundle validate / bundle summary as safe pre-flight (pre-approved, any page). [TESTED P4]
4. AppKit / Node reality
apps init needs --output-dir — it defaults to the workspace root (/Workspace/<name>), ignoring
the page CWD. Pass --output-dir . (page folder) or an explicit /Workspace/Users/<email>/<repo>.
[TESTED P14]
- No local
npm/npx/corepack in the shell (only node is present), but the Apps runtime builds
server-side: a SNAPSHOT deploy runs npm install + npm run build (Vite) from un-built source. A
Genie-Code participant can edit client/src/*.tsx directly and redeploy with no local Node toolchain.
[TESTED P9/P11/P18 — verified: an edited string appeared in the server-built JS bundle]
apps deploy via runDatabricksCli is unreliable — the allow-list is non-deterministic (blocked
on one attempt, allowed on the next on the same page type — not cleanly page-gated) and CWD-defeated
(the enhanced build flow only fires when CWD = the project root, which never held in probes → it demands
APP_NAME and falls through to the build-skipping API-direct path; the enhanced flow also runs a local
build/typecheck/lint that needs npm, which is absent). The reliable cross-context path is the
SDK: w.apps.deploy(<name>, AppDeployment(source_code_path=…, mode=SNAPSHOT)) via executeCode,
which bypasses the allow-list and runs the build server-side. Prefer the SDK SNAPSHOT path; treat a
blocked apps deploy/apps init as transient — retry or set ENABLE_DATABRICKS_CLI=true, never declare
it impossible. [TESTED P10/P11/P32]
- Python toolchain IS present (Track A agent apps are Python, not Node). Unlike npm, the
uv binary
(/usr/local/bin/uv), pip 25.0.1, and Python 3.12 in a writable ephemeral venv ARE available in
the shell — uv pip install -e . / python -m pip install -e . against a pyproject.toml complete
in-session. The venv is ephemeral (no persistence across sessions/cluster restarts), so treat installs
as per-session. (The field guide only tested the Node toolchain; this closes the Python side.) [TESTED P19]
- A
uv-based FastAPI app builds server-side on a SNAPSHOT deploy, exactly like Node/Vite. The
agent-openai-advanced Track A template (pyproject.toml + app.yaml with a uv run … command) deploys
via the SDK w.apps.deploy(<name>, AppDeployment(source_code_path=…, mode=SNAPSHOT)): the platform resolves
deps with uv and starts the process server-side, reaching SUCCEEDED ("App started successfully") —
no local build. So Track A agent apps follow the same SDK-SNAPSHOT deploy path as AppKit, and the agent
app can live at a clone-rooted top-level root (SNAPSHOT copies the source). [TESTED P20]
- SDK ergonomics (carry into the deploy code):
w.apps.deploy(...) returns a Wait object — read
wait.response / wait.deployment_id, then poll w.apps.get_deployment(app_name, deployment_id) →
dep.status.state.value (IN_PROGRESS→SUCCEEDED) / dep.status.message. The App object has no
.status attribute — use app.compute_status, app.active_deployment, app.pending_deployment,
app.url. [TESTED P20]
- AppKit build failures trace to two import specifiers — preserve the scaffold. A pristine
apps init
ships client/src/index.css with @import "@databricks/appkit-ui/styles.css"; and .tsx importing from
@databricks/appkit-ui/react. Hand-regenerating these from memory reintroduces the bare
@databricks/appkit-ui (no React export) and extension-less …/styles (unresolvable) — the dominant
first-deploy failure. Edit App.tsx/index.css incrementally; keep ErrorBoundary.tsx. [TESTED P24/P25]
- There is no local typecheck — a regex pre-flight is the only static gate.
npm/npx/corepack are
dangling symlinks and tsc cannot resolve @databricks/appkit-ui/vite/client without node_modules,
so the import-specifier failure is not catchable locally. Before a (~50s) deploy, scan
client/src/** via executeCode+regex for the two bad specifiers and fix hits first. [TESTED P30]
- The static gate catches more than import paths; author with literal characters. A round-2 run still
burned deploy cycles on classes the path-only gate missed, so the regex pre-flight now also blocks an
empty Radix
<SelectItem value=""> (runtime crash on menu open), an escaped single-quote in a JSX
attribute (Vite/rolldown parse crash), and a stray \uXXXX escape artifact, and flags for review any
unused named import (noUnusedLocals → hard TS6133 build failure). Because you author .tsx through
Python open().write(...), prefer triple-quoted raw strings and write the real '/" characters —
double-escaping is what produces the \u0027-style artifacts the gate flags. [TESTED P33]
- Build logs are unreadable from compute — escalate to
/logz in a browser. deployment.status.message
- REST only say "check /logz";
databricks apps logs <name> → OAuth error; /logz over raw HTTP → 401.
On FAILED, hand the operator <app-url>/logz (already authenticated) to read the exact error TS…
line; no-browser fallback = the 2–3-file batch ladder (redeploy small batches; the batch that flips
green→FAILED holds the break). [TESTED P26/P27/P28]
package-lock.json is a hard requirement on SNAPSHOT. Deleting it hard-fails the source-export phase
in ~10s (RESOURCE_DOES_NOT_EXIST), before npm install. Never delete it as a reset. [TESTED P29]
- A green deploy does NOT mean a working app. A server boot crash →
FAILED (agent-visible); a client
runtime crash compiles and deploys SUCCEEDED/ACTIVE but renders blank — invisible to the agent.
Require a human render check in the browser (the scaffold ErrorBoundary surfaces the stack). [TESTED P31]
5. Agent-Skills install
databricks aitools install (and the legacy experimental aitools install) is hard-blocked via
runDatabricksCli — the whole aitools verb family is not allow-listed. [TESTED P12] The working path is
git clone of databricks/databricks-agent-skills (git present, github.com reachable). [TESTED P13]
npx @databricks/appkit docs is unavailable (no npm); fetch AppKit docs via WebFetch instead. [TESTED P13]
6. Genie Spaces
Use the RULE_8 three tiers from databricks-asset-bundles ("Genie Spaces — three deploy tiers"):
Tier 1 native genie_spaces resource (preferred, landing ~this month), Tier 2 provisioning-job (active
fallback, both clients), Tier 3 createAsset({assetType:"genie", …}) + REST PATCH — the Genie-Code
hybrid dev-authoring loop. [TESTED P8] createAsset returns a live Space ID but builds only a shell
(tables registered; 0 instructions, 0 benchmarks, and metric views miscategorized under
data_sources.tables). Populate the FULL serialized_space with PATCH /api/2.0/genie/spaces/{id},
then PERSIST that JSON into the bundle so a job reproduces it — Tier 3 is sanctioned for dev iteration
only when the JSON is persisted (an orphan Space with no file is the regression). The Space title
always carries the per-user prefix. See §6c for the create→extract-back→persist mechanics. Point to the
spine for the canonical recipe; do not restate it.
Genie API gotchas (TESTED). Find Spaces with searchAssets(assetTypes=["datarooms"]), but the ONLY
supported mutation is PATCH /api/2.0/genie/spaces/{id} with a full body — NEVER PATCH /api/2.0/data-rooms/{id} (it silently wipes serialized_space to {}). Export config only via
GET /api/2.0/genie/spaces/{id}?include_serialized_space=true (there is no /export endpoint — it
404s). readAssetById(assetType="genie") is unsupported — use the REST GET. Every text field in
serialized_space is List[str]; data_sources.tables/metric_views entries carry NO id; all IDs are
uuid4().hex. The canonical contract + _assert_sql_arrays validator live in
semantic-layer/04-genie-space-export-import-api/SKILL.md.
6b. Knowledge Assistants (Agent Bricks)
A Knowledge Assistant is not a bundle resource (no knowledge_assistant DABs type) — like Genie Spaces
it is an authoring-discipline exception created via the Agent Bricks REST API. The Genie-Code gotcha:
the bundled databricks-sdk 0.67.0 has no w.knowledge_assistants wrapper (hasattr → False), even
though the REST API is live. Call it through the generic SDK escape hatch w.api_client.do(<verb>, <path>, body=…) — no SDK upgrade needed (an uv pip install -U databricks-sdk would be ephemeral and is
unnecessary). Verified contract (mirrors the newer SDK's KnowledgeAssistantsAPI): [TESTED P22]
| Operation | Verb + path |
|---|
| list | GET /api/2.1/knowledge-assistants |
| create | POST /api/2.1/knowledge-assistants (body requires ≥1 knowledge source) |
| get / update / delete | GET / PATCH / DELETE /api/2.1/{name} |
| list / create sources | GET / POST /api/2.1/{parent}/knowledge-sources |
| sync sources | POST /api/2.1/{name}/knowledge-sources:sync |
GET /api/2.1/knowledge-assistants returned a live KA under Genie Code runtime auth. Some verbs also answer
on the older /api/2.0/... prefix (the API is mid-migration) — prefer 2.1, fall back to 2.0 only if a
2.1 verb 404s. Readiness polls via serving-endpoints get / WorkspaceClient.serving_endpoints (both
available). [TESTED P22]
6c. Semantic-layer authoring — native create + extract-back (TVF / Metric View / Dashboard)
The semantic layer (TVFs, Metric Views, Genie Spaces, AI/BI Dashboards) uses a hybrid model distinct
from the lakehouse table-DDL discipline in §8 (schemas/tables remain bundle-job-only): author the
definition file FIRST, apply it natively for a fast dev loop, extract the live asset back and diff it
against the file, then keep the Asset Bundle as the version-controlled source of truth and the non-dev
deploy mechanism. The invariant: persisted file + live matches file + bundle validates + job ran once in
dev. An orphan asset (no file) or drift (live ≠ file) is the regression.
| Artifact | Create natively (dev) | Extract definition back | Persist to bundle | Notes / blocked |
|---|
| TVF | executeCode SQL CREATE OR REPLACE FUNCTION … RETURNS TABLE(…); INVOKE-test SELECT * FROM fn(…) | DESCRIBE FUNCTION EXTENDED + information_schema.routines.routine_definition | .sql with ${catalog}/${gold_schema} placeholders | SHOW CREATE FUNCTION blocked (PARSE_SYNTAX_ERROR) |
| Metric View | executeCode SQL CREATE OR REPLACE VIEW … WITH METRICS LANGUAGE YAML AS $$…$$; validate with a MEASURE() query | readTable → metadata.view_query_text (or UC REST GET /api/2.1/unity-catalog/tables/{full_name}.view_definition) | .yaml with placeholders | SHOW CREATE TABLE blocked for METRIC_VIEW; appears in information_schema.tables (type METRIC_VIEW), NOT .views |
| Genie Space | createAsset(assetType="genie", tableIdentifiers=[…]) shell → PATCH /api/2.0/genie/spaces/{id} full serialized_space (run _assert_sql_arrays first) | GET /api/2.0/genie/spaces/{id}?include_serialized_space=true (assert non-zero instructions/benchmarks/sql_functions; MV under metric_views) | full serialized_space JSON | see §6 gotchas; never PATCH /data-rooms/{id} |
| AI/BI Dashboard | createAsset(assetType="dashboard") then AUTO-NAVIGATE openAsset(assetType="dashboard", assetId=<uuid>) + print a clickable link; author widgets on the canvas | readAssetById(assetType="dashboard", assetId=<uuid>) → full .lvdash.json (UUID=published; treeNodeId=draft+path) | .lvdash.json | widget editing requires the canvas page; no remote widget edit |
Native authoring competence: TVFs and Metric Views are FULLY native — use the native using-metric-views
and writing-sql skills (more accurate than the workshop 01/02, which are CI/validation references
only). Genie Spaces need the workshop 04 (JSON schema + _assert_sql_arrays validator) — load it.
Dashboards are authored by navigation, then the extracted .lvdash.json feeds the existing
deploy_dashboard.py bundle job for persistence + cross-env redeploy.
7. Verifying a deployed app
A deployed App sits behind the Databricks Apps OAuth gate — a raw Authorization: Bearer token (even
SDK w.config.token) is rejected (/api/health → 401). [TESTED P16] Two working ways:
- Browser (simplest manual verify) — open
w.apps.get(<name>).url; the OAuth flow establishes the
session. Use apps logs <name> for backend assertions.
- Programmatic — replay the 3-hop Apps OAuth handshake in one
requests.Session() so the CSRF
cookie persists through the callback (PKCE match), then reuse the session for all /api/* calls.
[TESTED P17] Reusable snippet in
references/app-verification.md.
Serverless token nuance (decisive — captured live). On serverless compute w.config.token is
None; the runtime bearer that the OIDC authorize endpoint (Hop 2) accepts comes from
w.config.authenticate()["Authorization"] (a short dkea… token). Use that for Hop 2 — a missing/raw
token bounces Hop 2 to /login.html. After Hop 3 the __Host-databricksapps session cookie authenticates
everything; no Authorization header is needed post-handshake. The 3-hop handshake is confirmed not
just for static apps but against a Track A Agent App /invocations (FastAPI host, not Model Serving):
POST /invocations carrying only the session cookie → 200 + a valid ChatCompletion body. [TESTED P21]
8. How a Genie Code session actually operates (operating model)
This is how a session runs — distilled from the field forks (someone ran this workshop on Genie Code):
- A pre-authenticated
WorkspaceClient is available via executeCode (no auth step). Run Python/SQL on
serverless directly.
- Read and write workspace files, not
/tmp — /tmp is not durable and is not where artifacts
belong. Build artifacts in memory or write to a project/workspace path.
- Anchor every relative artifact path to
artifact_root, never the page CWD or your home dir.
artifact_root is the workshop PROJECT root that skills/vibecoding-state captures into the
## Environment Capabilities block (= the local repo root on ide_cli; the USER PROJECT path
/Workspace/Users/<email>/<repo> on genie_code, which is a git clone of the workshop repo — a git
working tree, so generated bundles/apps/docs are recognized as Databricks Asset Bundles). The skill tree is
copied from artifact_root to skills_install_root = /Workspace/Users/<email>/.assistant/skills/<repo>
(the read-only discovery path); artifacts build in artifact_root, skills load from skills_install_root. A
bare docs/design_prd.md is
unsafe here because Genie Code's CWD is page-type-dependent (the bundle root on a bundle page, the
workspace home otherwise — see §"Resolved vs. open"), so the same relative path resolves to different
places. Write deliverables to <ARTIFACT_ROOT>/<relpath> (e.g. <ARTIFACT_ROOT>/docs/design_prd.md),
filling <ARTIFACT_ROOT> from the captured artifact_root. /tmp remains forbidden. The kickstart git clones the workshop repo INTO artifact_root (the workspace path), making it a git working tree where
bundles are recognized — a bare mkdir does NOT (TESTED, §3). Confirm <artifact_root>/.git is present
before the first write (vibecoding-state's resolve_root / bootstrap step 0 do this; git clone if
absent, then confirm with os.path.exists to clear the FUSE create-then-validate gap; a Repos-managed Git
folder is the documented fallback). [TESTED P2]
- The data-product bundle anchors to
dp_bundle_root, a dedicated subdir — not the bare artifact_root root.
skills/vibecoding-state captures dp_bundle_root = <artifact_root>/<use_case_slug>_dab (e.g.
…/vibe-coding-workshop/booking_app_dab). The whole DP pipeline (bronze→silver→gold→semantic) writes its
databricks.yml / src/ / resources/ UNDER <DP_BUNDLE_ROOT>. Writing them at the bare artifact_root
root (the framework clone itself) is the observed "one level too high" bug — the bundle then has no clean
page-context root to deploy from. Because bundle deploy's CWD is pinned to the current page's bundle root, <DP_BUNDLE_ROOT> is ALSO the page you deploy
from: be on that folder's page, then run bundle validate/deploy/run. A databricks.yml not found
error means you are on the wrong page — navigate to <DP_BUNDLE_ROOT>; never fall back to direct SQL.
- Load every workshop skill by its clone-rooted
readSkillFile path, never a bare repo-relative path or
@-mention. Genie Code loads skills through readSkillFile, which has no repo-root-relative
resolution: a file under .assistant/skills/ is loadable only as skills/{path-after-.assistant/skills/}.
Because the skill tree is copied to .assistant/skills/<clone-folder>/, a repo-relative skill path X/Y/SKILL.md
must be loaded as readSkillFile("<skill_ref_root>/X/Y/SKILL.md") where skill_ref_root is captured by
skills/vibecoding-state (= "skills/" + basename(skills_install_root), default skills/vibe-coding-workshop;
empty on ide_cli, where @-mentions resolve from the workspace root). Note skill_ref_root is anchored
to skills_install_root (the .assistant/skills/<repo> copy), NOT artifact_root (the git-cloned user
project) — so skills keep loading from the .assistant/skills copy even though artifacts build in the project. Nesting depth is irrelevant —
e.g. data_product_accelerator/skills/bronze/00-bronze-layer-setup/SKILL.md loads as
skills/vibe-coding-workshop/data_product_accelerator/skills/bronze/00-bronze-layer-setup/SKILL.md. A bare
@data_product_accelerator/… sends Genie Code on a goose chase. AGENTS.md does not help here — it is
read once at the clone root and does not propagate across Agent threads, so each prompt must name the
skill by its skill_ref_root-prefixed path (the genie-code prompt forks do exactly this). [TESTED — user-observed]
- The forks evolved a small helper shape —
w, read_file/write_file, run_sql, run_job_by_name.
These are session conveniences for inspection and orchestration, NOT artifact-creation channels.
SUPERSEDED — do not resurrect. The forks also carried ad-hoc SDK-creation primitives
(create_job, create_pipeline_idempotent, make_job_notebook). Those created un-versioned workspace
state that diverged from the IDE's bundle output — that divergence was itself the regression. They
are superseded by the bundle-deploy spine (databricks-asset-bundles): every artifact is a bundle
resource brought to life by bundle deploy, identically on both clients. The only sanctioned in-session
creation is RULE_8 Tier 3 Genie-Space createAsset (last-resort). [decision #6/#8; M3 §2a Bucket C]
This explicitly includes data-product table DDL. Creating Bronze/Silver/Gold schemas and tables —
CREATE SCHEMA, CREATE TABLE, DEEP CLONE, ALTER TABLE … SET TBLPROPERTIES, CLUSTER BY, and the
data load — directly via executeCode/spark.sql is the SAME regression: it produces live tables with
no versioned bundle behind them. Those statements are the body of a bundle job notebook, executed by
bundle run, not run by hand. The frictionless executeCode path is the trap (it "works" and the tables
appear, so the gate passes) — but it bypasses the spine. If bundle deploy is blocked, FIX the page
context (open the dp_bundle_root bundle editor, §"bundle-deploy reality"); do not fall back to
direct SQL, the Jobs/Pipelines REST API (jobs/create, /api/2.0/pipelines), or the SDK — those are an
escape hatch only on explicit operator authorization, and the field-confirmed fix is the bundle editor, not
the API. Read-only inspection (SHOW TABLES, DESCRIBE, SELECT COUNT(*)) via executeCode is fine.
This section is the canonical home for the session operating model — the RULE_0 client_context
preamble and the PRE-REQUISITES Genie branch point here rather than re-inlining it.
9. Why the portability rules exist (rationale)
The single-body portability rules (authored elsewhere) exist because of these Genie behaviors — kept here
as the explanation, not the rule:
- No
--var resolver at the page. Asset Bundle variables resolve at deploy time; you cannot
"pass a var" interactively. The agnostic body states the concrete-value requirement once.
/tmp is not durable and is not the place to write deliverables — write to a workspace/project path.
- CWD is page-type-dependent, so a bare relative path (
docs/design_prd.md) is unstable across pages.
The artifact_root rule above exists for this reason: resolve relative artifacts against the captured
clone root, not the page CWD. The agnostic body keeps a single anchored form (<ARTIFACT_ROOT>/…).
- No repo-root-relative skill resolution and no cross-thread
AGENTS.md. readSkillFile only resolves
skills/{path-after-.assistant/skills/}, and AGENTS.md is read once at the clone root without propagating
to later Agent threads. The skill_ref_root rule above exists for this reason: prompts (and the
genie-code forks) name each skill by its skill_ref_root-prefixed path so it loads regardless of thread.
- Don't rely on
jq / raw shell for control flow — build and inspect artifacts in-memory via the SDK.
10. Session ergonomics — parallel skill reads, file-write tiers, and executeCode timeouts
Three behaviors that materially change session speed and reliability. All [TESTED — user-observed,
Gold-design run].
- Read every skill a phase needs in ONE batched turn.
readSkillFile calls run in parallel —
issuing all of a phase's skill reads in a single turn returned every file successfully, whereas
serializing them costs one full turn each. When a step's Step-1 list (or an orchestrator's "Mandatory
Skill Dependencies") names several skills with no inter-dependency, load them together, not one per
turn. [TESTED]
- File writes — two paths, choose by situation (there is NO single-call, compute-free file-creation
tool):
executeCode with open(path,"w").write(...) — one call; creates and writes any workspace file
directly; but needs warm serverless compute (see cold-start below). Best for creating many files
or large content — once compute is warm.
createAsset → readFile → workspaceUpdateFile — a compute-free trio (no cold-start risk),
but with two field-proven constraints: workspaceUpdateFile cannot create a new file (the file must
already exist) and requires the file to have been read in the current thread first. So:
createAsset (with assetType: file) makes the empty file → readFile satisfies the read-first guard →
workspaceUpdateFile populates it. Three calls, zero compute. Best for updating a single
already-read file, or writing a few files before compute is warm. [TESTED]
- Verify file writes with
os.path.exists / os.listdir, NOT listFiles. After writing a file via
executeCode open(...), confirm it landed with os.path.exists(path) (or os.listdir(dir)) in the
same executeCode block. Do NOT use listFiles to confirm a just-written file: the workspace REST
API that backs listFiles lags files written through the FUSE mount, so it returns false "missing-file"
negatives — and you waste turns recreating files that already exist. [TESTED — a live run saw
listFiles=7 while os.listdir=12 for the same directory immediately after writing]
executeCode cold start & timeout — never starve the first call. The first executeCode in a
session pays a serverless cold start of ~3–5 minutes before any code runs; subsequent calls are warm
(~0 s). timeoutMinutes defaults to 15 (minimum 5). Never set timeoutMinutes below 15 — the
only thing a smaller budget buys is a cold-start timeout and a wasted retry (the retry then "succeeds"
only because the failed first attempt warmed the compute). For heavy phases (e.g. Gold design — CSV
parsing, per-table YAML generation, cross-table validation) set it higher (≥ 20), and/or send a
trivial print("ready") warm-up call first so the cold start is paid once, up front. [TESTED — two
5-min timeouts on cold first calls; identical code on warm compute ran instantly]
Resolved vs. open
The field guide flagged several items [CONTESTED]/[INCOMPLETE]. The session-2/3 probes closed them
— do not re-import stale doubt:
| Field-guide open item | Status now |
|---|
Does the Apps runtime run npm run build server-side from un-built source? [INCOMPLETE] | RESOLVED — yes. [TESTED P18 / field guide §6.2] |
Is bundle deploy page-context-gated or safety-guardrail-gated? [CONTESTED] | Practical rule settled (formally still [CONTESTED]): --help/validate/summary always run; a real deploy needs an explicit non-prod --target dev (targetless → content guardrail). Operate on that rule; don't re-litigate the why. [TESTED P4–P6] |
Is the runDatabricksCli CWD the page's bundle root or the workspace home? [CONTESTED] | Page-type-dependent: = the bundle root on a bundle page (proven), = workspace home on non-bundle pages (notebook/AppKit). Be on the bundle's page to deploy it. [TESTED P2] |
Is FUSE-invisibility of new files latency or a hard boundary? [OPEN] | Unresolved upstream — treat as a boundary in-session: edit the on-page file. [TESTED P3] |
Does any allow-listed path reach the AppKit enhanced (apps deploy) build in-session? [OPEN] | Not demonstrated — use the SDK SNAPSHOT path (build still runs server-side). [TESTED P11/P18] |
Does a uv/FastAPI (non-Node) app also build server-side on SNAPSHOT? (not in field guide) | RESOLVED — yes. uv-based Track A agent apps install deps + start server-side → SUCCEEDED. [TESTED P20] |
Does the 3-hop OAuth handshake work against a Track A Agent App /invocations, and what token does Hop 2 need on serverless? (extends P16/P17) | RESOLVED — yes; Hop 2 uses w.config.authenticate() (not w.config.token, which is None on serverless). [TESTED P21] |
| Does the Knowledge Assistant API work on Genie Code's SDK 0.67.0? (agents track) | RESOLVED — REST yes, SDK wrapper no. w.knowledge_assistants absent in 0.67.0; call /api/2.1/knowledge-assistants via w.api_client.do (no upgrade). [TESTED P22] |
Is the full mlflow.genai eval stack on the Genie runtime? | RESOLVED — yes. mlflow 3.8.1 has scorers, evaluate, and optimize_prompts (GEPA). [TESTED P23] |
AppKit hardening ledger (P24–P37)
Live Genie-Code probes from the AppKit hardening sessions (2026-06-03), distilling an ~11-deploy booking-app
failure (P24–P32) plus a round-2 deploy of the hardened skills (P33) and the Lakebase fork probes (P34–P37d)
into preventable causes. These extend the P1–P23 ledger; the §4 AppKit facts cite these rows.
| # | Finding | Result |
|---|
| P24 | Root cause of the original build failures (reproduce-then-bisect) | Green only when BOTH fixed: index.css @import "@databricks/appkit-ui/styles" → styles.css; component imports from "@databricks/appkit-ui" → /react. Neither alone. |
| P25 | Scaffold defaults are correct | Pristine apps init ships styles.css + /react; the agent had overwritten correct files with hallucinated specifiers (regenerating from memory). |
| P26 | Build logs unreadable from compute | SDK/REST return only generic "check /logz"; apps logs → OAuth error; programmatic /logz → PKCE/401. No agent-visible build error. |
| P27 | /logz is human-readable in a browser | Authenticated <app-url>/logz shows the exact App.tsx(L,C): error TS… + full Vite/tsc pipeline. Escalation = hand the operator the link. |
| P28 | Deploy ladder localizes a break; cost = the deploy loop | 2–3-file batches localize a break; deploy ≈ 50s cold / 30s warm; file writes ≈ 0.15s → deploy loop is the bottleneck, not file I/O. |
| P29 | package-lock.json is a hard deploy requirement | Deleting it fails at the source-export phase in ~10s (RESOURCE_DOES_NOT_EXIST), before npm install. |
| P30 | No functional npm; local full typecheck impossible | node present; npm/npx/corepack dangling symlinks; tsc can't resolve appkit-ui/vite without node_modules. → grep/regex pre-flight is the only static gate. |
| P31 | Green deploy ≠ working app | Server boot crash → FAILED (visible); client runtime crash → SUCCEEDED/ACTIVE but blank (invisible) → human render check required; keep ErrorBoundary. |
| P32 | CLI allow-list non-deterministic; agent fabricated page-state | apps init blocked once then allowed on the same page type; agent reported an "Apps page" it never navigated to. SDK deploy bypasses the guardrail → reliable. Don't-fabricate-state. |
| P33 | Round-2 run of the hardened skills — residual static-but-uncaught classes | Import-specifier gate + lockfile rule + human render gate all held. New misses: deploy #1 FAILED on an unused import (TS6133/noUnusedLocals); a green deploy then crashed at runtime on <SelectItem value=""> (Radix needs a non-empty value) — the 02-build skill's own gotcha had prescribed value="". Also external Unsplash hotlinks went blank (browser egress) and \u0027 artifacts appeared from Python-written source. → extend the regex gate (empty value, escaped quote, \uXXXX blocking + unused-import review), fix the harmful gotcha, mandate an onError data-URI image fallback. |
| P34 | Dependencies edit package.json directly (no local install) | Adding a dependency by editing package.json works — the server-side install at deploy reconciles it; the lockfile must stay in place (P29). The Lakebase setup fork edits package.json rather than shelling a local install. |
| P35 | databricks.yml resources are inert on the SDK SNAPSHOT path | A postgres_projects declared in databricks.yml never materializes via the SNAPSHOT deploy (it is the Terraform spine, not applied here) → provision Lakebase over REST (POST /api/2.0/postgres/projects) + PATCH /api/2.0/apps/{name} to bind, instead of declaring bundle resources. |
| P36 | databricks apps validate is blocked / page-dependent | Not reliably runnable from the agent on Genie Code → substitute a local YAML structural check (parse app.yaml + package.json, assert valueFrom: postgres + DB_SCHEMA + the dependency). |
| P37b/d | Canonical Lakebase wiring boots straight to RUNNING when bound | The supported wiring shape is the onPluginsReady(appkit) hook on createApp + appkit.server.extend(...) — NOT server({ autoStart: false }) + a manual AppKit.server.start() (the listener double-listen()s → boot crash; the earlier "autoStart:false broken" read was self-inflicted by omitting/duplicating start()). The lakebase plugin imports from the framework entrypoint (@databricks/appkit), not from the driver package (@databricks/lakebase). Binding the postgres resource BEFORE the first plugin-bearing deploy → RUNNING with no CRASHED hop; an unbound app carrying valueFrom: postgres boots CRASHED. |
Reference files
- allow-list-and-commands.md — the full
runDatabricksCli
allow-list tiers (per-command, probe-cited), deploy/CWD/FUSE detail, apps init --output-dir,
postgres list-*, the git-clone install path.
- app-verification.md — the 3-hop OAuth
requests.Session() snippet
and the server-side build evidence.
Related skills
databricks-asset-bundles — the canonical deploy contract + Genie-Space tiers (this skill points
there; it does not restate deploy mechanics).
databricks-expert-agent — surface-scoping + path-fallback discipline (cross-references this skill).
skills/vibecoding-state — detects the client and writes the capability block; points here for the
behavioral detail.