Why
Issue and change-request ids are 26-character ULIDs, and the short form shown in the CLI and the web is an 8-character prefix of the ULID. A ULID’s first ten characters are a timestamp, so those prefixes are nearly constant for a whole era of issues: in this repository 27 issues and 17 change requests share only seven distinct six-character prefixes, and four pairs already collide at eight. The alias people actually type is therefore the longest part of the id with the least information in it.
A small ordinal is what people remember and say out loud, and [#42](/r/vole/n/42) is the
convention every developer already knows. That means a shared sequence across
issues and change requests, GitHub-style, with the ULID remaining the identity:
paths, front matter, closes:, backlinks and the audit log are unchanged, and no
file gains a new field.
Reassigning numbers server-side at push time was considered and rejected: it adds a second serialized write on the one linear branch per issue, needs a ledger that must never reuse a number, leaves a window where an issue exists without a number, and makes numbers differ between two instances serving the same history. Deriving the number from creation order in the branch history is deterministic, needs no write, retroactively numbers existing issues with no backfill, and is the same answer on every clone.
What
numberis the 1-based rank of an entity’s creation commit in the linearvolebranch history, over the union ofissues/<ulid>/issue.mdandrequests/<ulid>/request.md.- One implementation, used by the CLI and the server, so
[#42](/r/vole/n/42)in prose always matches whatvole issue listprints. - The index in
data/index.dbcaches the mapping; it stays rebuildable withvole admin index rebuildand is not authoritative. - Determinism: within one commit that adds several entities, order by ULID (which is creation order); a path deleted and re-added keeps its first number.
- Resolution: a bare number is an alias; a 26-character ULID is an id; anything
else is a ULID prefix, as today.
[#42](/r/vole/n/42)in a body links to the entity; the web serves/r/<repo>/n/42as a kind-agnostic redirect. vole change new --closes 42and the browser’s Closes field resolve the alias to the ULID before writing, so nothing stored changes.
Scope
- shared numbering in
metadataover onegit logwalk, with tests -
forge/indextable plus rebuild, and lookups by number and by id - client resolution for issues and change requests,
%(number)verb -
vole issue newprints the number it got - issue and request pages and lists show
#Ninstead of the 8-character slice -
#Ncross-references and the/r/<repo>/n/<number>route -
--closesaccepts a number - docs:
docs/issues.md,docs/change-requests.md,docs/cli.md,docs/architecture.md,CONTRIBUTING.md
Acceptance criteria
vole issue list --format '%(number) %(title)'numbers issues and change requests in one sequence, oldest first, gap-free, and matches the web.vole issue show 42,vole change show 42and/r/<repo>/n/42all reach the same entity, and naming the wrong kind says so rather than guessing.- A repository with existing issues needs no migration: numbers appear from history alone.
vole admin index rebuildreproduces the same numbers.- Editing or closing an entity does not change its number; deleting one does not give its number to another.