Turn the user's intent into a minimal pilot.yaml, validate it, and render its
inputs. The result is a credential-free, version-1 manifest plus a rendered
.cookbook/generated.tfvars.json. The coordinator (cookbookctl) validates and
renders locally; it makes no live changes to any BTP account.
Start or resume
- Work from the Cookbook repository root. Read
pilot.yaml.example,docs/cookbookctl.md, andREADME.mdbefore editing state. - Run
./cookbookctl pilots --json(read-only, works without a manifest) to see the active pilot's stage progress and any parked pilots. Never print secrets or full environment files. - Open with the question that summary answers: use the existing pilot state,
or start fresh?
- If only parked pilots exist, offer to restore one with
./cookbookctl unpark <dir>or start fresh. - If an active pilot exists, summarize its architecture and completed stages, then ask whether to resume, revise, or park it.
- Resume preserves the confirmed architecture. Do not re-ask answered questions.
- Revise preserves only the choices the user explicitly says to keep. Re-interview every dimension they want to change.
- Park is the recommended path when the user wants a new pilot:
./cookbookctl parkmoves the manifest and all local workflow state to.cookbook/parked/<subdomain>-<stamp>/without contacting BTP, and the next interview starts clean../cookbookctl unpark <dir>restores it later. - To start a fresh pilot in place of an existing one, park the current manifest and run the interview below from a blank decision set. Never carry the old agent specification or its architecture into the new one unless the user explicitly selects each reused choice.
- If only parked pilots exist, offer to restore one with
- Otherwise ask for one starting mode:
- Quick local prototype: follow
recipes/01-scaffold-agent/README.md; BTP is not required yet. - Full setup: run the interview below, write
pilot.yaml, validate, and render. - Resume: locate existing local state and report what is missing.
- Quick local prototype: follow
Do not write pilot.yaml until the user has confirmed the resulting summary.
Interview for a full setup
Use progressive disclosure. Ask a small group of related questions, record the answers, and only ask follow-ups enabled by those answers. Material choices require an explicit answer. "No preference" is a valid answer; recommend the first listed option and record it only after the user accepts it.
Prefer structured choices with concrete suggestions over free-text prompts throughout the interview: every question should offer selectable options (with a recommended default first) plus an escape hatch to type something else. Reserve plain free text for genuinely open content that no suggestion can cover.
1. Outcome
- Do not open with free-text questions. Present the starter scenarios in
templates/as one multiple-choice question — structured options where the harness supports them (Claude Code: AskUserQuestion), a numbered list otherwise. One option per template, labeled with itsscenario.titleand one-linescenario.summary, plus a final Custom agent option for an outcome described from scratch. - When a scenario is selected, copy its
agentblock as the starting decisions: show the derived kebab-case name, purpose, audience, mock tools, and skills, and ask the user to confirm or adjust them (the name and the eventual data system are the two most common edits;scenario.eventual_systemis a suggestion, not a requirement). - Only for Custom agent, ask the open questions in plain text: what the agent does, who uses it, and which business system or API eventually supplies real data — then derive and confirm a kebab-case name and a comparable tool/skill set.
- Default to mock tools for the first conversation unless the user explicitly wants a live backend now. Never request backend secrets in chat.
2. Account
- Probe the session before asking anything: run
./cookbookctl accounts --jsonfirst. It is read-only, needs no manifest, and doubles as the login check — when no BTP CLI session exists it exits 1 with that finding; when one exists it reports the session's global account and every visible subaccount. When a manifest exists, it also assesses that manifest's own subdomain asavailable(free to create) oralready-exists(the subdomain is taken). - Only when the probe reports no session, ask the user to authenticate
themselves with
btp login --sso, then rerun the discovery. Never ask an already-logged-in user to log in, and never accept passwords, tokens, service keys, certificates, or API keys in chat orpilot.yaml. - The visible subaccount list is raw BTP inventory, not a list of pilots this
workspace owns. Resume applies only to workspace-managed pilots surfaced by
./cookbookctl pilots --json(see Start or resume). Never present an existing BTP subaccount as a pilot to resume — the account list proves only that a subdomain exists, not that this workspace created or manages it. - When a session exists, open with one targeting chooser built from the
discovery output — "You are targeting (subdomain
<x>). Create a new subaccount, or resume a workspace-managed pilot?" — with structured options:- Create a new subaccount (recommended default): collect display name, subdomain, BTP region, administrators, and production relevance; propose a subdomain that does not collide with any visible subaccount.
- Resume a workspace-managed pilot — offer this only for the active or
parked pilots reported by
pilots --json, never for a bare subaccount row. - If the targeted global account is wrong, ask the user to rerun
btp login --sso, select the intended account during login, and rerun the discovery; a CLI session is bound to one global account at a time. Prefillaccount.global_account_subdomainfrom the session.
- Treat every visible subaccount as a collision to avoid: when a chosen new
subdomain matches one — the probe reports
already-existsfor the manifest subdomain — report it and pick a non-colliding subdomain. Do not reuse or adopt an existing subaccount; the public flow has no adoption path.
3. Runtime
- Cloud Foundry: recommend for the simplest application deployment. Ask for
runtime memory, at least one space, and its developers. Leave
api_url,bootstrap_user, anduser_originunset unless the user has independently verified them; do not guess a shard-specific CF API URL or carry a bootstrap identity from another subaccount. - Kyma: use for Kubernetes, containers, or cluster-level controls. Ask for
requiredversusif_available, hyperscaler plan, cluster region, machine type, autoscaler bounds, and later the expected kube context. - Never enable both runtimes.
if_availablepauses when quota is absent; it does not silently fall back to Cloud Foundry. Ask explicitly before changing the selected runtime.
4. Task state and application stack
- In memory: recommend for MVPs. Explain that state is lost on restart and not shared across replicas.
- HANA: use when tasks must survive restarts or scale-out. Explain that it
requires TypeScript + CAP and adds the
hana / hdi-sharedentitlement. Ask the user to confirm that constrained stack; do not offer Python or Express with HANA. - With in-memory state, explicitly ask: Which application stack does your team
prefer?
- TypeScript + Express — recommended for the smallest pilot.
- TypeScript + CAP — for CAP teams or an MTA-oriented application shape.
- Python — the Python A2A SDK / Uvicorn shape; do not call it Express.
- No preference — record TypeScript + Express only after the user accepts the recommendation.
- Never infer the stack from an existing manifest, installed local runtime, example agent, or "use a sensible placeholder" answer.
5. Model
- SAP AI Core: add
aicore / extended; verify regional availability and remaining entitlement before selecting it. Run the region preflight (scripts/region-preflight/region-preflight.sh <region>) for sovereign or unfamiliar regions first. - OpenAI-compatible: omit AI Core. Collect only non-secret endpoint metadata; credentials remain environment variables or runtime secrets.
- If the target region does not expose AI Core, require an approved OpenAI-compatible endpoint. Do not select an unavailable provider.
6. Joule
- Ask whether Joule integration is required, and capture the answer as a manifest choice.
- If no, omit Joule, Destination, and XSUAA.
- If yes, verify regional availability; collect developer users or groups,
identity-provider origin, tenant type, and whether Process Automation is
included. Enabling Joule automatically adds
destination / liteandxsuaa / application. Treat Destination Lite and XSUAA as required dependencies, not automatically as paid services.
7. Confirm
Show one compact summary before writing anything:
Subaccount: <new name> (<region>)
Runtime: <Cloud Foundry memory | Kyma plan/cluster region>
Application: <language + framework>
Task store: <memory | HANA>
LLM: <AI Core | OpenAI-compatible>
Joule: <disabled | enabled>
Data: <mock | named system/API>
Mark every material choice as user-selected, required by another choice, or
recommended default accepted. Ask for confirmation of this architecture.
Confirmation is permission to write pilot.yaml and run local validation and
rendering — nothing else.
Materialize the decisions
- Copy the structure of
pilot.yaml.exampleinto the gitignoredpilot.yamland remove examples not selected by the user. - Populate
agent.specwith at least one realistic tool and one skill. Use abackend: mockmarker until live connectivity is explicitly selected. - Keep
services.entitlementsfor user-added services only.cookbookctlautomatically closes these dependencies:- AI Core provider ->
aicore / extended - HANA task store ->
hana / hdi-shared - Joule ->
destination / liteandxsuaa / application - CF memory, Kyma, and Joule use their dedicated manifest sections.
- AI Core provider ->
- Run
./cookbookctl validatefor a state-free schema and dependency check. Then run./cookbookctl renderto materialize and inspect.cookbook/generated.tfvars.json. - Run the region preflight for sovereign or unfamiliar regions before selecting AI Core or Joule.
Validate, render, and resume
./cookbookctl validateis state-free: it checks the manifest's schema and dependency closure without contacting BTP. Run it after every edit../cookbookctl renderwrites.cookbook/generated.tfvars.jsonfrom the validated manifest. It contains no secrets and noagent.specfields.- Use
./cookbookctl statusand./cookbookctl pilotsto confirm the current pilot's stage or to resume one later. - Editing
pilot.yamlchanges its content hash, so re-runrenderafter any change. - Use
./cookbookctl parkand./cookbookctl unpark <dir>to switch between pilots without losing local state.