diff --git a/00-META/checks/words-allowed.md b/00-META/checks/words-allowed.md index 824a7624..b3d99a77 100644 --- a/00-META/checks/words-allowed.md +++ b/00-META/checks/words-allowed.md @@ -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 | diff --git a/00-META/glossary.md b/00-META/glossary.md index 98e7f073..55fdab79 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -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 | — | diff --git a/01-RESEARCH/042-the-mesh-keeps-its-own-issues/00-overview.md b/01-RESEARCH/042-the-mesh-keeps-its-own-issues/00-overview.md index 5a3e8823..b4272350 100644 --- a/01-RESEARCH/042-the-mesh-keeps-its-own-issues/00-overview.md +++ b/01-RESEARCH/042-the-mesh-keeps-its-own-issues/00-overview.md @@ -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 diff --git a/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 b/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 new file mode 100644 index 00000000..fab9c52b --- /dev/null +++ b/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 @@ -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`. diff --git a/03-DESIGN/01-to-be/53-the-issue-tracker.md b/03-DESIGN/01-to-be/53-the-issue-tracker.md new file mode 100644 index 00000000..e9be03b7 --- /dev/null +++ b/03-DESIGN/01-to-be/53-the-issue-tracker.md @@ -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 | 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. diff --git a/03-DESIGN/01-to-be/README.md b/03-DESIGN/01-to-be/README.md index 0ad621dc..4620cef9 100644 --- a/03-DESIGN/01-to-be/README.md +++ b/03-DESIGN/01-to-be/README.md @@ -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