Skip to content

feat: sync issue template tags/categories from scripts.json - #65

Merged
pfeerick merged 6 commits into
mainfrom
chore/schema-driven-issue-templates
Aug 4, 2026
Merged

feat: sync issue template tags/categories from scripts.json#65
pfeerick merged 6 commits into
mainfrom
chore/schema-driven-issue-templates

Conversation

@pfeerick

@pfeerick pfeerick commented Aug 4, 2026

Copy link
Copy Markdown
Member

Summary

  • category and tags were hardcoded in four separate places (validate_scripts.py, issue_to_scripts.py, and both issue form templates) and had already drifted once (e.g. the battery checkbox was added to only one template). scripts.schema.json is now the single canonical source, with tools/sync_issue_template_options.py regenerating both templates' marker-bounded option blocks and the schema's examples fields from actual usage in scripts.json.
  • Tags are a pure usage snapshot (a tag that stops being used drops out on the next sync). Categories are monotonic add-only — they drive the gallery's top-level nav tabs, so one hitting zero current entries is never auto-pruned by default, only flagged via an unused_categories CI notice. A new --prune-categories flag lets a maintainer deliberately remove currently-unused categories on demand (--check --prune-categories to preview, --write --prune-categories to apply), rather than requiring a hand-edit of the schema JSON.
  • Both issue templates gain a free-text "New Category" field (mirroring "Additional Tags"), since the dropdown previously had no way to introduce a new value — issue_to_scripts.py no longer hard-rejects unrecognized categories.
  • .vscode/settings.json associates scripts.schema.json for editor-time validation/autocomplete on scripts.json.
  • Tags/categories sort case-insensitively (str.casefold) so e.g. "Games & Fun" sorts before "GPS & Mapping" as a human would expect, rather than plain ASCII order.

CI (validate-issue-templates.yml)

  • check (PRs): read-only --check, gates on drift.
  • sync (push): self-heals same-repo branches by running --write and committing the correction back — this is what lands the fix directly in the PR that introduced a new tag/category, including direct-edit PRs that never went through an issue form. Uses GITHUB_TOKEN, so it can't push to forks and (deliberately) can't trigger further workflow runs, avoiding recursion.
  • suggest-fix-for-fork-prs (pull_request_target, fork PRs only): GITHUB_TOKEN can never push to a fork under any trigger, so instead this safely computes the same fix (checks out the base branch's trusted script, only ever treats the PR's scripts.json as inert data, never executes anything from the fork) and posts it as a sticky PR comment with both resolution paths — a maintainer pulling the branch in, or running --write locally and committing it.
  • All four workflows in the repo (including the three pre-existing ones, not just this PR's new one) have enable-cache: false added to their setup-uv steps — there's no lock/requirements file for uv to key a cache on, so it was warning that the cache could never invalidate.

Test plan

  • uv run tools/test_sync_issue_template_options.py — 10 unit tests covering multi-region splicing, monotonic category retention vs. tag pruning, --prune-categories, drift detection, and idempotency
  • uv run tools/validate_scripts.py --scripts-json scripts.json — 58 entries valid, no new errors
  • uv run tools/sync_issue_template_options.py --check — in sync
  • issue_to_scripts.py fixture runs: dropdown-only, free-text-only, both (free text wins), neither (correctly rejected)
  • YAML/JSON validity of all new/changed files
  • Live verification of the pull_request_target sticky-comment job against a real fork PR (can't be simulated locally)

🤖 Generated with Claude Code

pfeerick and others added 6 commits August 4, 2026 10:31
Category and tag lists were hardcoded in four places (validate_scripts.py,
issue_to_scripts.py, and both issue form templates) and had already drifted
once. scripts.schema.json is now the single canonical source: tags and
categories are auto-promoted from actual usage in scripts.json (tags are a
pure usage snapshot; categories are monotonic add-only since they drive the
site's nav tabs and shouldn't vanish just because they're briefly empty).

tools/sync_issue_template_options.py regenerates the schema's examples and
both templates' marker-bounded option blocks, with --check/--write modes and
tests. A new "New Category" free-text field mirrors the existing "Additional
Tags" field, since the dropdown previously had no way to introduce a new
value. CI (validate-issue-templates.yml) self-heals same-repo branches on
push, gates PRs with --check, and — since GITHUB_TOKEN can never push to a
fork — posts a sticky comment with the diff and self-resolve instructions on
fork PRs via a carefully scoped pull_request_target job.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
No lock/requirements file exists in this repo for setup-uv to key a cache
on, so it was warning that the cache could never invalidate. There's nothing
to actually cache (the tools/*.py scripts are stdlib-only), so disable it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same fix as the issue-template workflow — no lock/requirements file exists
for setup-uv to key a cache on, so it was warning the cache could never
invalidate. Nothing to cache here either.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Completes the same fix for gh-pages.yml and script-submission.yml — no
lock/requirements file exists for setup-uv to key a cache on in this repo,
so it was warning the cache could never invalidate. Nothing to cache here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Plain sorted() put 'GPS & Mapping' before 'Games & Fun' (uppercase 'P' <
lowercase 'a' in ASCII) — not the order a human reading the dropdown would
expect. Also fixes a related bug the case-insensitive change would otherwise
introduce: the schema-vs-current comparisons re-sorted both sides before
comparing, which hid ordering drift in scripts.schema.json's own examples
arrays (they're supposed to already hold the canonical sorted list, so a
stale on-disk order needs to be caught, not normalized away).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Removing a stale, currently-unused category previously meant hand-editing
scripts.schema.json's examples array directly. --prune-categories overrides
the normal monotonic-add-only behavior for a single invocation, removing
categories with zero current scripts.json entries from the schema and both
templates in the same pass as a normal --write.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@pfeerick pfeerick changed the title Sync issue template tags/categories from scripts.json feat: sync issue template tags/categories from scripts.json Aug 4, 2026
@pfeerick
pfeerick merged commit dfd11cf into main Aug 4, 2026
10 checks passed
pfeerick added a commit that referenced this pull request Aug 4, 2026
…#69)

The push job in validate-issue-templates.yml was supposed to self-heal
bot-created branches, but script-submission.yml's branch-creating push uses
the default GITHUB_TOKEN, and GitHub Actions suppresses new workflow-
triggering events (including push) for GITHUB_TOKEN-authored pushes — the
same rule that prevents the sync job's own corrective commits from causing
an infinite loop also silently prevented it from ever running in the first
place for these branches. Confirmed against two real submissions (#67, #68):
both landed with a permanently-failing check and no auto-fix.

Rather than fight the retrigger suppression, run the sync script directly
in script-submission.yml before committing, so the schema/templates are
already correct in the very first commit of every bot-generated PR. The
push job in validate-issue-templates.yml remains as a backstop for
genuine human-authored pushes (e.g. direct-edit PRs), which aren't subject
to this suppression.

Also fixes a stale error-comment string referencing the category enum
check that was removed in #65.

Co-authored-by: Peter Feerick <5500713+pfeerick@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant