Skip to main content
Run this before add-epic, every time an ask arrives — an issue you are about to mirror, a request in conversation, a line off a roadmap doc.

The gap it closes

The conductor has always accepted work. It did not triage it. add-epic validates the id, the lane and the priority, refuses a tracker item another epic already holds (matched on externalUrl first, bare externalId only when neither side has a URL — at every writer, not only add-epic), and appends. The only dedup that existed was identity-based — same id, or same externalUrl. That correctly stops /pm:sync from mirroring one issue twice, and it does nothing at all about the same ask arriving under a different name. That failure has exactly one symptom: the backlog only ever grows, and every entry looks equally legitimate.
Measured in PM’s own repository: the change-registered-under-two-lanes integrity check reports four live pairs that are one change registered twice, under different lanes and different names. Identity dedup found none of them. A human reading the backlog did.

Getting the candidate set

--limit N (default 5) bounds how many candidates come back. It must be a positive integer — a valueless or non-numeric --limit is refused rather than coerced, and any other flag is rejected by name. A wrong bound on a recall device silently hides the twin the whole command exists to surface. Quote the ask: it is ONE argument, and a second unquoted word is refused as an extra argument rather than silently dropped (0.44.0). It may stand before or after --limit — triage --limit 3 "fix the render stamp" reads the ask — while triage --limit 5 with no ask still prints usage. A quoted ask that begins with -- but is not shaped like a flag ("--story <n> is 1-indexed") is the ask, not a flag.

The engine computes candidates and never a verdict

This is the line the whole design turns on, and the command states it in its own output: verdict is always null. PM is an instruction layer. An engine that decided two asks were “the same” would be making a semantic judgment on prose, which is exactly the thing it must not do — so it hands you a ranked, auditable candidate set and stops.

Why the ranking has no stoplist

Scoring is IDF over the backlog itself — ln((N+1)/df). A term’s weight comes from how rare it is in your own epics, so house vocabulary self-neutralizes: “Implementation Plan” in a title does not drag half the index in, and nobody maintains a curated stopword list that goes stale. The tokenizer reads any script, not only ASCII (0.50.0). A word is a run of letters, marks and digits in any script, of three or more characters, and text is NFC-normalised first so a composed and a decomposed spelling of one word match. Before, every letter outside a-z was a separator: an epic titled in Cyrillic and the identical ask both reduced to no words, and triage returned no candidates — the same answer as “nothing overlaps”. Chinese, Japanese and Korean text, which puts no spaces between words, is split into overlapping two-character tokens instead, so a reworded ask still shares tokens with the epic it matches. Because a long CJK ask carries one such token per character, an epic that shares ONLY those needs at least two of them, and at least one for every ten in the ask — so common words such as “new” do not fill the list. Every candidate carries the tokens that earned its score. That is what makes a lexical surface auditable rather than an oracle — you can see at a glance that a match is real, or that it rode in on one shared word.
Triage is not a substitute for the externalUrl check during sync, and that check is not a substitute for triage. They catch different things: identity dedup stops the same item landing twice; triage surfaces the same ask under a different name. Run both.

Declining an ask

An ask you screen and decide against ends the way all work in PM ends — with a recorded disposition carrying its reason, never by deletion:
declined is a terminal outcome, not a status — archived already means terminal, so nothing about status-driven behavior changes. It records that a judgment was made, which is precisely what deletion destroys.

Epics & Lanes

What happens to an ask once you decide to register it.