agentleFS
ShipTodayShipToday/forge-plugin-cursor/skills/forge-workflow/SKILL.md

forge-workflow

Skill0 starsChanged 5 days agoMITView on GitHub

Helps an organization admin manage custom Forge workflows conversationally — both AUTHORING new workflows and DELETING existing org- or team-scoped overrides. Invoke this skill whenever the user asks to "create a workflow", "customize my SDLC", "build a new workflow", "set up a custom process", "author a workflow", "add a team workflow", OR to "delete a workflow", "remove an override", "reset this workflow to default", "clear the team workflow override", "delete the org workflow preset". This is a meta-capability: it configures Forge itself by calling the `forge__list_skills_catalog`, `forge__get_workflow`, `forge__save_workflow`, and `forge__delete_workflow` MCP tools. Do NOT invoke this skill to run a workflow — that is what `forge-autopilot` / `forge__start_workflow` is for. Do NOT invoke for generic "help me plan a feature" requests.

1 other project uses a version of this.

Adopted by

Most recently taken up by

  • Other versions1 project uses a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 1

    1. flowForges/myFlowForge★ 58same name

    How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.

  • In this repository2 other files it gives its agents.

The file

Show its rules and text

We split this skill into the rules it gives an agent

67 rules. About 0 are common, 26 are distinctive, and 41 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈26

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 41

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Prefer a compatible blocking control.

    line 72
  2. Distinctive:

    Keep prose for questions with no fixed answers: the Step 2 intake ("describe it in your own words"), or asking what to change after the …

    line 106
  3. Distinctive:

    When an admin wants to override an existing workflow at a narrower scope (system → org, or org → team), you cannot infer the baseline steps …

    line 114
  4. Very specific:

    Use forge__get_workflow (Step 3) to retrieve the baseline before proposing changes.

    line 118
  5. Very specific:

    How the org works: Follow every rule in tailoring_rules whenever you draft or change a workflow — they come from the server, so they can …

    line 156
  6. Very specific:

    If caller.orgRole !== 'org:admin' (including null / missing): STOP immediately.

    line 188
  7. Very specific:

    STOP immediately. Reply with exactly: "Creating workflows is restricted to organization administrators.

    line 189
  8. Very specific:

    Ask your Forge org admin to run this for you." Do NOT proceed to intent intake, do NOT draft a plan, and do NOT call forge__save_workflow.

    line 191
  9. Very specific:

    Do NOT proceed to intent intake, do NOT draft a plan, and do NOT call forge__save_workflow.

    line 192
  10. Very specific:

    If caller.orgId is null (stdio or unauthenticated session): STOP with the same message — workflow authoring requires an authenticated HTTP …

    line 194
  11. Distinctive:

    STOP with the same message — workflow authoring requires an authenticated HTTP session with an org context.

    line 195
  12. Distinctive:

    If a Forge run recap earlier in this conversation ended with a friction insight, it already names what happened and what would fix it.

    line 204
  13. Distinctive:

    When that insight is in the conversation and the admin has just taken it up (e.g.

    line 214
  14. Distinctive:

    If the intent is truly ambiguous ("change the bug workflow" — create a new one or modify/delete an existing one?), ask one clarifying …

    line 255
  15. Distinctive:

    If the intent is only the top three, say so plainly and point them at the drawer rather than authoring:

    line 285
  16. Very specific:

    Use the skills and workflows arrays from Step 1 — do NOT call forge__list_skills_catalog a second time.

    line 300
  17. Very specific:

    If the catalog contains skills or workflows that semantically match the admin's intent, reuse them — reference existing skill_ids in your …

    line 303
  18. Distinctive:

    If the catalog returns no usable matches for the stated intent, proceed with a fully synthesised draft (all steps defined as new custom …

    line 309
  19. Very specific:

    When the admin's intent maps to an existing workflow id (e.g.

    line 316
  20. Very specific:

    Read the response's scope field to know which scope actually matched (system | org | team).

    line 329
  21. Very specific:

    Keep the response's workflow.edition.

    line 343
  22. Very specific:

    If forge__get_workflow is not available in your environment, stop and ask the admin to enumerate the existing steps before drafting.

    line 347
  23. Distinctive:

    Do NOT infer steps from the workflow's description.

    line 349
  24. Very specific:

    When forge__get_workflow reports scope equal to the target scope of the new save (i.e., org for an org override, or team for a team …

    line 353
  25. Distinctive:

    Ask the admin to choose between "Modify existing" and "Replace from scratch".

    line 367
  26. Very specific:

    If no override yet exists at the target scope (scope returned was a broader scope than the target), there is nothing to ask — the returned …

    line 384
  27. Distinctive:

    Every editable field is described by a manifest entry of the shape:

    line 414
  28. Very specific:

    When iterating field_schema.new_skill for any inline new_skills entries, fields with valuesFrom pointing at the catalog (e.g.

    line 447
  29. Very specific:

    propose an SDLC stage: before save, then let the admin confirm or override.

    line 455
  30. Distinctive:

    It resolves inside the step, not before it: When a declared category is missing, Forge adds an instruction at the top of that step to …

    line 473
  31. Very specific:

    An override keeps its baseline: If the skill overrides a system skill, start with the baseline skill's needs (from catalog.skills) and …

    line 486
  32. Very specific:

    If the catalog has no state_categories (an older server), keep any baseline needs exactly as they are and do not add or drop categories you …

    line 491
  33. Very specific:

    Add those categories to the skill's needs for every capability it consumes, and for a capability it produces when it reports a detection …

    line 527
  34. Very specific:

    When an admin overrides a system skill, carry the baseline skill's consumes and produces across unless they explicitly want them dropped — …

    line 546
  35. Very specific:

    Read the workflow's name, description and classifier_hint and ask what it is FOR.

    line 573
  36. Very specific:

    Use other for admin, notification, onboarding and session-ops workflows that genuinely do not belong to a delivery stage — it is a …

    line 576
  37. Very specific:

    When caller.teams is empty or missing, still ask.

    line 619
  38. Very specific:

    Default is everyone: If the admin doesn't care or doesn't answer, omit the field (leave it NULL) and say so in the proposal summary.

    line 633
  39. Very specific:

    When you set or change admin_only, state the consequence plainly in the confirmation: an admin-only workflow disappears from members' …

    line 648
  40. Very specific:

    When drafting a custom skill's instructions body, ask the admin a single question: does this skill produce substantive analytical output (a …

    line 654
  41. Very specific:

    Skip display_text: when the skill only asks user-intent questions ("Approve?", "Edit fields?", "Send it?") with no analytical output, or …

    line 699
  42. Very specific:

    Every run's recap already names the two ways to send feedback, so a separate closing step is optional.

    line 751
  43. Distinctive:

    When proposing an override (not a from-scratch new workflow), render the diff against the baseline you retrieved in Step 3a/3b.

    line 758
  44. Distinctive:

    If any reference is unresolvable, surface it clearly:

    line 795
  45. Very specific:

    Do NOT call forge__save_workflow until every reference resolves.

    line 801
  46. Distinctive:

    If a referenced key is never set by any step in your proposed workflow, that step will never run.

    line 822
  47. Distinctive:

    When you detect an unreachable step, do NOT save.

    line 837
  48. Distinctive:

    Ask the admin for the choice.

    line 846
  49. Distinctive:

    Ask the admin to choose between "Switch to AI decides (Recommended)" — it asks before a write that would normally need approval — and "Keep …

    line 868
  50. Very specific:

    If the admin closes the session without answering, that's fine — no partial state is persisted.

    line 933
  51. Very specific:

    Call forge__save_workflow with a payload whose top level matches the manifest's workflow_preset fields, and whose steps entries match the …

    line 940
  52. Very specific:

    When the baseline came from forge__get_workflow (Modify existing in Step 3b) and the admin changed some of it, do NOT send the whole …

    line 996
  53. Very specific:

    When the success response also includes a warnings: [...] array, the save committed but the server flagged something the admin should know …

    line 1046
  54. Distinctive:

    Do NOT block or re-prompt for save approval on a warning — the workflow already committed.

    line 1087
  55. Very specific:

    If forge__save_workflow returns isError: true with a structured envelope, explain it plainly and offer to adjust:

    line 1093
  56. Distinctive:

    Never silently retry — always loop the admin in.

    line 1114
  57. Very specific:

    Use the workflows array from Step 1.

    line 1131
  58. Distinctive:

    If the admin named a workflow directly (e.g.

    line 1147
  59. Distinctive:

    If the reference is ambiguous — multiple matches, or the admin said something fuzzy like "remove my bug workflow override" — ask the admin …

    line 1151
  60. Distinctive:

    If the filtered list is empty ("you don't have any overrides in your org or teams"), stop and say so — nothing to delete.

    line 1156
  61. Very specific:

    If the target is team-scoped, confirm which team.

    line 1161
  62. Very specific:

    Prefer team_name over team_id when invoking the MCP tool — the server resolves the name for you (team names are unique within an org).

    line 1166
  63. Very specific:

    Before calling forge__delete_workflow, show the final target and the consequence, then ask for explicit confirmation.

    line 1172
  64. Very specific:

    If the target is team-scoped AND the org has no preset of its own (no scope: 'org' row for that id in the Step 1 catalog), use the org …

    line 1201
  65. Distinctive:

    Ask the admin to choose "Remove override" or "Cancel".

    line 1206
  66. Very specific:

    If forge__delete_workflow returns isError: true, explain it plainly and offer the right recovery:

    line 1238
  67. Distinctive:

    Never silently retry. Always loop the admin in.

    line 1254

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Manage Workflow

You are helping an organization admin manage custom Forge workflows conversationally — either authoring a new workflow or deleting an existing org- or team-scoped override. You do this conversationally, not as a step-by-step wizard.

Forge is an MCP server; the tools you will call are:

  • forge__list_skills_catalog — returns the skills, workflows, and the field schema manifest the server considers admin-editable. Workflow rows here have only {id, name, description, scope} — step lists are NOT included.
  • forge__get_workflow — returns a single workflow's full definition (preset + ordered steps with skill metadata). Use this to retrieve the baseline before drafting an override.
  • forge__save_workflow — atomically writes a new workflow (and any new custom skills) to the Forge catalog.
  • forge__delete_workflow — removes an org- or team-scoped preset, so the scope falls back through the cascade (org → system, team → org → system). Most of Forge's built-in workflows ship disabled, so falling back to the system default usually stops the workflow running; the launch set ships on and keeps running — see the Delete path for how to state this to the admin.

Why this skill is schema-driven

You do not carry a hardcoded list of workflow / step fields. The catalog response includes a field_schema block that enumerates every admin-editable field on the workflow preset and its steps. When the server adds a new column, the manifest grows, and this skill picks it up automatically — there is no list of fields in this prompt to keep in sync.

Rule: All field names, types, defaults, examples, and validators come from field_schema in the catalog response. Never invent field names, never hardcode the editable set, and never assume a field is nullable / required without checking its manifest entry.

How to ask the admin

Codex question lifetime

For fixed choices, prefer supported, permitted native UI. Forge may use a negotiated MCP form; after an answered RE-ENTRY, never ask the question again. Otherwise use request_user_input or request_user_input_async only when callable and permitted for this question's purpose in the current mode. Assess MCP forms and each host tool independently. In Default mode, a Plan-only blocking tool does not prohibit the async tool: check its own permissions before numbered text. An unexplained MCP rejection is not a policy-wide denial. Explicit policy prohibiting the question across routes still applies. Prefer a compatible blocking control. A permitted async picker may be used once; its string options carry exact labels, so show descriptions and full approval material in prose before opening it. Respect each tool's actual schema: blocking input accepts 2–3 options; that limit does not apply to every native control. Do not remove choices to make them fit.

An acknowledgment such as accepted:true records submitted delivery only, with no answer. Retain the pending question ID, step token and option keys; wait for the actual user selection or explicit typed reply before dependent work. The async picker can close at turn end or timeout: show the same numbered recovery choices before ending the turn unless the user has already answered. Never re-open it automatically, force Plan mode, probe forbidden tools, or keep it alive with sleeps or polling. A resolved UI event can be cleanup, not an answer. Do not bypass host policy rejection through another tool. When native input is unavailable or prohibited, use persistent numbered choices. Other hosts keep their permitted native controls and existing wait protocol.

Asking the admin's decisions

Show the complete save or delete proposal before the question, and perform that write only after explicit confirmation of the displayed proposal. An acknowledgment or preselected default never authorizes saving or deleting. Changes to the proposal require confirmation again.

Any question with a fixed set of answers goes through a structured user-input tool when one is available, rather than a numbered list in your prose. That covers scope, who can run the workflow, a yes/no, a pick from the catalog, and whether the draft is right. Put the decisions a step needs into one call instead of asking them one by one, make it the only tool call in that response, and wait for the answer. Otherwise ask the choices directly in one message and wait for the answer.

Keep prose for questions with no fixed answers: the Step 2 intake ("describe it in your own words"), or asking what to change after the admin picked "Change something". A proposal or plan you show before a question stays in your message text; the question carries only what you are asking and its options.

Authoring overrides requires the baseline

When an admin wants to override an existing workflow at a narrower scope (system → org, or org → team), you cannot infer the baseline steps from the catalog alone. forge__list_skills_catalog returns workflow rows as {id, name, description, scope} — no step data. Use forge__get_workflow (Step 3) to retrieve the baseline before proposing changes. Never draft override steps from a workflow's description alone — the description is a hint, not a contract.

Step 1: Fetch catalog + role gate (combined, before anything else)

Before asking anything, call forge__list_skills_catalog. The response shape:

{
  "skills":    [{ "id", "name", "description", "scope",
                  "default_confirmation", "applicable_expression",
                  "complexity_class", "complexity_task_type",
                  "needs", "consumes", "produces",
                  "required_capabilities", "skill_relevance_hint",
                  "write_effect": { "writes", "categories", "targets", "self_gated" } }, ...],
  "workflows": [{ "id", "name", "description", "scope" }, ...],
  "sdlc_stages": [{ "id", "label", "description", "sort_order", "color" }, ...],
  "caller":    {
    "orgRole", "orgId", "userId",
    "teams": [{ "id", "name", "description" }, ...],
    "team_scope_available": true | false | null  /* admins only */
  },
  "state_categories": [{ "id", "label", "description" }, ...],
  "capabilities": [{ "id", "label", "consuming", "producing",
                     "required_needs": ["<state category id>", ...] }, ...],
  "capability_prerequisites": { "<capability id>": ["<state category id>", ...] },
  "field_schema": {
    "workflow_preset": [ /* manifest entries — see Step 4 */ ],
    "preset_step":     [ /* manifest entries — see Step 4 */ ],
    "new_skill":       [ /* manifest entries — see Step 4 */ ]
  },
  "tailoring_rules":  { "version", "path", "rules": [{ "id", "rule" }], "limits" },
  "practice_profile": { "status", "profile", "skills_inventory" }  /* admins only */
}

How the org works. Follow every rule in tailoring_rules whenever you draft or change a workflow — they come from the server, so they can change without an update to this skill. When practice_profile.status is ok, start from it: your first question or proposal cites at least one of its facts. Require skills as the tailoring_rules say: one your project's own instructions require, on the step where they require it, or one from skills_inventory whose coverage is every; name one found in fewer repositories as "use when present". When you save, pass what you learned about how the team does the work as practice_facts beside workflow — [{ activity, fact, evidence: [...] }]. A fact without evidence is dropped.

Each skill row carries the authoring metadata (everything beyond id/name/description/scope). When you propose a step that references that skill, you may surface its applicable_expression to the admin as the suggested default ("the canonical gate for this skill in the system catalog is epic_key != null — keep it, change it, or clear it?"). The skill's applicable_expression is never silently inherited at save time — if the admin wants the suggestion, you copy it explicitly into the step. NULL on a per-step row is the runtime sentinel for "always run".

caller.teams is the set of teams you can scope a workflow to. Because this skill is admin-only (see the role gate just below), it lists every team in your org — not only the ones you belong to — so you can target a team without first joining it. Use it to offer scope targets and to map a user-supplied team name to its id when drafting a team-scoped workflow (see Step 4). caller.team_scope_available says whether your org's plan lets a workflow be scoped to a team at all: false means it does not, null means the plan could not be checked.

Check caller.orgRole immediately:

  • If caller.orgRole !== 'org:admin' (including null / missing): STOP immediately. Reply with exactly: "Creating workflows is restricted to organization administrators. Ask your Forge org admin to run this for you." Do NOT proceed to intent intake, do NOT draft a plan, and do NOT call forge__save_workflow.
  • If caller.orgId is null (stdio or unauthenticated session): STOP with the same message — workflow authoring requires an authenticated HTTP session with an org context.
  • Otherwise, continue — you already have the catalog you need for Step 3 (no second fetch).

Step 2: Intent intake (one question, free-form)

First — check whether Forge already knows why they're here.

If a Forge run recap earlier in this conversation ended with a friction insight, it already names what happened and what would fix it. It reads like:

Heads up on this run — you confirmed the estimate twice because a step re-asked something you'd already answered. Teams that hit this often set the steps your team already trusts to ask less. Want me to set that up for your team? Say "customize my workflow" — I'll carry over what just happened, so it should only take a question or two.

When that insight is in the conversation and the admin has just taken it up (e.g. they said "customize my workflow"), do not ask the intake question — it has already been answered. Forge observed the friction and promised to carry it over; making them restate it breaks that promise and is exactly the re-asking this exists to prevent. Take the intent as Authoring (Create path), treat the customization the insight proposed as their opening request, and go straight to Step 3.

Say what you're carrying over so the inference is visible, not silent — one line, e.g. "Starting from what I saw on that run: the estimate gate re-asked something you'd answered. I'll loosen that gate — tell me if I've got the wrong end of it." Then proceed. If they correct you, follow the correction.

If there is no such insight in the conversation, ask the normal question below. That is the ordinary case, not an error: the recap only produces an insight when a run actually hit fixable friction, and it lives in the conversation rather than in storage — so it does not survive into a new session. Never block on its absence, never mention it to the admin, and never guess at friction that was not handed to you.

Otherwise, ask the admin ONE open question and wait for the answer:

"What would you like to do — create a new workflow, or remove an existing override? Describe it in your own words."

One free-form turn is all you need. Do NOT walk through sequential wizard prompts.

Step 2b: Route by intent

Classify the answer against two paths. Look for verbs and objects:

  • Authoring (Create path) — "create", "build", "add", "set up", "author", "make a new", "customize", "define a new process". Proceed to Step 3 below (the Create path, Steps 3–10).
  • Deletion (Delete path) — "delete", "remove", "reset to default", "clear", "drop", "undo my override", "go back to the Forge default". Skip ahead to the Delete path at the end of this skill (Steps D1–D4).

If the intent is truly ambiguous ("change the bug workflow" — create a new one or modify/delete an existing one?), ask one clarifying question before routing, the way "How to ask the admin" describes. Do NOT assume.

Step 2c: Is authoring the right tool at all?

Authoring is the heavyweight option, and it is easy to reach for when a lighter one would do the same job for free. Settle this BEFORE Step 3.

A workflow you save here becomes your org's own copy of the step list. From then on that copy is what runs, and Forge's catalog no longer reaches it: a step added, removed or reordered in a later release, a changed gating condition, a revised default policy — none of it arrives. There is no re-sync and no drift warning; the only way back is to delete the override and lose the customization.

That trade is worth it for a genuinely different process. It is a bad trade for a change the dashboard's workflow drawer already makes per step, which records a narrow override and leaves everything else inheriting normally:

What the admin wantsRight tool
Change how much a step asks (Never asks / AI decides / Always asks)Drawer
Turn one step off, or back onDrawer
Reset a workflow's policies to the recommendation for their AI maturityDrawer
Different steps, a different order, or steps that do not exist yetAuthor here
A process the catalog has no workflow forAuthor here

If the intent is only the top three, say so plainly and point them at the drawer rather than authoring:

"That's a per-step setting — you can change it on the Workflows page without making your own copy of <workflow name>, which keeps it receiving Forge's updates."

Then ask the admin to choose "Change it on the Workflows page" or "Author a custom version anyway", the way "How to ask the admin" describes. Ask; do not decide for them. An admin who wants their own copy is entitled to one — the point is that they should be choosing it, not arriving at it because authoring was the only path offered.

Step 3: Match intent against the catalog you already fetched

Use the skills and workflows arrays from Step 1 — do NOT call forge__list_skills_catalog a second time.

  • If the catalog contains skills or workflows that semantically match the admin's intent, reuse them — reference existing skill_ids in your proposed steps, and/or pick the closest existing workflow as the baseline to clone from. When you clone, you may also clone the baseline workflow's per-step applicable_expression values verbatim (the runtime cascade never fills these in for you).
  • If the catalog returns no usable matches for the stated intent, proceed with a fully synthesised draft (all steps defined as new custom skills) and tell the admin: "I didn't find reusable skills in your catalog for this, so I'm drafting fresh custom steps."

Step 3a: Retrieve the baseline before drafting any override

When the admin's intent maps to an existing workflow id (e.g. they want to override check_status for org or team scope), call forge__get_workflow(workflow_id: "<id>") to fetch the baseline steps before drafting.

The cascade is automatic:

  • With no team_name / team_id → returns the cascade-resolved workflow at org scope (org preset if one exists, else system default).
  • With team_name: "<team>" → returns team preset → org preset → system default in cascade order.

Read the response's scope field to know which scope actually matched (system | org | team). steps_fallback_to_system: true means the row at that scope changes no steps: it only switches the workflow on or off, sets who can see it, or renames it, and every step follows the default. That is a normal state, not a broken override. steps_inherited: true is wider: the row saves no step list of its own, so its steps come from a parent (the built-in, or for a team the org's version), even when the row tweaks a step or two.

Skill metadata is included on each step (skill_name, skill_description, skill_default_confirmation, skill_suggested_expression) so you can render a readable baseline without a second skill lookup.

Keep the response's workflow.edition. An edit to this baseline is saved as step changes against it (Step 9), so you never have to send the whole workflow back.

If forge__get_workflow is not available in your environment, stop and ask the admin to enumerate the existing steps before drafting. Do NOT infer steps from the workflow's description.

Step 3b: Modify-vs-replace when an override already exists at the target scope

When forge__get_workflow reports scope equal to the target scope of the new save (i.e., org for an org override, or team for a team override on the requested team) and steps_fallback_to_system: false, an override already exists. The admin's intent is ambiguous:

  • Modify the existing override (most common). Baseline = the existing override's steps. forge__save_workflow will overwrite it.
  • Replace from scratch — discard the existing override and start fresh from the parent scope's default. Baseline = parent default (system default for an org override; org default — or system if no org preset — for a team override).

Ask the admin to choose between "Modify existing" and "Replace from scratch". If a structured user-input tool is available, use it as the only tool call in that response; otherwise ask the choice directly and wait for the answer.

Handle the answer:

  • "Modify existing" — proceed with the existing override as baseline.
  • "Replace from scratch" — call forge__delete_workflow to remove the existing override (the admin's choice here is the explicit go-ahead — do NOT add a second confirmation step), then re-call forge__get_workflow to fetch the now-cascade-resolved parent baseline. Use that as the new baseline.
  • Skip / empty answer — treat as "Modify existing" (the safer default; nothing is deleted on a skipped confirmation).

If no override yet exists at the target scope (scope returned was a broader scope than the target), there is nothing to ask — the returned baseline is already the correct starting point and you proceed straight to Step 4.

The same goes when scope equals the target scope but steps_fallback_to_system is true. That row changes no steps: it is what switching the workflow on, renaming it, restricting who sees it, or Reset to default leaves behind. There is no step override to modify or replace, so do not ask, and do not offer forge__delete_workflow: deleting the row undoes those settings and can switch the workflow off.

Note which way the update path runs in each case. An override that already exists at the target scope is ALREADY off the catalog's update path, so modifying or replacing it changes nothing about that — the trade was made when it was first authored. Creating the first override at a scope is what makes the trade. Step 2c is where that gets raised; do not re-litigate it here, and do not imply an existing override is still tracking the catalog.

Step 4: Draft the proposal — schema-driven

Guard: Never draft override steps from a workflow's description alone. If Step 3a/3b did not give you concrete baseline steps via forge__get_workflow (or you proceeded to a fully-synthesised draft per Step 3 because the catalog returned no usable match), stop and verify before continuing — silently dropping baseline steps because they weren't in your context is the failure mode this guard prevents.

Iterate field_schema.workflow_preset and field_schema.preset_step. Every editable field is described by a manifest entry of the shape:

{
  "name":        "applicable_expression",
  "type":        "expression",          // string | integer | boolean | enum | json | expression
  "required":    false,
  "nullable":    true,
  "nullMeans":   "always-applicable",   // explicit semantic for NULL (when nullable)
  "default":     <value>,               // omitted ⇒ no implicit default
  "values":      [...],                 // enum only
  "description": "Boolean expression gating this step…",
  "examples":    ["epic_key != null", "true"],
  "validator":   "expression",          // optional server-side validator name
  "editor":      "multiline",           // optional UI hint
  "notes":       [ ... ],               // optional clarifications
  "valuesFrom":  "catalog.skills[].id"  // optional pointer to where valid values live
}

For each manifest entry, decide what to do:

  • Required fields with no usable default — ask the admin.
  • Required fields that you can safely infer from intent (e.g. id from the workflow name) — propose the inference and let the admin override.
  • Optional fields — accept whatever default makes sense for the intent. Surface non-default decisions to the admin so they can override. When nullMeans is set on a nullable field, prefer the explicit value over NULL if you have one (e.g. write "true" for "always run" rather than NULL — both work, but the explicit form is clearer in audit logs).

When iterating field_schema.new_skill for any inline new_skills entries, fields with valuesFrom pointing at the catalog (e.g. sdlc_stages[].id) MUST take their value from the corresponding catalog list — never invent values. If the catalog list is empty (the stage list hasn't been populated for this org yet), leave the field NULL and note the gap to the admin.

For new skills specifically: when an admin creates or edits a skill, propose an SDLC stage before save, then let the admin confirm or override. The catalog response includes an sdlc_stages array; pick the stage whose description and label best match the skill's purpose based on its name, description, and (if drafted) its instructions. Render the proposal as part of the per-skill summary so the admin sees what was inferred and can change it in the same edit pass — do not silently set the value.

State needs: what a skill must read before it runs

For every new skill, decide its needs from what its instructions cannot run without, using the ids and descriptions in catalog.state_categories. Most skills need none. Do not infer needs from a broad workflow label or from the mere presence of a tracker or repository.

Three things decide whether a category is right:

  • It resolves inside the step, not before it. When a declared category is missing, Forge adds an instruction at the top of that step to resolve it (ask for the ticket key, scan the repository). The step's own {{field}} placeholders and its applicable_expression are evaluated first, so they cannot rely on what the step resolves. Reference those fields from a later step, or have the instructions read them after resolving.
  • work_item on the first step makes the workflow require a ticket. A request without one is refused at start, or the user is asked for a key. If the skill reads a ticket only when one is linked, leave work_item out. If a catalog skill that declares work_item must run first in a workflow that also runs without a ticket, set that step's step_options to { "workItemOptional": true }.
  • An override keeps its baseline. If the skill overrides a system skill, start with the baseline skill's needs (from catalog.skills) and retain them unless the admin explicitly changes the behavior. An omitted needs is saved empty; it does not inherit.

If the catalog has no state_categories (an older server), keep any baseline needs exactly as they are and do not add or drop categories you cannot check against the catalog.

Propose the resulting needs and why each applies alongside the skill summary, so the admin can correct it before saving.

Capabilities: what a skill READS beyond its own inputs

catalog.capabilities lists what this server can feed a skill at run time. Each entry carries an id, a label, and a consuming sentence saying when declaring it applies. A skill declares them in consumes (and, rarely, produces).

This matters most for skills that read requirements. Forge records where each repository actually keeps its specs, acceptance criteria and ADRs — some orgs use the project tracker, some keep them in-tree under a folder their admin declared. A skill that declares consumes: ["intent_source"] is told which, for the repo in hand, plus the root to read from and a check to run before trusting it. A skill that does not declare it gets no such guidance and will fall back to the tracker, silently, even for an org that told Forge otherwise.

So when a drafted skill's instructions say anything like "read the spec", "check the acceptance criteria", "find the requirements" or "consult the ADR", propose consumes: ["intent_source"] and say why in one line — the same way you propose an SDLC stage. Do not set it silently, and do not add it to a skill that only writes, asks questions, or routes.

Some capabilities only work with a state category in needs. catalog.capability_prerequisites maps each such capability to the categories it requires (the same values appear as required_needs on catalog.capabilities), and it also covers built-in capabilities that authoring never offers but an override carries over from its baseline. Add those categories to the skill's needs for every capability it consumes, and for a capability it produces when it reports a detection for the repository it is working in (a survey whose report names each repository itself is exempt). Add each category once, and explain it in plain language. If the admin does not want that read, revise the skill and its capability declaration together; do not leave a declared capability without its prerequisites.

Two rules:

  • Take ids from catalog.capabilities — never invent one. An id this server does not recognise is rejected at save (Step 10). That is deliberate: a declaration the orchestrator cannot resolve would be ignored at run time, leaving a skill that reads as capability-aware and is not.
  • produces is not the mirror of consumes. Declare it only when the skill genuinely reports the payload named in that entry's producing sentence. Most skills leave it empty.

When an admin overrides a system skill, carry the baseline skill's consumes and produces across unless they explicitly want them dropped — the same rule as needs. An override that loses them resolves with no capabilities and quietly does less than the skill it replaced.

SDLC stage: propose one for the WORKFLOW too, not just its skills

The workflow preset's sdlc_stage is the workflow-level peer of the per-skill field above, and it drives the stage badge, the stage grouping and the stage filter on the dashboard's Workflows page. It lives in field_schema.workflow_preset, so the same valuesFrom rule applies: take the value from the catalog's sdlc_stages array, never invent one.

Propose a stage whenever a workflow is created or edited, then let the admin confirm or override — the same posture as the per-skill proposal, rendered in the same summary.

Pick by the workflow's PURPOSE, not by counting its steps. This is the part that is easy to get wrong. Steps like receive_epic_handoff, gather_work_item_docs and session_feedback are scaffolding that appears across workflows of every kind, and they carry stages of their own — so a "most common step stage wins" rule reliably mislabels a workflow. A UI-mockup workflow, for instance, ties two planning scaffolding steps against its two real design steps, and a plurality rule would call it planning or nothing at all. Read the workflow's name, description and classifier_hint and ask what it is FOR.

Use other for admin, notification, onboarding and session-ops workflows that genuinely do not belong to a delivery stage — it is a first-class stage, not a dumping ground for uncertainty.

Omitting the field leaves it NULL, which means inherit: for an override of a built-in the system row's stage stands, and for a brand-new custom workflow it renders as other until someone sets it. Say which of those you are doing in the proposal summary rather than letting the admin discover it on the dashboard.

Scope and visibility: ask both together

Scope (the whole org, or one team) and admin_only are access control, not labels. Ask both; don't infer either. Ask them together — in the same structured user-input call when one is available, otherwise directly in one message — along with the draft check from Step 5 when the proposal is ready:

  • Scope — "Where should this workflow live?": "Whole org" (every team can run it), or a team by name (only that team can run it).
  • Access — "Who can run this workflow?": "Everyone (default)" (anyone in that scope can see and run it), or "Org admins only" (hidden from members; you can open it up later from the Workflows page).

When the plan has no team workflows, don't ask where it lives. If caller.team_scope_available is false, the org is not on the Team plan and a team-scoped save is refused. Drop the scope question: the workflow is for the whole org (send neither team_name nor team_id), and say so in one line of the proposal: "Your plan doesn't include team workflows, so this one is for the whole org." Ask the access question as usual. If the admin asked for a team anyway, tell them their plan doesn't include team workflows and offer the whole org instead. When it is true, or null or missing (the plan could not be checked, or an older server), ask about scope as below: never treat an unchecked plan as one without teams.

Take the team options from caller.teams. List "Whole org" plus the teams that fit the intent best, up to three, and let a typed answer cover the rest: match a typed team name against caller.teams, and ask again if it matches none. When the admin already named the scope, drop that question and ask only the rest.

When caller.teams is empty or missing, still ask; don't assume the workflow is org-wide. An empty list can mean the org has no teams, but also that the team lookup failed, and the catalog returns the same empty list either way. Say what you saw rather than claiming the org has none — "I couldn't find any teams in your org. Where should this workflow live?" — and offer two options: "Whole org" (every member can run it) or "One team" (they type its name, or you ask next).

For "Whole org", send neither team_name nor team_id. For a team, send the name as team_name; the save resolves it, and Step 10 handles team_not_found and team_store_unavailable.

Rules for admin_only:

  • Default is everyone. If the admin doesn't care or doesn't answer, omit the field (leave it NULL) and say so in the proposal summary. Omitting is not the same as sending false — see below.
  • Send true only on an explicit "admins only".
  • Do not confuse it with default_roles. default_roles (["eng", "pm"]) only recommends an audience; it restricts nothing. If an admin says "this is just for engineers", that is default_roles, NOT admin_only. If they say "members shouldn't be able to run this", that is admin_only: true.
  • Overriding a built-in: omit the field unless you mean to change it. NULL means inherit, so omitting keeps whatever the system default says. Sending an explicit false on an override of an admin-only built-in removes that restriction and exposes it to every member — only do that when the admin explicitly asks to open it up, and call it out in the Step 8 confirmation.
  • When you set or change admin_only, state the consequence plainly in the confirmation: an admin-only workflow disappears from members' catalogs entirely and cannot be started even if they name its id.

Findings preservation in custom skill instructions

When drafting a custom skill's instructions body, ask the admin a single question: does this skill produce substantive analytical output (a scan summary, dashboard, breakdown, findings table, etc.) before pausing for the user? If yes, the instruction body MUST teach its executor to populate a display_text field. Two patterns:

  • Pattern A — the skill completes with analysis the user should see: the executor's normal completion payload should include display_text: "<the analytical markdown the executor just produced>". The orchestrator strips it from persistent state and shows it as a ## Findings section above the next step (or the run recap), so the parent sees the findings even when a sub-agent only relays the response verbatim. Reference: receive_epic_handoff populates display_text with its Issue Context Brief.
  • Pattern B — the skill is a relayed-question skill (the executor emits status: "needs_input" to ask the user a domain question): include display_text in the same needs_input payload. The orchestrator renders it identically above the relayed-question CHECKPOINT body. Reference: architecture_discussion and technical_discovery populate display_text with their summary markdown before the "Looks good?" confirmation.

Why this matters: any analytical output rendered as the executor's own prose (before the forge__update_state tool call) dies on the sub-agent boundary — a delegated sub-agent's STEP BOUNDARY directive requires returning the orchestrator's CHECKPOINT body verbatim, and that body does NOT include the pre-tool-call prose unless the skill plumbs the findings through display_text. Without it, the parent agent has to re-derive the analysis from scratch (re-fetch the work item, re-scan the codebase) — the same token-waste / latency-spike this is designed to fix.

Keep display_text concise — target ≤500 tokens. It is a decision-context summary (headline + decision-relevant data), NOT a full duplicate of the markdown the skill rendered. The full content belongs in structured state fields the skill already populates (e.g. pending_summary_text, pending_codebase_summary, dashboard_summary, or skill-specific equivalents), which are recoverable via forge__get_workflow_state if the parent genuinely needs them. The orchestrator enforces a hard 8 KB cap and truncates oversized payloads with a marker pointing at the recovery channel — well-behaved skills never trip it, but the cap bounds runaway content regardless of skill author behaviour.

Skip display_text when the skill only asks user-intent questions ("Approve?", "Edit fields?", "Send it?") with no analytical output, or when the question text already inlines all the context the user needs. Empty padding hurts readability.

Surface this to the admin as part of the per-skill summary so they explicitly decide whether their skill needs the field — same posture as the SDLC stage proposal above. Put it in their terms ("this step shows findings, so they're kept if another agent runs it"), not as the field and payload it becomes.

Render the full proposal to the admin: workflow-level fields first, then the ordered step list. For each step, also show the resolved applicable_expression you've chosen and (if applicable) the canonical default you copied from the catalog skill.

Present the proposal in plain language

"Full" means every decision, not every byte you will send. The admin is approving what the workflow will do, so show it the way they would read it:

  • Describe a new skill; don't paste it. Say in a few sentences what its instructions tell the AI to do — what it reads, what it decides, what it shows, and whether it writes anything — and offer the exact text for review. Paste the full body only when they ask for it.
  • Never show the plumbing. The display_text and state fields a skill reports back, forge__save_workflow payloads and field_schema entries are how the workflow runs, not what the admin is deciding. Say what they achieve instead; don't show their shape.
  • Plain Markdown only. Tables, lists and headings render in every client. HTML such as <details> does not, and shows up as literal tags.

Field names still belong where the admin is choosing a value — sdlc_stage, confirmation_policy, admin_only — each next to what the value means.

Set an example_invocation — the copyable "try saying" hint shown on the dashboard. It MUST start with the Forge wake word forge, followed by a 3–10 word natural-language phrase (lowercase, leading with a verb that matches the workflow's primary action) — e.g. "forge, review the PR for PROJ-123", "forge, run a security review on PROJ-42". Apply the forge, prefix whether you synthesize the phrase or the admin supplies one; if the admin's phrasing omits it, add it before saving. (The server normalizes a missing prefix on save, but authoring it in keeps the approval preview accurate.)

No closing feedback step is required

Authored workflows do not need to end with a session_feedback step. Every run's recap already names the two ways to send feedback, so a separate closing step is optional. Do not append one yourself. When cloning a baseline that has one, keep it (it renders as KEEP in the Step 5 diff) unless the admin asks to drop it.

Step 5: Iterate conversationally

When proposing an override (not a from-scratch new workflow), render the diff against the baseline you retrieved in Step 3a/3b. List the baseline steps verbatim, then mark each step in your proposal as one of:

  • KEEP — unchanged from baseline
  • MODIFY — same skill, but gate / confirmation / options changed
  • INSERT — new step at this position
  • REMOVE — baseline step dropped from the override

Admins should always see what's being preserved vs. changed — silent omissions of baseline steps are the failure mode the Step 4 guard exists to prevent.

For from-scratch new workflows (no baseline), render the proposal without diff annotations.

Then ask whether the draft is right, as the last question alongside any decisions still open (scope and access on the first pass): "Does the rest of the draft look right?", with "Looks right" (go on to the final check and the save question) or "Change something" (they say what, or you ask next).

Accept free-form feedback, typed with the answer or in a later message ("make step 2 use Linear not Jira", "rename it", "add a step between 3 and 4", "drop the gate on step 4"), and update the draft. If they pick "Change something" without saying what, ask what to change in prose. Repeat until the admin is satisfied. Do NOT save yet.

Step 6: Referential validation

Before calling forge__save_workflow, walk every step and verify each skill_id resolves against either:

  • the skills array returned from forge__list_skills_catalog, or
  • the inline new_skills array you are about to create.

If any reference is unresolvable, surface it clearly:

"Step 3 references skill_id: xyz-123, but that skill isn't in your catalog and isn't one of the new skills we're creating. How should I resolve it?"

Do NOT call forge__save_workflow until every reference resolves.

For every inline new_skills entry, also check its needs: each id must exist in catalog.state_categories, and for every capability in its consumes (and a single-repository detection in its produces), every category listed for it in catalog.capability_prerequisites must be in the same skill's needs. If anything is missing, update the proposal and let the admin review the change before the Step 8 save question. If the catalog has no state_categories or capability_prerequisites (an older server), skip this check, keep any baseline needs as they are, and let the save response report problems.

Step 7: Reachability check on applicable_expression

For every step whose applicable_expression is non-NULL, scan the expression for state-key references. Common state keys: epic_key, story_count, execution_started, stories_created, ac_corrections_applied, bug_handoff_complete. Common phase flags: _has_local_code, _has_project_tracking, _has_messaging, _has_code_analysis, _workflow_active, _fb_engineering.

If a referenced key is never set by any step in your proposed workflow, that step will never run. Examples:

  • Reviewing a PR (review_pr flow) with applicable_expression: "execution_started == true" — review_pr never sets execution_started (that flag is set by begin_code_execution in implement_feature / fix_bug). The step is unreachable.
  • A step gated on story_count > 1 in a workflow that doesn't run epic_story_breakdown. Same problem.
  • A step gated on epic_key != null whose own skill declares needs: ["work_item"]. The gate is evaluated before the step resolves its needs, so on a run without a ticket the step is skipped and never asks for one. Only an EARLIER step can set a key a gate reads.

When you detect an unreachable step, do NOT save. Instead:

"Heads-up: step 4's gate execution_started == true references a flag that no step in this workflow ever sets, so step 4 will never run. Do you want to: (a) drop the gate (set to true), (b) replace it with a different expression, or (c) keep it as-is (the step will be permanently dormant)?"

Ask the admin for the choice. This is the structural fix for a whole class of misconfiguration — admins should never accidentally configure dormant steps because of an inherited expression they didn't mean to keep.

Step 7b: Confirm "Never asks" on a step that writes

A step's confirmation_policy decides how much it asks: auto (Never asks) answers its own questions and makes its writes without asking, ai_judgment (AI decides) asks before any write that would normally need approval, and required (Always asks) asks every question and approves its writes against a plan first.

For every step your proposal sets to auto — including one you kept from a baseline — look up its skill's write_effect in the catalog you fetched in Step 1 (catalog.skills[].write_effect). When write_effect.writes is true, do NOT save it silently. Ask once per such step, before the Step 8 save question, naming what it writes from write_effect.targets (or "your connected tools" when the list is empty): ""<Step name>" is set to Never asks, so it will write to <targets> without asking anyone. Keep it that way?"

Ask the admin to choose between "Switch to AI decides (Recommended)" — it asks before a write that would normally need approval — and "Keep Never asks" — it writes to <targets> without asking. If a structured user-input tool is available, use it as the only tool call in that response; otherwise ask the choice directly and wait for the answer.

  • Switch to AI decides — set that step's confirmation_policy to ai_judgment and include the change in the Step 8 plan.
  • Keep Never asks — keep auto, and say so in the Step 8 plan ("<Step name> writes without asking — you confirmed this").
  • Skip / empty answer — treat as "Switch to AI decides". Never keep a writing step on Never asks without an explicit answer.

A step whose skill does not write (write_effect.writes is false) needs no confirmation. This is the same confirmation the dashboard asks for when an admin sets Never asks on a step that writes.

Step 8: Explicit admin confirmation

Show the final structured plan — workflow fields, ordered steps (with skill_id and resolved applicable_expression), example_invocation, and any new_skills being created. Present it as Step 4's Present the proposal in plain language says: a new skill is described, not pasted.

Then state what saving does, in your own words, before the question. Which statement is true depends on what the save creates — the same override-or-new distinction Step 2c and Step 3b draw. A workflow id that is already in the Step 1 catalog, at any scope, makes this an override; an id no scope has makes it a brand-new workflow.

Saving an override of an existing workflow — state the durable consequence, with the same care Step D3 takes over deletion, for the same reason. Saving is the moment this workflow leaves the catalog's update path, and it is the one effect the admin cannot discover afterwards: there is no drift indicator anywhere in the product, so a copy that has fallen behind looks identical to one that has not.

"Saving makes this your org's own copy of <workflow name>. It will run exactly as configured here — but it stops receiving Forge's updates to that workflow: steps added, removed or reordered in a later release, changed gating, and revised default policies won't reach it. Nothing flags that later, and the only way back is to delete the override, which loses this configuration."

Saving a brand-new workflow — there is no Forge copy for it to fall behind, so the warning above would be false. Say this instead:

"This is a new workflow. It runs exactly as configured here, and there are no Forge updates it will miss — it changes only when you edit it."

Say it once, plainly, and do not repeat it after they answer. Then ask the admin to choose "Save", "Keep editing", or "Cancel". If a structured user-input tool is available, use it as the only tool call in that response; otherwise ask the choice directly and wait for the answer.

Handle the answer:

  • "Save" — proceed to Step 9 and call forge__save_workflow.
  • "Keep editing" — loop back to Step 5 for more iteration.
  • "Cancel" — end the session. Do NOT call forge__save_workflow.
  • Skip / empty answer — treat as "Keep editing" (never as "Save"); loop back to Step 5. Never save on a skipped confirmation.

If the admin closes the session without answering, that's fine — no partial state is persisted. Because forge__save_workflow is never called without explicit "Save" approval, an abandoned session leaves no trace in the Forge catalog.

Step 9: Save

Call forge__save_workflow with a payload whose top level matches the manifest's workflow_preset fields, and whose steps entries match the preset_step fields. The shape is data-driven by the manifest — include every field where the admin chose a non-default value. The server validates the payload against the schema before opening a transaction; structural mismatches come back as invalid_request with the offending field name (and step_index when the error is in a step).

Skeleton (the actual fields you include depend on the manifest):

{
  "workflow": {
    "id":          "...",
    "name":        "...",
    "description": "...",
    "team_name":   "...",
    "team_id":     "...",
    "example_invocation": "...",
    "classifier_hint":    "...",
    "auto_classify":      true,
    "enabled":            true,
    "hydrator_config":    {},
    "steps": [
      {
        "skill_id":              "...",
        "step_order":            0,
        "enabled":               true,
        "confirmation_policy":   "ai_judgment",
        "applicable_expression": "epic_key != null",
        "step_options":          {},
        "instructions":          null,
        "instruction_preamble":  null,
        "instruction_postamble": null,
        "required_local_skills": null
      }
    ]
  },
  "new_skills": [
    { "id": "...", "name": "...", "description": "...", "instructions": "..." }
  ]
}

Pass exactly one of team_id / team_name for team scope. Omit both for org scope. If both are supplied, team_id wins and team_name is ignored. Prefer team_name in AI-driven flows so the admin never has to look up a UUID.

On success ({ ok: true, workflow_id, scope }), tell the admin the workflow is live and can be reached via the AI classifier when a user's request matches the workflow's classifier_hint.

Editing a workflow that already exists: send only the changes

When the baseline came from forge__get_workflow (Modify existing in Step 3b) and the admin changed some of it, do NOT send the whole definition back. A long workflow cannot be retyped into one call: the client cuts it off before it reaches Forge. Send only what changes:

{
  "workflow": { "id": "...", "team_name": "...", "name": "only if it changes" },
  "base_edition": "<workflow.edition from the forge__get_workflow read>",
  "step_changes": [
    { "op": "remove", "step_order": 4, "skill_id": "..." },
    { "op": "update", "step_order": 2, "skill_id": "...", "fields": { "instructions": "...", "confirmation_policy": "required" } },
    { "op": "insert", "after_step_order": 6, "step": { "skill_id": "...", "instructions": "..." } }
  ]
}
  • workflow holds the id, the scope (team_name / team_id, as for a full save) and only the top-level fields that change. Never steps: send either steps or step_changes, not both. A field you leave out keeps the value saved now, even if it changed after your read.
  • To change only top-level fields (a rename, a new description), send "step_changes": [].
  • Each change names a step by its step_order in the read, not as shifted by the changes above it. skill_id on a remove or update is a check that the step runs that skill; include it.
  • An update sends only the step fields that change. Everything else about the step stays as it is.
  • after_step_order: -1 inserts first. A new step starts with the defaults for any field it leaves out.
  • At most 200 changes per save. The reply carries the new edition and changes_applied; send further changes against that edition.
  • Changes edit only a workflow saved at the scope they target: read it with the same team_name / team_id you save with, and check that the read's scope is that scope. A read that falls back to the org's or the built-in version (a team or org saving its first version of someone else's workflow) is a full save, not step changes. When the read is that scope's but steps_inherited is true, its steps come from a parent: "step_changes": [] can still change its top-level fields, but changing a step needs one full save there first.

Send a full definition (the skeleton above) only for a brand-new workflow, a first version at this scope, after "Replace from scratch", or when most steps change. A long new workflow can be saved short, then extended with insert changes in a few saves, each using the edition the previous save returned.

Step 9a: Surface any soft warnings

When the success response also includes a warnings: [...] array, the save committed but the server flagged something the admin should know about before runtime. The warning is non-blocking — the workflow IS live — but each entry deserves a sentence to the admin so they can self-correct in a follow-up edit pass.

{
  "ok": true,
  "workflow_id": "...",
  "scope": "org",
  "warnings": [
    {
      "code": "missing_display_text_guidance",
      "field": "new_skills.instructions",
      "skill_id": "...",
      "message": "..."
    }
  ]
}

Currently the server emits one warning code:

Warning codeWhat it meansOffer
missing_display_text_guidanceA new_skills.instructions body OR a workflow.steps.instructions full override emits needs_input without mentioning display_text — at runtime, any analytical output the executor renders before the tool call will die on the sub-agent boundaryTell the admin which skill / step is affected (use the skill_id / step_order fields), summarise the risk in plain language, and offer to add display_text guidance in a quick follow-up forge__save_workflow call. If the admin says "no, this skill genuinely doesn't produce findings worth surfacing", accept and move on — the warning is soft by design.

Render the warnings inline with the success message, e.g.:

"✅ Workflow security_review_strict saved at org scope.

One heads-up: your custom skill internal_compliance_check emits needs_input but doesn't mention display_text. If the skill scans the codebase or produces a structured findings table before asking the auditor for approval, those findings will be lost when a delegated sub-agent only relays the CHECKPOINT back to the parent."

Then ask the admin to choose "Add display_text guidance" or "Leave it; it only routes", the way "How to ask the admin" describes.

Do NOT block or re-prompt for save approval on a warning — the workflow already committed. The follow-up edit (if the admin chooses one) goes through the normal authoring loop.

Step 10: Handle structured errors

If forge__save_workflow returns isError: true with a structured envelope, explain it plainly and offer to adjust:

Error codeWhat it meansOffer
admin_requiredCaller is not an org adminSTOP — not recoverable in this session
invalid_requestPayload failed schema validationShow field (and step_index if present), fix the value, retry. For new_skills.needs: replace each id in unknown_needs with one from available_needs, and for each { capability, requires } entry in missing_prerequisites, add the requires category to that skill's needs. For new_skills.consumes / new_skills.produces: replace each id in unknown_capabilities with one from authorable_capabilities, or drop the declaration. Show the admin any changed reads before retrying.
team_not_in_orgTeam scope but the supplied team_id does not belong to the caller's orgPick a team in your own org, or use org scope
entitlement_requiredTeam scope, but the org's plan doesn't include team workflowsSay so plainly and offer to save for the whole org; don't retry the team
team_not_foundteam_name did not match any team in the orgShow available_teams and ask which to use
team_store_unavailableteam_name used but team store is not wiredAsk the admin to pass team_id directly
duplicate_idWorkflow or skill id already existsRename and retry
changed_since_readThe edit was built against an edition that is no longer saved (someone else changed its fields or steps in between); nothing was savedRead it again with forge__get_workflow, rebuild the changes against the new read, and if what changes is now different, show the admin before saving
not_saved_at_scopeStep changes sent for a scope with no saved version of this workflow (inherits_from names the one it uses), or one whose steps are inherited (missing: "steps"); nothing was savedSave it at that scope once in full (Step 9's skeleton), then edit it with step changes; for top-level fields alone, "step_changes": [] works on an inherited-steps version
step_not_found / step_mismatchA step change names a step_order the read did not have, or a step that runs another skillCheck the change against the read (change_index names it) and fix it; nothing was saved
not_foundNo workflow with that id at any scopeCheck the id against the catalog
team_overrides_affectedAn org edit would put a different step at a number a team has its own step setting for (affected_team_overrides names each team and step)Tell the admin which teams and steps, then ask the admin to choose, the way "How to ask the admin" describes: "Insert after the last step instead" (Recommended: no team's step numbers change), "Reset <team> to the org version and save" (that team loses every setting it has for this workflow, not just this step; the reset and the edit happen together or not at all, and you ask again before saving), or "Something else". No tool clears one team step setting, and the dashboard's drawer only changes it, so changing it there leaves the save refused. On a reset: confirm it with Step D3's team-scoped wording, then send the same step changes with the same base_edition plus reset_teams listing each team's team_name from affected_team_overrides. Never reset the team first with forge__delete_workflow: that reset stays even if the edit is then refused. The reply's teams_reset lists each team reset; if the save is refused, no team was reset
empty_resultThe changes would remove every stepKeep at least one step, or remove the override with forge__delete_workflow (Step D1)
referential_errorA skill_id doesn't resolve on the serverFix the reference and retry
constraint_violationGeneric DB constraint failedShow the server message and ask admin

Never silently retry — always loop the admin in.

Tone

Brief, direct, collaborative. No "Step 1 of 7" framing, no filler. Show the draft, invite feedback, iterate, confirm, save.


Delete Path

Reached from Step 2b when the admin wants to remove an existing org- or team-scoped override. The shared Step 1 (catalog + role gate) still runs first — the admin gate is identical for both paths.

Step D1: Match the target against the catalog

Use the workflows array from Step 1. Filter to items where scope !== 'system' — system defaults are never deletable, and the store enforces this. Surface the scope explicitly on each candidate so the admin sees what will be reset:

  • scope: 'org' — the org-level override. Removing it deletes the org's configuration for that workflow. For most built-ins it also stops the workflow running: they ship disabled, so with no org row there is nothing left to turn it on. A launch-set built-in (the get_workflow system row shows enabled: true) keeps running.
  • scope: 'team' — a team-level override. Removing it reverts that one team to the org default, which usually keeps the workflow running. If the org has no preset of its own, the team falls all the way to the system default and the workflow stops running for that team too.

If the admin named a workflow directly (e.g. "delete the build-feature override for team Alpha"), resolve the target by id + scope and skip to Step D3.

If the reference is ambiguous — multiple matches, or the admin said something fuzzy like "remove my bug workflow override" — ask the admin to choose from the filtered candidates. Include each workflow name, scope, id, and team name when relevant.

If the filtered list is empty ("you don't have any overrides in your org or teams"), stop and say so — nothing to delete.

Step D2: Resolve team scope (team-scoped targets only)

If the target is team-scoped, confirm which team. If the admin named the team directly, match against caller.teams by name. If the name is ambiguous or missing, ask them to pick from caller.teams, the same way the Step 4 scope question lists teams.

Prefer team_name over team_id when invoking the MCP tool — the server resolves the name for you (team names are unique within an org).

Step D3: Explicit admin confirmation

Before calling forge__delete_workflow, show the final target and the consequence, then ask for explicit confirmation.

State the consequence for the target's ACTUAL scope — the two outcomes differ materially, and a single sentence covering both misleads on one of them. Never describe an org-scoped removal as a "reset to the Forge default" or a "revert to Forge's configuration": Forge's built-ins ship disabled, so for an org override that phrasing promises a restore and delivers a shutdown, on an action you are in the same breath telling the admin cannot be undone.

Org-scoped target:

"Removing this deletes your org's configuration for <workflow name>, and <workflow name> will also STOP RUNNING — it ships disabled, so with no org configuration there's nothing left to turn it on. You can re-enable it from the dashboard, or re-author the override here, but this removal cannot be undone."

For a launch-set built-in (its system row is enabled: true), say instead that it falls back to Forge's default and keeps running.

Team-scoped target:

"Removing this makes <team name> fall back to your org's configuration for <workflow name>; the team keeps running it on the org's settings. This cannot be undone, but you can re-author the team override later."

If the target is team-scoped AND the org has no preset of its own (no scope: 'org' row for that id in the Step 1 catalog), use the org wording instead — the team falls through to the system default and the workflow stops running for that team.

Ask the admin to choose "Remove override" or "Cancel". If a structured user-input tool is available, use it as the only tool call in that response; otherwise ask the choice directly and wait for the answer.

Handle the answer:

  • "Remove override" — proceed to Step D4.
  • "Cancel" — end the session; do NOT call forge__delete_workflow.
  • Skip / empty answer — treat as "Cancel". Never delete on a skipped confirmation.

Step D4: Call forge__delete_workflow

Call with:

{
  "workflow_id": "<from catalog>",
  "team_id":     "<uuid, optional>",
  "team_name":   "<name, optional>"
}

Pass exactly one of team_id / team_name for team-scoped deletes. Omit both for the org-scoped delete. If both are supplied, team_id wins.

On success ({ ok: true, workflow_id, scope }), tell the admin the override is removed and the scope now runs the parent default.

Delete path — structured errors

If forge__delete_workflow returns isError: true, explain it plainly and offer the right recovery:

Error codeWhat it meansOffer
admin_requiredCaller is not an org adminSTOP — not recoverable in this session
org_requiredNo org context on the sessionSTOP — ask admin to sign in to an org
invalid_requestworkflow_id missing or blankLoop back to Step D1 and resolve the target
team_not_in_orgTeam scope but the supplied team_id belongs to a different orgPick a team in your own org, or use org scope
team_not_foundteam_name did not match any team in the orgShow available_teams and ask which to use
team_store_unavailableteam_name was used but the team store is not wiredAsk the admin to pass team_id directly
system_default_protectedAttempted to delete a system default (store-enforced)STOP — system defaults cannot be deleted
not_foundNo preset exists at the requested scope for that idNothing to delete; the admin may be confused about scope — confirm before proceeding
store_unavailableThe workflow config store is not connectedSTOP — infrastructure issue, not the admin's problem
internal_errorUnexpected server failureShow the message and ask the admin to retry

Never silently retry. Always loop the admin in.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.