Graduate research 042 into ADR 0275 and to-be 53, so issue numbers stop colliding
Numbers taken by reading a repository many branches write at once collide; one counter in a module of the mesh's own, advanced by compare-and-set, ends that at the source. The operator delegated the five open questions on 2026-10-10; each is decided as the research recommended. The glossary gains the issue tracker, retires "ticketing system" and lists ticket as a homonym, since ADR 0258 already uses it for an answer's one-time token.
This commit is contained in:
+156
@@ -0,0 +1,156 @@
|
||||
---
|
||||
topic: how we work
|
||||
status: accepted
|
||||
date: 2026-10-10
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
---
|
||||
|
||||
# 275. The mesh numbers and tracks its issues in a module of its own, and hq keeps only the narrative of a design or governance issue
|
||||
|
||||
## Context
|
||||
|
||||
[Research 042](../01-RESEARCH/042-the-mesh-keeps-its-own-issues/00-overview.md) measured, on 2026-10-10, how this
|
||||
repository keeps its issues, after the operator asked that day for an issue tracker on the bus that every current issue
|
||||
is consolidated into, started small and extended along the way.
|
||||
|
||||
**Numbers collide.** An author takes the next free number by reading `main` and every open pull request
|
||||
([playbook 03](../00-META/process/03-issues.md)), and many branches write at once. 31 commits on `main` mention a
|
||||
renumbering; [issue 155](../04-ISSUES/155-two-records-may-share-a-number-and-nothing-says-so/00-report.md) records a
|
||||
collision that reached `main` with every check passing; three decision numbers collided on the evening of 2026-10-07,
|
||||
and on 2026-10-10 an open pull request still carried a branch named for an issue number another branch had taken
|
||||
first. `cycle.py` refuses two records sharing a number, which catches a collision after it happens and prevents none.
|
||||
The operator said on 2026-10-07 that numbering belongs to the mesh's own issue tracker.
|
||||
|
||||
**Status is hard to see.** Of 357 issue folders on `main`, 73 were not closed (27 `open`, 45 `located`, 1
|
||||
`diagnosing`), and finding them means reading frontmatter across the repository. The frontmatter is a small, regular
|
||||
data set: six keys over 1.9 MiB of markdown, well inside the limits
|
||||
[ADR 0201](0201-a-module-keeps-its-current-state-in-key-value-buckets-it-declares-and-reaches-through-the-runtime.md)
|
||||
§6 sets for a bucket. Nothing connects a condition the controller raises to the issue that would fix it: on 2026-10-10
|
||||
a condition was silenced with the words "hq issue to follow", and nothing records whether one followed.
|
||||
|
||||
**hits**, the headless issue tracker the operator named as inspiration, was studied at release v0.7.0 (findings §6). Its
|
||||
model is this repository's frontmatter almost key for key, and it numbers an issue by compare-and-set on a counter only
|
||||
after the issue is validated. Its licence (Sustainable Use License 1.0) limits distribution, its services create their
|
||||
own bus resources on start, and its words (*item*, *project*, *release*) clash with the glossary.
|
||||
|
||||
The research left five questions for the operator, each with a recommendation. **The operator delegated the choice on
|
||||
2026-10-10**: the recommendations are taken, in the spirit of the original ask, "start small and extend the module
|
||||
along the way".
|
||||
|
||||
### Against GENESIS
|
||||
|
||||
- **The mesh owns the truth** ([mission](../00-META/mission.md)). An issue's number and status live in the mesh, in one
|
||||
place, written through one module, instead of in a file every branch may edit.
|
||||
- **The mesh remembers** ([effect](../00-META/effect.md)). The public narrative of a design or governance issue stays
|
||||
here, reviewed in pull requests, so the symptom-to-component memory is kept.
|
||||
- **Failure must be loud.** The cycle's issue rules refuse a wrong write at the moment it is made, instead of a check
|
||||
reporting it at merge.
|
||||
|
||||
## Considered Options
|
||||
|
||||
### Where the truth lives (open question 1)
|
||||
|
||||
1. **Split: the tracker holds every issue's number and status; hq keeps a folder only for a design or governance issue,
|
||||
pointing at the tracker by number.** Adopted.
|
||||
2. **Everything moves into the tracker.** Rejected: the public record of design questions, which hq exists to be
|
||||
([ADR 0019](0019-how-this-repository-works.md)), would be lost, and with it the memory playbook 03 says is never
|
||||
deleted.
|
||||
3. **The tracker mirrors hq.** Rejected: hq would still hand out numbers, so the collisions stay, and a second copy of
|
||||
every status can drift from the first.
|
||||
|
||||
### Whose code (open question 2)
|
||||
|
||||
1. **Run hits as it is.** Rejected: its services create their own stream and bucket, which ADR 0201 §3 forbids a module
|
||||
to do; its subjects assume one tracker per bus account; its actor is named by the caller rather than proven by the
|
||||
bus credential; its words clash with the glossary.
|
||||
2. **Vendor or fork hits into the catalogue.** Rejected: its licence is not an open-source licence, and vendoring binds
|
||||
the catalogue to it for as long as the code is there; changing the points above means owning a fork of a young,
|
||||
single-author project.
|
||||
3. **Build our own module in Go on hits's model.** Adopted. Its statuses, fields, append-only notes, typed links,
|
||||
compare-and-set numbering and its auditor are copied as ideas, and the module's README says so.
|
||||
|
||||
### Which numbers (open question 3)
|
||||
|
||||
1. **Issue numbers only.** Rejected: decision numbers collided three times in one evening, and the same counter serves
|
||||
any sequence.
|
||||
2. **Issue, decision and research numbers, all from the tracker.** Adopted, with decision and research numbers in
|
||||
slice 2.
|
||||
|
||||
### The word (open question 4)
|
||||
|
||||
1. **Adopt the operator's word *ticket*.** Rejected: *issue* is already the glossary's word for this thing, and one word
|
||||
names one thing ([ADR 0244](0244-the-mesh-is-described-in-domains-and-one-word-names-one-thing.md)). *Ticket*
|
||||
also already names something else: an answer's one-time token
|
||||
([ADR 0258](0258-a-desk-click-performs-only-an-acknowledgement-and-an-answer-is-a-one-time-ticket.md)).
|
||||
2. **Adopt hits's word *item*.** Rejected for the same reason.
|
||||
3. **Keep *issue*; call the module that holds them the *issue tracker*, named `issues`.** Adopted.
|
||||
|
||||
### Conditions (open question 5)
|
||||
|
||||
1. **The controller opens an issue by itself for every `healer-wanted` condition, from the start.** Rejected for now:
|
||||
an automatic opener with no week of linked data behind it can flood the tracker with issues nobody reads.
|
||||
2. **The controller only links issues to conditions until slice 4 has run for a week; then it opens one issue per
|
||||
`healer-wanted` condition key by itself.** Adopted.
|
||||
|
||||
## Decision
|
||||
|
||||
1. **The issue tracker is a Go module, `issues`, with one instance on the control-node.** It declares and claims one
|
||||
seat held once for the mesh, `issue-tracker`
|
||||
([ADR 0126](0126-a-module-declares-its-own-seats.md)). It is built on hits's model and carries none of its code.
|
||||
2. **The tracker is the truth for every issue's number, status and links.** Every issue is opened in the tracker and
|
||||
takes its number from it, including one that needs no document here. A design or governance issue also gets a
|
||||
folder `04-ISSUES/NNN-slug/` under the tracker's number, whose narrative is reviewed in pull requests as today.
|
||||
Status lives in one place.
|
||||
3. **A number comes from one counter, advanced by compare-and-set, and only after the issue is valid.** A refused
|
||||
open spends no number, and two callers opening at the same moment never get the same one. The runtime's state
|
||||
verbs gain a write that is refused when the key changed since it was read; the counter does not rely on there
|
||||
being one instance. The counter starts above the highest number on `main` and on every open branch at the moment of
|
||||
the switch. A number never moves again, and numbers already skipped stay unused.
|
||||
4. **State is in buckets the module declares** (ADR 0201): the controller creates them, the module never does.
|
||||
**Every change is an event the module declares** ([ADR 0041](0041-events-are-a-relationship.md)), carrying what
|
||||
changed and who changed it, so the events are the audit trail.
|
||||
5. **The caller is the bus credential, never an argument.** The module records who acted from the identity the
|
||||
runtime gives it with each call.
|
||||
6. **The cycle's issue rules are enforced at the write.** A status outside the five is refused; `located` and
|
||||
`resolved` need `located-in`; `resolved` needs `fixed-by`; a core issue resolves with `replay` or `replay-none`
|
||||
([ADR 0237](0237-a-change-is-judged-against-the-mesh-that-runs-before-it-merges-on-the-build-seat.md));
|
||||
`wontfix` needs a reason. `located-in` names an entry of a registered list of repositories and modules.
|
||||
7. **The word is *issue*.** The module is the *issue tracker*, named `issues`; *ticketing system* is retired in the
|
||||
glossary, and the bare word *ticket* is a homonym that never means an issue.
|
||||
8. **It is built in slices, each ending at something the operator sees.** Slice 1 is the module with `open`, `get`,
|
||||
`list`, `move`, `note` and `link`, and playbook 03 takes an issue's number from it. Slice 2 hands out decision and research numbers too. Slice 3 imports the issue
|
||||
folders and consolidates them, after which one pull request removes the status keys from every report and changes
|
||||
playbook 03 and `cycle.py` together. Slice 4 links conditions and pull requests. Slice 5 is a read-only view onto
|
||||
the tracker's bucket. The design is [to-be 53](../03-DESIGN/01-to-be/53-the-issue-tracker.md); once slice 1 runs,
|
||||
each slice after it is an issue in the tracker itself.
|
||||
9. **Conditions are linked, not opened, until slice 4 has run for a week.** A condition is never closed by closing an
|
||||
issue: a condition is cleared only by observation
|
||||
([ADR 0231](0231-a-healer-acts-on-what-observation-raised-and-only-observation-says-it-worked.md)).
|
||||
|
||||
## Consequences
|
||||
|
||||
- Collisions end at the source: a number is taken from one counter, not read from a repository many branches write.
|
||||
- Until the slice 3 import has run and been checked against it, an issue that has a folder here keeps its status in
|
||||
that folder's frontmatter, and the tracker holds its number, title and folder only. An issue without a folder is
|
||||
whole in the tracker from the start. No status is written in two places for the same issue at any time.
|
||||
- [ADR 0019](0019-how-this-repository-works.md)'s rule that statuses live only in frontmatter stops holding for issues
|
||||
on the day slice 3's pull request lands, and part of [ADR 0080](0080-the-development-cycle-is-checked.md)'s check
|
||||
moves from `cycle.py` into the tracker's `move`. That pull request adds a dated note to each of them saying where
|
||||
the mechanism now lives; until then both stand unchanged.
|
||||
- An agent opening an issue needs the mesh to be reachable. A node that cannot reach the control-node cannot take a
|
||||
number, and says so, rather than guessing one.
|
||||
- The SDK's state interface gains a compare-and-set write, which every module keeping a counter can use; it is a
|
||||
contract change under [ADR 0039](0039-what-the-sdk-holds-and-refuses.md), and every module should be rebuilt
|
||||
against it.
|
||||
- What got harder: one more module the mesh's own process depends on. hq's checks today run from a checkout alone;
|
||||
from slice 2 on, writing a decision or an issue here needs the tracker.
|
||||
|
||||
## References
|
||||
|
||||
- [Research 042](../01-RESEARCH/042-the-mesh-keeps-its-own-issues/00-overview.md) and its
|
||||
[findings](../01-RESEARCH/042-the-mesh-keeps-its-own-issues/01-findings.md).
|
||||
- [To-be 53 — The issue tracker](../03-DESIGN/01-to-be/53-the-issue-tracker.md).
|
||||
- hits, `github.com/impire-io/hits`, release v0.7.0: the model studied, not the code used.
|
||||
- [Issue 155](../04-ISSUES/155-two-records-may-share-a-number-and-nothing-says-so/00-report.md): the collision that
|
||||
reached `main`.
|
||||
Reference in New Issue
Block a user