Guides / Building software with AI agents

QnA MD file format: tags, fields, statuses and log lines

The QnA MD tag grammar (spec v0.1 draft): six item types, ids, fields, options and (rec), status values, log verbs, timestamps, escaping and our conventions.

Product help · Format reference

A QnA MD file is an ordinary Markdown file with questions and decisions stored in HTML comments, which Markdown renderers pass through without showing, so the tags are present in the file and invisible on a rendered page. This reference describes the tag format as it stands in version 0.1 of our draft specification, and the conventions we follow when writing tags by hand or asking an agent to write them. Why the format looks this way is covered in why we built QnA MD. The specification itself will be published under the Creative Commons Attribution 4.0 license; until then this page is its public description.

Anatomy of a tag

A tag is an HTML comment whose first word is oq: followed by a type in lowercase letters. The rest of the first line carries the id, written with a hash sign. Each following line is one of three things:

  • A field: key: value, starting in the first column.
  • An option: - [ ] KEY. Label, or - [x] once chosen.
  • The log: a line reading log:, after which every line up to the closing --> is a log entry.

A line indented by spaces or a tab continues the field or log entry above it, even if it looks like key: value. An unindented key: line always starts a new field. Continued fields are read back joined with spaces; continued log values keep their line breaks.

The six item types

TypeUse it forAnswer recorded as
confirmA yes or no question, with the two options written outThe chosen option
choiceOne option from a listThe chosen option
multiSeveral options from a listThe chosen keys, such as A, C
rankPutting the listed options in orderThe order, such as C > A > B
askFree text, when the options cannot be listedThe text
decisionA statement that people approveApproval ballots

Two more types work at file level: oq:file and oq:edits, described below. A parser keeps a tag of unknown type and reports a warning, as it does for a tag with no id.

Ids and fields

An id may use letters, digits, underscores, dots and hyphens, and must be unique within the file (the specification requires it; the reference parser does not yet check it). The fields are:

  • by: the handle that wrote the tag and when.
  • to: who the item waits on, as @a, @b. Empty or absent means everyone.
  • status: one of open (the default when the line is missing), partial, deferred, resolved or vetoed.
  • Q: the question, or D: for a decision.
  • why: the rationale, and ref: a path or URL the item concerns.
  • approve: and quorum:, for decisions only. @a > @b is an ordered chain, approved in turn. @a, @b, @c with quorum: 2 needs any two. Without a quorum, everyone listed must approve.

An option key is one or two capital letters or digits followed by a period. A line such as - [ ] Yes has no key prefix, so its whole label serves as the key, which is how confirm options are usually written. A trailing (rec) marks the recommended option.

Log entries and what they do to status

A log entry reads - @who TIME verb: value. The timestamp is UTC to the minute, written YYYY-MM-DDTHH:MMZ: the ISO 8601 shape that RFC 3339 also uses, but without seconds.

VerbStatus afterwards
answerresolved
partialpartial
deferdeferred
vetovetoed
reopen, rejectopen
approveresolved once the chain or quorum is complete, otherwise open
noteunchanged

A decision can list placeholder ballots, - [ ] @who, which an approval fills in as - [x] @who TIME approve. Approvals count only after the last reject or reopen, so one rejection restarts the chain. The ninth verb, edit, belongs to the edit trail.

A written value never contains a raw -->, which would close the comment early: writers store it as --\> and readers turn it back. A multi-line value continues on lines indented by two spaces, and blank lines are dropped.

File-level tags

oq:file carries the format version as v=0.1 and a people: list of lines like - @maria = Maria Lopez. Writing (agent) after a name marks a participant that is not a person. oq:edits holds one log line per edit to the prose outside the tags, such as edit: +3/-1 lines; the QnA MD editor adds these when someone changes the text in its Edit view.

Rules for anything that writes a tag

A conforming writer changes only the one tag it is answering and leaves every other byte of the file alone. Within that tag it edits the single status: line or inserts one if it is missing, appends log entries after log: (creating that line if absent), and fills the first placeholder ballot after the last rejection before appending a new one. A tag in a file with Windows line endings is written back with Windows line endings. These rules keep every answer a small diff.

Worked example: a rank and a two-line answer

This file has a people list, a rank tag with no status: line and a why: that runs onto a second line, and an ask:

<!-- oq:file v=0.1
people:
- @maria = Maria Lopez
- @builder = Build agent (agent)
-->

# Search page

<!-- oq:rank #search-02
by: @builder 2026-10-14T21:30Z
to: @maria
Q: Rank the result filters for the first release.
- [ ] A. Date
- [ ] B. Author
- [ ] C. File type
why: Only two filters fit on a phone screen
  without scrolling.
log:
-->

<!-- oq:ask #search-03
by: @builder 2026-10-14T21:31Z
to: @maria
status: open
Q: What should the empty-results message say?
log:
-->

Maria ranks File type first, then Date, then Author, and types a two-line answer whose second line contains an arrow. The reference writer produces:

<!-- oq:file v=0.1
people:
- @maria = Maria Lopez
- @builder = Build agent (agent)
-->

# Search page

<!-- oq:rank #search-02
status: resolved
by: @builder 2026-10-14T21:30Z
to: @maria
Q: Rank the result filters for the first release.
- [ ] A. Date
- [ ] B. Author
- [ ] C. File type
why: Only two filters fit on a phone screen
  without scrolling.
log:
- @maria 2026-10-15T08:05Z answer: C > A > B
-->

<!-- oq:ask #search-03
by: @builder 2026-10-14T21:31Z
to: @maria
status: resolved
Q: What should the empty-results message say?
log:
- @maria 2026-10-15T08:07Z answer: Nothing matched.
  Try fewer words, or widen the scope under Settings --\> Search.
-->

Three details are worth noticing. The rank tag had no status line, so one was inserted directly after the first line. The answer's second line is indented two spaces, and its arrow is stored as --\>, so the comment still ends where it should. And the parser reads the why: back as one sentence, joined with a space. The parser reports no warnings on either version.

Conventions we follow

The format allows more than is wise. These habits make files easier to answer and easier for an agent to act on:

  • Narrowest type first. Use confirm before choice, and ask only when the options cannot be listed. A tap is faster than typing on a phone, and a listed option cannot be misread.
  • One question per tag. Two questions in one tag get one answer, and nobody can tell which part it covers.
  • An id prefix per topic, such as rel- for a release or search- for search, numbered in order. Ids never change and are never reused; a revisited question gets a new id that names the old one.
  • A recommendation, with a reason. Mark one option (rec) and say why in why:. Put the source in ref: so the answer can be checked later.
  • Write only under your own handle. An agent never writes an answer, ballot or check mark for a person, and never changes an existing log line. It may add a note under its own handle.
  • Start every tag at the start of a line, outside lists and code blocks. The parser does not know about Markdown code fences, so a sample tag inside a fence in a QnA MD file becomes a live question.

For examples of these conventions in practice, see QnA MD use cases.

Sources

Read next

All in Building software with AI agents · Browse all topics