Skip to content

ci: enforce issue quality for feature requests + disable blank issues - #225

Closed
Wibias wants to merge 3 commits into
lidge-jun:devfrom
Wibias:codex/enforce-issue-quality
Closed

Wibias wants to merge 3 commits into
lidge-jun:devfrom
Wibias:codex/enforce-issue-quality

Conversation

@Wibias

@Wibias Wibias commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Two changes to reduce low-quality issue submissions:

  1. New workflow .github/workflows/enforce-issue-quality.yml: auto-closes feature requests that lack actionable detail (empty/too-short sections, repeated text, title-only body). Reopens automatically when the author edits the issue to pass validation. Skips trusted contributors (OWNER/MEMBER/COLLABORATOR).

  2. Config change .github/ISSUE_TEMPLATE/config.yml: sets \�lank_issues_enabled: false\ to prevent bypassing the structured issue forms entirely.

Motivation

Issues like #208 where the body merely repeats the title with no actionable detail. The structured form fields were being bypassed via blank issues, and even when the form was used, GitHub doesn't validate whether answers are useful.

Review notes

  • The workflow only targets feature requests (enhancement label or [Feature]: title prefix)
  • Bug reports and other issue types are unaffected
  • Trusted contributors (OWNER/MEMBER/COLLABORATOR) are exempt
  • The workflow stores state in a hidden HTML comment so it can restore/reopen correctly
  • Uses \�ctions/github-script@v9\ with no checkout step (no contributor code execution in \issues\ trigger)
  • Concurrency group prevents race conditions on rapid edits

- Add .github/workflows/enforce-issue-quality.yml: auto-closes feature
  requests that lack actionable detail (empty/too-short sections,
  repeated text, title-only body). Reopens automatically when the
  author edits the issue to pass validation. Skips trusted contributors
  (OWNER/MEMBER/COLLABORATOR).

- Set blank_issues_enabled: false in .github/ISSUE_TEMPLATE/config.yml
  to prevent bypassing the structured issue forms.

Addresses the class of issues like #208 where the body merely repeats
the title with no actionable detail.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 63e58456c1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread .github/workflows/enforce-issue-quality.yml Outdated
Comment thread .github/workflows/enforce-issue-quality.yml Outdated
Comment thread .github/workflows/enforce-issue-quality.yml Outdated
Comment thread .github/workflows/enforce-issue-quality.yml Outdated
Comment thread .github/workflows/enforce-issue-quality.yml Outdated
Wibias added 2 commits July 21, 2026 22:15
- Pin actions/github-script to immutable SHA matching enforce-pr-target.yml
- Gate on feature-form title prefix AND section headings, not just the
  enhancement label, so a mislabeled bug/support issue is never validated
  as a feature request
- Use Unicode-aware tokenization (\\p{L}\\p{N}) with a character-count
  fallback for unspaced CJK scripts, so localized answers are not closed
  as too short
- Restrict getSection lookahead to known template headings so a user H3
  inside an answer does not truncate the section
- Fetch live issue state before reopening to avoid a race where a stale
  event payload skips the reopen and leaves bot state inactive
- Make the Version field required in the bug report form so reports
  always include the affected version
- Extend the issue-quality enforcer to also validate bug reports, with
  a deliberately narrow check: only close when both Summary and
  Reproduction are effectively empty, or when the body merely repeats
  the title. No word-count floors or section-distinctness rules, so a
  terse but real crash report is never auto-closed
- Detect the form by title prefix AND section headings (not labels) so
  a mislabeled issue is never validated with the wrong form's rules
- Type-aware closing guidance (feature vs bug)
- getSection now recognizes bug-form headings as section boundaries
@Wibias
Wibias marked this pull request as draft July 21, 2026 21:40
@lidge-jun

Copy link
Copy Markdown
Owner

Thank you @Wibias for tackling the issue-quality problem — the intent is exactly right.

Two design issues prevent us from merging as-is: (1) the reopen logic relies solely on the bot's persistent HTML marker, so it can reopen an issue a maintainer intentionally closed; (2) the workflow also targets bugs despite the title/description claiming feature-only scope.

We'd welcome a revised version that tracks manual-closure ownership (check closed_by or record the actor) and extracts the validation logic into a testable script. Closing for now — happy to review a v2.

@lidge-jun lidge-jun closed this Jul 21, 2026
@lidge-jun

Copy link
Copy Markdown
Owner

Detailed Review — PR #225

What we reviewed

We ran an adversarial code review (Sol subagent, gpt-5.6-sol) that read the full gh pr diff 225. The review examined the workflow logic, trigger conditions, state management, reopening behavior, and security model.

Findings

The intent is exactly right. Low-quality feature requests (empty sections, repeated text, title-only body) are a real problem, and automating their triage is valuable. The exemption for trusted contributors (OWNER/MEMBER/COLLABORATOR) and the use of actions/github-script@v9 without a checkout step (preventing contributor code execution on issues triggers) are both good security choices. The concurrency group for preventing race conditions on rapid edits is well thought out.

Three design issues we identified:

  1. Maintainer-closed reopen bug — The reopen logic at lines 422-435 relies solely on the bot's persistent HTML comment marker to decide whether to reopen an edited issue. If a maintainer manually closes an issue that was previously bot-closed, and the author then edits it to pass validation, the bot will reopen it — overriding the maintainer's intentional closure. The fix is to check closed_by or record the closing actor in the HTML state marker, so the bot only reopens issues it closed that weren't subsequently closed by a human.

  2. Bug targeting despite feature-only scope — Lines 54-57 show the workflow also triggers on issues with the bug label, despite the PR title, description, and all documentation claiming it only targets feature requests. This is either a copy-paste error or an undocumented expansion of scope. Bug reports have different quality expectations than feature requests, and applying the same validation (section length, uniqueness) to both would produce false positives on terse but valid bug reports.

  3. Untested state machine — The 450-line workflow contains a complex state machine (open/close/reopen transitions, HTML marker parsing, section validation) with no behavioral tests. The validation logic and state management should be extracted into a standalone script (e.g., .github/scripts/validate-issue-quality.js) that can be unit-tested independently of the workflow runner.

Recommendations for a v2

  • Track manual closure ownership: store the closing actor in the HTML comment or check github.event.issue.closed_by before reopening
  • Remove bug-label targeting or document it explicitly with bug-appropriate thresholds
  • Extract validation into a tested script with cases for: valid short feature request, invalid empty feature request, maintainer-closed reopen prevention, and the 10600-style word-boundary edge cases in title parsing

We'd be happy to review a revised version. Thank you @Wibias for the initiative — the automation concept is solid and we want to ship something like this.

Ingwannu pushed a commit that referenced this pull request Jul 22, 2026
…s on wrong-branch retarget (#229)

* feat(issues): redesign OpenCodex issue forms

- Bug report: add client/integration and area dropdowns, require version
  and OS, split logs from screenshots, add upload field for attachments,
  add redacted-config JSON field. Remove [Bug]: title prefix.
- Feature proposal: restructure around goal/blocker/behaviour/example,
  require a concrete usage example, add workflow-specific checks.
  Remove [Feature]: title prefix.
- New provider/API compatibility form for endpoint, request/response
  shape, and upstream-spec-anchored reports.
- New documentation form for missing, incorrect, outdated, or broken docs.
- Disable blank issues (config.yml).

* refactor(ci): extract and harden issue-quality validation

Move all classification and validation logic into a pure CommonJS module
(.github/scripts/issue-quality.cjs) with zero runtime dependencies. The
workflow (.github/workflows/enforce-issue-quality.yml) now only handles
GitHub API operations: fetching live issue state, managing the hidden bot
comment, closing/reopening, and respecting maintainer overrides.

Key design decisions:
- Detection uses distinct section headings, not title prefixes.
- Legacy [Bug]: and [Feature]: prefixed issues remain supported.
- Stored bot kind survives heading removal by the author.
- No word-count or character-count thresholds anywhere.
- Closure ownership: reopen only when the bot's stored closedAt and
  stateReason exactly match the live issue and no maintainer superseded.
- Trusted authors (OWNER/MEMBER/COLLABORATOR) are exempt.
- Single hidden bot comment, updated in place (no duplicates).

* test(ci): cover issue classification and reopening rules

29 tests using node:test and node:assert/strict (zero dependencies):
- Detection: new and legacy forms for all four kinds, stored bot kind,
  manually applied labels on unrelated issues.
- Validation: #208-style duplicates, concise actionable content, CJK
  submissions, empty reports, terse crash reports, provider-compat
  missing request/response, documentation corrections.
- Normalisation: No response, HTML comments, punctuation, filler phrases.
- Closure ownership: timestamp match/mismatch, state reason, inactive
  bot state, already-open issue, maintainer override.

The test workflow runs on PRs and pushes touching the issue templates,
validator, or enforcement workflow. It also validates that all issue-form
YAML files parse correctly, use only supported element types, and have
unique IDs.

* chore(ci): minimise PR-target workflow permissions

Drop contents: write from enforce-pr-target.yml. The workflow only
needs pull-requests: write to update titles, draft status, and comments.
No behavioural change.

* fix(ci): check closed_by before reopening (owner feedback on #225)

The reopen decision now verifies that the bot itself was the last actor
to close the issue (closed_by === 'github-actions[bot]'). A human closing
the issue, even with a matching timestamp, is treated as intentional
closure and the bot will not reopen.

Addresses finding 1 from the repo owner's review of PR #225.

* fix(ci): prevent false positives on legacy forms and misleading bot comment

- Feature validation: blocker and example sections are only required when
  their headings exist (new form). Legacy forms with only 'Problem to solve'
  and 'Proposed solution' no longer trigger the missing-sections reason.
- Bug validation: version/OS absence only closes when the headings exist in
  the body. Legacy bug reports without those fields are not penalised.
- Workflow: maintainer reopen comment now says 'Maintainer decision respected'
  instead of 'Automated check passed', which was false when the issue was
  still invalid.
- Tests: two new cases lock in legacy-form compatibility.

* feat(issues): use provider-compatibility label on compat form

Separates provider/API issues from generic enhancement requests in the
issue list. The label needs to be created in the repo by a maintainer:
  gh label create provider-compatibility --color 0075ca --description 'Incompatible provider, endpoint, or API integration'

* fix(ci): address Codex review comments on PR #229

1. Legacy _No response_ in old optional env fields no longer triggers the
   'Version and Operating system are both missing' reason. Added isRawPlaceholder()
   to distinguish intentionally blank optional fields from actively cleared ones.

2. Heading-removal bypass closed: when detectIssueKind() returns null on an
   edited issue but a form label (bug/enhancement/documentation/provider-compatibility)
   is still present, the workflow uses that label as a fallback kind instead of
   skipping enforcement silently.

3. Provider/API compatibility validator now checks that provider and endpoint
   fields are non-empty. A report where those headings exist but were cleared
   after submission is rejected.

36 tests pass.

* ci: ping PR author when retargeting to dev
@Wibias
Wibias deleted the codex/enforce-issue-quality branch July 25, 2026 07:00
agentHits pushed a commit to agentHits/opencodex that referenced this pull request Sep 17, 2026
…s on wrong-branch retarget (lidge-jun#229)

* feat(issues): redesign OpenCodex issue forms

- Bug report: add client/integration and area dropdowns, require version
  and OS, split logs from screenshots, add upload field for attachments,
  add redacted-config JSON field. Remove [Bug]: title prefix.
- Feature proposal: restructure around goal/blocker/behaviour/example,
  require a concrete usage example, add workflow-specific checks.
  Remove [Feature]: title prefix.
- New provider/API compatibility form for endpoint, request/response
  shape, and upstream-spec-anchored reports.
- New documentation form for missing, incorrect, outdated, or broken docs.
- Disable blank issues (config.yml).

* refactor(ci): extract and harden issue-quality validation

Move all classification and validation logic into a pure CommonJS module
(.github/scripts/issue-quality.cjs) with zero runtime dependencies. The
workflow (.github/workflows/enforce-issue-quality.yml) now only handles
GitHub API operations: fetching live issue state, managing the hidden bot
comment, closing/reopening, and respecting maintainer overrides.

Key design decisions:
- Detection uses distinct section headings, not title prefixes.
- Legacy [Bug]: and [Feature]: prefixed issues remain supported.
- Stored bot kind survives heading removal by the author.
- No word-count or character-count thresholds anywhere.
- Closure ownership: reopen only when the bot's stored closedAt and
  stateReason exactly match the live issue and no maintainer superseded.
- Trusted authors (OWNER/MEMBER/COLLABORATOR) are exempt.
- Single hidden bot comment, updated in place (no duplicates).

* test(ci): cover issue classification and reopening rules

29 tests using node:test and node:assert/strict (zero dependencies):
- Detection: new and legacy forms for all four kinds, stored bot kind,
  manually applied labels on unrelated issues.
- Validation: lidge-jun#208-style duplicates, concise actionable content, CJK
  submissions, empty reports, terse crash reports, provider-compat
  missing request/response, documentation corrections.
- Normalisation: No response, HTML comments, punctuation, filler phrases.
- Closure ownership: timestamp match/mismatch, state reason, inactive
  bot state, already-open issue, maintainer override.

The test workflow runs on PRs and pushes touching the issue templates,
validator, or enforcement workflow. It also validates that all issue-form
YAML files parse correctly, use only supported element types, and have
unique IDs.

* chore(ci): minimise PR-target workflow permissions

Drop contents: write from enforce-pr-target.yml. The workflow only
needs pull-requests: write to update titles, draft status, and comments.
No behavioural change.

* fix(ci): check closed_by before reopening (owner feedback on lidge-jun#225)

The reopen decision now verifies that the bot itself was the last actor
to close the issue (closed_by === 'github-actions[bot]'). A human closing
the issue, even with a matching timestamp, is treated as intentional
closure and the bot will not reopen.

Addresses finding 1 from the repo owner's review of PR lidge-jun#225.

* fix(ci): prevent false positives on legacy forms and misleading bot comment

- Feature validation: blocker and example sections are only required when
  their headings exist (new form). Legacy forms with only 'Problem to solve'
  and 'Proposed solution' no longer trigger the missing-sections reason.
- Bug validation: version/OS absence only closes when the headings exist in
  the body. Legacy bug reports without those fields are not penalised.
- Workflow: maintainer reopen comment now says 'Maintainer decision respected'
  instead of 'Automated check passed', which was false when the issue was
  still invalid.
- Tests: two new cases lock in legacy-form compatibility.

* feat(issues): use provider-compatibility label on compat form

Separates provider/API issues from generic enhancement requests in the
issue list. The label needs to be created in the repo by a maintainer:
  gh label create provider-compatibility --color 0075ca --description 'Incompatible provider, endpoint, or API integration'

* fix(ci): address Codex review comments on PR lidge-jun#229

1. Legacy _No response_ in old optional env fields no longer triggers the
   'Version and Operating system are both missing' reason. Added isRawPlaceholder()
   to distinguish intentionally blank optional fields from actively cleared ones.

2. Heading-removal bypass closed: when detectIssueKind() returns null on an
   edited issue but a form label (bug/enhancement/documentation/provider-compatibility)
   is still present, the workflow uses that label as a fallback kind instead of
   skipping enforcement silently.

3. Provider/API compatibility validator now checks that provider and endpoint
   fields are non-empty. A report where those headings exist but were cleared
   after submission is rejected.

36 tests pass.

* ci: ping PR author when retargeting to dev
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.

2 participants