Skip to content

[Chore] validate-issue-structure.sh — config-driven PreToolUse hook for gh issue create #107

@atlas-apex

Description

@atlas-apex

Driver

Agents routinely bypass the /feature, /bug, and /task skills when filing tickets in bulk. The skills are interactive (one-question-at-a-time, per their own rule), which makes them impractical for "file 10 tickets right now" intents — the agent silently falls back to raw gh issue create with bespoke body shapes. Result: tickets that are information-rich but don't match the team standard.

The failure happened before any tool call — self-discipline prose ("BLOCKING REQUIREMENT: invoke the relevant Skill tool") did not prevent it. Precedent for mechanical enforcement of this class of rule: validate-commit-format.sh, validate-pr-create.sh.

Scope

Add .claude/hooks/validate-issue-structure.sh, wired to PreToolUse on Bash matching gh issue create …. The hook:

  1. Parses command args to extract --title and --body (or --body-file).
  2. Extracts the title prefix ([Foo] style) and looks up the expected body schema from project config (see below) — never hardcoded.
  3. Enforces the schema: required sections must be present; checkbox counts must be > 0 where configured.
  4. On fail: exits 2 with a message that names the missing section and suggests the matching skill.
  5. Skip condition: body contains <!-- validate-issue-structure: skip --> — escape hatch for legitimate off-template tickets (epics, meta-threads).

Config-driven — not hardcoded

A separate .claude/project-config.json (or an extension to onboarding.yaml) carries the team's ticket schema. Default ships with the current skill templates; teams override without touching framework code.

ticket:
  prefix_whitelist: [Feature, Bug, Chore, Refactor, Testing, CI, Docs]
  required_sections:
    Feature: ["User Story", "Acceptance Criteria"]
    Chore:   ["Driver", "Scope", "Acceptance Criteria"]
    Bug:     ["Given / When / Then", "Repro"]
    Docs:    ["Driver", "Acceptance Criteria"]
  label_priority_scheme: "P0,P1,P2,P3"   # some teams use priority-p0 etc

The hook, /feature, /task, /bug, and the batch skill (companion ticket) all read from the same config. One source of truth per project; zero coupling in framework code to any specific team's conventions.

Acceptance Criteria

  • validate-issue-structure.sh exists and is executable.
  • Reads ticket schema from .claude/project-config.json (or the chosen config location) with a baked-in default that ships with the framework.
  • Blocks (exit 2) when required sections are missing, naming each missing section.
  • Does NOT block when the skip marker is present.
  • Block messages point at the correct skill for the detected prefix.
  • Fixtures in .claude/hooks/tests/ cover pass and fail paths per prefix.
  • .claude/settings.json wired with the matcher.
  • docs/rule-audit.md lists the hook.
  • Default config covers the shipped skill templates exactly — zero behavioural drift on a fresh fork with no config overrides.

Risks / Dependencies

  • Blocked by the config format landing — the hook enforces against something, and that something is the config schema. The companion config-and-whitelist ticket lands first.
  • Edge case: legitimate off-template tickets (epics, meta-threads). The skip marker handles this; document it in the rule file so contributors don't guess.
  • Over-strict parsing would false-positive. Mitigation: grep for headers, don't parse markdown trees. Start permissive, tighten after logs.

Glossary

Term Definition
PreToolUse hook Shell script invoked by Claude Code before a tool call. Exit 0 allows; exit 2 blocks with a message shown to the user and agent.
Skip marker HTML comment in the body telling the hook to let this invocation through. Keeps the escape hatch explicit and grep-able.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High — material gap or user-impactingenhancementNew feature or requesthas-ticket

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions