Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81aad4189b |
@@ -0,0 +1,93 @@
|
||||
---
|
||||
status: active
|
||||
initiated: 2026-10-10
|
||||
touches:
|
||||
- 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
|
||||
- 02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md
|
||||
- 02-DECISIONS/0206-a-node-reports-the-anthropic-grant-it-holds-and-the-licence-manager-adopts-a-licence-by-refreshing-it.md
|
||||
- 00-META/process/03-issues.md
|
||||
- 00-META/glossary.md
|
||||
- the agent module's tools and the licences it spends
|
||||
became: []
|
||||
---
|
||||
|
||||
# 043 — An issue's complexity, as a hint to which model should solve it
|
||||
|
||||
## What
|
||||
|
||||
Whether the issue tracker can estimate **how hard an issue is to solve**, and use that estimate to
|
||||
suggest **which model an agent should solve it with**: a smaller, cheaper model for a simple fix, the
|
||||
strongest available for a change to the core of the mesh. The idea is the operator's, from 2026-10-10,
|
||||
and he named it a research topic, not a build.
|
||||
|
||||
The tracker already asks a model one question about every new issue: the duplicate check
|
||||
([ADR 0278](../../02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md))
|
||||
reads the new issue's report against the backlog, through the agent module's tool that answers a
|
||||
structured question with no tools of its own. An estimate of complexity could be one more answer from
|
||||
the same kind of judgement. Whether it should be, and whether its answer is worth acting on, is what this
|
||||
effort is for.
|
||||
|
||||
## Why
|
||||
|
||||
Agents solve most issues on the mesh, and every session spends a licence's allowance. Today the model a
|
||||
session runs is whatever the session was started with: nothing in an issue says what it needs, and
|
||||
nothing records afterwards which model solved it, how long it took or whether its fix held. A strong
|
||||
model on a one-line fix spends more than it has to; a weak model on a change to the controller or the
|
||||
node-engine can produce a fix that looks done, passes a careless review, and breaks a node that updates
|
||||
unattended. The second cost is far larger than the first, so a hint is only worth having if it is right
|
||||
where it matters most.
|
||||
|
||||
## The questions
|
||||
|
||||
1. **What "complexity" means here.** Lines changed, repositories touched, whether a core component or
|
||||
the store's schema is involved, whether the cause is known yet, whether a decision is needed first?
|
||||
Are these one scale or several? Which of them can be read from a **report**, before anyone has
|
||||
diagnosed the issue, and which only become known once it is `located`? An estimate made at `open` may
|
||||
have to be revised at `located`, and the question is whether the first estimate is worth making at all.
|
||||
2. **Who estimates, and when.** A judgement at `open`, beside the duplicate check; one at `located`, when
|
||||
the owner is known; the agent that diagnoses the issue, in its own words; or the operator. Can the
|
||||
owner named in `located-in` alone carry most of the signal (a module's own tool versus the controller)?
|
||||
3. **Where the hint lives.** A field stored on each issue, set and changed through a verb with who set it
|
||||
and why, or a value **derived** each time from the issue's fields and a setting that maps classes of
|
||||
work to models? A stored hint can be overruled per issue; a derived one changes everywhere when the
|
||||
setting changes, which matters when a newer model arrives. Is the hint a model's name, or a tier that a
|
||||
setting maps to whatever model holds that tier today?
|
||||
4. **How the outcome is measured, so the choice rests on evidence.** What the mesh would have to record
|
||||
per solved issue: the model used, tokens and wall time spent, whether the pull request passed its
|
||||
independent review and both required checks (`mesh/merge-gate`, `mesh/repo-check`) the first time,
|
||||
how many rounds of rework it took, and whether the issue was reopened or a follow-up opened against
|
||||
the same cause. Which of these does the mesh already hold (the tracker's notes and links, the
|
||||
forge's review history, the delivery's checks), and which are recorded nowhere today? Without this,
|
||||
any mapping from complexity to model is an assertion.
|
||||
5. **The risk of a weak model on a core change.** Failure must be loud: how does a session that was
|
||||
given too weak a model say so, rather than hand over a fix that reports success? Should a hint ever
|
||||
lower the model below what the session was started with, or only raise it? Should some owners (the
|
||||
controller, the node-engine, the store, the foundation) be outside the hint entirely, always solved
|
||||
with the strongest model? Is review by a stronger model than the author's a cheaper safeguard than
|
||||
choosing the author well?
|
||||
6. **How it relates to the fields being added to the tracker.** Severity says how much an issue hurts;
|
||||
what an issue needs says what must happen before it can move. Complexity says how hard the fix is.
|
||||
Are these three separate fields, or is complexity derivable from the other two and the owner? A
|
||||
severe issue can be simple, and a minor one can sit in the core.
|
||||
7. **Who acts on the hint.** Does an agent session read it and start a sub-agent with that model; does
|
||||
the operator see it when choosing what to hand off; does a future dispatcher assign work by it? A
|
||||
hint nobody reads changes nothing, and the effort should name its reader before anything is built.
|
||||
8. **Prior art.** Routing a request to the cheapest model that answers it well is studied for single
|
||||
questions (model cascades and routers that learn from preference data); estimating a task's
|
||||
difficulty is studied for software issues in agent benchmarks, and in teams as estimation by size or
|
||||
points. What carries over to a repair done by an agent across a pull request, review and delivery,
|
||||
and what does not?
|
||||
|
||||
## Words
|
||||
|
||||
The glossary has no word for an issue's estimated difficulty, nor for a suggested model. This effort
|
||||
uses **complexity** and **model hint** as plain descriptions until a decision settles them, and
|
||||
proposes them then through the glossary's own rules.
|
||||
|
||||
## What it touches
|
||||
|
||||
The issue tracker's fields and verbs ([to-be 53](../../03-DESIGN/01-to-be/53-the-issue-tracker.md)),
|
||||
the judgement the duplicate check already makes (ADR 0278), the agent module and the licences it spends
|
||||
(ADR 0206), the issues playbook ([03](../../00-META/process/03-issues.md)), and, if outcomes are recorded,
|
||||
the forge's reviews and the delivery's required checks as sources of evidence.
|
||||
@@ -181,12 +181,6 @@ gate waits rather than fails. What a consumer finds while its provider is health
|
||||
reaches a person through the operator's conversation, or a healer; a healer that restarts on health is its
|
||||
own decision.
|
||||
|
||||
> **The mechanism changed — 2026-10-10, by ADR 0280.** Rule 6 still stands for the node-engine and for
|
||||
> every module but one: the dnsmasq module restarts its own resolver when the resolver stops answering,
|
||||
> braked and said as its health, by the operator's word
|
||||
> ([ADR 0280](0280-the-resolver-restarts-itself-when-it-stops-answering-braked-and-said-as-its-health.md)).
|
||||
> Any other restart on health still needs its own record.
|
||||
|
||||
**7. A declaration is proved before it is trusted.** `module check` refuses a `health` field that names an
|
||||
endpoint the module does not declare, an interval, timeout or count outside its bounds, or a tool the module
|
||||
does not serve. A bed from mesh-lab, run by the catalogue's check on the build seat, starts every resource
|
||||
|
||||
-106
@@ -1,106 +0,0 @@
|
||||
---
|
||||
topic: what runs on it
|
||||
status: accepted
|
||||
date: 2026-10-10
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
extends: 02-DECISIONS/0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md
|
||||
---
|
||||
|
||||
# 280. The resolver restarts itself when it stops answering, braked, and says it as its health
|
||||
|
||||
## Context
|
||||
|
||||
[ADR 0240](0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md) rule 6 says nothing is
|
||||
restarted for being unhealthy, and that "a healer that restarts on health is its own decision"; its
|
||||
options left that to "a record of its own under ADR 0231". This is that record, for one module.
|
||||
|
||||
[Issue 348](../04-ISSUES/348-a-resolver-that-stopped-answering-ran-on-unnoticed-and-builds-sent-after-it-were-blamed/00-report.md):
|
||||
on 2026-10-09 the mesh's resolver on the anchor refused every question on its private address for
|
||||
twenty minutes (10:57:57 to 11:18:12 UTC) while its unit stayed active and its journal said nothing.
|
||||
Loopback still answered. The certificate authority failed its health check from 10:59:37 UTC, and builds
|
||||
sent in that window were blamed for a failure that was the resolver's. Two facts shape the answer:
|
||||
|
||||
- **The cause is a known failure of the resolver itself.** dnsmasq 2.93 closes every listener when an
|
||||
address re-read fails while a bridge is torn down; reproduced in a network namespace, 53 closings in
|
||||
120 teardowns. The catalogue's fix binds each address once at start (`bind-interfaces`), which removes
|
||||
the known cause, but a resolver that stops answering for another reason looks the same.
|
||||
- **A resolver that stops answering can cut its machine off from the rest of the mesh by name.** Then
|
||||
nothing outside the machine reaches it to repair it: not the gate's rollback
|
||||
([ADR 0236](0236-a-build-is-judged-on-its-first-machine-and-put-back-by-something-other-than-itself-and-so-it-rolls-out-unattended.md)),
|
||||
and not a healer in the controller
|
||||
([ADR 0231](0231-a-healer-acts-on-what-observation-raised-and-only-observation-says-it-worked.md)),
|
||||
which acts by sending to the machine over the bus.
|
||||
|
||||
## Considered Options
|
||||
|
||||
1. *Keep rule 6 and let the condition reach the operator.* Rejected: the operator is often asleep or away,
|
||||
and twenty minutes of a dead resolver on the anchor stalls every machine that resolves through it. The
|
||||
restart is what the operator did by hand on 2026-10-09.
|
||||
2. *A healer row in the controller's registry (ADR 0231) that restarts the unit.* Rejected for this case:
|
||||
the repair must reach a machine whose name lookups are failing, and the controller reaches a machine
|
||||
only through the bus. It remains the right shape for a repair that does not cut its own path.
|
||||
3. **The module repairs its own resolver, as ADR 0224 §5 lets a provider repair a known failure:
|
||||
braked, observed by its next look, and said as its health.** Chosen.
|
||||
|
||||
## Decision
|
||||
|
||||
**The dnsmasq module restarts its own resolver when the resolver stops answering, and only then.** Its
|
||||
long-running watch asks the machine's own name on every listen address, over UDP and TCP, every 15
|
||||
seconds. After two failing looks in a row it restarts the resolver's unit. It restarts only a unit that is
|
||||
running; a stopped or failed unit is the service manager's and the operator's.
|
||||
|
||||
**The repair is braked.** At most one restart every two minutes and three within an hour. The budget is
|
||||
kept in the watch's state on the machine, so a restart of the watch itself does not reset it. When the
|
||||
budget is spent the watch restarts nothing more until the oldest restart is an hour old, and says why.
|
||||
|
||||
**Only the next look says whether it worked** (ADR 0231's rule, applied in the module). A restart helped
|
||||
only when a look within a minute after it answered. The module's declared health check (`dnsmasq_health`,
|
||||
ADR 0240 rule 2) reads the watch: a restart that helped leaves the module healthy; one that did not, or a
|
||||
spent budget, is unhealthy, which the node-engine states and the controller raises as
|
||||
`module.dnsmasq.<machine>.unhealthy`. The operator hears of it then.
|
||||
|
||||
**Every restart is said**, as the module's `restarted` event and in the watch's state, which
|
||||
`dnsmasq_health` shows.
|
||||
|
||||
**The operator's word.** Rule 6 and the catalogue change (mesh-catalog pull request 161) both said this
|
||||
departure needed the operator's word or a healer record. The operator gave it on 2026-10-10 by approving
|
||||
the merge, knowing that it makes the resolver restart itself.
|
||||
|
||||
**This is one module's exception, not a change to rule 6.** Any other module that wants to restart on
|
||||
health needs its own record, or a healer row under ADR 0231.
|
||||
|
||||
## Consequences
|
||||
|
||||
- A resolver that stops answering is back within about 30 seconds plus its start time, instead of after a
|
||||
person notices it.
|
||||
- **A module's own repair is not a healer.** It is not counted by the controller's mesh-wide brake, and
|
||||
it is not listed by the controller's `healers` verb. Its own brake (three an hour) is what bounds it,
|
||||
and its `restarted` event and health state are where it is seen.
|
||||
- A restart hides a failure for as long as it helps. The evidence of each one is kept in the event and
|
||||
the watch's state, so a resolver that is restarted every hour is still visible there. Nothing raises a
|
||||
condition for repeated restarts that each helped; that is not built, and the operator hears only of a
|
||||
restart that did not help.
|
||||
- The rule 6 test in the node-engine still holds: the node-engine restarts nothing. The restart is the
|
||||
module's, made through the service manager on its own machine.
|
||||
|
||||
## References
|
||||
|
||||
- [ADR 0240](0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md) rule 6, which this
|
||||
record makes one exception to.
|
||||
- [ADR 0231](0231-a-healer-acts-on-what-observation-raised-and-only-observation-says-it-worked.md) — the
|
||||
rules a repair keeps: braked, and only observation says it worked.
|
||||
- [ADR 0224](0224-a-provider-that-keeps-failing-a-consumer-is-a-problem-the-controller-reports.md) §5 —
|
||||
a module repairing a known failure of its own.
|
||||
- [Issue 348](../04-ISSUES/348-a-resolver-that-stopped-answering-ran-on-unnoticed-and-builds-sent-after-it-were-blamed/00-report.md).
|
||||
- mesh-catalog pull request 161, branch `fix/the-resolver-answers-or-is-restarted`, `modules/dnsmasq`.
|
||||
|
||||
## How it is checked
|
||||
|
||||
| What | Checked by |
|
||||
|---|---|
|
||||
| two failing looks restart the unit, within the gap and the hourly budget | `TestTheWatchRestartsOnASecondFailingLookWithinItsBudget` (mesh-catalog `modules/dnsmasq`) |
|
||||
| a restart helped only when a look soon after it answered; health says whether it did | `TestTheWatchRestartsAResolverThatStoppedAnsweringAndHealthSaysWhetherItHelped`, `TestARestartHelpedOnlyWhenTheResolverAnsweredSoonAfterIt` |
|
||||
| only a running unit is restarted | `TestTheWatchRestartsOnlyAUnitThatRuns` |
|
||||
| the budget survives the watch's own restart | `TestTheWatchLoopKeepsItsBudgetAcrossItsOwnRestart` |
|
||||
| live | `dnsmasq_health` on each machine that runs the resolver: its restarts within the hour and why |
|
||||
@@ -2,7 +2,7 @@
|
||||
layer: to-be
|
||||
status: in-progress
|
||||
code: [mesh-host, mesh-controller, mesh-lab, mesh-catalog]
|
||||
updated: 2026-10-10
|
||||
updated: 2026-10-08
|
||||
decisions:
|
||||
- 02-DECISIONS/0254-a-wait-for-a-persons-new-login-is-not-a-gates-failure-and-each-module-of-a-send-keeps-its-own-pass.md
|
||||
- 02-DECISIONS/0240-a-module-says-how-it-is-healthy-and-the-node-engine-judges-it.md
|
||||
@@ -10,7 +10,6 @@ decisions:
|
||||
- 02-DECISIONS/0247-a-machine-with-a-vpn-client-routes-names-by-domain-through-a-resolver-of-its-own.md
|
||||
- 02-DECISIONS/0236-a-build-is-judged-on-its-first-machine-and-put-back-by-something-other-than-itself-and-so-it-rolls-out-unattended.md
|
||||
- 02-DECISIONS/0227-the-core-holds-nine-rules-each-checked-and-is-built-to-them-in-six-phases.md
|
||||
- 02-DECISIONS/0280-the-resolver-restarts-itself-when-it-stops-answering-braked-and-said-as-its-health.md
|
||||
- 02-DECISIONS/0256-a-machines-hardware-condition-is-read-by-one-module-on-every-machine-and-a-fault-is-said-through-its-health.md
|
||||
---
|
||||
|
||||
@@ -210,11 +209,6 @@ The runtime restarts what exits, as now. The node-engine never restarts, recreat
|
||||
being unhealthy. The condition reaches the operator through the conversation (to-be 46), or a healer once a
|
||||
record gives one that act.
|
||||
|
||||
One module repairs itself, by its own record: the dnsmasq module's watch restarts the resolver's unit when
|
||||
it stops answering, at most once every two minutes and three times an hour, and says through its declared
|
||||
health whether the restart helped ([ADR 0280](../../02-DECISIONS/0280-the-resolver-restarts-itself-when-it-stops-answering-braked-and-said-as-its-health.md)). The node-engine still restarts
|
||||
nothing; any other restart on health needs a record of its own.
|
||||
|
||||
## 8. A declaration is proved before it is trusted
|
||||
|
||||
- **`module check`** refuses a `health` field naming an endpoint the module does not declare under
|
||||
@@ -489,8 +483,7 @@ controller.
|
||||
|
||||
## What is not decided here
|
||||
|
||||
- Whether a healer restarts what stays unhealthy (its own record, under ADR 0231). The resolver's own
|
||||
restart is decided, for that module alone, by ADR 0280.
|
||||
- Whether a healer restarts what stays unhealthy (its own record, under ADR 0231).
|
||||
- Health of scheduled work, and of what a module's events do (issue 276).
|
||||
- A slow starter that needs more than five minutes to be ready — a change to the bound, recorded, when one
|
||||
exists.
|
||||
|
||||
Reference in New Issue
Block a user