# Tags and filters

A tag is a bare key (`bug`) or `key:value` (`status:doing`). A **filter** is a
conjunction of terms over a task's tags — the one primitive every board,
lane, and query is built from. Resist adding to the grammar until a board
someone actually needs cannot be expressed.

| term | matches |
| --- | --- |
| `status` | any task with a `status` tag or a `status:*` tag |
| `status:doing` | exactly that tag |
| `-status` | negation: tasks with no `status` tag — the **inbox** |
| `project:none` | the implicit value: tasks with no `project` tag. Works for every key (`working:none` is nobody); `status:inbox` is its alias for status |
| `is:open` / `is:closed` / `is:any` | closed-ness; `is:open` is implied |
| `archived` / `archived:any` | only archived tasks / archived ones too; left out otherwise (archived means "don't look at it") |
| `type:practice` / `type:any` | what kind of entity (board #2472): practices, policies, guidance. With no `type:` term a filter finds tasks only (a task is the implicit type: `type:task` is no type tag); `type:any` finds every kind |
| `#38` | the task with that number, open or closed (a number names one task; an explicit `is:` still applies) |
| `~word` | substring of title or body (case-insensitive); a number also matches task numbers that contain it (`~92` finds #92 and #1392) |

```js
import { compileFilter } from 'tosijs-virta'

const inbox = compileFilter('-status')
const uiBugs = compileFilter('project:tosijs-ui bug is:any')

const task = { tags: ['bug', 'project:tosijs-ui'], closedAt: null, num: 7, title: 'x', body: '' }
preview.textContent = `inbox: ${inbox(task)}, uiBugs: ${uiBugs(task)}`
```
```test
test('the example evaluates both filters', () => {
  expect(preview.textContent).toBe('inbox: true, uiBugs: true')
})
```
