一键导入
add-python-task
TRIGGER when user asks to add a Python-backed workflow task to an existing Python-backed microservice.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
TRIGGER when user asks to add a Python-backed workflow task to an existing Python-backed microservice.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
TRIGGER when the user asks to upgrade the project to a newer or the latest version of Microbus, or to update the framework. Each Microbus release ships this one self-contained skill; it applies that release's single-version migration, then chains to the next release's copy of this skill until the target version is reached.
TRIGGER when user asks to create, scaffold, or initialize a new microservice.
How to choose the hostname of a new Microbus microservice. Referenced by the add-microservice and add-sql-microservice scaffolding skills (and, through their delegation to add-microservice, by add-python-microservice and import-openapi-microservice). Consult it whenever a microservice's hostname is being chosen.
Performs an architectural review of a microservice-based system built on the Microbus framework. Covers only cross-cutting, cross-microservice concerns - service boundaries, the dependency graph, coupling, cross-service consistency, data ownership, workflow composition, edge security, and system operations. Anything judgeable inside a single microservice directory belongs to the review-microservice skill and is out of scope here. Produces a structured report with findings and recommendations.
Reviews the microservices touched by a set of changes - by default the whole current feature branch versus its merge-base with main, plus any uncommitted work. Runs the review-microservice skill on each changed microservice and the review-architecture skill scoped to those microservices and their graph neighbors, then consolidates one report. Use before merging a branch or before committing working-tree changes.
Performs a thorough review of a single Microbus microservice. Checks for completeness, framework compliance, code quality, security, test coverage, documentation, API design, and data access performance. Produces a structured report with findings and recommendations.
| name | add-python-task |
| description | TRIGGER when user asks to add a Python-backed workflow task to an existing Python-backed microservice. |
CRITICAL: Read .claude/rules/python.txt and .claude/rules/workflows.txt before proceeding. The former covers the Go-Python bridge; the latter covers task lifecycle, flow state, retries, and OnTimeout/OnError routing.
CRITICAL: Do NOT explore or analyze other microservices unless explicitly instructed to do so. The instructions in this skill are self-contained to this microservice.
Copy this checklist and track your progress:
Adding a Python-backed workflow task:
- [ ] Step 1: Verify the microservice is Python-backed
- [ ] Step 2: Determine the task signature
- [ ] Step 3: Run add-task with Manual + Tags("python")
- [ ] Step 4: Replace the task body with the Call+Await durability pattern
- [ ] Step 5: Add the Python function to service.py
- [ ] Step 6: Housekeeping
The microservice must already have python.go and a service.py at its root. If not, run add-python-microservice first (it works both for fresh microservices and for extending an existing one without overwriting business logic).
Determine the Go task signature. Task signatures include flow *workflow.Flow after ctx but before the typed inputs. Outputs that share a name with an input must use the Out suffix on the output side (read-modify-write pattern).
func MyTask(ctx context.Context, flow *workflow.Flow, input1 string, input2 int) (output1 float64, err error)
Python sees the inputs as a dict keyed by the field names in MyTaskIn. Python does not see ctx or flow.
add-task with Manual + Tags("python")Run the add-task skill, with these overrides applied as you reach each of its steps:
Step 7 (Declare the Task in definition.go): add Manual: true and Tags: []string{"python"} to the define.Task var, so the task is registered as a manual subscription tagged python and stays off the bus until the venv liveness callback activates the python-tagged group when Python is ready:
var MyTask = define.Task{ // MARKER: MyTask
Host: Hostname, Method: "POST", Route: ":428/my-task",
Manual: true, Tags: []string{"python"},
In: MyTaskIn{}, Out: MyTaskOut{},
}
Step 8 (Generate the Boilerplate): run normally. genservice emits the sub.Manual() and sub.Tag("python") wiring into the generated intermediate.go, and scaffolds the task stub in service.go and the test in service_test.go.
Step 9 (Implement the Logic in service.go): skip. Step 4 below replaces the generated stub's body.
Step 10 (Test the Task): run normally, then add a one-line opt-in HINT immediately after the app.RunInTest(t) line so a future reader can switch the test from mock-only to real-Python without hunting through docs:
app.RunInTest(t)
// HINT: Uncomment to spin up real Python and exercise actual execution (slow on first run)
// svc.StartPyVenv(ctx)
Step 11 (Housekeeping): skip; housekeeping runs once at the end of this skill instead.
When add-task finishes, return here for Step 4.
In service.go, replace the task body that add-task left as a stub with the durable Call+Await pattern. The callID is persisted in flow state under the key pyCallID; if the task is re-entered (via an unlimited flow.Retry gated on a 408 timeout after the step's time budget expires), the existing callID is re-Awaited rather than a fresh call being issued. This lets a Python computation that exceeds the framework's 15-minute hop ceiling survive across task retries.
func (svc *Service) MyTask(ctx context.Context, flow *workflow.Flow, input1 string, input2 int) (output1 float64, err error) { // MARKER: MyTask
if svc.venv == nil || !svc.venv.Ready() {
return 0, errors.New("venv not ready", http.StatusServiceUnavailable)
}
callID := flow.GetString("pyCallID")
if callID == "" {
in := myserviceapi.MyTaskIn{
Input1: input1,
Input2: input2,
}
callID, err = svc.venv.Call(ctx, "my_task", in)
if err != nil {
return 0, errors.Trace(err)
}
flow.SetString("pyCallID", callID)
}
var out myserviceapi.MyTaskOut
err = svc.venv.Await(ctx, callID, &out)
if err != nil {
if errors.StatusCode(err) == http.StatusRequestTimeout && flow.Retry(0, 1.0, 0, 0) {
return 0, nil
}
flow.SetString("pyCallID", "") // clear on terminal error so downstream steps don't see it
return 0, errors.Trace(err)
}
flow.SetString("pyCallID", "") // clear on success so downstream steps don't see it
return out.Output1, nil
}
How the pattern composes:
pyCallID is empty, the task issues Call, persists the returned callID, then Awaits. If the step time budget allows the call to complete, the result is returned and pyCallID is cleared.pyCallID is the previously-issued callID. The Python work has been running this whole time. The task skips Call and goes straight to Await, which either completes immediately (Python finished) or waits up to the next step time budget.pyCallID is cleared so the workflow's later steps don't see the now-consumed callID in flow state.service.pyAppend a Python function to service.py (at the microservice's root) whose name is the snake_case form of the Go task name, accepting a dict and returning a dict. The docstring is the same description text as the task's godoc on the Go side.
def my_task(args): # MARKER: MyTask
"""MyTask does X."""
input1 = args["input1"]
input2 = args["input2"]
# ... compute ...
return {"output1": 42.0}
Follow the housekeeping skill.