diff --git a/.claude/skills/hq-diagnose/SKILL.md b/.claude/skills/hq-diagnose/SKILL.md index 3fe1e8d6..d30b3d90 100644 --- a/.claude/skills/hq-diagnose/SKILL.md +++ b/.claude/skills/hq-diagnose/SKILL.md @@ -24,14 +24,16 @@ result is not "nothing to learn" — it is the reason the next person repeats th ## Steps 1. Read the issue: `mesh_call issue-tracker.get` with its number, and `00-report.md` when it has a - folder. Move it to `diagnosing` with `issue-tracker.move`. + folder. Before you start, move it to `diagnosing` with `issue-tracker.move` and `by`, your name and node as + who@node (such as `fix-399@laptop`). The tracker refuses it while another worker is current, naming them. 2. Investigate. The trail goes in `issue-tracker.note`, **dated** in its text, including what was ruled out and how; for an issue with a folder, in `01-diagnosis.md` there as well. Archaeology — which commit, which pull request, which date a behaviour changed — is the most valuable content. 3. When the owner is known, `issue-tracker.move` to `located` with `located-in`: entries of the tracker's registered list of owners (repositories, and modules as `mesh-catalog modules/`); the tracker refuses an owner it does not know. -4. On resolution: `issue-tracker.move` to `resolved` with `fixed-by`, and `replay` or `replay-none` +4. Before you start the fix: `issue-tracker.move` to `fixing` with `by`. +5. On resolution: `issue-tracker.move` to `resolved`, taken only from `fixing`, with `fixed-by`, and `replay` or `replay-none` for a core issue. If the root cause was a design gap, run `hq-graduate` for the amendment and give `amended-design`. Nothing of this is written in the report's frontmatter. diff --git a/.claude/skills/hq-status/SKILL.md b/.claude/skills/hq-status/SKILL.md index 34d56fa5..d165ed27 100644 --- a/.claude/skills/hq-status/SKILL.md +++ b/.claude/skills/hq-status/SKILL.md @@ -16,7 +16,7 @@ generated on demand and never written back to disk. | Research | `01-RESEARCH/NNN-*/00-overview.md` | `status` (`active` / `graduated` / `abandoned`), `initiated`, `touches`, `became` | | Design | `03-DESIGN/**/*.md` (not READMEs) | `layer` (`as-is` / `to-be`), `status` (`designed` / `in-progress` / `implemented` / `abandoned`), `code`, `updated`, `decisions` | | Decisions | `02-DECISIONS/NNNN-*.md` | `status` (`proposed` / `accepted` / `superseded`), `date`, `deciders`, `reconstructed`, `superseded-by`, `extends` | -| Issues | the issue tracker, not a file: `mesh_call issue-tracker.list` (optionally `status`, `owner`, `phrase`) and `issue-tracker.get` | `status` (`open` / `diagnosing` / `located` / `resolved` / `wontfix`), `opened`, `located-in`, `fixed-by`, `amended-design`, `record` (the folder here, when it has one) | +| Issues | the issue tracker, not a file: `mesh_call issue-tracker.list` (optionally `status`, `owner`, `phrase`) and `issue-tracker.get` | `status` (`open` / `diagnosing` / `located` / `fixing` / `resolved` / `wontfix`), `worker` and `stale` while `diagnosing` or `fixing`, `opened`, `located-in`, `fixed-by`, `amended-design`, `record` (the folder here, when it has one) | ## Steps diff --git a/00-META/glossary.md b/00-META/glossary.md index b9662d74..b6d30706 100644 --- a/00-META/glossary.md +++ b/00-META/glossary.md @@ -551,7 +551,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 and the issue tracker. **Decided by** ADR 0019, 0080, 0244, 0275. +**Recorded in** this repository and the issue tracker. **Decided by** ADR 0019, 0080, 0244, 0275, 0283. - **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 @@ -576,6 +576,11 @@ Not a domain of the mesh but of the way it is built, listed because its words me duplicate (the **duplicate check**), its **severity** and what it **needs**, a suggested owner, and the issues it depends on or relates to; it fills only what nobody gave ([ADR 0279](../02-DECISIONS/0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md)). - **ready** — of an issue: every issue it depends on is wontfix, or resolved with its fix delivered. +- **worker** — of an issue: the agent or person, with its node (who@node), that moved it to `diagnosing` or `fixing` + and works on it; another is refused while the worker is current, unless it gives a reason to **take it over**. A + worker with nothing done on the issue for a day is **stale**. **Fixing** is the status an issue is at while its fix + is worked on; an issue is resolved only from it ([ADR 0283](../02-DECISIONS/0283-an-agent-moves-an-issue-to-fixing-under-its-own-name-and-an-issue-is-resolved-only-from-fixing.md)). + Not a *claim* or a *holder*, which are a module's on a seat. - **hq check** — one of `00-META/checks/`, run by `merge-check.sh` as this repository's check. ## Homonyms diff --git a/00-META/process/03-issues.md b/00-META/process/03-issues.md index eebce869..f86f40ff 100644 --- a/00-META/process/03-issues.md +++ b/00-META/process/03-issues.md @@ -71,10 +71,20 @@ import read, which every imported issue names (`imported.commit` in `issue-track drift. **An issue wholly in the tracker** is done at this step. -2. Investigate. The trail goes in `issue-tracker.note`, and, for a folder-backed issue, in `01-diagnosis.md` in the - same folder as well — dated, including what was ruled out. The status moves through `issue-tracker.move`: - to `diagnosing`, then to `located` with `located-in`, which the tracker refuses without a registered owner. -3. Resolve. `issue-tracker.move` to `resolved` with `fixed-by`, and, if the root cause was a design gap, +2. Investigate. **Before you start, move the issue to `diagnosing` with `by`**: who works on it and its node, as + who@node (`fix-399@laptop`, `operator@workstation`). The tracker keeps you as the issue's **worker**, and `get` and `list` + show it with since when. The trail goes in `issue-tracker.note`, and, for a folder-backed issue, in + `01-diagnosis.md` in the same folder as well — dated, including what was ruled out. A note keeps you the worker; a + worker with nothing done on the issue for a day is stale. Then move it to `located` with `located-in`, which the + tracker refuses without a registered owner; that ends your work on it. +3. Fix. **Before you start the fix, move the issue to `fixing` with `by`**, even when you diagnosed it yourself. The + tracker refuses the move while another worker is current, naming them: ask them, or take it over with + `take-over` saying why, which is noted on the issue. A stale worker may be taken over from without a reason. + Name the fix in `fixed-by` when it merges, with a move to `fixing` that gives `by` again; the issue stays + `fixing` until the acceptance is proven live + ([ADR 0283](../../02-DECISIONS/0283-an-agent-moves-an-issue-to-fixing-under-its-own-name-and-an-issue-is-resolved-only-from-fixing.md), + [ADR 0281](../../02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md)). +4. Resolve. `issue-tracker.move` to `resolved`, which the tracker takes only from `fixing`, with `fixed-by`, and, if the root cause was a design gap, run playbook [02](02-graduation.md) and give `amended-design`. **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 — resolves with `replay`, the id of its replay in mesh-lab's replays register, proved to fail on @@ -97,8 +107,11 @@ import read, which every imported issue names (`imported.commit` in `issue-track - An issue whose answer is a general lesson should also be written to the knowledge base, so the next person searching a symptom finds it. Both, not either. - `wontfix` is legitimate and requires a sentence saying why; the tracker refuses it without one. -- **A rule states how it is checked.** The cycle's rules on an issue — a status one of the five, `located` with an - owner, `resolved` with a fix, a core issue with its replay, `wontfix` with a reason — are refused at the write by +- **An agent says when it starts work on an issue**, by moving it to `diagnosing` or `fixing` with `by`. Two agents + never start the same work without knowing: the second is refused, naming the first. +- **A rule states how it is checked.** The cycle's rules on an issue — a status one of the six, `diagnosing` and + `fixing` with a worker and no other current one, `located` with an owner, `resolved` only from `fixing` and with a + fix, a core issue with its replay, `wontfix` with a reason — are refused at the write by `issue-tracker.move` (mesh-catalog `modules/mesh-issues`, its table test over the rules). `cycle.py` checks what this repository still holds: no two folders share a number, every report has frontmatter with the day it was `opened:`, and no report carries a status key. 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 index f0b69f70..dffa4f0d 100644 --- 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 @@ -116,6 +116,13 @@ along the way". `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. + + > **The mechanism changed — 2026-10-10, by [ADR 0283](0283-an-agent-moves-an-issue-to-fixing-under-its-own-name-and-an-issue-is-resolved-only-from-fixing.md).** + > What still stands: the cycle's rules are enforced at the write, and §5 for who acted. What moved: there are six + > statuses, `fixing` between `located` and `resolved`; a move to `diagnosing` or `fixing` names the issue's worker + > in `by`, which is not the actor of §5 but who works on it as they state it; and `resolved` is reached only from + > `fixing`. + 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`, diff --git a/02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md b/02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md index 6795afff..f225efd4 100644 --- a/02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md +++ b/02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md @@ -84,6 +84,11 @@ therefore name a fix in `fixed-by` when it merges, with the issue still `located `move` to the status an issue already has is taken when it changes what the issue holds, such as its `fixed-by`, and refused when it changes nothing. `move` still requires `fixed-by` to resolve. +> **The mechanism changed — 2026-10-10, by [ADR 0283](0283-an-agent-moves-an-issue-to-fixing-under-its-own-name-and-an-issue-is-resolved-only-from-fixing.md).** +> What still stands: delivered is not resolved, and an issue is resolved after the proof. What moved: the fix is +> named while the issue stays `fixing`, not `located`, because an issue is resolved only from `fixing`, and a move +> to `fixing` names its worker in `by`. + ### 4. A stuck fix is loud A fix merged and not delivered after **two hours** (the module's setting `stuck-after`) is said once: a note on the diff --git a/02-DECISIONS/0283-an-agent-moves-an-issue-to-fixing-under-its-own-name-and-an-issue-is-resolved-only-from-fixing.md b/02-DECISIONS/0283-an-agent-moves-an-issue-to-fixing-under-its-own-name-and-an-issue-is-resolved-only-from-fixing.md new file mode 100644 index 00000000..3aef27a0 --- /dev/null +++ b/02-DECISIONS/0283-an-agent-moves-an-issue-to-fixing-under-its-own-name-and-an-issue-is-resolved-only-from-fixing.md @@ -0,0 +1,169 @@ +--- +topic: how we work +status: accepted +date: 2026-10-10 +deciders: jochen +reconstructed: false +extends: 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 +--- + +# 283. An agent moves an issue to fixing under its own name, and an issue is resolved only from fixing + +## Context + +On 2026-10-10 the operator asked that an agent say when it starts work on an issue, and chose the word for the +status it moves the issue to: **fixing**. The statuses become open → diagnosing → located → fixing → resolved, or +wontfix. + +**What the tracker holds, measured on 2026-10-10** (`issue-tracker.list`): 43 issues at `open`, 1 at `diagnosing`, +51 at `located`. Nothing on an issue says who works on it. Two agents can start on the same issue and neither sees +the other. Every move is recorded as by `unproven`: the runtime does not hand a module the identity of its caller +(issue 340), so [ADR 0275](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) +§5 ("the caller is the bus credential, never an argument") records nobody in particular. And an issue moves to +`resolved` from any status, so nothing shows that anyone said they started the fix before it was resolved. + +[To-be 53](../03-DESIGN/01-to-be/53-the-issue-tracker.md) names "claims" under *What it leaves out*, as needing a +decision of its own. This is that decision. The glossary already uses *claim* for a module taking a spot on a seat, +and *holder* for the module that holds a seat, so neither names the person or agent working on an issue. The +glossary has no word for that; this record proposes **worker**, added to the glossary in the same change. + +The record was searched first ("claim", "who works on", "assignee", "fixing", "take over"): to-be 53's *What it +leaves out*, ADR 0275 §5 and §6, and [ADR 0281](0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md) +§3, which has an agent name a fix while the issue stays `located`. + +## Considered Options + +### How the worker is named + +1. *The caller's identity, as the runtime proves it.* Not possible as the mesh stands: the runtime does not say who + called (issue 340). When it does, a node's credential is shared by every session on that node, so two agents on + one machine would still read as one. +2. **`by`, given by the worker on the move, as who@node.** Chosen. The worker states its own name. ADR 0275 §5 + still holds for *who acted*: the tracker keeps recording that from the runtime, never from an argument. `by` names + who works on the issue, which is a different fact. Because a worker names itself, the refusal below lets honest + workers see each other; it does not stop a worker that gives a false name. +3. *An assignee, set by the operator or the triage.* Refused. Nobody hands out work: agents choose it from `list`, + and the triage estimates and does not decide. + +### What a second worker meets + +1. *Nothing: both are shown.* Refused. Two agents starting the same fix without knowing is what this record prevents. +2. **A refusal naming the worker, unless the move says why it takes the issue over; the reason is noted.** Chosen. +3. *A refusal with no way past it until the worker moves on.* Refused. An agent whose session ended would keep the + issue forever. + +### When a worker counts as gone + +1. *A fixed time after the worker started.* Refused. A long fix that keeps writing notes would lose its issue. +2. **A time with nothing done on the issue: no move, note, link or set by a caller.** Chosen. The tracker's own + writes (the triage, the duplicate check, the delivery's notes) do not count, because they say nothing about + whether the worker is still there. + +### What may be resolved + +1. *Any issue with a fix named, as today.* Refused. Nothing shows that anyone started the fix. +2. **Only an issue at `fixing`.** Chosen. This is the check that the agent rule holds: to resolve an issue, someone + must first have moved it to `fixing` under their own name. + +## Decision + +### 1. Six statuses + +An issue is `open`, `diagnosing`, `located`, `fixing`, `resolved` or `wontfix`. **Fixing** means someone works on its +fix. `wontfix` is unchanged: it is reached from any status, with a reason. + +### 2. A move to diagnosing or fixing names its worker + +A move to `diagnosing` or `fixing` needs `by`: the issue's **worker**, the agent or person who works on it and the +node it works on, written as who@node (`fix-399@laptop`, `operator@workstation`). These are the limits: + +- `who` is letters, digits and `.`, `_`, `/`, `-`; +- `node` is lower-case letters, digits and `-`; +- the whole is at most 120 bytes and is kept in lower case; +- it is refused when it has the shape of a secret, like any field of an issue. + +The issue keeps its worker (`by`, `since`, and `active`, the last time a caller did anything on it) while it stays +at `diagnosing` or `fixing`. A move to any other status ends the work. The same worker moving the issue from +`diagnosing` to `fixing` keeps its `since`. A move to `fixing` that names a fix while the issue stays `fixing` is a +move to `fixing` too, and needs `by`. `by` on a move to any other status is refused. `get` shows the worker, and +each row of `list` shows `worker`, `since` and `stale`. + +### 3. Another worker is refused while the worker is current + +A move to `diagnosing` or `fixing` by a different worker is refused while the worker is current. The refusal names +the worker, since when it has worked on the issue, and when it was last active. A move that gives `take-over` with a +reason takes the issue over, and a note on the issue says who took it from whom and why. `take-over` is refused +when nobody else works on the issue, and on a move to a status that has no worker. + +### 4. A worker who did nothing for a day is stale + +A worker with no move, note, link or set by a caller on the issue for `worker-stale-after` (a setting of the module, +**24 hours** by default) is **stale**. `list` says `stale`, `get` says `worker-stale`, and another worker may take the +issue over without a reason; the take-over is still noted. 24 hours is a working day and a night. An agent whose +session ended, or whose machine is asleep, frees the issue by the next day. An agent that is still working keeps it +by noting what it found, which playbook 03 already asks for. + +### 5. Resolved is reached only from fixing + +A move to `resolved` is refused unless the issue is at `fixing`. An issue already resolved may still have what it +holds corrected. The other rules for `resolved` (`located-in`, `fixed-by`, and a replay for a core issue) are +unchanged. + +### 6. The event and the reads carry the worker + +The `moved` event carries: + +- `by`: the worker, when the move names one; who acted, for a move that names none; +- `since`: when the worker started; +- `caller`: who acted, as the runtime proves it (`unproven` today); +- `took-over-from` and `take-over`: on a take-over; +- `released`: the worker whose work the move ended. + +`get` carries `worker` and `worker-stale`. Another module can read who works on an issue from either. No event +carries a secret, because `by` and the take-over reason are refused when they have the shape of one. + +### 7. Fixing is not closed + +The duplicate check, the triage, readiness and the ready-to-verify note treat `fixing` like `diagnosing` and +`located`. A fix is named in `fixed-by` while the issue stays at `fixing`, and the issue is resolved after the proof +(ADR 0281 §3, which said `located`). + +### 8. Agents say when they start + +An agent moves an issue to `diagnosing` or `fixing` with `by` before it starts work on it. Playbook 03 says so. The +rule for every session is proposed through the agent module's shared instructions and taken on the operator's +warrant ([ADR 0274](0274-an-agent-proposes-a-shared-instruction-section-and-every-node-takes-it-only-on-the-operators-warrant-which-it-proves-itself.md)). + +## Consequences + +- `list` shows who works on what and since when. A second agent learns of the first before it starts, not after + both have opened pull requests. +- Each of the 51 issues at `located` now goes through `fixing` before it is resolved. An agent that resolves one + names itself first. +- `by` is what the worker says it is. A false name passes, and the tracker cannot tell. The proven caller is + tracked by issue 340. +- A worker that stops without moving the issue on keeps others out for up to 24 hours, unless they give a reason. + The reason is on the issue for the operator to read. +- The next step is that an alert names the issue tracking its condition and that issue's state in plain words, such + as "an agent on the laptop is fixing it since 21:10". That needs a link from an issue to a condition key or kind, and is + tracked by issue 400. This record makes the worker readable for it, from `get` and from the `moved` event. + +## How it is checked + +| Rule | Checked by | +|---|---| +| A move to diagnosing or fixing names its worker as who@node; `by` or `take-over` on another status is refused; a `by` with a secret's shape is refused | the tracker's table test over the cycle's rules (mesh-catalog `modules/mesh-issues`), one row per refusal, each refused row changing nothing and saying no event | +| Another worker is refused while the worker is current, naming the worker | the tracker's test of a second worker at `diagnosing` and at `fixing`, 23 hours after the first: refused, the issue unchanged, no event | +| A take-over gives a reason, which is noted; the event says from whom | the tracker's take-over test: the note, the worker, and `took-over-from` and `take-over` on the `moved` event | +| A stale worker is shown and may be taken over from without a reason; a caller's note keeps the worker current, the tracker's own note does not; the bound is the setting | the tracker's stale-worker tests, with a clock the test moves | +| **Resolved only from fixing.** This is the check that the agent rule holds: no issue is resolved unless someone first moved it to `fixing` under their own name | the table test's rows resolving from `open`, `located` and `diagnosing`, each refused; live, a resolve of an issue at `located` is refused | +| Fixing is not closed | the tracker's test that every status but `resolved` and `wontfix` is offered to the triage | +| Live | an agent moves its issue to `fixing` with `by`, and `get` and `list` show the worker with since; a resolve of an issue at `located` is refused, naming the rule | + +## References + +- [ADR 0275](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): the tracker and its rules, which this record extends +- [ADR 0281](0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md): delivered is not resolved; the fix is now named while the issue is `fixing` +- [ADR 0274](0274-an-agent-proposes-a-shared-instruction-section-and-every-node-takes-it-only-on-the-operators-warrant-which-it-proves-itself.md): how the rule for every session is taken +- [To-be 53](../03-DESIGN/01-to-be/53-the-issue-tracker.md): the design this record amends +- Playbook [03 — issues](../00-META/process/03-issues.md) diff --git a/03-DESIGN/01-to-be/53-the-issue-tracker.md b/03-DESIGN/01-to-be/53-the-issue-tracker.md index 98729730..815c8130 100644 --- a/03-DESIGN/01-to-be/53-the-issue-tracker.md +++ b/03-DESIGN/01-to-be/53-the-issue-tracker.md @@ -8,6 +8,7 @@ decisions: - 02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md - 02-DECISIONS/0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md - 02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md + - 02-DECISIONS/0283-an-agent-moves-an-issue-to-fixing-under-its-own-name-and-an-issue-is-resolved-only-from-fixing.md --- # 53 — The issue tracker @@ -68,8 +69,8 @@ agent finds them with `mesh_search` ("open an issue") and calls them as `issue-t |---|---|---| | `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 | +| `list` | optionally a status, an owner, a phrase | compact rows, newest first, short enough to read in a chat: number, status, owner, title, and the worker with since and whether it is stale | +| `move` | a number, a status, and what that status needs; `by` for `diagnosing` and `fixing`, and `take-over` with a reason to take an issue over from another worker | 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 | | `set` | a number and any of `severity`, `needs`, `suggested-owner` | changes them as a person decides (§ 10) | @@ -80,11 +81,16 @@ Each verb's description says in plain words what it does and what it refuses, wi 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. +caller; the module records that identity as who acted. No verb takes an actor. `by` on `move` is not one: it names +the issue's worker, who works on it, as the worker states it +([ADR 0283](../../02-DECISIONS/0283-an-agent-moves-an-issue-to-fixing-under-its-own-name-and-an-issue-is-resolved-only-from-fixing.md)). ## 3. What one issue holds -- `number` (its key), `title`, `status` (`open`, `diagnosing`, `located`, `resolved`, `wontfix`), `opened`, `closed`; +- `number` (its key), `title`, `status` (`open`, `diagnosing`, `located`, `fixing`, `resolved`, `wontfix`), `opened`, + `closed`; +- `worker`, while it is `diagnosing` or `fixing`: who works on it as who@node, `since` when, and `active`, the last + time a caller did anything on it (§ 5); - `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; @@ -137,7 +143,9 @@ at the moment the module is switched on. Numbers already skipped stay unused, an **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. +the events sees a change as it happens. `moved` also carries the worker a move names, as `by`, with `since`, and who +acted as `caller`; on a take-over `took-over-from` and the reason, and `released` when a move ends the work. No +event carries a secret, and an issue holds none. ## 5. The cycle's rules, at the write @@ -145,13 +153,17 @@ the events sees a change as it happens. No event carries a secret, and an issue | The issue moves to | Needs | |---|---| -| any status | one of the five | +| any status | one of the six | +| `diagnosing` or `fixing` | `by`, the worker as who@node; refused while another worker is current, naming them, unless `take-over` gives a reason, which is noted; a worker with nothing done on the issue for `worker-stale-after` (24 hours) is stale and may be taken over from without one | +| any other status | no `by` and no `take-over`; the move ends the work | | `located` | `located-in` names at least one registered owner | -| `resolved` | `located-in`, and `fixed-by` saying what fixed it | +| `resolved` | the issue is at `fixing` (or already `resolved`, to correct what it holds); `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` checked the folders that carried status until +A refused move changes nothing and says which rule refused it. The rows for `diagnosing`, `fixing` and `resolved` are +[ADR 0283](../../02-DECISIONS/0283-an-agent-moves-an-issue-to-fixing-under-its-own-name-and-an-issue-is-resolved-only-from-fixing.md)'s: +resolved only from `fixing` is the check that an agent said it started the fix. `cycle.py` checked the folders that carried status until slice 3; since then it refuses a status key in any report, and judges no issue's status. ## 6. How the operator and agents use it @@ -161,6 +173,11 @@ or governance issue, writes `04-ISSUES/NNN-slug/` under it with the number in th 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. +**An agent says when it starts.** Before it starts work on an issue, an agent moves it to `diagnosing` (to find where +it lives) or `fixing` (to fix it) with `by`, its own name and node. `list` then shows who works on what, and a second +agent is refused before it starts the same work. An agent that keeps working notes what it finds, which keeps it the +worker; one that stops moves the issue on, or is stale a day later. + ## 7. How it is checked | Rule | Checked by | @@ -171,6 +188,7 @@ in one call, from any session, and the responder on a channel answers from it on | 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 | +| A worker is named on a move to diagnosing or fixing, another is refused while it is current, a take-over is noted, a stale one may be taken over from, and resolved is reached only from fixing | the rows of [ADR 0283](../../02-DECISIONS/0283-an-agent-moves-an-issue-to-fixing-under-its-own-name-and-an-issue-is-resolved-only-from-fixing.md)'s "How it is checked" | | An issue's delivery line follows the delivery; ready to verify once; a stuck fix said once and cleared | the rows of [ADR 0281](../../02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md)'s "How it is checked" | | The triage fills only what is empty, links no cycle, and an issue is ready only when its dependencies' fixes are delivered | the rows of [ADR 0279](../../02-DECISIONS/0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md)'s "How it is checked" | | The duplicate check acts only on a clear duplicate, never on an older or a resolved issue, once per issue, loudly when it cannot judge | the rows of [ADR 0278](../../02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md)'s "How it is checked" | @@ -195,7 +213,7 @@ within a few minutes, keeping its report on the older one; an issue that repeats was checked** ([ADR 0278](../../02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md)). - **Started by the tracker's own `opened` event**, which the module consumes. Only an issue still at `open` is - checked, against every older issue at `open`, `diagnosing` or `located`; resolved and wontfix issues are never + checked, against every older issue at `open`, `diagnosing`, `located` or `fixing`; resolved and wontfix issues are never offered or changed. - **Judged in two questions** to the agent module's `claude_code_judge` on the tracker's node: a shortlist of at most five by titles, then a verdict on the reports (`duplicate`, `related` or `none`, whether it is certain, @@ -249,13 +267,14 @@ stuck after its merge is said** ([ADR 0281](../../02-DECISIONS/0281-an-issue-kno - `get` and each row of `list` carry **delivery**, one line: "fix not delivered: …", "held: …", "fix merged, not delivered", "fix not merged", "nothing heard …", "delivered to every machine it should reach". - When every fix of an issue not yet resolved is delivered, it gains a note and the event `ready-to-verify`, once; - it is resolved after the acceptance is proven live. A fix is named while the issue stays `located`: `move` to the - same status changes what the issue holds. + it is resolved after the acceptance is proven live. A fix is named while the issue stays `fixing`: `move` to the + same status, with `by`, changes what the issue holds. - A fix merged and not delivered after `stuck-after` (two hours) is said once: a note on the issue and a warning on the operator's channel, cleared when it is delivered. It is not a controller condition: a module that runs only its bundle cannot raise one. ## What it leaves out -Priorities beyond severity, claims, notifications beyond the `ready` and `ready-to-verify` events and the stuck-fix warning, 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. +Priorities beyond severity, notifications beyond the `ready` and `ready-to-verify` events and the stuck-fix warning, 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. Who works on an issue, listed here until +2026-10-10, is settled by [ADR 0283](../../02-DECISIONS/0283-an-agent-moves-an-issue-to-fixing-under-its-own-name-and-an-issue-is-resolved-only-from-fixing.md).