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:
@@ -14,3 +14,4 @@ list only shrinks.
|
||||
| `01-RESEARCH/034-the-mesh-in-domains/03-the-clashes.md` | kept | as above: every clash names the words that clashed |
|
||||
| `01-RESEARCH/034-the-mesh-in-domains/04-how-the-glossary-is-checked.md` | kept | as above: the check's own examples |
|
||||
| `01-RESEARCH/034-the-mesh-in-domains/05-a-glossary-by-domain.md` | kept | as above: the proposed glossary, with the entries of the day |
|
||||
| `01-RESEARCH/042-the-mesh-keeps-its-own-issues/00-overview.md` | kept | the effort that settled the word: it records the operator's ask in the words it was made in |
|
||||
|
||||
+11
-3
@@ -518,7 +518,7 @@ asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272.
|
||||
## The record — how this repository works
|
||||
|
||||
Not a domain of the mesh but of the way it is built, listed because its words meet the mesh's.
|
||||
**Recorded in** this repository. **Decided by** ADR 0019, 0080, 0244.
|
||||
**Recorded in** this repository and the issue tracker. **Decided by** ADR 0019, 0080, 0244, 0275.
|
||||
|
||||
- **research effort**, **graduation**, **hand-off**, **playbook** — an investigation in `01-RESEARCH/`;
|
||||
its closing into a decision and a design; a design given to a code repository; a documented workflow
|
||||
@@ -527,8 +527,14 @@ Not a domain of the mesh but of the way it is built, listed because its words me
|
||||
record, or corrected by a marked **progressive insight**. Always qualified in this repository: bare
|
||||
*record* has other meanings (see [Homonyms](#homonyms)).
|
||||
- **design** — a document in `03-DESIGN/`: **as-is** (what runs) or **to-be** (what is being built).
|
||||
- **issue** — a report in `04-ISSUES/` of something wrong with the mesh at the level of design or
|
||||
governance; an **incident** is a past event that taught a rule.
|
||||
- **issue** — a question about the mesh that someone must answer, numbered and tracked by the issue tracker;
|
||||
one at the level of design or governance also has a report in `04-ISSUES/` under the same number
|
||||
([ADR 0275](../02-DECISIONS/0275-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.md)).
|
||||
An **incident** is a past event that taught a rule.
|
||||
- **issue tracker** — the module that holds the mesh's issues and hands out their numbers, named `issues`,
|
||||
holding the seat `issue-tracker` ([53 — The issue tracker](../03-DESIGN/01-to-be/53-the-issue-tracker.md)).
|
||||
Not hits's *item*; bare *ticket* is a homonym (see [Homonyms](#homonyms)).
|
||||
*Not:* ~~ticketing system~~ (hq), ~~ticket system~~ (hq)
|
||||
- **hq check** — one of `00-META/checks/`, run by `merge-check.sh` as this repository's check.
|
||||
|
||||
## Homonyms
|
||||
@@ -575,6 +581,8 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical.
|
||||
| | a human | **operator** or **person** | words every domain uses |
|
||||
| firewall | what the mesh enforces | **packet filter** | Connectivity |
|
||||
| | what was found on a machine | **found firewall** | Connectivity |
|
||||
| ticket | an answer's one-time token (ADR 0258) | **answer ticket** | Operator and conversation |
|
||||
| | a question someone must answer | **issue** | The record |
|
||||
| deploy | the per-node part of a delivery plan | **deploy plan** | Change and delivery |
|
||||
| | a wrapped program's own deploy | its own word | — |
|
||||
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
---
|
||||
status: active
|
||||
status: graduated
|
||||
initiated: 2026-10-10
|
||||
touches: []
|
||||
became: []
|
||||
touches:
|
||||
- 02-DECISIONS/0019-how-this-repository-works.md
|
||||
- 02-DECISIONS/0080-the-development-cycle-is-checked.md
|
||||
- 00-META/process/03-issues.md
|
||||
- 00-META/glossary.md
|
||||
- the runtime's state verbs and the SDK's state interface
|
||||
became:
|
||||
- 03-DESIGN/01-to-be/53-the-issue-tracker.md
|
||||
- 02-DECISIONS/0275-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.md
|
||||
---
|
||||
|
||||
# 042 — The mesh keeps its own issues
|
||||
@@ -60,7 +67,9 @@ condition was silenced with the words "hq issue to follow", and nothing records
|
||||
and the asks of [ADR 0259](../../02-DECISIONS/0259-an-ask-is-a-message-on-the-bus-any-channel-answers-it-and-the-asker-acts-on-a-warrant.md).
|
||||
- The records module, which indexes this repository for agents.
|
||||
|
||||
`touches:` stays empty until the effort settles which of these it changes.
|
||||
**Graduated 2026-10-10** into [ADR 0275](../../02-DECISIONS/0275-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.md)
|
||||
and [to-be 53](../../03-DESIGN/01-to-be/53-the-issue-tracker.md). The operator delegated the five open questions; each
|
||||
was decided as recommended. `touches:` names what the decision changes.
|
||||
|
||||
## Words
|
||||
|
||||
|
||||
+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`.
|
||||
@@ -0,0 +1,174 @@
|
||||
---
|
||||
layer: to-be
|
||||
status: designed
|
||||
code: []
|
||||
updated: 2026-10-10
|
||||
decisions:
|
||||
- 02-DECISIONS/0275-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.md
|
||||
---
|
||||
|
||||
# 53 — The issue tracker
|
||||
|
||||
**The mesh keeps its issues in a module of its own, `issues`, one instance on the control-node. It hands out every
|
||||
issue's number from one counter that two callers can never both advance, holds each issue's status and links, refuses
|
||||
a change the development cycle would refuse, and announces every change on the bus. hq keeps a folder only for a design
|
||||
or governance issue, under the tracker's number.**
|
||||
([ADR 0275](../../02-DECISIONS/0275-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.md),
|
||||
from [research 042](../../01-RESEARCH/042-the-mesh-keeps-its-own-issues/00-overview.md).)
|
||||
|
||||
An **issue** is a question about the mesh that someone must answer. The **issue tracker** is the module that holds
|
||||
issues, named `issues`. The glossary holds both words.
|
||||
|
||||
**Acceptance.** An agent opens an issue and gets the next number; two agents opening at the same moment never get the
|
||||
same one.
|
||||
|
||||
This document designs slice 1 in detail. The slices after it are listed in [§ 8](#8-the-slices-after-the-first),
|
||||
each with what the operator sees when it is done.
|
||||
|
||||
## 1. The parts
|
||||
|
||||
```
|
||||
control-node
|
||||
|
||||
issues (seat issue-tracker, held once for the mesh)
|
||||
├ its process, launched by the node runtime
|
||||
├ bucket issues one key per issue, by number; a short history per key
|
||||
├ bucket counters one key per sequence; slice 1 has `issue` only
|
||||
├ setting owners the registered list `located-in` may name
|
||||
├ verbs open, get, list, move, note, link,
|
||||
│ served by the control-node's tool runner
|
||||
└ events opened, moved, noted, linked
|
||||
|
||||
any agent session, on any node
|
||||
mesh MCP server ── mesh_search, mesh_describe, mesh_call ──▶ the verbs, over the bus
|
||||
```
|
||||
|
||||
The module is written in Go against the SDK. It is built on the model of hits, a headless issue tracker on NATS, and
|
||||
carries none of its code; its README says so.
|
||||
|
||||
## 2. The seat and its verbs
|
||||
|
||||
The module declares the seat `issue-tracker` at mesh scope and claims it exclusively
|
||||
([ADR 0126](../../02-DECISIONS/0126-a-module-declares-its-own-seats.md)): there is one tracker per mesh. Its verbs are
|
||||
the seat's tools, served through the tool runner and announced on the bus
|
||||
([ADR 0197](../../02-DECISIONS/0197-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md)), so an
|
||||
agent finds them with `mesh_search` ("open an issue") and calls them as `issue-tracker.<verb>`.
|
||||
|
||||
| Verb | Takes | Does |
|
||||
|---|---|---|
|
||||
| `open` | a title, the report as text, optionally `located-in` and a folder path | checks the issue, then takes the next number and writes it; returns the number. Refuses an empty title or report, and an owner not on the registered list |
|
||||
| `get` | a number | the issue whole: fields, notes, links |
|
||||
| `list` | optionally a status, an owner, a phrase | compact rows, newest first, short enough to read in a chat: number, status, owner, title |
|
||||
| `move` | a number, a status, and what that status needs | changes the status under the rules of § 5 |
|
||||
| `note` | a number and a text | appends to the issue's notes; a note is never edited |
|
||||
| `link` | a number, a kind and a target, or `remove` | adds or removes a typed link |
|
||||
|
||||
Each verb's description says in plain words what it does and what it refuses, without the mesh's internal names, so
|
||||
an agent picks the right one from `mesh_search` alone. A refusal says what was wrong and what would be accepted.
|
||||
|
||||
**The caller is the bus credential, never an argument.** The runtime hands the module the identity of each call's
|
||||
caller; the module records that identity as who acted. No verb takes an actor.
|
||||
|
||||
## 3. What one issue holds
|
||||
|
||||
- `number` (its key), `title`, `status` (`open`, `diagnosing`, `located`, `resolved`, `wontfix`), `opened`, `closed`;
|
||||
- `located-in`: entries of the registered list of owners (repositories and modules), never free text;
|
||||
- `fixed-by` (pull requests or commits), `amended-design`, `replay` or `replay-none`, and a reason for `wontfix`;
|
||||
- `notes`: appended, each with who wrote it, when, and the text;
|
||||
- `links`, typed: to another issue (`duplicates`, `relates-to`), a pull request, a decision record, a design, a
|
||||
condition key or a delivery;
|
||||
- `record`: the path of the issue's folder in hq, when it has one.
|
||||
|
||||
**The registered list of owners** is a setting of the module, changed through the controller's settings verb like any
|
||||
other module setting. Slice 1 starts it with the repositories in hq's `00-META/repos.md` and the modules the catalogue
|
||||
registers.
|
||||
|
||||
**An issue that has a folder in hq** keeps its status in that folder's frontmatter until the import of slice 3 has run
|
||||
and been checked. Until then the tracker holds its number, title and `record`, takes its notes and links, and `move`
|
||||
refuses it with the reason: its status lives in its folder. An issue without a folder is whole in the tracker from the
|
||||
start. No status is written in two places for one issue.
|
||||
|
||||
## 4. State, the counter and events
|
||||
|
||||
**Buckets the module declares, the controller creates**
|
||||
([ADR 0201](../../02-DECISIONS/0201-a-module-keeps-its-current-state-in-key-value-buckets-it-declares-and-reaches-through-the-runtime.md)).
|
||||
The manifest's `state` names two buckets, `issues` and `counters`, with their options: `issues` keeps a short history
|
||||
per key, `counters` one value per key, neither expires. The module never creates a bucket. One issue is one value; at
|
||||
the 256 KiB a value may be, a note that would push an issue past it is refused with that reason (the largest issue
|
||||
folder in hq is 25 KiB).
|
||||
|
||||
**The counter is advanced by compare-and-set.** The runtime's state verbs are `get`, `put`, `delete`, `keys` and
|
||||
`watch`, and none of them refuses a write because the key changed since it was read. Slice 1 adds two, in the runtime
|
||||
and the SDK's state interface together:
|
||||
|
||||
- `create` writes a key only if it does not exist;
|
||||
- `update` writes a key only if its revision is still the one the caller read, and returns the new revision.
|
||||
|
||||
Whether the bus's own key-value store already offers these to the runtime is the first thing the build checks; it is
|
||||
the mechanism the design rests on either way. `open` then runs in this order:
|
||||
|
||||
1. validate the issue; a refused issue spends no number;
|
||||
2. read `counters/issue` and its revision;
|
||||
3. `update` it to the next number at that revision; if another caller moved it first, the write is refused, and
|
||||
`open` reads again and retries, a bounded number of times;
|
||||
4. `create` the issue's key; an existing key is a fault the module reports loudly and never overwrites.
|
||||
|
||||
So the counter does not rely on there being one instance: two instances, or two calls racing in one, still never hand
|
||||
out one number twice. **The counter starts** above the highest issue number on `main` and on every open branch of hq
|
||||
at the moment the module is switched on. Numbers already skipped stay unused, and a number never moves.
|
||||
|
||||
**Every change is an event the module declares** under `emits`
|
||||
([ADR 0041](../../02-DECISIONS/0041-events-are-a-relationship.md)): `opened`, `moved`, `noted`, `linked`. Each carries
|
||||
the number, what changed, and who changed it, so the events are the audit trail, and anything watching the bucket or
|
||||
the events sees a change as it happens. No event carries a secret, and an issue holds none.
|
||||
|
||||
## 5. The cycle's rules, at the write
|
||||
|
||||
`move` refuses what `cycle.py` refuses at merge today, and the rules playbook 03 states:
|
||||
|
||||
| The issue moves to | Needs |
|
||||
|---|---|
|
||||
| any status | one of the five |
|
||||
| `located` | `located-in` names at least one registered owner |
|
||||
| `resolved` | `located-in`, and `fixed-by` saying what fixed it |
|
||||
| `resolved`, for a core issue (its `located-in` names the controller, the node-engine, the tool runner, the SDK, or the catalogue's bus, forge or build agent) | `replay` (an id from mesh-lab's replays register) or `replay-none` saying why none is possible ([ADR 0237](../../02-DECISIONS/0237-a-change-is-judged-against-the-mesh-that-runs-before-it-merges-on-the-build-seat.md)) |
|
||||
| `wontfix` | a reason |
|
||||
|
||||
A refused move changes nothing and says which rule refused it. `cycle.py` keeps checking the folders that still carry
|
||||
status until slice 3.
|
||||
|
||||
## 6. How the operator and agents use it
|
||||
|
||||
Through the mesh's tools first. An agent opening an issue calls `issue-tracker.open`, gets the number, and, for a design
|
||||
or governance issue, writes `04-ISSUES/NNN-slug/` under it with the number in the report. Playbook 03's first step
|
||||
changes to that the day slice 1 is live; nothing else in playbook 03 changes in slice 1. `list` answers "what is open"
|
||||
in one call, from any session, and the responder on a channel answers from it once the seat exists.
|
||||
|
||||
## 7. How it is checked
|
||||
|
||||
| Rule | Checked by |
|
||||
|---|---|
|
||||
| Two opens at once never share a number | the module's test over a real bus: many concurrent `open` calls, from two instances, yield distinct consecutive numbers; the runtime's test that `update` at a stale revision is refused |
|
||||
| A refused open spends no number | the module's test: an invalid open, then a valid one, gets the next number |
|
||||
| An issue's key is never overwritten | the module's test: a `create` on an existing key is refused and reported |
|
||||
| The cycle's rules hold at the write | the module's table test over § 5, one case per row, each refused case changing nothing |
|
||||
| The module creates no bucket | the runtime refuses a module that tries (ADR 0201 §3); the controller's raise asserts both buckets |
|
||||
| The caller is who acted | the module's test that the recorded actor is the call's identity, whatever the arguments say |
|
||||
| Live | on the mesh, an agent calls `issue-tracker.open` and gets the next number; two sessions on two nodes open at the same moment and get two different numbers; `list` shows both |
|
||||
|
||||
## 8. The slices after the first
|
||||
|
||||
| Slice | Delivers | Done when the operator sees |
|
||||
|---|---|---|
|
||||
| 1 | The `issues` module: `open`, `get`, `list`, `move`, `note`, `link`; compare-and-set in the runtime and the SDK; playbook 03 takes its numbers from it | an agent opens an issue and gets the next number; two agents opening at the same moment never get the same one |
|
||||
| 2 | Decision and research numbers from the tracker, each its own key in `counters`; a number is reserved when a branch starts | two agents writing a decision at once get two numbers, and no hq pull request is renumbered |
|
||||
| 3 | `import`: every issue folder in hq read through the records module and written as an issue with its own number, idempotent and showing what it would change before it does; the four consolidation passes of research 042 (fixed, duplicates, owners, grouping), each a list confirmed before it is applied; an audit of `fixed-by` against the forge; then one pull request removes the status keys from every report, changes playbook 03 and `cycle.py`, and adds dated notes to ADR 0019 and 0080 | the not-closed list comes from one call and shrinks by what was already fixed or duplicated, each with its reason |
|
||||
| 4 | Conditions and pull requests linked: an issue links a condition by its key and the controller shows the link; a merged pull request notes every issue it names; after a week of that, the controller opens one issue per `healer-wanted` condition key by itself | a condition shows its issue, and a merge notes the issues it names |
|
||||
| 5 | A read-only view onto the `issues` bucket over the bus | the operator reads issues in a browser without an agent |
|
||||
|
||||
Once slice 1 runs, each slice after it is opened as an issue in the tracker, so the tracker carries its own work.
|
||||
|
||||
## What it leaves out
|
||||
|
||||
Priorities, claims, notifications, full-text or semantic search, the forge's own issue lists, and an index of any kind:
|
||||
none of them is in any slice above, and each needs a decision of its own.
|
||||
@@ -52,6 +52,7 @@ document is written and this one's status becomes `implemented`.
|
||||
| [`50-split-dns-on-a-machine-with-a-vpn-client.md`](50-split-dns-on-a-machine-with-a-vpn-client.md) | **In progress.** A machine whose VPN client pushes servers of its own runs a resolver of its own, on a node seat, that routes the VPN's domains over its link and every other name to the mesh's resolvers; the VPN client's module carries the adapter | [ADR 0247](../../02-DECISIONS/0247-a-machine-with-a-vpn-client-routes-names-by-domain-through-a-resolver-of-its-own.md), [ADR 0223](../../02-DECISIONS/0223-the-mesh-has-two-resolvers-and-a-machine-lists-only-them.md) |
|
||||
| [`51-the-registries-keep-what-is-named.md`](51-the-registries-keep-what-is-named.md) | **In progress.** The artifact store, the package registry and each machine's container runtime get tools that say what they hold, what names each thing and what could go; every removal is a dry run unless a person asks and says why, and what decides is always a record | [ADR 0251](../../02-DECISIONS/0251-the-registries-say-what-they-hold-and-keep-what-is-named-and-a-machine-keeps-the-image-it-runs-and-the-one-before.md), [ADR 0189](../../02-DECISIONS/0189-the-store-keeps-what-the-records-name.md) |
|
||||
| [`52-shares-and-mounts.md`](52-shares-and-mounts.md) | **In progress.** A node shares one folder at a time through `nfs-server`, which provides it as a provision to the private network only, mapping every client to the folder's owner; a node that asks mounts it through `mounts`, a mount unit and an automount per share, so nothing mounts or fails at boot; a device that comes and goes is an occasional source whose absence is a state | [ADR 0263](../../02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md), [ADR 0051](../../02-DECISIONS/0051-shared-data-is-the-operators.md), [ADR 0092](../../02-DECISIONS/0092-an-operator-delivers-a-pair-credential.md) |
|
||||
| [`53-the-issue-tracker.md`](53-the-issue-tracker.md) | **Designed.** The mesh keeps its issues in one module, `issues`, on the control-node: every number from one counter advanced by compare-and-set, the cycle's issue rules refused at the write, an event per change; hq keeps a folder only for a design or governance issue | [ADR 0275](../../02-DECISIONS/0275-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.md) |
|
||||
|
||||
## Not yet written
|
||||
|
||||
|
||||
Reference in New Issue
Block a user