[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6EKJCDS84KZCE9F8G5D2ZW0":3,"skill-same:01M6EKJCDS84KZCE9F8G5D2ZW0":86},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":43,"commitSha":44,"skillDirectory":45,"forksCount":46,"repositoryHtmlUrl":47,"repositoryHomepageUrl":6,"ownerHtmlUrl":48,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":49,"install":50,"resources":63},"01M6EKJCDS84KZCE9F8G5D2ZW0","workflow",null,"Write an Ironflow WorkflowHandler - typed input schema, steps (shell, http, agent, approval, human input, signal, decision, sub-workflow, parallel), registration in handlers(). Loaded by the ironflow hub for the workflow verb.","ThomasTartrau\u002Fironflow","ThomasTartrau","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F145434513?v=4","01M6EKHJ55CEQZVP23J48EMFQ1","Thomas Tartrau","01M6EKHJ9CJE78ZBQDPQ9M13C2",2,"MIT","https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow\u002Fblob\u002Fmain\u002Fplugins\u002Fironflow\u002Fskills\u002Fworkflow\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[5,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"http","shell","parallel","decision","write","approval","schema","hub","registration","human","false","input","ironflow","for","2026-11-10 10:12:30",false,"---\nname: workflow\ndescription: Write an Ironflow WorkflowHandler - typed input schema, steps (shell, http, agent, approval, human input, signal, decision, sub-workflow, parallel), registration in handlers(). Loaded by the ironflow hub for the workflow verb.\nuser-invocable: false\n---\n\n# Ironflow workflow\n\nA workflow is a struct implementing `WorkflowHandler`. Control flow is plain Rust. Each `ctx.*` call is a persisted step with its own status, duration and cost.\n\n## 1. Gather the shape\n\nAsk only what the code cannot guess, in one AskUserQuestion call:\n\n- workflow name (kebab-case, unique) and one-line purpose\n- input fields (name, type, required or default)\n- the steps in order, and which of them run in parallel\n- whether a human approval gate sits somewhere, and whether an agent step needs a budget above `0.10` USD\n\nThen locate the workflows crate: `grep -rl \"pub fn handlers\" --include=*.rs .`\n\n## 2. Write the handler\n\nOne file per handler in the workflows crate, `snake_case.rs`. Template:\n\n```rust\nuse ironflow_engine::config::{ApprovalConfig, ShellConfig, StepConfig};\nuse ironflow_engine::context::WorkflowContext;\nuse ironflow_engine::handler::{HandlerFuture, WorkflowHandler, input_schema_for};\nuse schemars::JsonSchema;\nuse serde::Deserialize;\nuse serde_json::Value;\n\n\u002F\u002F\u002F Input payload of the `deploy` workflow. The dashboard renders a form from it.\n#[derive(Debug, Deserialize, JsonSchema)]\npub struct DeployInput {\n    \u002F\u002F\u002F Git ref to deploy.\n    pub git_ref: String,\n    \u002F\u002F\u002F Target environment.\n    #[serde(default = \"default_env\")]\n    pub environment: String,\n}\n\nfn default_env() -> String {\n    \"staging\".to_string()\n}\n\n\u002F\u002F\u002F Builds, tests in parallel, waits for a human, deploys.\npub struct Deploy;\n\nimpl WorkflowHandler for Deploy {\n    fn name(&self) -> &str {\n        \"deploy\"\n    }\n\n    fn description(&self) -> &str {\n        \"Build, run checks in parallel, wait for approval, deploy.\"\n    }\n\n    fn category(&self) -> Option\u003C&str> {\n        Some(\"ops\")\n    }\n\n    fn input_schema(&self) -> Option\u003CValue> {\n        Some(input_schema_for::\u003CDeployInput>())\n    }\n\n    fn execute\u003C'a>(&'a self, ctx: &'a mut WorkflowContext) -> HandlerFuture\u003C'a> {\n        Box::pin(async move {\n            let input: DeployInput = ctx.input().await?;\n\n            ctx.shell(\n                \"build\",\n                ShellConfig::new(\"cargo build --release\").env(\"GIT_REF\", &input.git_ref),\n            )\n            .await?;\n\n            \u002F\u002F Fan out. `true` = fail fast on the first error.\n            let checks = ctx\n                .parallel(\n                    vec![\n                        (\"test\", StepConfig::Shell(ShellConfig::new(\"cargo test\"))),\n                        (\"lint\", StepConfig::Shell(ShellConfig::new(\"cargo clippy\"))),\n                    ],\n                    true,\n                )\n                .await?;\n            if !checks.iter().all(|c| c.output.is_success()) {\n                return Ok(());\n            }\n\n            if ctx\n                .when(\"production run\", |i: &DeployInput| i.environment == \"production\")\n                .await?\n            {\n                \u002F\u002F The run suspends here. After approval the handler is replayed from\n                \u002F\u002F the top: completed steps come back from cache, this code runs again.\n                ctx.approval(\n                    \"approve-production\",\n                    ApprovalConfig::new(\"Ship to production?\").with_timeout_seconds(3600),\n                )\n                .await?;\n            }\n\n            ctx.shell(\n                \"deploy\",\n                ShellConfig::new(\".\u002Fdeploy.sh \\\"$ENVIRONMENT\\\"\").env(\"ENVIRONMENT\", &input.environment),\n            )\n            .await?;\n\n            Ok(())\n        })\n    }\n}\n```\n\nThen add the source display, which needs the real file name:\n\n```rust,ignore\n    fn source_code(&self) -> Option\u003C&str> {\n        Some(include_str!(\"deploy.rs\"))\n    }\n```\n\nStep catalogue with every config builder: `references\u002Fsteps.md`. Read it before writing an agent, http, signal (`ctx.wait_for_signal`), secret or artifact step. Sending a signal from a workflow (`ctx.send_signal`): `references\u002Fsignal-send.md`. A workflow that runs on a cron schedule (a time of day, a timezone, no overlapping runs): `references\u002Fschedule.md`. Sub-workflows (`ctx.workflow`, `ctx.workflow_with`, children in parallel with `ctx.workflows_with`): `references\u002Fsub-workflows.md`.\n\n## 3. Register\n\nIn the workflows crate `lib.rs`: `mod deploy;`, `pub use deploy::{Deploy, DeployInput};`, and one line in `handlers()`:\n\n```rust,ignore\npub fn handlers() -> Vec\u003CBox\u003Cdyn WorkflowHandler>> {\n    vec![Box::new(Hello), Box::new(Deploy)]\n}\n```\n\nThat list feeds both the server and the worker. Nothing else to touch.\n\n## 4. Verify\n\n```bash\ncargo build -p workflows\ncargo test -p workflows\n```\n\nThen offer, in one sentence, the workflow reviewer (`\u002Fironflow review`) and the test skill (`\u002Fironflow test \u003Cname>`). Do not run either without an answer.\n\n## Rules that are not obvious\n\n- **Branches are invisible to the planner unless you declare them.** A plain `if` works, but `ctx.when(\"production run\", |i: &DeployInput| i.environment == \"production\").await?` and `ctx.when_dynamic(\"build succeeded\", build.is_success())` make it show up in `ironflow-cli run plan \u003Cname>`, which lists the steps a run would create without executing anything. The first argument is a label, never parsed; the closure gets the typed input. See `references\u002Fsteps.md`.\n- **No stringly-typed access.** Read step outputs with `stdout()`, `stderr()`, `exit_code()`, `status()`, `body()`, `error()` (the message of an `allow_failure` step that failed), the input with `ctx.input::\u003CT>()`, decisions through a `#[derive(DecisionAnswers)]` struct, agent answers through `.output::\u003CT>()`, and artifacts through the handle `step.artifact(\"file\")?`. Never index the raw output JSON by key, and never copy a step name by hand where a handle exists. Sub-workflow children implement `TypedWorkflow`; `ctx.workflow(&Child, ChildInput { .. })` returns `run_id()` as a `Uuid`.\n- **Step names are cache keys.** Stable, unique within the run, no timestamps or random ids. In a loop, suffix with the loop index. `ctx.parallel` enforces it for its branches: a duplicate name fails the run before any step starts. A child workflow's step is named after its handler: when the same child runs twice (a loop, a `ctx.workflows_with` wave), give each one `WorkflowOptions::step_name`.\n- **Approval replays the handler.** Anything that is not a `ctx.*` step runs again after approval. Keep side effects inside steps. Details and a safe pattern: `references\u002Fapproval-replay.md`.\n- **Agent steps: tools and structured output on one step.** `AgentStepConfig::new(prompt).allow_tool(Tool::Read).output::\u003CT>()` lets the agent read, then answer with a `T`: `ctx.agent` returns the `T` itself. Do not split it into a tools step and a formatting step. On an HTTP provider, `.tool_profile(BUG)` (a `ToolProfile` constant shared with the worker) picks a named tool profile registered on the worker (`references\u002Fsteps.md`); a Claude CLI provider refuses it.\n- **Agent steps: a reusable agent is an `impl AgentRole`.** An agent reused across workflows (persona, tools, rights, output type) is an `impl AgentRole` called with `ctx.agent(\"review\", Reviewer.task(input))`, never a function returning `AgentStepConfig` (`references\u002Fsteps.md`).\n- **Agent output enters a prompt only as untrusted.** `out.text()` is an `AgentText`: pass it with `Prompt::new().text(\"..\").untrusted(&out.text())`, never `format!` it into the next prompt. A typed answer derives `AgentAnswer` and declares its text fields `AgentText` (a `String` field fails to compile); `.untrusted(&answer.field)` in a prompt, `.raw()` for a log, an artifact, a comment or a shell argument. `{:?}` prints `AgentText(\u003CN bytes>)`, not the text (`references\u002Fsteps.md`).\n- **Budget.** Claude Code's system cache alone costs about `0.04` USD, so `max_budget_usd` below `0.10` fails. Put a `default_max_cost_usd` on the handler when it contains an agent step.\n- **User data goes through `env`, never into the command string.** `ShellConfig::new(\"echo \\\"$X\\\"\").env(\"X\", value)`.\n- **A failing step fails the run.** No hidden retries. `allow_failure()` on a step config, or `ctx.operation_allow_failure()` for an operation, lets the run continue with status `Warning`; `retry_policy(RetryPolicy::...)` opts into retries explicitly. On a shell step, `exit_code_as_output()` makes a non-zero exit a normal output (`exit_code()`, `is_success()`) instead of a failure.\n- **`ctx.input::\u003CT>()` errors are `EngineError::Serialization`.** The engine also validates the payload against `input_schema()` before creating a run through the API, so the handler rarely sees a bad payload.\n","# Ironflow workflow\n\nA workflow is a struct implementing `WorkflowHandler`. Control flow is plain Rust. Each `ctx.*` call is a persisted step with its own status, duration and cost.\n\n## 1. Gather the shape\n\nAsk only what the code cannot guess, in one AskUserQuestion call:\n\n- workflow name (kebab-case, unique) and one-line purpose\n- input fields (name, type, required or default)\n- the steps in order, and which of them run in parallel\n- whether a human approval gate sits somewhere, and whether an agent step needs a budget above `0.10` USD\n\nThen locate the workflows crate: `grep -rl \"pub fn handlers\" --include=*.rs .`\n\n## 2. Write the handler\n\nOne file per handler in the workflows crate, `snake_case.rs`. Template:\n\n```rust\nuse ironflow_engine::config::{ApprovalConfig, ShellConfig, StepConfig};\nuse ironflow_engine::context::WorkflowContext;\nuse ironflow_engine::handler::{HandlerFuture, WorkflowHandler, input_schema_for};\nuse schemars::JsonSchema;\nuse serde::Deserialize;\nuse serde_json::Value;\n\n\u002F\u002F\u002F Input payload of the `deploy` workflow. The dashboard renders a form from it.\n#[derive(Debug, Deserialize, JsonSchema)]\npub struct DeployInput {\n    \u002F\u002F\u002F Git ref to deploy.\n    pub git_ref: String,\n    \u002F\u002F\u002F Target environment.\n    #[serde(default = \"default_env\")]\n    pub environment: String,\n}\n\nfn default_env() -> String {\n    \"staging\".to_string()\n}\n\n\u002F\u002F\u002F Builds, tests in parallel, waits for a human, deploys.\npub struct Deploy;\n\nimpl WorkflowHandler for Deploy {\n    fn name(&self) -> &str {\n        \"deploy\"\n    }\n\n    fn description(&self) -> &str {\n        \"Build, run checks in parallel, wait for approval, deploy.\"\n    }\n\n    fn category(&self) -> Option\u003C&str> {\n        Some(\"ops\")\n    }\n\n    fn input_schema(&self) -> Option\u003CValue> {\n        Some(input_schema_for::\u003CDeployInput>())\n    }\n\n    fn execute\u003C'a>(&'a self, ctx: &'a mut WorkflowContext) -> HandlerFuture\u003C'a> {\n        Box::pin(async move {\n            let input: DeployInput = ctx.input().await?;\n\n            ctx.shell(\n                \"build\",\n                ShellConfig::new(\"cargo build --release\").env(\"GIT_REF\", &input.git_ref),\n            )\n            .await?;\n\n            \u002F\u002F Fan out. `true` = fail fast on the first error.\n            let checks = ctx\n                .parallel(\n                    vec![\n                        (\"test\", StepConfig::Shell(ShellConfig::new(\"cargo test\"))),\n                        (\"lint\", StepConfig::Shell(ShellConfig::new(\"cargo clippy\"))),\n                    ],\n                    true,\n                )\n                .await?;\n            if !checks.iter().all(|c| c.output.is_success()) {\n                return Ok(());\n            }\n\n            if ctx\n                .when(\"production run\", |i: &DeployInput| i.environment == \"production\")\n                .await?\n            {\n                \u002F\u002F The run suspends here. After approval the handler is replayed from\n                \u002F\u002F the top: completed steps come back from cache, this code runs again.\n                ctx.approval(\n                    \"approve-production\",\n                    ApprovalConfig::new(\"Ship to production?\").with_timeout_seconds(3600),\n                )\n                .await?;\n            }\n\n            ctx.shell(\n                \"deploy\",\n                ShellConfig::new(\".\u002Fdeploy.sh \\\"$ENVIRONMENT\\\"\").env(\"ENVIRONMENT\", &input.environment),\n            )\n            .await?;\n\n            Ok(())\n        })\n    }\n}\n```\n\nThen add the source display, which needs the real file name:\n\n```rust,ignore\n    fn source_code(&self) -> Option\u003C&str> {\n        Some(include_str!(\"deploy.rs\"))\n    }\n```\n\nStep catalogue with every config builder: `references\u002Fsteps.md`. Read it before writing an agent, http, signal (`ctx.wait_for_signal`), secret or artifact step. Sending a signal from a workflow (`ctx.send_signal`): `references\u002Fsignal-send.md`. A workflow that runs on a cron schedule (a time of day, a timezone, no overlapping runs): `references\u002Fschedule.md`. Sub-workflows (`ctx.workflow`, `ctx.workflow_with`, children in parallel with `ctx.workflows_with`): `references\u002Fsub-workflows.md`.\n\n## 3. Register\n\nIn the workflows crate `lib.rs`: `mod deploy;`, `pub use deploy::{Deploy, DeployInput};`, and one line in `handlers()`:\n\n```rust,ignore\npub fn handlers() -> Vec\u003CBox\u003Cdyn WorkflowHandler>> {\n    vec![Box::new(Hello), Box::new(Deploy)]\n}\n```\n\nThat list feeds both the server and the worker. Nothing else to touch.\n\n## 4. Verify\n\n```bash\ncargo build -p workflows\ncargo test -p workflows\n```\n\nThen offer, in one sentence, the workflow reviewer (`\u002Fironflow review`) and the test skill (`\u002Fironflow test \u003Cname>`). Do not run either without an answer.\n\n## Rules that are not obvious\n\n- **Branches are invisible to the planner unless you declare them.** A plain `if` works, but `ctx.when(\"production run\", |i: &DeployInput| i.environment == \"production\").await?` and `ctx.when_dynamic(\"build succeeded\", build.is_success())` make it show up in `ironflow-cli run plan \u003Cname>`, which lists the steps a run would create without executing anything. The first argument is a label, never parsed; the closure gets the typed input. See `references\u002Fsteps.md`.\n- **No stringly-typed access.** Read step outputs with `stdout()`, `stderr()`, `exit_code()`, `status()`, `body()`, `error()` (the message of an `allow_failure` step that failed), the input with `ctx.input::\u003CT>()`, decisions through a `#[derive(DecisionAnswers)]` struct, agent answers through `.output::\u003CT>()`, and artifacts through the handle `step.artifact(\"file\")?`. Never index the raw output JSON by key, and never copy a step name by hand where a handle exists. Sub-workflow children implement `TypedWorkflow`; `ctx.workflow(&Child, ChildInput { .. })` returns `run_id()` as a `Uuid`.\n- **Step names are cache keys.** Stable, unique within the run, no timestamps or random ids. In a loop, suffix with the loop index. `ctx.parallel` enforces it for its branches: a duplicate name fails the run before any step starts. A child workflow's step is named after its handler: when the same child runs twice (a loop, a `ctx.workflows_with` wave), give each one `WorkflowOptions::step_name`.\n- **Approval replays the handler.** Anything that is not a `ctx.*` step runs again after approval. Keep side effects inside steps. Details and a safe pattern: `references\u002Fapproval-replay.md`.\n- **Agent steps: tools and structured output on one step.** `AgentStepConfig::new(prompt).allow_tool(Tool::Read).output::\u003CT>()` lets the agent read, then answer with a `T`: `ctx.agent` returns the `T` itself. Do not split it into a tools step and a formatting step. On an HTTP provider, `.tool_profile(BUG)` (a `ToolProfile` constant shared with the worker) picks a named tool profile registered on the worker (`references\u002Fsteps.md`); a Claude CLI provider refuses it.\n- **Agent steps: a reusable agent is an `impl AgentRole`.** An agent reused across workflows (persona, tools, rights, output type) is an `impl AgentRole` called with `ctx.agent(\"review\", Reviewer.task(input))`, never a function returning `AgentStepConfig` (`references\u002Fsteps.md`).\n- **Agent output enters a prompt only as untrusted.** `out.text()` is an `AgentText`: pass it with `Prompt::new().text(\"..\").untrusted(&out.text())`, never `format!` it into the next prompt. A typed answer derives `AgentAnswer` and declares its text fields `AgentText` (a `String` field fails to compile); `.untrusted(&answer.field)` in a prompt, `.raw()` for a log, an artifact, a comment or a shell argument. `{:?}` prints `AgentText(\u003CN bytes>)`, not the text (`references\u002Fsteps.md`).\n- **Budget.** Claude Code's system cache alone costs about `0.04` USD, so `max_budget_usd` below `0.10` fails. Put a `default_max_cost_usd` on the handler when it contains an agent step.\n- **User data goes through `env`, never into the command string.** `ShellConfig::new(\"echo \\\"$X\\\"\").env(\"X\", value)`.\n- **A failing step fails the run.** No hidden retries. `allow_failure()` on a step config, or `ctx.operation_allow_failure()` for an operation, lets the run continue with status `Warning`; `retry_policy(RetryPolicy::...)` opts into retries explicitly. On a shell step, `exit_code_as_output()` makes a non-zero exit a normal output (`exit_code()`, `is_success()`) instead of a failure.\n- **`ctx.input::\u003CT>()` errors are `EngineError::Serialization`.** The engine also validates the payload against `input_schema()` before creating a run through the API, so the handler rarely sees a bad payload.\n",{"name":5,"description":7,"user-invocable":39},"main","efb82a31b29b49288c6f0909bbe2b1b80520dc46","plugins\u002Fironflow\u002Fskills\u002Fworkflow",1,"https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow","https:\u002F\u002Fgithub.com\u002FThomasTartrau","2026-11-04 18:52:31",{"source":8,"skillName":5,"commands":51,"prompt":61,"zipAvailable":62},[52,55,58],{"tool":53,"command":54},"npx","npx skills add ThomasTartrau\u002Fironflow --skill workflow",{"tool":56,"command":57},"bunx","bunx skills add ThomasTartrau\u002Fironflow --skill workflow",{"tool":59,"command":60},"pnpm","pnpm dlx skills add ThomasTartrau\u002Fironflow --skill workflow","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow\u002Fblob\u002Fmain\u002Fplugins\u002Fironflow\u002Fskills\u002Fworkflow\u002FSKILL.md",true,[64,70,74,78,82],{"path":65,"resourceType":66,"resourceTypeDesc":67,"sizeBytes":68,"sourceHtmlUrl":69},"references\u002Fapproval-replay.md","REFERENCE","参考文档",4571,"https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow\u002Fblob\u002Fmain\u002Fplugins\u002Fironflow\u002Fskills\u002Fworkflow\u002Freferences\u002Fapproval-replay.md",{"path":71,"resourceType":66,"resourceTypeDesc":67,"sizeBytes":72,"sourceHtmlUrl":73},"references\u002Fschedule.md",1813,"https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow\u002Fblob\u002Fmain\u002Fplugins\u002Fironflow\u002Fskills\u002Fworkflow\u002Freferences\u002Fschedule.md",{"path":75,"resourceType":66,"resourceTypeDesc":67,"sizeBytes":76,"sourceHtmlUrl":77},"references\u002Fsignal-send.md",2476,"https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow\u002Fblob\u002Fmain\u002Fplugins\u002Fironflow\u002Fskills\u002Fworkflow\u002Freferences\u002Fsignal-send.md",{"path":79,"resourceType":66,"resourceTypeDesc":67,"sizeBytes":80,"sourceHtmlUrl":81},"references\u002Fsteps.md",40489,"https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow\u002Fblob\u002Fmain\u002Fplugins\u002Fironflow\u002Fskills\u002Fworkflow\u002Freferences\u002Fsteps.md",{"path":83,"resourceType":66,"resourceTypeDesc":67,"sizeBytes":84,"sourceHtmlUrl":85},"references\u002Fsub-workflows.md",11697,"https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow\u002Fblob\u002Fmain\u002Fplugins\u002Fironflow\u002Fskills\u002Fworkflow\u002Freferences\u002Fsub-workflows.md",[87,115,131,148,161,178],{"skillNo":88,"name":89,"nameZh":6,"description":90,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":98,"publishedAt":38},"01M6EKJ8WSG7KC0M71GSN3N82K","upgrade","Upgrade the Ironflow crates of a project and migrate its code, or audit existing code against the current Ironflow idioms. Reads the changelog between the installed and the target release, maps every change to the project's code, then proposes or applies the migrations. Loaded by the ironflow hub for the upgrade and audit verbs.","https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow\u002Fblob\u002Fmain\u002Fplugins\u002Fironflow\u002Fskills\u002Fupgrade\u002FSKILL.md",[93],{"categoryNo":94,"categoryName":95,"taxonomyType":96,"taxonomyTypeDesc":97},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[89,99,100,101,102,103,104,105,106,31,107,108,109,110,111,112,113,34,114,36],"change","code","changelog","audit","Bash","migrate","diff","log","maps","migrations","show","scan","git log","project","cargo","scripts",{"skillNo":116,"name":117,"nameZh":6,"description":118,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":119,"categories":120,"keywords":125,"publishedAt":38},"01M6EKJ5G6553VH8BES3V9CY5B","test","Write an end-to-end test for an Ironflow workflow handler - real Engine, InMemoryStore, RecordReplayProvider fixtures, assertions on run status and step names. Loaded by the ironflow hub for the test verb.","https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow\u002Fblob\u002Fmain\u002Fplugins\u002Fironflow\u002Fskills\u002Ftest\u002FSKILL.md",[121,124],{"categoryNo":122,"categoryName":123,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[117,5,126,127,128,28,31,129,130,34,36],"status","run","engine","fixtures","end",{"skillNo":132,"name":133,"nameZh":6,"description":134,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":135,"categories":136,"keywords":138,"publishedAt":38},"01M6EKJ0NJYWEPJ9XX55GSYR44","setup","Scaffold a new Ironflow project - a Cargo workspace with a workflows lib, an API server binary, a worker binary, a hello workflow and its end-to-end test. Loaded by the ironflow hub for the setup verb.","https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow\u002Fblob\u002Fmain\u002Fplugins\u002Fironflow\u002Fskills\u002Fsetup\u002FSKILL.md",[137],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[133,5,117,139,140,141,142,143,103,144,31,112,145,130,146,147,113,34,114,36],"workflows","hello","server","api","api server","workspace","worker","binary","lib",{"skillNo":149,"name":150,"nameZh":6,"description":151,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":152,"categories":153,"keywords":154,"publishedAt":38},"01M6EKHWQRBXVMK7YXT3DV3XDH","operation","Write a custom Ironflow Operation - an integration (GitLab, Slack, any HTTP API) executed as a tracked step through ctx.operation(). Loaded by the ironflow hub for the operation verb.","https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow\u002Fblob\u002Fmain\u002Fplugins\u002Fironflow\u002Fskills\u002Foperation\u002FSKILL.md",[],[150,155,24,156,157,142,28,31,158,34,36,159,37,160],"slack","gitlab","integration","as","http api","custom",{"skillNo":162,"name":36,"nameZh":6,"description":163,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":164,"categories":165,"keywords":168,"publishedAt":38},"01M6EKHS2MNPSZ9A0B96HWGHZG","Entry point for building on the Ironflow workflow engine. Use for setting up an Ironflow project (server + worker), writing a WorkflowHandler, writing a custom Operation, testing a workflow end to end, reviewing a handler, upgrading the ironflow crates and migrating the code, or auditing existing code against the current Ironflow idioms. Trigger on \"ironflow\", \"workflow handler\", \"ironflow setup\", \"ctx.shell\", \"ctx.agent\", \"approval gate\", \"upgrade ironflow\", \"bump ironflow\", \"créer un workflow ironflow\", \"mettre en place ironflow\", \"mettre à jour ironflow\", \"migrer ironflow\".","https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow\u002Fblob\u002Fmain\u002Fplugins\u002Fironflow\u002Fskills\u002Fironflow\u002FSKILL.md",[166,167],{"categoryNo":122,"categoryName":123,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[36,169,170,171,5,117,100,102,141,128,172,173,174,175,176,89,112,145,130,177],"testing","writing","review","use","version","up","en","bump","trigger",{"skillNo":179,"name":180,"nameZh":6,"description":181,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":182,"categories":183,"keywords":184,"publishedAt":38},"01M6EKHNAEEZPNYY5ST70AXTHV","ops-crate","Generate a new ops\u002Fxxx crate for an external service integration. Identifies the right pattern (A=wrapper, B=HTTP direct, C=macro), scaffolds the full structure, and runs the quality checklist. Trigger on \"ops crate\", \"new ops\", \"creer un crate ops\", \"add ops\u002Fxxx\", \"nouveau crate ops\", \"ironflow ops\".","https:\u002F\u002Fgithub.com\u002FThomasTartrau\u002Fironflow\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fops-crate\u002FSKILL.md",[],[185,186,24,187,157,188,189,190,191,192,193,194,195,196,177,197,198,199],"ops","ops crate","generate","macro","direct","quality","Wrapper","checklist","structure","pattern","full","service","name","add","scaffolds"]