Skip to main content
Some questions about a ticket depend on what else is open. Is this the fifth report of the same outage? Is the customer describing an error three other customers reported this morning? Write the queue’s name on every ticket as an attribute, such as attributes.queue set to support, and the judgment reads the other open tickets with the same value. Each queue is one block of a blocking relation, one of the relations, with the queue’s name as its key.

Start from the queue starter

The triage.against_queue starter is this judgment, ready-made. Fill it with your own paths:
POST /v1/namespaces/acme%2Fhelpdesk/judgments
  • It judges only open tickets, the documents whose kind is ticket and whose status is open. A ticket that is closed leaves the judgment: it is no longer judged or answered, and no longer shown to the other tickets in its queue.
  • It reads the ticket’s subject and the first 1,000 characters of its body, and from its queue the newest 10 other open tickets created within 14 days of the queue’s newest open ticket: when each was created, its subject, and the first 200 characters of its body. An edit past either cut changes nothing the judgment reads, so it re-judges nothing. See cut a long field.
  • Each queue is its own. Tickets in billing never see the tickets in support. Write the queue’s name as a short string of letters, digits, -, _ and .. A ticket with no queue, or a value that is not such a string, is in no queue: it is judged with no other tickets shown, and the criteria say such a ticket repeats nothing.
  • The answer is yes or no. It does not name the tickets the problem repeats. Its evaluation lists every ticket it read in related_documents, so you can see what it was compared with.
  • Thresholds. repeat at 0.8 and review at 0.5: group a ticket above repeat with the problem it repeats, and send the band between them to a person. These are starting points; post outcomes to measure and tune them.
  • Your own wording. Send dry_run: true to get the filled definition, change its question or criteria, and create from that. The same relation serves any question that depends on what else is open.
In the dashboard, the relation editor’s “Add: Queue as context” fills the same relation; set the judgment’s Applies to to your open tickets. Unless the namespace already has one, the first version also builds a reference index on attributes.queue, listed as a reference_index job in the create response’s job_ids; the judgment’s answers are unavailable until it is done. The context is capped at 1,500 tokens with max_tokens, which keeps each judgment standard in size. Over the cap, the oldest of the queue’s tickets are left out first, before any of the ticket’s own text is cut, and the evaluation records context_truncated: true.

What it is not

  • Not everything open. It shows the newest 10 open tickets created within 14 days of the queue’s newest one. An open ticket older than that, or past the newest 10, is not shown, however much it matters. The 14 days count back from the queue’s newest open ticket, not from now, so a quiet queue still shows its last tickets. Raise last_n (up to 254) or window in the definition to show more; every ticket shown is in every judgment’s context, so its size class grows with it.
  • Creation order, not priority. Newest means most recently created. Editing a ticket, raising its priority or reassigning it does not move it up, and the tickets shown are not the ones your team will work next. The judgment cannot tell what is ahead of this ticket in your process. Another order is not available on this relation: the tickets shown are always the newest.
  • Not their answers. It reads the other tickets’ text, never their answers to this or any other judgment.

What it costs

A ticket’s own writes re-judge it, as for any judgment: a new ticket is judged within seconds, against its queue as it stands then. The cost to plan for is the other direction. Every open ticket reads the same newest few, so a change to them re-judges the whole queue:
re-judgments a month = queue changes a month × open tickets per queue inside the re-judge scope

What counts as a queue change

  • A write to a ticket that is open before or after it, such as a new ticket, an edit to an open one, or closing one, makes every open ticket in its queue pending from the moment the write acks.
  • After a quiet period, Brussle reads the queue once and compares what it shows with what it showed before. Each queue waits on its own until it has had no such write for freshness.fanout.debounce_ms, 10 minutes by default, or until fanout.max_wait_ms, 1 hour by default, has passed since the first write it has not yet read. So a burst of writes costs one read of the queue, and a queue that is never quiet for 10 minutes is read once an hour.
  • Nothing shown changed, such as a new assignee, a tag, an edit past a cut, or a closed ticket that was not among the newest shown: the open tickets are fresh again, with no engine call and nothing counted.
  • Something shown changed, such as a new ticket, or one of the shown tickets closed or its subject edited: every open ticket in the queue inside the re-judge scope is re-judged. Nearly all of their contexts change, so nearly every one is billed. One whose context did not change keeps its answer and is not billed, but counts toward the rolling limit.
Until the queue is read, its open tickets read pending with their last answers, so in a queue written to all day they read pending much of the time. answers: "fresh_only" leaves pending answers out: list a busy queue’s answers without it.

Worked numbers

Each ticket’s context and question come to about 1,300 tokens: a standard judgment, at $0.25 per 1,000. A is well inside the default rolling limit of 300,000 re-judgments in any 30 days. B is nearly twice it: the first 300,000 run, $75 of re-judging, and the rest are deferred until the window has room. C trades freshness for cost: the other open tickets see a new ticket up to 4 hours later, and read pending until then, while the new ticket itself is still judged within seconds. D re-judges only the newest open tickets; the older ones keep their answers and read stale. Splitting a queue in four does not help a queue written to around the clock, since each part is still read once an hour: it helps when each part is quiet for longer.

The limits it runs under

Each is a setting in freshness.fanout, sent at create or changed later with a PATCH:
  • rolling_limit, 300,000 by default: the most open tickets the judgment’s queue changes may re-judge in any 30 days. Within it, a change runs on its own, at most 1,000 tickets at a time. Past it, the tickets that fit are re-judged and the rest wait: their answers read stale with stale_reason limit_reached, and the events feed records one judgment.limit_reached with limit rolling_limit. They catch up on their own when older re-judgments leave the 30-day window, or at once when a PATCH raises the limit. With several queues, the queues that have spent the most of the limit wait first, so one busy queue cannot leave every other queue’s tickets stale.
  • block_cap, 1,000 by default: the most open tickets one queue may hold. A create is refused with invalid_request when a queue already holds more, naming it in details.key; closed tickets do not count, since they are outside Applies to. A queue that grows past the cap later waits, whatever the rolling limit: its open tickets read stale (limit_reached), and the events feed records judgment.limit_reached with limit block_cap and the queue in key. It runs again once tickets are closed and the queue is back within the cap, or when a PATCH raises it.
  • scope.created_within, 30 days by default: an open ticket created longer before the change is not re-judged. It keeps its answer and reads stale with stale_reason referenced_changed until its own next write. If tickets stay open for weeks and their answers matter, send "scope": {"created_within": null}: the Applies to on open tickets already keeps closed ones out.
See the rolling limit for how deferral catches up, and match one record to another for the same limits on another blocking relation.

What you confirm

Without confirm: true, the create returns its cost estimate and creates nothing:
replay runs the last 30 days of writes through the judgment: the open tickets’ own writes, and each queue’s tickets created in that time that are still open, re-judging the open tickets then in their queue after the quiet period. It cannot see a ticket that was closed, edited or deleted in those 30 days, so a ticket opened and closed within the month leaves no trace of the changes it made. It says so with "excludes": ["candidate_edits", "candidate_deletes"] and lower_bound: true, and its figures are “at least”. In a queue where most tickets close within days, the real figure can be several times the estimate. What bounds it is the rolling limit: fanout.cost_usd_at_limit is the most the queue changes can cost in any 30 days. Sending confirm: true agrees to both, and no queue change waits for a person after that.