Skip to content

feat(scenarios): create Python-script scenarios and edit their script - #214

Open
crmapj wants to merge 2 commits into
release/0.9.0from
feat/python-script-scenarios
Open

crmapj wants to merge 2 commits into
release/0.9.0from
feat/python-script-scenarios

Conversation

@crmapj

@crmapj crmapj commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Prompted by user feedback.

Adds Python-script scenarios (scenario type custom_python) to Headless. Cobuild creates only step-based scenarios and cannot edit a scenario's script, so there was no supported path. This PR adds two narrow Design-only direct writes and exposes the script in the existing inspection tool.

create_python_scenario(project_key, name, script)
  name already used in the project -> refuse with that scenario's id; nothing created
  create_scenario(name, "custom_python", {params: {}, active: false, triggers: [], reporters: []})
    Dataiku refused (DataikuException) -> raise; nothing was created
    no answer (transport error)        -> raise "check list_scenarios before retrying"
  settings.code = script; settings.save()      # definition save first, then script
  re-read: type == custom_python and code == script
    yes -> status python_scenario_created
    no / error -> status python_scenario_script_unconfirmed + scenario_id + hint
                  ("may still hold Dataiku's sample script; do not run it;
                    retry set_python_scenario_script; do not create again")

set_python_scenario_script(project_key, scenario_id, script)
  type != custom_python -> refuse, nothing saved
  settings.code = script; settings.save()
  re-read code != script -> raise ("repeating this call is safe")

get_scenario_settings
+ adds "script" for custom_python scenarios (the SDK already fetched it); step-based output unchanged

Dataiku accepts duplicate scenario names, so the name check keeps a retried call from leaving a second scenario that holds the sample script.

Why settings.save() and not the script endpoint alone: the definition save runs Dataiku's code-permission and license checks for Python scenarios, and the SDK performs it before the script write. A caller Dataiku refuses therefore never reaches the script.

 dataiku_mcp/tools/scenarios.py      + create_python_scenario, set_python_scenario_script (PROJECT_EDIT_POLICY)
                                     ~ get_scenario_settings returns the script of a Python-script scenario
 CODING_STANDARDS_AND_STRUCTURE.md   + fixed exception in the Cobuild write-routing convention
 docs/capabilities.md                + inventory, Automation exclusions, 133 tools
 skills/dataiku-headless/
   SKILL.md                          + routing for Python-script scenarios; Automation exclusion
   references/scenarios.md           + scenario types; "Python-Script Scenarios" workflow,
                                       recovery from an unconfirmed script, safety rules
 tests/test_python_scenarios.py      + 18 focused tests
 tests/test_tool_surface.py, tests/test_instance_policy.py  + pinned surface and Design-only policy

Out of scope: the scenario's Python code environment (the scenario inherits the default), and triggers, reporters, and activation, which stay with Cobuild as for any scenario.

Evidence

Before: Cobuild created only step-based scenarios (at most with a custom_python step), and no tool could read or write a scenario's script.

After, automated: tests/test_python_scenarios.py (fakes mirror the SDK's definition-then-script save order; no live instance)

create                       -> create_scenario(name, "custom_python", inactive, no triggers/reporters); status created
name already used            -> ValueError naming the existing id; create_scenario never called
Dataiku refuses create       -> DataikuException propagates; nothing created
transport error on create    -> RuntimeError telling the caller to check list_scenarios first
definition or script save fails after create -> python_scenario_script_unconfirmed + scenario_id; sample script left; still inactive
read-back differs            -> python_scenario_script_unconfirmed, never "created"
set script                   -> only the script changes; name/tags/triggers/reporters/active/params identical
step-based target            -> ValueError, nothing saved
Dataiku refuses script save  -> error propagates; old script kept
read-back differs on edit    -> RuntimeError
blank name/script/id         -> ValueError before any SDK call
get_scenario_settings        -> adds script for custom_python only

Local gates on this head: uv sync --locked, uv run pre-commit run --all-files (all hooks pass), uv run pytest (852 passed), uv build plus wheel import smoke on Python 3.12, cz check on the branch.

After, live: run against a personal Design node on DSS 15.0.0 through the real stdio launcher (uv run --locked --script runtime/run_mcp.py --transport stdio), calling the registered tools over MCP in a synthetic scratch project. The script only wrote a marker into that project's own variables and printed it. The test used no reporters, email, inference, or external calls.

create with a name in use      -> refused, names the existing scenario id; scenario list unchanged
create_python_scenario(v1)     -> python_scenario_created; independent SDK read: type custom_python,
                                  active false, triggers [], reporters [], no steps, script == v1
list_scenarios / get_scenario_settings -> type custom_python; script == v1
set script on a step-based scenario     -> refused; that scenario unchanged
blank script                   -> refused; script still v1
set_python_scenario_script(v2) -> script == v2; definition identical apart from Dataiku's version tag
run_scenario(wait)             -> scenario_run_completed, outcome SUCCESS (manual trigger)
synthetic result               -> project variable marker == "v2"; run log contains the v2 marker line
final state                    -> still inactive, no triggers, script == v2

Merge Danger

Door: two-way

The tools only add capabilities. Reverting removes them; scenarios created through them remain ordinary Python-script scenarios that the UI can edit or delete.

Blast Radius: moderate

A Python-script scenario runs arbitrary Python as its run-as user when someone runs it. Creation never runs it: scenarios start inactive without triggers, and Dataiku still requires code-writing permission and a license for Python scenarios. A script save rewrites the scenario definition just read, so a concurrent edit to that scenario is lost (documented in the skill). get_scenario_settings responses for Python-script scenarios grow by the script. The catalog grows by two tools (133).

Shared files with #213 (recipe code environment): both PRs edit the tool total in docs/capabilities.md, the Automation exclusion lists, the routing convention, and the pinned tool tests. The diffs are independent, but whichever merges second needs a small textual rebase (134 tools in total).

🤖 Generated with Claude Code

crmapj and others added 2 commits November 9, 2026 14:38
Add create_python_scenario and set_python_scenario_script, narrow Design-only
direct writes for custom_python scenarios. Cobuild creates only step-based
scenarios and cannot edit a scenario's script.

Creation makes the scenario inactive and without triggers, saves the script,
and confirms it by reading it back. When the scenario exists but its script is
not confirmed, the tool returns python_scenario_script_unconfirmed with the
scenario_id instead of reporting success. Script edits replace the whole
script, keep every other setting, and refuse step-based scenarios.
get_scenario_settings now includes the script of a Python-script scenario.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dataiku accepts duplicate scenario names, so a create retried after a lost
response could leave a second scenario holding Dataiku's sample script. The
tool now refuses a name already in use and returns that scenario's id.

Also state the Python-script scenario exception consistently in the
capability matrix and routing convention, and correct the skill's note on
code environments for existing scenarios.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant