Skip to content

feat(discussion): add support for Discussion Templates - #14017

Draft
jamietanna wants to merge 2 commits into
cli:trunkfrom
jamietanna:feat/discussion-templates
Draft

feat(discussion): add support for Discussion Templates#14017
jamietanna wants to merge 2 commits into
cli:trunkfrom
jamietanna:feat/discussion-templates

Conversation

@jamietanna

@jamietanna jamietanna commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Description

Per #14015, it would be useful to present the user with an interactive
prompt when authoring a Discussion on a repository that has Discussion
Template(s) in place.

This builds on top of code from
https://github.com/jamietanna/gh-discussion (Apache-2.0) and rewritten
in the style of the project by Claude Sonnet 5.

Closes #14015.

How did you test this change?

# running it against a repo with Discussions enabled, but no templates
% go run ./cmd/gh discussion create  --repo JamieTanna-Mend-testing/renovatebot-renovate-discussions-42829
? Discussion title Example
? Discussion category Announcements
? Discussion body [(e) to launch nvim]
# ^^ not using a template

# running it against a repo with Discussions enabled + a template
% go run ./cmd/gh discussion create  --repo JamieTanna-Mend-testing/discussion-closing
? Discussion title Example
? Discussion category Mend Hosted Request
? What would you like help with? Upgrade to the Community (OSS) plan
? Please share the URL to the organisation you're creating this request for https://example.com
Please provide any relevant information about your project, such as the license(s) used by your project.

? Additional information [(e) to launch nvim, enter to skip]
...
# ^^ uses the form

Key points

Notes for reviewers

Authorship and follow-up

Who wrote this:

  • A human wrote it.
  • An agent wrote it under close human direction.
  • An agent wrote it independently, and no human has guided the implementation beyond the initial prompt.

Who answers review comments:

  • @jamietanna will read and reply directly. Name the account.
  • An agent will draft replies and @username will read them before they are posted.
  • Nobody has explicitly committed to replying.

@github-actions github-actions Bot added external pull request originating outside of the CLI core team needs-triage needs to be reviewed labels Jul 30, 2026
Per cli#14015, it would be useful to present the user with an interactive
prompt when authoring a Discussion on a repository that has Discussion
Template(s) in place.

This builds on top of code from
https://github.com/jamietanna/gh-discussion (Apache-2.0) and rewritten
in the style of the project by Claude Sonnet 5.

Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
@jamietanna
jamietanna force-pushed the feat/discussion-templates branch from 5d75422 to c513704 Compare July 30, 2026 13:07
@jamietanna
jamietanna marked this pull request as ready for review July 30, 2026 13:19
@jamietanna
jamietanna requested a review from a team as a code owner July 30, 2026 13:19
@jamietanna
jamietanna requested review from Copilot and tidy-dev July 30, 2026 13:19
@github-actions github-actions Bot added unmet-requirements and removed needs-triage needs to be reviewed labels Jul 30, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your pull request! Unfortunately, it doesn't meet the requirements for review:

  • None of the referenced issues have the help wanted label

Please update your PR to address the above. This PR will be automatically closed in 4 days if these requirements are not met.

Full contribution requirements
  1. Include a detailed description of what this PR does
  2. Link to an issue with the help wanted label (use Fixes #123 or Closes #123)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for GitHub Discussion Category Forms (“discussion templates”) to gh discussion create, enabling an interactive, field-by-field prompt when a repository defines a .github/DISCUSSION_TEMPLATE/<category-slug>.yml file for the selected discussion category.

Changes:

  • Introduces YAML parsing + prompting logic for Discussion Category Forms (markdown blocks, input, textarea, dropdown, checkboxes).
  • Fetches the category form via the REST “contents” endpoint and uses it to generate a web-UI-like markdown body when creating discussions interactively.
  • Adds unit tests covering template parsing, fetching, and the updated create flow.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
pkg/cmd/discussion/shared/template.go Adds form schema parsing, remote fetching, and interactive prompting to assemble a discussion body from a category form.
pkg/cmd/discussion/shared/template_test.go Adds tests for YAML unmarshalling, REST fetch behavior, and prompt-to-body rendering.
pkg/cmd/discussion/create/create.go Wires template prompting into gh discussion create when --body/--body-file isn’t provided.
pkg/cmd/discussion/create/create_test.go Extends create tests with HTTP stubs and a template-present scenario.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +56 to +63
func (e *TemplateElement) UnmarshalYAML(value *yaml.Node) error {
var shape struct {
Type string `yaml:"type"`
Attributes yaml.Node `yaml:"attributes"`
Validations struct {
Required bool `yaml:"required"`
} `yaml:"validations"`
}
Comment on lines +52 to +54
If the selected category defines a Discussion Category Form (a
%[1]s.github/DISCUSSION_TEMPLATE/<category>.yml%[1]s file), and %[1]s--body%[1]s/%[1]s--body-file%[1]s
was not given, the form's fields are prompted for individually instead of a single free-text body.
Comment on lines +210 to +214
func promptBody(opts *CreateOptions, repo ghrepo.Interface, categorySlug string) (string, error) {
httpClient, err := opts.HttpClient()
if err != nil {
return "", err
}
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

pkg/cmd/discussion/shared/template.go:277

  • For optional dropdowns, the skip check compares the selected label to the literal string "Skip". If a repository template legitimately includes an option labeled "Skip", selecting it would be treated as “leave blank” and silently discarded. Safer to detect skip by index (only when the extra option was appended) rather than by string value.
		if err != nil {
			return "", err
		}
		if options[idx] == skipOption {
			return "", nil

@babakks

babakks commented Jul 30, 2026

Copy link
Copy Markdown
Member

@jamietanna, I mentioned I'm going to close the PR for now, but I'm also happy to keep it open if you're okay with turning it into a draft.

@babakks
babakks requested review from babakks and removed request for tidy-dev July 30, 2026 14:06
@jamietanna
jamietanna marked this pull request as draft July 30, 2026 14:37
saeedkolivand added a commit to saeedkolivand/crosskit that referenced this pull request Jul 31, 2026
Checking the pull request head out means this job executes that PR's code —
install lifecycle scripts included — while holding a subscription token,
`pull-requests: write` and `actions: write`. The owner gate does not cover
that. It decides who may TRIGGER a review, not whose code then runs, and the
ordinary sequence is the dangerous one: a stranger opens a pull request, the
owner reads it and comments `@claude review`.

`ci.yml` builds fork pull requests safely because `pull_request` hands it a
read-only token and no secrets. This job has both, and `actions: write` widens
it further, since a poisoned cache entry outlives the run and is restored by a
later job on `main`.

So the head must be a branch of this repository. The `issue_comment` payload
cannot answer that — `issue.pull_request` carries only URLs — so it is resolved
through the API in a step that runs before anything is checked out, and the
job stops with a readable error otherwise. Reviewing a fork stays possible by
hand, which is what it was before this branch.

Resolving the SHA rather than trusting `refs/pull/N/head` also closes the
window between the check and the checkout, since that ref moves when the
contributor pushes.

Verified both directions against live pull requests: this one resolves to
saeedkolivand/crosskit and passes, and cli/cli#14017 resolves to jamietanna/cli
and takes the exit path.

Install scripts stay enabled rather than `--ignore-scripts`, now that the code
is this repository's: an install that differs from the one `ci.yml` performs
would have the review measuring something other than the gate, which is the
failure this branch exists to fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
saeedkolivand added a commit to saeedkolivand/crosskit that referenced this pull request Jul 31, 2026
* ci: let the PR review run the code instead of only reading it

Every review this cycle closed with the same line: "I could not run the gate:
this checkout has no node_modules and pnpm install isn't in this job's allowed
tools." So every finding was reasoned from the diff — and in this repo the
questions that matter most are the ones reading cannot settle. Does the
assertion still fail when the fix is reverted? Does a green jsdom test survive a
real browser? Both have hidden a real bug behind a passing suite here.

Dependencies are installed and packages built in the workflow rather than by the
agent: the lockfile stays frozen, the pnpm store cache applies, and the
allowed-tools list never needs a command that mutates dependencies. Building
also matters because the playgrounds import dist rather than src, so without it
a suite the review runs would measure whatever was last built.

Chromium is installed because the browser suite is the one that catches what
jsdom structurally cannot — inert blocking focus, and which of two overlapping
fixed elements a press reaches.

The system prompt now asks it to reproduce before reporting, to revert a fix and
confirm the test actually fails before calling coverage adequate, and to mark
anything it could not reproduce as unconfirmed.

Allowed tools are read-and-run only: no install, no write, no push.

* ci: check out the pull request, not the default branch

Round 1 review, four findings, each confirmed against evidence rather than
taken on the reviewer's word.

The one that mattered: an `issue_comment` event points GITHUB_REF at the
default branch, so the checkout landed on `main` and every step this branch
adds — install, build, browser download — would have run against main's tree.
Confirmed in the log of the review that raised it, which shows
`git checkout --force -B main refs/remotes/origin/main` before the action does
its own checkout of the PR branch. The build writes `packages/*/dist`, which is
gitignored and therefore survives that switch, and the playgrounds import it —
trap 2, automated. That is strictly worse than what it replaces: today a review
says it could not run the gate; with the wrong ref it reports a confident green
measured against main. Pinned to `refs/pull/N/head`.

Reviews have opened by saying they cannot post inline comments, and the pin
explains why: the inline-comment MCP server starts only when `--allowedTools`
names one of its tools, and tag mode's own list does not
(install-mcp-server.ts:80,138-142; modes/tag/index.ts:123-133). The comment
claiming tag mode grants them was wrong; naming the tool is what starts it.

Tag mode runs `--permission-mode acceptEdits` and already allows `git add` and
`git commit`, so telling the reviewer to delete code and confirm a test fails
handed it a way to mutate the tree and no way to put it back. Added the restore
verbs and an instruction to leave the tree clean and never commit.

`test:browser` did not exist on the branch this was cut from, only on the one
that has since merged. Rebased, both suite names are real, and the prompt now
says they are disjoint rather than implying one covers the other.

Also: the action's post step was failing with "cache write denied: token has no
writable scopes". An explicit permissions block makes every omitted scope
`none`, so `cache: pnpm` was inert in both directions and every review
cold-installed. Timeout raised to 45 for the work the job has gained, since a
killed job posts nothing at all.

Corrected two comments that claimed more than the file delivers: installing in
the workflow does not stop the agent mutating dependencies, because
`Bash(pnpm --filter:*)` is a prefix rule that also matches `pnpm --filter x add`.
The owner gate is what bounds this job, and it now says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* ci: run the review only on branches of this repository

Checking the pull request head out means this job executes that PR's code —
install lifecycle scripts included — while holding a subscription token,
`pull-requests: write` and `actions: write`. The owner gate does not cover
that. It decides who may TRIGGER a review, not whose code then runs, and the
ordinary sequence is the dangerous one: a stranger opens a pull request, the
owner reads it and comments `@claude review`.

`ci.yml` builds fork pull requests safely because `pull_request` hands it a
read-only token and no secrets. This job has both, and `actions: write` widens
it further, since a poisoned cache entry outlives the run and is restored by a
later job on `main`.

So the head must be a branch of this repository. The `issue_comment` payload
cannot answer that — `issue.pull_request` carries only URLs — so it is resolved
through the API in a step that runs before anything is checked out, and the
job stops with a readable error otherwise. Reviewing a fork stays possible by
hand, which is what it was before this branch.

Resolving the SHA rather than trusting `refs/pull/N/head` also closes the
window between the check and the checkout, since that ref moves when the
contributor pushes.

Verified both directions against live pull requests: this one resolves to
saeedkolivand/crosskit and passes, and cli/cli#14017 resolves to jamietanna/cli
and takes the exit path.

Install scripts stay enabled rather than `--ignore-scripts`, now that the code
is this repository's: an install that differs from the one `ci.yml` performs
would have the review measuring something other than the gate, which is the
failure this branch exists to fix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

external pull request originating outside of the CLI core team unmet-requirements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Discussion Templates

3 participants