Writing ticket and commit classification rules

The full reference for EngLedger's classification rule engines: every condition field, every operator, matching semantics, and what happens when rules change.

Updated 12 July 2026

This is the reference for the two classification rule engines: exact fields, operators and matching behaviour. For what classification is and where the results land, start with classification concepts; for worked examples of common setups, see the rules cookbook.

How rules evaluate

Both engines share the same evaluation model:

  • Rules run in priority order (1 first) and the first matching rule wins. Nothing after it is considered.
  • All conditions on a rule must match (AND). For OR logic, create several rules, or use a list operator ("is one of") on a single field.
  • Only active rules evaluate. Deactivating a rule is a way to park it without deleting it.
  • A new rule is appended at the lowest priority; drag rules to reorder.
  • If no rule matches, the organisation's fallback classification applies, and the result is recorded with source fallback, visibly, not silently (see reading rule coverage).

For tickets there is one gate before any rule runs: ticket types on the organisation's excluded list are marked excluded and never reach the rules. Excluded work stays out of the ledger entirely.

Every rule tracks how often it has matched and when it last matched, so you can spot rules that never fire.

Ticket rules

Ticket rules (Classification → Ticket Rules) test fields on the synced ticket. A rule's output is a financial classification (capitalisable or non-capitalisable), a work type (feature, maintenance, support or technical debt), and optionally one or more categories.

Condition fields

FieldTypeNotes
Issue keyTexte.g. PROJ-123
Issue typeTextStory, Bug, Task, …
StatusTextThe ticket's current status
SummaryTextThe ticket title
PriorityTextJira and Bitbucket tickets
Story pointsNumberJira tickets
LabelsListJira and GitHub tickets
ComponentsListJira and Bitbucket tickets (Bitbucket: component)
AssigneeTextDisplay name
ReporterTextDisplay name
SprintTextBitbucket: milestone
State reasonTextGitHub issues (completed, not planned, …)
ProviderTextjira, github or bitbucket, useful to scope a rule to one tracker

Jira custom fields are also available as condition fields. EngLedger discovers them from your synced Jira data and lists them alongside the core fields, with the number of tickets that carry a value. Object-valued fields (selects, users) are matched on their display name or option value; multi- value fields behave as lists.

Operators by field type

Field typeOperators
Textequals, not equals, contains, not contains, starts with, ends with, is one of, is not one of
Numberequals, not equals, greater than, less than, greater than or equal, less than or equal
Listincludes, not includes, includes one of, includes none of
Datebefore, after, between

Every field type also supports is empty and is not empty. A field counts as empty when it has no value, is an empty list, or contains only whitespace.

Matching semantics worth knowing:

  • Text and list comparisons are case-insensitive. bug matches Bug.
  • "Is one of" / "includes one of" take a comma-separated list of values; spaces around commas are ignored. "Is one of" matches when the field equals any listed value; "includes one of" matches when a list field contains at least one of them.
  • A missing value never matches a comparison operator: a ticket with no story points fails both story points greater than 3 and story points less than 3. Test for presence with is empty / is not empty.
  • Between is inclusive of both end dates. Date operators apply to Jira custom date fields.

Testing a rule before saving

The rule builder's test shows how many existing tickets the conditions match, with sample issue keys. The count respects priority: tickets a higher-priority rule would capture first are not counted, so the number reflects what the rule would actually classify from where it sits in the list.

Commit rules

Commit rules (Classification → Commit Rules) classify commits that don't inherit a classification from a ticket. A commit linked to a ticket takes that ticket's classification (source ticket); if it's linked to several, a capitalisable one wins the tie. Commit rules only evaluate when there is no usable ticket link, which is exactly their job: catching the unlinked work described in the effort model.

Two other states short-circuit everything: a manual commit classification is never overwritten by rules, and a dismissed commit carries no classification at all.

Condition fields

FieldMatches against
Branch nameThe branch the commit was observed on
Commit messageThe full commit message
File pathEach changed file's path: the condition matches if any changed file matches

Operators

All commit rule fields are text and share one operator set:

OperatorBehaviour
equalsExact match, case-insensitive
containsSubstring match, case-insensitive
starts with / ends withPrefix / suffix match, case-insensitive
matches (regex)Regular expression match (see below)
in / not inComma-separated list of exact values, case-insensitive
is empty / is not emptyPresence test

Regex matching takes a full pattern including delimiters, for example /^(hotfix|release)\// against branch name, or /\.(md|mdx)$/ against file path. Unlike the other operators, regex is case-sensitive unless you add the i flag (/docs/i). A pattern that fails to compile simply never matches. It doesn't error, so test it.

The commit rules screen includes a preview: paste a branch name, commit message or file list and see which rule would match and what classification would result, before any real commit is touched.

When rules change

Classification is retrospective. The engines keep already-classified work in line with the current rules, with two deliberate exceptions: manual overrides are never touched, and work in a locked reporting period is never touched (see period close).

For ticket rules:

  • Editing or deleting a rule automatically re-evaluates the tickets that rule had classified. Each ticket runs through the full rule list again and takes whatever now matches: another rule or the fallback.
  • Creating a rule doesn't reclassify anything by itself. New rules apply to newly synced tickets; to apply one to existing work, run Reclassify all tickets from the rules page. That re-evaluates every ticket except manual overrides, excluded types and locked periods, and reports how many changed and how many were skipped.

For commit rules, any change (create, edit or delete) automatically re-runs classification for commits currently classified by rule or fallback. Ticket-inherited and manual classifications are left alone.

Every ticket classification change, whatever triggered it, is written to an immutable history (previous values, new values, source and who triggered it) and the change flows through to the affected effort entries and any linked commits. The numbers move, and the record of the move is kept.

Override precedence

When several mechanisms could classify the same work, the order is fixed:

PriorityTicketsCommits
1Manual overrideManual override
2Linked ticket's classification
3First matching ruleFirst matching commit rule
4Organisation fallbackOrganisation fallback

A manual override (set from the review screens, or in bulk) wins over every rule and survives every reclassification run. Period locks sit above the whole table: once a period is locked, nothing in it reclassifies.

Reading rule coverage in the Confidence Report

Every effort entry records how it was classified, and the Confidence Report scores it: manual overrides highest, rule matches high, fallbacks low. The low-confidence grey zone is, in practice, a list of the work your rules don't cover: each fallback-classified entry is a ticket or commit no rule matched.

That makes the grey zone the working to-do list for rule authors: review what's in it, write or extend rules for the patterns you see, reclassify, and watch the zone shrink. The report shows honest coverage rather than hiding thin spots, the same posture as the rest of the ledger.