Files
github-spec-kit/workflows/PUBLISHING.md
Zied Jlassi f846d6526c fix(workflows): validate requires keys and reject phantom permissions gate (#3079)
* fix(workflows): validate requires keys and reject phantom permissions gate

A workflow's `requires` block was parsed but its keys were never
validated, so a typo or an unsupported key was silently ignored. Most
importantly, authors could write `requires.permissions.shell: true`
expecting a runtime capability gate — but no such gate exists: a `shell`
step always runs with the user's privileges. The declaration gave a
false sense of sandboxing.

`validate_workflow` now accepts only the recognised keys
(`speckit_version`, `integrations`, `tools`, `mcp`) and rejects anything
else, with an explicit error for `requires.permissions` pointing authors
to `gate` steps for approval. Docs and the model comment are updated to
state that `requires` is advisory, not a security boundary.

- Reject non-mapping `requires`, unknown keys, and `requires.permissions`
- Clarify workflows reference + PUBLISHING.md shell-step guidance
- Tests for valid keys, non-mapping, unknown key, and permissions

Signed-off-by: Zied Jlassi <6190550+zied-jlassi@users.noreply.github.com>
Assisted-by: AI

* fix(workflows): address review feedback on requires validation

Follow-up to the review on #3079:

- Guard `requires` validation on `is not None` instead of truthiness so a
  falsy non-mapping value (e.g. `requires: []` or `requires: ''`) is
  reported as an error instead of being silently skipped; `requires:`
  (YAML null) is still treated as an omitted block. Add a regression test.
- Reword the workflows security note so `requires.permissions` is shown
  as rejected/unsupported rather than as a valid example of `requires`.
- Standardize on US spelling (`_RECOGNIZED_REQUIRES_KEYS`, "recognized")
  to match the surrounding code and ease searching.
- Tighten the permissions-rejection test to assert on specific message
  markers (`requires.permissions` and the `gate` guidance) so it fails if
  the validation path or wording drifts.

Assisted-by: AI
Signed-off-by: Zied Jlassi (Architect AI) <6190550+zied-jlassi@users.noreply.github.com>

* fix(workflows): scope requires validation to workflow keys (drop tools/mcp)

tools and mcp belong to the bundle manifest requires schema (bundler/models/manifest.py, resolved in bundler/services/resolver.py), not the workflow requires validated here. Drop them from _RECOGNIZED_REQUIRES_KEYS and revert the PUBLISHING.md claim that this PR had introduced, so workflow requires only recognizes speckit_version and integrations.

This keeps the existing docs accurate and resolves the inline doc-consistency review comments.

Signed-off-by: Zied Jlassi <6190550+zied-jlassi@users.noreply.github.com>

* refactor(workflows): type WorkflowDefinition.requires as Any pre-validation

self.requires holds the raw parsed value, which before validate_workflow()
runs may be a non-mapping (None for a bare 'requires:', a list for
'requires: []', etc.). Annotating it dict[str, Any] was misleading for
editors/type-checkers; use Any and document that validate_workflow() enforces
the mapping shape.

Addresses Copilot review feedback on engine.py.

Signed-off-by: Zied Jlassi <6190550+zied-jlassi@users.noreply.github.com>

* fix(workflows): reject YAML-null requires: as a non-mapping

Address Copilot review: validate requires the same way as inputs. A
bare requires: parses as YAML null and was previously treated as an
omitted block, which is inconsistent with inputs and lets a stray
requires: line be silently ignored.

Drop the is-not-None guard and check isinstance(..., dict) directly: an
omitted block still defaults to {} (valid), but a present-but-non-mapping
value -- YAML null, [] or '' -- is now an authoring error that surfaces.

Tests: add YAML-null rejection + an omitted-is-still-valid guard test.
Signed-off-by: Zied Jlassi <6190550+zied-jlassi@users.noreply.github.com>

---------

Signed-off-by: Zied Jlassi <6190550+zied-jlassi@users.noreply.github.com>
Signed-off-by: Zied Jlassi (Architect AI) <6190550+zied-jlassi@users.noreply.github.com>
2026-06-24 14:49:43 -05:00

10 KiB

Workflow Publishing Guide

This guide explains how to publish your workflow to the Spec Kit workflow catalog, making it discoverable by specify workflow search.

Table of Contents

  1. Prerequisites
  2. Prepare Your Workflow
  3. Submit to Catalog
  4. Verification Process
  5. Release Workflow
  6. Best Practices

Prerequisites

Before publishing a workflow, ensure you have:

  1. Valid Workflow: A working workflow.yml that passes specify workflow run validation
  2. Git Repository: Workflow hosted on GitHub (or other public git hosting)
  3. Documentation: README.md with description, inputs, and step graph
  4. License: Open source license file (MIT, Apache 2.0, etc.)
  5. Versioning: Semantic versioning in the workflow.version field
  6. Testing: Workflow tested on real projects

Prepare Your Workflow

1. Workflow Structure

Host your workflow in a repository with this structure:

your-workflow/
├── workflow.yml               # Required: Workflow definition
├── README.md                  # Required: Documentation
├── LICENSE                    # Required: License file
└── CHANGELOG.md               # Recommended: Version history

2. workflow.yml Validation

Verify your definition is valid:

schema_version: "1.0"

workflow:
  id: "your-workflow"              # Unique lowercase-hyphenated ID
  name: "Your Workflow Name"       # Human-readable name
  version: "1.0.0"                 # Semantic version
  author: "Your Name or Organization"
  description: "Brief description (one sentence)"
  integration: claude              # Default integration (optional)
  model: "claude-sonnet-4-20250514"         # Default model (optional)

requires:
  speckit_version: ">=0.6.1"
  integrations:
    any: ["claude", "gemini"]      # At least one required

inputs:
  spec:
    type: string
    required: true
    prompt: "Describe what you want to build"
  scope:
    type: string
    default: "full"
    enum: ["full", "backend-only", "frontend-only"]

steps:
  - id: specify
    command: speckit.specify
    input:
      args: "{{ inputs.spec }}"

  - id: review
    type: gate
    message: "Review the output."
    options: [approve, reject]
    on_reject: abort

Validation Checklist:

  • id is lowercase alphanumeric with hyphens (single-character IDs are allowed)
  • version follows semantic versioning (X.Y.Z)
  • description is concise
  • All step IDs are unique
  • Step types are valid: command, prompt, shell, gate, if, switch, while, do-while, fan-out, fan-in
  • Required fields present per step type (e.g., condition for if, expression for switch)
  • Input types are valid: string, number, boolean
  • Step IDs do not contain : (reserved for engine-generated nested IDs like parentId:childId)

3. Test Locally

# Run with required inputs
specify workflow run ./workflow.yml --input spec="Build a user authentication system with OAuth support"

# Check validation
specify workflow info ./workflow.yml

# Resume after a gate pause
specify workflow resume <run_id>

# Check run status
specify workflow status <run_id>

4. Create GitHub Release

Create a GitHub release for your workflow version:

git tag v1.0.0
git push origin v1.0.0

The raw YAML URL will be:

https://raw.githubusercontent.com/your-org/spec-kit-workflow-your-workflow/v1.0.0/workflow.yml

5. Test Installation from URL

specify workflow add your-workflow
# (once published to catalog)

Submit to Catalog

Understanding the Catalogs

Spec Kit uses a dual-catalog system:

  • catalog.json — Official, verified workflows (install allowed by default)
  • catalog.community.json — Community-contributed workflows (discovery only by default)

All community workflows should be submitted to catalog.community.json.

1. Fork the spec-kit Repository

git clone https://github.com/YOUR-USERNAME/spec-kit.git
cd spec-kit

2. Add Workflow to Community Catalog

Edit workflows/catalog.community.json and add your workflow.

⚠️ Entries must be sorted alphabetically by workflow ID. Insert your workflow in the correct position within the "workflows" object.

{
  "schema_version": "1.0",
  "updated_at": "2026-04-10T00:00:00Z",
  "catalog_url": "https://raw.githubusercontent.com/github/spec-kit/main/workflows/catalog.community.json",
  "workflows": {
    "your-workflow": {
      "id": "your-workflow",
      "name": "Your Workflow Name",
      "description": "Brief description of what your workflow automates",
      "author": "Your Name",
      "version": "1.0.0",
      "url": "https://raw.githubusercontent.com/your-org/spec-kit-workflow-your-workflow/v1.0.0/workflow.yml",
      "repository": "https://github.com/your-org/spec-kit-workflow-your-workflow",
      "license": "MIT",
      "requires": {
        "speckit_version": ">=0.15.0"
      },
      "tags": [
        "category",
        "automation"
      ],
      "created_at": "2026-04-10T00:00:00Z",
      "updated_at": "2026-04-10T00:00:00Z"
    }
  }
}

3. Submit Pull Request

git checkout -b add-your-workflow
git add workflows/catalog.community.json
git commit -m "Add your-workflow to community catalog

- Workflow ID: your-workflow
- Version: 1.0.0
- Author: Your Name
- Description: Brief description
"
git push origin add-your-workflow

Pull Request Checklist:

## Workflow Submission

**Workflow Name**: Your Workflow Name
**Workflow ID**: your-workflow
**Version**: 1.0.0
**Repository**: https://github.com/your-org/spec-kit-workflow-your-workflow

### Checklist
- [ ] Valid workflow.yml (passes `specify workflow info`)
- [ ] README.md with description, inputs, and step graph
- [ ] LICENSE file included
- [ ] GitHub release created with raw YAML URL
- [ ] Workflow tested end-to-end with `specify workflow run`
- [ ] All gate steps have clear review messages
- [ ] Input prompts are descriptive
- [ ] Added to workflows/catalog.community.json (alphabetical order)

Verification Process

After submission, maintainers will review:

  1. Definition validation — valid workflow.yml, correct schema
  2. Step correctness — all step types used correctly, no dangling references
  3. Input design — clear prompts, sensible defaults and enums
  4. Security — no malicious shell commands, safe operations
  5. Documentation — clear README explaining what the workflow does and when to use it

Once verified, the workflow appears in specify workflow search.


Release Workflow

When releasing a new version:

  1. Update version in workflow.yml
  2. Update CHANGELOG.md
  3. Tag and push: git tag v1.1.0 && git push origin v1.1.0
  4. Submit PR to update version and url in workflows/catalog.community.json

Best Practices

Step Design

  • Use gates at decision points — place gate steps after each major output so users can review before proceeding
  • Keep steps focused — each step should do one thing; prefer more steps over complex single steps
  • Provide clear gate messages — explain what to review and what approve/reject means

Inputs

  • Use descriptive prompts — the prompt field is shown to users when running the workflow
  • Set sensible defaults — optional inputs should have defaults that work for the common case
  • Constrain with enums — when there's a fixed set of valid values, use enum for validation
  • Type appropriately — use number for counts, boolean for flags, string for names

Shell Steps

  • Shell runs with the user's privileges — a shell step executes a local command directly; there is no capability sandbox. requires is an advisory pre-condition block (recognised keys: speckit_version, integrations), not a runtime permission gate — there is no requires.permissions. Gate sensitive commands explicitly with a gate step.
  • Avoid destructive commands — don't delete files or directories without explicit confirmation via a gate
  • Quote variables — use proper quoting in shell commands to handle spaces
  • Check exit codes — shell step failures stop the workflow; make sure commands are robust

Security: shell steps execute arbitrary code

Workflow shell steps execute their run field through /bin/sh (POSIX) or the platform shell. There is no sandbox between the step and the user's machine: a malicious or buggy run block can read environment variables, modify files outside the project, exfiltrate data, or escalate privileges.

Catalog-listed workflows are reviewed at submission time (see Verification Process), but you should still treat every install as code-execution from an untrusted source until you have read the workflow.yml:

  • Before installing a workflow, fetch the raw YAML and audit every shell step's run field directly. specify workflow info <name> only shows metadata (name, version, inputs, step IDs/types) — not the shell content that would actually execute.
  • Prefer explicit commands over interpolation in run blocks: {{ inputs.something }} substitutions should be quoted and constrained via enum so a malicious input can't inject shell syntax.
  • Limit privilege: shell steps inherit the user's environment. Workflows that need elevated access (sudo, secrets, GitHub tokens) should call them out explicitly in the README so reviewers can spot the requirement.
  • Authors: if your workflow has shell steps that look risky out of context (deletions, network calls, credential reads), document the rationale in your README. Maintainers will reject submissions whose shell steps can't be justified at review time.

Integration Flexibility

  • Set integration at workflow level — use the workflow.integration field as the default
  • Allow per-step overrides — let individual steps specify a different integration if needed
  • Document required integrations — list which integrations must be installed in requires.integrations

Expression References

  • Only reference prior steps — expressions like {{ steps.plan.output.file }} only work if plan ran before the current step
  • Use default filter{{ val | default('fallback') }} prevents failures from missing values
  • Keep expressions simple — complex logic should be in shell steps, not expressions