# Vibecoding State

> Vibecoding State — Workshop Runtime Contract

- Skill: `databricks-solutions/vibecoding-state` (Agent Skill, multi-file: 11 files)
- Install (CLI): `npx skillmds@latest add databricks-solutions/vibecoding-state`
- Raw SKILL.md: https://api.skillmd.com/api/skills/databricks-solutions/vibecoding-state/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: databricks-solutions (https://skillmd.com/u/databricks-solutions)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/databricks-solutions/vibecoding-state

---


# Vibecoding State — Workshop Runtime Contract

This skill is the single source of truth for how every prompt in the repo-root [`Instructions.md`](../../Instructions.md) workshop and its worked instantiations (e.g. [`example/skyloyalty/WALKTHROUGH.md`](../../example/skyloyalty/WALKTHROUGH.md)) interacts with the shared per-user **state file** and the async per-prompt **retrospective file**.

Prompts do **not** re-state the "Vibecoding state contract / Preamble / Postamble / Pathway Applicability guard / Retrospective contract" prose. They invoke this skill with typed parameters and this skill owns the behavior.

---

## When to Use

Invoke this skill — always — when executing any vibecoding workshop prompt. Specifically:

- **Every** prompt in [`Instructions.md`](../../Instructions.md) (Prompt 0.1 through 8.24).
- **Every** prompt in `example/<use_case>/WALKTHROUGH.md` (e.g. SkyLoyalty's 22 prompts).
- **Every** post-workshop per-prompt retro + the single session rollup.

Do not invoke for: one-off terminal commands, ad-hoc debugging, or anything outside the workshop flow.

Candidate future skills that this skill deliberately does **not** absorb (they stay inline in prompts for now): `workspace-preflight` (`databricks compute-policies list`, `databricks current-user me`), `llm-endpoint-health` (`databricks serving-endpoints get <endpoint>`), `bundle-deploy` (`databricks bundle validate/deploy/run`), `derive-app-name` (email + use_case_slug → APP_NAME), `prd-reader`. If you end up duplicating any of those across prompts, extract them next.

---

## Operations

All thirteen operations take a typed parameter bag. The prompt invokes the operation by name (e.g. `vibecoding-state.enter`) and passes only the dynamic values. Everything else (file paths, log schema, gate rules, pathway matrix, resolved spec) is owned by this skill.

### Operation: `bootstrap`

**When:** Once per workshop run, at Prompt 0.1 (`Instructions.md`) or Prompt 1 (`WALKTHROUGH.md`). Only runs if no live state file exists.

**Inputs:**

| Param | Type | Required | Description |
|---|---|---|---|
| `use_case_slug` | string | required | Short hyphenated lowercase slug, ≤ 26 chars. Drives the bootstrap path. |
| `workspace_url` | string | required | The only value the operator provides by hand. Every subsequent prompt reads it from state. |
| `pathway` | `A` \| `B` \| `C` \| `D` | required | |
| `track` | `A` \| `B` \| `C` \| `n/a` | required | `n/a` for pathways A, B. |
| `prd_path` | string | required | e.g. `example/<use_case_slug>/docs/design_prd.md`. |
| `llm_endpoint` | string | required | e.g. `databricks-claude-sonnet-4-6`. |
| `dabs_bundle_path` | string \| `n/a` | required | |

**Behavior:**

0. **Detect environment & write the `## Environment Capabilities` block (RULE_0 / RULE_1 enabling).** Before any other step, resolve the active coding client and write the capability block (schema in [`references/state-template.md`](references/state-template.md) § *Environment Capabilities*) as **section 0** of the state file. This is the block every prompt and the `enter` / `exit` operations read to resolve the deploy verb, CLI channel, and state-file root — so no skill or prompt body assumes a local IDE. Detection signal **[inference — pending the live Genie Code probe]**: if a Databricks-managed CLI channel is present in-session (the `runDatabricksCli` tool / Genie serverless markers), set `client_context: genie_code`, `cli_channel: runDatabricksCli`, `bundle_deploy.page_context_required: true`, both `state_file_root` and `artifact_root` to the **user project workspace path** (`/Workspace/Users/<email>/<repo>` — a **git clone** of the workshop repo, so it is a git working tree and generated bundles are recognized), and `skills_install_root` to the **skills copy path** (`/Workspace/Users/<email>/.assistant/skills/<repo>`, a copy of the tree for discovery, no git required); otherwise set `client_context: ide_cli`, `cli_channel: local_shell`, `bundle_deploy.page_context_required: false`, and `state_file_root`, `artifact_root`, and `skills_install_root` all to the local repo path. **Ensure `artifact_root` (and thus `state_file_root`, which equals it) is a git-backed working tree** — the kickstart `git clone`s the workshop repo into it; if `<artifact_root>/.git` is absent, `git clone <workshop remote>` into it before writing the `## Environment Capabilities` block (a bare `mkdir` leaves generated bundles unrecognized — TESTED; a Repos-managed Git folder is the documented fallback). On `genie_code` operate via the workspace path / `executeCode`, mindful of the FUSE create-then-validate gap (clone, then confirm `os.path.exists(<artifact_root>/.git)` before writing); a non-PRD-first flow may reach `bootstrap` before the clone exists. (`artifact_root` defaults equal to `state_file_root`; it is where relative artifact paths resolve as `<ARTIFACT_ROOT>/<relpath>` — see `skills/genie-code-environment` §8. `skills_install_root` is the read-side anchor: a copy of the clone on `genie_code`, the same repo on `ide_cli`.) **Also set `skill_ref_root`** — the prefix that makes a repo-relative *skill* path loadable: on `genie_code` it is the `readSkillFile` prefix `"skills/" + basename(skills_install_root)` (e.g. `skills/vibe-coding-workshop`, because the skills copy lives under `.assistant/skills/<repo>` and any file there loads as `skills/{path-after-.assistant/skills/}`); on `ide_cli` it is empty (repo-relative skill paths / `@`-mentions resolve as-is). See `skills/genie-code-environment` §8 for how prompts use it. **Also set `dp_bundle_root`** = `<artifact_root>/{user_schema_prefix}_<use_case_slug>_dab` (the `use_case_slug` bootstrap param is available here; `{user_schema_prefix}` is the SAME username-derived prefix used for the `{user_schema_prefix}_bronze`/`_silver`/`_gold` schemas — companion app on `genie_code`, authenticated-user derivation on `ide_cli`, e.g. `jane_d`) — the self-contained Databricks Asset Bundle project directory the whole data-product pipeline (bronze→silver→gold→semantic) writes its `databricks.yml` / `src/` / `resources/` into, and the `bundle deploy` page-context root on `genie_code`. The username prefix disambiguates concurrent users in a shared workspace; the bundle `name:` inside `databricks.yml` MUST match this prefixed folder name (`bundle: { name: {user_schema_prefix}_<use_case_slug>_dab }`). It is the same shape on both clients (a dedicated subdir of the repo/clone root, NOT the bare root and NOT inside a read-only framework dir); see `skills/genie-code-environment` §8. **Also set `app_root`** = `<artifact_root>/<app_name>` — the AppKit application track's analog of `dp_bundle_root`: a TOP-LEVEL sibling of `<use_case_slug>_dab` (NOT nested under `apps_lakebase/`) that holds the scaffolded app (`app.yaml` / `databricks.yml` / `server/` / `client/` + `<app_root>/.vibecoding-state.md`) on BOTH clients, so the app's root folder has parity regardless of client. It is the `apps init --output-dir` target on `genie_code`, deploys via `apps deploy` (no `bundle` page-context pin), and is `<pending>` until `APP_NAME` is resolved (Module 1 / prompt 04); `n/a` for Pathway D. **Also set `agent_app_root`** = `<artifact_root>/<agent_app_name>` — the Track A custom-agent track's analog of `app_root`: a TOP-LEVEL sibling of `<app_name>` and `<use_case_slug>_dab` (NOT nested under `apps_lakebase/`) that holds the cloned agent framework (`app.yaml` / `pyproject.toml` / `databricks.yml` / `server/` + `<agent_app_root>/.vibecoding-state.md`) on BOTH clients, so the agent app's root folder has parity regardless of client. On `genie_code` it is the `apps init --output-dir` target and the `uv`/FastAPI server builds server-side via `apps deploy` (`mode=SNAPSHOT`) — there is no local `uv run dev` loop. It is `<pending>` until `AGENT_APP_NAME` is resolved (Track A clone / prompt 43); `n/a` for Pathways A/B. The remaining fields are client-invariant: `bundle_deploy.verb` is always `bundle deploy --target dev`, `app_deploy` is always `{ verb: "apps deploy", gated: true }`, and `destructive_ops` is always `confirm_required`. **Also seed `genie_code_manifest_loaded` (G3):** `n/a` on `ide_cli` (the check is inert there), `false` on `genie_code` — on Genie Code the first deploy/divergent prompt's `enter` halts until the `skills/genie-code-environment` manifest is read in-thread and flips it `true` (see `references/spec-schema.md` § *Genie Code manifest-load gate*). Record the detection signal that fired in a trailing `# detected_via:` comment so a later live probe can confirm or correct it. For *how* the detected `genie_code` client actually behaves — the `runDatabricksCli` allow-list tiers, the bundle-deploy CWD pin and FUSE create-then-validate gap, the App-scaffold output-dir rule, and the deployed-app OAuth session pattern — see the **`genie-code-environment`** skill (this skill *detects* the client; that skill *explains* it).
1. **Workspace URL normalization & placeholder refusal.** Trim any trailing slash from `workspace_url` and normalize the scheme to `https://`. Refuse any literal placeholder — `<your-workspace-url>`, the empty string, or `https://<...>` — by halting with a remediation hint pointing the operator at Workshop Choices. Record the normalized value in `bootstrap_preflight.workspace_url_normalized` and the placeholder check in `workspace_url_was_placeholder`.
2. **Profile selection & host match.** Run `databricks auth profiles` and select the profile whose host equals the normalized workspace host. Store it in `bootstrap_preflight.workspace_profile`. If no profile matches, set `workspace_host_auth_status: wrong_host` and halt; if the matching profile fails `databricks current-user me`, set `unauthenticated` and halt; on success set `authenticated`.
3. **CLI version preflight (client-branched — Gap-1).** **On `ide_cli`:** capture `databricks --version` into `bootstrap_preflight.databricks_cli_version`, compare against `bootstrap_preflight.databricks_cli_min_version` (default `0.295.0`, may be raised by workshop config), and halt with an upgrade hint if the installed version is older. **On `genie_code`:** `databricks --version` is **hard-blocked** (no local CLI binary — the managed `runDatabricksCli` channel does not expose a version string; CONFIRMED P1), so set `bootstrap_preflight.databricks_cli_version: unknown_on_genie_code` and **skip the numeric comparison and the version halt entirely**. Any later decision that would have keyed off the numeric version (the `databricks_cli_min_version` registry check, and `skill_helper_resolution`'s CLI-version gate) falls back to the **behavior probe** — `databricks bundle validate` accepting the field/deploy without stripping — instead of a version number. The managed channel always tracks a current CLI, so the only real risk a version gate guards against (a stale local install) cannot occur on Genie Code.
4. **Apps quota preflight.** Before any `databricks apps create` happens (Pathways A/B/C), run `databricks apps list --output json` and record `current_count`, `max_count`, and `free_slots` in `bootstrap_preflight.apps_quota`. If `free_slots < 1`, halt with a clean-up hint listing the apps the operator can delete.
5. **First state file path.** Write the bootstrap live file to `example/<use_case_slug>/.vibecoding-state.md` (used in this repo's `example/` examples) or `workshops/<use_case_slug>/.vibecoding-state.md` (used by external adopters that follow the `workshops/` convention). Whichever path is chosen, record it verbatim in `bootstrap_preflight.first_state_file_path` so `migrate_canonical` knows exactly which file to move once `$APP_NAME` is resolved. Set `bootstrap_preflight.app_name_known_at_bootstrap = false` — `$APP_NAME` is resolved later by Module 1 / Prompt 2.
6. **Copy the template.** Copy [`references/state-template.md`](references/state-template.md) to `bootstrap_preflight.first_state_file_path`.
7. Replace `<USE_CASE_SLUG>` in the title and both `<ISO timestamp>` placeholders with the current ISO timestamp.
8. Write all `Workshop Choices` from the params above.
9. Run `databricks auth profiles` + `databricks current-user me` to capture `PROFILE`, `Workspace host`, `User email` into `Global Variables`. (`PROFILE` is the same value already stored as `bootstrap_preflight.workspace_profile`.)
10. Run the serverless-only preflight (`databricks compute-policies list`) and set `workspace_serverless_only`.
11. **DAB dev-mode schema-prefix detection.** If `dabs_bundle_path` is a real path (not `n/a`), shell out to `databricks bundle validate --target dev --output json` from that path and inspect every UC schema resource. If any resource has dev-mode prefixing applied (a name shaped like `dev_${user}_<schema>` instead of the unprefixed `<schema>`), set `bootstrap_preflight.bundle_dev_mode_schema_prefix_detected = true`. Every downstream prompt that passes `agent_schema`, `ops_schema`, or `uc_schema` into a job/notebook MUST read the resolved (prefixed) schema name from state — never the unprefixed variable name.
12. **Verify-job summary semantics.** Set `bootstrap_preflight.verify_summary_semantics` from the merged workshop config. Default `pass_field`/`warn_field`/`fail_field` are `"pass"`, `"warn"`, `"fail"` (matching `05_verify_infrastructure.py`'s `notebook.exit` payload). `warning_policy` defaults to `allow_with_notes`; set to `block_if_load_bearing` for use cases whose verify-job emits warnings that must block the gate. When `warning_policy == block_if_load_bearing`, any warning whose name matches an entry in the workshop's `gate_load_bearing_checks[]` list blocks the first verify gate (PASS only when `fail == 0` AND no load-bearing warning is present).
13. **Derive `variant_id`** from `pathway` + `track` using the deterministic derivation table in [`references/resolver-prompt.md`](references/resolver-prompt.md) § *Variant-ID Derivation Table*.
14. **Invoke `resolve_spec`** (see below) to parse the PRD at `prd_path` and populate the six spec sections (`## Variant`, `## Resources`, `## UI`, `## Agent`, `## Governance`, `## Spec Provenance`). This is a hard step: if `resolve_spec` halts, `bootstrap` halts.

**Outputs:** Path of the bootstrap state file (also stored as `bootstrap_preflight.first_state_file_path`), populated with `## Environment Capabilities` (section 0), `## Bootstrap Preflight`, Workshop Choices, Global Variables (partial), and fully resolved Variant / Resources / UI / Agent / Governance / Provenance spec sections (schema v2.0). The workshop continues with this path until `migrate_canonical` runs.

**Errors:** If the bootstrap path already exists, abort — do not overwrite. The operator must remove or migrate the stale file. Any of the four halt rules in `references/spec-schema.md` § Bootstrap Preflight (steps 1–4 above) abort before the template is copied. If `resolve_spec` fails validation twice, abort and surface the validation errors; the operator must fix the PRD before re-running.

**DAB dev-mode schema-prefix rule (consumer-side).** If Databricks Asset Bundle dev mode prefixes UC schema resource names, notebook/job parameters must receive the resolved prefixed schema name, not the unprefixed variable. Bootstrap captures `bundle_dev_mode_schema_prefix_detected`; any prompt that passes `agent_schema`, `ops_schema`, or `uc_schema` to a job reads the resolved value from state.

### Operation: `resolve_root`

**When:** Any prompt that writes an artifact **before `bootstrap` has run** (e.g. the PRD-generation prompt produces `docs/design_prd.md` *before* the PRD exists to bootstrap from). It is the minimal, gate-free way to learn *where relative artifact paths must land* so a bare `docs/…` does not resolve against a page-dependent CWD on Genie Code. Idempotent — safe to call repeatedly; a later `bootstrap` supersedes it as the persistence owner.

**Inputs:** none required. (Optional `state_file_path` — if the caller already knows where a state file lives.)

**Behavior:**

1. **Read-if-present.** If a state file with a `## Environment Capabilities` block (section 0) already exists (the live file, or the bootstrap path `example/<use_case_slug>/.vibecoding-state.md` / `workshops/<use_case_slug>/.vibecoding-state.md`), read `client_context`, `artifact_root`, `skills_install_root`, and `skill_ref_root` from it and return them. Do **not** re-detect or rewrite.
2. **Else detect fresh (bootstrap step-0 detection ONLY).** Run just the client/root detection from `bootstrap` step 0 — no URL/profile/CLI/quota preflights, no template copy, no `resolve_spec`, no gates: if a Databricks-managed CLI channel is present (`runDatabricksCli` / Genie serverless markers) ⇒ `client_context: genie_code`, `artifact_root` = the **user project workspace path** (e.g. `/Workspace/Users/<email>/<repo>`, a **git clone** of the workshop repo), `skills_install_root` = the **skills copy path** (e.g. `/Workspace/Users/<email>/.assistant/skills/<repo>`, a copy for discovery), `skill_ref_root` = `"skills/" + basename(skills_install_root)` (e.g. `skills/vibe-coding-workshop`); otherwise ⇒ `client_context: ide_cli`, `artifact_root` = `skills_install_root` = the local repo root, `skill_ref_root` = empty.
3. **Ensure `artifact_root` is a git-backed working tree.** The kickstart `git clone`s the workshop repo into `artifact_root`; if `<artifact_root>/.git` is missing, `git clone <workshop remote>` into it so the caller's first relative write (e.g. the PRD at `<ARTIFACT_ROOT>/docs/design_prd.md`, produced *before* `bootstrap`) lands in a real, git-backed directory where generated bundles are recognized (a bare `mkdir` leaves them unrecognized — TESTED; a Repos-managed Git folder is the documented fallback). On `genie_code` operate via the workspace path / `executeCode`, mindful of the FUSE create-then-validate gap (clone, then confirm with `os.path.exists(<artifact_root>/.git)` before writing). This clone/check is the **only** filesystem side effect of `resolve_root`.
4. **Echo, do not persist.** Return `client_context` + `artifact_root` + `skills_install_root` + `skill_ref_root` for the caller to use immediately and to echo the project-root + skill-load rules to the operator. **No state file is written** — `bootstrap` step 0 remains the sole writer of section 0, so there is no "abort if path exists" conflict and no half-initialized state file.

**Outputs:** `{ client_context, artifact_root, skills_install_root, skill_ref_root }` (ephemeral). The calling prompt resolves its relative artifact path as `<ARTIFACT_ROOT>/<relpath>`, loads any repo-relative skill path `X/Y/SKILL.md` as `<skill_ref_root>/X/Y/SKILL.md` via `readSkillFile` (§8), and echoes: "artifacts resolve under the workshop project root (`artifact_root`, the git-cloned user project on Genie Code — not the skills copy) and skills load under `skill_ref_root` (= `skills/<clone-folder>` = `skills/` + basename of `skills_install_root` on Genie Code), never the page CWD."

**Errors:** None that halt — `resolve_root` is fail-soft. If detection is ambiguous, default to `ide_cli` / repo-root and note the assumption; the later `bootstrap` step 0 is authoritative.

### Operation: `resolve_spec`

**When:** Called automatically as the final step of `bootstrap`. May also be re-invoked manually if the PRD changes mid-workshop (rare; prefer restarting from bootstrap).

**Inputs:**

| Param | Type | Required | Description |
|---|---|---|---|
| `prd_path` | string | required | Path to the PRD. Read from `Workshop Choices.prd_path` if omitted. |
| `llm_endpoint` | string | required | LLM endpoint used to synthesize the spec. Read from `Workshop Choices.llm_endpoint` if omitted. Must be a Databricks Model Serving endpoint reachable from the workspace. |

**Behavior:**

1. Read the PRD contents from `prd_path` and compute `prd_sha256`.
2. Load the schema from [`references/spec-schema.md`](references/spec-schema.md) and the prompt from [`references/resolver-prompt.md`](references/resolver-prompt.md).
3. Derive `variant_id` deterministically from Workshop Choices (`pathway`, `track`) per the resolver prompt's *Variant-ID Derivation Table*. Inject it into the user prompt.
4. Call `llm_endpoint` with the system + user prompts. Expect a single YAML document with exactly six top-level keys: `variant_id`, `resources`, `ui`, `agent`, `governance`, `spec_provenance`.
5. Run the deterministic guards listed in the resolver prompt's "Post-resolution Guards" section:
   - YAML parses cleanly into a dict with the expected top-level keys.
   - Every validation rule in [`references/spec-schema.md`](references/spec-schema.md) §"Validation Rules" passes.
   - Consumer cross-check: each `agent.tools[].name` of `kind: function` with `language: python` reconciles with the PRD Tools Table.
   - Placeholder guard: no resource-ID-shaped strings leaked through.
   - Variant-ID echo check: emitted `variant_id` equals the one passed in.
   - MCP ref resolution: every `agent.tools[].mcp_server_ref` (kind=mcp) maps to an `agent.mcp_servers[].name`.
6. If any guard fails, retry **once** with the validation errors appended to the user prompt. Second failure halts bootstrap.
7. Split the validated YAML by top-level key and write each block into its matching state-file section (`## Variant`, `## Resources`, `## UI`, `## Agent`, `## Governance`, `## Spec Provenance`), replacing the `<pending>` placeholder body with a fenced ```yaml block containing the resolved content.
8. Stamp `## Spec Provenance` with `resolved_at`, `resolver_version: "2.0"`, `schema_version: "2.0"`, `prd_sha256`, and `llm_endpoint`.

**Outputs:** No return value; mutates the live state file.

**Errors:** Validation failure (after one retry), missing PRD file, unreachable `llm_endpoint`, or any LLM response that is not parseable YAML with the six expected keys — all halt bootstrap.

**Downstream contract:** Every consuming skill declares which fields it reads via a machine-parseable `fields_read:` YAML list in its frontmatter (see § *Field Consumer Contract* below and [`references/spec-schema.md`](references/spec-schema.md) §"Field Consumer Contract"). Prompts in `Instructions.md` and `WALKTHROUGH.md` reference resolved content by dotted path (e.g. `ui.user_journeys`, `agent.tools`, `governance.scorer_suite.guidelines`) instead of inlining domain-specific content. This is what makes the workshops use-case-agnostic.

### Operation: `hydrate_from_files`

**When:** Called once per workshop, before any prompt that reads `state://AgentSpec`, `state://AppSpec`, or `state://DataSpec`. The Agents Accelerator visible path calls it from prompt `uc_resources_foundation` (input_id 200, order 40), right after `op enter` succeeds and before any UC schema/volume creation.

**Purpose:** The Agents Accelerator design pair (`docs/agent_spec.yaml` from prompt 38, `docs/agent_tool_plan.yaml` from prompt 39) becomes the source of truth for the agent's intent. `hydrate_from_files` lifts those file values into the live state file so every downstream consumer (Track A build prompts, MLflow SDLC suite at prompts 50-56) keeps reading from `state://AgentSpec`, `state://AppSpec`, and `state://Spec Provenance` without an additional visible-path step. This is additive to `resolve_spec`: PRD-only LLM-driven workflows that never produce the `docs/*.yaml` files continue to work unchanged.

**Inputs:**

| Param | Type | Required | Description |
|---|---|---|---|
| `agent_spec_yaml`      | string | required | Path to the `docs/agent_spec.yaml` produced by prompt 38 (`agent_spec_design`). |
| `agent_tool_plan_yaml` | string | required | Path to the `docs/agent_tool_plan.yaml` produced by prompt 39 (`agent_tool_selection`). |
| `ui_design_md`         | string | required | Path to the `docs/ui_design.md` produced by prompt 04 (`cursor_copilot_ui_design`). |
| `prd_path`             | string | required | Path to `docs/design_prd.md` from prompt 03 (`prd_generation`); used for `source_prd` provenance. |
| `state_path`           | string | required | Path to the live state file (Pathways A/B/C: `apps_lakebase/$APP_NAME/.vibecoding-state.md`; Pathway D: `agents/$AGENT_NAME/.vibecoding-state.md`). |

**Behavior:**

1. **Read `agent_spec_yaml`.** Copy scalar/list `agent.*` fields directly into the state file's `## Agent` section as a fenced ```yaml``` block: `model`, `capabilities`, `personas`, `system_prompt`, `benchmark_seeds`, `must_do`, `must_not_do`. File values override any prior `resolve_spec` LLM output silently; the file is the source of truth.

   **Tool projection rule (preserves the v2.0 `fields_read: agent.tools` contract without rewriting any consumer).** The Agent Spec produced by step 38 follows the 00b schema (`tool_recommendations`, NOT `agent.tools`). Hydration MUST project tools into `state://AgentSpec.agent.tools[]` using these three sub-rules:

   a. Seed `state://AgentSpec.agent.tools[]` from `docs/agent_spec.yaml.tool_recommendations.managed_databricks[]` plus `tool_recommendations.external[]`. Map each entry to the v2.0 tool discriminated-union shape: populate `kind` (`hosted` | `function` | `mcp`), `name`, `surface`, `io_contract`, `readonly`, plus the kind-specific fields (`hosted_type` + `resource_ref`, or `language`, or `mcp_server_ref`). Carry `selected_by_default` forward to pre-mark entries.
   b. Overlay `docs/agent_tool_plan.yaml.selected_tools[]` on top. Any Tool Plan entry with the same `name` REPLACES the spec recommendation — binding selection wins over loose recommendation. Tool Plan entries with no matching spec recommendation are appended.
   c. Tool families absent from BOTH the spec and the Tool Plan are NOT written into `agent.tools[]`. Skipped families are recorded as skipped, not failed (consistent with step 44 semantics).

   The projection is the only legitimate way `state://AgentSpec.agent.tools[]` becomes populated on the Agents Accelerator visible path. Step 38 does NOT and MUST NOT write `agent.tools[]` directly into `docs/agent_spec.yaml`; downstream prompts that still cite `agent.tools[]` (e.g. step 44's prerequisite block before Pass 3.5) MUST be updated to read `tool_recommendations` (loose) plus `selected_tools` (binding) instead.
2. **Read `agent_tool_plan_yaml`.** Merge `selected_tools[]`, `selected_mcp_servers[]`, `runtime_config.llm`, and `resource_grants` into `## Agent` under new keys `selected_tools`, `selected_mcp_servers`, `runtime_config`, and `resource_grants`. Tools selected by the user in the Tool Plan win over the Agent Spec's recommendations.
3. **Read `ui_design_md`.** Parse loose markdown headings into `## UI` (`pages[]`, `personas[]`, `user_journeys[]`) on a best-effort basis. If the document is structured differently or only contains free-form prose, write the verbatim markdown into `## UI.raw_markdown` and emit a non-fatal warning so downstream SDLC prompts can still find personas/journeys textually.
4. **Stamp `## Spec Provenance`.** Compute `prd_sha256` from `prd_path` and write `resolved_at` (current UTC ISO timestamp), `resolver_version: "3.0"` (new tag distinguishing file-based hydration from LLM `resolve_spec`'s `"2.0"`), `schema_version: "2.0"`, `prd_sha256`, and `hydrated_from_files: true`.
5. **Optional `## Resources` (DataSpec).** If the PRD declares a Lakehouse/Resources section (Bronze tables, Genie Spaces, Vector Search indexes, etc.) and `## Resources` is already populated by an earlier `resolve_spec` run, leave it untouched. Otherwise write `## Resources` as `optional: true` with `tables: []`, `mark_skipped: "no Lakehouse track"`, signalling downstream prompts that `state://DataSpec.*` lookups should fall back gracefully (e.g. KA branch C in prompt 42 reads from `docs/design_prd.md` + `docs/agent_spec.yaml.agent.capabilities` instead of `state://DataSpec.glossary`).
6. **Idempotency.** Re-running with the same inputs is a no-op: each section is regenerated from the file values, sha256 is stable, and the state file ends in the same byte sequence (modulo the `resolved_at` timestamp, which is allowed to drift). Re-running with newer `docs/*.yaml` files overwrites cleanly.

**Outputs:** `{ hydrated: true, sections_written: ["## Agent", "## UI", "## Resources", "## Spec Provenance"] }`. The Agents Accelerator step 40 records `hydrated_from_files: true` and `resolver_version: "3.0"` in its `op exit` `captured` map.

**Errors:**

- Missing required input file → halt with an explicit pointer to the producing prompt: `agent_spec_yaml` → 38, `agent_tool_plan_yaml` → 39, `ui_design_md` → 04, `prd_path` → 03, `state_path` → bootstrap.
- `agent_spec.yaml` missing `agent.model` → halt with a pointer to prompt 38's model selection rule (Pass 1 / Pass 2 of the Agents Accelerator cleanup).
- `agent_tool_plan.yaml.runtime_config.llm.endpoint` equals the literal YAML-path string `docs/agent_spec.yaml.agent.model` → halt with the Pass 2 placeholder rule citation. This is defense-in-depth; prompt 39's generated prompt already forbids this.
- Any value at a documented Agent Spec / Tool Plan path is still wrapped in `{...}` (e.g. `{agent_sql_catalog}`) → halt with the Pass 2 placeholder-handling rule. The user must rerun prompt 39 with real values.

**Downstream contract:** After `hydrate_from_files` runs, every prompt that consumes `state://AgentSpec.agent.*`, `state://AppSpec.ui.*`, or `state://Spec Provenance.*` sees the file-derived values. `state://DataSpec.*` is `<pending>` or `optional: true` unless the Lakehouse track produced one. SDLC prompts (50-56) MUST handle the optional/pending DataSpec case gracefully — they already do (the Agents Accelerator visible path no longer requires Lakehouse outputs), but the `optional: true` flag makes the contract explicit.

**LLM driver prompt:** See [`references/hydrator-prompt.md`](references/hydrator-prompt.md) for the actual prompt that an LLM-driven implementation runs to execute this operation. That file is to `hydrate_from_files` what [`references/resolver-prompt.md`](references/resolver-prompt.md) is to `resolve_spec`.

### Operation: `enter`

**When:** At the **top** of every workshop prompt, before any domain skill or command runs.

**Inputs:**

| Param | Type | Required | Description |
|---|---|---|---|
| `prompt_id` | string | required | e.g. `0.1`, `3.2`, `5.A4`, `7.22.5`, or `20c`. Matches the heading the prompt will log under. |
| `require_prior_gate` | `{prompt_id: string, gate: string}` | optional | Assert the listed prompt's Gate equals the listed string. If it does not match, `enter` stops the workshop and surfaces the mismatch. |

**Behavior:**

1. **Locate the live state file:**
   - **First, resolve `state_file_root`** from the `## Environment Capabilities` block (section 0). Every path below is relative to it — the local repo root for `client_context: ide_cli`, the git-folder workspace path for `genie_code`. On a pre-capability state file (no section 0), default `state_file_root` to the repo root and proceed without assuming a deploy channel.
   - If `$APP_NAME` is known (Pathways A/B/C), use `<app_root>/.vibecoding-state.md` (= `<state_file_root>/<app_name>/.vibecoding-state.md`, the TOP-LEVEL app dir — NOT `apps_lakebase/<app_name>/`).
   - Else if `$AGENT_APP_NAME` is known (Track A agent app, Pathways C/D), use `<agent_app_root>/.vibecoding-state.md` (= `<state_file_root>/<agent_app_name>/.vibecoding-state.md`, the TOP-LEVEL agent app dir — NOT `apps_lakebase/<agent_app_name>/`).
   - **Else if `dp_bundle_root` is resolved (a data-product / lakehouse run with no app or agent app — e.g. the Gold-design→Bronze→Silver→Gold→semantic pipeline), use `<dp_bundle_root>/.vibecoding-state.md`** (= `<state_file_root>/{user_schema_prefix}_<use_case_slug>_dab/.vibecoding-state.md`). This is the **canonical DP-track live file** — the data-product analog of `<app_root>` / `<agent_app_root>`. The FIRST data-product prompt to run (Gold design, step 09 — or Bronze, step 10, if design was skipped) **bootstrap-creates** it from [`references/state-template.md`](references/state-template.md) if absent (a real create, copying the template + filling Workshop Choices from the prior `example/` bootstrap file if present — not just `resolve_root`); every later DP step appends to it. Do NOT leave data-product state in the temporary `example/` bootstrap path — that path carries no durable record and was the root cause of "state survived only in chat summary."
   - Else fall back to the bootstrap path `<state_file_root>/example/<use_case_slug>/.vibecoding-state.md` (pre-bootstrap only — once `dp_bundle_root`/`app_root`/`agent_app_root` is known, migrate/bootstrap-create the canonical file above).
   - If none of these exists yet: for the **`dp_bundle_root`/`app_root`/`agent_app_root` canonical paths, bootstrap-create the file from the template** (this is the first prompt of that track); only stop and tell the operator to run `bootstrap` first if even `use_case_slug`/Workshop Choices are unknown.
2. **Schema v2.0 gate (hard fail).** Parse the `## Spec Provenance` YAML block. If `schema_version != "2.0"`, halt with:

   ```
   State file uses schema v<X> — this repo is strictly v2.0.
   Run: python scripts/migrate-spec-v1-to-v2.py <path-to-state-file>
   Then re-run this prompt.
   ```

   No best-effort reads against older shapes.
3. **Read end-to-end.** Treat `Workshop Choices`, `Global Variables`, and `Captured Resource IDs` as authoritative. **Never ask the operator for a value that already exists in state.**
4. **Multi-file resolution via `state_file_set`.** Parse the `## State File Set` block.
   - If `state_file_set.secondary.path` is `<pending>` or null, treat the located file as the only source — done.
   - If `secondary` is declared and `prompt_id` is in `secondary.required_for_prompts`, read the secondary file too. Halt if it does not exist (remediation: run `bootstrap` for the missing side or correct `secondary.path`).
   - Walk both files following `lookup_order` (e.g. `["primary", "secondary"]`). For each canonical field requested by the prompt, the first hit wins.
   - For any canonical field present in **both** files with different values: halt unless `conflict_policy: primary_wins` is set explicitly. With `primary_wins`, use the primary value and append the conflict to `## State Contract Audit` (`fields_read_but_never_produced` / `captured_fields_with_no_consumer` are unaffected; conflicts get their own audit row).
5. **Apply `canonical_names` aliasing on read.** Parse the `## Canonical Names` block. Whenever the prompt reads an env var, HTTP header, jq path, or state field listed as a legacy/incorrect spelling on the left of a `canonical_names` map, resolve to the canonical name on the right. If the map's value is `null` (e.g. `http_headers.x-forwarded-user-info: null`), the name does not exist; halt and surface a hint pointing at the canonical alternatives. `state_contract_audit` performs the same checks at audit time.
6. **Enforce the prior gate** if `require_prior_gate` is set. On mismatch, halt unless a matching entry exists in `state_overrides[]` (see step 7). Mismatches that are NOT covered by an override stop the workshop.
7. **Apply `state_overrides[]` (fail-closed escape hatch).** Parse `## State Overrides`. An override entry covers a failing gate when ALL of the following hold: `prompt_id` matches the current prompt; `gate_type` matches the failing gate (`require_prior_gate` for step 6, `hard_assert` / `preflight_check` for step 9 below, `pathway_applicability` for step 8); and `expires_at` is in the future relative to the current ISO timestamp. A covered failure is treated as satisfied and the override is logged in the prompt's `Resolved issues / workarounds` Per-Step Log entry. Expired or missing entries DO NOT cover the failure — `enter` halts. Cross-session continuity (e.g. resuming from a previous run's state file) is recorded as a `state_override` whose `reason` cites the earlier run's evidence; it is NOT a prose exception.
8. **Consult the Pathway Applicability Matrix** (below). If the prompt does not apply for this `pathway`/`track` and no `state_override` with `gate_type: pathway_applicability` covers it, synthesize a `## Prompt <prompt_id> — Skipped (N/A for pathway <X>)` section with `Gate: Skipped — N/A for pathway <X>`, append it, and return a `skipped: true` signal so the prompt body short-circuits. With a covering override, proceed (the override `reason` is logged in the prompt's Per-Step Log entry).
9. **Enforce `deferred_actions[]`.** Parse `## Deferred Actions`. Halt if the current prompt's role is in any open `deferred_action`'s `target_prompt_roles[]` and its `divergence_check` evaluates to false (or fails to parse under the divergence-check grammar in `references/spec-schema.md` § *Deferred Actions*). Halt is suppressed only when a `state_override` with `gate_type: hard_assert` (or `preflight_check`) on the current `prompt_id` exists AND its `references[]` contains `"deferred_action:<id>"` matching the action's `id`. If `divergence_check` returns true, flip `status` to `resolved` and proceed. Entries with `status: waived` are skipped without evaluation.
10. **Enforce `mlflow_eval_known_quality_issues[]`.** Parse `## MLflow Eval Known Quality Issues`. If the current prompt's role appears in any open issue's `target_prompt_roles_blocked[]`, halt with a remediation hint citing `id`, `source_prompt_role`, and `error_signature` — unless a `state_override` exists on the current `prompt_id` whose `references[]` contains `"known_issue:<id>"` matching the issue's `id`. The issue's `error_signature` SHOULD be a name from `gate_load_bearing_checks[]` so audits and verify summaries align, but `enter` halts purely on `target_prompt_roles_blocked[]` membership and `status: open` — it does NOT consult `gate_load_bearing_checks[]` directly (that list drives `state_contract_audit` and the verify-job `warning_policy: block_if_load_bearing` rule per § *Gate Load Bearing Checks* in `references/spec-schema.md`).
11. **Enforce `preflight_check_registry`.** Parse `## Preflight Check Registry`. For each registry entry, if the current prompt's role appears in `blocks_prompt_roles[]`, evaluate the matching state field per the *State-field mapping* table in `references/spec-schema.md` § *Preflight Check Registry*. If the pass condition is not met (the field is `<pending>`, missing, or fails the predicate), halt with a remediation hint pointing at `owner`. The halt is suppressed only when a matching `state_override` with `gate_type: preflight_check` exists on the current `prompt_id` whose `affected_state_field` matches the check's state field. For `reflection_lm_large_context_probe`, the check is treated as passing only when a synchronous ≥80000-character probe has been run against the currently bound `llm_role_endpoints.reflection_lm.endpoint` and `endpoint_guardrail_audit[<reflection_lm.endpoint>].long_context_ok == true` AND `accepted_min_context_chars >= 80000` — the generic bootstrap endpoint guardrail pass is NOT sufficient on its own. The owning skill (`instruction_iteration`) MUST invoke the probe synchronously before any 08b diff-summary helper or iteration helper call. For `genie_code_manifest_loaded` (G3, the one **client-conditional** check), evaluate it **only when `environment_capabilities.client_context == 'genie_code'`** — it is inert on `ide_cli` — and on Genie Code halt the deploy roles until `environment_capabilities.genie_code_manifest_loaded == true` (the agent has read `skills/genie-code-environment` in-thread); see `references/spec-schema.md` § *Genie Code manifest-load gate*.
12. **Resolve variables.** Return the subset of state the prompt body needs (it is declared in the prompt's `enter` invocation — e.g. `APP_NAME`, `PROFILE`, `warehouse_id`). **Always also return the environment-capability set** so any deploy/run instruction the prompt emits uses the resolved verb + channel instead of assuming a local IDE: `bundle_deploy.verb` (always `bund

…(truncated)
