Hamburger Cross Icon
Ticket Field
+
Jira Collector

Ticket Field + Jira Collector

✓ Guardrail ↓ Collector Stable Devex Build And Ci

Enforce Ticket Field using data collected by Jira Collector. Automatically check devex build and ci standards on every PR.

Guardrail: Ensures a configured ticket field, such as a Jira custom field, has an allowed value.
Data Source: Extract Jira ticket references from pull request titles and descriptions, validate them against the Jira REST API taking the best one that exists, and detect ticket reuse across PRs.

How Jira Collector Powers This Guardrail

The Jira Collector gathers metadata from your vcs systems. This data flows into Lunar's Component JSON, where the Ticket Field guardrail evaluates it against your standards.

When enabled, this check runs automatically on every PR and in AI coding workflows, providing real-time enforcement with actionable feedback.

1
↓ Jira Collector Gathers Data Collector
Extracts metadata from code, configs, and tool outputs
2
{ } Component JSON
Data centralized in structured format for evaluation
3
✓ Ticket Field Checks Guardrail
Pass/fail result with actionable feedback in PRs

Quick Start Configuration

Add both the collector and policy to your lunar-config.yml to enable this guardrail.

📄 lunar-config.yml
# Step 1: Enable the Jira Collector
collectors:
  - uses: github://earthly/lunar-lib/collectors/jira@v1.0.5
    # with: ...

# Step 2: Enable the Ticket Guardrails
policies:
  - uses: github://earthly/lunar-lib/policies/ticket@v1.0.5
    include: [ticket-field]
    # with: ...

What Jira Collector Collects

This collector gathers the following data that the Ticket Field guardrail evaluates.

Collector code

ticket

Extracts Jira ticket IDs from the PR title and description, takes the best one the Jira REST API confirms exists, and writes normalized ticket data to .vcs.pr.ticket and native Jira data to .vcs.pr.ticket.native.jira.

Collector after-json

ticket-from-json

After-json variant of ticket: resolves the ticket from .vcs.pr (title and description) in Component JSON (populated by the github or gitlab collector) instead of calling the GitHub API, validates candidates against the Jira REST API, and writes the same .vcs.pr.ticket (+ .vcs.pr.ticket.native.jira). Works on any VCS provider that populates .vcs.pr and needs no GH_TOKEN. Uses the experimental after-json hook; the code-hook ticket collector above is kept for compatibility. Transitional: once after-json is no longer experimental, this logic will be folded into the ticket collector and ticket-from-json will be removed.

Collector code

ticket-history

Queries the Lunar SQL database to count how many other PRs reference the same ticket. Resolves the ticket the same way the ticket collector does, so both agree on which one the PR is for. Writes .vcs.pr.ticket.reuse_count for policy evaluation.

Example Data Flow

Here's an example of the data that Jira Collector writes to the Component JSON, which Ticket Field then evaluates.

{ } component.json From Jira Collector
{
  "vcs": {
    "pr": {
      "ticket": {
        "id": "ABC-123",
        "source": { "tool": "jira", "integration": "api" },
        "url": "https://acme.atlassian.net/browse/ABC-123",
        "valid": true,
        "status": "In Progress",
        "type": "Story",
        "summary": "Implement payment validation",
        "assignee": "jane@acme.com",
        "reuse_count": 0,
        "native": {
          "jira": { "...full Jira API response..." }
        }
      }
    }
  }
}

Configuration Options

Jira Collector Inputs

Input Required Default Description
ticket_prefix Required — Character(s) before the ticket ID in the PR title or description (empty = match anywhere)
ticket_suffix Required — Character(s) after the ticket ID in the PR title or description (empty = match anywhere)
ticket_pattern Optional [A-Za-z][A-Za-z0-9]+-[0-9]+ Regex pattern for ticket ID (without prefix/suffix). The default accepts a project key of two or more characters, so XX-1 matches as well as ABC-123. It is permissive enough to also match tokens like UTF-8 or SHA-256; Jira does not know those, so they are skipped as candidates, but narrowing it to your project keys (e.g. (ABC|OPS)-[0-9]+) saves the lookups and is worth doing when Jira validation is not configured.
ticket_keywords Optional fixes|fixed|fix|closes|closed|close|resolves|resolved|resolve|ticket|issue Regex alternation of the keywords that mark a description reference as this PR's own ticket ("Fixes ABC-123", "Ticket: ABC-123"). Matched case-insensitively and ranked above any bare reference, so a dependency or related ticket mentioned earlier in the body does not win. Only applies to the description; the title is always matched bare.
max_ticket_candidates Optional 5 How many candidate keys to try against Jira before giving up. Candidates are ordered best-first, and each one costs a Jira lookup per sub-collector, so this bounds what a description full of key-shaped tokens can cost.
ticket_path Optional .vcs.pr.ticket Where the ticket is recorded, as a path under .vcs.pr. To record a second reference next to the delivery ticket, such as an architecture-review submission, import the collector again under its own `name`, with a `ticket_pattern` for that reference and a path of its own (e.g. .vcs.pr.architecture_review).
jira_base_url Required — Jira instance base URL (e.g. https://acme.atlassian.net)
jira_user Required — Jira user email for API authentication
jira_retries Optional 3 How many times to retry a Jira lookup that fails transiently — connection refused, timeout, 429, 5xx. A rejected credential or an unknown ticket is not retried.

Ticket Guardrails Inputs

Input Required Default Description
allowed_sources Required — Comma-separated list of allowed ticket sources (e.g. "jira,linear"). Empty = any.
allowed_statuses Required — Comma-separated list of allowed ticket statuses (empty = any)
disallowed_statuses Required — Comma-separated list of disallowed ticket statuses (empty = none)
allowed_types Required — Comma-separated list of allowed issue types (empty = any)
max_ticket_reuse Optional 3 Maximum number of other PRs allowed to reference the same ticket
ticket_field Required — Field the `ticket-field` check reads, as a path relative to the ticket (e.g. native.jira.fields.customfield_10042 for a Jira custom field). Empty skips the check.
allowed_field_values Required — Values `ticket-field` accepts: comma-separated, one per line, or a JSON array for values that contain a comma. Empty accepts any value, so the field only has to be set.
ticket_path Optional .vcs.pr.ticket Where every check reads the ticket from. Point a second import of this policy at the path a second jira collector import records to (e.g. .vcs.pr.architecture_review) to check that reference as well.
skip_authors Required — PR author logins whose PRs every check skips, such as dependency bots (e.g. dependabot[bot], renovate[bot]). Comma-separated or one per line; matched exactly, ignoring case, so keep the [bot] suffix GitHub gives an app. Empty skips no one.

Ready to Automate Your Standards?

See how Lunar can turn your AGENTS.md, engineering wiki, compliance docs, or postmortem action items into automated guardrails with our 300+ built-in guardrails.

Works with any process
check AI agent rules & prompt files
check Post-mortem action items
check Security & compliance policies
check Testing & quality requirements
Auto-ID My Guardrails
Paste your AGENTS.md or manual process doc and get guardrails in minutes
Book a Demo