From 3e39b50f3c5048b2cafc84692bc886a1b59ec76b Mon Sep 17 00:00:00 2001 From: Manfred Riem <15701806+mnriem@users.noreply.github.com> Date: Mon, 22 Jun 2026 17:14:34 -0500 Subject: [PATCH 1/4] Require preset-usage README with Spec Kit CLI syntax in submissions Tighten the community preset submission workflow so it validates the README referenced by the documentation field rather than merely checking for a root README. The workflow now fails submissions whose linked README lacks a valid 'specify preset add ...' command and flags monorepo submissions that point documentation at a generic root README. - Add a required Documentation URL field to the preset issue template - Add validation step 2d (documentation README + CLI-syntax check) to .github/workflows/add-community-preset.md and recompile the lock file - Document the stricter usage-README requirement and reviewer content check in presets/PUBLISHING.md Closes #3103 Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/preset_submission.yml | 14 ++++++ .../workflows/add-community-preset.lock.yml | 2 +- .github/workflows/add-community-preset.md | 33 ++++++++++++-- presets/PUBLISHING.md | 45 +++++++++++++++++-- 4 files changed, 87 insertions(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/preset_submission.yml b/.github/ISSUE_TEMPLATE/preset_submission.yml index 19244e6651..a0a28e23e1 100644 --- a/.github/ISSUE_TEMPLATE/preset_submission.yml +++ b/.github/ISSUE_TEMPLATE/preset_submission.yml @@ -77,6 +77,18 @@ body: validations: required: true + - type: input + id: documentation + attributes: + label: Documentation URL + description: | + Link to the README that explains how to use **this preset** (not a general product/framework pitch). + Prefer the preset-scoped README (e.g. `presets//README.md` in a monorepo) over the repository root README. + It must contain at least one valid `specify preset ...` install command matching the Download URL above. + placeholder: "https://github.com/your-org/spec-kit-presets/blob/main/your-preset/README.md" + validations: + required: true + - type: input id: license attributes: @@ -177,6 +189,8 @@ body: required: true - label: README.md with description and usage instructions required: true + - label: Linked README explains how to use this preset and includes a valid `specify preset add ...` command matching the download URL + required: true - label: LICENSE file included required: true - label: GitHub release created with version tag diff --git a/.github/workflows/add-community-preset.lock.yml b/.github/workflows/add-community-preset.lock.yml index 9aec9914f1..5372810ca6 100644 --- a/.github/workflows/add-community-preset.lock.yml +++ b/.github/workflows/add-community-preset.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"b4ba1db5fdec754fa825cc3160879924118bc454a781eed70ef6c90beab83a95","body_hash":"392ace500b7cb9b0aa6b020d150841de398bcbcfe54dbad729f0d860d698bde2","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"b4ba1db5fdec754fa825cc3160879924118bc454a781eed70ef6c90beab83a95","body_hash":"dfacaadcf6509cd141c6d99bffcdd205f4f2533c47964c45a257d678e7a018fe","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]} # This file was automatically generated by gh-aw (v0.79.8). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/add-community-preset.md b/.github/workflows/add-community-preset.md index bc2a8115e1..68f01e4a06 100644 --- a/.github/workflows/add-community-preset.md +++ b/.github/workflows/add-community-preset.md @@ -73,6 +73,7 @@ fields): | Author | `author` | Yes | | Repository URL | `repository` | Yes | | Download URL | `download-url` | Yes | +| Documentation URL | `documentation` | Yes | | License | `license` | Yes | | Required Spec Kit Version | `speckit-version` | Yes | | Required Extensions | `required-extensions` | No | @@ -103,14 +104,40 @@ deciding pass/fail: - Confirm the repository contains a `README.md` file - Confirm the repository contains a `LICENSE` file -### 2d. Release and download URL validation +### 2d. Documentation README validation + +The `documentation` field must point to the README that explains **how to use this +preset** — not just any file named `README.md`, and not a product/framework pitch. + +- Fetch the **exact URL** in the `documentation` field (convert GitHub `blob` URLs to + their `raw.githubusercontent.com` equivalent before fetching) and confirm it resolves + to a readable Markdown file. +- **Validate that the README contains a valid Spec Kit CLI install command.** The fetched + README must contain at least one `specify preset add ...` invocation. The strongest + signal is the catalog-install form whose URL matches the submitted **Download URL**: + - `specify preset add --from ` (preferred — the URL must match exactly), or + - `specify preset add `, or + - `specify preset add --dev ` + + If **no** `specify preset add ...` command is present, the README is treated as a generic + description/pitch rather than preset-usage documentation — **fail this check** and tell the + submitter to add a valid install command (ideally `specify preset add --from `). +- **Prefer a preset-scoped README in monorepos.** If `documentation` resolves to a generic + repository-root README in a monorepo (the preset lives in a subdirectory such as + `presets//` and a preset-scoped README exists there), **flag it** in your comment and + recommend the submitter point `documentation` at the preset-scoped README + (e.g. `presets//README.md`) so the catalog surfaces usage instead of marketing. Treat + this as a flag rather than a hard failure **only if** the root README still contains a valid + `specify preset add ...` command for this preset; otherwise it fails check 2d above. + +### 2e. Release and download URL validation - The download URL should follow the pattern `https://github.com///archive/refs/tags/v.zip` or `https://github.com///releases/download//.zip` - Verify a GitHub release exists matching the submitted version -### 2e. Submission checklists +### 2f. Submission checklists - Confirm that all required checkboxes in the Testing Checklist and Submission Requirements sections are checked (`[x]`) @@ -154,7 +181,7 @@ Insert the entry in **alphabetical order by preset ID** within the "repository": "", "download_url": "", "homepage": "", - "documentation": "", + "documentation": "", "license": "", "requires": { "speckit_version": "" diff --git a/presets/PUBLISHING.md b/presets/PUBLISHING.md index 661614e5c0..fbafa20ad1 100644 --- a/presets/PUBLISHING.md +++ b/presets/PUBLISHING.md @@ -19,7 +19,7 @@ Before publishing a preset, ensure you have: 1. **Valid Preset**: A working preset with a valid `preset.yml` manifest 2. **Git Repository**: Preset hosted on GitHub (or other public git hosting) -3. **Documentation**: README.md with description and usage instructions +3. **Documentation**: A preset-scoped README.md that explains how to use **this preset**, including a valid `specify preset add ...` install command (see [Usage README requirements](#6-usage-readme-requirements)) 4. **License**: Open source license file (MIT, Apache 2.0, etc.) 5. **Versioning**: Semantic versioning (e.g., 1.0.0) 6. **Testing**: Preset tested on real projects with `specify preset add --dev` @@ -147,6 +147,38 @@ https://github.com/your-org/spec-kit-preset-your-preset/archive/refs/tags/v1.0.0 specify preset add --from https://github.com/your-org/spec-kit-preset-your-preset/archive/refs/tags/v1.0.0.zip ``` +### 6. Usage README Requirements + +The catalog `documentation` field must point at a README that explains how to use +**this preset** — not a product pitch for a broader framework or a separate CLI. The +submission workflow validates the linked README and **fails** submissions that don't +meet these requirements: + +- **Point `documentation` at the preset-scoped README.** In a monorepo where the preset + lives in a subdirectory (e.g. `presets//`), link the README inside that directory + (`presets//README.md`) rather than the repository-root README. The root README is + often a marketing/overview page; the catalog should surface preset usage instead. (Keep + this usage README **outside** the release ZIP — it should be discoverable *before* a user + downloads the artifact.) +- **Include a valid Spec Kit CLI install command.** The linked README must contain at least + one `specify preset add ...` invocation. Preferably use the catalog-install form whose URL + matches your Download URL: + + ```bash + specify preset add --from https://github.com///releases/download/vX.Y.Z/-X.Y.Z.zip + ``` + + `specify preset add ` and `specify preset add --dev ` are also accepted, but the + `--from ` form is the clearest signal that the README documents this exact + preset release. +- **Cover the minimum structure** so a reader can decide whether the preset fits: + - What the preset does / what it provides + - The install command using Spec Kit CLI syntax (above) + - When to use it / when not to use it + +A submission whose linked README lacks a valid `specify preset add ...` command is treated +as a generic description rather than usage documentation and will be flagged for changes. + --- ## Submit to Catalog @@ -186,6 +218,7 @@ Edit `presets/catalog.community.json` and add your preset. "version": "1.0.0", "download_url": "https://github.com/your-org/spec-kit-preset-your-preset/archive/refs/tags/v1.0.0.zip", "repository": "https://github.com/your-org/spec-kit-preset-your-preset", + "documentation": "https://github.com/your-org/spec-kit-preset-your-preset/blob/main/README.md", "license": "MIT", "requires": { "speckit_version": ">=0.1.0" @@ -242,7 +275,7 @@ git push origin add-your-preset ### Checklist - [ ] Valid preset.yml manifest -- [ ] README.md with description and usage +- [ ] Preset-scoped README with a valid `specify preset add ...` command, linked from `documentation` - [ ] LICENSE file included - [ ] GitHub release created - [ ] Preset tested with `specify preset add --dev` @@ -263,7 +296,13 @@ After submission, maintainers will review: 2. **Template quality** — templates are useful and well-structured 3. **Command coherence** — commands reference sections that exist in templates 4. **Security** — no malicious content, safe file operations -5. **Documentation** — clear README explaining what the preset does +5. **Documentation** — the README linked from `documentation` explains how to use *this* preset and contains a valid `specify preset add ...` command + +> **Reviewer note:** the workflow can mechanically check *structure* (the linked README +> resolves, contains a valid `specify preset add ...` snippet, and the download URL matches), +> but whether the README genuinely documents *this* preset is partly a content judgment. A +> human reviewer should still confirm the linked doc isn't just a funnel to a separate +> product or CLI before approving. Once verified, `verified: true` is set and the preset appears in `specify preset search`. From e0ab77f0af8931a42afd3fa3c00692ea638e8870 Mon Sep 17 00:00:00 2001 From: Manfred Riem <15701806+mnriem@users.noreply.github.com> Date: Tue, 23 Jun 2026 06:46:41 -0500 Subject: [PATCH 2/4] Align preset README docs with workflow's actual enforcement Address PR review feedback on #3104: - PUBLISHING.md: clarify that only README resolution + a valid 'specify preset add ...' command are mechanically enforced; the preset-scoped-README and minimum-structure items are reviewer expectations, not automated checks. - PUBLISHING.md: state that a missing 'specify preset add ...' command is a hard validation failure (check 2d), not just 'flagged for changes'. - preset_submission.yml: require 'specify preset add ...' (not the looser 'specify preset ...') to match the workflow validation. Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/preset_submission.yml | 2 +- presets/PUBLISHING.md | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/preset_submission.yml b/.github/ISSUE_TEMPLATE/preset_submission.yml index a0a28e23e1..98bfca9321 100644 --- a/.github/ISSUE_TEMPLATE/preset_submission.yml +++ b/.github/ISSUE_TEMPLATE/preset_submission.yml @@ -84,7 +84,7 @@ body: description: | Link to the README that explains how to use **this preset** (not a general product/framework pitch). Prefer the preset-scoped README (e.g. `presets//README.md` in a monorepo) over the repository root README. - It must contain at least one valid `specify preset ...` install command matching the Download URL above. + It must contain at least one valid `specify preset add ...` install command (ideally `specify preset add --from `) matching the Download URL above. placeholder: "https://github.com/your-org/spec-kit-presets/blob/main/your-preset/README.md" validations: required: true diff --git a/presets/PUBLISHING.md b/presets/PUBLISHING.md index fbafa20ad1..2319eb6408 100644 --- a/presets/PUBLISHING.md +++ b/presets/PUBLISHING.md @@ -150,9 +150,13 @@ specify preset add --from https://github.com/your-org/spec-kit-preset-your-prese ### 6. Usage README Requirements The catalog `documentation` field must point at a README that explains how to use -**this preset** — not a product pitch for a broader framework or a separate CLI. The -submission workflow validates the linked README and **fails** submissions that don't -meet these requirements: +**this preset** — not a product pitch for a broader framework or a separate CLI. + +The submission workflow **mechanically enforces** two of these requirements: the linked +README must resolve to a readable file, and it must contain at least one valid +`specify preset add ...` command. The remaining items (preferring a preset-scoped README, +covering the minimum structure) are expectations a human reviewer checks — follow them so +your submission isn't sent back for changes. - **Point `documentation` at the preset-scoped README.** In a monorepo where the preset lives in a subdirectory (e.g. `presets//`), link the README inside that directory @@ -160,9 +164,9 @@ meet these requirements: often a marketing/overview page; the catalog should surface preset usage instead. (Keep this usage README **outside** the release ZIP — it should be discoverable *before* a user downloads the artifact.) -- **Include a valid Spec Kit CLI install command.** The linked README must contain at least - one `specify preset add ...` invocation. Preferably use the catalog-install form whose URL - matches your Download URL: +- **Include a valid Spec Kit CLI install command** *(enforced)*. The linked README must + contain at least one `specify preset add ...` invocation. Preferably use the + catalog-install form whose URL matches your Download URL: ```bash specify preset add --from https://github.com///releases/download/vX.Y.Z/-X.Y.Z.zip @@ -176,8 +180,8 @@ meet these requirements: - The install command using Spec Kit CLI syntax (above) - When to use it / when not to use it -A submission whose linked README lacks a valid `specify preset add ...` command is treated -as a generic description rather than usage documentation and will be flagged for changes. +A submission whose linked README lacks a valid `specify preset add ...` command **fails +validation** (workflow check 2d) and will not be added until corrected. --- From da15c1831b88012897c1256c0647afc677944e2d Mon Sep 17 00:00:00 2001 From: Manfred Riem <15701806+mnriem@users.noreply.github.com> Date: Tue, 23 Jun 2026 07:35:02 -0500 Subject: [PATCH 3/4] Tighten preset README validation and docs per PR review Address PR review feedback on #3104: - Workflow Step 2c: drop the generic repo-root README.md check so the README requirement is enforced exactly once, in Step 2d, against the file the documentation field points to (avoids monorepo false-positive). - Workflow Step 2d: restrict the documentation URL to GitHub-hosted README URLs (github.com/.../blob/... or raw.githubusercontent.com/...) before fetching user-provided input. - PUBLISHING.md: add the required 'id' field to the example catalog entry. - preset_submission.yml: fix the Documentation URL placeholder to match the recommended monorepo presets//README.md pattern. - Recompile add-community-preset.lock.yml (body hash only). Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/preset_submission.yml | 2 +- .github/workflows/add-community-preset.lock.yml | 2 +- .github/workflows/add-community-preset.md | 9 ++++++++- presets/PUBLISHING.md | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/preset_submission.yml b/.github/ISSUE_TEMPLATE/preset_submission.yml index 98bfca9321..f1ffc7e912 100644 --- a/.github/ISSUE_TEMPLATE/preset_submission.yml +++ b/.github/ISSUE_TEMPLATE/preset_submission.yml @@ -85,7 +85,7 @@ body: Link to the README that explains how to use **this preset** (not a general product/framework pitch). Prefer the preset-scoped README (e.g. `presets//README.md` in a monorepo) over the repository root README. It must contain at least one valid `specify preset add ...` install command (ideally `specify preset add --from `) matching the Download URL above. - placeholder: "https://github.com/your-org/spec-kit-presets/blob/main/your-preset/README.md" + placeholder: "https://github.com/your-org/spec-kit-presets/blob/main/presets/your-preset/README.md" validations: required: true diff --git a/.github/workflows/add-community-preset.lock.yml b/.github/workflows/add-community-preset.lock.yml index 5372810ca6..2a79c54dee 100644 --- a/.github/workflows/add-community-preset.lock.yml +++ b/.github/workflows/add-community-preset.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"b4ba1db5fdec754fa825cc3160879924118bc454a781eed70ef6c90beab83a95","body_hash":"dfacaadcf6509cd141c6d99bffcdd205f4f2533c47964c45a257d678e7a018fe","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"b4ba1db5fdec754fa825cc3160879924118bc454a781eed70ef6c90beab83a95","body_hash":"40a7720e0fe5e74dd1439b6f4c212c0e86c8f16a84073c23698b532c18a17032","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]} # This file was automatically generated by gh-aw (v0.79.8). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/add-community-preset.md b/.github/workflows/add-community-preset.md index 68f01e4a06..3428c8671e 100644 --- a/.github/workflows/add-community-preset.md +++ b/.github/workflows/add-community-preset.md @@ -101,14 +101,21 @@ deciding pass/fail: ### 2c. Repository validation - Fetch the repository URL — confirm it exists and is publicly accessible - Confirm the repository contains a `preset.yml` file -- Confirm the repository contains a `README.md` file - Confirm the repository contains a `LICENSE` file +> The README requirement is enforced once, in **Step 2d**, against the specific file the +> `documentation` field points to — not a generic repository-root `README.md`. This avoids +> the monorepo false-positive where a root README exists but isn't the preset-usage doc. + ### 2d. Documentation README validation The `documentation` field must point to the README that explains **how to use this preset** — not just any file named `README.md`, and not a product/framework pitch. +- **Restrict the URL to GitHub before fetching.** The `documentation` value is + user-provided input. Only accept GitHub-hosted README URLs — `https://github.com///blob//` + or `https://raw.githubusercontent.com////`. If the URL points + anywhere else (or isn't a URL), **fail this check** and do not fetch it. - Fetch the **exact URL** in the `documentation` field (convert GitHub `blob` URLs to their `raw.githubusercontent.com` equivalent before fetching) and confirm it resolves to a readable Markdown file. diff --git a/presets/PUBLISHING.md b/presets/PUBLISHING.md index 2319eb6408..4db9d5c781 100644 --- a/presets/PUBLISHING.md +++ b/presets/PUBLISHING.md @@ -217,6 +217,7 @@ Edit `presets/catalog.community.json` and add your preset. "presets": { "your-preset": { "name": "Your Preset Name", + "id": "your-preset", "description": "Brief description of what your preset provides", "author": "Your Name", "version": "1.0.0", From 843d7002d37e741aad3d5f8a3fc9861b898c9b0b Mon Sep 17 00:00:00 2001 From: Manfred Riem <15701806+mnriem@users.noreply.github.com> Date: Tue, 23 Jun 2026 08:26:47 -0500 Subject: [PATCH 4/4] Refine preset README validation rules per PR review Address PR review feedback on #3104: - Workflow Step 2d: broaden the documentation URL allowlist to also accept github.com/.../raw/... URLs; strip any fragment/query before fetching so the target is deterministic; clarify that a 'specify preset add --from ' command only counts when its URL matches the submitted Download URL (a different --from URL does not satisfy the requirement, though other accepted forms still can). - PUBLISHING.md: show both accepted download URL shapes (tag archive and release asset) in the README install example instead of implying only the releases/download form. - preset_submission.yml: remove the ambiguous generic 'README.md with description and usage instructions' checkbox; the linked-README requirement is the single source of truth. - Recompile add-community-preset.lock.yml (body hash only). Assisted-by: GitHub Copilot (model: Claude Opus 4.8, autonomous) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/ISSUE_TEMPLATE/preset_submission.yml | 4 +-- .../workflows/add-community-preset.lock.yml | 2 +- .github/workflows/add-community-preset.md | 32 +++++++++++++------ presets/PUBLISHING.md | 3 ++ 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/preset_submission.yml b/.github/ISSUE_TEMPLATE/preset_submission.yml index f1ffc7e912..02c72c9eea 100644 --- a/.github/ISSUE_TEMPLATE/preset_submission.yml +++ b/.github/ISSUE_TEMPLATE/preset_submission.yml @@ -187,9 +187,7 @@ body: options: - label: Valid `preset.yml` manifest included required: true - - label: README.md with description and usage instructions - required: true - - label: Linked README explains how to use this preset and includes a valid `specify preset add ...` command matching the download URL + - label: Linked README (Documentation URL) explains how to use this preset and includes a valid `specify preset add ...` command matching the download URL required: true - label: LICENSE file included required: true diff --git a/.github/workflows/add-community-preset.lock.yml b/.github/workflows/add-community-preset.lock.yml index 2a79c54dee..c4b19298f6 100644 --- a/.github/workflows/add-community-preset.lock.yml +++ b/.github/workflows/add-community-preset.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"b4ba1db5fdec754fa825cc3160879924118bc454a781eed70ef6c90beab83a95","body_hash":"40a7720e0fe5e74dd1439b6f4c212c0e86c8f16a84073c23698b532c18a17032","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"b4ba1db5fdec754fa825cc3160879924118bc454a781eed70ef6c90beab83a95","body_hash":"3952bd62908616e275f8485b4ef1dbb1eb64b688c4c98735138deb2eb91dcd55","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]} # This file was automatically generated by gh-aw (v0.79.8). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/add-community-preset.md b/.github/workflows/add-community-preset.md index 3428c8671e..b46ed3895d 100644 --- a/.github/workflows/add-community-preset.md +++ b/.github/workflows/add-community-preset.md @@ -113,22 +113,34 @@ The `documentation` field must point to the README that explains **how to use th preset** — not just any file named `README.md`, and not a product/framework pitch. - **Restrict the URL to GitHub before fetching.** The `documentation` value is - user-provided input. Only accept GitHub-hosted README URLs — `https://github.com///blob//` - or `https://raw.githubusercontent.com////`. If the URL points - anywhere else (or isn't a URL), **fail this check** and do not fetch it. -- Fetch the **exact URL** in the `documentation` field (convert GitHub `blob` URLs to - their `raw.githubusercontent.com` equivalent before fetching) and confirm it resolves - to a readable Markdown file. + user-provided input. Only accept GitHub-hosted README URLs: + - `https://github.com///blob//` + - `https://github.com///raw//` + - `https://raw.githubusercontent.com////` + + If the URL points anywhere else (or isn't a URL), **fail this check** and do not fetch it. +- Fetch the **exact URL** in the `documentation` field. First strip any fragment (`#...`) + or query string (`?...`) — these are common when copying from the browser UI and must be + ignored so the fetch target is deterministic. Then convert GitHub `blob`/`raw` web URLs to + their `raw.githubusercontent.com` equivalent before fetching, and confirm it resolves to a + readable Markdown file. - **Validate that the README contains a valid Spec Kit CLI install command.** The fetched README must contain at least one `specify preset add ...` invocation. The strongest signal is the catalog-install form whose URL matches the submitted **Download URL**: - - `specify preset add --from ` (preferred — the URL must match exactly), or + - `specify preset add --from ` (preferred), or - `specify preset add `, or - `specify preset add --dev ` - If **no** `specify preset add ...` command is present, the README is treated as a generic - description/pitch rather than preset-usage documentation — **fail this check** and tell the - submitter to add a valid install command (ideally `specify preset add --from `). + A `specify preset add --from ` command only counts when its `` **matches the + submitted Download URL exactly**. A `--from` command pointing at a *different* URL does + **not** satisfy the install-command requirement (treat it as if absent) — but the README + may still pass on one of the other accepted forms (`specify preset add ` or + `specify preset add --dev `). + + If **no** accepted `specify preset add ...` command is present, the README is treated as a + generic description/pitch rather than preset-usage documentation — **fail this check** and + tell the submitter to add a valid install command (ideally + `specify preset add --from `). - **Prefer a preset-scoped README in monorepos.** If `documentation` resolves to a generic repository-root README in a monorepo (the preset lives in a subdirectory such as `presets//` and a preset-scoped README exists there), **flag it** in your comment and diff --git a/presets/PUBLISHING.md b/presets/PUBLISHING.md index 4db9d5c781..a18f334051 100644 --- a/presets/PUBLISHING.md +++ b/presets/PUBLISHING.md @@ -169,6 +169,9 @@ your submission isn't sent back for changes. catalog-install form whose URL matches your Download URL: ```bash + # is the same URL you submit as the catalog Download URL — + # either the tag archive or a release asset, e.g.: + specify preset add --from https://github.com///archive/refs/tags/vX.Y.Z.zip specify preset add --from https://github.com///releases/download/vX.Y.Z/-X.Y.Z.zip ```